diff --git a/contexts.nim b/contexts.nim new file mode 100644 index 0000000..b6351e3 --- /dev/null +++ b/contexts.nim @@ -0,0 +1,29 @@ +import tables + +type TaskContext* = ref object + caption*: string + timing*: int64 + info*: seq[string] + warn*: seq[string] + err*: seq[string] + skipped*: bool + +type GlobalContext* = ref object + stop*:bool + timing*: int64 + tasks*: OrderedTable[string, TaskContext] + completed*: OrderedTable[string, TaskContext] + +var + context = GlobalContext() + +context.tasks = initOrderedTable[string, TaskContext]() + +proc globalContext*(): GlobalContext = + return context + +proc getOrAddContext*(name: string): var TaskContext = + if not context.tasks.contains(name): + let newContext = TaskContext(caption: name, info: @[], err: @[], skipped: true) + context.tasks.add(name, newContext) + return context.tasks[name] \ No newline at end of file diff --git a/nimb.nim b/nimb.nim index a260fa1..1a034cc 100644 --- a/nimb.nim +++ b/nimb.nim @@ -14,7 +14,7 @@ let currDir = getCurrentDir() putEnv("nimbfilePath", currDir) let nimbfilePath = currDir / buildFile -echo "Nimbfile path: " & nimbfilePath +echo "nimbfile path: " & nimbfilePath if not existsFile(nimbfilePath): quit("error: nimb file not found", 1) diff --git a/tasks/assemblyInfo.nim b/tasks/assemblyInfo.nim index 4b7269c..ad30274 100644 --- a/tasks/assemblyInfo.nim +++ b/tasks/assemblyInfo.nim @@ -1,12 +1,15 @@ -template assemblyinfoOf*(body: untyped) = - proc `assemblyinfoOf Task`*() = - when not declaredInScope(outputPath): - var outputPath {.inject.}: string - var asmversion {.inject.}: Version - var copyright {.inject.}: string +import ../contexts +import version +template assemblyinfoOf*(context: TaskContext; body: untyped) = + when not declaredInScope(outputPath): + var outputPath {.inject.}: string + var asmversion {.inject.}: Version + var copyright {.inject.}: string + proc `assemblyinfoOf Task`*() = proc `assemblyinfoOf Body`() = body `assemblyinfoOf Body`() + context.info.add("detected assembly version $1" % [$asmversion]) var buffer = "" buffer.add("using System.Reflection;\n\c") buffer.add("using System.Runtime.InteropServices;\n\c") @@ -14,4 +17,5 @@ template assemblyinfoOf*(body: untyped) = buffer.add("[assembly: AssemblyFileVersion(\"$1\")]\n\c" % [asmversion.short()]) buffer.add("[assembly: AssemblyInformationalVersionAttribute(\"$1\")]\n\c" % [asmversion.shortWithDirty()]) buffer.add("[assembly: AssemblyCopyrightAttribute(\"$1\")]\n\c" % [copyright]) - writeFile(outputPath / "SolutionVersion.cs", buffer) + writeFile(outputPath / "SolutionVersion.cs", buffer) + context.info.add("file SolutionVersion.cs put in $1" % [outputPath]) diff --git a/tasks/cleanbuildfolder.nim b/tasks/cleanbuildfolder.nim new file mode 100644 index 0000000..333e5fa --- /dev/null +++ b/tasks/cleanbuildfolder.nim @@ -0,0 +1,12 @@ +import ../contexts +import strutils + +template cleanBuildFolder*(context: TaskContext; body: untyped) = + when not declaredInScope(buildFolder): + var buildFolder {.inject.}: string + proc `cleanBuildFolder Task`*() = + proc `cleanBuildFolder Body`() = body + `cleanBuildFolder Body`() + removeDir(buildFolder, context.warn) + makeDir(buildFolder, context.err) + context.info.add("cleaned folder $1" % [buildFolder]) \ No newline at end of file diff --git a/tasks/dotnet.nim b/tasks/dotnet.nim new file mode 100644 index 0000000..1bf0050 --- /dev/null +++ b/tasks/dotnet.nim @@ -0,0 +1,34 @@ +import ../contexts +import ../tools/runner + +template dotnetbuild*(context: TaskContext; body: untyped) = + when not declaredInScope(slnFile): + var slnFile {.inject.}: string + proc `dotnetbuild Task`*() = + proc `dotnetbuild Body`() = body + `dotnetbuild Body`() + var output = runAbs("dotnet restore " & slnFile.toAbsolutePath, context.err) + context.info.add(output) + output = runAbs("dotnet build " & slnFile.toAbsolutePath, context.err) + context.info.add(output) + +template dotnettest*(context: TaskContext; body: untyped) = + when not declaredInScope(testsProject): + var testsProject {.inject.}: string + proc `dotnettest Task`*() = + proc `dotnettest Body`() = body + `dotnettest Body`() + var output = runAbs("dotnet test " & testsProject, context.err) + context.info.add(output) + +template dotnetgcfix*(context: TaskContext; body: untyped) = + # specify server gc manually in *.config file + when not declaredInScope(targetConfig): + var targetConfig {.inject.}: string + proc `dotnetgcfix Task`*() = + proc `dotnetgcfix Body`() = body + `dotnetgcfix Body`() + var config = targetConfig.readFile() + config = config.replace(" ", " \r\n ") + targetConfig.writeFile(config) + context.info.add("dotnet gc fix was written to $1" % [targetConfig]) \ No newline at end of file diff --git a/tasks/filetools.nim b/tasks/filetools.nim index 79f14b8..561d596 100644 --- a/tasks/filetools.nim +++ b/tasks/filetools.nim @@ -1,33 +1,30 @@ import strutils -proc removeDir*(dir: string) = - echo "test" - proc removeDirSafe(dir: string) = - echo "removed $1" % [dir] - let dirs = listDirs(dir) +proc removeDir*(dir: string; errors: var seq[string]) = + let dirs = listDirs(dir) - for dir in dirs: - try: - removeDirSafe(dir) - rmDir(dir) - except: continue + for dir in dirs: + try: + removeDir(dir, errors) + rmDir(dir) + except: + errors.add("can't remove $1" % [dir]) + continue - let files = listFiles(dir) + let files = listFiles(dir) - for file in files: - try: - rmFile(file) - except: continue + for file in files: + try: + rmFile(file) + except: + errors.add("can't remove $1" % [file]) + continue - removeDirSafe(dir) - -proc makeDir*(dir: string) = +proc makeDir*(dir: string; errors: var seq[string]) = if existsDir(dir): - echo "dir $1 already exists" % [dir] return try: mkDir dir - echo "created dir $1" % [dir] except: - echo "can't create $1 dir" % [dir] \ No newline at end of file + errors.add("can't create $1 dir" % [dir]) \ No newline at end of file diff --git a/tasks/nimbscript.nim b/tasks/nimbscript.nim index ac73fe8..1d40576 100644 --- a/tasks/nimbscript.nim +++ b/tasks/nimbscript.nim @@ -1,2 +1,33 @@ -template call*(name: untyped) = - `name Task`() \ No newline at end of file +import system +import strutils +import tables +import ../contexts +import ../tools/time +import ../tools/prettyreport + +template getContext(name: untyped): TaskContext = + let taskName = astToStr(name) + getOrAddContext(taskName) + +template call*(name: untyped) = + if not globalContext().stop: + var taskContext = getContext(name) + let before = getTicks() + `name Task`() + let delta = getTicks() - before + taskContext.timing = delta + taskContext.skipped = false + if taskContext.err.len > 0: + globalContext().stop = true + +template nimbTask*(name: untyped; body: untyped): untyped = + var taskContext = getContext(name) + `name`(taskContext, body) + +template nimbExecute*(actions: untyped): untyped = + task build, "": + let before = getTicks() + actions + let delta = getTicks() - before + globalContext().timing = delta + prettyReport(globalContext()) \ No newline at end of file diff --git a/tasks/nuget.nim b/tasks/nuget.nim index 1eb9761..d3a7a57 100644 --- a/tasks/nuget.nim +++ b/tasks/nuget.nim @@ -1,9 +1,13 @@ import strutils import ospaths -import runner +import ../tools/runner import buildtools +import ../contexts -template nugetpack*(body: untyped) = +proc logInfo*(self: TaskContext, msg: string): untyped = + self.info.add(msg) + +template nugetpack*(context: TaskContext; body: untyped) = proc `nugetpack Task`*() = when not declaredInScope(packageId): var nugetExecutable {.inject.}: string @@ -33,8 +37,8 @@ template nugetpack*(body: untyped) = var filesXml = "\n" for item in items(files): filesXml.add(spaces(12) & "\n" % [item.fillWithQuotes()]) - - makeDir(outputDir) + + makeDir(outputDir, context.err) let xmlTemplate = """ @@ -63,11 +67,12 @@ template nugetpack*(body: untyped) = nuspecDir = nuspecDir / fullPackageId & ".nuspec" - echo "creating .nuspec for $1 in $2..." % [part, nuspecDir] + context.logInfo("creating .nuspec for $1 in $2..." % [part, nuspecDir]) + writeFile(nuspecDir, xml) - echo "created .nuspec for $1 packing..." % [part] + context.logInfo("created .nuspec for $1 packing..." % [part]) let nugetCmd = nugetExecutable & " pack \\\"$1\\\" -OutputDirectory \\\"$2\\\"" % [nuspecDir, outputDir.toAbsolutePath] - echo run nugetCmd + context.logInfo(run(nugetCmd, context.err)) proc add*(param: string): string= result = "src=$1" % [param] @@ -78,7 +83,7 @@ proc add*(str: string, param: string): string= proc exclude*(str: string, param: string): string= result = str & " exclude=$1" % [param] -template nugetpush*(body: untyped) = +template nugetpush*(context: TaskContext; body: untyped) = proc `nugetpush Task`*() = when not declaredInScope(packageId): var nugetExecutable {.inject.}: string @@ -96,13 +101,12 @@ template nugetpush*(body: untyped) = if ext != ".nupkg": continue if name.contains("dirty"): - echo "nuget $1 is dirty, nugetpush ignored" % [name] + context.logInfo("nuget $1 is dirty, nugetpush ignored" % [name]) continue let nugetCmd = nugetExecutable & " push \\\"$1\\\" -ApiKey $2 -Source $3" % [file, repoKey, repoUrl] - echo run nugetCmd - echo "completed." + context.logInfo(run(nugetCmd, context.err)) -template nugetrestore*(body: untyped) = +template nugetrestore*(context: TaskContext; body: untyped) = proc `nugetrestore Task`*() = when not declaredInScope(packageId): var nugetExecutable {.inject.}: string @@ -112,4 +116,4 @@ template nugetrestore*(body: untyped) = `nugetpack Body`() let nugetCmd = nugetExecutable & " restore " & dir.toAbsolutePath - echo run nugetCmd + context.logInfo(run(nugetCmd, context.err)) diff --git a/tasks/nunit.nim b/tasks/nunit.nim index c7a11ad..f85a8d9 100644 --- a/tasks/nunit.nim +++ b/tasks/nunit.nim @@ -1,7 +1,8 @@ -import runner import buildtools +import ../tools/runner +import ../contexts -template nunitrun*(body: untyped) = +template nunitrun*(context: TaskContext; body: untyped) = proc `nunitrun Task`*() = when not declaredInScope(outputPath): var assembliesDir {.inject.}: string @@ -57,4 +58,4 @@ template nunitrun*(body: untyped) = let nunitCmd = "$1 $2 /framework=net-4.5 /result=$3\\NUnit_report.xml /noheader -x86" % [nunitRunnerExe, assembliesList, assembliesDir] - echo run nunitCmd \ No newline at end of file + echo run(nunitCmd, context.err) \ No newline at end of file diff --git a/tasks/version.nim b/tasks/version.nim index 56aeb7d..bf64072 100644 --- a/tasks/version.nim +++ b/tasks/version.nim @@ -1,5 +1,5 @@ import strutils -import runner +import ../tools/runner type Version* = object @@ -25,8 +25,8 @@ proc short* (version: Version): string = # parsing output. It looks like: v1.0-0-g69d5874d6aa1cbfd2ef5d5205162b872cccb0471-dirty proc getVersion*(): Version = let scriptPath = getEnv("nimbfilePath") - let output = runAbs("git -C $1 describe --abbrev=64 --first-parent --long --dirty --always" % scriptPath) - echo "git output: " & output + var errors: seq[string] = @[] + let output = runAbs("git -C $1 describe --abbrev=64 --first-parent --long --dirty --always" % scriptPath, errors) let splitMajor = output.split('.') @@ -44,5 +44,4 @@ proc getVersion*(): Version = let splitMinor = output.split('-') result = Version(major: "0", minor: "0", patch: "0", hash: hash) result.isDirty = splitMinor.len() > 3 - echo "detected version: $1" % [$result] diff --git a/tools/prettyreport.nim b/tools/prettyreport.nim new file mode 100644 index 0000000..efcae5f --- /dev/null +++ b/tools/prettyreport.nim @@ -0,0 +1,37 @@ +import ../contexts +import strutils +import tables +import time + +proc prettyReport*(context: GlobalContext) = + echo "nimb report" + + for task in context.tasks.values: + if task.skipped: + continue + + echo task.caption.indent(1, "\t") + for line in task.info: + echo line.indent(2, "\t") + + if task.warn.len > 0: + echo "warnings: $1".indent(2, "\t") % [$task.warn.len] + for line in task.warn: + echo line.indent(3, "\t") + + if task.err.len > 0: + echo "errors: $1".indent(2, "\t") % [$task.err.len] + for line in task.err: + echo line.indent(3, "\t") + + echo "executed tasks:" + for task in context.tasks.values: + if task.skipped: + continue + let taskTiming = float(task.timing) * 0.001 + let taskTimingStr = "$1 ms" % [$taskTiming] + let line = "$1 $2" % [task.caption, taskTimingStr] + echo line.indent(1, "\t") + + let globalTiming = float(context.timing) * 0.001 + echo "nimb done in $1 ms" % [$globalTiming] diff --git a/tasks/runner.nim b/tools/runner.nim similarity index 55% rename from tasks/runner.nim rename to tools/runner.nim index c39367e..8912a87 100644 --- a/tasks/runner.nim +++ b/tools/runner.nim @@ -1,8 +1,7 @@ import ospaths import strutils -proc run(path:string, cmd: string): string = - echo "executing: " & cmd +proc run(path:string; cmd: string; errors: var seq[string]): string = let utilPath = staticExec("nimb -utilpath").strip() let utilCmd = utilpath / "nimbexec.exe \"$1\"" % [path / cmd] @@ -13,16 +12,16 @@ proc run(path:string, cmd: string): string = let output = lines[1..lines.len-1].join("\n") if code>0: - raise newException(Exception, "Command $1 execution failed with code $2 and output:\r\n $3" % [cmd, $code, output]) + errors.add(output) return output -proc run*(cmd: string): string = +proc run*(cmd: string; errors: var seq[string]): string = let scriptPath = getEnv("nimbfilePath") - run(scriptPath, cmd) + run(scriptPath, cmd, errors) -proc runAbs*(cmd: string): string = - run("", cmd) +proc runAbs*(cmd: string; errors: var seq[string]): string = + run("", cmd, errors) -proc quote*(str: string): string = +proc quote*(str: string; errors: var seq[string]): string = return "\"$1\"" % [str] \ No newline at end of file diff --git a/tools/time.nim b/tools/time.nim new file mode 100644 index 0000000..1bb3a4e --- /dev/null +++ b/tools/time.nim @@ -0,0 +1,9 @@ +import ospaths +import strutils + +proc getTicks*(): int64 = + let utilPath = staticExec("nimb -utilpath").strip() + let utilCmd = utilpath / "nimbtime.exe" + let resultOutput = staticExec(utilCmd).strip() + let code = parseBiggestInt(resultOutput) + return code diff --git a/utils/nimbtime.nim b/utils/nimbtime.nim new file mode 100644 index 0000000..daf6fee --- /dev/null +++ b/utils/nimbtime.nim @@ -0,0 +1,5 @@ +include "system/timers" + +import strutils + +echo getTicks().Nanos \ No newline at end of file