refactoring: explict config state instead of globals

This commit is contained in:
Andreas Rumpf
2017-02-01 15:39:56 +01:00
parent 5565b9ef10
commit f04d21f279
12 changed files with 110 additions and 79 deletions

View File

@@ -1221,7 +1221,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
injectG()
result = newModule(g, module)
if optGenIndex in gGlobalOptions and g.generatedHeader == nil:
let f = if headerFile.len > 0: headerFile else: gProjectFull
let f = if graph.config.headerFile.len > 0: graph.config.headerFile else: gProjectFull
g.generatedHeader = rawNewModule(g, module,
changeFileExt(completeCFilePath(f), hExt))
incl g.generatedHeader.flags, isHeaderFile
@@ -1373,11 +1373,12 @@ proc myClose(b: PPassContext, n: PNode): PNode =
for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym)
genMainProc(m)
proc cgenWriteModules*(backend: RootRef) =
proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
let g = BModuleList(backend)
# we need to process the transitive closure because recursive module
# deps are allowed (and the system module is processed in the wrong
# order anyway)
g.config = config
if g.generatedHeader != nil: finishModule(g.generatedHeader)
while g.forwardedProcsCounter > 0:
for m in cgenModules(g):

View File

@@ -115,6 +115,7 @@ type
breakPointId*: int
breakpoints*: Rope # later the breakpoints are inserted into the main proc
typeInfoMarker*: TypeCache
config*: ConfigRef
TCGen = object of TPassContext # represents a C source file
s*: TCFileSections # sections of the C file

View File

@@ -47,7 +47,8 @@ type
passPP # preprocessor called processCommand()
proc processCommand*(switch: string, pass: TCmdLinePass)
proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo)
proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
config: ConfigRef = nil)
# implementation
@@ -312,7 +313,8 @@ proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
expectArg(switch, arg, pass, info)
options.inclDynlibOverride(arg)
proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
config: ConfigRef = nil) =
var
theOS: TSystemOS
cpu: TSystemCPU
@@ -523,7 +525,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: cLinkedLibs.add arg.processPath(info)
of "header":
headerFile = arg
if config != nil: config.headerFile = arg
incl(gGlobalOptions, optGenIndex)
of "index":
processOnOffSwitchG({optGenIndex}, arg, pass, info)
@@ -646,6 +648,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optNoCppExceptions)
defineSymbol("noCppExceptions")
of "cppdefine":
expectArg(switch, arg, pass, info)
if config != nil:
config.cppDefine(arg)
else:
if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
else: invalidCmdLineOption(pass, switch, info)

View File

@@ -101,3 +101,4 @@ proc initDefines*() =
defineSymbol("nimImmediateDeprecated")
defineSymbol("nimNewShiftOps")
defineSymbol("nimDistros")
defineSymbol("nimHasCppDefine")

View File

@@ -72,7 +72,7 @@ proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) =
#registerPass(cleanupPass())
compileProject(graph, cache)
cgenWriteModules(graph.backend)
cgenWriteModules(graph.backend, graph.config)
if gCmd != cmdRun:
let proj = changeFileExt(gProjectFull, "")
extccomp.callCCompiler(proj)
@@ -294,4 +294,4 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
resetAttributes()
proc mainCommand*() = mainCommand(newModuleGraph(), newIdentCache())
proc mainCommand*() = mainCommand(newModuleGraph(newConfigRef()), newIdentCache())

View File

@@ -25,7 +25,7 @@
## - Its dependent module stays the same.
##
import ast, intsets, tables
import ast, intsets, tables, options
type
ModuleGraph* = ref object
@@ -39,16 +39,18 @@ type
importStack*: seq[int32] # The current import stack. Used for detecting recursive
# module dependencies.
backend*: RootRef # minor hack so that a backend can extend this easily
config*: ConfigRef
{.this: g.}
proc newModuleGraph*(): ModuleGraph =
proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph =
result = ModuleGraph()
initStrTable(result.packageSyms)
result.deps = initIntSet()
result.modules = @[]
result.importStack = @[]
result.inclToMod = initTable[int32, int32]()
result.config = config
proc resetAllModules*(g: ModuleGraph) =
initStrTable(packageSyms)

View File

@@ -37,7 +37,7 @@ proc prependCurDir(f: string): string =
else:
result = f
proc handleCmdLine(cache: IdentCache) =
proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
if paramCount() == 0:
writeCommandLineUsage()
else:
@@ -59,22 +59,22 @@ proc handleCmdLine(cache: IdentCache) =
gProjectName = p.name
else:
gProjectPath = canonicalizePath getCurrentDir()
loadConfigs(DefaultConfig) # load all config files
loadConfigs(DefaultConfig, config) # load all config files
let scriptFile = gProjectFull.changeFileExt("nims")
if fileExists(scriptFile):
runNimScript(cache, scriptFile, freshDefines=false)
runNimScript(cache, scriptFile, freshDefines=false, config)
# 'nim foo.nims' means to just run the NimScript file and do nothing more:
if scriptFile == gProjectFull: return
elif fileExists(gProjectPath / "config.nims"):
# directory wide NimScript file
runNimScript(cache, gProjectPath / "config.nims", freshDefines=false)
runNimScript(cache, gProjectPath / "config.nims", freshDefines=false, config)
# now process command line arguments again, because some options in the
# command line can overwite the config file's settings
extccomp.initVars()
processCmdLine(passCmd2, "")
if options.command == "":
rawMessage(errNoCommand, command)
mainCommand(newModuleGraph(), cache)
mainCommand(newModuleGraph(config), cache)
if optHints in gOptions and hintGCStats in gNotes: echo(GC_getStatistics())
#echo(GC_getStatistics())
if msgs.gErrorCounter == 0:
@@ -118,5 +118,5 @@ when compileOption("gc", "v2") or compileOption("gc", "refc"):
condsyms.initDefines()
when not defined(selftest):
handleCmdLine(newIdentCache())
handleCmdLine(newIdentCache(), newConfigRef())
msgQuit(int8(msgs.gErrorCounter > 0))

View File

@@ -21,37 +21,37 @@ proc ppGetTok(L: var TLexer, tok: var TToken) =
rawGetTok(L, tok)
while tok.tokType in {tkComment}: rawGetTok(L, tok)
proc parseExpr(L: var TLexer, tok: var TToken): bool
proc parseAtom(L: var TLexer, tok: var TToken): bool =
proc parseExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool
proc parseAtom(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
if tok.tokType == tkParLe:
ppGetTok(L, tok)
result = parseExpr(L, tok)
result = parseExpr(L, tok, config)
if tok.tokType == tkParRi: ppGetTok(L, tok)
else: lexMessage(L, errTokenExpected, "\')\'")
elif tok.ident.id == ord(wNot):
ppGetTok(L, tok)
result = not parseAtom(L, tok)
result = not parseAtom(L, tok, config)
else:
result = isDefined(tok.ident)
ppGetTok(L, tok)
proc parseAndExpr(L: var TLexer, tok: var TToken): bool =
result = parseAtom(L, tok)
proc parseAndExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
result = parseAtom(L, tok, config)
while tok.ident.id == ord(wAnd):
ppGetTok(L, tok) # skip "and"
var b = parseAtom(L, tok)
var b = parseAtom(L, tok, config)
result = result and b
proc parseExpr(L: var TLexer, tok: var TToken): bool =
result = parseAndExpr(L, tok)
proc parseExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
result = parseAndExpr(L, tok, config)
while tok.ident.id == ord(wOr):
ppGetTok(L, tok) # skip "or"
var b = parseAndExpr(L, tok)
var b = parseAndExpr(L, tok, config)
result = result or b
proc evalppIf(L: var TLexer, tok: var TToken): bool =
proc evalppIf(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
ppGetTok(L, tok) # skip 'if' or 'elif'
result = parseExpr(L, tok)
result = parseExpr(L, tok, config)
if tok.tokType == tkColon: ppGetTok(L, tok)
else: lexMessage(L, errTokenExpected, "\':\'")
@@ -66,20 +66,20 @@ type
TJumpDest = enum
jdEndif, jdElseEndif
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest)
proc doElse(L: var TLexer, tok: var TToken) =
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef)
proc doElse(L: var TLexer, tok: var TToken; config: ConfigRef) =
if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
ppGetTok(L, tok)
if tok.tokType == tkColon: ppGetTok(L, tok)
if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif)
if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config)
proc doElif(L: var TLexer, tok: var TToken) =
proc doElif(L: var TLexer, tok: var TToken; config: ConfigRef) =
if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
var res = evalppIf(L, tok)
if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif)
var res = evalppIf(L, tok, config)
if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config)
else: condStack[high(condStack)] = true
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) =
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef) =
var nestedIfs = 0
while true:
if tok.ident != nil and tok.ident.s == "@":
@@ -89,11 +89,11 @@ proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) =
inc(nestedIfs)
of wElse:
if dest == jdElseEndif and nestedIfs == 0:
doElse(L, tok)
doElse(L, tok, config)
break
of wElif:
if dest == jdElseEndif and nestedIfs == 0:
doElif(L, tok)
doElif(L, tok, config)
break
of wEnd:
if nestedIfs == 0:
@@ -108,16 +108,16 @@ proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) =
else:
ppGetTok(L, tok)
proc parseDirective(L: var TLexer, tok: var TToken) =
proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef) =
ppGetTok(L, tok) # skip @
case whichKeyword(tok.ident)
of wIf:
setLen(condStack, len(condStack) + 1)
let res = evalppIf(L, tok)
let res = evalppIf(L, tok, config)
condStack[high(condStack)] = res
if not res: jumpToDirective(L, tok, jdElseEndif)
of wElif: doElif(L, tok)
of wElse: doElse(L, tok)
if not res: jumpToDirective(L, tok, jdElseEndif, config)
of wElif: doElif(L, tok, config)
of wElse: doElse(L, tok, config)
of wEnd: doEnd(L, tok)
of wWrite:
ppGetTok(L, tok)
@@ -146,58 +146,58 @@ proc parseDirective(L: var TLexer, tok: var TToken) =
ppGetTok(L, tok)
else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok))
proc confTok(L: var TLexer, tok: var TToken) =
proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef) =
ppGetTok(L, tok)
while tok.ident != nil and tok.ident.s == "@":
parseDirective(L, tok) # else: give the token to the parser
parseDirective(L, tok, config) # else: give the token to the parser
proc checkSymbol(L: TLexer, tok: TToken) =
if tok.tokType notin {tkSymbol..pred(tkIntLit), tkStrLit..tkTripleStrLit}:
lexMessage(L, errIdentifierExpected, tokToStr(tok))
proc parseAssignment(L: var TLexer, tok: var TToken) =
proc parseAssignment(L: var TLexer, tok: var TToken; config: ConfigRef) =
if tok.ident.s == "-" or tok.ident.s == "--":
confTok(L, tok) # skip unnecessary prefix
confTok(L, tok, config) # skip unnecessary prefix
var info = getLineInfo(L, tok) # save for later in case of an error
checkSymbol(L, tok)
var s = tokToStr(tok)
confTok(L, tok) # skip symbol
confTok(L, tok, config) # skip symbol
var val = ""
while tok.tokType == tkDot:
add(s, '.')
confTok(L, tok)
confTok(L, tok, config)
checkSymbol(L, tok)
add(s, tokToStr(tok))
confTok(L, tok)
confTok(L, tok, config)
if tok.tokType == tkBracketLe:
# BUGFIX: val, not s!
# BUGFIX: do not copy '['!
confTok(L, tok)
confTok(L, tok, config)
checkSymbol(L, tok)
add(val, tokToStr(tok))
confTok(L, tok)
if tok.tokType == tkBracketRi: confTok(L, tok)
confTok(L, tok, config)
if tok.tokType == tkBracketRi: confTok(L, tok, config)
else: lexMessage(L, errTokenExpected, "']'")
add(val, ']')
let percent = tok.ident != nil and tok.ident.s == "%="
if tok.tokType in {tkColon, tkEquals} or percent:
if len(val) > 0: add(val, ':')
confTok(L, tok) # skip ':' or '=' or '%'
confTok(L, tok, config) # skip ':' or '=' or '%'
checkSymbol(L, tok)
add(val, tokToStr(tok))
confTok(L, tok) # skip symbol
confTok(L, tok, config) # skip symbol
while tok.ident != nil and tok.ident.s == "&":
confTok(L, tok)
confTok(L, tok, config)
checkSymbol(L, tok)
add(val, tokToStr(tok))
confTok(L, tok)
confTok(L, tok, config)
if percent:
processSwitch(s, strtabs.`%`(val, options.gConfigVars,
{useEnvironment, useEmpty}), passPP, info)
{useEnvironment, useEmpty}), passPP, info, config)
else:
processSwitch(s, val, passPP, info)
processSwitch(s, val, passPP, info, config)
proc readConfigFile(filename: string; cache: IdentCache) =
proc readConfigFile(filename: string; cache: IdentCache; config: ConfigRef) =
var
L: TLexer
tok: TToken
@@ -207,8 +207,8 @@ proc readConfigFile(filename: string; cache: IdentCache) =
initToken(tok)
openLexer(L, filename, stream, cache)
tok.tokType = tkEof # to avoid a pointless warning
confTok(L, tok) # read in the first token
while tok.tokType != tkEof: parseAssignment(L, tok)
confTok(L, tok, config) # read in the first token
while tok.tokType != tkEof: parseAssignment(L, tok, config)
if len(condStack) > 0: lexMessage(L, errTokenExpected, "@end")
closeLexer(L)
rawMessage(hintConf, filename)
@@ -225,22 +225,22 @@ proc getSystemConfigPath(filename: string): string =
if not existsFile(result): result = joinPath([p, "etc", filename])
if not existsFile(result): result = "/etc/" & filename
proc loadConfigs*(cfg: string; cache: IdentCache) =
proc loadConfigs*(cfg: string; cache: IdentCache; config: ConfigRef = nil) =
setDefaultLibpath()
if optSkipConfigFile notin gGlobalOptions:
readConfigFile(getSystemConfigPath(cfg), cache)
readConfigFile(getSystemConfigPath(cfg), cache, config)
if optSkipUserConfigFile notin gGlobalOptions:
readConfigFile(getUserConfigPath(cfg), cache)
readConfigFile(getUserConfigPath(cfg), cache, config)
var pd = if gProjectPath.len > 0: gProjectPath else: getCurrentDir()
if optSkipParentConfigFiles notin gGlobalOptions:
for dir in parentDirs(pd, fromRoot=true, inclusive=false):
readConfigFile(dir / cfg, cache)
readConfigFile(dir / cfg, cache, config)
if optSkipProjConfigFile notin gGlobalOptions:
readConfigFile(pd / cfg, cache)
readConfigFile(pd / cfg, cache, config)
if gProjectName.len != 0:
# new project wide config file:
@@ -251,8 +251,8 @@ proc loadConfigs*(cfg: string; cache: IdentCache) =
projectConfig = changeFileExt(gProjectFull, "nimrod.cfg")
if fileExists(projectConfig):
rawMessage(warnDeprecated, projectConfig)
readConfigFile(projectConfig, cache)
readConfigFile(projectConfig, cache, config)
proc loadConfigs*(cfg: string) =
proc loadConfigs*(cfg: string; config: ConfigRef = nil) =
# for backwards compatibility only.
loadConfigs(cfg, newIdentCache())
loadConfigs(cfg, newIdentCache(), config)

View File

@@ -102,6 +102,17 @@ type
ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
ideHighlight, ideOutline
ConfigRef* = ref object ## eventually all global configuration should be moved here
cppDefines*: HashSet[string]
headerFile*: string
proc newConfigRef*(): ConfigRef =
result = ConfigRef(cppDefines: initSet[string](),
headerFile: "")
proc cppDefine*(c: ConfigRef; define: string) =
c.cppDefines.incl define
var
gIdeCmd*: IdeCmd
@@ -122,7 +133,7 @@ var
outFile*: string = ""
docSeeSrcUrl*: string = "" # if empty, no seeSrc will be generated. \
# The string uses the formatting variables `path` and `line`.
headerFile*: string = ""
#headerFile*: string = ""
gVerbosity* = 1 # how verbose the compiler is
gNumberOfProcessors*: int # number of processors
gWholeProject*: bool # for 'doc2': output any dependency

View File

@@ -25,7 +25,8 @@ proc listDirs(a: VmArgs, filter: set[PathComponent]) =
if kind in filter: result.add path
setResult(a, result)
proc setupVM*(module: PSym; cache: IdentCache; scriptName: string): PEvalContext =
proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
config: ConfigRef = nil): PEvalContext =
# For Nimble we need to export 'setupVM'.
result = newCtx(module, cache)
result.mode = emRepl
@@ -133,12 +134,15 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string): PEvalContext
gModuleOverrides[key] = val
cbconf selfExe:
setResult(a, os.getAppFilename())
cbconf cppDefine:
if config != nil:
options.cppDefine(config, a.getString(0))
proc runNimScript*(cache: IdentCache; scriptName: string;
freshDefines=true) =
freshDefines=true; config: ConfigRef=nil) =
passes.gIncludeFile = includeModule
passes.gImportModule = importModule
let graph = newModuleGraph()
let graph = newModuleGraph(config)
if freshDefines: initDefines()
defineSymbol("nimscript")
@@ -150,7 +154,7 @@ proc runNimScript*(cache: IdentCache; scriptName: string;
var m = graph.makeModule(scriptName)
incl(m.flags, sfMainModule)
vm.globalCtx = setupVM(m, cache, scriptName)
vm.globalCtx = setupVM(m, cache, scriptName, config)
graph.compileSystemModule(cache)
discard graph.processModule(m, llStreamOpen(scriptName, fmRead), nil, cache)

View File

@@ -293,6 +293,11 @@ template task*(name: untyped; description: string; body: untyped): untyped =
setCommand "nop"
`name Task`()
proc cppDefine*(define: string) =
## tell Nim that ``define`` is a C preprocessor ``#define`` and so always
## needs to be mangled.
builtin
when not defined(nimble):
# nimble has its own implementation for these things.
var

View File

@@ -418,7 +418,7 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
options.gProjectName = unixToNativePath(p.key)
# if processArgument(pass, p, argsCount): break
proc handleCmdLine(cache: IdentCache) =
proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
if paramCount() == 0:
stdout.writeline(Usage)
else:
@@ -444,23 +444,23 @@ proc handleCmdLine(cache: IdentCache) =
gPrefixDir = binaryPath.splitPath().head.parentDir()
#msgs.writelnHook = proc (line: string) = logStr(line)
loadConfigs(DefaultConfig, cache) # load all config files
loadConfigs(DefaultConfig, cache, config) # load all config files
# now process command line arguments again, because some options in the
# command line can overwite the config file's settings
options.command = "nimsuggest"
let scriptFile = gProjectFull.changeFileExt("nims")
if fileExists(scriptFile):
runNimScript(cache, scriptFile, freshDefines=false)
runNimScript(cache, scriptFile, freshDefines=false, config)
# 'nim foo.nims' means to just run the NimScript file and do nothing more:
if scriptFile == gProjectFull: return
elif fileExists(gProjectPath / "config.nims"):
# directory wide NimScript file
runNimScript(cache, gProjectPath / "config.nims", freshDefines=false)
runNimScript(cache, gProjectPath / "config.nims", freshDefines=false, config)
extccomp.initVars()
processCmdLine(passCmd2, "")
let graph = newModuleGraph()
let graph = newModuleGraph(config)
graph.suggestMode = true
mainCommand(graph, cache)
@@ -472,4 +472,4 @@ when false:
condsyms.initDefines()
defineSymbol "nimsuggest"
handleCmdline(newIdentCache())
handleCmdline(newIdentCache(), newConfigRef())