koch.nim 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. #
  2. #
  3. # Maintenance program for Nim
  4. # (c) Copyright 2017 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # See doc/koch.txt for documentation.
  10. #
  11. when defined(gcc) and defined(windows):
  12. when defined(x86):
  13. {.link: "icons/koch.res".}
  14. else:
  15. {.link: "icons/koch_icon.o".}
  16. when defined(amd64) and defined(windows) and defined(vcc):
  17. {.link: "icons/koch-amd64-windows-vcc.res" .}
  18. when defined(i386) and defined(windows) and defined(vcc):
  19. {.link: "icons/koch-i386-windows-vcc.res" .}
  20. import
  21. os, strutils, parseopt, osproc, streams
  22. const VersionAsString = system.NimVersion
  23. const
  24. HelpText = """
  25. +-----------------------------------------------------------------+
  26. | Maintenance program for Nim |
  27. | Version $1|
  28. | (c) 2017 Andreas Rumpf |
  29. +-----------------------------------------------------------------+
  30. Build time: $2, $3
  31. Usage:
  32. koch [options] command [options for command]
  33. Options:
  34. --help, -h shows this help and quits
  35. Possible Commands:
  36. boot [options] bootstraps with given command line options
  37. distrohelper [bindir] helper for distro packagers
  38. tools builds Nim related tools
  39. nimble builds the Nimble tool
  40. Boot options:
  41. -d:release produce a release version of the compiler
  42. -d:useLinenoise use the linenoise library for interactive mode
  43. (not needed on Windows)
  44. -d:avoidTimeMachine only for Mac OS X, excludes nimcache dir from backups
  45. Commands for core developers:
  46. web [options] generates the website and the full documentation
  47. website [options] generates only the website
  48. csource -d:release builds the C sources for installation
  49. pdf builds the PDF documentation
  50. zip builds the installation zip package
  51. xz builds the installation tar.xz package
  52. testinstall test tar.xz package; Unix only!
  53. tests [options] run the testsuite
  54. temp options creates a temporary compiler for testing
  55. winrelease creates a Windows release
  56. pushcsource push generated C sources to its repo
  57. Web options:
  58. --googleAnalytics:UA-... add the given google analytics code to the docs. To
  59. build the official docs, use UA-48159761-1
  60. """
  61. const gaCode = " --googleAnalytics:UA-48159761-1"
  62. proc exe(f: string): string =
  63. result = addFileExt(f, ExeExt)
  64. when defined(windows):
  65. result = result.replace('/','\\')
  66. template withDir(dir, body) =
  67. let old = getCurrentDir()
  68. try:
  69. setCurrentDir(dir)
  70. body
  71. finally:
  72. setCurrentdir(old)
  73. proc findNim(): string =
  74. var nim = "nim".exe
  75. result = "bin" / nim
  76. if existsFile(result): return
  77. for dir in split(getEnv("PATH"), PathSep):
  78. if existsFile(dir / nim): return dir / nim
  79. # assume there is a symlink to the exe or something:
  80. return nim
  81. proc exec(cmd: string, errorcode: int = QuitFailure, additionalPath = "") =
  82. let prevPath = getEnv("PATH")
  83. if additionalPath.len > 0:
  84. var absolute = additionalPATH
  85. if not absolute.isAbsolute:
  86. absolute = getCurrentDir() / absolute
  87. echo("Adding to $PATH: ", absolute)
  88. putEnv("PATH", prevPath & PathSep & absolute)
  89. echo(cmd)
  90. if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
  91. putEnv("PATH", prevPath)
  92. proc nimexec(cmd: string) =
  93. exec findNim() & " " & cmd
  94. proc execCleanPath(cmd: string,
  95. additionalPath = ""; errorcode: int = QuitFailure) =
  96. # simulate a poor man's virtual environment
  97. let prevPath = getEnv("PATH")
  98. when defined(windows):
  99. let CleanPath = r"$1\system32;$1;$1\System32\Wbem" % getEnv"SYSTEMROOT"
  100. else:
  101. const CleanPath = r"/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin"
  102. putEnv("PATH", CleanPath & PathSep & additionalPath)
  103. echo(cmd)
  104. if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
  105. putEnv("PATH", prevPath)
  106. proc testUnixInstall() =
  107. let oldCurrentDir = getCurrentDir()
  108. try:
  109. let destDir = getTempDir()
  110. copyFile("build/nim-$1.tar.xz" % VersionAsString,
  111. destDir / "nim-$1.tar.xz" % VersionAsString)
  112. setCurrentDir(destDir)
  113. execCleanPath("tar -xJf nim-$1.tar.xz" % VersionAsString)
  114. setCurrentDir("nim-$1" % VersionAsString)
  115. execCleanPath("sh build.sh")
  116. # first test: try if './bin/nim --version' outputs something sane:
  117. let output = execProcess("./bin/nim --version").splitLines
  118. if output.len > 0 and output[0].contains(VersionAsString):
  119. echo "Version check: success"
  120. execCleanPath("./bin/nim c koch.nim")
  121. execCleanPath("./koch boot -d:release", destDir / "bin")
  122. # check the docs build:
  123. execCleanPath("./koch web", destDir / "bin")
  124. # check nimble builds:
  125. execCleanPath("./koch tools")
  126. # check the tests work:
  127. execCleanPath("./koch tests", destDir / "bin")
  128. else:
  129. echo "Version check: failure"
  130. finally:
  131. setCurrentDir oldCurrentDir
  132. proc tryExec(cmd: string): bool =
  133. echo(cmd)
  134. result = execShellCmd(cmd) == 0
  135. proc safeRemove(filename: string) =
  136. if existsFile(filename): removeFile(filename)
  137. proc overwriteFile(source, dest: string) =
  138. safeRemove(dest)
  139. moveFile(source, dest)
  140. proc copyExe(source, dest: string) =
  141. safeRemove(dest)
  142. copyFile(dest=dest, source=source)
  143. inclFilePermissions(dest, {fpUserExec})
  144. const
  145. compileNimInst = "tools/niminst/niminst"
  146. proc csource(args: string) =
  147. nimexec(("cc $1 -r $3 --var:version=$2 --var:mingw=none csource " &
  148. "--main:compiler/nim.nim compiler/installer.ini $1") %
  149. [args, VersionAsString, compileNimInst])
  150. proc bundleNimbleSrc() =
  151. ## bunldeNimbleSrc() bundles a specific Nimble commit with the tarball. We
  152. ## always bundle the latest official release.
  153. if not dirExists("dist/nimble/.git"):
  154. exec("git clone https://github.com/nim-lang/nimble.git dist/nimble")
  155. withDir("dist/nimble"):
  156. exec("git checkout -f stable")
  157. exec("git pull")
  158. proc bundleNimbleExe() =
  159. bundleNimbleSrc()
  160. # now compile Nimble and copy it to $nim/bin for the installer.ini
  161. # to pick it up:
  162. nimexec("c -d:release dist/nimble/src/nimble.nim")
  163. copyExe("dist/nimble/src/nimble".exe, "bin/nimble".exe)
  164. proc buildNimble(latest: bool) =
  165. # old installations created nim/nimblepkg/*.nim files. We remove these
  166. # here so that it cannot cause problems (nimble bug #306):
  167. if dirExists("bin/nimblepkg"):
  168. removeDir("bin/nimblepkg")
  169. # if koch is used for a tar.xz, build the dist/nimble we shipped
  170. # with the tarball:
  171. var installDir = "dist/nimble"
  172. if not latest and dirExists(installDir) and not dirExists("dist/nimble/.git"):
  173. discard "don't do the git dance"
  174. else:
  175. if not dirExists("dist/nimble/.git"):
  176. if dirExists(installDir):
  177. var id = 0
  178. while dirExists("dist/nimble" & $id):
  179. inc id
  180. installDir = "dist/nimble" & $id
  181. exec("git clone https://github.com/nim-lang/nimble.git " & installDir)
  182. withDir(installDir):
  183. if latest:
  184. exec("git checkout -f master")
  185. else:
  186. exec("git checkout -f stable")
  187. exec("git pull")
  188. nimexec("c --noNimblePath -p:compiler -d:release " & installDir / "src/nimble.nim")
  189. copyExe(installDir / "src/nimble".exe, "bin/nimble".exe)
  190. proc bundleNimsuggest(buildExe: bool) =
  191. if buildExe:
  192. nimexec("c --noNimblePath -d:release -p:compiler nimsuggest/nimsuggest.nim")
  193. copyExe("nimsuggest/nimsuggest".exe, "bin/nimsuggest".exe)
  194. removeFile("nimsuggest/nimsuggest".exe)
  195. proc buildVccTool() =
  196. nimexec("c -o:bin/vccexe.exe tools/vccenv/vccexe")
  197. proc bundleWinTools() =
  198. nimexec("c tools/finish.nim")
  199. copyExe("tools/finish".exe, "finish".exe)
  200. removeFile("tools/finish".exe)
  201. buildVccTool()
  202. nimexec("c -o:bin/nimgrab.exe -d:ssl tools/nimgrab.nim")
  203. when false:
  204. # not yet a tool worth including
  205. nimexec(r"c --cc:vcc --app:gui -o:bin\downloader.exe -d:ssl --noNimblePath " &
  206. r"--path:..\ui tools\downloader.nim")
  207. proc zip(args: string) =
  208. bundleNimbleSrc()
  209. bundleNimsuggest(false)
  210. bundleWinTools()
  211. nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
  212. [VersionAsString, compileNimInst])
  213. exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" %
  214. ["tools/niminst/niminst".exe, VersionAsString])
  215. proc xz(args: string) =
  216. bundleNimbleSrc()
  217. bundleNimsuggest(false)
  218. nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
  219. [VersionAsString, compileNimInst])
  220. exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim xz compiler/installer.ini" %
  221. ["tools" / "niminst" / "niminst".exe, VersionAsString])
  222. proc buildTool(toolname, args: string) =
  223. nimexec("cc $# $#" % [args, toolname])
  224. copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe)
  225. proc buildTools(latest: bool) =
  226. let nimsugExe = "bin/nimsuggest".exe
  227. nimexec "c --noNimblePath -p:compiler -d:release -o:" & nimsugExe &
  228. " nimsuggest/nimsuggest.nim"
  229. let nimgrepExe = "bin/nimgrep".exe
  230. nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim"
  231. when defined(windows): buildVccTool()
  232. buildNimble(latest)
  233. proc nsis(args: string) =
  234. bundleNimbleExe()
  235. bundleNimsuggest(true)
  236. bundleWinTools()
  237. # make sure we have generated the niminst executables:
  238. buildTool("tools/niminst/niminst", args)
  239. #buildTool("tools/nimgrep", args)
  240. # produce 'nim_debug.exe':
  241. #exec "nim c compiler" / "nim.nim"
  242. #copyExe("compiler/nim".exe, "bin/nim_debug".exe)
  243. exec(("tools" / "niminst" / "niminst --var:version=$# --var:mingw=mingw$#" &
  244. " nsis compiler/installer.ini") % [VersionAsString, $(sizeof(pointer)*8)])
  245. proc geninstall(args="") =
  246. nimexec("cc -r $# --var:version=$# --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini $#" %
  247. [compileNimInst, VersionAsString, args])
  248. proc install(args: string) =
  249. geninstall()
  250. exec("sh ./install.sh $#" % args)
  251. proc web(args: string) =
  252. nimexec("js tools/dochack/dochack.nim")
  253. nimexec("cc -r tools/nimweb.nim $# web/website.ini --putenv:nimversion=$#" %
  254. [args, VersionAsString])
  255. proc website(args: string) =
  256. nimexec("cc -r tools/nimweb.nim $# --website web/website.ini --putenv:nimversion=$#" %
  257. [args, VersionAsString])
  258. proc pdf(args="") =
  259. exec("$# cc -r tools/nimweb.nim $# --pdf web/website.ini --putenv:nimversion=$#" %
  260. [findNim(), args, VersionAsString], additionalPATH=findNim().splitFile.dir)
  261. # -------------- boot ---------------------------------------------------------
  262. proc findStartNim: string =
  263. # we try several things before giving up:
  264. # * bin/nim
  265. # * $PATH/nim
  266. # If these fail, we try to build nim with the "build.(sh|bat)" script.
  267. var nim = "nim".exe
  268. result = "bin" / nim
  269. if existsFile(result): return
  270. for dir in split(getEnv("PATH"), PathSep):
  271. if existsFile(dir / nim): return dir / nim
  272. when defined(Posix):
  273. const buildScript = "build.sh"
  274. if existsFile(buildScript):
  275. if tryExec("./" & buildScript): return "bin" / nim
  276. else:
  277. const buildScript = "build.bat"
  278. if existsFile(buildScript):
  279. if tryExec(buildScript): return "bin" / nim
  280. echo("Found no nim compiler and every attempt to build one failed!")
  281. quit("FAILURE")
  282. proc thVersion(i: int): string =
  283. result = ("compiler" / "nim" & $i).exe
  284. proc boot(args: string) =
  285. var output = "compiler" / "nim".exe
  286. var finalDest = "bin" / "nim".exe
  287. # default to use the 'c' command:
  288. let bootOptions = if args.len == 0 or args.startsWith("-"): "c" else: ""
  289. let smartNimcache = (if "release" in args: "nimcache/r_" else: "nimcache/d_") &
  290. hostOs & "_" & hostCpu
  291. copyExe(findStartNim(), 0.thVersion)
  292. for i in 0..2:
  293. echo "iteration: ", i+1
  294. exec i.thVersion & " $# $# --nimcache:$# compiler" / "nim.nim" % [bootOptions, args,
  295. smartNimcache]
  296. if sameFileContent(output, i.thVersion):
  297. copyExe(output, finalDest)
  298. echo "executables are equal: SUCCESS!"
  299. return
  300. copyExe(output, (i+1).thVersion)
  301. copyExe(output, finalDest)
  302. when not defined(windows): echo "[Warning] executables are still not equal"
  303. # -------------- clean --------------------------------------------------------
  304. const
  305. cleanExt = [
  306. ".ppu", ".o", ".obj", ".dcu", ".~pas", ".~inc", ".~dsk", ".~dpr",
  307. ".map", ".tds", ".err", ".bak", ".pyc", ".exe", ".rod", ".pdb", ".idb",
  308. ".idx", ".ilk"
  309. ]
  310. ignore = [
  311. ".bzrignore", "nim", "nim.exe", "koch", "koch.exe", ".gitignore"
  312. ]
  313. proc cleanAux(dir: string) =
  314. for kind, path in walkDir(dir):
  315. case kind
  316. of pcFile:
  317. var (_, name, ext) = splitFile(path)
  318. if ext == "" or cleanExt.contains(ext):
  319. if not ignore.contains(name):
  320. echo "removing: ", path
  321. removeFile(path)
  322. of pcDir:
  323. case splitPath(path).tail
  324. of "nimcache":
  325. echo "removing dir: ", path
  326. removeDir(path)
  327. of "dist", ".git", "icons": discard
  328. else: cleanAux(path)
  329. else: discard
  330. proc removePattern(pattern: string) =
  331. for f in walkFiles(pattern):
  332. echo "removing: ", f
  333. removeFile(f)
  334. proc clean(args: string) =
  335. removePattern("web/*.html")
  336. removePattern("doc/*.html")
  337. cleanAux(getCurrentDir())
  338. for kind, path in walkDir(getCurrentDir() / "build"):
  339. if kind == pcDir:
  340. echo "removing dir: ", path
  341. removeDir(path)
  342. # -------------- builds a release ---------------------------------------------
  343. proc winReleaseArch(arch: string) =
  344. doAssert arch in ["32", "64"]
  345. let cpu = if arch == "32": "i386" else: "amd64"
  346. template withMingw(path, body) =
  347. let prevPath = getEnv("PATH")
  348. putEnv("PATH", path & PathSep & prevPath)
  349. try:
  350. body
  351. finally:
  352. putEnv("PATH", prevPath)
  353. withMingw r"..\mingw" & arch & r"\bin":
  354. # Rebuilding koch is necessary because it uses its pointer size to
  355. # determine which mingw link to put in the NSIS installer.
  356. nimexec "c --out:koch_temp --cpu:$# koch" % cpu
  357. exec "koch_temp boot -d:release --cpu:$#" % cpu
  358. exec "koch_temp zip -d:release"
  359. overwriteFile r"build\nim-$#.zip" % VersionAsString,
  360. r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch]
  361. proc winRelease() =
  362. # Build -docs file:
  363. when true:
  364. web(gaCode)
  365. withDir "web/upload/" & VersionAsString:
  366. exec "7z a -tzip docs-$#.zip *.html" % VersionAsString
  367. overwriteFile "web/upload/$1/docs-$1.zip" % VersionAsString,
  368. "web/upload/download/docs-$1.zip" % VersionAsString
  369. when true:
  370. csource("-d:release")
  371. when true:
  372. winReleaseArch "32"
  373. when true:
  374. winReleaseArch "64"
  375. # -------------- tests --------------------------------------------------------
  376. template `|`(a, b): string = (if a.len > 0: a else: b)
  377. proc tests(args: string) =
  378. # we compile the tester with taintMode:on to have a basic
  379. # taint mode test :-)
  380. nimexec "cc --taintMode:on tests/testament/tester"
  381. # Since tests take a long time (on my machine), and we want to defy Murhpys
  382. # law - lets make sure the compiler really is freshly compiled!
  383. nimexec "c --lib:lib -d:release --opt:speed compiler/nim.nim"
  384. let tester = quoteShell(getCurrentDir() / "tests/testament/tester".exe)
  385. let success = tryExec tester & " " & (args|"all")
  386. if not existsEnv("TRAVIS") and not existsEnv("APPVEYOR"):
  387. exec tester & " html"
  388. if not success:
  389. quit("tests failed", QuitFailure)
  390. proc temp(args: string) =
  391. proc splitArgs(a: string): (string, string) =
  392. # every --options before the command (indicated by starting
  393. # with not a dash) is part of the bootArgs, the rest is part
  394. # of the programArgs:
  395. let args = os.parseCmdLine a
  396. result = ("", "")
  397. var i = 0
  398. while i < args.len and args[i][0] == '-':
  399. result[0].add " " & quoteShell(args[i])
  400. inc i
  401. while i < args.len:
  402. result[1].add " " & quoteShell(args[i])
  403. inc i
  404. var output = "compiler" / "nim".exe
  405. var finalDest = "bin" / "nim_temp".exe
  406. # 125 is the magic number to tell git bisect to skip the current
  407. # commit.
  408. let (bootArgs, programArgs) = splitArgs(args)
  409. exec("nim c " & bootArgs & " compiler" / "nim", 125)
  410. copyExe(output, finalDest)
  411. if programArgs.len > 0: exec(finalDest & " " & programArgs)
  412. proc xtemp(cmd: string) =
  413. let d = getAppDir()
  414. copyExe(d / "bin" / "nim".exe, d / "bin" / "nim_backup".exe)
  415. try:
  416. withDir(d):
  417. temp"-d:debug"
  418. copyExe(d / "bin" / "nim_temp".exe, d / "bin" / "nim".exe)
  419. exec(cmd)
  420. finally:
  421. copyExe(d / "bin" / "nim_backup".exe, d / "bin" / "nim".exe)
  422. proc pushCsources() =
  423. if not dirExists("../csources/.git"):
  424. quit "[Error] no csources git repository found"
  425. csource("-d:release")
  426. let cwd = getCurrentDir()
  427. try:
  428. copyDir("build/c_code", "../csources/c_code")
  429. copyFile("build/build.sh", "../csources/build.sh")
  430. copyFile("build/build.bat", "../csources/build.bat")
  431. copyFile("build/build64.bat", "../csources/build64.bat")
  432. copyFile("build/makefile", "../csources/makefile")
  433. setCurrentDir("../csources")
  434. for kind, path in walkDir("c_code"):
  435. if kind == pcDir:
  436. exec("git add " & path / "*.c")
  437. exec("git commit -am \"updated csources to version " & NimVersion & "\"")
  438. exec("git push origin master")
  439. exec("git tag -am \"Version $1\" v$1" % NimVersion)
  440. exec("git push origin v$1" % NimVersion)
  441. finally:
  442. setCurrentDir(cwd)
  443. proc valgrind(cmd: string) =
  444. # somewhat hacky: '=' sign means "pass to valgrind" else "pass to Nim"
  445. let args = parseCmdLine(cmd)
  446. var nimcmd = ""
  447. var valcmd = ""
  448. for i, a in args:
  449. if i == args.len-1:
  450. # last element is the filename:
  451. valcmd.add ' '
  452. valcmd.add changeFileExt(a, ExeExt)
  453. nimcmd.add ' '
  454. nimcmd.add a
  455. elif '=' in a:
  456. valcmd.add ' '
  457. valcmd.add a
  458. else:
  459. nimcmd.add ' '
  460. nimcmd.add a
  461. exec("nim c" & nimcmd)
  462. let supp = getAppDir() / "tools" / "nimgrind.supp"
  463. exec("valgrind --suppressions=" & supp & valcmd)
  464. proc showHelp() =
  465. quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
  466. CompileDate, CompileTime], QuitSuccess)
  467. var op = initOptParser()
  468. op.next()
  469. case op.kind
  470. of cmdLongOption, cmdShortOption: showHelp()
  471. of cmdArgument:
  472. case normalize(op.key)
  473. of "boot": boot(op.cmdLineRest)
  474. of "clean": clean(op.cmdLineRest)
  475. of "web": web(op.cmdLineRest)
  476. of "doc", "docs": web("--onlyDocs " & op.cmdLineRest)
  477. of "json2": web("--json2 " & op.cmdLineRest)
  478. of "website": website(op.cmdLineRest & gaCode)
  479. of "web0":
  480. # undocumented command for Araq-the-merciful:
  481. web(op.cmdLineRest & gaCode)
  482. of "pdf": pdf()
  483. of "csource", "csources": csource(op.cmdLineRest)
  484. of "zip": zip(op.cmdLineRest)
  485. of "xz": xz(op.cmdLineRest)
  486. of "nsis": nsis(op.cmdLineRest)
  487. of "geninstall": geninstall(op.cmdLineRest)
  488. of "distrohelper": geninstall()
  489. of "install": install(op.cmdLineRest)
  490. of "testinstall": testUnixInstall()
  491. of "test", "tests": tests(op.cmdLineRest)
  492. of "temp": temp(op.cmdLineRest)
  493. of "xtemp": xtemp(op.cmdLineRest)
  494. of "winrelease": winRelease()
  495. of "wintools": bundleWinTools()
  496. of "nimble": buildNimble(existsDir(".git"))
  497. of "nimsuggest": bundleNimsuggest(buildExe=true)
  498. of "tools": buildTools(existsDir(".git"))
  499. of "pushcsource", "pushcsources": pushCsources()
  500. of "valgrind": valgrind(op.cmdLineRest)
  501. else: showHelp()
  502. of cmdEnd: showHelp()