summary refs log tree commit diff stats
path: root/tests/misc
Commit message (Expand)AuthorAgeFilesLines
* Fixed sizeOf to sizeof (#12347)Ridho Pratama2019-10-031-1/+1
* close #6731 by writing a test casenarimiran2019-09-132-0/+5
* added more tests and updated the changelogAraq2019-09-021-0/+14
* Remove ENDB (#12095)Clyybber2019-08-311-1/+0
* refactor sizealignoffset (#12077)Andreas Rumpf2019-08-281-16/+37
* fixes #11958 (#12013)Andreas Rumpf2019-08-231-1/+35
* fixes #11972Araq2019-08-201-1/+5
* int128 on firstOrd, lastOrd and lengthOrd (#11701)Arne Döring2019-08-071-1/+1
* fix #11812 (#11829)Arne Döring2019-07-271-0/+78
* close #9891 by adding a testnarimiran2019-07-252-0/+19
* fixes #11792 (#11793)cooldome2019-07-211-0/+30
* Offsetof fixes (#11690)Arne Döring2019-07-092-1/+85
* fixes #11660 (#11677)Andreas Rumpf2019-07-071-0/+6
* [backport] fix #11320 (#11538)Arne Döring2019-07-031-0/+13
* parseopt fix (#11363) [backport]Jasper Jenkins2019-06-011-1/+2
* Remove immediate pragma (#11308)Arne Döring2019-05-291-1/+1
* right shift is now by default sign preserving (#11322)Arne Döring2019-05-292-6/+6
* make tests green againAraq2019-05-271-3/+1
* Enable range checking for unsigned integers (#11313)Oscar Nihlgård2019-05-251-1/+1
* Add test case for #3766 (#11252)Oscar Nihlgård2019-05-151-0/+14
* Make range checks in semConv (#7164)Oscar Nihlgård2019-05-101-0/+43
* low/high for float ranges (#11177)Oscar Nihlgård2019-05-061-0/+14
* sizeof alignof offsetof macros api (#10855)Arne Döring2019-03-191-0/+51
* tsizeof test is now correct (#10788)Arne Döring2019-03-061-16/+47
* make megatest green againAndreas Rumpf2019-03-051-0/+1
* sizealign of union type (#10780)Arne Döring2019-03-041-2/+10
* quickfix: make `tsizeof3` also work on 32-bitnarimiran2019-02-281-2/+2
* add tests for recently closed issues (#10722)Miran2019-02-233-0/+29
* 32 bit fixes (#10608)Arne Döring2019-02-132-2/+33
* revive #10228 (fix #9880) (#10610)Timothee Cour2019-02-132-10/+4
* Implement {.booldefine.} (#10533)Oscar Nihlgård2019-02-031-0/+18
* gc: destructors is beginning to work (#10483)Andreas Rumpf2019-01-292-2/+2
* properly deprecate parseopt2 (#10452)Miran2019-01-251-28/+2
* fix #9629 every binary cmd line option allows on/off/empty=on (#10353)Timothee Cour2019-01-181-1/+4
* parseopt2.cmdLineRest is now correct too (#10304)Timothee Cour2019-01-151-16/+19
* fix testTimothee Cour2019-01-141-2/+13
* fix #9842 #9951: `nim -r` and parseopt.cmdLineRest are now correctTimothee Cour2019-01-141-44/+92
* Fix for sizeof bitsize combination (#10227)Arne Döring2019-01-071-0/+12
* fixes #10082Andreas Rumpf2018-12-301-0/+11
* use anon structs and unions for a much better debug experience (#10055)Andreas Rumpf2018-12-201-23/+20
* Merge pull request #9881 from timotheecour/pr_ref_9880Andreas Rumpf2018-12-112-9/+32
|\
| * refs #9880 show index and bound in lots of `index out of bounds` errorsTimothee Cour2018-12-092-9/+32
* | testament: joinable is now an explicit concept of a test specAndreas Rumpf2018-12-112-0/+2
* | Testament: refactoring; makes the test joiner greenAraq2018-12-112-0/+2
* | lots of small changesArne Döring2018-12-113-5/+7
* | megatest checks output and nimoutArne Döring2018-12-111-4/+3
* | megatest can be executedArne Döring2018-12-111-98/+2
* | require errormsg to be specified before file.Arne Döring2018-12-1123-79/+24
* | fixes #9868Araq2018-12-111-0/+2
|/
* fixes #9868Araq2018-12-051-0/+4
n570'>570 571 572 573 574 575 576 577 578 579 580 581 582 583
#
#
#         Maintenance program for Nim
#        (c) Copyright 2017 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#
#    See doc/koch.txt for documentation.
#

when defined(gcc) and defined(windows):
  when defined(x86):
    {.link: "icons/koch.res".}
  else:
    {.link: "icons/koch_icon.o".}

when defined(amd64) and defined(windows) and defined(vcc):
  {.link: "icons/koch-amd64-windows-vcc.res" .}
when defined(i386) and defined(windows) and defined(vcc):
  {.link: "icons/koch-i386-windows-vcc.res" .}

import
  os, strutils, parseopt, osproc, streams

const VersionAsString = system.NimVersion

const
  HelpText = """
+-----------------------------------------------------------------+
|         Maintenance program for Nim                             |
|             Version $1|
|             (c) 2017 Andreas Rumpf                              |
+-----------------------------------------------------------------+
Build time: $2, $3

Usage:
  koch [options] command [options for command]
Options:
  --help, -h               shows this help and quits
Possible Commands:
  boot [options]           bootstraps with given command line options
  distrohelper [bindir]    helper for distro packagers
  tools                    builds Nim related tools
  nimble                   builds the Nimble tool
Boot options:
  -d:release               produce a release version of the compiler
  -d:useLinenoise          use the linenoise library for interactive mode
                           (not needed on Windows)

Commands for core developers:
  web [options]            generates the website and the full documentation
                           (see `nimweb.nim` for cmd line options)
  website [options]        generates only the website
  csource -d:release       builds the C sources for installation
  pdf                      builds the PDF documentation
  zip                      builds the installation zip package
  xz                       builds the installation tar.xz package
  testinstall              test tar.xz package; Unix only!
  tests [options]          run the testsuite (run a subset of tests by
                           specifying a category, e.g. `tests cat async`)
  temp options             creates a temporary compiler for testing
  winrelease               creates a Windows release
  pushcsource              push generated C sources to its repo
Web options:
  --googleAnalytics:UA-... add the given google analytics code to the docs. To
                           build the official docs, use UA-48159761-1
"""

const gaCode = " --googleAnalytics:UA-48159761-1"

proc exe(f: string): string =
  result = addFileExt(f, ExeExt)
  when defined(windows):
    result = result.replace('/','\\')

template withDir(dir, body) =
  let old = getCurrentDir()
  try:
    setCurrentDir(dir)
    body
  finally:
    setCurrentdir(old)

proc findNim(): string =
  var nim = "nim".exe
  result = "bin" / nim
  if existsFile(result): return
  for dir in split(getEnv("PATH"), PathSep):
    if existsFile(dir / nim): return dir / nim
  # assume there is a symlink to the exe or something:
  return nim

proc exec(cmd: string, errorcode: int = QuitFailure, additionalPath = "") =
  let prevPath = getEnv("PATH")
  if additionalPath.len > 0:
    var absolute = additionalPATH
    if not absolute.isAbsolute:
      absolute = getCurrentDir() / absolute
    echo("Adding to $PATH: ", absolute)
    putEnv("PATH", (if prevPath.len > 0: prevPath & PathSep else: "") & absolute)
  echo(cmd)
  if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
  putEnv("PATH", prevPath)

proc nimexec(cmd: string) =
  exec findNim() & " " & cmd

proc execCleanPath(cmd: string,
                   additionalPath = ""; errorcode: int = QuitFailure) =
  # simulate a poor man's virtual environment
  let prevPath = getEnv("PATH")
  when defined(windows):
    let CleanPath = r"$1\system32;$1;$1\System32\Wbem" % getEnv"SYSTEMROOT"
  else:
    const CleanPath = r"/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin"
  putEnv("PATH", CleanPath & PathSep & additionalPath)
  echo(cmd)
  if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
  putEnv("PATH", prevPath)

proc testUnixInstall() =
  let oldCurrentDir = getCurrentDir()
  try:
    let destDir = getTempDir()
    copyFile("build/nim-$1.tar.xz" % VersionAsString,
             destDir / "nim-$1.tar.xz" % VersionAsString)
    setCurrentDir(destDir)
    execCleanPath("tar -xJf nim-$1.tar.xz" % VersionAsString)
    setCurrentDir("nim-$1" % VersionAsString)
    execCleanPath("sh build.sh")
    # first test: try if './bin/nim --version' outputs something sane:
    let output = execProcess("./bin/nim --version").splitLines
    if output.len > 0 and output[0].contains(VersionAsString):
      echo "Version check: success"
      execCleanPath("./bin/nim c koch.nim")
      execCleanPath("./koch boot -d:release", destDir / "bin")
      # check the docs build:
      execCleanPath("./koch web", destDir / "bin")
      # check nimble builds:
      execCleanPath("./koch tools")
      # check the tests work:
      execCleanPath("./koch tests", destDir / "bin")
    else:
      echo "Version check: failure"
  finally:
    setCurrentDir oldCurrentDir

proc tryExec(cmd: string): bool =
  echo(cmd)
  result = execShellCmd(cmd) == 0

proc safeRemove(filename: string) =
  if existsFile(filename): removeFile(filename)

proc overwriteFile(source, dest: string) =
  safeRemove(dest)
  moveFile(source, dest)

proc copyExe(source, dest: string) =
  safeRemove(dest)
  copyFile(dest=dest, source=source)
  inclFilePermissions(dest, {fpUserExec})

const
  compileNimInst = "tools/niminst/niminst"

proc csource(args: string) =
  nimexec(("cc $1 -r $3 --var:version=$2 --var:mingw=none csource " &
           "--main:compiler/nim.nim compiler/installer.ini $1") %
       [args, VersionAsString, compileNimInst])

proc bundleNimbleSrc() =
  ## bunldeNimbleSrc() bundles a specific Nimble commit with the tarball. We
  ## always bundle the latest official release.
  if not dirExists("dist/nimble/.git"):
    exec("git clone https://github.com/nim-lang/nimble.git dist/nimble")
  withDir("dist/nimble"):
    exec("git checkout -f stable")
    exec("git pull")

proc bundleNimbleExe() =
  bundleNimbleSrc()
  # now compile Nimble and copy it to $nim/bin for the installer.ini
  # to pick it up:
  nimexec("c -d:release --nilseqs:on dist/nimble/src/nimble.nim")
  copyExe("dist/nimble/src/nimble".exe, "bin/nimble".exe)

proc buildNimble(latest: bool) =
  # old installations created nim/nimblepkg/*.nim files. We remove these
  # here so that it cannot cause problems (nimble bug #306):
  if dirExists("bin/nimblepkg"):
    removeDir("bin/nimblepkg")
  # if koch is used for a tar.xz, build the dist/nimble we shipped
  # with the tarball:
  var installDir = "dist/nimble"
  if not latest and dirExists(installDir) and not dirExists("dist/nimble/.git"):
    discard "don't do the git dance"
  else:
    if not dirExists("dist/nimble/.git"):
      if dirExists(installDir):
        var id = 0
        while dirExists("dist/nimble" & $id):
          inc id
        installDir = "dist/nimble" & $id
      exec("git clone https://github.com/nim-lang/nimble.git " & installDir)
    withDir(installDir):
      if latest:
        exec("git checkout -f master")
      else:
        exec("git checkout -f stable")
      exec("git pull")
  nimexec("c --noNimblePath -p:compiler --nilseqs:on -d:release " & installDir / "src/nimble.nim")
  copyExe(installDir / "src/nimble".exe, "bin/nimble".exe)

proc bundleNimsuggest(buildExe: bool) =
  if buildExe:
    nimexec("c --noNimblePath -d:release -p:compiler nimsuggest/nimsuggest.nim")
    copyExe("nimsuggest/nimsuggest".exe, "bin/nimsuggest".exe)
    removeFile("nimsuggest/nimsuggest".exe)

proc buildVccTool() =
  nimexec("c -o:bin/vccexe.exe tools/vccenv/vccexe")

proc bundleWinTools() =
  nimexec("c tools/finish.nim")
  copyExe("tools/finish".exe, "finish".exe)
  removeFile("tools/finish".exe)
  buildVccTool()
  nimexec("c -o:bin/nimgrab.exe -d:ssl tools/nimgrab.nim")
  nimexec("c -o:bin/nimgrep.exe tools/nimgrep.nim")
  when false:
    # not yet a tool worth including
    nimexec(r"c --cc:vcc --app:gui -o:bin\downloader.exe -d:ssl --noNimblePath " &
            r"--path:..\ui tools\downloader.nim")

proc zip(args: string) =
  bundleNimbleExe()
  bundleNimsuggest(true)
  bundleWinTools()
  nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
       [VersionAsString, compileNimInst])
  exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" %
       ["tools/niminst/niminst".exe, VersionAsString])

proc ensureCleanGit() =
   let (outp, status) = osproc.execCmdEx("git diff")
   if outp.len != 0:
     quit "Not a clean git repository; 'git diff' not empty!"
   if status != 0:
     quit "Not a clean git repository; 'git diff' returned non-zero!"

proc xz(args: string) =
  ensureCleanGit()
  bundleNimbleSrc()
  bundleNimsuggest(false)
  nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
       [VersionAsString, compileNimInst])
  exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim xz compiler/installer.ini" %
       ["tools" / "niminst" / "niminst".exe, VersionAsString])

proc buildTool(toolname, args: string) =
  nimexec("cc $# $#" % [args, toolname])
  copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe)

proc buildTools(latest: bool) =
  nimexec "c --noNimblePath -p:compiler -d:release -o:" & ("bin/nimsuggest".exe) &
      " nimsuggest/nimsuggest.nim"

  nimexec "c -d:release -o:" & ("bin/nimgrep".exe) & " tools/nimgrep.nim"
  when defined(windows): buildVccTool()

  nimexec "c -o:" & ("bin/nimpretty".exe) & " nimpretty/nimpretty.nim"

  buildNimble(latest)

proc nsis(args: string) =
  bundleNimbleExe()
  bundleNimsuggest(true)
  bundleWinTools()
  # make sure we have generated the niminst executables:
  buildTool("tools/niminst/niminst", args)
  #buildTool("tools/nimgrep", args)
  # produce 'nim_debug.exe':
  #exec "nim c compiler" / "nim.nim"
  #copyExe("compiler/nim".exe, "bin/nim_debug".exe)
  exec(("tools" / "niminst" / "niminst --var:version=$# --var:mingw=mingw$#" &
        " nsis compiler/installer.ini") % [VersionAsString, $(sizeof(pointer)*8)])

proc geninstall(args="") =
  nimexec("cc -r $# --var:version=$# --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini $#" %
       [compileNimInst, VersionAsString, args])

proc install(args: string) =
  geninstall()
  exec("sh ./install.sh $#" % args)

proc web(args: string) =
  nimexec("js tools/dochack/dochack.nim")
  nimexec("cc -r tools/nimweb.nim $# web/website.ini --putenv:nimversion=$#" %
       [args, VersionAsString])

proc website(args: string) =
  nimexec("cc -r tools/nimweb.nim $# --website web/website.ini --putenv:nimversion=$#" %
       [args, VersionAsString])

proc pdf(args="") =
  exec("$# cc -r tools/nimweb.nim $# --pdf web/website.ini --putenv:nimversion=$#" %
       [findNim(), args, VersionAsString], additionalPATH=findNim().splitFile.dir)

# -------------- boot ---------------------------------------------------------

proc findStartNim: string =
  # we try several things before giving up:
  # * bin/nim
  # * $PATH/nim
  # If these fail, we try to build nim with the "build.(sh|bat)" script.
  var nim = "nim".exe
  result = "bin" / nim
  if existsFile(result): return
  for dir in split(getEnv("PATH"), PathSep):
    if existsFile(dir / nim): return dir / nim

  when defined(Posix):
    const buildScript = "build.sh"
    if existsFile(buildScript):
      if tryExec("./" & buildScript): return "bin" / nim
  else:
    const buildScript = "build.bat"
    if existsFile(buildScript):
      if tryExec(buildScript): return "bin" / nim

  echo("Found no nim compiler and every attempt to build one failed!")
  quit("FAILURE")

proc thVersion(i: int): string =
  result = ("compiler" / "nim" & $i).exe

proc boot(args: string) =
  var output = "compiler" / "nim".exe
  var finalDest = "bin" / "nim".exe
  # default to use the 'c' command:
  let bootOptions = if args.len == 0 or args.startsWith("-"): "c" else: ""
  let smartNimcache = (if "release" in args: "nimcache/r_" else: "nimcache/d_") &
                      hostOs & "_" & hostCpu

  copyExe(findStartNim(), 0.thVersion)
  for i in 0..2:
    echo "iteration: ", i+1
    exec i.thVersion & " $# $# --nimcache:$# compiler" / "nim.nim" % [bootOptions, args,
        smartNimcache]
    if sameFileContent(output, i.thVersion):
      copyExe(output, finalDest)
      echo "executables are equal: SUCCESS!"
      return
    copyExe(output, (i+1).thVersion)
  copyExe(output, finalDest)
  when not defined(windows): echo "[Warning] executables are still not equal"

# -------------- clean --------------------------------------------------------

const
  cleanExt = [
    ".ppu", ".o", ".obj", ".dcu", ".~pas", ".~inc", ".~dsk", ".~dpr",
    ".map", ".tds", ".err", ".bak", ".pyc", ".exe", ".rod", ".pdb", ".idb",
    ".idx", ".ilk"
  ]
  ignore = [
    ".bzrignore", "nim", "nim.exe", "koch", "koch.exe", ".gitignore"
  ]

proc cleanAux(dir: string) =
  for kind, path in walkDir(dir):
    case kind
    of pcFile:
      var (_, name, ext) = splitFile(path)
      if ext == "" or cleanExt.contains(ext):
        if not ignore.contains(name):
          echo "removing: ", path
          removeFile(path)
    of pcDir:
      case splitPath(path).tail
      of "nimcache":
        echo "removing dir: ", path
        removeDir(path)
      of "dist", ".git", "icons": discard
      else: cleanAux(path)
    else: discard

proc removePattern(pattern: string) =
  for f in walkFiles(pattern):
    echo "removing: ", f
    removeFile(f)

proc clean(args: string) =
  removePattern("web/*.html")
  removePattern("doc/*.html")
  cleanAux(getCurrentDir())
  for kind, path in walkDir(getCurrentDir() / "build"):
    if kind == pcDir:
      echo "removing dir: ", path
      removeDir(path)

# -------------- builds a release ---------------------------------------------

proc winReleaseArch(arch: string) =
  doAssert arch in ["32", "64"]
  let cpu = if arch == "32": "i386" else: "amd64"

  template withMingw(path, body) =
    let prevPath = getEnv("PATH")
    putEnv("PATH", (if path.len > 0: path & PathSep else: "") & prevPath)
    try:
      body
    finally:
      putEnv("PATH", prevPath)

  withMingw r"..\mingw" & arch & r"\bin":
    # Rebuilding koch is necessary because it uses its pointer size to
    # determine which mingw link to put in the NSIS installer.
    nimexec "c --cpu:$# koch" % cpu
    exec "koch boot -d:release --cpu:$#" % cpu
    exec "koch zip -d:release"
    overwriteFile r"build\nim-$#.zip" % VersionAsString,
             r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch]

proc winRelease*() =
  # Now used from "tools/winrelease" and not directly supported by koch
  # anymore!
  # Build -docs file:
  when true:
    web(gaCode)
    withDir "web/upload/" & VersionAsString:
      exec "7z a -tzip docs-$#.zip *.html" % VersionAsString
    overwriteFile "web/upload/$1/docs-$1.zip" % VersionAsString,
                  "web/upload/download/docs-$1.zip" % VersionAsString
  when true:
    csource("-d:release")
  when true:
    winReleaseArch "32"
  when true:
    winReleaseArch "64"

# -------------- tests --------------------------------------------------------

template `|`(a, b): string = (if a.len > 0: a else: b)

proc tests(args: string) =
  # we compile the tester with taintMode:on to have a basic
  # taint mode test :-)
  nimexec "cc --taintMode:on --opt:speed tests/testament/tester"
  # Since tests take a long time (on my machine), and we want to defy Murhpys
  # law - lets make sure the compiler really is freshly compiled!
  nimexec "c --lib:lib -d:release --opt:speed compiler/nim.nim"
  let tester = quoteShell(getCurrentDir() / "tests/testament/tester".exe)
  let success = tryExec tester & " " & (args|"all")
  if not existsEnv("TRAVIS") and not existsEnv("APPVEYOR"):
    exec tester & " html"
  if not success:
    quit("tests failed", QuitFailure)

proc temp(args: string) =
  proc splitArgs(a: string): (string, string) =
    # every --options before the command (indicated by starting
    # with not a dash) is part of the bootArgs, the rest is part
    # of the programArgs:
    let args = os.parseCmdLine a
    result = ("", "")
    var i = 0
    while i < args.len and args[i][0] == '-':
      result[0].add " " & quoteShell(args[i])
      inc i
    while i < args.len:
      result[1].add " " & quoteShell(args[i])
      inc i

  var output = "compiler" / "nim".exe
  var finalDest = "bin" / "nim_temp".exe
  # 125 is the magic number to tell git bisect to skip the current
  # commit.
  let (bootArgs, programArgs) = splitArgs(args)
  let nimexec = findNim()
  exec(nimexec & " c -d:debug --debugger:native " & bootArgs & " compiler" / "nim", 125)
  copyExe(output, finalDest)
  if programArgs.len > 0: exec(finalDest & " " & programArgs)

proc xtemp(cmd: string) =
  let d = getAppDir()
  copyExe(d / "bin" / "nim".exe, d / "bin" / "nim_backup".exe)
  try:
    withDir(d):
      temp""
    copyExe(d / "bin" / "nim_temp".exe, d / "bin" / "nim".exe)
    exec(cmd)
  finally:
    copyExe(d / "bin" / "nim_backup".exe, d / "bin" / "nim".exe)

proc pushCsources() =
  if not dirExists("../csources/.git"):
    quit "[Error] no csources git repository found"
  csource("-d:release")
  let cwd = getCurrentDir()
  try:
    copyDir("build/c_code", "../csources/c_code")
    copyFile("build/build.sh", "../csources/build.sh")
    copyFile("build/build.bat", "../csources/build.bat")
    copyFile("build/build64.bat", "../csources/build64.bat")
    copyFile("build/makefile", "../csources/makefile")

    setCurrentDir("../csources")
    for kind, path in walkDir("c_code"):
      if kind == pcDir:
        exec("git add " & path / "*.c")
    exec("git commit -am \"updated csources to version " & NimVersion & "\"")
    exec("git push origin master")
    exec("git tag -am \"Version $1\" v$1" % NimVersion)
    exec("git push origin v$1" % NimVersion)
  finally:
    setCurrentDir(cwd)

proc valgrind(cmd: string) =
  # somewhat hacky: '=' sign means "pass to valgrind" else "pass to Nim"
  let args = parseCmdLine(cmd)
  var nimcmd = ""
  var valcmd = ""
  for i, a in args:
    if i == args.len-1:
      # last element is the filename:
      valcmd.add ' '
      valcmd.add changeFileExt(a, ExeExt)
      nimcmd.add ' '
      nimcmd.add a
    elif '=' in a:
      valcmd.add ' '
      valcmd.add a
    else:
      nimcmd.add ' '
      nimcmd.add a
  exec("nim c" & nimcmd)
  let supp = getAppDir() / "tools" / "nimgrind.supp"
  exec("valgrind --suppressions=" & supp & valcmd)

proc showHelp() =
  quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
                   CompileDate, CompileTime], QuitSuccess)

when isMainModule:
  var op = initOptParser()
  op.next()
  case op.kind
  of cmdLongOption, cmdShortOption: showHelp()
  of cmdArgument:
    case normalize(op.key)
    of "boot": boot(op.cmdLineRest)
    of "clean": clean(op.cmdLineRest)
    of "web": web(op.cmdLineRest)
    of "doc", "docs": web("--onlyDocs " & op.cmdLineRest)
    of "json2": web("--json2 " & op.cmdLineRest)
    of "website": website(op.cmdLineRest & gaCode)
    of "web0":
      # undocumented command for Araq-the-merciful:
      web(op.cmdLineRest & gaCode)
    of "pdf": pdf()
    of "csource", "csources": csource(op.cmdLineRest)
    of "zip": zip(op.cmdLineRest)
    of "xz": xz(op.cmdLineRest)
    of "nsis": nsis(op.cmdLineRest)
    of "geninstall": geninstall(op.cmdLineRest)
    of "distrohelper": geninstall()
    of "install": install(op.cmdLineRest)
    of "testinstall": testUnixInstall()
    of "test", "tests": tests(op.cmdLineRest)
    of "temp": temp(op.cmdLineRest)
    of "xtemp": xtemp(op.cmdLineRest)
    #of "winrelease": winRelease()
    of "wintools": bundleWinTools()
    of "nimble": buildNimble(existsDir(".git"))
    of "nimsuggest": bundleNimsuggest(buildExe=true)
    of "tools": buildTools(existsDir(".git"))
    of "pushcsource", "pushcsources": pushCsources()
    of "valgrind": valgrind(op.cmdLineRest)
    else: showHelp()
  of cmdEnd: showHelp()