compiler refactoring; use typesafe path handing; docgen: render symbols between modules

This commit is contained in:
Andreas Rumpf 2018-09-07 01:53:09 +02:00
commit ad9db5cb1a
45 changed files with 816 additions and 504 deletions

View file

@ -12,7 +12,7 @@
import import
ast, astalgo, ropes, options, strutils, nimlexbase, msgs, cgendata, rodutils, ast, astalgo, ropes, options, strutils, nimlexbase, msgs, cgendata, rodutils,
intsets, platform, llstream, tables, sighashes intsets, platform, llstream, tables, sighashes, pathutils
# Careful! Section marks need to contain a tabulator so that they cannot # Careful! Section marks need to contain a tabulator so that they cannot
# be part of C string literals. # be part of C string literals.
@ -226,7 +226,7 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
when not defined(nimhygiene): when not defined(nimhygiene):
{.pragma: inject.} {.pragma: inject.}
template withCFile(cfilename: string, body: untyped) = template withCFile(cfilename: AbsoluteFile, body: untyped) =
var s = llStreamOpen(cfilename, fmRead) var s = llStreamOpen(cfilename, fmRead)
if s == nil: return if s == nil: return
var L {.inject.}: TBaseLexer var L {.inject.}: TBaseLexer
@ -238,7 +238,7 @@ template withCFile(cfilename: string, body: untyped) =
body body
closeBaseLexer(L) closeBaseLexer(L)
proc readMergeInfo*(cfilename: string, m: BModule) = proc readMergeInfo*(cfilename: AbsoluteFile, m: BModule) =
## reads the merge meta information into `m`. ## reads the merge meta information into `m`.
withCFile(cfilename): withCFile(cfilename):
readKey(L, k) readKey(L, k)
@ -251,7 +251,7 @@ type
f: TCFileSections f: TCFileSections
p: TCProcSections p: TCProcSections
proc readMergeSections(cfilename: string, m: var TMergeSections) = proc readMergeSections(cfilename: AbsoluteFile, m: var TMergeSections) =
## reads the merge sections into `m`. ## reads the merge sections into `m`.
withCFile(cfilename): withCFile(cfilename):
readKey(L, k) readKey(L, k)
@ -285,7 +285,7 @@ proc mergeRequired*(m: BModule): bool =
#echo "not empty: ", i, " ", m.initProc.s[i] #echo "not empty: ", i, " ", m.initProc.s[i]
return true return true
proc mergeFiles*(cfilename: string, m: BModule) = proc mergeFiles*(cfilename: AbsoluteFile, m: BModule) =
## merges the C file with the old version on hard disc. ## merges the C file with the old version on hard disc.
var old: TMergeSections var old: TMergeSections
readMergeSections(cfilename, old) readMergeSections(cfilename, old)

View file

@ -14,7 +14,7 @@ import
nversion, nimsets, msgs, std / sha1, bitsets, idents, types, nversion, nimsets, msgs, std / sha1, bitsets, idents, types,
ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth, ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth,
condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
lowerings, semparallel, tables, sets, ndi, lineinfos lowerings, semparallel, tables, sets, ndi, lineinfos, pathutils
import strutils except `%` # collides with ropes.`%` import strutils except `%` # collides with ropes.`%`
@ -1064,7 +1064,8 @@ proc genFilenames(m: BModule): Rope =
discard cgsym(m, "dbgRegisterFilename") discard cgsym(m, "dbgRegisterFilename")
result = nil result = nil
for i in 0..<m.config.m.fileInfos.len: for i in 0..<m.config.m.fileInfos.len:
result.addf("dbgRegisterFilename($1);$N", [m.config.m.fileInfos[i].projPath.makeCString]) result.addf("dbgRegisterFilename($1);$N",
[m.config.m.fileInfos[i].projPath.string.makeCString])
proc genMainProc(m: BModule) = proc genMainProc(m: BModule) =
const const
@ -1348,7 +1349,7 @@ proc initProcOptions(m: BModule): TOptions =
let opts = m.config.options let opts = m.config.options
if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts
proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule = proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule =
new(result) new(result)
result.g = g result.g = g
result.tmpBase = rope("TM" & $hashOwner(module) & "_") result.tmpBase = rope("TM" & $hashOwner(module) & "_")
@ -1376,7 +1377,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
incl result.flags, preventStackTrace incl result.flags, preventStackTrace
excl(result.preInitProc.options, optStackTrace) excl(result.preInitProc.options, optStackTrace)
let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi") let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi")
else: "" else: AbsoluteFile""
open(result.ndi, ndiName, g.config) open(result.ndi, ndiName, g.config)
proc nullify[T](arr: var T) = proc nullify[T](arr: var T) =
@ -1427,7 +1428,7 @@ proc resetCgenModules*(g: BModuleList) =
for m in cgenModules(g): resetModule(m) for m in cgenModules(g): resetModule(m)
proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule = proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
result = rawNewModule(g, module, toFullPath(conf, module.position.FileIndex)) result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex))
proc newModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule = proc newModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
# we should create only one cgen module for each module sym # we should create only one cgen module for each module sym
@ -1446,7 +1447,7 @@ proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
injectG() injectG()
result = newModule(g, module, graph.config) result = newModule(g, module, graph.config)
if optGenIndex in graph.config.globalOptions and g.generatedHeader == nil: if optGenIndex in graph.config.globalOptions and g.generatedHeader == nil:
let f = if graph.config.headerFile.len > 0: graph.config.headerFile let f = if graph.config.headerFile.len > 0: AbsoluteFile graph.config.headerFile
else: graph.config.projectFull else: graph.config.projectFull
g.generatedHeader = rawNewModule(g, module, g.generatedHeader = rawNewModule(g, module,
changeFileExt(completeCFilePath(graph.config, f), hExt)) changeFileExt(completeCFilePath(graph.config, f), hExt))
@ -1477,9 +1478,9 @@ proc writeHeader(m: BModule) =
if optUseNimNamespace in m.config.globalOptions: result.add closeNamespaceNim() if optUseNimNamespace in m.config.globalOptions: result.add closeNamespaceNim()
result.addf("#endif /* $1 */$n", [guard]) result.addf("#endif /* $1 */$n", [guard])
if not writeRope(result, m.filename): if not writeRope(result, m.filename):
rawMessage(m.config, errCannotOpenFile, m.filename) rawMessage(m.config, errCannotOpenFile, m.filename.string)
proc getCFile(m: BModule): string = proc getCFile(m: BModule): AbsoluteFile =
let ext = let ext =
if m.compileToCpp: ".cpp" if m.compileToCpp: ".cpp"
elif m.config.cmd == cmdCompileToOC or sfCompileToObjC in m.module.flags: ".m" elif m.config.cmd == cmdCompileToOC or sfCompileToObjC in m.module.flags: ".m"
@ -1523,18 +1524,18 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
if not equalsFile(code, cfile.cname): if not equalsFile(code, cfile.cname):
if isDefined(m.config, "nimdiff"): if isDefined(m.config, "nimdiff"):
if fileExists(cfile.cname): if fileExists(cfile.cname):
copyFile(cfile.cname, cfile.cname & ".backup") copyFile(cfile.cname.string, cfile.cname.string & ".backup")
echo "diff ", cfile.cname, ".backup ", cfile.cname echo "diff ", cfile.cname.string, ".backup ", cfile.cname.string
else: else:
echo "new file ", cfile.cname echo "new file ", cfile.cname.string
if not writeRope(code, cfile.cname): if not writeRope(code, cfile.cname):
rawMessage(m.config, errCannotOpenFile, cfile.cname) rawMessage(m.config, errCannotOpenFile, cfile.cname.string)
return return
if existsFile(cfile.obj) and os.fileNewer(cfile.obj, cfile.cname): if fileExists(cfile.obj) and os.fileNewer(cfile.obj.string, cfile.cname.string):
result = false result = false
else: else:
if not writeRope(code, cfile.cname): if not writeRope(code, cfile.cname):
rawMessage(m.config, errCannotOpenFile, cfile.cname) rawMessage(m.config, errCannotOpenFile, cfile.cname.string)
# We need 2 different logics here: pending modules (including # We need 2 different logics here: pending modules (including
# 'nim__dat') may require file merging for the combination of dead code # 'nim__dat') may require file merging for the combination of dead code
@ -1570,14 +1571,14 @@ proc writeModule(m: BModule, pending: bool) =
finishTypeDescriptions(m) finishTypeDescriptions(m)
var code = genModule(m, cf) var code = genModule(m, cf)
if not writeRope(code, cfile): if not writeRope(code, cfile):
rawMessage(m.config, errCannotOpenFile, cfile) rawMessage(m.config, errCannotOpenFile, cfile.string)
addFileToCompile(m.config, cf) addFileToCompile(m.config, cf)
else: else:
# Consider: first compilation compiles ``system.nim`` and produces # Consider: first compilation compiles ``system.nim`` and produces
# ``system.c`` but then compilation fails due to an error. This means # ``system.c`` but then compilation fails due to an error. This means
# that ``system.o`` is missing, so we need to call the C compiler for it: # that ``system.o`` is missing, so we need to call the C compiler for it:
var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {}) var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
if not existsFile(cf.obj): cf.flags = {CfileFlag.Cached} if not fileExists(cf.obj): cf.flags = {CfileFlag.Cached}
addFileToCompile(m.config, cf) addFileToCompile(m.config, cf)
close(m.ndi) close(m.ndi)
@ -1592,7 +1593,7 @@ proc updateCachedModule(m: BModule) =
var code = genModule(m, cf) var code = genModule(m, cf)
if not writeRope(code, cfile): if not writeRope(code, cfile):
rawMessage(m.config, errCannotOpenFile, cfile) rawMessage(m.config, errCannotOpenFile, cfile.string)
else: else:
cf.flags = {CfileFlag.Cached} cf.flags = {CfileFlag.Cached}
addFileToCompile(m.config, cf) addFileToCompile(m.config, cf)

View file

@ -11,7 +11,7 @@
import import
ast, astalgo, ropes, passes, options, intsets, platform, sighashes, ast, astalgo, ropes, passes, options, intsets, platform, sighashes,
tables, ndi, lineinfos tables, ndi, lineinfos, pathutils
from modulegraphs import ModuleGraph from modulegraphs import ModuleGraph
@ -136,8 +136,8 @@ type
s*: TCFileSections # sections of the C file s*: TCFileSections # sections of the C file
flags*: set[Codegenflag] flags*: set[Codegenflag]
module*: PSym module*: PSym
filename*: string filename*: AbsoluteFile
cfilename*: string # filename of the module (including path, cfilename*: AbsoluteFile # filename of the module (including path,
# without extension) # without extension)
tmpBase*: Rope # base for temp identifier generation tmpBase*: Rope # base for temp identifier generation
typeCache*: TypeCache # cache the generated types typeCache*: TypeCache # cache the generated types

View file

@ -1,10 +1,17 @@
#
#
# The Nim Compiler
# (c) Copyright 2018 Nim contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Helpers for binaries that use compiler passes, eg: nim, nimsuggest, nimfix ## Helpers for binaries that use compiler passes, eg: nim, nimsuggest, nimfix
# TODO: nimfix should use this; currently out of sync
import import
compiler/[options, idents, nimconf, scriptconfig, extccomp, commands, msgs, lineinfos, modulegraphs, condsyms], options, idents, nimconf, scriptconfig, extccomp, commands, msgs,
std/os lineinfos, modulegraphs, condsyms, os, pathutils
type type
NimProg* = ref object NimProg* = ref object
@ -21,27 +28,27 @@ proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) =
self.processCmdLine(passCmd1, "", conf) self.processCmdLine(passCmd1, "", conf)
if self.supportsStdinFile and conf.projectName == "-": if self.supportsStdinFile and conf.projectName == "-":
conf.projectName = "stdinfile" conf.projectName = "stdinfile"
conf.projectFull = "stdinfile" conf.projectFull = AbsoluteFile "stdinfile"
conf.projectPath = canonicalizePath(conf, getCurrentDir()) conf.projectPath = AbsoluteDir getCurrentDir()
conf.projectIsStdin = true conf.projectIsStdin = true
elif conf.projectName != "": elif conf.projectName != "":
try: try:
conf.projectFull = canonicalizePath(conf, conf.projectName) conf.projectFull = canonicalizePath(conf, AbsoluteFile conf.projectName)
except OSError: except OSError:
conf.projectFull = conf.projectName conf.projectFull = AbsoluteFile conf.projectName
let p = splitFile(conf.projectFull) let p = splitFile(conf.projectFull)
let dir = if p.dir.len > 0: p.dir else: getCurrentDir() let dir = if p.dir.isEmpty: AbsoluteDir getCurrentDir() else: p.dir
conf.projectPath = canonicalizePath(conf, dir) conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile dir)
conf.projectName = p.name conf.projectName = p.name
else: else:
conf.projectPath = canonicalizePath(conf, getCurrentDir()) conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile getCurrentDir())
proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: ConfigRef): bool = proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: ConfigRef): bool =
loadConfigs(DefaultConfig, cache, conf) # load all config files loadConfigs(DefaultConfig, cache, conf) # load all config files
if self.suggestMode: if self.suggestMode:
conf.command = "nimsuggest" conf.command = "nimsuggest"
proc runNimScriptIfExists(path: string)= proc runNimScriptIfExists(path: AbsoluteFile)=
if fileExists(path): if fileExists(path):
runNimScript(cache, path, freshDefines = false, conf) runNimScript(cache, path, freshDefines = false, conf)
@ -53,8 +60,8 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
runNimScriptIfExists(getUserConfigPath(DefaultConfigNims)) runNimScriptIfExists(getUserConfigPath(DefaultConfigNims))
if optSkipParentConfigFiles notin conf.globalOptions: if optSkipParentConfigFiles notin conf.globalOptions:
for dir in parentDirs(conf.projectPath, fromRoot = true, inclusive = false): for dir in parentDirs(conf.projectPath.string, fromRoot = true, inclusive = false):
runNimScriptIfExists(dir / DefaultConfigNims) runNimScriptIfExists(AbsoluteDir(dir) / DefaultConfigNims)
if optSkipProjConfigFile notin conf.globalOptions: if optSkipProjConfigFile notin conf.globalOptions:
runNimScriptIfExists(conf.projectPath / DefaultConfigNims) runNimScriptIfExists(conf.projectPath / DefaultConfigNims)
@ -63,10 +70,10 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
if not self.suggestMode: if not self.suggestMode:
runNimScriptIfExists(scriptFile) runNimScriptIfExists(scriptFile)
# 'nim foo.nims' means to just run the NimScript file and do nothing more: # 'nim foo.nims' means to just run the NimScript file and do nothing more:
if fileExists(scriptFile) and scriptFile.cmpPaths(conf.projectFull) == 0: if fileExists(scriptFile) and scriptFile == conf.projectFull:
return false return false
else: else:
if scriptFile.cmpPaths(conf.projectFull) != 0: if scriptFile != conf.projectFull:
runNimScriptIfExists(scriptFile) runNimScriptIfExists(scriptFile)
else: else:
# 'nimsuggest foo.nims' means to just auto-complete the NimScript file # 'nimsuggest foo.nims' means to just auto-complete the NimScript file

View file

@ -26,7 +26,8 @@ bootSwitch(usedNoGC, defined(nogc), "--gc:none")
import import
os, msgs, options, nversion, condsyms, strutils, extccomp, platform, os, msgs, options, nversion, condsyms, strutils, extccomp, platform,
wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils, lineinfos wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils, lineinfos,
pathutils
# but some have deps to imported modules. Yay. # but some have deps to imported modules. Yay.
bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc") bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
@ -208,7 +209,7 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
proc processCompile(conf: ConfigRef; filename: string) = proc processCompile(conf: ConfigRef; filename: string) =
var found = findFile(conf, filename) var found = findFile(conf, filename)
if found == "": found = filename if found.isEmpty: found = AbsoluteFile filename
extccomp.addExternalFileToCompile(conf, found) extccomp.addExternalFileToCompile(conf, found)
const const
@ -292,31 +293,32 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
else: invalidCmdLineOption(conf, passCmd1, switch, info) else: invalidCmdLineOption(conf, passCmd1, switch, info)
proc processPath(conf: ConfigRef; path: string, info: TLineInfo, proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
notRelativeToProj = false): string = notRelativeToProj = false): AbsoluteDir =
let p = if os.isAbsolute(path) or '$' in path: let p = if os.isAbsolute(path) or '$' in path:
path path
elif notRelativeToProj: elif notRelativeToProj:
getCurrentDir() / path getCurrentDir() / path
else: else:
conf.projectPath / path conf.projectPath.string / path
try: try:
result = pathSubs(conf, p, toFullPath(conf, info).splitFile().dir) result = AbsoluteDir pathSubs(conf, p, toFullPath(conf, info).splitFile().dir)
except ValueError: except ValueError:
localError(conf, info, "invalid path: " & p) localError(conf, info, "invalid path: " & p)
result = p result = AbsoluteDir p
proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): string = proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): AbsoluteDir =
let path = if path[0] == '"': strutils.unescape(path) else: path let path = if path.len > 0 and path[0] == '"': strutils.unescape(path)
else: path
let basedir = toFullPath(conf, info).splitFile().dir let basedir = toFullPath(conf, info).splitFile().dir
let p = if os.isAbsolute(path) or '$' in path: let p = if os.isAbsolute(path) or '$' in path:
path path
else: else:
basedir / path basedir / path
try: try:
result = pathSubs(conf, p, basedir) result = AbsoluteDir pathSubs(conf, p, basedir)
except ValueError: except ValueError:
localError(conf, info, "invalid path: " & p) localError(conf, info, "invalid path: " & p)
result = p result = AbsoluteDir p
const const
errInvalidNumber = "$1 is not a valid number" errInvalidNumber = "$1 is not a valid number"
@ -331,9 +333,9 @@ proc trackDirty(conf: ConfigRef; arg: string, info: TLineInfo) =
if parseUtils.parseInt(a[3], column) <= 0: if parseUtils.parseInt(a[3], column) <= 0:
localError(conf, info, errInvalidNumber % a[2]) localError(conf, info, errInvalidNumber % a[2])
let dirtyOriginalIdx = fileInfoIdx(conf, a[1]) let dirtyOriginalIdx = fileInfoIdx(conf, AbsoluteFile a[1])
if dirtyOriginalIdx.int32 >= 0: if dirtyOriginalIdx.int32 >= 0:
msgs.setDirtyFile(conf, dirtyOriginalIdx, a[0]) msgs.setDirtyFile(conf, dirtyOriginalIdx, AbsoluteFile a[0])
conf.m.trackPos = newLineInfo(dirtyOriginalIdx, line, column) conf.m.trackPos = newLineInfo(dirtyOriginalIdx, line, column)
@ -345,7 +347,7 @@ proc track(conf: ConfigRef; arg: string, info: TLineInfo) =
localError(conf, info, errInvalidNumber % a[1]) localError(conf, info, errInvalidNumber % a[1])
if parseUtils.parseInt(a[2], column) <= 0: if parseUtils.parseInt(a[2], column) <= 0:
localError(conf, info, errInvalidNumber % a[2]) localError(conf, info, errInvalidNumber % a[2])
conf.m.trackPos = newLineInfo(conf, a[0], line, column) conf.m.trackPos = newLineInfo(conf, AbsoluteFile a[0], line, column)
proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if pass in {passCmd2, passPP}: if pass in {passCmd2, passPP}:
@ -359,14 +361,16 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
case switch.normalize case switch.normalize
of "path", "p": of "path", "p":
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)
addPath(conf, if pass == passPP: processCfgPath(conf, arg, info) else: processPath(conf, arg, info), info) addPath(conf, if pass == passPP: processCfgPath(conf, arg, info)
else: processPath(conf, arg, info), info)
of "nimblepath", "babelpath": of "nimblepath", "babelpath":
# keep the old name for compat # keep the old name for compat
if pass in {passCmd2, passPP} and optNoNimblePath notin conf.globalOptions: if pass in {passCmd2, passPP} and optNoNimblePath notin conf.globalOptions:
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)
var path = processPath(conf, arg, info, notRelativeToProj=true) var path = processPath(conf, arg, info, notRelativeToProj=true)
let nimbleDir = getEnv("NIMBLE_DIR") let nimbleDir = AbsoluteDir getEnv("NIMBLE_DIR")
if nimbleDir.len > 0 and pass == passPP: path = nimbleDir / "pkgs" if not nimbleDir.isEmpty and pass == passPP:
path = nimbleDir / RelativeDir"pkgs"
nimblePath(conf, path, info) nimblePath(conf, path, info)
of "nonimblepath", "nobabelpath": of "nonimblepath", "nobabelpath":
expectNoArg(conf, switch, arg, pass, info) expectNoArg(conf, switch, arg, pass, info)
@ -374,20 +378,14 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "excludepath": of "excludepath":
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)
let path = processPath(conf, arg, info) let path = processPath(conf, arg, info)
conf.searchPaths.keepItIf(it != path)
conf.searchPaths.keepItIf(cmpPaths(it, path) != 0) conf.lazyPaths.keepItIf(it != path)
conf.lazyPaths.keepItIf(cmpPaths(it, path) != 0)
if (len(path) > 0) and (path[len(path) - 1] == DirSep):
let strippedPath = path[0 .. (len(path) - 2)]
conf.searchPaths.keepItIf(cmpPaths(it, strippedPath) != 0)
conf.lazyPaths.keepItIf(cmpPaths(it, strippedPath) != 0)
of "nimcache": of "nimcache":
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)
conf.nimcacheDir = processPath(conf, arg, info, true) conf.nimcacheDir = processPath(conf, arg, info, true)
of "out", "o": of "out", "o":
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)
conf.outFile = arg conf.outFile = AbsoluteFile arg
of "docseesrcurl": of "docseesrcurl":
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)
conf.docSeeSrcUrl = arg conf.docSeeSrcUrl = arg
@ -411,7 +409,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
if pass in {passCmd2, passPP}: processCompile(conf, arg) if pass in {passCmd2, passPP}: processCompile(conf, arg)
of "link": of "link":
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}: addExternalFileToLink(conf, arg) if pass in {passCmd2, passPP}:
addExternalFileToLink(conf, AbsoluteFile arg)
of "debuginfo": of "debuginfo":
expectNoArg(conf, switch, arg, pass, info) expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optCDebug) incl(conf.globalOptions, optCDebug)
@ -581,7 +580,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
if pass in {passCmd2, passPP}: conf.cLibs.add processPath(conf, arg, info) if pass in {passCmd2, passPP}: conf.cLibs.add processPath(conf, arg, info)
of "clib": of "clib":
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}: conf.cLinkedLibs.add processPath(conf, arg, info) if pass in {passCmd2, passPP}:
conf.cLinkedLibs.add processPath(conf, arg, info).string
of "header": of "header":
if conf != nil: conf.headerFile = arg if conf != nil: conf.headerFile = arg
incl(conf.globalOptions, optGenIndex) incl(conf.globalOptions, optGenIndex)
@ -742,7 +742,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg) if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg)
else: invalidCmdLineOption(conf, pass, switch, info) else: invalidCmdLineOption(conf, pass, switch, info)
template gCmdLineInfo*(): untyped = newLineInfo(config, "command line", 1, 1) template gCmdLineInfo*(): untyped = newLineInfo(config, AbsoluteFile"command line", 1, 1)
proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) = proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) =
var cmd, arg: string var cmd, arg: string

View file

@ -10,7 +10,8 @@
# This module implements a dependency file generator. # This module implements a dependency file generator.
import import
os, options, ast, astalgo, msgs, ropes, idents, passes, modulepaths os, options, ast, astalgo, msgs, ropes, idents, passes, modulepaths,
pathutils
from modulegraphs import ModuleGraph from modulegraphs import ModuleGraph
@ -45,10 +46,10 @@ proc addDotDependency(c: PPassContext, n: PNode): PNode =
else: else:
discard discard
proc generateDot*(graph: ModuleGraph; project: string) = proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) =
let b = Backend(graph.backend) let b = Backend(graph.backend)
discard writeRope("digraph $1 {$n$2}$n" % [ discard writeRope("digraph $1 {$n$2}$n" % [
rope(changeFileExt(extractFilename(project), "")), b.dotGraph], rope(project.splitFile.name), b.dotGraph],
changeFileExt(project, "dot")) changeFileExt(project, "dot"))
proc myOpen(graph: ModuleGraph; module: PSym): PPassContext = proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =

View file

@ -16,7 +16,8 @@ import
wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast, wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast,
packages/docutils/rst, packages/docutils/rstgen, packages/docutils/rst, packages/docutils/rstgen,
packages/docutils/highlite, sempass2, json, xmltree, cgi, packages/docutils/highlite, sempass2, json, xmltree, cgi,
typesrenderer, astalgo, modulepaths, lineinfos, sequtils, intsets typesrenderer, astalgo, modulepaths, lineinfos, sequtils, intsets,
pathutils
type type
TSections = array[TSymKind, Rope] TSections = array[TSymKind, Rope]
@ -34,6 +35,8 @@ type
exampleCounter: int exampleCounter: int
emitted: IntSet # we need to track which symbols have been emitted emitted: IntSet # we need to track which symbols have been emitted
# already. See bug #3655 # already. See bug #3655
destFile*: AbsoluteFile
thisDir*: AbsoluteDir
PDoc* = ref TDocumentor ## Alias to type less. PDoc* = ref TDocumentor ## Alias to type less.
@ -48,12 +51,12 @@ proc whichType(d: PDoc; n: PNode): PSym =
proc attachToType(d: PDoc; p: PSym): PSym = proc attachToType(d: PDoc; p: PSym): PSym =
let params = p.ast.sons[paramsPos] let params = p.ast.sons[paramsPos]
# first check the first parameter, then the return type,
# then the other parameter:
template check(i) = template check(i) =
result = whichType(d, params[i]) result = whichType(d, params[i])
if result != nil: return result if result != nil: return result
# first check the first parameter, then the return type,
# then the other parameter:
if params.len > 1: check(1) if params.len > 1: check(1)
if params.len > 0: check(0) if params.len > 0: check(0)
for i in 2..<params.len: check(i) for i in 2..<params.len: check(i)
@ -74,10 +77,10 @@ template declareClosures =
of mwUnknownSubstitution: k = warnUnknownSubstitutionX of mwUnknownSubstitution: k = warnUnknownSubstitutionX
of mwUnsupportedLanguage: k = warnLanguageXNotSupported of mwUnsupportedLanguage: k = warnLanguageXNotSupported
of mwUnsupportedField: k = warnFieldXNotSupported of mwUnsupportedField: k = warnFieldXNotSupported
globalError(conf, newLineInfo(conf, filename, line, col), k, arg) globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg)
proc docgenFindFile(s: string): string {.procvar.} = proc docgenFindFile(s: string): string {.procvar.} =
result = options.findFile(conf, s) result = options.findFile(conf, s).string
if result.len == 0: if result.len == 0:
result = getCurrentDir() / s result = getCurrentDir() / s
if not existsFile(result): result = "" if not existsFile(result): result = ""
@ -90,13 +93,24 @@ proc parseRst(text, filename: string,
result = rstParse(text, filename, line, column, hasToc, rstOptions, result = rstParse(text, filename, line, column, hasToc, rstOptions,
docgenFindFile, compilerMsgHandler) docgenFindFile, compilerMsgHandler)
proc newDocumentor*(filename: string; cache: IdentCache; conf: ConfigRef): PDoc = proc getOutFile2(conf: ConfigRef; filename: RelativeFile,
ext: string, dir: RelativeDir): AbsoluteFile =
if optWholeProject in conf.globalOptions:
# This is correct, for 'nim doc --project' we interpret the '--out' option as an
# absolute directory, not as a filename!
let d = if conf.outFile.isEmpty: conf.projectPath / dir else: AbsoluteDir(conf.outFile)
createDir(d)
result = d / changeFileExt(filename, ext)
else:
result = getOutFile(conf, filename, ext)
proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef): PDoc =
declareClosures() declareClosures()
new(result) new(result)
result.conf = conf result.conf = conf
result.cache = cache result.cache = cache
initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex), initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex),
conf.configVars, filename, {roSupportRawDirective}, conf.configVars, filename.string, {roSupportRawDirective},
docgenFindFile, compilerMsgHandler) docgenFindFile, compilerMsgHandler)
if conf.configVars.hasKey("doc.googleAnalytics"): if conf.configVars.hasKey("doc.googleAnalytics"):
@ -120,8 +134,12 @@ proc newDocumentor*(filename: string; cache: IdentCache; conf: ConfigRef): PDoc
result.jArray = newJArray() result.jArray = newJArray()
initStrTable result.types initStrTable result.types
result.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string; status: int; content: string) = result.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string; status: int; content: string) =
localError(conf, newLineInfo(conf, d.filename, -1, -1), warnUser, "only 'rst2html' supports the ':test:' attribute") localError(conf, newLineInfo(conf, AbsoluteFile d.filename, -1, -1),
warnUser, "only 'rst2html' supports the ':test:' attribute")
result.emitted = initIntSet() result.emitted = initIntSet()
result.destFile = getOutFile2(conf, relativeTo(filename, conf.projectPath),
HtmlExt, RelativeDir"htmldocs")
result.thisDir = result.destFile.splitFile.dir
proc dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) = proc dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) =
if conf.cmd != cmdRst2tex: addf(dest, xml, args) if conf.cmd != cmdRst2tex: addf(dest, xml, args)
@ -227,6 +245,10 @@ proc getPlainDocstring(n: PNode): string =
result = getPlainDocstring(n.sons[i]) result = getPlainDocstring(n.sons[i])
if result.len > 0: return if result.len > 0: return
proc belongsToPackage(conf: ConfigRef; module: PSym): bool =
result = module.kind == skModule and module.owner != nil and
module.owner.id == conf.mainPackageId
proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRenderFlags = {}) = proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRenderFlags = {}) =
var r: TSrcGen var r: TSrcGen
var literal = "" var literal = ""
@ -259,8 +281,22 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
dispA(d.conf, result, "<span class=\"FloatNumber\">$1</span>", dispA(d.conf, result, "<span class=\"FloatNumber\">$1</span>",
"\\spanFloatNumber{$1}", [rope(esc(d.target, literal))]) "\\spanFloatNumber{$1}", [rope(esc(d.target, literal))])
of tkSymbol: of tkSymbol:
dispA(d.conf, result, "<span class=\"Identifier\">$1</span>", let s = getTokSym(r)
"\\spanIdentifier{$1}", [rope(esc(d.target, literal))]) if s != nil and s.kind == skType and sfExported in s.flags and
s.owner != nil and belongsToPackage(d.conf, s.owner) and
d.target == outHtml:
let full = AbsoluteFile toFullPath(d.conf, FileIndex s.owner.position)
let tmp = getOutFile2(d.conf, full.relativeTo(d.conf.projectPath),
HtmlExt, RelativeDir"htmldocs")
let external = tmp.relativeTo(d.thisDir, '/')
result.addf "<a href=\"$1#$2\"><span class=\"Identifier\">$3</span></a>",
[rope changeFileExt(external, "html").string, rope literal,
rope(esc(d.target, literal))]
else:
dispA(d.conf, result, "<span class=\"Identifier\">$1</span>",
"\\spanIdentifier{$1}", [rope(esc(d.target, literal))])
of tkSpaces, tkInvalid: of tkSpaces, tkInvalid:
add(result, literal) add(result, literal)
of tkCurlyDotLe: of tkCurlyDotLe:
@ -290,23 +326,24 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
proc testExample(d: PDoc; ex: PNode) = proc testExample(d: PDoc; ex: PNode) =
if d.conf.errorCounter > 0: return if d.conf.errorCounter > 0: return
let outputDir = d.conf.getNimcacheDir / "runnableExamples" let outputDir = d.conf.getNimcacheDir / RelativeDir"runnableExamples"
createDir(outputDir) createDir(outputDir)
inc d.exampleCounter inc d.exampleCounter
let outp = outputDir / extractFilename(d.filename.changeFileExt"" & let outp = outputDir / RelativeFile(extractFilename(d.filename.changeFileExt"" &
"_examples" & $d.exampleCounter & ".nim") "_examples" & $d.exampleCounter & ".nim"))
#let nimcache = outp.changeFileExt"" & "_nimcache" #let nimcache = outp.changeFileExt"" & "_nimcache"
renderModule(ex, d.filename, outp, conf = d.conf) renderModule(ex, d.filename, outp.string, conf = d.conf)
let backend = if isDefined(d.conf, "js"): "js" let backend = if isDefined(d.conf, "js"): "js"
elif isDefined(d.conf, "cpp"): "cpp" elif isDefined(d.conf, "cpp"): "cpp"
elif isDefined(d.conf, "objc"): "objc" elif isDefined(d.conf, "objc"): "objc"
else: "c" else: "c"
if os.execShellCmd(os.getAppFilename() & " " & backend & if os.execShellCmd(os.getAppFilename() & " " & backend &
" --nimcache:" & outputDir & " -r " & outp) != 0: " --nimcache:" & quoteShell(outputDir) &
quit "[Examples] failed: see " & outp " -r " & quoteShell(outp)) != 0:
quit "[Examples] failed: see " & outp.string
else: else:
# keep generated source file `outp` to allow inspection. # keep generated source file `outp` to allow inspection.
rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp]) rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string])
removeFile(outp.changeFileExt(ExeExt)) removeFile(outp.changeFileExt(ExeExt))
proc extractImports(n: PNode; result: PNode) = proc extractImports(n: PNode; result: PNode) =
@ -449,10 +486,8 @@ proc newUniquePlainSymbol(d: PDoc, original: string): string =
result = original result = original
d.seenSymbols[original] = "" d.seenSymbols[original] = ""
return return
# Iterate over possible numeric variants of the original name. # Iterate over possible numeric variants of the original name.
var count = 2 var count = 2
while true: while true:
result = original & "_" & $count result = original & "_" & $count
if not d.seenSymbols.hasKey(result): if not d.seenSymbols.hasKey(result):
@ -460,7 +495,6 @@ proc newUniquePlainSymbol(d: PDoc, original: string): string =
break break
count += 1 count += 1
proc complexName(k: TSymKind, n: PNode, baseName: string): string = proc complexName(k: TSymKind, n: PNode, baseName: string): string =
## Builds a complex unique href name for the node. ## Builds a complex unique href name for the node.
## ##
@ -482,11 +516,9 @@ proc complexName(k: TSymKind, n: PNode, baseName: string): string =
of skTemplate: result.add(".t" & defaultParamSeparator) of skTemplate: result.add(".t" & defaultParamSeparator)
of skConverter: result.add(".c" & defaultParamSeparator) of skConverter: result.add(".c" & defaultParamSeparator)
else: discard else: discard
if len(n) > paramsPos and n[paramsPos].kind == nkFormalParams: if len(n) > paramsPos and n[paramsPos].kind == nkFormalParams:
result.add(renderParamTypes(n[paramsPos])) result.add(renderParamTypes(n[paramsPos]))
proc isCallable(n: PNode): bool = proc isCallable(n: PNode): bool =
## Returns true if `n` contains a callable node. ## Returns true if `n` contains a callable node.
case n.kind case n.kind
@ -495,7 +527,6 @@ proc isCallable(n: PNode): bool =
else: else:
result = false result = false
proc docstringSummary(rstText: string): string = proc docstringSummary(rstText: string): string =
## Returns just the first line or a brief chunk of text from a rst string. ## Returns just the first line or a brief chunk of text from a rst string.
## ##
@ -523,7 +554,6 @@ proc docstringSummary(rstText: string): string =
result.delete(pos, last) result.delete(pos, last)
result.add("…") result.add("…")
proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
if not isVisible(d, nameNode): return if not isVisible(d, nameNode): return
let let
@ -545,8 +575,8 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
break break
plainName.add(literal) plainName.add(literal)
# Render the HTML hyperlink. nodeToHighlightedHtml(d, n, result, {renderNoBody, renderNoComments,
nodeToHighlightedHtml(d, n, result, {renderNoBody, renderNoComments, renderDocComments}) renderDocComments, renderSyms})
inc(d.id) inc(d.id)
let let
@ -563,16 +593,18 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
var seeSrcRope: Rope = nil var seeSrcRope: Rope = nil
let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc") let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc")
if docItemSeeSrc.len > 0: if docItemSeeSrc.len > 0:
let cwd = canonicalizePath(d.conf, getCurrentDir()) let path = relativeTo(AbsoluteFile toFullPath(d.conf, n.info), d.conf.projectPath, '/')
var path = toFullPath(d.conf, n.info) when false:
if path.startsWith(cwd): let cwd = canonicalizePath(d.conf, getCurrentDir())
path = path[cwd.len+1 .. ^1].replace('\\', '/') var path = toFullPath(d.conf, n.info)
if path.startsWith(cwd):
path = path[cwd.len+1 .. ^1].replace('\\', '/')
let gitUrl = getConfigVar(d.conf, "git.url") let gitUrl = getConfigVar(d.conf, "git.url")
if gitUrl.len > 0: if gitUrl.len > 0:
let commit = getConfigVar(d.conf, "git.commit", "master") let commit = getConfigVar(d.conf, "git.commit", "master")
let develBranch = getConfigVar(d.conf, "git.devel", "devel") let develBranch = getConfigVar(d.conf, "git.devel", "devel")
dispA(d.conf, seeSrcRope, "$1", "", [ropeFormatNamedVars(d.conf, docItemSeeSrc, dispA(d.conf, seeSrcRope, "$1", "", [ropeFormatNamedVars(d.conf, docItemSeeSrc,
["path", "line", "url", "commit", "devel"], [rope path, ["path", "line", "url", "commit", "devel"], [rope path.string,
rope($n.info.line), rope gitUrl, rope commit, rope develBranch])]) rope($n.info.line), rope gitUrl, rope commit, rope develBranch])])
add(d.section[k], ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item"), add(d.section[k], ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item"),
@ -611,9 +643,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
name = getName(d, nameNode) name = getName(d, nameNode)
comm = $genRecComment(d, n) comm = $genRecComment(d, n)
r: TSrcGen r: TSrcGen
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
result = %{ "name": %name, "type": %($k), "line": %n.info.line.int, result = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
"col": %n.info.col} "col": %n.info.col}
if comm.len > 0: if comm.len > 0:
@ -626,7 +656,6 @@ proc checkForFalse(n: PNode): bool =
proc traceDeps(d: PDoc, it: PNode) = proc traceDeps(d: PDoc, it: PNode) =
const k = skModule const k = skModule
if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket: if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
let sep = it[0] let sep = it[0]
let dir = it[1] let dir = it[1]
@ -637,11 +666,16 @@ proc traceDeps(d: PDoc, it: PNode) =
for x in it[2]: for x in it[2]:
a.sons[2] = x a.sons[2] = x
traceDeps(d, a) traceDeps(d, a)
else: elif it.kind == nkSym and belongsToPackage(d.conf, it.sym):
let full = AbsoluteFile toFullPath(d.conf, FileIndex it.sym.position)
let tmp = getOutFile2(d.conf, full.relativeTo(d.conf.projectPath), HtmlExt,
RelativeDir"htmldocs")
let external = relativeTo(tmp, d.thisDir, '/')
if d.section[k] != nil: add(d.section[k], ", ") if d.section[k] != nil: add(d.section[k], ", ")
dispA(d.conf, d.section[k], dispA(d.conf, d.section[k],
"<a class=\"reference external\" href=\"$1.html\">$1</a>", "<a class=\"reference external\" href=\"$2\">$1</a>",
"$1", [rope(splitFile(getModuleName(d.conf, it)).name)]) "$1", [rope esc(d.target, it.sym.name.s),
rope changeFileExt(external, "html").string])
proc generateDoc*(d: PDoc, n: PNode) = proc generateDoc*(d: PDoc, n: PNode) =
case n.kind case n.kind
@ -829,29 +863,23 @@ proc genOutFile(d: PDoc): Rope =
proc generateIndex*(d: PDoc) = proc generateIndex*(d: PDoc) =
if optGenIndex in d.conf.globalOptions: if optGenIndex in d.conf.globalOptions:
writeIndexFile(d[], splitFile(d.conf.outFile).dir / let dest = getOutFile2(d.conf, relativeTo(AbsoluteFile d.filename, d.conf.projectPath),
splitFile(d.filename).name & IndexExt) IndexExt, RelativeDir"index")
writeIndexFile(d[], dest.string)
proc getOutFile2(conf: ConfigRef; filename, ext, dir: string): string = proc writeOutput*(d: PDoc, useWarning = false) =
if optWholeProject in conf.globalOptions:
let d = if conf.outFile != "": conf.outFile else: dir
createDir(d)
result = d / changeFileExt(filename, ext)
else:
result = getOutFile(conf, filename, ext)
proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) =
var content = genOutFile(d) var content = genOutFile(d)
if optStdout in d.conf.globalOptions: if optStdout in d.conf.globalOptions:
writeRope(stdout, content) writeRope(stdout, content)
else: else:
let outfile = getOutFile2(d.conf, filename, outExt, "htmldocs") template outfile: untyped = d.destFile
createDir(outfile.parentDir) #let outfile = getOutFile2(d.conf, shortenDir(d.conf, filename), outExt, "htmldocs")
createDir(outfile.splitFile.dir)
if not writeRope(content, outfile): if not writeRope(content, outfile):
rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile, outfile) rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile,
outfile.string)
proc writeOutputJson*(d: PDoc, filename, outExt: string, proc writeOutputJson*(d: PDoc, useWarning = false) =
useWarning = false) =
let content = %*{"orig": d.filename, let content = %*{"orig": d.filename,
"nimble": getPackageName(d.conf, d.filename), "nimble": getPackageName(d.conf, d.filename),
"entries": d.jArray} "entries": d.jArray}
@ -859,8 +887,7 @@ proc writeOutputJson*(d: PDoc, filename, outExt: string,
write(stdout, $content) write(stdout, $content)
else: else:
var f: File var f: File
if open(f, getOutFile2(d.conf, splitFile(filename).name, if open(f, d.destFile.string, fmWrite):
outExt, "jsondocs"), fmWrite):
write(f, $content) write(f, $content)
close(f) close(f)
else: else:
@ -872,26 +899,27 @@ proc commandDoc*(cache: IdentCache, conf: ConfigRef) =
var d = newDocumentor(conf.projectFull, cache, conf) var d = newDocumentor(conf.projectFull, cache, conf)
d.hasToc = true d.hasToc = true
generateDoc(d, ast) generateDoc(d, ast)
writeOutput(d, conf.projectFull, HtmlExt) writeOutput(d)
generateIndex(d) generateIndex(d)
proc commandRstAux(cache: IdentCache, conf: ConfigRef; filename, outExt: string) = proc commandRstAux(cache: IdentCache, conf: ConfigRef;
filename: AbsoluteFile, outExt: string) =
var filen = addFileExt(filename, "txt") var filen = addFileExt(filename, "txt")
var d = newDocumentor(filen, cache, conf) var d = newDocumentor(filen, cache, conf)
d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string; d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string;
status: int; content: string) = status: int; content: string) =
var outp: string var outp: AbsoluteFile
if filename.len == 0: if filename.len == 0:
inc(d.id) inc(d.id)
let nameOnly = splitFile(d.filename).name let nameOnly = splitFile(d.filename).name
let subdir = getNimcacheDir(conf) / nameOnly let subdir = getNimcacheDir(conf) / RelativeDir(nameOnly)
createDir(subdir) createDir(subdir)
outp = subdir / (nameOnly & "_snippet_" & $d.id & ".nim") outp = subdir / RelativeFile(nameOnly & "_snippet_" & $d.id & ".nim")
elif isAbsolute(filename): elif isAbsolute(filename):
outp = filename outp = AbsoluteFile filename
else: else:
# Nim's convention: every path is relative to the file it was written in: # Nim's convention: every path is relative to the file it was written in:
outp = splitFile(d.filename).dir / filename outp = splitFile(d.filename).dir.AbsoluteDir / RelativeFile(filename)
writeFile(outp, content) writeFile(outp, content)
let cmd = cmd % quoteShell(outp) let cmd = cmd % quoteShell(outp)
rawMessage(conf, hintExecuting, cmd) rawMessage(conf, hintExecuting, cmd)
@ -899,14 +927,12 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef; filename, outExt: string)
rawMessage(conf, errGenerated, "executing of external program failed: " & cmd) rawMessage(conf, errGenerated, "executing of external program failed: " & cmd)
d.isPureRst = true d.isPureRst = true
var rst = parseRst(readFile(filen), filen, 0, 1, d.hasToc, var rst = parseRst(readFile(filen.string), filen.string, 0, 1, d.hasToc,
{roSupportRawDirective}, conf) {roSupportRawDirective}, conf)
var modDesc = newStringOfCap(30_000) var modDesc = newStringOfCap(30_000)
#d.modDesc = newMutableRope(30_000)
renderRstToOut(d[], rst, modDesc) renderRstToOut(d[], rst, modDesc)
#freezeMutableRope(d.modDesc)
d.modDesc = rope(modDesc) d.modDesc = rope(modDesc)
writeOutput(d, filename, outExt) writeOutput(d)
generateIndex(d) generateIndex(d)
proc commandRst2Html*(cache: IdentCache, conf: ConfigRef) = proc commandRst2Html*(cache: IdentCache, conf: ConfigRef) =
@ -928,9 +954,9 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) =
writeRope(stdout, content) writeRope(stdout, content)
else: else:
#echo getOutFile(gProjectFull, JsonExt) #echo getOutFile(gProjectFull, JsonExt)
let filename = getOutFile(conf, conf.projectFull, JsonExt) let filename = getOutFile(conf, RelativeFile conf.projectName, JsonExt)
if not writeRope(content, filename): if not writeRope(content, filename):
rawMessage(conf, errCannotOpenFile, filename) rawMessage(conf, errCannotOpenFile, filename.string)
proc commandTags*(cache: IdentCache, conf: ConfigRef) = proc commandTags*(cache: IdentCache, conf: ConfigRef) =
var ast = parseFile(conf.projectMainIdx, cache, conf) var ast = parseFile(conf.projectMainIdx, cache, conf)
@ -945,12 +971,12 @@ proc commandTags*(cache: IdentCache, conf: ConfigRef) =
writeRope(stdout, content) writeRope(stdout, content)
else: else:
#echo getOutFile(gProjectFull, TagsExt) #echo getOutFile(gProjectFull, TagsExt)
let filename = getOutFile(conf, conf.projectFull, TagsExt) let filename = getOutFile(conf, RelativeFile conf.projectName, TagsExt)
if not writeRope(content, filename): if not writeRope(content, filename):
rawMessage(conf, errCannotOpenFile, filename) rawMessage(conf, errCannotOpenFile, filename.string)
proc commandBuildIndex*(cache: IdentCache, conf: ConfigRef) = proc commandBuildIndex*(cache: IdentCache, conf: ConfigRef) =
var content = mergeIndexes(conf.projectFull).rope var content = mergeIndexes(conf.projectFull.string).rope
let code = ropeFormatNamedVars(conf, getConfigVar(conf, "doc.file"), ["title", let code = ropeFormatNamedVars(conf, getConfigVar(conf, "doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time", "tableofcontents", "moduledesc", "date", "time",
@ -958,6 +984,6 @@ proc commandBuildIndex*(cache: IdentCache, conf: ConfigRef) =
["Index".rope, nil, nil, rope(getDateStr()), ["Index".rope, nil, nil, rope(getDateStr()),
rope(getClockStr()), content, nil, nil, nil]) rope(getClockStr()), content, nil, nil, nil])
# no analytics because context is not available # no analytics because context is not available
let filename = getOutFile(conf, "theindex", HtmlExt) let filename = getOutFile(conf, RelativeFile"theindex", HtmlExt)
if not writeRope(code, filename): if not writeRope(code, filename):
rawMessage(conf, errCannotOpenFile, filename) rawMessage(conf, errCannotOpenFile, filename.string)

View file

@ -11,7 +11,8 @@
# semantic checking. # semantic checking.
import import
os, options, ast, astalgo, msgs, ropes, idents, passes, docgen, lineinfos os, options, ast, astalgo, msgs, ropes, idents, passes, docgen, lineinfos,
pathutils
from modulegraphs import ModuleGraph from modulegraphs import ModuleGraph
@ -38,11 +39,11 @@ template closeImpl(body: untyped) {.dirty.} =
proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
closeImpl: closeImpl:
writeOutput(g.doc, toFullPath(graph.config, FileIndex g.module.position), HtmlExt, useWarning) writeOutput(g.doc, useWarning)
proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
closeImpl: closeImpl:
writeOutputJson(g.doc, toFullPath(graph.config, FileIndex g.module.position), ".json", useWarning) writeOutputJson(g.doc, useWarning)
proc processNode(c: PPassContext, n: PNode): PNode = proc processNode(c: PPassContext, n: PNode): PNode =
result = n result = n
@ -60,7 +61,8 @@ proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
var g: PGen var g: PGen
new(g) new(g)
g.module = module g.module = module
var d = newDocumentor(toFullPath(graph.config, FileIndex module.position), graph.cache, graph.config) var d = newDocumentor(AbsoluteFile toFullPath(graph.config, FileIndex module.position),
graph.cache, graph.config)
d.hasToc = true d.hasToc = true
g.doc = d g.doc = d
result = g result = g

View file

@ -14,7 +14,7 @@
import import
ropes, os, strutils, osproc, platform, condsyms, options, msgs, ropes, os, strutils, osproc, platform, condsyms, options, msgs,
lineinfos, std / sha1, streams lineinfos, std / sha1, streams, pathutils
type type
TInfoCCProp* = enum # properties of the C compiler: TInfoCCProp* = enum # properties of the C compiler:
@ -429,12 +429,13 @@ proc initVars*(conf: ConfigRef) =
if len(conf.ccompilerpath) == 0: if len(conf.ccompilerpath) == 0:
conf.ccompilerpath = getConfigVar(conf, conf.cCompiler, ".path") conf.ccompilerpath = getConfigVar(conf, conf.cCompiler, ".path")
proc completeCFilePath*(conf: ConfigRef; cfile: string, createSubDir: bool = true): string = proc completeCFilePath*(conf: ConfigRef; cfile: AbsoluteFile,
createSubDir: bool = true): AbsoluteFile =
result = completeGeneratedFilePath(conf, cfile, createSubDir) result = completeGeneratedFilePath(conf, cfile, createSubDir)
proc toObjFile*(conf: ConfigRef; filename: string): string = proc toObjFile*(conf: ConfigRef; filename: AbsoluteFile): AbsoluteFile =
# Object file for compilation # Object file for compilation
result = filename & "." & CC[conf.cCompiler].objExt result = AbsoluteFile(filename.string & "." & CC[conf.cCompiler].objExt)
proc addFileToCompile*(conf: ConfigRef; cf: Cfile) = proc addFileToCompile*(conf: ConfigRef; cf: Cfile) =
conf.toCompile.add(cf) conf.toCompile.add(cf)
@ -447,8 +448,8 @@ proc resetCompilationLists*(conf: ConfigRef) =
# Maybe we can do that in checkDep on the other hand? # Maybe we can do that in checkDep on the other hand?
conf.externalToLink.setLen 0 conf.externalToLink.setLen 0
proc addExternalFileToLink*(conf: ConfigRef; filename: string) = proc addExternalFileToLink*(conf: ConfigRef; filename: AbsoluteFile) =
conf.externalToLink.insert(filename, 0) conf.externalToLink.insert(filename.string, 0)
proc execWithEcho(conf: ConfigRef; cmd: string, msg = hintExecuting): int = proc execWithEcho(conf: ConfigRef; cmd: string, msg = hintExecuting): int =
rawMessage(conf, msg, cmd) rawMessage(conf, msg, cmd)
@ -459,14 +460,15 @@ proc execExternalProgram*(conf: ConfigRef; cmd: string, msg = hintExecuting) =
rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" % rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
cmd) cmd)
proc generateScript(conf: ConfigRef; projectFile: string, script: Rope) = proc generateScript(conf: ConfigRef; projectFile: AbsoluteFile, script: Rope) =
let (dir, name, ext) = splitFile(projectFile) let (_, name, _) = splitFile(projectFile)
let filename = getNimcacheDir(conf) / addFileExt("compile_" & name, let filename = getNimcacheDir(conf) / RelativeFile(addFileExt("compile_" & name,
platform.OS[conf.target.targetOS].scriptExt) platform.OS[conf.target.targetOS].scriptExt))
if writeRope(script, filename): if writeRope(script, filename):
copyFile(conf.libpath / "nimbase.h", getNimcacheDir(conf) / "nimbase.h") copyFile(conf.libpath / RelativeFile"nimbase.h",
getNimcacheDir(conf) / RelativeFile"nimbase.h")
else: else:
rawMessage(conf, errGenerated, "could not write to file: " & filename) rawMessage(conf, errGenerated, "could not write to file: " & filename.string)
proc getOptSpeed(conf: ConfigRef; c: TSystemCC): string = proc getOptSpeed(conf: ConfigRef; c: TSystemCC): string =
result = getConfigVar(conf, c, ".options.speed") result = getConfigVar(conf, c, ".options.speed")
@ -490,7 +492,7 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
# `optGenMapping` is included here for niminst. # `optGenMapping` is included here for niminst.
result = conf.globalOptions * {optGenScript, optGenMapping} != {} result = conf.globalOptions * {optGenScript, optGenMapping} != {}
proc cFileSpecificOptions(conf: ConfigRef; cfilename: string): string = proc cFileSpecificOptions(conf: ConfigRef; cfilename: AbsoluteFile): string =
result = conf.compileOptions result = conf.compileOptions
for option in conf.compileOptionsCmd: for option in conf.compileOptionsCmd:
if strutils.find(result, option, 0) < 0: if strutils.find(result, option, 0) < 0:
@ -513,7 +515,7 @@ proc cFileSpecificOptions(conf: ConfigRef; cfilename: string): string =
if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key)) if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
proc getCompileOptions(conf: ConfigRef): string = proc getCompileOptions(conf: ConfigRef): string =
result = cFileSpecificOptions(conf, "__dummy__") result = cFileSpecificOptions(conf, AbsoluteFile"__dummy__")
proc getLinkOptions(conf: ConfigRef): string = proc getLinkOptions(conf: ConfigRef): string =
result = conf.linkOptions & " " & conf.linkOptionsCmd & " " result = conf.linkOptions & " " & conf.linkOptionsCmd & " "
@ -526,8 +528,8 @@ proc needsExeExt(conf: ConfigRef): bool {.inline.} =
result = (optGenScript in conf.globalOptions and conf.target.targetOS == osWindows) or result = (optGenScript in conf.globalOptions and conf.target.targetOS == osWindows) or
(conf.target.hostOS == osWindows) (conf.target.hostOS == osWindows)
proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string = proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: AbsoluteFile): string =
result = if conf.cmd == cmdCompileToCpp and not cfile.endsWith(".c"): result = if conf.cmd == cmdCompileToCpp and not cfile.string.endsWith(".c"):
CC[compiler].cppCompiler CC[compiler].cppCompiler
else: else:
CC[compiler].compilerExe CC[compiler].compilerExe
@ -539,7 +541,7 @@ proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string
proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string = proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string =
result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
elif optMixedMode in conf.globalOptions and conf.cmd != cmdCompileToCpp: CC[compiler].cppCompiler elif optMixedMode in conf.globalOptions and conf.cmd != cmdCompileToCpp: CC[compiler].cppCompiler
else: getCompilerExe(conf, compiler, "") else: getCompilerExe(conf, compiler, AbsoluteFile"")
proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string = proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
var c = conf.cCompiler var c = conf.cCompiler
@ -565,43 +567,42 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
includeCmd = "" includeCmd = ""
compilePattern = getCompilerExe(conf, c, cfile.cname) compilePattern = getCompilerExe(conf, c, cfile.cname)
var cf = if noAbsolutePaths(conf): extractFilename(cfile.cname) var cf = if noAbsolutePaths(conf): AbsoluteFile extractFilename(cfile.cname.string)
else: cfile.cname else: cfile.cname
var objfile = var objfile =
if cfile.obj.len == 0: if cfile.obj.isEmpty:
if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths(conf): if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths(conf):
toObjFile(conf, cf) toObjFile(conf, cf).string
else: else:
completeCFilePath(conf, toObjFile(conf, cf)) completeCFilePath(conf, toObjFile(conf, cf)).string
elif noAbsolutePaths(conf): elif noAbsolutePaths(conf):
extractFilename(cfile.obj) extractFilename(cfile.obj.string)
else: else:
cfile.obj cfile.obj.string
# D files are required by nintendo switch libs for # D files are required by nintendo switch libs for
# compilation. They are basically a list of all includes. # compilation. They are basically a list of all includes.
let dfile = objfile.changeFileExt(".d").quoteShell() let dfile = objfile.changeFileExt(".d").quoteShell()
objfile = quoteShell(objfile) objfile = quoteShell(objfile)
cf = quoteShell(cf) let cfsh = quoteShell(cf)
result = quoteShell(compilePattern % [ result = quoteShell(compilePattern % [
"dfile", dfile, "dfile", dfile,
"file", cf, "objfile", objfile, "options", options, "file", cfsh, "objfile", objfile, "options", options,
"include", includeCmd, "nim", getPrefixDir(conf), "include", includeCmd, "nim", getPrefixDir(conf).string,
"nim", getPrefixDir(conf), "lib", conf.libpath]) "lib", conf.libpath.string])
add(result, ' ') add(result, ' ')
addf(result, CC[c].compileTmpl, [ addf(result, CC[c].compileTmpl, [
"dfile", dfile, "dfile", dfile,
"file", cf, "objfile", objfile, "file", cfsh, "objfile", objfile,
"options", options, "include", includeCmd, "options", options, "include", includeCmd,
"nim", quoteShell(getPrefixDir(conf)), "nim", quoteShell(getPrefixDir(conf)),
"nim", quoteShell(getPrefixDir(conf)),
"lib", quoteShell(conf.libpath)]) "lib", quoteShell(conf.libpath)])
proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash = proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash =
result = secureHash( result = secureHash(
$secureHashFile(cfile.cname) & $secureHashFile(cfile.cname.string) &
platform.OS[conf.target.targetOS].name & platform.OS[conf.target.targetOS].name &
platform.CPU[conf.target.targetCPU].name & platform.CPU[conf.target.targetCPU].name &
extccomp.CC[conf.cCompiler].name & extccomp.CC[conf.cCompiler].name &
@ -614,14 +615,14 @@ proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
var hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1") var hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1")
var currentHash = footprint(conf, cfile) var currentHash = footprint(conf, cfile)
var f: File var f: File
if open(f, hashFile, fmRead): if open(f, hashFile.string, fmRead):
let oldHash = parseSecureHash(f.readLine()) let oldHash = parseSecureHash(f.readLine())
close(f) close(f)
result = oldHash != currentHash result = oldHash != currentHash
else: else:
result = true result = true
if result: if result:
if open(f, hashFile, fmWrite): if open(f, hashFile.string, fmWrite):
f.writeLine($currentHash) f.writeLine($currentHash)
close(f) close(f)
@ -630,7 +631,7 @@ proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) =
c.flags.incl CfileFlag.Cached c.flags.incl CfileFlag.Cached
conf.toCompile.add(c) conf.toCompile.add(c)
proc addExternalFileToCompile*(conf: ConfigRef; filename: string) = proc addExternalFileToCompile*(conf: ConfigRef; filename: AbsoluteFile) =
var c = Cfile(cname: filename, var c = Cfile(cname: filename,
obj: toObjFile(conf, completeCFilePath(conf, filename, false)), obj: toObjFile(conf, completeCFilePath(conf, filename, false)),
flags: {CfileFlag.External}) flags: {CfileFlag.External})
@ -650,11 +651,11 @@ proc compileCFile(conf: ConfigRef; list: CFileList, script: var Rope, cmds: var
add(script, compileCmd) add(script, compileCmd)
add(script, "\n") add(script, "\n")
proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string = proc getLinkCmd(conf: ConfigRef; projectfile: AbsoluteFile, objfiles: string): string =
if optGenStaticLib in conf.globalOptions: if optGenStaticLib in conf.globalOptions:
var libname: string var libname: string
if conf.outFile.len > 0: if not conf.outFile.isEmpty:
libname = conf.outFile.expandTilde libname = conf.outFile.string.expandTilde
if not libname.isAbsolute(): if not libname.isAbsolute():
libname = getCurrentDir() / libname libname = getCurrentDir() / libname
else: else:
@ -679,13 +680,13 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
else: else:
exefile = splitFile(projectfile).name & platform.OS[conf.target.targetOS].exeExt exefile = splitFile(projectfile).name & platform.OS[conf.target.targetOS].exeExt
builddll = "" builddll = ""
if conf.outFile.len > 0: if not conf.outFile.isEmpty:
exefile = conf.outFile.expandTilde exefile = conf.outFile.string.expandTilde
if not exefile.isAbsolute(): if not exefile.isAbsolute():
exefile = getCurrentDir() / exefile exefile = getCurrentDir() / exefile
if not noAbsolutePaths(conf): if not noAbsolutePaths(conf):
if not exefile.isAbsolute(): if not exefile.isAbsolute():
exefile = joinPath(splitFile(projectfile).dir, exefile) exefile = string(splitFile(projectfile).dir / RelativeFile(exefile))
when false: when false:
if optCDebug in conf.globalOptions: if optCDebug in conf.globalOptions:
writeDebugInfo(exefile.changeFileExt("ndb")) writeDebugInfo(exefile.changeFileExt("ndb"))
@ -693,7 +694,7 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
# Map files are required by Nintendo Switch compilation. They are a list # Map files are required by Nintendo Switch compilation. They are a list
# of all function calls in the library and where they come from. # of all function calls in the library and where they come from.
let mapfile = quoteShell(getNimcacheDir(conf) / splitFile(projectFile).name & ".map") let mapfile = quoteShell(getNimcacheDir(conf) / RelativeFile(splitFile(projectFile).name & ".map"))
let linkOptions = getLinkOptions(conf) & " " & let linkOptions = getLinkOptions(conf) & " " &
getConfigVar(conf, conf.cCompiler, ".options.linker") getConfigVar(conf, conf.cCompiler, ".options.linker")
@ -703,7 +704,7 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
result = quoteShell(result % ["builddll", builddll, result = quoteShell(result % ["builddll", builddll,
"mapfile", mapfile, "mapfile", mapfile,
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
"exefile", exefile, "nim", getPrefixDir(conf), "lib", conf.libpath]) "exefile", exefile, "nim", getPrefixDir(conf).string, "lib", conf.libpath.string])
result.add ' ' result.add ' '
addf(result, linkTmpl, ["builddll", builddll, addf(result, linkTmpl, ["builddll", builddll,
"mapfile", mapfile, "mapfile", mapfile,
@ -761,7 +762,7 @@ proc execCmdsInParallel(conf: ConfigRef; cmds: seq[string]; prettyCb: proc (idx:
rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" % rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
cmds.join()) cmds.join())
proc callCCompiler*(conf: ConfigRef; projectfile: string) = proc callCCompiler*(conf: ConfigRef; projectfile: AbsoluteFile) =
var var
linkCmd: string linkCmd: string
if conf.globalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}: if conf.globalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
@ -787,7 +788,7 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) =
add(objfiles, quoteShell( add(objfiles, quoteShell(
addFileExt(objFile, CC[conf.cCompiler].objExt))) addFileExt(objFile, CC[conf.cCompiler].objExt)))
for x in conf.toCompile: for x in conf.toCompile:
let objFile = if noAbsolutePaths(conf): x.obj.extractFilename else: x.obj let objFile = if noAbsolutePaths(conf): x.obj.extractFilename else: x.obj.string
add(objfiles, ' ') add(objfiles, ' ')
add(objfiles, quoteShell(objFile)) add(objfiles, quoteShell(objFile))
@ -804,7 +805,7 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) =
#from json import escapeJson #from json import escapeJson
import json import json
proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) = proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: AbsoluteFile) =
template lit(x: untyped) = f.write x template lit(x: untyped) = f.write x
template str(x: untyped) = template str(x: untyped) =
when compiles(escapeJson(x, buf)): when compiles(escapeJson(x, buf)):
@ -821,7 +822,7 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
let compileCmd = getCompileCFileCmd(conf, it) let compileCmd = getCompileCFileCmd(conf, it)
if pastStart: lit "],\L" if pastStart: lit "],\L"
lit "[" lit "["
str it.cname str it.cname.string
lit ", " lit ", "
str compileCmd str compileCmd
pastStart = true pastStart = true
@ -851,11 +852,10 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
var buf = newStringOfCap(50) var buf = newStringOfCap(50)
let file = projectfile.splitFile.name let jsonFile = toGeneratedFile(conf, projectfile, "json")
let jsonFile = toGeneratedFile(conf, file, "json")
var f: File var f: File
if open(f, jsonFile, fmWrite): if open(f, jsonFile.string, fmWrite):
lit "{\"compile\":[\L" lit "{\"compile\":[\L"
cfiles(conf, f, buf, conf.toCompile, false) cfiles(conf, f, buf, conf.toCompile, false)
lit "],\L\"link\":[\L" lit "],\L\"link\":[\L"
@ -868,11 +868,10 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
lit "\L}\L" lit "\L}\L"
close(f) close(f)
proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: string) = proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: AbsoluteFile) =
let file = projectfile.splitFile.name let jsonFile = toGeneratedFile(conf, projectfile, "json")
let jsonFile = toGeneratedFile(conf, file, "json")
try: try:
let data = json.parseFile(jsonFile) let data = json.parseFile(jsonFile.string)
let toCompile = data["compile"] let toCompile = data["compile"]
doAssert toCompile.kind == JArray doAssert toCompile.kind == JArray
var cmds: TStringSeq = @[] var cmds: TStringSeq = @[]
@ -896,11 +895,11 @@ proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
except: except:
when declared(echo): when declared(echo):
echo getCurrentException().getStackTrace() echo getCurrentException().getStackTrace()
quit "error evaluating JSON file: " & jsonFile quit "error evaluating JSON file: " & jsonFile.string
proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope = proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope =
for it in list: for it in list:
addf(result, "--file:r\"$1\"$N", [rope(it.cname)]) addf(result, "--file:r\"$1\"$N", [rope(it.cname.string)])
proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) = proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) =
if optGenMapping notin conf.globalOptions: return if optGenMapping notin conf.globalOptions: return
@ -914,9 +913,9 @@ proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) =
getConfigVar(conf, conf.cCompiler, ".options.linker"))) getConfigVar(conf, conf.cCompiler, ".options.linker")))
add(code, "\n[Environment]\nlibpath=") add(code, "\n[Environment]\nlibpath=")
add(code, strutils.escape(conf.libpath)) add(code, strutils.escape(conf.libpath.string))
addf(code, "\n[Symbols]$n$1", [symbolMapping]) addf(code, "\n[Symbols]$n$1", [symbolMapping])
let filename = joinPath(conf.projectPath, "mapping.txt") let filename = conf.projectPath / RelativeFile"mapping.txt"
if not writeRope(code, filename): if not writeRope(code, filename):
rawMessage(conf, errGenerated, "could not write to file: " & filename) rawMessage(conf, errGenerated, "could not write to file: " & filename.string)

View file

@ -11,7 +11,7 @@
import import
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
renderer, filters, lineinfos renderer, filters, lineinfos, pathutils
type type
TParseState = enum TParseState = enum
@ -199,7 +199,8 @@ proc parseLine(p: var TTmplParser) =
inc(j) inc(j)
llStreamWrite(p.outp, "\\n\"") llStreamWrite(p.outp, "\\n\"")
proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode; conf: ConfigRef): PLLStream = proc filterTmpl*(stdin: PLLStream, filename: AbsoluteFile,
call: PNode; conf: ConfigRef): PLLStream =
var p: TTmplParser var p: TTmplParser
p.config = conf p.config = conf
p.info = newLineInfo(conf, filename, 0, 0) p.info = newLineInfo(conf, filename, 0, 0)

View file

@ -11,7 +11,7 @@
import import
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
renderer renderer, pathutils
proc invalidPragma(conf: ConfigRef; n: PNode) = proc invalidPragma(conf: ConfigRef; n: PNode) =
localError(conf, n.info, localError(conf, n.info,
@ -47,7 +47,7 @@ proc boolArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: bool):
elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false
else: invalidPragma(conf, n) else: invalidPragma(conf, n)
proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: string, call: PNode): PLLStream = proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: AbsoluteFile, call: PNode): PLLStream =
var pattern = strArg(conf, call, "startswith", 1, "") var pattern = strArg(conf, call, "startswith", 1, "")
var leading = boolArg(conf, call, "leading", 2, true) var leading = boolArg(conf, call, "leading", 2, true)
var trailing = boolArg(conf, call, "trailing", 3, true) var trailing = boolArg(conf, call, "trailing", 3, true)
@ -61,7 +61,7 @@ proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: string, call: PNo
llStreamWriteln(result, line) llStreamWriteln(result, line)
llStreamClose(stdin) llStreamClose(stdin)
proc filterReplace*(conf: ConfigRef; stdin: PLLStream, filename: string, call: PNode): PLLStream = proc filterReplace*(conf: ConfigRef; stdin: PLLStream, filename: AbsoluteFile, call: PNode): PLLStream =
var sub = strArg(conf, call, "sub", 1, "") var sub = strArg(conf, call, "sub", 1, "")
if len(sub) == 0: invalidPragma(conf, call) if len(sub) == 0: invalidPragma(conf, call)
var by = strArg(conf, call, "by", 2, "") var by = strArg(conf, call, "by", 2, "")

View file

@ -10,7 +10,7 @@
## Module that implements ``gorge`` for the compiler. ## Module that implements ``gorge`` for the compiler.
import msgs, std / sha1, os, osproc, streams, strutils, options, import msgs, std / sha1, os, osproc, streams, strutils, options,
lineinfos lineinfos, pathutils
proc readOutput(p: Process): (string, int) = proc readOutput(p: Process): (string, int) =
result[0] = "" result[0] = ""
@ -26,7 +26,7 @@ proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (str
let workingDir = parentDir(toFullPath(conf, info)) let workingDir = parentDir(toFullPath(conf, info))
if cache.len > 0:# and optForceFullMake notin gGlobalOptions: if cache.len > 0:# and optForceFullMake notin gGlobalOptions:
let h = secureHash(cmd & "\t" & input & "\t" & cache) let h = secureHash(cmd & "\t" & input & "\t" & cache)
let filename = options.toGeneratedFile(conf, "gorge_" & $h, "txt") let filename = toGeneratedFile(conf, AbsoluteFile("gorge_" & $h), "txt").string
var f: File var f: File
if open(f, filename): if open(f, filename):
result = (f.readAll, 0) result = (f.readAll, 0)

View file

@ -9,7 +9,7 @@
## This module contains a simple persistent id generator. ## This module contains a simple persistent id generator.
import idents, strutils, os, options import idents, strutils, os, options, pathutils
var gFrontEndId*: int var gFrontEndId*: int
@ -36,18 +36,18 @@ proc setId*(id: int) {.inline.} =
proc idSynchronizationPoint*(idRange: int) = proc idSynchronizationPoint*(idRange: int) =
gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1 gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1
proc toGid(conf: ConfigRef; f: string): string = proc toGid(conf: ConfigRef; f: AbsoluteFile): string =
# we used to use ``f.addFileExt("gid")`` (aka ``$project.gid``), but this # we used to use ``f.addFileExt("gid")`` (aka ``$project.gid``), but this
# will cause strange bugs if multiple projects are in the same folder, so # will cause strange bugs if multiple projects are in the same folder, so
# we simply use a project independent name: # we simply use a project independent name:
result = options.completeGeneratedFilePath(conf, "nim.gid") result = options.completeGeneratedFilePath(conf, AbsoluteFile"nim.gid").string
proc saveMaxIds*(conf: ConfigRef; project: string) = proc saveMaxIds*(conf: ConfigRef; project: AbsoluteFile) =
var f = open(toGid(conf, project), fmWrite) var f = open(toGid(conf, project), fmWrite)
f.writeLine($gFrontEndId) f.writeLine($gFrontEndId)
f.close() f.close()
proc loadMaxIds*(conf: ConfigRef; project: string) = proc loadMaxIds*(conf: ConfigRef; project: AbsoluteFile) =
var f: File var f: File
if open(f, toGid(conf, project), fmRead): if open(f, toGid(conf, project), fmRead):
var line = newStringOfCap(20) var line = newStringOfCap(20)

View file

@ -7,15 +7,12 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
# This module implements the symbol importing mechanism. ## This module implements the symbol importing mechanism.
import import
intsets, strutils, os, ast, astalgo, msgs, options, idents, lookups, intsets, strutils, os, ast, astalgo, msgs, options, idents, lookups,
semdata, passes, renderer, modulepaths, sigmatch, lineinfos semdata, passes, renderer, modulepaths, sigmatch, lineinfos
proc evalImport*(c: PContext, n: PNode): PNode
proc evalFrom*(c: PContext, n: PNode): PNode
proc readExceptSet*(c: PContext, n: PNode): IntSet = proc readExceptSet*(c: PContext, n: PNode): IntSet =
assert n.kind in {nkImportExceptStmt, nkExportExceptStmt} assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
result = initIntSet() result = initIntSet()
@ -140,7 +137,7 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym =
c.config.options) c.config.options)
proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym = proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
var f = checkModuleName(c.config, n) let f = checkModuleName(c.config, n)
if f != InvalidFileIDX: if f != InvalidFileIDX:
let L = c.graph.importStack.len let L = c.graph.importStack.len
let recursion = c.graph.importStack.find(f) let recursion = c.graph.importStack.find(f)
@ -168,7 +165,8 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
else: else:
message(c.config, n.info, warnDeprecated, result.name.s) message(c.config, n.info, warnDeprecated, result.name.s)
suggestSym(c.config, n.info, result, c.graph.usageSym, false) suggestSym(c.config, n.info, result, c.graph.usageSym, false)
importStmtResult.add newStrNode(toFullPath(c.config, f), n.info) importStmtResult.add newSymNode(result, n.info)
#newStrNode(toFullPath(c.config, f), n.info)
proc transformImportAs(c: PContext; n: PNode): PNode = proc transformImportAs(c: PContext; n: PNode): PNode =
if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as": if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as":
@ -188,7 +186,7 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
importAllSymbolsExcept(c, m, emptySet) importAllSymbolsExcept(c, m, emptySet)
#importForwarded(c, m.ast, emptySet) #importForwarded(c, m.ast, emptySet)
proc evalImport(c: PContext, n: PNode): PNode = proc evalImport*(c: PContext, n: PNode): PNode =
result = newNodeI(nkImportStmt, n.info) result = newNodeI(nkImportStmt, n.info)
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
let it = n.sons[i] let it = n.sons[i]
@ -212,7 +210,7 @@ proc evalImport(c: PContext, n: PNode): PNode =
else: else:
impMod(c, it, result) impMod(c, it, result)
proc evalFrom(c: PContext, n: PNode): PNode = proc evalFrom*(c: PContext, n: PNode): PNode =
result = newNodeI(nkImportStmt, n.info) result = newNodeI(nkImportStmt, n.info)
checkMinSonsLen(n, 2, c.config) checkMinSonsLen(n, 2, c.config)
n.sons[0] = transformImportAs(c, n.sons[0]) n.sons[0] = transformImportAs(c, n.sons[0])

View file

@ -32,7 +32,7 @@ import
ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options, ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables, nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables,
times, ropes, math, passes, ccgutils, wordrecg, renderer, times, ropes, math, passes, ccgutils, wordrecg, renderer,
intsets, cgmeth, lowerings, sighashes, lineinfos, rodutils intsets, cgmeth, lowerings, sighashes, lineinfos, rodutils, pathutils
from modulegraphs import ModuleGraph from modulegraphs import ModuleGraph
@ -2254,7 +2254,7 @@ proc genClass(conf: ConfigRef; obj: PType; content: Rope; ext: string) =
"class $#$# {$n$#$n}$n") % "class $#$# {$n$#$n}$n") %
[rope(VersionAsString), cls, extends, content] [rope(VersionAsString), cls, extends, content]
let outfile = changeFileExt(completeCFilePath(conf, $cls), ext) let outfile = changeFileExt(completeCFilePath(conf, AbsoluteFile $cls), ext)
discard writeRopeIfNotEqual(result, outfile) discard writeRopeIfNotEqual(result, outfile)
proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
@ -2268,11 +2268,11 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
else: "nimsystem" else: "nimsystem"
let code = wholeCode(graph, m) let code = wholeCode(graph, m)
let outfile = let outfile =
if m.config.outFile.len > 0: if not m.config.outFile.isEmpty:
if m.config.outFile.isAbsolute: m.config.outFile if m.config.outFile.string.isAbsolute: m.config.outFile
else: getCurrentDir() / m.config.outFile else: AbsoluteFile(getCurrentDir() / m.config.outFile.string)
else: else:
changeFileExt(completeCFilePath(m.config, f), ext) changeFileExt(completeCFilePath(m.config, AbsoluteFile f), ext)
discard writeRopeIfNotEqual(genHeader() & code, outfile) discard writeRopeIfNotEqual(genHeader() & code, outfile)
for obj, content in items(globals.classes): for obj, content in items(globals.classes):
genClass(m.config, obj, content, ext) genClass(m.config, obj, content, ext)

View file

@ -17,7 +17,7 @@
import import
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream, hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
wordrecg, lineinfos wordrecg, lineinfos, pathutils
const const
MaxLineLength* = 80 # lines longer than this lead to a warning MaxLineLength* = 80 # lines longer than this lead to a warning
@ -232,7 +232,7 @@ proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream;
lex.previousToken.fileIndex = fileIdx lex.previousToken.fileIndex = fileIdx
lex.config = config lex.config = config
proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream; proc openLexer*(lex: var TLexer, filename: AbsoluteFile, inputstream: PLLStream;
cache: IdentCache; config: ConfigRef) = cache: IdentCache; config: ConfigRef) =
openLexer(lex, fileInfoIdx(config, filename), inputstream, cache, config) openLexer(lex, fileInfoIdx(config, filename), inputstream, cache, config)

View file

@ -10,7 +10,7 @@
## This module contains the ``TMsgKind`` enum as well as the ## This module contains the ``TMsgKind`` enum as well as the
## ``TLineInfo`` object. ## ``TLineInfo`` object.
import ropes, tables import ropes, tables, pathutils
const const
explanationsBaseUrl* = "https://nim-lang.org/docs/manual" explanationsBaseUrl* = "https://nim-lang.org/docs/manual"
@ -179,8 +179,8 @@ const
type type
TFileInfo* = object TFileInfo* = object
fullPath*: string # This is a canonical full filesystem path fullPath*: AbsoluteFile # This is a canonical full filesystem path
projPath*: string # This is relative to the project's root projPath*: RelativeFile # This is relative to the project's root
shortName*: string # short name of the module shortName*: string # short name of the module
quotedName*: Rope # cached quoted short name for codegen quotedName*: Rope # cached quoted short name for codegen
# purposes # purposes
@ -191,7 +191,7 @@ type
# used for better error messages and # used for better error messages and
# embedding the original source in the # embedding the original source in the
# generated code # generated code
dirtyfile*: string # the file that is actually read into memory dirtyfile*: AbsoluteFile # the file that is actually read into memory
# and parsed; usually "" but is used # and parsed; usually "" but is used
# for 'nimsuggest' # for 'nimsuggest'
hash*: string # the checksum of the file hash*: string # the checksum of the file

View file

@ -14,7 +14,7 @@ import
strutils, os, intsets, strtabs strutils, os, intsets, strtabs
import options, ast, astalgo, msgs, semdata, ropes, idents, import options, ast, astalgo, msgs, semdata, ropes, idents,
lineinfos lineinfos, pathutils
const const
Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'} Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
@ -42,7 +42,7 @@ proc overwriteFiles*(conf: ConfigRef) =
let newFile = if gOverWrite: conf.m.fileInfos[i].fullpath let newFile = if gOverWrite: conf.m.fileInfos[i].fullpath
else: conf.m.fileInfos[i].fullpath.changeFileExt(".pretty.nim") else: conf.m.fileInfos[i].fullpath.changeFileExt(".pretty.nim")
try: try:
var f = open(newFile, fmWrite) var f = open(newFile.string, fmWrite)
for line in conf.m.fileInfos[i].lines: for line in conf.m.fileInfos[i].lines:
if doStrip: if doStrip:
f.write line.strip(leading = false, trailing = true) f.write line.strip(leading = false, trailing = true)
@ -51,7 +51,7 @@ proc overwriteFiles*(conf: ConfigRef) =
f.write(conf.m.fileInfos[i], "\L") f.write(conf.m.fileInfos[i], "\L")
f.close f.close
except IOError: except IOError:
rawMessage(conf, errGenerated, "cannot open file: " & newFile) rawMessage(conf, errGenerated, "cannot open file: " & newFile.string)
proc `=~`(s: string, a: openArray[string]): bool = proc `=~`(s: string, a: openArray[string]): bool =
for x in a: for x in a:

View file

@ -10,7 +10,7 @@
## Low-level streams for high performance. ## Low-level streams for high performance.
import import
strutils strutils, pathutils
# support '-d:useGnuReadline' for backwards compatibility: # support '-d:useGnuReadline' for backwards compatibility:
when not defined(windows) and (defined(useGnuReadline) or defined(useLinenoise)): when not defined(windows) and (defined(useGnuReadline) or defined(useLinenoise)):
@ -41,10 +41,10 @@ proc llStreamOpen*(f: File): PLLStream =
result.f = f result.f = f
result.kind = llsFile result.kind = llsFile
proc llStreamOpen*(filename: string, mode: FileMode): PLLStream = proc llStreamOpen*(filename: AbsoluteFile, mode: FileMode): PLLStream =
new(result) new(result)
result.kind = llsFile result.kind = llsFile
if not open(result.f, filename, mode): result = nil if not open(result.f, filename.string, mode): result = nil
proc llStreamOpen*(): PLLStream = proc llStreamOpen*(): PLLStream =
new(result) new(result)

View file

@ -19,7 +19,7 @@ import
cgen, jsgen, json, nversion, cgen, jsgen, json, nversion,
platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen, platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
docgen2, parser, modules, ccgutils, sigmatch, ropes, docgen2, parser, modules, ccgutils, sigmatch, ropes,
modulegraphs, tables, rod, lineinfos modulegraphs, tables, rod, lineinfos, pathutils
from magicsys import resetSysTypes from magicsys import resetSysTypes
@ -30,8 +30,8 @@ proc semanticPasses(g: ModuleGraph) =
registerPass g, verbosePass registerPass g, verbosePass
registerPass g, semPass registerPass g, semPass
proc writeDepsFile(g: ModuleGraph; project: string) = proc writeDepsFile(g: ModuleGraph; project: AbsoluteFile) =
let f = open(changeFileExt(project, "deps"), fmWrite) let f = open(changeFileExt(project, "deps").string, fmWrite)
for m in g.modules: for m in g.modules:
if m != nil: if m != nil:
f.writeLine(toFullPath(g.config, m.position.FileIndex)) f.writeLine(toFullPath(g.config, m.position.FileIndex))
@ -47,8 +47,9 @@ proc commandGenDepend(graph: ModuleGraph) =
let project = graph.config.projectFull let project = graph.config.projectFull
writeDepsFile(graph, project) writeDepsFile(graph, project)
generateDot(graph, project) generateDot(graph, project)
execExternalProgram(graph.config, "dot -Tpng -o" & changeFileExt(project, "png") & execExternalProgram(graph.config, "dot -Tpng -o" &
' ' & changeFileExt(project, "dot")) changeFileExt(project, "png").string &
' ' & changeFileExt(project, "dot").string)
proc commandCheck(graph: ModuleGraph) = proc commandCheck(graph: ModuleGraph) =
graph.config.errorMax = high(int) # do not stop after first error graph.config.errorMax = high(int) # do not stop after first error
@ -126,7 +127,7 @@ proc commandEval(graph: ModuleGraph; exp: string) =
makeStdinModule(graph)) makeStdinModule(graph))
proc commandScan(cache: IdentCache, config: ConfigRef) = proc commandScan(cache: IdentCache, config: ConfigRef) =
var f = addFileExt(mainCommandArg(config), NimExt) var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt)
var stream = llStreamOpen(f, fmRead) var stream = llStreamOpen(f, fmRead)
if stream != nil: if stream != nil:
var var
@ -140,7 +141,7 @@ proc commandScan(cache: IdentCache, config: ConfigRef) =
if tok.tokType == tkEof: break if tok.tokType == tkEof: break
closeLexer(L) closeLexer(L)
else: else:
rawMessage(config, errGenerated, "cannot open file: " & f) rawMessage(config, errGenerated, "cannot open file: " & f.string)
const const
PrintRopeCacheStats = false PrintRopeCacheStats = false
@ -231,11 +232,11 @@ proc mainCommand*(graph: ModuleGraph) =
for s in definedSymbolNames(conf.symbols): definedSymbols.elems.add(%s) for s in definedSymbolNames(conf.symbols): definedSymbols.elems.add(%s)
var libpaths = newJArray() var libpaths = newJArray()
for dir in conf.searchPaths: libpaths.elems.add(%dir) for dir in conf.searchPaths: libpaths.elems.add(%dir.string)
var dumpdata = % [ var dumpdata = % [
(key: "version", val: %VersionAsString), (key: "version", val: %VersionAsString),
(key: "project_path", val: %conf.projectFull), (key: "project_path", val: %conf.projectFull.string),
(key: "defined_symbols", val: definedSymbols), (key: "defined_symbols", val: definedSymbols),
(key: "lib_paths", val: libpaths) (key: "lib_paths", val: libpaths)
] ]
@ -247,7 +248,7 @@ proc mainCommand*(graph: ModuleGraph) =
for s in definedSymbolNames(conf.symbols): msgWriteln(conf, s, {msgStdout, msgSkipHook}) for s in definedSymbolNames(conf.symbols): msgWriteln(conf, s, {msgStdout, msgSkipHook})
msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook}) msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook})
for it in conf.searchPaths: msgWriteln(conf, it) for it in conf.searchPaths: msgWriteln(conf, it.string)
of "check": of "check":
conf.cmd = cmdCheck conf.cmd = cmdCheck
commandCheck(graph) commandCheck(graph)

View file

@ -7,9 +7,8 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
import ast, renderer, strutils, msgs, options, idents, os, lineinfos import ast, renderer, strutils, msgs, options, idents, os, lineinfos,
pathutils, nimblecmd
import nimblecmd
when false: when false:
const const
@ -160,7 +159,7 @@ proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
# This returns the full canonical path for a given module import # This returns the full canonical path for a given module import
let modulename = getModuleName(conf, n) let modulename = getModuleName(conf, n)
let fullPath = findModule(conf, modulename, toFullPath(conf, n.info)) let fullPath = findModule(conf, modulename, toFullPath(conf, n.info))
if fullPath.len == 0: if fullPath.isEmpty:
if doLocalError: if doLocalError:
let m = if modulename.len > 0: modulename else: $n let m = if modulename.len > 0: modulename else: $n
localError(conf, n.info, "cannot open file: " & m) localError(conf, n.info, "cannot open file: " & m)

View file

@ -12,7 +12,7 @@
import import
ast, astalgo, magicsys, std / sha1, msgs, cgendata, sigmatch, options, ast, astalgo, magicsys, std / sha1, msgs, cgendata, sigmatch, options,
idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod, idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod,
lineinfos lineinfos, pathutils
proc resetSystemArtifacts*(g: ModuleGraph) = proc resetSystemArtifacts*(g: ModuleGraph) =
magicsys.resetSysTypes(g) magicsys.resetSysTypes(g)
@ -102,19 +102,21 @@ proc connectCallbacks*(graph: ModuleGraph) =
proc compileSystemModule*(graph: ModuleGraph) = proc compileSystemModule*(graph: ModuleGraph) =
if graph.systemModule == nil: if graph.systemModule == nil:
connectCallbacks(graph) connectCallbacks(graph)
graph.config.m.systemFileIdx = fileInfoIdx(graph.config, graph.config.libpath / "system.nim") graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
graph.config.libpath / RelativeFile"system.nim")
discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule}) discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule})
proc wantMainModule*(conf: ConfigRef) = proc wantMainModule*(conf: ConfigRef) =
if conf.projectFull.len == 0: if conf.projectFull.isEmpty:
fatal(conf, newLineInfo(conf, "command line", 1, 1), errGenerated, "command expects a filename") fatal(conf, newLineInfo(conf, AbsoluteFile"command line", 1, 1), errGenerated,
"command expects a filename")
conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt)) conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))
proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIDX) = proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIDX) =
connectCallbacks(graph) connectCallbacks(graph)
let conf = graph.config let conf = graph.config
wantMainModule(conf) wantMainModule(conf)
let systemFileIdx = fileInfoIdx(conf, conf.libpath / "system.nim") let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
let projectFile = if projectFileIdx == InvalidFileIDX: conf.projectMainIdx else: projectFileIdx let projectFile = if projectFileIdx == InvalidFileIDX: conf.projectMainIdx else: projectFileIdx
graph.importStack.add projectFile graph.importStack.add projectFile
if projectFile == systemFileIdx: if projectFile == systemFileIdx:
@ -123,8 +125,11 @@ proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIDX) =
graph.compileSystemModule() graph.compileSystemModule()
discard graph.compileModule(projectFile, {sfMainModule}) discard graph.compileModule(projectFile, {sfMainModule})
proc makeModule*(graph: ModuleGraph; filename: string): PSym = proc makeModule*(graph: ModuleGraph; filename: AbsoluteFile): PSym =
result = graph.newModule(fileInfoIdx(graph.config, filename)) result = graph.newModule(fileInfoIdx(graph.config, filename))
result.id = getID() result.id = getID()
proc makeStdinModule*(graph: ModuleGraph): PSym = graph.makeModule"stdin" proc makeModule*(graph: ModuleGraph; filename: string): PSym =
result = makeModule(graph, AbsoluteFile filename)
proc makeStdinModule*(graph: ModuleGraph): PSym = graph.makeModule(AbsoluteFile"stdin")

View file

@ -9,7 +9,7 @@
import import
options, strutils, os, tables, ropes, platform, terminal, macros, options, strutils, os, tables, ropes, platform, terminal, macros,
lineinfos lineinfos, pathutils
proc toCChar*(c: char; result: var string) = proc toCChar*(c: char; result: var string) =
case c case c
@ -35,20 +35,20 @@ proc makeCString*(s: string): Rope =
add(result, rope(res)) add(result, rope(res))
proc newFileInfo(fullPath, projPath: string): TFileInfo = proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo =
result.fullPath = fullPath result.fullPath = fullPath
#shallow(result.fullPath) #shallow(result.fullPath)
result.projPath = projPath result.projPath = projPath
#shallow(result.projPath) #shallow(result.projPath)
let fileName = projPath.extractFilename let fileName = fullPath.extractFilename
result.shortName = fileName.changeFileExt("") result.shortName = fileName.changeFileExt("")
result.quotedName = fileName.makeCString result.quotedName = fileName.makeCString
result.quotedFullName = fullPath.makeCString result.quotedFullName = fullPath.string.makeCString
result.lines = @[] result.lines = @[]
when defined(nimpretty): when defined(nimpretty):
if result.fullPath.len > 0: if not result.fullPath.isEmpty:
try: try:
result.fullContent = readFile(result.fullPath) result.fullContent = readFile(result.fullPath.string)
except IOError: except IOError:
#rawMessage(errCannotOpenFile, result.fullPath) #rawMessage(errCannotOpenFile, result.fullPath)
# XXX fixme # XXX fixme
@ -58,39 +58,39 @@ when defined(nimpretty):
proc fileSection*(conf: ConfigRef; fid: FileIndex; a, b: int): string = proc fileSection*(conf: ConfigRef; fid: FileIndex; a, b: int): string =
substr(conf.m.fileInfos[fid.int].fullContent, a, b) substr(conf.m.fileInfos[fid.int].fullContent, a, b)
proc fileInfoKnown*(conf: ConfigRef; filename: string): bool = proc fileInfoKnown*(conf: ConfigRef; filename: AbsoluteFile): bool =
var var
canon: string canon: AbsoluteFile
try: try:
canon = canonicalizePath(conf, filename) canon = canonicalizePath(conf, filename)
except: except:
canon = filename canon = filename
result = conf.m.filenameToIndexTbl.hasKey(canon) result = conf.m.filenameToIndexTbl.hasKey(canon.string)
proc fileInfoIdx*(conf: ConfigRef; filename: string; isKnownFile: var bool): FileIndex = proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile; isKnownFile: var bool): FileIndex =
var var
canon: string canon: AbsoluteFile
pseudoPath = false pseudoPath = false
try: try:
canon = canonicalizePath(conf, filename) canon = canonicalizePath(conf, filename)
shallow(canon) shallow(canon.string)
except: except:
canon = filename canon = filename
# The compiler uses "filenames" such as `command line` or `stdin` # The compiler uses "filenames" such as `command line` or `stdin`
# This flag indicates that we are working with such a path here # This flag indicates that we are working with such a path here
pseudoPath = true pseudoPath = true
if conf.m.filenameToIndexTbl.hasKey(canon): if conf.m.filenameToIndexTbl.hasKey(canon.string):
result = conf.m.filenameToIndexTbl[canon] result = conf.m.filenameToIndexTbl[canon.string]
else: else:
isKnownFile = false isKnownFile = false
result = conf.m.fileInfos.len.FileIndex result = conf.m.fileInfos.len.FileIndex
conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: filename conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: RelativeFile filename
else: shortenDir(conf, canon))) else: relativeTo(canon, conf.projectPath)))
conf.m.filenameToIndexTbl[canon] = result conf.m.filenameToIndexTbl[canon.string] = result
proc fileInfoIdx*(conf: ConfigRef; filename: string): FileIndex = proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile): FileIndex =
var dummy: bool var dummy: bool
result = fileInfoIdx(conf, filename, dummy) result = fileInfoIdx(conf, filename, dummy)
@ -99,7 +99,7 @@ proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
result.line = uint16(line) result.line = uint16(line)
result.col = int16(col) result.col = int16(col)
proc newLineInfo*(conf: ConfigRef; filename: string, line, col: int): TLineInfo {.inline.} = proc newLineInfo*(conf: ConfigRef; filename: AbsoluteFile, line, col: int): TLineInfo {.inline.} =
result = newLineInfo(fileInfoIdx(conf, filename), line, col) result = newLineInfo(fileInfoIdx(conf, filename), line, col)
@ -152,13 +152,16 @@ proc getInfoContext*(conf: ConfigRef; index: int): TLineInfo =
else: result = conf.m.msgContext[i] else: result = conf.m.msgContext[i]
template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string = template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
(if fileIdx.int32 < 0 or conf == nil: "???" else: conf.m.fileInfos[fileIdx.int32].projPath) if fileIdx.int32 < 0 or conf == nil:
"???"
else:
conf.m.fileInfos[fileIdx.int32].projPath.string
proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string = proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string =
if fileIdx.int32 < 0 or conf == nil: result = "???" if fileIdx.int32 < 0 or conf == nil: result = "???"
else: result = conf.m.fileInfos[fileIdx.int32].fullPath else: result = conf.m.fileInfos[fileIdx.int32].fullPath.string
proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: string) = proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: AbsoluteFile) =
assert fileIdx.int32 >= 0 assert fileIdx.int32 >= 0
conf.m.fileInfos[fileIdx.int32].dirtyFile = filename conf.m.fileInfos[fileIdx.int32].dirtyFile = filename
@ -170,10 +173,10 @@ proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string =
assert fileIdx.int32 >= 0 assert fileIdx.int32 >= 0
shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash) shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash)
proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): string = proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): AbsoluteFile =
if fileIdx.int32 < 0: if fileIdx.int32 < 0:
result = "???" result = AbsoluteFile"???"
elif conf.m.fileInfos[fileIdx.int32].dirtyFile.len > 0: elif not conf.m.fileInfos[fileIdx.int32].dirtyFile.isEmpty:
result = conf.m.fileInfos[fileIdx.int32].dirtyFile result = conf.m.fileInfos[fileIdx.int32].dirtyFile
else: else:
result = conf.m.fileInfos[fileIdx.int32].fullPath result = conf.m.fileInfos[fileIdx.int32].fullPath
@ -188,9 +191,9 @@ proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
if info.fileIndex.int32 < 0: if info.fileIndex.int32 < 0:
result = "???" result = "???"
elif optListFullPaths in conf.globalOptions: elif optListFullPaths in conf.globalOptions:
result = conf.m.fileInfos[info.fileIndex.int32].fullPath result = conf.m.fileInfos[info.fileIndex.int32].fullPath.string
else: else:
result = conf.m.fileInfos[info.fileIndex.int32].projPath result = conf.m.fileInfos[info.fileIndex.int32].projPath.string
proc toLinenumber*(info: TLineInfo): int {.inline.} = proc toLinenumber*(info: TLineInfo): int {.inline.} =
result = int info.line result = int info.line

View file

@ -10,7 +10,7 @@
## This module implements the generation of ``.ndi`` files for better debugging ## This module implements the generation of ``.ndi`` files for better debugging
## support of Nim code. "ndi" stands for "Nim debug info". ## support of Nim code. "ndi" stands for "Nim debug info".
import ast, msgs, ropes, options import ast, msgs, ropes, options, pathutils
type type
NdiFile* = object NdiFile* = object
@ -30,10 +30,10 @@ proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) =
template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) = template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) =
if f.enabled: doWrite(f, s, conf) if f.enabled: doWrite(f, s, conf)
proc open*(f: var NdiFile; filename: string; conf: ConfigRef) = proc open*(f: var NdiFile; filename: AbsoluteFile; conf: ConfigRef) =
f.enabled = filename.len > 0 f.enabled = not filename.isEmpty
if f.enabled: if f.enabled:
f.f = open(filename, fmWrite, 8000) f.f = open(filename.string, fmWrite, 8000)
f.buf = newStringOfCap(20) f.buf = newStringOfCap(20)
proc close*(f: var NdiFile) = proc close*(f: var NdiFile) =

View file

@ -21,7 +21,8 @@ when defined(i386) and defined(windows) and defined(vcc):
import import
commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
extccomp, strutils, os, osproc, platform, main, parseopt, extccomp, strutils, os, osproc, platform, main, parseopt,
nodejs, scriptconfig, idents, modulegraphs, lineinfos, cmdlinehelper nodejs, scriptconfig, idents, modulegraphs, lineinfos, cmdlinehelper,
pathutils
when hasTinyCBackend: when hasTinyCBackend:
import tccgen import tccgen
@ -30,12 +31,12 @@ when defined(profiler) or defined(memProfiler):
{.hint: "Profiling support is turned on!".} {.hint: "Profiling support is turned on!".}
import nimprof import nimprof
proc prependCurDir(f: string): string = proc prependCurDir(f: AbsoluteFile): AbsoluteFile =
when defined(unix): when defined(unix):
if os.isAbsolute(f): result = f if os.isAbsolute(f.string): result = f
else: result = "./" & f else: result = AbsoluteFile("./" & f.string)
else: else:
result = f result = AbsoluteFile f
proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
var p = parseopt.initOptParser(cmd) var p = parseopt.initOptParser(cmd)
@ -78,15 +79,15 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
if optRun in conf.globalOptions: if optRun in conf.globalOptions:
if conf.cmd == cmdCompileToJS: if conf.cmd == cmdCompileToJS:
var ex: string var ex: string
if conf.outFile.len > 0: if not conf.outFile.isEmpty:
ex = conf.outFile.prependCurDir.quoteShell ex = conf.outFile.prependCurDir.quoteShell
else: else:
ex = quoteShell( ex = quoteShell(
completeCFilePath(conf, changeFileExt(conf.projectFull, "js").prependCurDir)) completeCFilePath(conf, changeFileExt(conf.projectFull, "js").prependCurDir))
execExternalProgram(conf, findNodeJs() & " " & ex & ' ' & conf.arguments) execExternalProgram(conf, findNodeJs() & " " & ex & ' ' & conf.arguments)
else: else:
var binPath: string var binPath: AbsoluteFile
if conf.outFile.len > 0: if not conf.outFile.isEmpty:
# If the user specified an outFile path, use that directly. # If the user specified an outFile path, use that directly.
binPath = conf.outFile.prependCurDir binPath = conf.outFile.prependCurDir
else: else:

View file

@ -10,9 +10,9 @@
## Implements some helper procs for Nimble (Nim's package manager) support. ## Implements some helper procs for Nimble (Nim's package manager) support.
import parseutils, strutils, strtabs, os, options, msgs, sequtils, import parseutils, strutils, strtabs, os, options, msgs, sequtils,
lineinfos lineinfos, pathutils
proc addPath*(conf: ConfigRef; path: string, info: TLineInfo) = proc addPath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) =
if not conf.searchPaths.contains(path): if not conf.searchPaths.contains(path):
conf.searchPaths.insert(path, 0) conf.searchPaths.insert(path, 0)
@ -112,9 +112,9 @@ proc addNimblePath(conf: ConfigRef; p: string, info: TLineInfo) =
if not path.isAbsolute(): if not path.isAbsolute():
path = p / path path = p / path
if not contains(conf.searchPaths, path): if not contains(conf.searchPaths, AbsoluteDir path):
message(conf, info, hintPath, path) message(conf, info, hintPath, path)
conf.lazyPaths.insert(path, 0) conf.lazyPaths.insert(AbsoluteDir path, 0)
proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) = proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) =
var packages = newStringTable(modeStyleInsensitive) var packages = newStringTable(modeStyleInsensitive)
@ -126,9 +126,9 @@ proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) =
for p in packages.chosen: for p in packages.chosen:
addNimblePath(conf, p, info) addNimblePath(conf, p, info)
proc nimblePath*(conf: ConfigRef; path: string, info: TLineInfo) = proc nimblePath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) =
addPathRec(conf, path, info) addPathRec(conf, path.string, info)
addNimblePath(conf, path, info) addNimblePath(conf, path.string, info)
when isMainModule: when isMainModule:
proc v(s: string): Version = s.newVersion proc v(s: string): Version = s.newVersion

View file

@ -11,7 +11,7 @@
import import
llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer, llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer,
options, idents, wordrecg, strtabs, lineinfos options, idents, wordrecg, strtabs, lineinfos, pathutils
# ---------------- configuration file parser ----------------------------- # ---------------- configuration file parser -----------------------------
# we use Nim's scanner here to save space and work # we use Nim's scanner here to save space and work
@ -201,8 +201,8 @@ proc parseAssignment(L: var TLexer, tok: var TToken;
else: else:
processSwitch(s, val, passPP, info, config) processSwitch(s, val, passPP, info, config)
proc readConfigFile( proc readConfigFile(filename: AbsoluteFile; cache: IdentCache;
filename: string; cache: IdentCache; config: ConfigRef): bool = config: ConfigRef): bool =
var var
L: TLexer L: TLexer
tok: TToken tok: TToken
@ -219,24 +219,24 @@ proc readConfigFile(
closeLexer(L) closeLexer(L)
return true return true
proc getUserConfigPath*(filename: string): string = proc getUserConfigPath*(filename: RelativeFile): AbsoluteFile =
result = joinPath([getConfigDir(), "nim", filename]) result = getConfigDir().AbsoluteDir / RelativeDir"nim" / filename
proc getSystemConfigPath*(conf: ConfigRef; filename: string): string = proc getSystemConfigPath*(conf: ConfigRef; filename: RelativeFile): AbsoluteFile =
# try standard configuration file (installation did not distribute files # try standard configuration file (installation did not distribute files
# the UNIX way) # the UNIX way)
let p = getPrefixDir(conf) let p = getPrefixDir(conf)
result = joinPath([p, "config", filename]) result = p / RelativeDir"config" / filename
when defined(unix): when defined(unix):
if not existsFile(result): result = joinPath([p, "etc/nim", filename]) if not fileExists(result): result = p / RelativeDir"etc/nim" / filename
if not existsFile(result): result = "/etc/nim/" & filename if not fileExists(result): result = AbsoluteDir"/etc/nim" / filename
proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) = proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef) =
setDefaultLibpath(conf) setDefaultLibpath(conf)
var configFiles = newSeq[string]() var configFiles = newSeq[AbsoluteFile]()
template readConfigFile(path: string) = template readConfigFile(path) =
let configPath = path let configPath = path
if readConfigFile(configPath, cache, conf): if readConfigFile(configPath, cache, conf):
add(configFiles, configPath) add(configFiles, configPath)
@ -247,10 +247,10 @@ proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) =
if optSkipUserConfigFile notin conf.globalOptions: if optSkipUserConfigFile notin conf.globalOptions:
readConfigFile(getUserConfigPath(cfg)) readConfigFile(getUserConfigPath(cfg))
let pd = if conf.projectPath.len > 0: conf.projectPath else: getCurrentDir() let pd = if not conf.projectPath.isEmpty: conf.projectPath else: AbsoluteDir(getCurrentDir())
if optSkipParentConfigFiles notin conf.globalOptions: if optSkipParentConfigFiles notin conf.globalOptions:
for dir in parentDirs(pd, fromRoot=true, inclusive=false): for dir in parentDirs(pd.string, fromRoot=true, inclusive=false):
readConfigFile(dir / cfg) readConfigFile(AbsoluteDir(dir) / cfg)
if optSkipProjConfigFile notin conf.globalOptions: if optSkipProjConfigFile notin conf.globalOptions:
readConfigFile(pd / cfg) readConfigFile(pd / cfg)
@ -264,4 +264,4 @@ proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) =
for filename in configFiles: for filename in configFiles:
# delayed to here so that `hintConf` is honored # delayed to here so that `hintConf` is honored
rawMessage(conf, hintConf, filename) rawMessage(conf, hintConf, filename.string)

View file

@ -11,7 +11,7 @@
import import
ast, astalgo, modules, passes, condsyms, ast, astalgo, modules, passes, condsyms,
options, sem, semdata, llstream, vm, vmdef, options, sem, semdata, llstream, vm, vmdef,
modulegraphs, idents, os modulegraphs, idents, os, pathutils
type type
Interpreter* = ref object ## Use Nim as an interpreter with this object Interpreter* = ref object ## Use Nim as an interpreter with this object
@ -103,8 +103,8 @@ proc createInterpreter*(scriptName: string;
registerPass(graph, evalPass) registerPass(graph, evalPass)
for p in searchPaths: for p in searchPaths:
conf.searchPaths.add(p) conf.searchPaths.add(AbsoluteDir p)
if conf.libpath.len == 0: conf.libpath = p if conf.libpath.isEmpty: conf.libpath = AbsoluteDir p
var m = graph.makeModule(scriptName) var m = graph.makeModule(scriptName)
incl(m.flags, sfMainModule) incl(m.flags, sfMainModule)

View file

@ -15,6 +15,6 @@ const
VersionAsString* = system.NimVersion VersionAsString* = system.NimVersion
RodFileVersion* = "1223" # modify this if the rod-format changes! RodFileVersion* = "1223" # modify this if the rod-format changes!
NimCompilerApiVersion* = 2 ## Check for the existance of this before accessing it NimCompilerApiVersion* = 3 ## Check for the existance of this before accessing it
## as older versions of the compiler API do not ## as older versions of the compiler API do not
## declare this. ## declare this.

View file

@ -9,7 +9,7 @@
import import
os, strutils, strtabs, osproc, sets, lineinfos, platform, os, strutils, strtabs, osproc, sets, lineinfos, platform,
prefixmatches prefixmatches, pathutils
from terminal import isatty from terminal import isatty
from times import utc, fromUnix, local, getTime, format, DateTime from times import utc, fromUnix, local, getTime, format, DateTime
@ -135,7 +135,7 @@ type
External ## file was introduced via .compile pragma External ## file was introduced via .compile pragma
Cfile* = object Cfile* = object
cname*, obj*: string cname*, obj*: AbsoluteFile
flags*: set[CFileFlag] flags*: set[CFileFlag]
CfileList* = seq[Cfile] CfileList* = seq[Cfile]
@ -203,13 +203,14 @@ type
## symbols are always guaranteed to be style ## symbols are always guaranteed to be style
## insensitive. Otherwise hell would break lose. ## insensitive. Otherwise hell would break lose.
packageCache*: StringTableRef packageCache*: StringTableRef
searchPaths*: seq[string] searchPaths*: seq[AbsoluteDir]
lazyPaths*: seq[string] lazyPaths*: seq[AbsoluteDir]
outFile*, prefixDir*, libpath*, nimcacheDir*: string outFile*: AbsoluteFile
prefixDir*, libpath*, nimcacheDir*: AbsoluteDir
dllOverrides, moduleOverrides*: StringTableRef dllOverrides, moduleOverrides*: StringTableRef
projectName*: string # holds a name like 'nim' projectName*: string # holds a name like 'nim'
projectPath*: string # holds a path like /home/alice/projects/nim/compiler/ projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/
projectFull*: string # projectPath/projectName projectFull*: AbsoluteFile # projectPath/projectName
projectIsStdin*: bool # whether we're compiling from stdin projectIsStdin*: bool # whether we're compiling from stdin
projectMainIdx*: FileIndex # the canonical path id of the main module projectMainIdx*: FileIndex # the canonical path id of the main module
command*: string # the main command (e.g. cc, check, scan, etc) command*: string # the main command (e.g. cc, check, scan, etc)
@ -221,9 +222,9 @@ type
# The string uses the formatting variables `path` and `line`. # The string uses the formatting variables `path` and `line`.
# the used compiler # the used compiler
cIncludes*: seq[string] # directories to search for included files cIncludes*: seq[AbsoluteDir] # directories to search for included files
cLibs*: seq[string] # directories to search for lib files cLibs*: seq[AbsoluteDir] # directories to search for lib files
cLinkedLibs*: seq[string] # libraries to link cLinkedLibs*: seq[string] # libraries to link
externalToLink*: seq[string] # files to link in addition to the file externalToLink*: seq[string] # files to link in addition to the file
# we compiled (*) # we compiled (*)
@ -302,12 +303,13 @@ proc newConfigRef*(): ConfigRef =
packageCache: newPackageCache(), packageCache: newPackageCache(),
searchPaths: @[], searchPaths: @[],
lazyPaths: @[], lazyPaths: @[],
outFile: "", prefixDir: "", libpath: "", nimcacheDir: "", outFile: AbsoluteFile"", prefixDir: AbsoluteDir"",
libpath: AbsoluteDir"", nimcacheDir: AbsoluteDir"",
dllOverrides: newStringTable(modeCaseInsensitive), dllOverrides: newStringTable(modeCaseInsensitive),
moduleOverrides: newStringTable(modeStyleInsensitive), moduleOverrides: newStringTable(modeStyleInsensitive),
projectName: "", # holds a name like 'nim' projectName: "", # holds a name like 'nim'
projectPath: "", # holds a path like /home/alice/projects/nim/compiler/ projectPath: AbsoluteDir"", # holds a path like /home/alice/projects/nim/compiler/
projectFull: "", # projectPath/projectName projectFull: AbsoluteFile"", # projectPath/projectName
projectIsStdin: false, # whether we're compiling from stdin projectIsStdin: false, # whether we're compiling from stdin
projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module
command: "", # the main command (e.g. cc, check, scan, etc) command: "", # the main command (e.g. cc, check, scan, etc)
@ -401,7 +403,7 @@ template optPreserveOrigSource*(conf: ConfigRef): untyped =
optEmbedOrigSrc in conf.globalOptions optEmbedOrigSrc in conf.globalOptions
const const
genSubDir* = "nimcache" genSubDir* = RelativeDir"nimcache"
NimExt* = "nim" NimExt* = "nim"
RodExt* = "rod" RodExt* = "rod"
HtmlExt* = "html" HtmlExt* = "html"
@ -409,10 +411,10 @@ const
TagsExt* = "tags" TagsExt* = "tags"
TexExt* = "tex" TexExt* = "tex"
IniExt* = "ini" IniExt* = "ini"
DefaultConfig* = "nim.cfg" DefaultConfig* = RelativeFile"nim.cfg"
DefaultConfigNims* = "config.nims" DefaultConfigNims* = RelativeFile"config.nims"
DocConfig* = "nimdoc.cfg" DocConfig* = RelativeFile"nimdoc.cfg"
DocTexConfig* = "nimdoc.tex.cfg" DocTexConfig* = RelativeFile"nimdoc.tex.cfg"
const oKeepVariableNames* = true const oKeepVariableNames* = true
@ -437,56 +439,61 @@ proc getConfigVar*(conf: ConfigRef; key: string, default = ""): string =
proc setConfigVar*(conf: ConfigRef; key, val: string) = proc setConfigVar*(conf: ConfigRef; key, val: string) =
conf.configVars[key] = val conf.configVars[key] = val
proc getOutFile*(conf: ConfigRef; filename, ext: string): string = proc getOutFile*(conf: ConfigRef; filename: RelativeFile, ext: string): AbsoluteFile =
if conf.outFile != "": result = conf.outFile if not conf.outFile.isEmpty: result = conf.outFile
else: result = changeFileExt(filename, ext) else: result = conf.projectPath / changeFileExt(filename, ext)
proc getPrefixDir*(conf: ConfigRef): string = proc getPrefixDir*(conf: ConfigRef): AbsoluteDir =
## Gets the prefix dir, usually the parent directory where the binary resides. ## Gets the prefix dir, usually the parent directory where the binary resides.
## ##
## This is overridden by some tools (namely nimsuggest) via the ``conf.prefixDir`` ## This is overridden by some tools (namely nimsuggest) via the ``conf.prefixDir``
## global. ## field.
if conf.prefixDir != "": result = conf.prefixDir if not conf.prefixDir.isEmpty: result = conf.prefixDir
else: result = splitPath(getAppDir()).head else: result = AbsoluteDir splitPath(getAppDir()).head
proc setDefaultLibpath*(conf: ConfigRef) = proc setDefaultLibpath*(conf: ConfigRef) =
# set default value (can be overwritten): # set default value (can be overwritten):
if conf.libpath == "": if conf.libpath.isEmpty:
# choose default libpath: # choose default libpath:
var prefix = getPrefixDir(conf) var prefix = getPrefixDir(conf)
when defined(posix): when defined(posix):
if prefix == "/usr": conf.libpath = "/usr/lib/nim" if prefix == AbsoluteDir"/usr":
elif prefix == "/usr/local": conf.libpath = "/usr/local/lib/nim" conf.libpath = AbsoluteDir"/usr/lib/nim"
else: conf.libpath = joinPath(prefix, "lib") elif prefix == AbsoluteDir"/usr/local":
else: conf.libpath = joinPath(prefix, "lib") conf.libpath = AbsoluteDir"/usr/local/lib/nim"
else:
conf.libpath = prefix / RelativeDir"lib"
else:
conf.libpath = prefix / RelativeDir"lib"
# Special rule to support other tools (nimble) which import the compiler # Special rule to support other tools (nimble) which import the compiler
# modules and make use of them. # modules and make use of them.
let realNimPath = findExe("nim") let realNimPath = findExe("nim")
# Find out if $nim/../../lib/system.nim exists. # Find out if $nim/../../lib/system.nim exists.
let parentNimLibPath = realNimPath.parentDir.parentDir / "lib" let parentNimLibPath = realNimPath.parentDir.parentDir / "lib"
if not fileExists(conf.libpath / "system.nim") and if not fileExists(conf.libpath.string / "system.nim") and
fileExists(parentNimlibPath / "system.nim"): fileExists(parentNimlibPath / "system.nim"):
conf.libpath = parentNimLibPath conf.libpath = AbsoluteDir parentNimLibPath
proc canonicalizePath*(conf: ConfigRef; path: string): string = proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
# on Windows, 'expandFilename' calls getFullPathName which doesn't do # on Windows, 'expandFilename' calls getFullPathName which doesn't do
# case corrections, so we have to use this convoluted way of retrieving # case corrections, so we have to use this convoluted way of retrieving
# the true filename (see tests/modules and Nimble uses 'import Uri' instead # the true filename (see tests/modules and Nimble uses 'import Uri' instead
# of 'import uri'): # of 'import uri'):
when defined(windows): when defined(windows):
result = path.expandFilename result = AbsoluteFile path.string.expandFilename
for x in walkFiles(result): for x in walkFiles(result.string):
return x return AbsoluteFile x
else: else:
result = path.expandFilename result = AbsoluteFile path.string.expandFilename
proc shortenDir*(conf: ConfigRef; dir: string): string = proc shortenDir*(conf: ConfigRef; dir: string): string {.
deprecated: "use 'relativeTo' instead".} =
## returns the interesting part of a dir ## returns the interesting part of a dir
var prefix = conf.projectPath & DirSep var prefix = conf.projectPath.string & DirSep
if startsWith(dir, prefix): if startsWith(dir, prefix):
return substr(dir, len(prefix)) return substr(dir, len(prefix))
prefix = getPrefixDir(conf) & DirSep prefix = getPrefixDir(conf).string & DirSep
if startsWith(dir, prefix): if startsWith(dir, prefix):
return substr(dir, len(prefix)) return substr(dir, len(prefix))
result = dir result = dir
@ -509,87 +516,87 @@ proc getOsCacheDir(): string =
else: else:
result = getHomeDir() / genSubDir result = getHomeDir() / genSubDir
proc getNimcacheDir*(conf: ConfigRef): string = proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir =
# XXX projectName should always be without a file extension! # XXX projectName should always be without a file extension!
result = if conf.nimcacheDir.len > 0: result = if not conf.nimcacheDir.isEmpty:
conf.nimcacheDir conf.nimcacheDir
elif conf.cmd == cmdCompileToJS: elif conf.cmd == cmdCompileToJS:
shortenDir(conf, conf.projectPath) / genSubDir conf.projectPath / genSubDir
else: getOsCacheDir() / splitFile(conf.projectName).name & else:
(if isDefined(conf, "release"): "_r" else: "_d") AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name &
(if isDefined(conf, "release"): "_r" else: "_d"))
proc pathSubs*(conf: ConfigRef; p, config: string): string = proc pathSubs*(conf: ConfigRef; p, config: string): string =
let home = removeTrailingDirSep(os.getHomeDir()) let home = removeTrailingDirSep(os.getHomeDir())
result = unixToNativePath(p % [ result = unixToNativePath(p % [
"nim", getPrefixDir(conf), "nim", getPrefixDir(conf).string,
"lib", conf.libpath, "lib", conf.libpath.string,
"home", home, "home", home,
"config", config, "config", config,
"projectname", conf.projectName, "projectname", conf.projectName,
"projectpath", conf.projectPath, "projectpath", conf.projectPath.string,
"projectdir", conf.projectPath, "projectdir", conf.projectPath.string,
"nimcache", getNimcacheDir(conf)]) "nimcache", getNimcacheDir(conf).string])
if "~/" in result: if "~/" in result:
result = result.replace("~/", home & '/') result = result.replace("~/", home & '/')
proc toGeneratedFile*(conf: ConfigRef; path, ext: string): string = proc toGeneratedFile*(conf: ConfigRef; path: AbsoluteFile,
ext: string): AbsoluteFile =
## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod" ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
var (head, tail) = splitPath(path) let (head, tail) = splitPath(path.string)
#if len(head) > 0: head = shortenDir(head & dirSep) result = getNimcacheDir(conf) / RelativeFile changeFileExt(tail, ext)
result = joinPath([getNimcacheDir(conf), changeFileExt(tail, ext)])
#echo "toGeneratedFile(", path, ", ", ext, ") = ", result
proc completeGeneratedFilePath*(conf: ConfigRef; f: string, createSubDir: bool = true): string = proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile,
var (head, tail) = splitPath(f) createSubDir: bool = true): AbsoluteFile =
#if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep)) let (head, tail) = splitPath(f.string)
var subdir = getNimcacheDir(conf) # / head let subdir = getNimcacheDir(conf)
if createSubDir: if createSubDir:
try: try:
createDir(subdir) createDir(subdir.string)
except OSError: except OSError:
writeLine(stdout, "cannot create directory: " & subdir) writeLine(stdout, "cannot create directory: " & subdir.string)
quit(1) quit(1)
result = joinPath(subdir, tail) result = subdir / RelativeFile tail
#echo "completeGeneratedFilePath(", f, ") = ", result #echo "completeGeneratedFilePath(", f, ") = ", result
proc rawFindFile(conf: ConfigRef; f: string; suppressStdlib: bool): string = proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile =
for it in conf.searchPaths: for it in conf.searchPaths:
if suppressStdlib and it.startsWith(conf.libpath): if suppressStdlib and it.string.startsWith(conf.libpath.string):
continue continue
result = joinPath(it, f) result = it / f
if existsFile(result): if fileExists(result):
return canonicalizePath(conf, result) return canonicalizePath(conf, result)
result = "" result = AbsoluteFile""
proc rawFindFile2(conf: ConfigRef; f: string): string = proc rawFindFile2(conf: ConfigRef; f: RelativeFile): AbsoluteFile =
for i, it in conf.lazyPaths: for i, it in conf.lazyPaths:
result = joinPath(it, f) result = it / f
if existsFile(result): if fileExists(result):
# bring to front # bring to front
for j in countDown(i,1): for j in countDown(i,1):
swap(conf.lazyPaths[j], conf.lazyPaths[j-1]) swap(conf.lazyPaths[j], conf.lazyPaths[j-1])
return canonicalizePath(conf, result) return canonicalizePath(conf, result)
result = "" result = AbsoluteFile""
template patchModule(conf: ConfigRef) {.dirty.} = template patchModule(conf: ConfigRef) {.dirty.} =
if result.len > 0 and conf.moduleOverrides.len > 0: if not result.isEmpty and conf.moduleOverrides.len > 0:
let key = getPackageName(conf, result) & "_" & splitFile(result).name let key = getPackageName(conf, result.string) & "_" & splitFile(result).name
if conf.moduleOverrides.hasKey(key): if conf.moduleOverrides.hasKey(key):
let ov = conf.moduleOverrides[key] let ov = conf.moduleOverrides[key]
if ov.len > 0: result = ov if ov.len > 0: result = AbsoluteFile(ov)
proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): string {.procvar.} = proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): AbsoluteFile {.procvar.} =
if f.isAbsolute: if f.isAbsolute:
result = if f.existsFile: f else: "" result = if f.existsFile: AbsoluteFile(f) else: AbsoluteFile""
else: else:
result = rawFindFile(conf, f, suppressStdlib) result = rawFindFile(conf, RelativeFile f, suppressStdlib)
if result.len == 0: if result.isEmpty:
result = rawFindFile(conf, f.toLowerAscii, suppressStdlib) result = rawFindFile(conf, RelativeFile f.toLowerAscii, suppressStdlib)
if result.len == 0: if result.isEmpty:
result = rawFindFile2(conf, f) result = rawFindFile2(conf, RelativeFile f)
if result.len == 0: if result.isEmpty:
result = rawFindFile2(conf, f.toLowerAscii) result = rawFindFile2(conf, RelativeFile f.toLowerAscii)
patchModule(conf) patchModule(conf)
const stdlibDirs = [ const stdlibDirs = [
@ -599,7 +606,7 @@ const stdlibDirs = [
"wrappers", "wrappers/linenoise", "wrappers", "wrappers/linenoise",
"windows", "posix", "js"] "windows", "posix", "js"]
proc findModule*(conf: ConfigRef; modulename, currentModule: string): string = proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFile =
# returns path to module # returns path to module
const pkgPrefix = "pkg/" const pkgPrefix = "pkg/"
const stdPrefix = "std/" const stdPrefix = "std/"
@ -610,13 +617,13 @@ proc findModule*(conf: ConfigRef; modulename, currentModule: string): string =
if m.startsWith(stdPrefix): if m.startsWith(stdPrefix):
let stripped = m.substr(stdPrefix.len) let stripped = m.substr(stdPrefix.len)
for candidate in stdlibDirs: for candidate in stdlibDirs:
let path = (conf.libpath / candidate / stripped) let path = (conf.libpath.string / candidate / stripped)
if fileExists(path): if fileExists(path):
m = path m = path
break break
let currentPath = currentModule.splitFile.dir let currentPath = currentModule.splitFile.dir
result = currentPath / m result = AbsoluteFile currentPath / m
if not existsFile(result): if not fileExists(result):
result = findFile(conf, m) result = findFile(conf, m)
patchModule(conf) patchModule(conf)

View file

@ -41,10 +41,10 @@ proc getPackageName*(conf: ConfigRef; path: string): string =
dec parents dec parents
if parents <= 0: break if parents <= 0: break
proc withPackageName*(conf: ConfigRef; path: string): string = proc withPackageName*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
let x = getPackageName(conf, path) let x = getPackageName(conf, path.string)
if x.len == 0: if x.len == 0:
result = path result = path
else: else:
let (p, file, ext) = path.splitFile let (p, file, ext) = path.splitFile
result = (p / (x & '_' & file)) & ext result = p / RelativeFile((x & '_' & file) & ext)

View file

@ -27,7 +27,8 @@ when isMainModule:
outp.close outp.close
import import
llstream, lexer, idents, strutils, ast, astalgo, msgs, options, lineinfos llstream, lexer, idents, strutils, ast, astalgo, msgs, options, lineinfos,
pathutils
when defined(nimpretty2): when defined(nimpretty2):
import layouter import layouter
@ -114,7 +115,7 @@ proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream,
p.strongSpaces = strongSpaces p.strongSpaces = strongSpaces
p.emptyNode = newNode(nkEmpty) p.emptyNode = newNode(nkEmpty)
proc openParser*(p: var TParser, filename: string, inputStream: PLLStream, proc openParser*(p: var TParser, filename: AbsoluteFile, inputStream: PLLStream,
cache: IdentCache; config: ConfigRef; cache: IdentCache; config: ConfigRef;
strongSpaces=false) = strongSpaces=false) =
openParser(p, fileInfoIdx(config, filename), inputStream, cache, config, strongSpaces) openParser(p, fileInfoIdx(config, filename), inputStream, cache, config, strongSpaces)
@ -2270,7 +2271,7 @@ proc parseString*(s: string; cache: IdentCache; config: ConfigRef;
# XXX for now the builtin 'parseStmt/Expr' functions do not know about strong # XXX for now the builtin 'parseStmt/Expr' functions do not know about strong
# spaces... # spaces...
parser.lex.errorHandler = errorHandler parser.lex.errorHandler = errorHandler
openParser(parser, filename, stream, cache, config, false) openParser(parser, AbsoluteFile filename, stream, cache, config, false)
result = parser.parseAll result = parser.parseAll
closeParser(parser) closeParser(parser)

View file

@ -14,7 +14,7 @@ import
strutils, options, ast, astalgo, llstream, msgs, platform, os, strutils, options, ast, astalgo, llstream, msgs, platform, os,
condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
nimsets, syntaxes, times, idgen, modulegraphs, reorder, rod, nimsets, syntaxes, times, idgen, modulegraphs, reorder, rod,
lineinfos lineinfos, pathutils
type type
@ -106,7 +106,7 @@ proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool =
proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex = proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex =
let fullPath = findModule(conf, module, relativeTo) let fullPath = findModule(conf, module, relativeTo)
if fullPath.len == 0: if fullPath.isEmpty:
result = InvalidFileIDX result = InvalidFileIDX
else: else:
result = fileInfoIdx(conf, fullPath) result = fileInfoIdx(conf, fullPath)
@ -160,7 +160,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {
let filename = toFullPathConsiderDirty(graph.config, fileIdx) let filename = toFullPathConsiderDirty(graph.config, fileIdx)
s = llStreamOpen(filename, fmRead) s = llStreamOpen(filename, fmRead)
if s == nil: if s == nil:
rawMessage(graph.config, errCannotOpenFile, filename) rawMessage(graph.config, errCannotOpenFile, filename.string)
return false return false
else: else:
s = stream s = stream

254
compiler/pathutils.nim Normal file
View file

@ -0,0 +1,254 @@
#
#
# The Nim Compiler
# (c) Copyright 2018 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Path handling utilities for Nim. Strictly typed code in order
## to avoid the never ending time sink in getting path handling right.
## Might be a candidate for the stdlib later.
import os, strutils
type
AbsoluteFile* = distinct string
AbsoluteDir* = distinct string
RelativeFile* = distinct string
RelativeDir* = distinct string
proc isEmpty*(x: AbsoluteFile): bool {.inline.} = x.string.len == 0
proc isEmpty*(x: AbsoluteDir): bool {.inline.} = x.string.len == 0
proc isEmpty*(x: RelativeFile): bool {.inline.} = x.string.len == 0
proc isEmpty*(x: RelativeDir): bool {.inline.} = x.string.len == 0
proc copyFile*(source, dest: AbsoluteFile) =
os.copyFile(source.string, dest.string)
proc removeFile*(x: AbsoluteFile) {.borrow.}
proc splitFile*(x: AbsoluteFile): tuple[dir: AbsoluteDir, name, ext: string] =
let (a, b, c) = splitFile(x.string)
result = (dir: AbsoluteDir(a), name: b, ext: c)
proc extractFilename*(x: AbsoluteFile): string {.borrow.}
proc fileExists*(x: AbsoluteFile): bool {.borrow.}
proc dirExists*(x: AbsoluteDir): bool {.borrow.}
proc quoteShell*(x: AbsoluteFile): string {.borrow.}
proc quoteShell*(x: AbsoluteDir): string {.borrow.}
proc cmpPaths*(x, y: AbsoluteDir): int {.borrow.}
proc createDir*(x: AbsoluteDir) {.borrow.}
type
PathIter = object
i, prev: int
notFirst: bool
proc hasNext(it: PathIter; x: string): bool =
it.i < x.len
proc next(it: var PathIter; x: string): (int, int) =
it.prev = it.i
if not it.notFirst and x[it.i] in {DirSep, AltSep}:
# absolute path:
inc it.i
else:
while it.i < x.len and x[it.i] notin {DirSep, AltSep}: inc it.i
if it.i > it.prev:
result = (it.prev, it.i-1)
elif hasNext(it, x):
result = next(it, x)
# skip all separators:
while it.i < x.len and x[it.i] in {DirSep, AltSep}: inc it.i
it.notFirst = true
iterator dirs(x: string): (int, int) =
var it: PathIter
while hasNext(it, x): yield next(it, x)
when false:
iterator dirs(x: string): (int, int) =
var i = 0
var first = true
while i < x.len:
let prev = i
if first and x[i] in {DirSep, AltSep}:
# absolute path:
inc i
else:
while i < x.len and x[i] notin {DirSep, AltSep}: inc i
if i > prev:
yield (prev, i-1)
first = false
# skip all separators:
while i < x.len and x[i] in {DirSep, AltSep}: inc i
proc isDot(x: string; bounds: (int, int)): bool =
bounds[1] == bounds[0] and x[bounds[0]] == '.'
proc isDotDot(x: string; bounds: (int, int)): bool =
bounds[1] == bounds[0] + 1 and x[bounds[0]] == '.' and x[bounds[0]+1] == '.'
proc isSlash(x: string; bounds: (int, int)): bool =
bounds[1] == bounds[0] and x[bounds[0]] in {DirSep, AltSep}
proc canon(x: string; result: var string; state: var int) =
# state: 0th bit set if isAbsolute path. Other bits count
# the number of path components.
for b in dirs(x):
if (state shr 1 == 0) and isSlash(x, b):
result.add DirSep
state = state or 1
elif result.len > (state and 1) and isDotDot(x, b):
var d = result.len
# f/..
while d > (state and 1) and result[d-1] != DirSep:
dec d
setLen(result, d)
elif isDot(x, b):
discard "discard the dot"
else:
if result.len > (state and 1): result.add DirSep
result.add substr(x, b[0], b[1])
inc state, 2
proc canon(x: string): string =
# - Turn multiple slashes into single slashes.
# - Resolve '/foo/../bar' to '/bar'.
# - Remove './' from the path.
result = newStringOfCap(x.len)
var state = 0
canon(x, result, state)
when FileSystemCaseSensitive:
template `!=?`(a, b: char): bool = toLowerAscii(a) != toLowerAscii(b)
else:
template `!=?`(a, b: char): bool = a != b
proc relativeTo(full, base: string; sep = DirSep): string =
if full.len == 0: return ""
var f, b: PathIter
var ff = (0, -1)
var bb = (0, -1) # (int, int)
result = newStringOfCap(full.len)
# skip the common prefix:
while f.hasNext(full) and b.hasNext(base):
ff = next(f, full)
bb = next(b, base)
let diff = ff[1] - ff[0]
if diff != bb[1] - bb[0]: break
var same = true
for i in 0..diff:
if full[i + ff[0]] !=? base[i + bb[0]]:
same = false
break
if not same: break
ff = (0, -1)
bb = (0, -1)
# for i in 0..diff:
# result.add base[i + bb[0]]
# /foo/bar/xxx/ -- base
# /foo/bar/baz -- full path
# ../baz
# every directory that is in 'base', needs to add '..'
while true:
if bb[1] >= bb[0]:
if result.len > 0 and result[^1] != sep:
result.add sep
result.add ".."
if not b.hasNext(base): break
bb = b.next(base)
# add the rest of 'full':
while true:
if ff[1] >= ff[0]:
if result.len > 0 and result[^1] != sep:
result.add sep
for i in 0..ff[1] - ff[0]:
result.add full[i + ff[0]]
if not f.hasNext(full): break
ff = f.next(full)
when true:
proc eqImpl(x, y: string): bool =
when FileSystemCaseSensitive:
result = toLowerAscii(canon x) == toLowerAscii(canon y)
else:
result = canon(x) == canon(y)
proc `==`*(x, y: AbsoluteFile): bool = eqImpl(x.string, y.string)
proc `==`*(x, y: AbsoluteDir): bool = eqImpl(x.string, y.string)
proc `==`*(x, y: RelativeFile): bool = eqImpl(x.string, y.string)
proc `==`*(x, y: RelativeDir): bool = eqImpl(x.string, y.string)
proc `/`*(base: AbsoluteDir; f: RelativeFile): AbsoluteFile =
assert isAbsolute(base.string)
assert(not isAbsolute(f.string))
result = AbsoluteFile newStringOfCap(base.string.len + f.string.len)
var state = 0
canon(base.string, result.string, state)
canon(f.string, result.string, state)
proc `/`*(base: AbsoluteDir; f: RelativeDir): AbsoluteDir =
assert isAbsolute(base.string)
assert(not isAbsolute(f.string))
result = AbsoluteDir newStringOfCap(base.string.len + f.string.len)
var state = 0
canon(base.string, result.string, state)
canon(f.string, result.string, state)
proc relativeTo*(fullPath: AbsoluteFile, baseFilename: AbsoluteDir;
sep = DirSep): RelativeFile =
RelativeFile(relativeTo(fullPath.string, baseFilename.string, sep))
proc toAbsolute*(file: string; base: AbsoluteDir): AbsoluteFile =
if isAbsolute(file): result = AbsoluteFile(file)
else: result = base / RelativeFile file
proc changeFileExt*(x: AbsoluteFile; ext: string): AbsoluteFile {.borrow.}
proc changeFileExt*(x: RelativeFile; ext: string): RelativeFile {.borrow.}
proc addFileExt*(x: AbsoluteFile; ext: string): AbsoluteFile {.borrow.}
proc addFileExt*(x: RelativeFile; ext: string): RelativeFile {.borrow.}
proc writeFile*(x: AbsoluteFile; content: string) {.borrow.}
when isMainModule and defined(posix):
doAssert canon"/foo/../bar" == "/bar"
doAssert canon"foo/../bar" == "bar"
doAssert canon"/f/../bar///" == "/bar"
doAssert canon"f/..////bar" == "bar"
doAssert canon"../bar" == "../bar"
doAssert canon"/../bar" == "/../bar"
doAssert canon("foo/../../bar/") == "../bar"
doAssert canon("./bla/blob/") == "bla/blob"
doAssert canon(".hiddenFile") == ".hiddenFile"
doAssert canon("./bla/../../blob/./zoo.nim") == "../blob/zoo.nim"
doAssert canon("C:/file/to/this/long") == "C:/file/to/this/long"
doAssert canon("") == ""
doAssert canon("foobar") == "foobar"
doAssert canon("f/////////") == "f"
doAssert relativeTo("/foo/bar//baz.nim", "/foo") == "bar/baz.nim"
doAssert relativeTo("/Users/me/bar/z.nim", "/Users/other/bad") == "../../me/bar/z.nim"
doAssert relativeTo("/Users/me/bar/z.nim", "/Users/other") == "../me/bar/z.nim"
doAssert relativeTo("/Users///me/bar//z.nim", "//Users/") == "me/bar/z.nim"
doAssert relativeTo("/Users/me/bar/z.nim", "/Users/me") == "bar/z.nim"
doAssert relativeTo("", "/users/moo") == ""
doAssert relativeTo("foo", "") == "foo"
echo string(AbsoluteDir"/Users/me///" / RelativeFile"z.nim")

View file

@ -12,7 +12,7 @@
import import
os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer, os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees, wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees,
types, lookups, lineinfos types, lookups, lineinfos, pathutils
const const
FirstCallConv* = wNimcall FirstCallConv* = wNimcall
@ -442,26 +442,22 @@ proc processUndef(c: PContext, n: PNode) =
else: else:
invalidPragma(c, n) invalidPragma(c, n)
type proc relativeFile(c: PContext; n: PNode; ext=""): AbsoluteFile =
TLinkFeature = enum
linkNormal, linkSys
proc relativeFile(c: PContext; n: PNode; ext=""): string =
var s = expectStrLit(c, n) var s = expectStrLit(c, n)
if ext.len > 0 and splitFile(s).ext == "": if ext.len > 0 and splitFile(s).ext == "":
s = addFileExt(s, ext) s = addFileExt(s, ext)
result = parentDir(toFullPath(c.config, n.info)) / s result = AbsoluteFile parentDir(toFullPath(c.config, n.info)) / s
if not fileExists(result): if not fileExists(result):
if isAbsolute(s): result = s if isAbsolute(s): result = AbsoluteFile s
else: else:
result = findFile(c.config, s) result = findFile(c.config, s)
if result.len == 0: result = s if result.isEmpty: result = AbsoluteFile s
proc processCompile(c: PContext, n: PNode) = proc processCompile(c: PContext, n: PNode) =
proc docompile(c: PContext; it: PNode; src, dest: string) = proc docompile(c: PContext; it: PNode; src, dest: AbsoluteFile) =
var cf = Cfile(cname: src, obj: dest, flags: {CfileFlag.External}) var cf = Cfile(cname: src, obj: dest, flags: {CfileFlag.External})
extccomp.addExternalFileToCompile(c.config, cf) extccomp.addExternalFileToCompile(c.config, cf)
recordPragma(c, it, "compile", src, dest) recordPragma(c, it, "compile", src.string, dest.string)
proc getStrLit(c: PContext, n: PNode; i: int): string = proc getStrLit(c: PContext, n: PNode; i: int): string =
n.sons[i] = c.semConstExpr(c, n[i]) n.sons[i] = c.semConstExpr(c, n[i])
@ -478,30 +474,23 @@ proc processCompile(c: PContext, n: PNode) =
let dest = getStrLit(c, it, 1) let dest = getStrLit(c, it, 1)
var found = parentDir(toFullPath(c.config, n.info)) / s var found = parentDir(toFullPath(c.config, n.info)) / s
for f in os.walkFiles(found): for f in os.walkFiles(found):
let obj = completeCFilePath(c.config, dest % extractFilename(f)) let obj = completeCFilePath(c.config, AbsoluteFile(dest % extractFilename(f)))
docompile(c, it, f, obj) docompile(c, it, AbsoluteFile f, obj)
else: else:
let s = expectStrLit(c, n) let s = expectStrLit(c, n)
var found = parentDir(toFullPath(c.config, n.info)) / s var found = AbsoluteFile(parentDir(toFullPath(c.config, n.info)) / s)
if not fileExists(found): if not fileExists(found):
if isAbsolute(s): found = s if isAbsolute(s): found = AbsoluteFile s
else: else:
found = findFile(c.config, s) found = findFile(c.config, s)
if found.len == 0: found = s if found.isEmpty: found = AbsoluteFile s
let obj = toObjFile(c.config, completeCFilePath(c.config, found, false)) let obj = toObjFile(c.config, completeCFilePath(c.config, found, false))
docompile(c, it, found, obj) docompile(c, it, found, obj)
proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) = proc processLink(c: PContext, n: PNode) =
let found = relativeFile(c, n, CC[c.config.cCompiler].objExt) let found = relativeFile(c, n, CC[c.config.cCompiler].objExt)
case feature extccomp.addExternalFileToLink(c.config, found)
of linkNormal: recordPragma(c, n, "link", found.string)
extccomp.addExternalFileToLink(c.config, found)
recordPragma(c, n, "link", found)
of linkSys:
let dest = c.config.libpath / completeCFilePath(c.config, found, false)
extccomp.addExternalFileToLink(c.config, dest)
recordPragma(c, n, "link", dest)
else: internalError(c.config, n.info, "processCommonLink")
proc pragmaBreakpoint(c: PContext, n: PNode) = proc pragmaBreakpoint(c: PContext, n: PNode) =
discard getOptionalStr(c, n, "") discard getOptionalStr(c, n, "")
@ -594,8 +583,9 @@ proc pragmaLine(c: PContext, n: PNode) =
elif y.kind != nkIntLit: elif y.kind != nkIntLit:
localError(c.config, n.info, errIntLiteralExpected) localError(c.config, n.info, errIntLiteralExpected)
else: else:
# XXX this produces weird paths which are not properly resolved: # XXX check if paths are properly resolved this way:
n.info.fileIndex = msgs.fileInfoIdx(c.config, x.strVal) let dir = toFullPath(c.config, n.info).splitFile.dir
n.info.fileIndex = fileInfoIdx(c.config, AbsoluteDir(dir) / RelativeFile(x.strVal))
n.info.line = uint16(y.intVal) n.info.line = uint16(y.intVal)
else: else:
localError(c.config, n.info, "tuple expected") localError(c.config, n.info, "tuple expected")
@ -959,8 +949,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
of wDefine: processDefine(c, it) of wDefine: processDefine(c, it)
of wUndef: processUndef(c, it) of wUndef: processUndef(c, it)
of wCompile: processCompile(c, it) of wCompile: processCompile(c, it)
of wLink: processCommonLink(c, it, linkNormal) of wLink: processLink(c, it)
of wLinksys: processCommonLink(c, it, linkSys)
of wPassl: of wPassl:
let s = expectStrLit(c, it) let s = expectStrLit(c, it)
extccomp.addLinkOption(c.config, s) extccomp.addLinkOption(c.config, s)

View file

@ -15,11 +15,12 @@ import
type type
TRenderFlag* = enum TRenderFlag* = enum
renderNone, renderNoBody, renderNoComments, renderDocComments, renderNone, renderNoBody, renderNoComments, renderDocComments,
renderNoPragmas, renderIds, renderNoProcDefs renderNoPragmas, renderIds, renderNoProcDefs, renderSyms
TRenderFlags* = set[TRenderFlag] TRenderFlags* = set[TRenderFlag]
TRenderTok* = object TRenderTok* = object
kind*: TTokType kind*: TTokType
length*: int16 length*: int16
sym*: PSym
TRenderTokSeq* = seq[TRenderTok] TRenderTokSeq* = seq[TRenderTok]
TSrcGen* = object TSrcGen* = object
@ -105,11 +106,12 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) =
g.inGenericParams = false g.inGenericParams = false
g.config = config g.config = config
proc addTok(g: var TSrcGen, kind: TTokType, s: string) = proc addTok(g: var TSrcGen, kind: TTokType, s: string; sym: PSym = nil) =
var length = len(g.tokens) var length = len(g.tokens)
setLen(g.tokens, length + 1) setLen(g.tokens, length + 1)
g.tokens[length].kind = kind g.tokens[length].kind = kind
g.tokens[length].length = int16(len(s)) g.tokens[length].length = int16(len(s))
g.tokens[length].sym = sym
add(g.buf, s) add(g.buf, s)
proc addPendingNL(g: var TSrcGen) = proc addPendingNL(g: var TSrcGen) =
@ -165,11 +167,11 @@ proc dedent(g: var TSrcGen) =
dec(g.pendingNL, IndentWidth) dec(g.pendingNL, IndentWidth)
dec(g.lineLen, IndentWidth) dec(g.lineLen, IndentWidth)
proc put(g: var TSrcGen, kind: TTokType, s: string) = proc put(g: var TSrcGen, kind: TTokType, s: string; sym: PSym = nil) =
if kind != tkSpaces: if kind != tkSpaces:
addPendingNL(g) addPendingNL(g)
if len(s) > 0: if len(s) > 0:
addTok(g, kind, s) addTok(g, kind, s, sym)
inc(g.lineLen, len(s)) inc(g.lineLen, len(s))
else: else:
g.pendingWhitespace = s.len g.pendingWhitespace = s.len
@ -831,7 +833,7 @@ proc gident(g: var TSrcGen, n: PNode) =
t = tkSymbol t = tkSymbol
else: else:
t = tkOpr t = tkOpr
put(g, t, s) put(g, t, s, if n.kind == nkSym and renderSyms in g.flags: n.sym else: nil)
if n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags): if n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags):
when defined(debugMagics): when defined(debugMagics):
put(g, tkIntLit, $n.sym.id & $n.sym.magic) put(g, tkIntLit, $n.sym.id & $n.sym.magic)
@ -1498,3 +1500,9 @@ proc getNextTok*(r: var TSrcGen, kind: var TTokType, literal: var string) =
inc(r.idx) inc(r.idx)
else: else:
kind = tkEof kind = tkEof
proc getTokSym*(r: TSrcGen): PSym =
if r.idx > 0 and r.idx <= len(r.tokens):
result = r.tokens[r.idx-1].sym
else:
result = nil

View file

@ -11,7 +11,7 @@
import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types, import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types,
renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp, renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp,
btrees, trees, condsyms, nversion btrees, trees, condsyms, nversion, pathutils
## Todo: ## Todo:
## - Dependency computation should use *signature* hashes in order to ## - Dependency computation should use *signature* hashes in order to
@ -796,7 +796,7 @@ proc replay(g: ModuleGraph; module: PSym; n: PNode) =
flags: {CfileFlag.External}) flags: {CfileFlag.External})
extccomp.addExternalFileToCompile(g.config, cf) extccomp.addExternalFileToCompile(g.config, cf)
of "link": of "link":
extccomp.addExternalFileToLink(g.config, n[1].strVal) extccomp.addExternalFileToLink(g.config, AbsoluteFile n[1].strVal)
of "passl": of "passl":
extccomp.addLinkOption(g.config, n[1].strVal) extccomp.addLinkOption(g.config, n[1].strVal)
of "passc": of "passc":

View file

@ -58,6 +58,8 @@
import import
hashes hashes
from pathutils import AbsoluteFile
type type
FormatStr* = string # later we may change it to CString for better FormatStr* = string # later we may change it to CString for better
# performance of the code generator (assignments # performance of the code generator (assignments
@ -183,9 +185,9 @@ proc writeRope*(f: File, r: Rope) =
## writes a rope to a file. ## writes a rope to a file.
for s in leaves(r): write(f, s) for s in leaves(r): write(f, s)
proc writeRope*(head: Rope, filename: string): bool = proc writeRope*(head: Rope, filename: AbsoluteFile): bool =
var f: File var f: File
if open(f, filename, fmWrite): if open(f, filename.string, fmWrite):
if head != nil: writeRope(f, head) if head != nil: writeRope(f, head)
close(f) close(f)
result = true result = true
@ -314,16 +316,16 @@ proc equalsFile*(r: Rope, f: File): bool =
result = readBuffer(f, addr(buf[0]), 1) == 0 and result = readBuffer(f, addr(buf[0]), 1) == 0 and
btotal == rtotal # check that we've read all btotal == rtotal # check that we've read all
proc equalsFile*(r: Rope, filename: string): bool = proc equalsFile*(r: Rope, filename: AbsoluteFile): bool =
## returns true if the contents of the file `f` equal `r`. If `f` does not ## returns true if the contents of the file `f` equal `r`. If `f` does not
## exist, false is returned. ## exist, false is returned.
var f: File var f: File
result = open(f, filename) result = open(f, filename.string)
if result: if result:
result = equalsFile(r, f) result = equalsFile(r, f)
close(f) close(f)
proc writeRopeIfNotEqual*(r: Rope, filename: string): bool = proc writeRopeIfNotEqual*(r: Rope, filename: AbsoluteFile): bool =
# returns true if overwritten # returns true if overwritten
if not equalsFile(r, filename): if not equalsFile(r, filename):
result = writeRope(r, filename) result = writeRope(r, filename)

View file

@ -13,7 +13,7 @@
import import
ast, modules, idents, passes, passaux, condsyms, ast, modules, idents, passes, passaux, condsyms,
options, nimconf, sem, semdata, llstream, vm, vmdef, commands, msgs, options, nimconf, sem, semdata, llstream, vm, vmdef, commands, msgs,
os, times, osproc, wordrecg, strtabs, modulegraphs, lineinfos os, times, osproc, wordrecg, strtabs, modulegraphs, lineinfos, pathutils
# we support 'cmpIgnoreStyle' natively for efficiency: # we support 'cmpIgnoreStyle' natively for efficiency:
from strutils import cmpIgnoreStyle, contains from strutils import cmpIgnoreStyle, contains
@ -105,7 +105,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
cbconf exists: cbconf exists:
setResult(a, options.existsConfigVar(conf, a.getString 0)) setResult(a, options.existsConfigVar(conf, a.getString 0))
cbconf nimcacheDir: cbconf nimcacheDir:
setResult(a, options.getNimcacheDir(conf)) setResult(a, options.getNimcacheDir(conf).string)
cbconf paramStr: cbconf paramStr:
setResult(a, os.paramStr(int a.getInt 0)) setResult(a, os.paramStr(int a.getInt 0))
cbconf paramCount: cbconf paramCount:
@ -120,8 +120,8 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
if arg.len > 0: if arg.len > 0:
conf.projectName = arg conf.projectName = arg
let path = let path =
if conf.projectName.isAbsolute: conf.projectName if conf.projectName.isAbsolute: AbsoluteFile(conf.projectName)
else: conf.projectPath / conf.projectName else: conf.projectPath / RelativeFile(conf.projectName)
try: try:
conf.projectFull = canonicalizePath(conf, path) conf.projectFull = canonicalizePath(conf, path)
except OSError: except OSError:
@ -149,9 +149,9 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
cbconf cppDefine: cbconf cppDefine:
options.cppDefine(conf, a.getString(0)) options.cppDefine(conf, a.getString(0))
proc runNimScript*(cache: IdentCache; scriptName: string; proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
freshDefines=true; conf: ConfigRef) = freshDefines=true; conf: ConfigRef) =
rawMessage(conf, hintConf, scriptName) rawMessage(conf, hintConf, scriptName.string)
let graph = newModuleGraph(cache, conf) let graph = newModuleGraph(cache, conf)
connectCallbacks(graph) connectCallbacks(graph)
@ -169,7 +169,7 @@ proc runNimScript*(cache: IdentCache; scriptName: string;
var m = graph.makeModule(scriptName) var m = graph.makeModule(scriptName)
incl(m.flags, sfMainModule) incl(m.flags, sfMainModule)
graph.vm = setupVM(m, cache, scriptName, graph) graph.vm = setupVM(m, cache, scriptName.string, graph)
graph.compileSystemModule() # TODO: see why this unsets hintConf in conf.notes graph.compileSystemModule() # TODO: see why this unsets hintConf in conf.notes
discard graph.processModule(m, llStreamOpen(scriptName, fmRead)) discard graph.processModule(m, llStreamOpen(scriptName, fmRead))

View file

@ -32,7 +32,7 @@
# included from sigmatch.nim # included from sigmatch.nim
import algorithm, prefixmatches, lineinfos import algorithm, prefixmatches, lineinfos, pathutils
from wordrecg import wDeprecated from wordrecg import wDeprecated
when defined(nimsuggest): when defined(nimsuggest):
@ -319,7 +319,7 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
if n.kind == nkSym and n.sym.kind == skError and c.config.suggestVersion == 0: if n.kind == nkSym and n.sym.kind == skError and c.config.suggestVersion == 0:
# consider 'foo.|' where 'foo' is some not imported module. # consider 'foo.|' where 'foo' is some not imported module.
let fullPath = findModule(c.config, n.sym.name.s, toFullPath(c.config, n.info)) let fullPath = findModule(c.config, n.sym.name.s, toFullPath(c.config, n.info))
if fullPath.len == 0: if fullPath.isEmpty:
# error: no known module name: # error: no known module name:
typ = nil typ = nil
else: else:

View file

@ -11,7 +11,7 @@
import import
strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser, strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser,
filters, filter_tmpl, renderer, lineinfos filters, filter_tmpl, renderer, lineinfos, pathutils
type type
TFilterKind* = enum TFilterKind* = enum
@ -58,7 +58,7 @@ proc containsShebang(s: string, i: int): bool =
while j < s.len and s[j] in Whitespace: inc(j) while j < s.len and s[j] in Whitespace: inc(j)
result = s[j] == '/' result = s[j] == '/'
proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache; proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache;
config: ConfigRef): PNode = config: ConfigRef): PNode =
result = newNode(nkEmpty) result = newNode(nkEmpty)
var s = llStreamOpen(filename, fmRead) var s = llStreamOpen(filename, fmRead)
@ -100,7 +100,7 @@ proc getCallee(conf: ConfigRef; n: PNode): PIdent =
else: else:
localError(conf, n.info, "invalid filter: " & renderTree(n)) localError(conf, n.info, "invalid filter: " & renderTree(n))
proc applyFilter(p: var TParsers, n: PNode, filename: string, proc applyFilter(p: var TParsers, n: PNode, filename: AbsoluteFile,
stdin: PLLStream): PLLStream = stdin: PLLStream): PLLStream =
var ident = getCallee(p.config, n) var ident = getCallee(p.config, n)
var f = getFilter(ident) var f = getFilter(ident)
@ -121,7 +121,7 @@ proc applyFilter(p: var TParsers, n: PNode, filename: string,
msgWriteln(p.config, result.s) msgWriteln(p.config, result.s)
rawMessage(p.config, hintCodeEnd, []) rawMessage(p.config, hintCodeEnd, [])
proc evalPipe(p: var TParsers, n: PNode, filename: string, proc evalPipe(p: var TParsers, n: PNode, filename: AbsoluteFile,
start: PLLStream): PLLStream = start: PLLStream): PLLStream =
assert p.config != nil assert p.config != nil
result = start result = start
@ -161,8 +161,8 @@ proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode
p: TParsers p: TParsers
f: File f: File
let filename = toFullPathConsiderDirty(config, fileIdx) let filename = toFullPathConsiderDirty(config, fileIdx)
if not open(f, filename): if not open(f, filename.string):
rawMessage(config, errGenerated, "cannot open file: " & filename) rawMessage(config, errGenerated, "cannot open file: " & filename.string)
return return
openParsers(p, fileIdx, llStreamOpen(f), cache, config) openParsers(p, fileIdx, llStreamOpen(f), cache, config)
result = parseAll(p) result = parseAll(p)

View file

@ -13,7 +13,7 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): str
try: try:
var filename = parentDir(toFullPath(conf, info)) / file var filename = parentDir(toFullPath(conf, info)) / file
if not fileExists(filename): if not fileExists(filename):
filename = findFile(conf, file) filename = findFile(conf, file).string
result = readFile(filename) result = readFile(filename)
# we produce a fake include statement for every slurped filename, so that # we produce a fake include statement for every slurped filename, so that
# the module dependencies are accurate: # the module dependencies are accurate:

View file

@ -7,6 +7,8 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
import pathutils
template setX(k, field) {.dirty.} = template setX(k, field) {.dirty.} =
var s: seq[TFullReg] var s: seq[TFullReg]
move(s, cast[seq[TFullReg]](a.slots)) move(s, cast[seq[TFullReg]](a.slots))
@ -38,6 +40,8 @@ proc setResult*(a: VmArgs; n: PNode) =
s[a.ra].kind = rkNode s[a.ra].kind = rkNode
s[a.ra].node = n s[a.ra].node = n
proc setResult*(a: VmArgs; v: AbsoluteDir) = setResult(a, v.string)
proc setResult*(a: VmArgs; v: seq[string]) = proc setResult*(a: VmArgs; v: seq[string]) =
var s: seq[TFullReg] var s: seq[TFullReg]
move(s, cast[seq[TFullReg]](a.slots)) move(s, cast[seq[TFullReg]](a.slots))

View file

@ -82,7 +82,7 @@ proc registerAdditionalOps*(c: PCtx) =
setResult a, newTree(nkTupleConstr, newStrNode(nkStrLit, s), newIntNode(nkIntLit, e)) setResult a, newTree(nkTupleConstr, newStrNode(nkStrLit, s), newIntNode(nkIntLit, e))
proc getProjectPathWrapper(a: VmArgs) = proc getProjectPathWrapper(a: VmArgs) =
setResult a, c.config.projectPath setResult a, c.config.projectPath.string
wrap1f_math(sqrt) wrap1f_math(sqrt)
wrap1f_math(ln) wrap1f_math(ln)

View file

@ -20,7 +20,8 @@ import compiler / [options, commands, modules, sem,
passes, passaux, msgs, nimconf, passes, passaux, msgs, nimconf,
extccomp, condsyms, extccomp, condsyms,
sigmatch, ast, scriptconfig, sigmatch, ast, scriptconfig,
idents, modulegraphs, vm, prefixmatches, lineinfos, cmdlinehelper] idents, modulegraphs, vm, prefixmatches, lineinfos, cmdlinehelper,
pathutils]
when defined(windows): when defined(windows):
import winlean import winlean
@ -158,10 +159,11 @@ proc symFromInfo(graph: ModuleGraph; trackPos: TLineInfo): PSym =
if m != nil and m.ast != nil: if m != nil and m.ast != nil:
result = findNode(m.ast, trackPos) result = findNode(m.ast, trackPos)
proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int; proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
graph: ModuleGraph) = graph: ModuleGraph) =
let conf = graph.config let conf = graph.config
myLog("cmd: " & $cmd & ", file: " & file & ", dirtyFile: " & dirtyfile & myLog("cmd: " & $cmd & ", file: " & file.string &
", dirtyFile: " & dirtyfile.string &
"[" & $line & ":" & $col & "]") "[" & $line & ":" & $col & "]")
conf.ideCmd = cmd conf.ideCmd = cmd
if cmd == ideChk: if cmd == ideChk:
@ -175,8 +177,8 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
var isKnownFile = true var isKnownFile = true
let dirtyIdx = fileInfoIdx(conf, file, isKnownFile) let dirtyIdx = fileInfoIdx(conf, file, isKnownFile)
if dirtyfile.len != 0: msgs.setDirtyFile(conf, dirtyIdx, dirtyfile) if not dirtyfile.isEmpty: msgs.setDirtyFile(conf, dirtyIdx, dirtyfile)
else: msgs.setDirtyFile(conf, dirtyIdx, "") else: msgs.setDirtyFile(conf, dirtyIdx, AbsoluteFile"")
conf.m.trackPos = newLineInfo(dirtyIdx, line, col) conf.m.trackPos = newLineInfo(dirtyIdx, line, col)
conf.m.trackPosAttached = false conf.m.trackPosAttached = false
@ -186,7 +188,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
if not isKnownFile: if not isKnownFile:
graph.compileProject() graph.compileProject()
if conf.suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and if conf.suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and
dirtyfile.len == 0: dirtyfile.isEmpty:
discard "no need to recompile anything" discard "no need to recompile anything"
else: else:
let modIdx = graph.parentModule(dirtyIdx) let modIdx = graph.parentModule(dirtyIdx)
@ -204,12 +206,12 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
proc executeEpc(cmd: IdeCmd, args: SexpNode; proc executeEpc(cmd: IdeCmd, args: SexpNode;
graph: ModuleGraph) = graph: ModuleGraph) =
let let
file = args[0].getStr file = AbsoluteFile args[0].getStr
line = args[1].getNum line = args[1].getNum
column = args[2].getNum column = args[2].getNum
var dirtyfile = "" var dirtyfile = AbsoluteFile""
if len(args) > 3: if len(args) > 3:
dirtyfile = args[3].getStr("") dirtyfile = AbsoluteFile args[3].getStr("")
execute(cmd, file, dirtyfile, int(line), int(column), graph) execute(cmd, file, dirtyfile, int(line), int(column), graph)
proc returnEpc(socket: Socket, uid: BiggestInt, s: SexpNode|string, proc returnEpc(socket: Socket, uid: BiggestInt, s: SexpNode|string,
@ -431,11 +433,11 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
i += parseInt(cmd, col, i) i += parseInt(cmd, col, i)
if conf.ideCmd == ideKnown: if conf.ideCmd == ideKnown:
results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, orig)))) results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, AbsoluteFile orig))))
else: else:
if conf.ideCmd == ideChk: if conf.ideCmd == ideChk:
for cm in cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev) for cm in cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev)
execute(conf.ideCmd, orig, dirtyfile, line, col, graph) execute(conf.ideCmd, AbsoluteFile orig, AbsoluteFile dirtyfile, line, col, graph)
sentinel() sentinel()
proc recompileFullProject(graph: ModuleGraph) = proc recompileFullProject(graph: ModuleGraph) =
@ -451,7 +453,7 @@ proc mainThread(graph: ModuleGraph) =
let conf = graph.config let conf = graph.config
if gLogging: if gLogging:
for it in conf.searchPaths: for it in conf.searchPaths:
log(it) log(it.string)
proc wrHook(line: string) {.closure.} = proc wrHook(line: string) {.closure.} =
if gMode == mepc: if gMode == mepc:
@ -496,7 +498,7 @@ proc mainCommand(graph: ModuleGraph) =
wantMainModule(conf) wantMainModule(conf)
if not fileExists(conf.projectFull): if not fileExists(conf.projectFull):
quit "cannot find file: " & conf.projectFull quit "cannot find file: " & conf.projectFull.string
add(conf.searchPaths, conf.libpath) add(conf.searchPaths, conf.libpath)
@ -518,9 +520,9 @@ proc mainCommand(graph: ModuleGraph) =
of mtcp: createThread(inputThread, replTcp, (gPort, gAddress)) of mtcp: createThread(inputThread, replTcp, (gPort, gAddress))
of mepc: createThread(inputThread, replEpc, (gPort, gAddress)) of mepc: createThread(inputThread, replEpc, (gPort, gAddress))
of mcmdsug: createThread(inputThread, replCmdline, of mcmdsug: createThread(inputThread, replCmdline,
(gPort, "sug \"" & conf.projectFull & "\":" & gAddress)) (gPort, "sug \"" & conf.projectFull.string & "\":" & gAddress))
of mcmdcon: createThread(inputThread, replCmdline, of mcmdcon: createThread(inputThread, replCmdline,
(gPort, "con \"" & conf.projectFull & "\":" & gAddress)) (gPort, "con \"" & conf.projectFull.string & "\":" & gAddress))
mainThread(graph) mainThread(graph)
joinThread(inputThread) joinThread(inputThread)
close(requests) close(requests)
@ -602,11 +604,12 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
if binaryPath == "": if binaryPath == "":
raise newException(IOError, raise newException(IOError,
"Cannot find Nim standard library: Nim compiler not in PATH") "Cannot find Nim standard library: Nim compiler not in PATH")
conf.prefixDir = binaryPath.splitPath().head.parentDir() conf.prefixDir = AbsoluteDir binaryPath.splitPath().head.parentDir()
if not dirExists(conf.prefixDir / "lib"): conf.prefixDir = "" if not dirExists(conf.prefixDir / RelativeDir"lib"):
conf.prefixDir = AbsoluteDir""
#msgs.writelnHook = proc (line: string) = log(line) #msgs.writelnHook = proc (line: string) = log(line)
myLog("START " & conf.projectFull) myLog("START " & conf.projectFull.string)
discard self.loadConfigsAndRunMainCommand(cache, conf) discard self.loadConfigsAndRunMainCommand(cache, conf)