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:
Andreas Rumpf 2019-08-08 08:41:05 +02:00 • committed by GitHub
commit c8cffaf420
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 318 additions and 261 deletions

View file

@ -10,9 +10,9 @@
## This program verifies Nim against the testcases.
import
parseutils, strutils, pegs, os, osproc, streams, parsecfg, json,
marshal, backend, parseopt, specs, htmlgen, browsers, terminal,
algorithm, times, sets, md5, sequtils
strutils, pegs, os, osproc, streams, json,
backend, parseopt, specs, htmlgen, browsers, terminal,
algorithm, times, md5, sequtils
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 =
## Give each test a private nimcache dir so they don't clobber each other's.
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] =
let nimcache = nimcacheDir(filename, options, target)
let options = options & " " & quoteShell("--nimCache:" & nimcache) & extraOptions
return parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
result = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
"options", options, "file", filename.quoteShell,
"filedir", filename.getFileDir()])
proc callCompiler(cmdTemplate, filename, options: string,
target: TTarget, extraOptions=""): TSpec =
let c = prepareTestArgs(cmdTemplate, filename, options, target, extraOptions)
proc callCompiler(cmdTemplate, filename, options, nimcache: string,
target: TTarget,
extraOptions=""): TSpec =
let c = prepareTestArgs(cmdTemplate, filename, options, nimcache, target,
extraOptions)
result.cmd = quoteShellCommand(c)
var p = startProcess(command=c[0], args=c[1 .. ^1],
options={poStdErrToStdOut, poUsePath})
@ -370,7 +371,7 @@ proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec) =
currentPos = giv.find(line.strip, currentPos)
if currentPos < 0:
given.err = reMsgsDiffer
return
break
proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec,
expected: TSpec; r: var TResults) =
@ -391,9 +392,9 @@ proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec,
proc getTestSpecTarget(): TTarget =
if getEnv("NIM_COMPILE_TO_CPP", "false").string == "true":
return targetCpp
result = targetCpp
else:
return targetC
result = targetC
proc checkDisabled(r: var TResults, test: TTest): bool =
if test.spec.err in {reDisabled, reJoined}:
@ -401,13 +402,72 @@ proc checkDisabled(r: var TResults, test: TTest): bool =
r.addResult(test, targetC, "", "", test.spec.err)
inc(r.skipped)
inc(r.total)
return
true
result = false
else:
result = true
var count = 0
proc testSpecHelper(r: var TResults, test: TTest, expected: TSpec, target: TTarget, nimcache: string) =
case expected.action
of actionCompile:
var given = callCompiler(expected.getCmd, test.name, test.options, nimcache, target,
extraOptions = " --stdout --hint[Path]:off --hint[Processing]:off")
compilerOutputTests(test, target, given, expected, r)
of actionRun:
var given = callCompiler(expected.getCmd, test.name, test.options, nimcache, target)
if given.err != reSuccess:
r.addResult(test, target, "", "$ " & given.cmd & "\n" & given.nimout, given.err)
else:
let isJsTarget = target == targetJS
var exeFile = changeFileExt(test.name, if isJsTarget: "js" else: ExeExt)
if not existsFile(exeFile):
r.addResult(test, target, expected.output,
"executable not found: " & exeFile, reExeNotFound)
else:
let nodejs = if isJsTarget: findNodeJs() else: ""
if isJsTarget and nodejs == "":
r.addResult(test, target, expected.output, "nodejs binary not in PATH",
reExeNotFound)
else:
var exeCmd: string
var args = test.args
if isJsTarget:
exeCmd = nodejs
args = concat(@[exeFile], args)
else:
exeCmd = exeFile
var (_, buf, exitCode) = execCmdEx2(exeCmd, args, input = expected.input)
# 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.
if exitCode != 0: exitCode = 1
let bufB =
if expected.sortoutput:
var x = splitLines(strip(buf.string))
sort(x, system.cmp)
join(x, "\n")
else:
strip(buf.string)
if exitCode != expected.exitCode:
r.addResult(test, target, "exitcode: " & $expected.exitCode,
"exitcode: " & $exitCode & "\n\nOutput:\n" &
bufB, reExitCodesDiffer)
elif (expected.outputCheck == ocEqual and expected.output != bufB) or
(expected.outputCheck == ocSubstr and expected.output notin bufB):
given.err = reOutputsDiffer
r.addResult(test, target, expected.output, bufB, reOutputsDiffer)
else:
compilerOutputTests(test, target, given, expected, r)
of actionReject:
var given = callCompiler(expected.getCmd, test.name, test.options,
nimcache, target)
cmpMsgs(r, expected, given, test, target)
proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) =
var expected = test.spec
if expected.parseErrors.len > 0:
# targetC is a lie, but parameter is required
# targetC is a lie, but a parameter is required
r.addResult(test, targetC, "", expected.parseErrors, reInvalidSpec)
inc(r.total)
return
@ -422,73 +482,18 @@ proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) =
if target notin gTargets:
r.addResult(test, target, "", "", reDisabled)
inc(r.skipped)
continue
if simulate:
var count {.global.} = 0
count.inc
elif simulate:
inc count
echo "testSpec count: ", count, " expected: ", expected
continue
else:
let nimcache = nimcacheDir(test.name, test.options, target)
testSpecHelper(r, test, expected, target, nimcache)
case expected.action
of actionCompile:
var given = callCompiler(expected.getCmd, test.name, test.options, target,
extraOptions=" --stdout --hint[Path]:off --hint[Processing]:off")
compilerOutputTests(test, target, given, expected, r)
of actionRun:
# In this branch of code "early return" pattern is clearer than deep
# 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:
r.addResult(test, target, "", "$ " & given.cmd & "\n" & given.nimout, given.err)
continue
let isJsTarget = target == targetJS
var exeFile = changeFileExt(test.name, if isJsTarget: "js" else: ExeExt)
if not existsFile(exeFile):
r.addResult(test, target, expected.output,
"executable not found: " & exeFile, reExeNotFound)
continue
let nodejs = if isJsTarget: findNodeJs() else: ""
if isJsTarget and nodejs == "":
r.addResult(test, target, expected.output, "nodejs binary not in PATH",
reExeNotFound)
continue
var exeCmd: string
var args = test.args
if isJsTarget:
exeCmd = nodejs
args = concat(@[exeFile], args)
else:
exeCmd = exeFile
var (cmdLine, buf, exitCode) = execCmdEx2(exeCmd, args, input = expected.input)
# 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.
if exitCode != 0: exitCode = 1
let bufB =
if expected.sortoutput:
var x = splitLines(strip(buf.string))
sort(x, system.cmp)
join(x, "\n")
else:
strip(buf.string)
if exitCode != expected.exitCode:
r.addResult(test, target, "exitcode: " & $expected.exitCode,
"exitcode: " & $exitCode & "\n\nOutput:\n" &
bufB, reExitCodesDiffer)
continue
if (expected.outputCheck == ocEqual and expected.output != bufB) or
(expected.outputCheck == ocSubstr and expected.output notin bufB):
given.err = reOutputsDiffer
r.addResult(test, target, expected.output, bufB, reOutputsDiffer)
continue
compilerOutputTests(test, target, given, expected, r)
continue
of actionReject:
var given = callCompiler(expected.getCmd, test.name, test.options,
target)
cmpMsgs(r, expected, given, test, target)
continue
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) =
# 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.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
const disabledFilesDefault = @[
"LockFreeHash.nim",
@ -553,23 +567,18 @@ when defined(windows):
else:
const
# 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
proc loadSkipFrom(name: string): seq[string] =
if name.len() == 0: return
if name.len == 0: return
# One skip per line, comments start with #
# used by `nlvm` (at least)
try:
for line in lines(name):
let sline = line.strip()
if sline.len > 0 and not sline.startsWith("#"):
result.add sline
except:
echo "Could not load " & name & ", ignoring"
for line in lines(name):
let sline = line.strip()
if sline.len > 0 and not sline.startsWith("#"):
result.add sline
proc main() =
os.putenv "NIMTEST_COLOR", "never"