koch.nim 19 KB

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