Incremental compilation (IC): Improvements (#11881)
* IC: C codegen is aware of IC * manual: minor change to make VSCode's RST plugin render it properly * IC: minor refactoring * testament: code refactorings * rodutils: removed dead code * IC: always build the compiler with the IC feature * IC: C codegen improvements * IC: implement the undocumented -d:nimMustCache option for testing purposes * IC: added first basic tests * IC: extensive testing of the deserialization feature * testament: refactoring; better IC tests * IC: removes 'nimMustCache' flag; readonly does the same * testament: minor refactoring * update Nimble version * testament: removed dead code and imports; IC: added simple test * IC: progress
This commit is contained in:
parent
c0d240b8cd
commit
c8cffaf420
16 changed files with 318 additions and 261 deletions
|
|
@ -1108,7 +1108,8 @@ proc genProcNoForward(m: BModule, prc: PSym) =
|
||||||
# a check for ``m.declaredThings``.
|
# a check for ``m.declaredThings``.
|
||||||
if not containsOrIncl(m.declaredThings, prc.id):
|
if not containsOrIncl(m.declaredThings, prc.id):
|
||||||
#if prc.loc.k == locNone:
|
#if prc.loc.k == locNone:
|
||||||
# mangle the inline proc based on the module where it is defined - not on the first module that uses it
|
# mangle the inline proc based on the module where it is defined -
|
||||||
|
# not on the first module that uses it
|
||||||
fillProcLoc(findPendingModule(m, prc), prc.ast[namePos])
|
fillProcLoc(findPendingModule(m, prc), prc.ast[namePos])
|
||||||
#elif {sfExportc, sfImportc} * prc.flags == {}:
|
#elif {sfExportc, sfImportc} * prc.flags == {}:
|
||||||
# # reset name to restore consistency in case of hashing collisions:
|
# # reset name to restore consistency in case of hashing collisions:
|
||||||
|
|
@ -1781,10 +1782,6 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule
|
||||||
else: AbsoluteFile""
|
else: AbsoluteFile""
|
||||||
open(result.ndi, ndiName, g.config)
|
open(result.ndi, ndiName, g.config)
|
||||||
|
|
||||||
proc nullify[T](arr: var T) =
|
|
||||||
for i in low(arr)..high(arr):
|
|
||||||
arr[i] = Rope(nil)
|
|
||||||
|
|
||||||
proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
|
proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
|
||||||
result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex))
|
result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex))
|
||||||
|
|
||||||
|
|
@ -1875,7 +1872,9 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
if b == nil: return
|
if b == nil: return
|
||||||
var m = BModule(b)
|
var m = BModule(b)
|
||||||
if passes.skipCodegen(m.config, n): return
|
if passes.skipCodegen(m.config, n) or
|
||||||
|
not moduleHasChanged(m.g.graph, m.module):
|
||||||
|
return
|
||||||
m.initProc.options = initProcOptions(m)
|
m.initProc.options = initProcOptions(m)
|
||||||
#softRnl = if optLineDir in m.config.options: noRnl else: rnl
|
#softRnl = if optLineDir in m.config.options: noRnl else: rnl
|
||||||
# XXX replicate this logic!
|
# XXX replicate this logic!
|
||||||
|
|
@ -1886,9 +1885,10 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
|
||||||
genStmts(m.initProc, transformedN)
|
genStmts(m.initProc, transformedN)
|
||||||
|
|
||||||
proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
|
proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
|
||||||
result = true
|
|
||||||
if optForceFullMake notin m.config.globalOptions:
|
if optForceFullMake notin m.config.globalOptions:
|
||||||
if not equalsFile(code, cfile.cname):
|
if not moduleHasChanged(m.g.graph, m.module):
|
||||||
|
result = false
|
||||||
|
elif not equalsFile(code, cfile.cname):
|
||||||
if false:
|
if false:
|
||||||
#m.config.symbolFiles == readOnlySf: #isDefined(m.config, "nimdiff"):
|
#m.config.symbolFiles == readOnlySf: #isDefined(m.config, "nimdiff"):
|
||||||
if fileExists(cfile.cname):
|
if fileExists(cfile.cname):
|
||||||
|
|
@ -1898,12 +1898,15 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
|
||||||
echo "new file ", cfile.cname.string
|
echo "new file ", cfile.cname.string
|
||||||
if not writeRope(code, cfile.cname):
|
if not writeRope(code, cfile.cname):
|
||||||
rawMessage(m.config, errCannotOpenFile, cfile.cname.string)
|
rawMessage(m.config, errCannotOpenFile, cfile.cname.string)
|
||||||
return
|
result = true
|
||||||
if fileExists(cfile.obj) and os.fileNewer(cfile.obj.string, cfile.cname.string):
|
elif fileExists(cfile.obj) and os.fileNewer(cfile.obj.string, cfile.cname.string):
|
||||||
result = false
|
result = false
|
||||||
|
else:
|
||||||
|
result = true
|
||||||
else:
|
else:
|
||||||
if not writeRope(code, cfile.cname):
|
if not writeRope(code, cfile.cname):
|
||||||
rawMessage(m.config, errCannotOpenFile, cfile.cname.string)
|
rawMessage(m.config, errCannotOpenFile, cfile.cname.string)
|
||||||
|
result = true
|
||||||
|
|
||||||
# We need 2 different logics here: pending modules (including
|
# We need 2 different logics here: pending modules (including
|
||||||
# 'nim__dat') may require file merging for the combination of dead code
|
# 'nim__dat') may require file merging for the combination of dead code
|
||||||
|
|
@ -1915,6 +1918,7 @@ proc writeModule(m: BModule, pending: bool) =
|
||||||
let cfile = getCFile(m)
|
let cfile = getCFile(m)
|
||||||
|
|
||||||
if true or optForceFullMake in m.config.globalOptions:
|
if true or optForceFullMake in m.config.globalOptions:
|
||||||
|
if moduleHasChanged(m.g.graph, m.module):
|
||||||
genInitCode(m)
|
genInitCode(m)
|
||||||
finishTypeDescriptions(m)
|
finishTypeDescriptions(m)
|
||||||
if sfMainModule in m.module.flags:
|
if sfMainModule in m.module.flags:
|
||||||
|
|
@ -1926,7 +1930,7 @@ proc writeModule(m: BModule, pending: bool) =
|
||||||
var cf = Cfile(nimname: m.module.name.s, cname: cfile,
|
var cf = Cfile(nimname: m.module.name.s, cname: cfile,
|
||||||
obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||||
var code = genModule(m, cf)
|
var code = genModule(m, cf)
|
||||||
if code != nil:
|
if code != nil or m.config.symbolFiles != disabledSf:
|
||||||
when hasTinyCBackend:
|
when hasTinyCBackend:
|
||||||
if conf.cmd == cmdRun:
|
if conf.cmd == cmdRun:
|
||||||
tccgen.compileCCode($code)
|
tccgen.compileCCode($code)
|
||||||
|
|
@ -1983,6 +1987,7 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
|
||||||
for destructorCall in graph.globalDestructors:
|
for destructorCall in graph.globalDestructors:
|
||||||
n.add destructorCall
|
n.add destructorCall
|
||||||
if passes.skipCodegen(m.config, n): return
|
if passes.skipCodegen(m.config, n): return
|
||||||
|
if moduleHasChanged(graph, m.module):
|
||||||
# if the module is cached, we don't regenerate the main proc
|
# if the module is cached, we don't regenerate the main proc
|
||||||
# nor the dispatchers? But if the dispatchers changed?
|
# nor the dispatchers? But if the dispatchers changed?
|
||||||
# XXX emit the dispatchers into its own .c file?
|
# XXX emit the dispatchers into its own .c file?
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ import
|
||||||
wordrecg, parseutils, nimblecmd, parseopt, sequtils, lineinfos,
|
wordrecg, parseutils, nimblecmd, parseopt, sequtils, lineinfos,
|
||||||
pathutils, strtabs
|
pathutils, strtabs
|
||||||
|
|
||||||
|
from incremental import nimIncremental
|
||||||
|
|
||||||
# but some have deps to imported modules. Yay.
|
# but some have deps to imported modules. Yay.
|
||||||
bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
|
bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
|
||||||
bootSwitch(usedNativeStacktrace,
|
bootSwitch(usedNativeStacktrace,
|
||||||
|
|
@ -669,7 +671,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
helpOnError(conf, pass)
|
helpOnError(conf, pass)
|
||||||
of "symbolfiles": discard "ignore for backwards compat"
|
of "symbolfiles": discard "ignore for backwards compat"
|
||||||
of "incremental":
|
of "incremental":
|
||||||
when not defined(nimIncremental):
|
when not nimIncremental:
|
||||||
localError(conf, info, "the compiler was not built with " &
|
localError(conf, info, "the compiler was not built with " &
|
||||||
"incremental compilation features; bootstrap with " &
|
"incremental compilation features; bootstrap with " &
|
||||||
"-d:nimIncremental to enable")
|
"-d:nimIncremental to enable")
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,14 @@
|
||||||
## Basic type definitions the module graph needs in order to support
|
## Basic type definitions the module graph needs in order to support
|
||||||
## incremental compilations.
|
## incremental compilations.
|
||||||
|
|
||||||
const nimIncremental* = defined(nimIncremental)
|
const nimIncremental* = true # defined(nimIncremental)
|
||||||
|
|
||||||
import options, lineinfos
|
import options, lineinfos
|
||||||
|
|
||||||
when nimIncremental:
|
when nimIncremental:
|
||||||
import ast, msgs, intsets, btrees, db_sqlite, std / sha1, pathutils
|
import ast, msgs, intsets, btrees, db_sqlite, std / sha1, pathutils
|
||||||
from strutils import parseInt
|
from strutils import parseInt
|
||||||
|
from os import isAbsolute
|
||||||
|
|
||||||
type
|
type
|
||||||
Writer* = object
|
Writer* = object
|
||||||
|
|
@ -47,7 +48,7 @@ when nimIncremental:
|
||||||
|
|
||||||
proc hashFileCached*(conf: ConfigRef; fileIdx: FileIndex; fullpath: AbsoluteFile): string =
|
proc hashFileCached*(conf: ConfigRef; fileIdx: FileIndex; fullpath: AbsoluteFile): string =
|
||||||
result = msgs.getHash(conf, fileIdx)
|
result = msgs.getHash(conf, fileIdx)
|
||||||
if result.len == 0:
|
if result.len == 0 and isAbsolute(string fullpath):
|
||||||
result = $secureHashFile(string fullpath)
|
result = $secureHashFile(string fullpath)
|
||||||
msgs.setHash(conf, fileIdx, result)
|
msgs.setHash(conf, fileIdx, result)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,8 +77,6 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P
|
||||||
if result == nil:
|
if result == nil:
|
||||||
result = newModule(graph, fileIdx)
|
result = newModule(graph, fileIdx)
|
||||||
result.flags = result.flags + flags
|
result.flags = result.flags + flags
|
||||||
if sfMainModule in result.flags:
|
|
||||||
graph.config.mainPackageId = result.owner.id
|
|
||||||
result.id = id
|
result.id = id
|
||||||
registerModule(graph, result)
|
registerModule(graph, result)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,8 @@ const
|
||||||
nkExportStmt, nkExportExceptStmt, nkFromStmt, nkImportStmt, nkImportExceptStmt}
|
nkExportStmt, nkExportExceptStmt, nkFromStmt, nkImportStmt, nkImportExceptStmt}
|
||||||
|
|
||||||
proc prepareConfigNotes(graph: ModuleGraph; module: PSym) =
|
proc prepareConfigNotes(graph: ModuleGraph; module: PSym) =
|
||||||
|
if sfMainModule in module.flags:
|
||||||
|
graph.config.mainPackageId = module.owner.id
|
||||||
# don't be verbose unless the module belongs to the main package:
|
# don't be verbose unless the module belongs to the main package:
|
||||||
if module.owner.id == graph.config.mainPackageId:
|
if module.owner.id == graph.config.mainPackageId:
|
||||||
graph.config.notes = graph.config.mainPackageNotes
|
graph.config.notes = graph.config.mainPackageNotes
|
||||||
|
|
@ -121,7 +123,7 @@ proc prepareConfigNotes(graph: ModuleGraph; module: PSym) =
|
||||||
graph.config.notes = graph.config.foreignPackageNotes
|
graph.config.notes = graph.config.foreignPackageNotes
|
||||||
|
|
||||||
proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} =
|
proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} =
|
||||||
result = module.id >= 0
|
result = module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange")
|
||||||
|
|
||||||
proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {.discardable.} =
|
proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {.discardable.} =
|
||||||
if graph.stopCompile(): return true
|
if graph.stopCompile(): return true
|
||||||
|
|
@ -131,7 +133,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {
|
||||||
s: PLLStream
|
s: PLLStream
|
||||||
fileIdx = module.fileIdx
|
fileIdx = module.fileIdx
|
||||||
prepareConfigNotes(graph, module)
|
prepareConfigNotes(graph, module)
|
||||||
if not moduleHasChanged(graph, module):
|
if module.id < 0:
|
||||||
# new module caching mechanism:
|
# new module caching mechanism:
|
||||||
for i in 0 ..< graph.passes.len:
|
for i in 0 ..< graph.passes.len:
|
||||||
if not isNil(graph.passes[i].open) and not graph.passes[i].isFrontend:
|
if not isNil(graph.passes[i].open) and not graph.passes[i].isFrontend:
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types,
|
||||||
## - Dependency computation should use *signature* hashes in order to
|
## - Dependency computation should use *signature* hashes in order to
|
||||||
## avoid recompiling dependent modules.
|
## avoid recompiling dependent modules.
|
||||||
## - Patch the rest of the compiler to do lazy loading of proc bodies.
|
## - Patch the rest of the compiler to do lazy loading of proc bodies.
|
||||||
## - Patch the C codegen to cache proc bodies and maybe types.
|
|
||||||
|
|
||||||
template db(): DbConn = g.incr.db
|
template db(): DbConn = g.incr.db
|
||||||
|
|
||||||
|
|
@ -72,9 +71,12 @@ proc getModuleId(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): in
|
||||||
# not changed, so use the cached AST:
|
# not changed, so use the cached AST:
|
||||||
doAssert(result != 0)
|
doAssert(result != 0)
|
||||||
var cycleCheck = initIntSet()
|
var cycleCheck = initIntSet()
|
||||||
if not needsRecompile(g, fileIdx, fullpath, cycleCheck) and not g.incr.configChanged:
|
if not needsRecompile(g, fileIdx, fullpath, cycleCheck):
|
||||||
echo "cached successfully! ", string fullpath
|
if not g.incr.configChanged or g.config.symbolFiles == readOnlySf:
|
||||||
|
#echo "cached successfully! ", string fullpath
|
||||||
return -result
|
return -result
|
||||||
|
elif g.config.symbolFiles == readOnlySf:
|
||||||
|
internalError(g.config, "file needs to be recompiled: " & (string fullpath))
|
||||||
db.exec(sql"update modules set fullHash = ? where id = ?", currentFullhash, module[0])
|
db.exec(sql"update modules set fullHash = ? where id = ?", currentFullhash, module[0])
|
||||||
db.exec(sql"delete from deps where module = ?", module[0])
|
db.exec(sql"delete from deps where module = ?", module[0])
|
||||||
db.exec(sql"delete from types where module = ?", module[0])
|
db.exec(sql"delete from types where module = ?", module[0])
|
||||||
|
|
@ -296,7 +298,7 @@ proc encodeSym(g: ModuleGraph, s: PSym, result: var string) =
|
||||||
pushSym(w, s.owner)
|
pushSym(w, s.owner)
|
||||||
if s.flags != {}:
|
if s.flags != {}:
|
||||||
result.add('$')
|
result.add('$')
|
||||||
encodeVInt(cast[int32](s.flags), result)
|
encodeVBiggestInt(cast[int64](s.flags), result)
|
||||||
if s.magic != mNone:
|
if s.magic != mNone:
|
||||||
result.add('@')
|
result.add('@')
|
||||||
encodeVInt(ord(s.magic), result)
|
encodeVInt(ord(s.magic), result)
|
||||||
|
|
@ -723,7 +725,7 @@ proc loadSymFromBlob(g; b; info: TLineInfo): PSym =
|
||||||
result.owner = loadSym(g, decodeVInt(b.s, b.pos), result.info)
|
result.owner = loadSym(g, decodeVInt(b.s, b.pos), result.info)
|
||||||
if b.s[b.pos] == '$':
|
if b.s[b.pos] == '$':
|
||||||
inc(b.pos)
|
inc(b.pos)
|
||||||
result.flags = cast[TSymFlags](int32(decodeVInt(b.s, b.pos)))
|
result.flags = cast[TSymFlags](decodeVBiggestInt(b.s, b.pos))
|
||||||
if b.s[b.pos] == '@':
|
if b.s[b.pos] == '@':
|
||||||
inc(b.pos)
|
inc(b.pos)
|
||||||
result.magic = TMagic(decodeVInt(b.s, b.pos))
|
result.magic = TMagic(decodeVInt(b.s, b.pos))
|
||||||
|
|
@ -756,6 +758,7 @@ proc loadSymFromBlob(g; b; info: TLineInfo): PSym =
|
||||||
if b.s[b.pos] == '\24':
|
if b.s[b.pos] == '\24':
|
||||||
inc b.pos
|
inc b.pos
|
||||||
result.transformedBody = decodeNode(g, b, result.info)
|
result.transformedBody = decodeNode(g, b, result.info)
|
||||||
|
#result.transformedBody = nil
|
||||||
of skModule, skPackage:
|
of skModule, skPackage:
|
||||||
decodeInstantiations(g, b, result.info, result.usedGenerics)
|
decodeInstantiations(g, b, result.info, result.usedGenerics)
|
||||||
of skLet, skVar, skField, skForVar:
|
of skLet, skVar, skField, skForVar:
|
||||||
|
|
@ -769,7 +772,7 @@ proc loadSymFromBlob(g; b; info: TLineInfo): PSym =
|
||||||
|
|
||||||
if b.s[b.pos] == '(':
|
if b.s[b.pos] == '(':
|
||||||
#if result.kind in routineKinds:
|
#if result.kind in routineKinds:
|
||||||
# result.ast = decodeNodeLazyBody(b, result.info, result)
|
# result.ast = nil
|
||||||
#else:
|
#else:
|
||||||
result.ast = decodeNode(g, b, result.info)
|
result.ast = decodeNode(g, b, result.info)
|
||||||
if sfCompilerProc in result.flags:
|
if sfCompilerProc in result.flags:
|
||||||
|
|
@ -886,6 +889,10 @@ proc replay(g: ModuleGraph; module: PSym; n: PNode) =
|
||||||
internalAssert g.config, imported.id < 0
|
internalAssert g.config, imported.id < 0
|
||||||
of nkStmtList, nkStmtListExpr:
|
of nkStmtList, nkStmtListExpr:
|
||||||
for x in n: replay(g, module, x)
|
for x in n: replay(g, module, x)
|
||||||
|
of nkExportStmt:
|
||||||
|
for x in n:
|
||||||
|
doAssert x.kind == nkSym
|
||||||
|
strTableAdd(module.tab, x.sym)
|
||||||
else: discard "nothing to do for this node"
|
else: discard "nothing to do for this node"
|
||||||
|
|
||||||
proc loadNode*(g: ModuleGraph; module: PSym): PNode =
|
proc loadNode*(g: ModuleGraph; module: PSym): PNode =
|
||||||
|
|
|
||||||
|
|
@ -46,14 +46,9 @@ proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
|
||||||
of fcNegInf:
|
of fcNegInf:
|
||||||
result = "-INF"
|
result = "-INF"
|
||||||
else:
|
else:
|
||||||
when defined(nimNoArrayToCstringConversion):
|
|
||||||
result = newString(81)
|
result = newString(81)
|
||||||
let n = c_snprintf(result.cstring, result.len.uint, "%#.16e%s", f, literalPostfix.cstring)
|
let n = c_snprintf(result.cstring, result.len.uint, "%#.16e%s", f, literalPostfix.cstring)
|
||||||
setLen(result, n)
|
setLen(result, n)
|
||||||
else:
|
|
||||||
var buf: array[0..80, char]
|
|
||||||
discard c_snprintf(buf.cstring, buf.len.uint, "%#.16e%s", f, literalPostfix.cstring)
|
|
||||||
result = $buf.cstring
|
|
||||||
|
|
||||||
proc encodeStr*(s: string, result: var string) =
|
proc encodeStr*(s: string, result: var string) =
|
||||||
for i in 0 ..< len(s):
|
for i in 0 ..< len(s):
|
||||||
|
|
|
||||||
|
|
@ -95,8 +95,8 @@ and code execution in the executable.
|
||||||
|
|
||||||
The compiler parses Nim source code into an internal data structure called the
|
The compiler parses Nim source code into an internal data structure called the
|
||||||
`abstract syntax tree`:idx: (`AST`:idx:). Then, before executing the code or
|
`abstract syntax tree`:idx: (`AST`:idx:). Then, before executing the code or
|
||||||
compiling it into the executable, it transforms the AST through `semantic
|
compiling it into the executable, it transforms the AST through
|
||||||
analysis`:idx:. This adds semantic information such as expression types,
|
`semantic analysis`:idx:. This adds semantic information such as expression types,
|
||||||
identifier meanings, and in some cases expression values. An error detected
|
identifier meanings, and in some cases expression values. An error detected
|
||||||
during semantic analysis is called a `static error`:idx:. Errors described in
|
during semantic analysis is called a `static error`:idx:. Errors described in
|
||||||
this manual are static errors when not otherwise specified.
|
this manual are static errors when not otherwise specified.
|
||||||
|
|
|
||||||
2
koch.nim
2
koch.nim
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
const
|
const
|
||||||
NimbleStableCommit = "d15c8530cb7480ce39ffa85a2dd9819d2d4fc645" # 0.10.2
|
NimbleStableCommit = "da82e3111e662fc1b12f96b3cddd66c749c0f686" # master
|
||||||
|
|
||||||
when defined(gcc) and defined(windows):
|
when defined(gcc) and defined(windows):
|
||||||
when defined(x86):
|
when defined(x86):
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,9 @@
|
||||||
## Include for the tester that contains test suites that test special features
|
## Include for the tester that contains test suites that test special features
|
||||||
## of the compiler.
|
## of the compiler.
|
||||||
|
|
||||||
|
# included from tester.nim
|
||||||
|
|
||||||
import important_packages
|
import important_packages
|
||||||
import sequtils
|
|
||||||
|
|
||||||
const
|
const
|
||||||
specialCategories = [
|
specialCategories = [
|
||||||
|
|
@ -24,6 +25,7 @@ const
|
||||||
"gc",
|
"gc",
|
||||||
"io",
|
"io",
|
||||||
"js",
|
"js",
|
||||||
|
"ic",
|
||||||
"lib",
|
"lib",
|
||||||
"longgc",
|
"longgc",
|
||||||
"manyloc",
|
"manyloc",
|
||||||
|
|
@ -41,58 +43,52 @@ const
|
||||||
"dir with space"
|
"dir with space"
|
||||||
]
|
]
|
||||||
|
|
||||||
# included from tester.nim
|
proc isTestFile*(file: string): bool =
|
||||||
# ---------------- ROD file tests ---------------------------------------------
|
let (_, name, ext) = splitFile(file)
|
||||||
|
result = ext == ".nim" and name.startsWith("t")
|
||||||
|
|
||||||
|
# ---------------- IC tests ---------------------------------------------
|
||||||
|
|
||||||
|
proc icTests(r: var TResults; testsDir: string, cat: Category, options: string) =
|
||||||
const
|
const
|
||||||
rodfilesDir = "tests/rodfiles"
|
tooltests = ["compiler/nim.nim", "tools/nimgrep.nim"]
|
||||||
|
writeOnly = " --incremental:writeonly "
|
||||||
|
readOnly = " --incremental:readonly "
|
||||||
|
incrementalOn = " --incremental:on "
|
||||||
|
|
||||||
proc delNimCache(filename, options: string) =
|
template test(x: untyped) =
|
||||||
for target in low(TTarget)..high(TTarget):
|
testSpecWithNimcache(r, makeRawTest(file, x & options, cat), nimcache)
|
||||||
let dir = nimcacheDir(filename, options, target)
|
|
||||||
try:
|
|
||||||
removeDir(dir)
|
|
||||||
except OSError:
|
|
||||||
echo "[Warning] could not delete: ", dir
|
|
||||||
|
|
||||||
proc runRodFiles(r: var TResults, cat: Category, options: string) =
|
template editedTest(x: untyped) =
|
||||||
template test(filename: string, clearCacheFirst=false) =
|
var test = makeTest(file, x & options, cat)
|
||||||
if clearCacheFirst: delNimCache(filename, options)
|
test.spec.targets = {getTestSpecTarget()}
|
||||||
testSpec r, makeTest(rodfilesDir / filename, options, cat)
|
testSpecWithNimcache(r, test, nimcache)
|
||||||
|
|
||||||
|
const tempExt = "_temp.nim"
|
||||||
|
for it in walkDirRec(testsDir / "ic"):
|
||||||
|
if isTestFile(it) and not it.endsWith(tempExt):
|
||||||
|
let nimcache = nimcacheDir(it, options, getTestSpecTarget())
|
||||||
|
removeDir(nimcache)
|
||||||
|
|
||||||
# test basic recompilation scheme:
|
let content = readFile(it)
|
||||||
test "hallo", true
|
for fragment in content.split("#!EDIT!#"):
|
||||||
test "hallo"
|
let file = it.replace(".nim", tempExt)
|
||||||
when false:
|
writeFile(file, fragment)
|
||||||
# test incremental type information:
|
let oldPassed = r.passed
|
||||||
test "hallo2"
|
editedTest incrementalOn
|
||||||
|
if r.passed != oldPassed+1: break
|
||||||
|
|
||||||
# test type converters:
|
for file in tooltests:
|
||||||
test "aconv", true
|
let nimcache = nimcacheDir(file, options, getTestSpecTarget())
|
||||||
test "bconv"
|
removeDir(nimcache)
|
||||||
|
|
||||||
# test G, A, B example from the documentation; test init sections:
|
let oldPassed = r.passed
|
||||||
test "deada", true
|
test writeOnly
|
||||||
test "deada2"
|
|
||||||
|
|
||||||
when false:
|
if r.passed == oldPassed+1:
|
||||||
# test method generation:
|
test readOnly
|
||||||
test "bmethods", true
|
if r.passed == oldPassed+2:
|
||||||
test "bmethods2"
|
test readOnly & "-d:nimBackendAssumesChange "
|
||||||
|
|
||||||
# test generics:
|
|
||||||
test "tgeneric1", true
|
|
||||||
test "tgeneric2"
|
|
||||||
|
|
||||||
proc compileRodFiles(r: var TResults, cat: Category, options: string) =
|
|
||||||
template test(filename: untyped, clearCacheFirst=true) =
|
|
||||||
if clearCacheFirst: delNimCache(filename, options)
|
|
||||||
testSpec r, makeTest(rodfilesDir / filename, options, cat)
|
|
||||||
|
|
||||||
# test DLL interfacing:
|
|
||||||
test "gtkex1", true
|
|
||||||
test "gtkex2"
|
|
||||||
|
|
||||||
# --------------------- flags tests -------------------------------------------
|
# --------------------- flags tests -------------------------------------------
|
||||||
|
|
||||||
|
|
@ -116,12 +112,6 @@ proc flagTests(r: var TResults, cat: Category, options: string) =
|
||||||
|
|
||||||
# --------------------- DLL generation tests ----------------------------------
|
# --------------------- DLL generation tests ----------------------------------
|
||||||
|
|
||||||
proc safeCopyFile(src, dest: string) =
|
|
||||||
try:
|
|
||||||
copyFile(src, dest)
|
|
||||||
except OSError:
|
|
||||||
echo "[Warning] could not copy: ", src, " to ", dest
|
|
||||||
|
|
||||||
proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
|
proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
|
||||||
const rpath = when defined(macosx):
|
const rpath = when defined(macosx):
|
||||||
" --passL:-rpath --passL:@loader_path"
|
" --passL:-rpath --passL:@loader_path"
|
||||||
|
|
@ -153,11 +143,12 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
|
||||||
|
|
||||||
if "boehm" notin options:
|
if "boehm" notin options:
|
||||||
# force build required - see the comments in the .nim file for more details
|
# force build required - see the comments in the .nim file for more details
|
||||||
var hcr_integration = makeTest("tests/dll/nimhcr_integration.nim",
|
var hcri = makeTest("tests/dll/nimhcr_integration.nim",
|
||||||
options & " --forceBuild --hotCodeReloading:on" & rpath, cat)
|
options & " --forceBuild --hotCodeReloading:on" & rpath, cat)
|
||||||
hcr_integration.args = prepareTestArgs(hcr_integration.spec.getCmd, hcr_integration.name,
|
let nimcache = nimcacheDir(hcri.name, hcri.options, getTestSpecTarget())
|
||||||
hcr_integration.options, getTestSpecTarget())
|
hcri.args = prepareTestArgs(hcri.spec.getCmd, hcri.name,
|
||||||
testSpec r, hcr_integration
|
hcri.options, nimcache, getTestSpecTarget())
|
||||||
|
testSpec r, hcri
|
||||||
|
|
||||||
proc dllTests(r: var TResults, cat: Category, options: string) =
|
proc dllTests(r: var TResults, cat: Category, options: string) =
|
||||||
# dummy compile result:
|
# dummy compile result:
|
||||||
|
|
@ -363,7 +354,8 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) =
|
||||||
for i, test in tests:
|
for i, test in tests:
|
||||||
let filename = testsDir / test.addFileExt("nim")
|
let filename = testsDir / test.addFileExt("nim")
|
||||||
let testHash = getMD5(readFile(filename).string)
|
let testHash = getMD5(readFile(filename).string)
|
||||||
doAssert testHash == refHashes[i], "Nim in Action test " & filename & " was changed: " & $(i: i, testHash: testHash, refHash: refHashes[i])
|
doAssert testHash == refHashes[i], "Nim in Action test " & filename &
|
||||||
|
" was changed: " & $(i: i, testHash: testHash, refHash: refHashes[i])
|
||||||
# Run the tests.
|
# Run the tests.
|
||||||
for testfile in tests:
|
for testfile in tests:
|
||||||
test "tests/" & testfile & ".nim"
|
test "tests/" & testfile & ".nim"
|
||||||
|
|
@ -449,25 +441,6 @@ let
|
||||||
nimbleExe = findExe("nimble")
|
nimbleExe = findExe("nimble")
|
||||||
packageIndex = nimbleDir / "packages_official.json"
|
packageIndex = nimbleDir / "packages_official.json"
|
||||||
|
|
||||||
proc waitForExitEx(p: Process): int =
|
|
||||||
var outp = outputStream(p)
|
|
||||||
var line = newStringOfCap(120).TaintedString
|
|
||||||
while true:
|
|
||||||
if outp.readLine(line):
|
|
||||||
discard
|
|
||||||
else:
|
|
||||||
result = peekExitCode(p)
|
|
||||||
if result != -1: break
|
|
||||||
close(p)
|
|
||||||
|
|
||||||
proc getPackageDir(package: string): string =
|
|
||||||
## TODO - Replace this with dom's version comparison magic.
|
|
||||||
let commandOutput = execCmdEx("nimble path $#" % package)
|
|
||||||
if commandOutput.exitCode != QuitSuccess:
|
|
||||||
return ""
|
|
||||||
else:
|
|
||||||
result = commandOutput[0].string
|
|
||||||
|
|
||||||
iterator listPackages(): tuple[name, url, cmd: string, hasDeps: bool] =
|
iterator listPackages(): tuple[name, url, cmd: string, hasDeps: bool] =
|
||||||
let defaultCmd = "nimble test"
|
let defaultCmd = "nimble test"
|
||||||
let packageList = parseFile(packageIndex)
|
let packageList = parseFile(packageIndex)
|
||||||
|
|
@ -481,8 +454,8 @@ iterator listPackages(): tuple[name, url, cmd: string, hasDeps: bool] =
|
||||||
let name = package["name"].str
|
let name = package["name"].str
|
||||||
if name == n:
|
if name == n:
|
||||||
found = true
|
found = true
|
||||||
let p_url = package["url"].str
|
let pUrl = package["url"].str
|
||||||
yield (name, p_url, cmd, hasDeps)
|
yield (name, pUrl, cmd, hasDeps)
|
||||||
break
|
break
|
||||||
if not found:
|
if not found:
|
||||||
raise newException(ValueError, "Cannot find package '$#'." % n)
|
raise newException(ValueError, "Cannot find package '$#'." % n)
|
||||||
|
|
@ -551,7 +524,7 @@ proc testNimblePackages(r: var TResults, cat: Category) =
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
const AdditionalCategories = ["debugger", "examples", "lib"]
|
const AdditionalCategories = ["debugger", "examples", "lib", "ic"]
|
||||||
const MegaTestCat = "megatest"
|
const MegaTestCat = "megatest"
|
||||||
|
|
||||||
proc `&.?`(a, b: string): string =
|
proc `&.?`(a, b: string): string =
|
||||||
|
|
@ -584,17 +557,12 @@ proc isJoinableSpec(spec: TSpec): bool =
|
||||||
(spec.targets == {} or spec.targets == {targetC})
|
(spec.targets == {} or spec.targets == {targetC})
|
||||||
|
|
||||||
proc norm(s: var string) =
|
proc norm(s: var string) =
|
||||||
# equivalent of s/\n+/\n/g (could use a single pass over input if needed)
|
|
||||||
while true:
|
while true:
|
||||||
let tmp = s.replace("\n\n", "\n")
|
let tmp = s.replace("\n\n", "\n")
|
||||||
if tmp == s: break
|
if tmp == s: break
|
||||||
s = tmp
|
s = tmp
|
||||||
s = s.strip
|
s = s.strip
|
||||||
|
|
||||||
proc isTestFile*(file: string): bool =
|
|
||||||
let (_, name, ext) = splitFile(file)
|
|
||||||
result = ext == ".nim" and name.startsWith("t")
|
|
||||||
|
|
||||||
proc quoted(a: string): string =
|
proc quoted(a: string): string =
|
||||||
# todo: consider moving to system.nim
|
# todo: consider moving to system.nim
|
||||||
result.addQuoted(a)
|
result.addQuoted(a)
|
||||||
|
|
@ -607,7 +575,7 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) =
|
||||||
let cat = dir[testsDir.len .. ^1]
|
let cat = dir[testsDir.len .. ^1]
|
||||||
if kind == pcDir and cat notin specialCategories:
|
if kind == pcDir and cat notin specialCategories:
|
||||||
for file in walkDirRec(testsDir / cat):
|
for file in walkDirRec(testsDir / cat):
|
||||||
if not isTestFile(file): continue
|
if isTestFile(file):
|
||||||
let spec = parseSpec(file)
|
let spec = parseSpec(file)
|
||||||
if isJoinableSpec(spec):
|
if isJoinableSpec(spec):
|
||||||
specs.add spec
|
specs.add spec
|
||||||
|
|
@ -646,14 +614,13 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) =
|
||||||
|
|
||||||
let args = ["c", "--nimCache:" & outDir, "-d:testing", "--listCmd",
|
let args = ["c", "--nimCache:" & outDir, "-d:testing", "--listCmd",
|
||||||
"--listFullPaths:off", "--excessiveStackTrace:off", "megatest.nim"]
|
"--listFullPaths:off", "--excessiveStackTrace:off", "megatest.nim"]
|
||||||
proc onStdout(line: string) = echo line
|
|
||||||
var (cmdLine, buf, exitCode) = execCmdEx2(command = compilerPrefix, args = args, input = "")
|
var (cmdLine, buf, exitCode) = execCmdEx2(command = compilerPrefix, args = args, input = "")
|
||||||
if exitCode != 0:
|
if exitCode != 0:
|
||||||
echo "$ ", cmdLine
|
echo "$ ", cmdLine
|
||||||
echo buf.string
|
echo buf.string
|
||||||
quit("megatest compilation failed")
|
quit("megatest compilation failed")
|
||||||
|
|
||||||
# Could also use onStdout here.
|
|
||||||
(buf, exitCode) = execCmdEx("./megatest")
|
(buf, exitCode) = execCmdEx("./megatest")
|
||||||
if exitCode != 0:
|
if exitCode != 0:
|
||||||
echo buf.string
|
echo buf.string
|
||||||
|
|
@ -690,6 +657,8 @@ proc processCategory(r: var TResults, cat: Category,
|
||||||
when false:
|
when false:
|
||||||
compileRodFiles(r, cat, options)
|
compileRodFiles(r, cat, options)
|
||||||
runRodFiles(r, cat, options)
|
runRodFiles(r, cat, options)
|
||||||
|
of "ic":
|
||||||
|
icTests(r, testsDir, cat, options)
|
||||||
of "js":
|
of "js":
|
||||||
# only run the JS tests on Windows or Linux because Travis is bad
|
# only run the JS tests on Windows or Linux because Travis is bad
|
||||||
# and other OSes like Haiku might lack nodejs:
|
# and other OSes like Haiku might lack nodejs:
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
## HTML generator for the tester.
|
## HTML generator for the tester.
|
||||||
|
|
||||||
import cgi, backend, strutils, json, os, tables, times
|
import strutils, json, os, times
|
||||||
|
|
||||||
import "testamenthtml.nimf"
|
import "testamenthtml.nimf"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import strutils
|
|
||||||
|
|
||||||
template pkg(name: string; cmd = "nimble test"; hasDeps = false; url = ""): untyped =
|
template pkg(name: string; cmd = "nimble test"; hasDeps = false; url = ""): untyped =
|
||||||
packages.add((name, cmd, hasDeps, url))
|
packages.add((name, cmd, hasDeps, url))
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
import sequtils, parseutils, strutils, os, osproc, streams, parsecfg
|
import sequtils, parseutils, strutils, os, streams, parsecfg
|
||||||
|
|
||||||
var compilerPrefix* = findExe("nim")
|
var compilerPrefix* = findExe("nim")
|
||||||
|
|
||||||
|
|
@ -122,6 +122,9 @@ proc addLine*(self: var string; a,b: string) =
|
||||||
self.add b
|
self.add b
|
||||||
self.add "\n"
|
self.add "\n"
|
||||||
|
|
||||||
|
proc initSpec*(filename: string): TSpec =
|
||||||
|
result.file = filename
|
||||||
|
|
||||||
proc parseSpec*(filename: string): TSpec =
|
proc parseSpec*(filename: string): TSpec =
|
||||||
result.file = filename
|
result.file = filename
|
||||||
let specStr = extractSpec(filename)
|
let specStr = extractSpec(filename)
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@
|
||||||
## This program verifies Nim against the testcases.
|
## This program verifies Nim against the testcases.
|
||||||
|
|
||||||
import
|
import
|
||||||
parseutils, strutils, pegs, os, osproc, streams, parsecfg, json,
|
strutils, pegs, os, osproc, streams, json,
|
||||||
marshal, backend, parseopt, specs, htmlgen, browsers, terminal,
|
backend, parseopt, specs, htmlgen, browsers, terminal,
|
||||||
algorithm, times, sets, md5, sequtils
|
algorithm, times, md5, sequtils
|
||||||
|
|
||||||
include compiler/nodejs
|
include compiler/nodejs
|
||||||
|
|
||||||
|
|
@ -124,19 +124,20 @@ proc execCmdEx2(command: string, args: openarray[string]; workingDir, input: str
|
||||||
proc nimcacheDir(filename, options: string, target: TTarget): string =
|
proc nimcacheDir(filename, options: string, target: TTarget): string =
|
||||||
## Give each test a private nimcache dir so they don't clobber each other's.
|
## Give each test a private nimcache dir so they don't clobber each other's.
|
||||||
let hashInput = options & $target
|
let hashInput = options & $target
|
||||||
return "nimcache" / (filename & '_' & hashInput.getMD5)
|
result = "nimcache" / (filename & '_' & hashInput.getMD5)
|
||||||
|
|
||||||
proc prepareTestArgs(cmdTemplate, filename, options: string,
|
proc prepareTestArgs(cmdTemplate, filename, options, nimcache: string,
|
||||||
target: TTarget, extraOptions=""): seq[string] =
|
target: TTarget, extraOptions=""): seq[string] =
|
||||||
let nimcache = nimcacheDir(filename, options, target)
|
|
||||||
let options = options & " " & quoteShell("--nimCache:" & nimcache) & extraOptions
|
let options = options & " " & quoteShell("--nimCache:" & nimcache) & extraOptions
|
||||||
return parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
|
result = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
|
||||||
"options", options, "file", filename.quoteShell,
|
"options", options, "file", filename.quoteShell,
|
||||||
"filedir", filename.getFileDir()])
|
"filedir", filename.getFileDir()])
|
||||||
|
|
||||||
proc callCompiler(cmdTemplate, filename, options: string,
|
proc callCompiler(cmdTemplate, filename, options, nimcache: string,
|
||||||
target: TTarget, extraOptions=""): TSpec =
|
target: TTarget,
|
||||||
let c = prepareTestArgs(cmdTemplate, filename, options, target, extraOptions)
|
extraOptions=""): TSpec =
|
||||||
|
let c = prepareTestArgs(cmdTemplate, filename, options, nimcache, target,
|
||||||
|
extraOptions)
|
||||||
result.cmd = quoteShellCommand(c)
|
result.cmd = quoteShellCommand(c)
|
||||||
var p = startProcess(command=c[0], args=c[1 .. ^1],
|
var p = startProcess(command=c[0], args=c[1 .. ^1],
|
||||||
options={poStdErrToStdOut, poUsePath})
|
options={poStdErrToStdOut, poUsePath})
|
||||||
|
|
@ -370,7 +371,7 @@ proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec) =
|
||||||
currentPos = giv.find(line.strip, currentPos)
|
currentPos = giv.find(line.strip, currentPos)
|
||||||
if currentPos < 0:
|
if currentPos < 0:
|
||||||
given.err = reMsgsDiffer
|
given.err = reMsgsDiffer
|
||||||
return
|
break
|
||||||
|
|
||||||
proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec,
|
proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec,
|
||||||
expected: TSpec; r: var TResults) =
|
expected: TSpec; r: var TResults) =
|
||||||
|
|
@ -391,9 +392,9 @@ proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec,
|
||||||
|
|
||||||
proc getTestSpecTarget(): TTarget =
|
proc getTestSpecTarget(): TTarget =
|
||||||
if getEnv("NIM_COMPILE_TO_CPP", "false").string == "true":
|
if getEnv("NIM_COMPILE_TO_CPP", "false").string == "true":
|
||||||
return targetCpp
|
result = targetCpp
|
||||||
else:
|
else:
|
||||||
return targetC
|
result = targetC
|
||||||
|
|
||||||
proc checkDisabled(r: var TResults, test: TTest): bool =
|
proc checkDisabled(r: var TResults, test: TTest): bool =
|
||||||
if test.spec.err in {reDisabled, reJoined}:
|
if test.spec.err in {reDisabled, reJoined}:
|
||||||
|
|
@ -401,59 +402,34 @@ proc checkDisabled(r: var TResults, test: TTest): bool =
|
||||||
r.addResult(test, targetC, "", "", test.spec.err)
|
r.addResult(test, targetC, "", "", test.spec.err)
|
||||||
inc(r.skipped)
|
inc(r.skipped)
|
||||||
inc(r.total)
|
inc(r.total)
|
||||||
return
|
result = false
|
||||||
true
|
else:
|
||||||
|
result = true
|
||||||
|
|
||||||
proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) =
|
var count = 0
|
||||||
var expected = test.spec
|
|
||||||
if expected.parseErrors.len > 0:
|
|
||||||
# targetC is a lie, but parameter is required
|
|
||||||
r.addResult(test, targetC, "", expected.parseErrors, reInvalidSpec)
|
|
||||||
inc(r.total)
|
|
||||||
return
|
|
||||||
if not checkDisabled(r, test): return
|
|
||||||
|
|
||||||
expected.targets.incl targets
|
|
||||||
# still no target specified at all
|
|
||||||
if expected.targets == {}:
|
|
||||||
expected.targets = {getTestSpecTarget()}
|
|
||||||
for target in expected.targets:
|
|
||||||
inc(r.total)
|
|
||||||
if target notin gTargets:
|
|
||||||
r.addResult(test, target, "", "", reDisabled)
|
|
||||||
inc(r.skipped)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if simulate:
|
|
||||||
var count {.global.} = 0
|
|
||||||
count.inc
|
|
||||||
echo "testSpec count: ", count, " expected: ", expected
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
proc testSpecHelper(r: var TResults, test: TTest, expected: TSpec, target: TTarget, nimcache: string) =
|
||||||
case expected.action
|
case expected.action
|
||||||
of actionCompile:
|
of actionCompile:
|
||||||
var given = callCompiler(expected.getCmd, test.name, test.options, target,
|
var given = callCompiler(expected.getCmd, test.name, test.options, nimcache, target,
|
||||||
extraOptions = " --stdout --hint[Path]:off --hint[Processing]:off")
|
extraOptions = " --stdout --hint[Path]:off --hint[Processing]:off")
|
||||||
compilerOutputTests(test, target, given, expected, r)
|
compilerOutputTests(test, target, given, expected, r)
|
||||||
of actionRun:
|
of actionRun:
|
||||||
# In this branch of code "early return" pattern is clearer than deep
|
var given = callCompiler(expected.getCmd, test.name, test.options, nimcache, target)
|
||||||
# nested conditionals - the empty rows in between to clarify the "danger"
|
|
||||||
var given = callCompiler(expected.getCmd, test.name, test.options, target)
|
|
||||||
if given.err != reSuccess:
|
if given.err != reSuccess:
|
||||||
r.addResult(test, target, "", "$ " & given.cmd & "\n" & given.nimout, given.err)
|
r.addResult(test, target, "", "$ " & given.cmd & "\n" & given.nimout, given.err)
|
||||||
continue
|
else:
|
||||||
let isJsTarget = target == targetJS
|
let isJsTarget = target == targetJS
|
||||||
var exeFile = changeFileExt(test.name, if isJsTarget: "js" else: ExeExt)
|
var exeFile = changeFileExt(test.name, if isJsTarget: "js" else: ExeExt)
|
||||||
if not existsFile(exeFile):
|
if not existsFile(exeFile):
|
||||||
r.addResult(test, target, expected.output,
|
r.addResult(test, target, expected.output,
|
||||||
"executable not found: " & exeFile, reExeNotFound)
|
"executable not found: " & exeFile, reExeNotFound)
|
||||||
continue
|
else:
|
||||||
|
|
||||||
let nodejs = if isJsTarget: findNodeJs() else: ""
|
let nodejs = if isJsTarget: findNodeJs() else: ""
|
||||||
if isJsTarget and nodejs == "":
|
if isJsTarget and nodejs == "":
|
||||||
r.addResult(test, target, expected.output, "nodejs binary not in PATH",
|
r.addResult(test, target, expected.output, "nodejs binary not in PATH",
|
||||||
reExeNotFound)
|
reExeNotFound)
|
||||||
continue
|
else:
|
||||||
var exeCmd: string
|
var exeCmd: string
|
||||||
var args = test.args
|
var args = test.args
|
||||||
if isJsTarget:
|
if isJsTarget:
|
||||||
|
|
@ -461,7 +437,7 @@ proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) =
|
||||||
args = concat(@[exeFile], args)
|
args = concat(@[exeFile], args)
|
||||||
else:
|
else:
|
||||||
exeCmd = exeFile
|
exeCmd = exeFile
|
||||||
var (cmdLine, buf, exitCode) = execCmdEx2(exeCmd, args, input = expected.input)
|
var (_, buf, exitCode) = execCmdEx2(exeCmd, args, input = expected.input)
|
||||||
# Treat all failure codes from nodejs as 1. Older versions of nodejs used
|
# Treat all failure codes from nodejs as 1. Older versions of nodejs used
|
||||||
# to return other codes, but for us it is sufficient to know that it's not 0.
|
# to return other codes, but for us it is sufficient to know that it's not 0.
|
||||||
if exitCode != 0: exitCode = 1
|
if exitCode != 0: exitCode = 1
|
||||||
|
|
@ -476,19 +452,48 @@ proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) =
|
||||||
r.addResult(test, target, "exitcode: " & $expected.exitCode,
|
r.addResult(test, target, "exitcode: " & $expected.exitCode,
|
||||||
"exitcode: " & $exitCode & "\n\nOutput:\n" &
|
"exitcode: " & $exitCode & "\n\nOutput:\n" &
|
||||||
bufB, reExitCodesDiffer)
|
bufB, reExitCodesDiffer)
|
||||||
continue
|
elif (expected.outputCheck == ocEqual and expected.output != bufB) or
|
||||||
if (expected.outputCheck == ocEqual and expected.output != bufB) or
|
|
||||||
(expected.outputCheck == ocSubstr and expected.output notin bufB):
|
(expected.outputCheck == ocSubstr and expected.output notin bufB):
|
||||||
given.err = reOutputsDiffer
|
given.err = reOutputsDiffer
|
||||||
r.addResult(test, target, expected.output, bufB, reOutputsDiffer)
|
r.addResult(test, target, expected.output, bufB, reOutputsDiffer)
|
||||||
continue
|
else:
|
||||||
compilerOutputTests(test, target, given, expected, r)
|
compilerOutputTests(test, target, given, expected, r)
|
||||||
continue
|
|
||||||
of actionReject:
|
of actionReject:
|
||||||
var given = callCompiler(expected.getCmd, test.name, test.options,
|
var given = callCompiler(expected.getCmd, test.name, test.options,
|
||||||
target)
|
nimcache, target)
|
||||||
cmpMsgs(r, expected, given, test, target)
|
cmpMsgs(r, expected, given, test, target)
|
||||||
continue
|
|
||||||
|
|
||||||
|
proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) =
|
||||||
|
var expected = test.spec
|
||||||
|
if expected.parseErrors.len > 0:
|
||||||
|
# targetC is a lie, but a parameter is required
|
||||||
|
r.addResult(test, targetC, "", expected.parseErrors, reInvalidSpec)
|
||||||
|
inc(r.total)
|
||||||
|
return
|
||||||
|
if not checkDisabled(r, test): return
|
||||||
|
|
||||||
|
expected.targets.incl targets
|
||||||
|
# still no target specified at all
|
||||||
|
if expected.targets == {}:
|
||||||
|
expected.targets = {getTestSpecTarget()}
|
||||||
|
for target in expected.targets:
|
||||||
|
inc(r.total)
|
||||||
|
if target notin gTargets:
|
||||||
|
r.addResult(test, target, "", "", reDisabled)
|
||||||
|
inc(r.skipped)
|
||||||
|
elif simulate:
|
||||||
|
inc count
|
||||||
|
echo "testSpec count: ", count, " expected: ", expected
|
||||||
|
else:
|
||||||
|
let nimcache = nimcacheDir(test.name, test.options, target)
|
||||||
|
testSpecHelper(r, test, expected, target, nimcache)
|
||||||
|
|
||||||
|
proc testSpecWithNimcache(r: var TResults, test: TTest; nimcache: string) =
|
||||||
|
if not checkDisabled(r, test): return
|
||||||
|
for target in test.spec.targets:
|
||||||
|
inc(r.total)
|
||||||
|
testSpecHelper(r, test, test.spec, target, nimcache)
|
||||||
|
|
||||||
proc testC(r: var TResults, test: TTest, action: TTestAction) =
|
proc testC(r: var TResults, test: TTest, action: TTestAction) =
|
||||||
# runs C code. Doesn't support any specs, just goes by exit code.
|
# runs C code. Doesn't support any specs, just goes by exit code.
|
||||||
|
|
@ -529,6 +534,15 @@ proc makeTest(test, options: string, cat: Category): TTest =
|
||||||
result.spec = parseSpec(addFileExt(test, ".nim"))
|
result.spec = parseSpec(addFileExt(test, ".nim"))
|
||||||
result.startTime = epochTime()
|
result.startTime = epochTime()
|
||||||
|
|
||||||
|
proc makeRawTest(test, options: string, cat: Category): TTest =
|
||||||
|
result.cat = cat
|
||||||
|
result.name = test
|
||||||
|
result.options = options
|
||||||
|
result.spec = initSpec(addFileExt(test, ".nim"))
|
||||||
|
result.startTime = epochTime()
|
||||||
|
result.spec.action = actionCompile
|
||||||
|
result.spec.targets = {getTestSpecTarget()}
|
||||||
|
|
||||||
# TODO: fix these files
|
# TODO: fix these files
|
||||||
const disabledFilesDefault = @[
|
const disabledFilesDefault = @[
|
||||||
"LockFreeHash.nim",
|
"LockFreeHash.nim",
|
||||||
|
|
@ -553,23 +567,18 @@ when defined(windows):
|
||||||
else:
|
else:
|
||||||
const
|
const
|
||||||
# array of modules disabled from compilation test of stdlib.
|
# array of modules disabled from compilation test of stdlib.
|
||||||
# TODO: why the ["-"]? (previous code should've prob used seq[string] = @[] instead)
|
disabledFiles = disabledFilesDefault
|
||||||
disabledFiles = disabledFilesDefault & @["-"]
|
|
||||||
|
|
||||||
include categories
|
include categories
|
||||||
|
|
||||||
proc loadSkipFrom(name: string): seq[string] =
|
proc loadSkipFrom(name: string): seq[string] =
|
||||||
if name.len() == 0: return
|
if name.len == 0: return
|
||||||
|
|
||||||
# One skip per line, comments start with #
|
# One skip per line, comments start with #
|
||||||
# used by `nlvm` (at least)
|
# used by `nlvm` (at least)
|
||||||
try:
|
|
||||||
for line in lines(name):
|
for line in lines(name):
|
||||||
let sline = line.strip()
|
let sline = line.strip()
|
||||||
if sline.len > 0 and not sline.startsWith("#"):
|
if sline.len > 0 and not sline.startsWith("#"):
|
||||||
result.add sline
|
result.add sline
|
||||||
except:
|
|
||||||
echo "Could not load " & name & ", ignoring"
|
|
||||||
|
|
||||||
proc main() =
|
proc main() =
|
||||||
os.putenv "NIMTEST_COLOR", "never"
|
os.putenv "NIMTEST_COLOR", "never"
|
||||||
|
|
|
||||||
39
tests/ic/tgenerics.nim
Normal file
39
tests/ic/tgenerics.nim
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
discard """
|
||||||
|
output: "bar"
|
||||||
|
disabled: "true"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import tables
|
||||||
|
|
||||||
|
var tab: Table[string, string]
|
||||||
|
|
||||||
|
tab["foo"] = "bar"
|
||||||
|
echo tab["foo"]
|
||||||
|
|
||||||
|
#!EDIT!#
|
||||||
|
|
||||||
|
discard """
|
||||||
|
output: "bar 3"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import tables
|
||||||
|
|
||||||
|
var tab: Table[string, string]
|
||||||
|
var tab2: Table[string, int]
|
||||||
|
|
||||||
|
tab["foo"] = "bar"
|
||||||
|
tab2["meh"] = 3
|
||||||
|
echo tab["foo"], " ", tab2["meh"]
|
||||||
|
|
||||||
|
#!EDIT!#
|
||||||
|
|
||||||
|
discard """
|
||||||
|
output: "3"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import tables
|
||||||
|
|
||||||
|
var tab2: Table[string, int]
|
||||||
|
|
||||||
|
tab2["meh"] = 3
|
||||||
|
echo tab2["meh"]
|
||||||
28
tests/ic/thallo.nim
Normal file
28
tests/ic/thallo.nim
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
discard """
|
||||||
|
output: "Hello World"
|
||||||
|
"""
|
||||||
|
|
||||||
|
const str = "Hello World"
|
||||||
|
echo str
|
||||||
|
|
||||||
|
# Splitters are done with this special comment:
|
||||||
|
|
||||||
|
#!EDIT!#
|
||||||
|
|
||||||
|
discard """
|
||||||
|
output: "Hello World B"
|
||||||
|
"""
|
||||||
|
|
||||||
|
const str = "Hello World"
|
||||||
|
echo str, " B"
|
||||||
|
|
||||||
|
#!EDIT!#
|
||||||
|
|
||||||
|
discard """
|
||||||
|
output: "Hello World C"
|
||||||
|
"""
|
||||||
|
|
||||||
|
const str = "Hello World"
|
||||||
|
var x = 7
|
||||||
|
if 3+4 == x:
|
||||||
|
echo str, " C"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue