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
committed by Araq
parent b5730ec01f
commit 86556ebfdb
45 changed files with 816 additions and 504 deletions

View File

@@ -12,7 +12,7 @@
import
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
# be part of C string literals.
@@ -226,7 +226,7 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
when not defined(nimhygiene):
{.pragma: inject.}
template withCFile(cfilename: string, body: untyped) =
template withCFile(cfilename: AbsoluteFile, body: untyped) =
var s = llStreamOpen(cfilename, fmRead)
if s == nil: return
var L {.inject.}: TBaseLexer
@@ -238,7 +238,7 @@ template withCFile(cfilename: string, body: untyped) =
body
closeBaseLexer(L)
proc readMergeInfo*(cfilename: string, m: BModule) =
proc readMergeInfo*(cfilename: AbsoluteFile, m: BModule) =
## reads the merge meta information into `m`.
withCFile(cfilename):
readKey(L, k)
@@ -251,7 +251,7 @@ type
f: TCFileSections
p: TCProcSections
proc readMergeSections(cfilename: string, m: var TMergeSections) =
proc readMergeSections(cfilename: AbsoluteFile, m: var TMergeSections) =
## reads the merge sections into `m`.
withCFile(cfilename):
readKey(L, k)
@@ -285,7 +285,7 @@ proc mergeRequired*(m: BModule): bool =
#echo "not empty: ", i, " ", m.initProc.s[i]
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.
var old: TMergeSections
readMergeSections(cfilename, old)

View File

@@ -14,7 +14,7 @@ import
nversion, nimsets, msgs, std / sha1, bitsets, idents, types,
ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth,
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.`%`
@@ -1064,7 +1064,8 @@ proc genFilenames(m: BModule): Rope =
discard cgsym(m, "dbgRegisterFilename")
result = nil
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) =
const
@@ -1348,7 +1349,7 @@ proc initProcOptions(m: BModule): TOptions =
let opts = m.config.options
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)
result.g = g
result.tmpBase = rope("TM" & $hashOwner(module) & "_")
@@ -1376,7 +1377,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
incl result.flags, preventStackTrace
excl(result.preInitProc.options, optStackTrace)
let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi")
else: ""
else: AbsoluteFile""
open(result.ndi, ndiName, g.config)
proc nullify[T](arr: var T) =
@@ -1427,7 +1428,7 @@ proc resetCgenModules*(g: BModuleList) =
for m in cgenModules(g): resetModule(m)
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 =
# we should create only one cgen module for each module sym
@@ -1446,7 +1447,7 @@ proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
injectG()
result = newModule(g, module, graph.config)
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
g.generatedHeader = rawNewModule(g, module,
changeFileExt(completeCFilePath(graph.config, f), hExt))
@@ -1477,9 +1478,9 @@ proc writeHeader(m: BModule) =
if optUseNimNamespace in m.config.globalOptions: result.add closeNamespaceNim()
result.addf("#endif /* $1 */$n", [guard])
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 =
if m.compileToCpp: ".cpp"
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 isDefined(m.config, "nimdiff"):
if fileExists(cfile.cname):
copyFile(cfile.cname, cfile.cname & ".backup")
echo "diff ", cfile.cname, ".backup ", cfile.cname
copyFile(cfile.cname.string, cfile.cname.string & ".backup")
echo "diff ", cfile.cname.string, ".backup ", cfile.cname.string
else:
echo "new file ", cfile.cname
echo "new file ", cfile.cname.string
if not writeRope(code, cfile.cname):
rawMessage(m.config, errCannotOpenFile, cfile.cname)
rawMessage(m.config, errCannotOpenFile, cfile.cname.string)
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
else:
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
# 'nim__dat') may require file merging for the combination of dead code
@@ -1570,14 +1571,14 @@ proc writeModule(m: BModule, pending: bool) =
finishTypeDescriptions(m)
var code = genModule(m, cf)
if not writeRope(code, cfile):
rawMessage(m.config, errCannotOpenFile, cfile)
rawMessage(m.config, errCannotOpenFile, cfile.string)
addFileToCompile(m.config, cf)
else:
# Consider: first compilation compiles ``system.nim`` and produces
# ``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:
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)
close(m.ndi)
@@ -1592,7 +1593,7 @@ proc updateCachedModule(m: BModule) =
var code = genModule(m, cf)
if not writeRope(code, cfile):
rawMessage(m.config, errCannotOpenFile, cfile)
rawMessage(m.config, errCannotOpenFile, cfile.string)
else:
cf.flags = {CfileFlag.Cached}
addFileToCompile(m.config, cf)

View File

@@ -11,7 +11,7 @@
import
ast, astalgo, ropes, passes, options, intsets, platform, sighashes,
tables, ndi, lineinfos
tables, ndi, lineinfos, pathutils
from modulegraphs import ModuleGraph
@@ -136,8 +136,8 @@ type
s*: TCFileSections # sections of the C file
flags*: set[Codegenflag]
module*: PSym
filename*: string
cfilename*: string # filename of the module (including path,
filename*: AbsoluteFile
cfilename*: AbsoluteFile # filename of the module (including path,
# without extension)
tmpBase*: Rope # base for temp identifier generation
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
# TODO: nimfix should use this; currently out of sync
import
compiler/[options, idents, nimconf, scriptconfig, extccomp, commands, msgs, lineinfos, modulegraphs, condsyms],
std/os
options, idents, nimconf, scriptconfig, extccomp, commands, msgs,
lineinfos, modulegraphs, condsyms, os, pathutils
type
NimProg* = ref object
@@ -21,27 +28,27 @@ proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) =
self.processCmdLine(passCmd1, "", conf)
if self.supportsStdinFile and conf.projectName == "-":
conf.projectName = "stdinfile"
conf.projectFull = "stdinfile"
conf.projectPath = canonicalizePath(conf, getCurrentDir())
conf.projectFull = AbsoluteFile "stdinfile"
conf.projectPath = AbsoluteDir getCurrentDir()
conf.projectIsStdin = true
elif conf.projectName != "":
try:
conf.projectFull = canonicalizePath(conf, conf.projectName)
conf.projectFull = canonicalizePath(conf, AbsoluteFile conf.projectName)
except OSError:
conf.projectFull = conf.projectName
conf.projectFull = AbsoluteFile conf.projectName
let p = splitFile(conf.projectFull)
let dir = if p.dir.len > 0: p.dir else: getCurrentDir()
conf.projectPath = canonicalizePath(conf, dir)
let dir = if p.dir.isEmpty: AbsoluteDir getCurrentDir() else: p.dir
conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile dir)
conf.projectName = p.name
else:
conf.projectPath = canonicalizePath(conf, getCurrentDir())
conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile getCurrentDir())
proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: ConfigRef): bool =
loadConfigs(DefaultConfig, cache, conf) # load all config files
if self.suggestMode:
conf.command = "nimsuggest"
proc runNimScriptIfExists(path: string)=
proc runNimScriptIfExists(path: AbsoluteFile)=
if fileExists(path):
runNimScript(cache, path, freshDefines = false, conf)
@@ -53,8 +60,8 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
runNimScriptIfExists(getUserConfigPath(DefaultConfigNims))
if optSkipParentConfigFiles notin conf.globalOptions:
for dir in parentDirs(conf.projectPath, fromRoot = true, inclusive = false):
runNimScriptIfExists(dir / DefaultConfigNims)
for dir in parentDirs(conf.projectPath.string, fromRoot = true, inclusive = false):
runNimScriptIfExists(AbsoluteDir(dir) / DefaultConfigNims)
if optSkipProjConfigFile notin conf.globalOptions:
runNimScriptIfExists(conf.projectPath / DefaultConfigNims)
@@ -63,10 +70,10 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
if not self.suggestMode:
runNimScriptIfExists(scriptFile)
# '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
else:
if scriptFile.cmpPaths(conf.projectFull) != 0:
if scriptFile != conf.projectFull:
runNimScriptIfExists(scriptFile)
else:
# 'nimsuggest foo.nims' means to just auto-complete the NimScript file

View File

@@ -26,7 +26,8 @@ bootSwitch(usedNoGC, defined(nogc), "--gc:none")
import
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.
bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
@@ -208,7 +209,7 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
proc processCompile(conf: ConfigRef; filename: string) =
var found = findFile(conf, filename)
if found == "": found = filename
if found.isEmpty: found = AbsoluteFile filename
extccomp.addExternalFileToCompile(conf, found)
const
@@ -292,31 +293,32 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
else: invalidCmdLineOption(conf, passCmd1, switch, info)
proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
notRelativeToProj = false): string =
notRelativeToProj = false): AbsoluteDir =
let p = if os.isAbsolute(path) or '$' in path:
path
elif notRelativeToProj:
getCurrentDir() / path
else:
conf.projectPath / path
conf.projectPath.string / path
try:
result = pathSubs(conf, p, toFullPath(conf, info).splitFile().dir)
result = AbsoluteDir pathSubs(conf, p, toFullPath(conf, info).splitFile().dir)
except ValueError:
localError(conf, info, "invalid path: " & p)
result = p
result = AbsoluteDir p
proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): string =
let path = if path[0] == '"': strutils.unescape(path) else: path
proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): AbsoluteDir =
let path = if path.len > 0 and path[0] == '"': strutils.unescape(path)
else: path
let basedir = toFullPath(conf, info).splitFile().dir
let p = if os.isAbsolute(path) or '$' in path:
path
else:
basedir / path
try:
result = pathSubs(conf, p, basedir)
result = AbsoluteDir pathSubs(conf, p, basedir)
except ValueError:
localError(conf, info, "invalid path: " & p)
result = p
result = AbsoluteDir p
const
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:
localError(conf, info, errInvalidNumber % a[2])
let dirtyOriginalIdx = fileInfoIdx(conf, a[1])
let dirtyOriginalIdx = fileInfoIdx(conf, AbsoluteFile a[1])
if dirtyOriginalIdx.int32 >= 0:
msgs.setDirtyFile(conf, dirtyOriginalIdx, a[0])
msgs.setDirtyFile(conf, dirtyOriginalIdx, AbsoluteFile a[0])
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])
if parseUtils.parseInt(a[2], column) <= 0:
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) =
if pass in {passCmd2, passPP}:
@@ -359,14 +361,16 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
case switch.normalize
of "path", "p":
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":
# keep the old name for compat
if pass in {passCmd2, passPP} and optNoNimblePath notin conf.globalOptions:
expectArg(conf, switch, arg, pass, info)
var path = processPath(conf, arg, info, notRelativeToProj=true)
let nimbleDir = getEnv("NIMBLE_DIR")
if nimbleDir.len > 0 and pass == passPP: path = nimbleDir / "pkgs"
let nimbleDir = AbsoluteDir getEnv("NIMBLE_DIR")
if not nimbleDir.isEmpty and pass == passPP:
path = nimbleDir / RelativeDir"pkgs"
nimblePath(conf, path, info)
of "nonimblepath", "nobabelpath":
expectNoArg(conf, switch, arg, pass, info)
@@ -374,20 +378,14 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "excludepath":
expectArg(conf, switch, arg, pass, info)
let path = processPath(conf, arg, info)
conf.searchPaths.keepItIf(cmpPaths(it, path) != 0)
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)
conf.searchPaths.keepItIf(it != path)
conf.lazyPaths.keepItIf(it != path)
of "nimcache":
expectArg(conf, switch, arg, pass, info)
conf.nimcacheDir = processPath(conf, arg, info, true)
of "out", "o":
expectArg(conf, switch, arg, pass, info)
conf.outFile = arg
conf.outFile = AbsoluteFile arg
of "docseesrcurl":
expectArg(conf, switch, arg, pass, info)
conf.docSeeSrcUrl = arg
@@ -411,7 +409,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
if pass in {passCmd2, passPP}: processCompile(conf, arg)
of "link":
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":
expectNoArg(conf, switch, arg, pass, info)
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)
of "clib":
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":
if conf != nil: conf.headerFile = arg
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)
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) =
var cmd, arg: string

View File

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

View File

@@ -16,7 +16,8 @@ import
wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast,
packages/docutils/rst, packages/docutils/rstgen,
packages/docutils/highlite, sempass2, json, xmltree, cgi,
typesrenderer, astalgo, modulepaths, lineinfos, sequtils, intsets
typesrenderer, astalgo, modulepaths, lineinfos, sequtils, intsets,
pathutils
type
TSections = array[TSymKind, Rope]
@@ -34,6 +35,8 @@ type
exampleCounter: int
emitted: IntSet # we need to track which symbols have been emitted
# already. See bug #3655
destFile*: AbsoluteFile
thisDir*: AbsoluteDir
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 =
let params = p.ast.sons[paramsPos]
# first check the first parameter, then the return type,
# then the other parameter:
template check(i) =
result = whichType(d, params[i])
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 > 0: check(0)
for i in 2..<params.len: check(i)
@@ -74,10 +77,10 @@ template declareClosures =
of mwUnknownSubstitution: k = warnUnknownSubstitutionX
of mwUnsupportedLanguage: k = warnLanguageXNotSupported
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.} =
result = options.findFile(conf, s)
result = options.findFile(conf, s).string
if result.len == 0:
result = getCurrentDir() / s
if not existsFile(result): result = ""
@@ -90,13 +93,24 @@ proc parseRst(text, filename: string,
result = rstParse(text, filename, line, column, hasToc, rstOptions,
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()
new(result)
result.conf = conf
result.cache = cache
initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex),
conf.configVars, filename, {roSupportRawDirective},
conf.configVars, filename.string, {roSupportRawDirective},
docgenFindFile, compilerMsgHandler)
if conf.configVars.hasKey("doc.googleAnalytics"):
@@ -120,8 +134,12 @@ proc newDocumentor*(filename: string; cache: IdentCache; conf: ConfigRef): PDoc
result.jArray = newJArray()
initStrTable result.types
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.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]) =
if conf.cmd != cmdRst2tex: addf(dest, xml, args)
@@ -227,6 +245,10 @@ proc getPlainDocstring(n: PNode): string =
result = getPlainDocstring(n.sons[i])
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 = {}) =
var r: TSrcGen
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>",
"\\spanFloatNumber{$1}", [rope(esc(d.target, literal))])
of tkSymbol:
dispA(d.conf, result, "<span class=\"Identifier\">$1</span>",
"\\spanIdentifier{$1}", [rope(esc(d.target, literal))])
let s = getTokSym(r)
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:
add(result, literal)
of tkCurlyDotLe:
@@ -290,23 +326,24 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
proc testExample(d: PDoc; ex: PNode) =
if d.conf.errorCounter > 0: return
let outputDir = d.conf.getNimcacheDir / "runnableExamples"
let outputDir = d.conf.getNimcacheDir / RelativeDir"runnableExamples"
createDir(outputDir)
inc d.exampleCounter
let outp = outputDir / extractFilename(d.filename.changeFileExt"" &
"_examples" & $d.exampleCounter & ".nim")
let outp = outputDir / RelativeFile(extractFilename(d.filename.changeFileExt"" &
"_examples" & $d.exampleCounter & ".nim"))
#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"
elif isDefined(d.conf, "cpp"): "cpp"
elif isDefined(d.conf, "objc"): "objc"
else: "c"
if os.execShellCmd(os.getAppFilename() & " " & backend &
" --nimcache:" & outputDir & " -r " & outp) != 0:
quit "[Examples] failed: see " & outp
" --nimcache:" & quoteShell(outputDir) &
" -r " & quoteShell(outp)) != 0:
quit "[Examples] failed: see " & outp.string
else:
# 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))
proc extractImports(n: PNode; result: PNode) =
@@ -449,10 +486,8 @@ proc newUniquePlainSymbol(d: PDoc, original: string): string =
result = original
d.seenSymbols[original] = ""
return
# Iterate over possible numeric variants of the original name.
var count = 2
while true:
result = original & "_" & $count
if not d.seenSymbols.hasKey(result):
@@ -460,7 +495,6 @@ proc newUniquePlainSymbol(d: PDoc, original: string): string =
break
count += 1
proc complexName(k: TSymKind, n: PNode, baseName: string): string =
## 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 skConverter: result.add(".c" & defaultParamSeparator)
else: discard
if len(n) > paramsPos and n[paramsPos].kind == nkFormalParams:
result.add(renderParamTypes(n[paramsPos]))
proc isCallable(n: PNode): bool =
## Returns true if `n` contains a callable node.
case n.kind
@@ -495,7 +527,6 @@ proc isCallable(n: PNode): bool =
else:
result = false
proc docstringSummary(rstText: string): 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.add("")
proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
if not isVisible(d, nameNode): return
let
@@ -545,8 +575,8 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
break
plainName.add(literal)
# Render the HTML hyperlink.
nodeToHighlightedHtml(d, n, result, {renderNoBody, renderNoComments, renderDocComments})
nodeToHighlightedHtml(d, n, result, {renderNoBody, renderNoComments,
renderDocComments, renderSyms})
inc(d.id)
let
@@ -563,16 +593,18 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
var seeSrcRope: Rope = nil
let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc")
if docItemSeeSrc.len > 0:
let cwd = canonicalizePath(d.conf, getCurrentDir())
var path = toFullPath(d.conf, n.info)
if path.startsWith(cwd):
path = path[cwd.len+1 .. ^1].replace('\\', '/')
let path = relativeTo(AbsoluteFile toFullPath(d.conf, n.info), d.conf.projectPath, '/')
when false:
let cwd = canonicalizePath(d.conf, getCurrentDir())
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")
if gitUrl.len > 0:
let commit = getConfigVar(d.conf, "git.commit", "master")
let develBranch = getConfigVar(d.conf, "git.devel", "devel")
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])])
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)
comm = $genRecComment(d, n)
r: TSrcGen
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
result = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
"col": %n.info.col}
if comm.len > 0:
@@ -626,7 +656,6 @@ proc checkForFalse(n: PNode): bool =
proc traceDeps(d: PDoc, it: PNode) =
const k = skModule
if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
let sep = it[0]
let dir = it[1]
@@ -637,11 +666,16 @@ proc traceDeps(d: PDoc, it: PNode) =
for x in it[2]:
a.sons[2] = x
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], ", ")
dispA(d.conf, d.section[k],
"<a class=\"reference external\" href=\"$1.html\">$1</a>",
"$1", [rope(splitFile(getModuleName(d.conf, it)).name)])
"<a class=\"reference external\" href=\"$2\">$1</a>",
"$1", [rope esc(d.target, it.sym.name.s),
rope changeFileExt(external, "html").string])
proc generateDoc*(d: PDoc, n: PNode) =
case n.kind
@@ -829,29 +863,23 @@ proc genOutFile(d: PDoc): Rope =
proc generateIndex*(d: PDoc) =
if optGenIndex in d.conf.globalOptions:
writeIndexFile(d[], splitFile(d.conf.outFile).dir /
splitFile(d.filename).name & IndexExt)
let dest = getOutFile2(d.conf, relativeTo(AbsoluteFile d.filename, d.conf.projectPath),
IndexExt, RelativeDir"index")
writeIndexFile(d[], dest.string)
proc getOutFile2(conf: ConfigRef; filename, ext, dir: string): string =
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) =
proc writeOutput*(d: PDoc, useWarning = false) =
var content = genOutFile(d)
if optStdout in d.conf.globalOptions:
writeRope(stdout, content)
else:
let outfile = getOutFile2(d.conf, filename, outExt, "htmldocs")
createDir(outfile.parentDir)
template outfile: untyped = d.destFile
#let outfile = getOutFile2(d.conf, shortenDir(d.conf, filename), outExt, "htmldocs")
createDir(outfile.splitFile.dir)
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,
useWarning = false) =
proc writeOutputJson*(d: PDoc, useWarning = false) =
let content = %*{"orig": d.filename,
"nimble": getPackageName(d.conf, d.filename),
"entries": d.jArray}
@@ -859,8 +887,7 @@ proc writeOutputJson*(d: PDoc, filename, outExt: string,
write(stdout, $content)
else:
var f: File
if open(f, getOutFile2(d.conf, splitFile(filename).name,
outExt, "jsondocs"), fmWrite):
if open(f, d.destFile.string, fmWrite):
write(f, $content)
close(f)
else:
@@ -872,26 +899,27 @@ proc commandDoc*(cache: IdentCache, conf: ConfigRef) =
var d = newDocumentor(conf.projectFull, cache, conf)
d.hasToc = true
generateDoc(d, ast)
writeOutput(d, conf.projectFull, HtmlExt)
writeOutput(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 d = newDocumentor(filen, cache, conf)
d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string;
status: int; content: string) =
var outp: string
var outp: AbsoluteFile
if filename.len == 0:
inc(d.id)
let nameOnly = splitFile(d.filename).name
let subdir = getNimcacheDir(conf) / nameOnly
let subdir = getNimcacheDir(conf) / RelativeDir(nameOnly)
createDir(subdir)
outp = subdir / (nameOnly & "_snippet_" & $d.id & ".nim")
outp = subdir / RelativeFile(nameOnly & "_snippet_" & $d.id & ".nim")
elif isAbsolute(filename):
outp = filename
outp = AbsoluteFile filename
else:
# 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)
let cmd = cmd % quoteShell(outp)
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)
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)
var modDesc = newStringOfCap(30_000)
#d.modDesc = newMutableRope(30_000)
renderRstToOut(d[], rst, modDesc)
#freezeMutableRope(d.modDesc)
d.modDesc = rope(modDesc)
writeOutput(d, filename, outExt)
writeOutput(d)
generateIndex(d)
proc commandRst2Html*(cache: IdentCache, conf: ConfigRef) =
@@ -928,9 +954,9 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) =
writeRope(stdout, content)
else:
#echo getOutFile(gProjectFull, JsonExt)
let filename = getOutFile(conf, conf.projectFull, JsonExt)
let filename = getOutFile(conf, RelativeFile conf.projectName, JsonExt)
if not writeRope(content, filename):
rawMessage(conf, errCannotOpenFile, filename)
rawMessage(conf, errCannotOpenFile, filename.string)
proc commandTags*(cache: IdentCache, conf: ConfigRef) =
var ast = parseFile(conf.projectMainIdx, cache, conf)
@@ -945,12 +971,12 @@ proc commandTags*(cache: IdentCache, conf: ConfigRef) =
writeRope(stdout, content)
else:
#echo getOutFile(gProjectFull, TagsExt)
let filename = getOutFile(conf, conf.projectFull, TagsExt)
let filename = getOutFile(conf, RelativeFile conf.projectName, TagsExt)
if not writeRope(content, filename):
rawMessage(conf, errCannotOpenFile, filename)
rawMessage(conf, errCannotOpenFile, filename.string)
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",
"tableofcontents", "moduledesc", "date", "time",
@@ -958,6 +984,6 @@ proc commandBuildIndex*(cache: IdentCache, conf: ConfigRef) =
["Index".rope, nil, nil, rope(getDateStr()),
rope(getClockStr()), content, nil, nil, nil])
# 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):
rawMessage(conf, errCannotOpenFile, filename)
rawMessage(conf, errCannotOpenFile, filename.string)

View File

@@ -11,7 +11,8 @@
# semantic checking.
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
@@ -38,11 +39,11 @@ template closeImpl(body: untyped) {.dirty.} =
proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
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 =
closeImpl:
writeOutputJson(g.doc, toFullPath(graph.config, FileIndex g.module.position), ".json", useWarning)
writeOutputJson(g.doc, useWarning)
proc processNode(c: PPassContext, n: PNode): PNode =
result = n
@@ -60,7 +61,8 @@ proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
var g: PGen
new(g)
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
g.doc = d
result = g

View File

@@ -14,7 +14,7 @@
import
ropes, os, strutils, osproc, platform, condsyms, options, msgs,
lineinfos, std / sha1, streams
lineinfos, std / sha1, streams, pathutils
type
TInfoCCProp* = enum # properties of the C compiler:
@@ -429,12 +429,13 @@ proc initVars*(conf: ConfigRef) =
if len(conf.ccompilerpath) == 0:
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)
proc toObjFile*(conf: ConfigRef; filename: string): string =
proc toObjFile*(conf: ConfigRef; filename: AbsoluteFile): AbsoluteFile =
# Object file for compilation
result = filename & "." & CC[conf.cCompiler].objExt
result = AbsoluteFile(filename.string & "." & CC[conf.cCompiler].objExt)
proc addFileToCompile*(conf: ConfigRef; cf: Cfile) =
conf.toCompile.add(cf)
@@ -447,8 +448,8 @@ proc resetCompilationLists*(conf: ConfigRef) =
# Maybe we can do that in checkDep on the other hand?
conf.externalToLink.setLen 0
proc addExternalFileToLink*(conf: ConfigRef; filename: string) =
conf.externalToLink.insert(filename, 0)
proc addExternalFileToLink*(conf: ConfigRef; filename: AbsoluteFile) =
conf.externalToLink.insert(filename.string, 0)
proc execWithEcho(conf: ConfigRef; cmd: string, msg = hintExecuting): int =
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'" %
cmd)
proc generateScript(conf: ConfigRef; projectFile: string, script: Rope) =
let (dir, name, ext) = splitFile(projectFile)
let filename = getNimcacheDir(conf) / addFileExt("compile_" & name,
platform.OS[conf.target.targetOS].scriptExt)
proc generateScript(conf: ConfigRef; projectFile: AbsoluteFile, script: Rope) =
let (_, name, _) = splitFile(projectFile)
let filename = getNimcacheDir(conf) / RelativeFile(addFileExt("compile_" & name,
platform.OS[conf.target.targetOS].scriptExt))
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:
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 =
result = getConfigVar(conf, c, ".options.speed")
@@ -490,7 +492,7 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
# `optGenMapping` is included here for niminst.
result = conf.globalOptions * {optGenScript, optGenMapping} != {}
proc cFileSpecificOptions(conf: ConfigRef; cfilename: string): string =
proc cFileSpecificOptions(conf: ConfigRef; cfilename: AbsoluteFile): string =
result = conf.compileOptions
for option in conf.compileOptionsCmd:
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))
proc getCompileOptions(conf: ConfigRef): string =
result = cFileSpecificOptions(conf, "__dummy__")
result = cFileSpecificOptions(conf, AbsoluteFile"__dummy__")
proc getLinkOptions(conf: ConfigRef): string =
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
(conf.target.hostOS == osWindows)
proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string =
result = if conf.cmd == cmdCompileToCpp and not cfile.endsWith(".c"):
proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: AbsoluteFile): string =
result = if conf.cmd == cmdCompileToCpp and not cfile.string.endsWith(".c"):
CC[compiler].cppCompiler
else:
CC[compiler].compilerExe
@@ -539,7 +541,7 @@ proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string
proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string =
result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
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 =
var c = conf.cCompiler
@@ -565,43 +567,42 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
includeCmd = ""
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
var objfile =
if cfile.obj.len == 0:
if cfile.obj.isEmpty:
if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths(conf):
toObjFile(conf, cf)
toObjFile(conf, cf).string
else:
completeCFilePath(conf, toObjFile(conf, cf))
completeCFilePath(conf, toObjFile(conf, cf)).string
elif noAbsolutePaths(conf):
extractFilename(cfile.obj)
extractFilename(cfile.obj.string)
else:
cfile.obj
cfile.obj.string
# D files are required by nintendo switch libs for
# compilation. They are basically a list of all includes.
let dfile = objfile.changeFileExt(".d").quoteShell()
objfile = quoteShell(objfile)
cf = quoteShell(cf)
let cfsh = quoteShell(cf)
result = quoteShell(compilePattern % [
"dfile", dfile,
"file", cf, "objfile", objfile, "options", options,
"include", includeCmd, "nim", getPrefixDir(conf),
"nim", getPrefixDir(conf), "lib", conf.libpath])
"file", cfsh, "objfile", objfile, "options", options,
"include", includeCmd, "nim", getPrefixDir(conf).string,
"lib", conf.libpath.string])
add(result, ' ')
addf(result, CC[c].compileTmpl, [
"dfile", dfile,
"file", cf, "objfile", objfile,
"file", cfsh, "objfile", objfile,
"options", options, "include", includeCmd,
"nim", quoteShell(getPrefixDir(conf)),
"nim", quoteShell(getPrefixDir(conf)),
"lib", quoteShell(conf.libpath)])
proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash =
result = secureHash(
$secureHashFile(cfile.cname) &
$secureHashFile(cfile.cname.string) &
platform.OS[conf.target.targetOS].name &
platform.CPU[conf.target.targetCPU].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 currentHash = footprint(conf, cfile)
var f: File
if open(f, hashFile, fmRead):
if open(f, hashFile.string, fmRead):
let oldHash = parseSecureHash(f.readLine())
close(f)
result = oldHash != currentHash
else:
result = true
if result:
if open(f, hashFile, fmWrite):
if open(f, hashFile.string, fmWrite):
f.writeLine($currentHash)
close(f)
@@ -630,7 +631,7 @@ proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) =
c.flags.incl CfileFlag.Cached
conf.toCompile.add(c)
proc addExternalFileToCompile*(conf: ConfigRef; filename: string) =
proc addExternalFileToCompile*(conf: ConfigRef; filename: AbsoluteFile) =
var c = Cfile(cname: filename,
obj: toObjFile(conf, completeCFilePath(conf, filename, false)),
flags: {CfileFlag.External})
@@ -650,11 +651,11 @@ proc compileCFile(conf: ConfigRef; list: CFileList, script: var Rope, cmds: var
add(script, compileCmd)
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:
var libname: string
if conf.outFile.len > 0:
libname = conf.outFile.expandTilde
if not conf.outFile.isEmpty:
libname = conf.outFile.string.expandTilde
if not libname.isAbsolute():
libname = getCurrentDir() / libname
else:
@@ -679,13 +680,13 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
else:
exefile = splitFile(projectfile).name & platform.OS[conf.target.targetOS].exeExt
builddll = ""
if conf.outFile.len > 0:
exefile = conf.outFile.expandTilde
if not conf.outFile.isEmpty:
exefile = conf.outFile.string.expandTilde
if not exefile.isAbsolute():
exefile = getCurrentDir() / exefile
if not noAbsolutePaths(conf):
if not exefile.isAbsolute():
exefile = joinPath(splitFile(projectfile).dir, exefile)
exefile = string(splitFile(projectfile).dir / RelativeFile(exefile))
when false:
if optCDebug in conf.globalOptions:
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
# 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) & " " &
getConfigVar(conf, conf.cCompiler, ".options.linker")
@@ -703,7 +704,7 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
result = quoteShell(result % ["builddll", builddll,
"mapfile", mapfile,
"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 ' '
addf(result, linkTmpl, ["builddll", builddll,
"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'" %
cmds.join())
proc callCCompiler*(conf: ConfigRef; projectfile: string) =
proc callCCompiler*(conf: ConfigRef; projectfile: AbsoluteFile) =
var
linkCmd: string
if conf.globalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
@@ -787,7 +788,7 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) =
add(objfiles, quoteShell(
addFileExt(objFile, CC[conf.cCompiler].objExt)))
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, quoteShell(objFile))
@@ -804,7 +805,7 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) =
#from json import escapeJson
import json
proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: AbsoluteFile) =
template lit(x: untyped) = f.write x
template str(x: untyped) =
when compiles(escapeJson(x, buf)):
@@ -821,7 +822,7 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
let compileCmd = getCompileCFileCmd(conf, it)
if pastStart: lit "],\L"
lit "["
str it.cname
str it.cname.string
lit ", "
str compileCmd
pastStart = true
@@ -851,11 +852,10 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
var buf = newStringOfCap(50)
let file = projectfile.splitFile.name
let jsonFile = toGeneratedFile(conf, file, "json")
let jsonFile = toGeneratedFile(conf, projectfile, "json")
var f: File
if open(f, jsonFile, fmWrite):
if open(f, jsonFile.string, fmWrite):
lit "{\"compile\":[\L"
cfiles(conf, f, buf, conf.toCompile, false)
lit "],\L\"link\":[\L"
@@ -868,11 +868,10 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
lit "\L}\L"
close(f)
proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
let file = projectfile.splitFile.name
let jsonFile = toGeneratedFile(conf, file, "json")
proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: AbsoluteFile) =
let jsonFile = toGeneratedFile(conf, projectfile, "json")
try:
let data = json.parseFile(jsonFile)
let data = json.parseFile(jsonFile.string)
let toCompile = data["compile"]
doAssert toCompile.kind == JArray
var cmds: TStringSeq = @[]
@@ -896,11 +895,11 @@ proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
except:
when declared(echo):
echo getCurrentException().getStackTrace()
quit "error evaluating JSON file: " & jsonFile
quit "error evaluating JSON file: " & jsonFile.string
proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope =
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) =
if optGenMapping notin conf.globalOptions: return
@@ -914,9 +913,9 @@ proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) =
getConfigVar(conf, conf.cCompiler, ".options.linker")))
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])
let filename = joinPath(conf.projectPath, "mapping.txt")
let filename = conf.projectPath / RelativeFile"mapping.txt"
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
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
renderer, filters, lineinfos
renderer, filters, lineinfos, pathutils
type
TParseState = enum
@@ -199,7 +199,8 @@ proc parseLine(p: var TTmplParser) =
inc(j)
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
p.config = conf
p.info = newLineInfo(conf, filename, 0, 0)

View File

@@ -11,7 +11,7 @@
import
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
renderer
renderer, pathutils
proc invalidPragma(conf: ConfigRef; n: PNode) =
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
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 leading = boolArg(conf, call, "leading", 2, 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)
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, "")
if len(sub) == 0: invalidPragma(conf, call)
var by = strArg(conf, call, "by", 2, "")

View File

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

View File

@@ -9,7 +9,7 @@
## This module contains a simple persistent id generator.
import idents, strutils, os, options
import idents, strutils, os, options, pathutils
var gFrontEndId*: int
@@ -36,18 +36,18 @@ proc setId*(id: int) {.inline.} =
proc idSynchronizationPoint*(idRange: int) =
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
# will cause strange bugs if multiple projects are in the same folder, so
# 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)
f.writeLine($gFrontEndId)
f.close()
proc loadMaxIds*(conf: ConfigRef; project: string) =
proc loadMaxIds*(conf: ConfigRef; project: AbsoluteFile) =
var f: File
if open(f, toGid(conf, project), fmRead):
var line = newStringOfCap(20)

View File

@@ -7,15 +7,12 @@
# distribution, for details about the copyright.
#
# This module implements the symbol importing mechanism.
## This module implements the symbol importing mechanism.
import
intsets, strutils, os, ast, astalgo, msgs, options, idents, lookups,
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 =
assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
result = initIntSet()
@@ -140,7 +137,7 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym =
c.config.options)
proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
var f = checkModuleName(c.config, n)
let f = checkModuleName(c.config, n)
if f != InvalidFileIDX:
let L = c.graph.importStack.len
let recursion = c.graph.importStack.find(f)
@@ -168,7 +165,8 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
else:
message(c.config, n.info, warnDeprecated, result.name.s)
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 =
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)
#importForwarded(c, m.ast, emptySet)
proc evalImport(c: PContext, n: PNode): PNode =
proc evalImport*(c: PContext, n: PNode): PNode =
result = newNodeI(nkImportStmt, n.info)
for i in countup(0, sonsLen(n) - 1):
let it = n.sons[i]
@@ -212,7 +210,7 @@ proc evalImport(c: PContext, n: PNode): PNode =
else:
impMod(c, it, result)
proc evalFrom(c: PContext, n: PNode): PNode =
proc evalFrom*(c: PContext, n: PNode): PNode =
result = newNodeI(nkImportStmt, n.info)
checkMinSonsLen(n, 2, c.config)
n.sons[0] = transformImportAs(c, n.sons[0])

View File

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

View File

@@ -17,7 +17,7 @@
import
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
wordrecg, lineinfos
wordrecg, lineinfos, pathutils
const
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.config = config
proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream;
proc openLexer*(lex: var TLexer, filename: AbsoluteFile, inputstream: PLLStream;
cache: IdentCache; config: ConfigRef) =
openLexer(lex, fileInfoIdx(config, filename), inputstream, cache, config)

View File

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

View File

@@ -14,7 +14,7 @@ import
strutils, os, intsets, strtabs
import options, ast, astalgo, msgs, semdata, ropes, idents,
lineinfos
lineinfos, pathutils
const
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
else: conf.m.fileInfos[i].fullpath.changeFileExt(".pretty.nim")
try:
var f = open(newFile, fmWrite)
var f = open(newFile.string, fmWrite)
for line in conf.m.fileInfos[i].lines:
if doStrip:
f.write line.strip(leading = false, trailing = true)
@@ -51,7 +51,7 @@ proc overwriteFiles*(conf: ConfigRef) =
f.write(conf.m.fileInfos[i], "\L")
f.close
except IOError:
rawMessage(conf, errGenerated, "cannot open file: " & newFile)
rawMessage(conf, errGenerated, "cannot open file: " & newFile.string)
proc `=~`(s: string, a: openArray[string]): bool =
for x in a:

View File

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

View File

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

View File

@@ -7,9 +7,8 @@
# distribution, for details about the copyright.
#
import ast, renderer, strutils, msgs, options, idents, os, lineinfos
import nimblecmd
import ast, renderer, strutils, msgs, options, idents, os, lineinfos,
pathutils, nimblecmd
when false:
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
let modulename = getModuleName(conf, n)
let fullPath = findModule(conf, modulename, toFullPath(conf, n.info))
if fullPath.len == 0:
if fullPath.isEmpty:
if doLocalError:
let m = if modulename.len > 0: modulename else: $n
localError(conf, n.info, "cannot open file: " & m)

View File

@@ -12,7 +12,7 @@
import
ast, astalgo, magicsys, std / sha1, msgs, cgendata, sigmatch, options,
idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod,
lineinfos
lineinfos, pathutils
proc resetSystemArtifacts*(g: ModuleGraph) =
magicsys.resetSysTypes(g)
@@ -102,19 +102,21 @@ proc connectCallbacks*(graph: ModuleGraph) =
proc compileSystemModule*(graph: ModuleGraph) =
if graph.systemModule == nil:
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})
proc wantMainModule*(conf: ConfigRef) =
if conf.projectFull.len == 0:
fatal(conf, newLineInfo(conf, "command line", 1, 1), errGenerated, "command expects a filename")
if conf.projectFull.isEmpty:
fatal(conf, newLineInfo(conf, AbsoluteFile"command line", 1, 1), errGenerated,
"command expects a filename")
conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))
proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIDX) =
connectCallbacks(graph)
let conf = graph.config
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
graph.importStack.add projectFile
if projectFile == systemFileIdx:
@@ -123,8 +125,11 @@ proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIDX) =
graph.compileSystemModule()
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.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
options, strutils, os, tables, ropes, platform, terminal, macros,
lineinfos
lineinfos, pathutils
proc toCChar*(c: char; result: var string) =
case c
@@ -35,20 +35,20 @@ proc makeCString*(s: string): Rope =
add(result, rope(res))
proc newFileInfo(fullPath, projPath: string): TFileInfo =
proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo =
result.fullPath = fullPath
#shallow(result.fullPath)
result.projPath = projPath
#shallow(result.projPath)
let fileName = projPath.extractFilename
let fileName = fullPath.extractFilename
result.shortName = fileName.changeFileExt("")
result.quotedName = fileName.makeCString
result.quotedFullName = fullPath.makeCString
result.quotedFullName = fullPath.string.makeCString
result.lines = @[]
when defined(nimpretty):
if result.fullPath.len > 0:
if not result.fullPath.isEmpty:
try:
result.fullContent = readFile(result.fullPath)
result.fullContent = readFile(result.fullPath.string)
except IOError:
#rawMessage(errCannotOpenFile, result.fullPath)
# XXX fixme
@@ -58,39 +58,39 @@ when defined(nimpretty):
proc fileSection*(conf: ConfigRef; fid: FileIndex; a, b: int): string =
substr(conf.m.fileInfos[fid.int].fullContent, a, b)
proc fileInfoKnown*(conf: ConfigRef; filename: string): bool =
proc fileInfoKnown*(conf: ConfigRef; filename: AbsoluteFile): bool =
var
canon: string
canon: AbsoluteFile
try:
canon = canonicalizePath(conf, filename)
except:
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
canon: string
canon: AbsoluteFile
pseudoPath = false
try:
canon = canonicalizePath(conf, filename)
shallow(canon)
shallow(canon.string)
except:
canon = filename
# The compiler uses "filenames" such as `command line` or `stdin`
# This flag indicates that we are working with such a path here
pseudoPath = true
if conf.m.filenameToIndexTbl.hasKey(canon):
result = conf.m.filenameToIndexTbl[canon]
if conf.m.filenameToIndexTbl.hasKey(canon.string):
result = conf.m.filenameToIndexTbl[canon.string]
else:
isKnownFile = false
result = conf.m.fileInfos.len.FileIndex
conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: filename
else: shortenDir(conf, canon)))
conf.m.filenameToIndexTbl[canon] = result
conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: RelativeFile filename
else: relativeTo(canon, conf.projectPath)))
conf.m.filenameToIndexTbl[canon.string] = result
proc fileInfoIdx*(conf: ConfigRef; filename: string): FileIndex =
proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile): FileIndex =
var dummy: bool
result = fileInfoIdx(conf, filename, dummy)
@@ -99,7 +99,7 @@ proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
result.line = uint16(line)
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)
@@ -152,13 +152,16 @@ proc getInfoContext*(conf: ConfigRef; index: int): TLineInfo =
else: result = conf.m.msgContext[i]
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 =
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
conf.m.fileInfos[fileIdx.int32].dirtyFile = filename
@@ -170,10 +173,10 @@ proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string =
assert fileIdx.int32 >= 0
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:
result = "???"
elif conf.m.fileInfos[fileIdx.int32].dirtyFile.len > 0:
result = AbsoluteFile"???"
elif not conf.m.fileInfos[fileIdx.int32].dirtyFile.isEmpty:
result = conf.m.fileInfos[fileIdx.int32].dirtyFile
else:
result = conf.m.fileInfos[fileIdx.int32].fullPath
@@ -188,9 +191,9 @@ proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
if info.fileIndex.int32 < 0:
result = "???"
elif optListFullPaths in conf.globalOptions:
result = conf.m.fileInfos[info.fileIndex.int32].fullPath
result = conf.m.fileInfos[info.fileIndex.int32].fullPath.string
else:
result = conf.m.fileInfos[info.fileIndex.int32].projPath
result = conf.m.fileInfos[info.fileIndex.int32].projPath.string
proc toLinenumber*(info: TLineInfo): int {.inline.} =
result = int info.line

View File

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

View File

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

View File

@@ -10,9 +10,9 @@
## Implements some helper procs for Nimble (Nim's package manager) support.
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):
conf.searchPaths.insert(path, 0)
@@ -112,9 +112,9 @@ proc addNimblePath(conf: ConfigRef; p: string, info: TLineInfo) =
if not path.isAbsolute():
path = p / path
if not contains(conf.searchPaths, path):
if not contains(conf.searchPaths, AbsoluteDir 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) =
var packages = newStringTable(modeStyleInsensitive)
@@ -126,9 +126,9 @@ proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) =
for p in packages.chosen:
addNimblePath(conf, p, info)
proc nimblePath*(conf: ConfigRef; path: string, info: TLineInfo) =
addPathRec(conf, path, info)
addNimblePath(conf, path, info)
proc nimblePath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) =
addPathRec(conf, path.string, info)
addNimblePath(conf, path.string, info)
when isMainModule:
proc v(s: string): Version = s.newVersion

View File

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

View File

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

View File

@@ -15,6 +15,6 @@ const
VersionAsString* = system.NimVersion
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
## declare this.

View File

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

View File

@@ -41,10 +41,10 @@ proc getPackageName*(conf: ConfigRef; path: string): string =
dec parents
if parents <= 0: break
proc withPackageName*(conf: ConfigRef; path: string): string =
let x = getPackageName(conf, path)
proc withPackageName*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
let x = getPackageName(conf, path.string)
if x.len == 0:
result = path
else:
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
import
llstream, lexer, idents, strutils, ast, astalgo, msgs, options, lineinfos
llstream, lexer, idents, strutils, ast, astalgo, msgs, options, lineinfos,
pathutils
when defined(nimpretty2):
import layouter
@@ -114,7 +115,7 @@ proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream,
p.strongSpaces = strongSpaces
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;
strongSpaces=false) =
openParser(p, fileInfoIdx(config, filename), inputStream, cache, config, strongSpaces)
@@ -2235,7 +2236,7 @@ proc parseString*(s: string; cache: IdentCache; config: ConfigRef;
# XXX for now the builtin 'parseStmt/Expr' functions do not know about strong
# spaces...
parser.lex.errorHandler = errorHandler
openParser(parser, filename, stream, cache, config, false)
openParser(parser, AbsoluteFile filename, stream, cache, config, false)
result = parser.parseAll
closeParser(parser)

View File

@@ -14,7 +14,7 @@ import
strutils, options, ast, astalgo, llstream, msgs, platform, os,
condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
nimsets, syntaxes, times, idgen, modulegraphs, reorder, rod,
lineinfos
lineinfos, pathutils
type
@@ -106,7 +106,7 @@ proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool =
proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex =
let fullPath = findModule(conf, module, relativeTo)
if fullPath.len == 0:
if fullPath.isEmpty:
result = InvalidFileIDX
else:
result = fileInfoIdx(conf, fullPath)
@@ -160,7 +160,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {
let filename = toFullPathConsiderDirty(graph.config, fileIdx)
s = llStreamOpen(filename, fmRead)
if s == nil:
rawMessage(graph.config, errCannotOpenFile, filename)
rawMessage(graph.config, errCannotOpenFile, filename.string)
return false
else:
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
os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees,
types, lookups, lineinfos
types, lookups, lineinfos, pathutils
const
FirstCallConv* = wNimcall
@@ -442,26 +442,22 @@ proc processUndef(c: PContext, n: PNode) =
else:
invalidPragma(c, n)
type
TLinkFeature = enum
linkNormal, linkSys
proc relativeFile(c: PContext; n: PNode; ext=""): string =
proc relativeFile(c: PContext; n: PNode; ext=""): AbsoluteFile =
var s = expectStrLit(c, n)
if ext.len > 0 and splitFile(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 isAbsolute(s): result = s
if isAbsolute(s): result = AbsoluteFile s
else:
result = findFile(c.config, s)
if result.len == 0: result = s
if result.isEmpty: result = AbsoluteFile s
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})
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 =
n.sons[i] = c.semConstExpr(c, n[i])
@@ -478,30 +474,23 @@ proc processCompile(c: PContext, n: PNode) =
let dest = getStrLit(c, it, 1)
var found = parentDir(toFullPath(c.config, n.info)) / s
for f in os.walkFiles(found):
let obj = completeCFilePath(c.config, dest % extractFilename(f))
docompile(c, it, f, obj)
let obj = completeCFilePath(c.config, AbsoluteFile(dest % extractFilename(f)))
docompile(c, it, AbsoluteFile f, obj)
else:
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 isAbsolute(s): found = s
if isAbsolute(s): found = AbsoluteFile s
else:
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))
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)
case feature
of linkNormal:
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")
extccomp.addExternalFileToLink(c.config, found)
recordPragma(c, n, "link", found.string)
proc pragmaBreakpoint(c: PContext, n: PNode) =
discard getOptionalStr(c, n, "")
@@ -594,8 +583,9 @@ proc pragmaLine(c: PContext, n: PNode) =
elif y.kind != nkIntLit:
localError(c.config, n.info, errIntLiteralExpected)
else:
# XXX this produces weird paths which are not properly resolved:
n.info.fileIndex = msgs.fileInfoIdx(c.config, x.strVal)
# XXX check if paths are properly resolved this way:
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)
else:
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 wUndef: processUndef(c, it)
of wCompile: processCompile(c, it)
of wLink: processCommonLink(c, it, linkNormal)
of wLinksys: processCommonLink(c, it, linkSys)
of wLink: processLink(c, it)
of wPassl:
let s = expectStrLit(c, it)
extccomp.addLinkOption(c.config, s)

View File

@@ -15,11 +15,12 @@ import
type
TRenderFlag* = enum
renderNone, renderNoBody, renderNoComments, renderDocComments,
renderNoPragmas, renderIds, renderNoProcDefs
renderNoPragmas, renderIds, renderNoProcDefs, renderSyms
TRenderFlags* = set[TRenderFlag]
TRenderTok* = object
kind*: TTokType
length*: int16
sym*: PSym
TRenderTokSeq* = seq[TRenderTok]
TSrcGen* = object
@@ -105,11 +106,12 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) =
g.inGenericParams = false
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)
setLen(g.tokens, length + 1)
g.tokens[length].kind = kind
g.tokens[length].length = int16(len(s))
g.tokens[length].sym = sym
add(g.buf, s)
proc addPendingNL(g: var TSrcGen) =
@@ -165,11 +167,11 @@ proc dedent(g: var TSrcGen) =
dec(g.pendingNL, 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:
addPendingNL(g)
if len(s) > 0:
addTok(g, kind, s)
addTok(g, kind, s, sym)
inc(g.lineLen, len(s))
else:
g.pendingWhitespace = s.len
@@ -836,7 +838,7 @@ proc gident(g: var TSrcGen, n: PNode) =
t = tkSymbol
else:
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):
when defined(debugMagics):
put(g, tkIntLit, $n.sym.id & $n.sym.magic)
@@ -1541,3 +1543,9 @@ proc getNextTok*(r: var TSrcGen, kind: var TTokType, literal: var string) =
inc(r.idx)
else:
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,
renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp,
btrees, trees, condsyms, nversion
btrees, trees, condsyms, nversion, pathutils
## Todo:
## - Dependency computation should use *signature* hashes in order to
@@ -796,7 +796,7 @@ proc replay(g: ModuleGraph; module: PSym; n: PNode) =
flags: {CfileFlag.External})
extccomp.addExternalFileToCompile(g.config, cf)
of "link":
extccomp.addExternalFileToLink(g.config, n[1].strVal)
extccomp.addExternalFileToLink(g.config, AbsoluteFile n[1].strVal)
of "passl":
extccomp.addLinkOption(g.config, n[1].strVal)
of "passc":

View File

@@ -58,6 +58,8 @@
import
hashes
from pathutils import AbsoluteFile
type
FormatStr* = string # later we may change it to CString for better
# performance of the code generator (assignments
@@ -183,9 +185,9 @@ proc writeRope*(f: File, r: Rope) =
## writes a rope to a file.
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
if open(f, filename, fmWrite):
if open(f, filename.string, fmWrite):
if head != nil: writeRope(f, head)
close(f)
result = true
@@ -314,16 +316,16 @@ proc equalsFile*(r: Rope, f: File): bool =
result = readBuffer(f, addr(buf[0]), 1) == 0 and
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
## exist, false is returned.
var f: File
result = open(f, filename)
result = open(f, filename.string)
if result:
result = equalsFile(r, f)
close(f)
proc writeRopeIfNotEqual*(r: Rope, filename: string): bool =
proc writeRopeIfNotEqual*(r: Rope, filename: AbsoluteFile): bool =
# returns true if overwritten
if not equalsFile(r, filename):
result = writeRope(r, filename)

View File

@@ -13,7 +13,7 @@
import
ast, modules, idents, passes, passaux, condsyms,
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:
from strutils import cmpIgnoreStyle, contains
@@ -105,7 +105,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
cbconf exists:
setResult(a, options.existsConfigVar(conf, a.getString 0))
cbconf nimcacheDir:
setResult(a, options.getNimcacheDir(conf))
setResult(a, options.getNimcacheDir(conf).string)
cbconf paramStr:
setResult(a, os.paramStr(int a.getInt 0))
cbconf paramCount:
@@ -120,8 +120,8 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
if arg.len > 0:
conf.projectName = arg
let path =
if conf.projectName.isAbsolute: conf.projectName
else: conf.projectPath / conf.projectName
if conf.projectName.isAbsolute: AbsoluteFile(conf.projectName)
else: conf.projectPath / RelativeFile(conf.projectName)
try:
conf.projectFull = canonicalizePath(conf, path)
except OSError:
@@ -149,9 +149,9 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
cbconf cppDefine:
options.cppDefine(conf, a.getString(0))
proc runNimScript*(cache: IdentCache; scriptName: string;
proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
freshDefines=true; conf: ConfigRef) =
rawMessage(conf, hintConf, scriptName)
rawMessage(conf, hintConf, scriptName.string)
let graph = newModuleGraph(cache, conf)
connectCallbacks(graph)
@@ -169,7 +169,7 @@ proc runNimScript*(cache: IdentCache; scriptName: string;
var m = graph.makeModule(scriptName)
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
discard graph.processModule(m, llStreamOpen(scriptName, fmRead))

View File

@@ -32,7 +32,7 @@
# included from sigmatch.nim
import algorithm, prefixmatches, lineinfos
import algorithm, prefixmatches, lineinfos, pathutils
from wordrecg import wDeprecated
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:
# consider 'foo.|' where 'foo' is some not imported module.
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:
typ = nil
else:

View File

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

View File

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

View File

@@ -7,6 +7,8 @@
# distribution, for details about the copyright.
#
import pathutils
template setX(k, field) {.dirty.} =
var s: seq[TFullReg]
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].node = n
proc setResult*(a: VmArgs; v: AbsoluteDir) = setResult(a, v.string)
proc setResult*(a: VmArgs; v: seq[string]) =
var s: seq[TFullReg]
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))
proc getProjectPathWrapper(a: VmArgs) =
setResult a, c.config.projectPath
setResult a, c.config.projectPath.string
wrap1f_math(sqrt)
wrap1f_math(ln)

View File

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