<commit_before>#
#
#         Maintenance program for Nimrod  
#        (c) Copyright 2014 <NAME>
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

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

import
  os, strutils, parseopt, osproc, streams

when defined(withUpdate):
  import httpclient
when defined(haveZipLib):
  import zipfiles

const
  HelpText = """
+-----------------------------------------------------------------+
|         Maintenance program for Nimrod                          |
|             Version $1|
|             (c) 2014 <NAME>                              |
+-----------------------------------------------------------------+
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
  install [dir]            installs to given directory
  clean                    cleans Nimrod project; removes generated files
  web                      generates the website
  csource [options]        builds the C sources for installation
  zip                      builds the installation ZIP package
  inno [options]           builds the Inno Setup installer (for Windows)
  tests [options]          run the testsuite
  update                   updates nimrod to the latest version from github
                           (compile koch with -d:withUpdate to enable)
  temp options             creates a temporary compiler for testing
Boot options:
  -d:release               produce a release version of the compiler
  -d:tinyc                 include the Tiny C backend (not supported on Windows)
  -d:useGnuReadline        use the GNU readline library for interactive mode
                           (not needed on Windows)
  -d:nativeStacktrace      use native stack traces (only for Mac OS X or Linux)
  -d:noCaas                build Nimrod without CAAS support
  -d:avoidTimeMachine      only for Mac OS X, excludes nimcache dir from backups
"""

proc exe(f: string): string = return addFileExt(f, ExeExt)

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

proc exec(cmd: string) =
  echo(cmd)
  if execShellCmd(cmd) != 0: quit("FAILURE")

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

const
  compileNimInst = "-d:useLibzipSrc tools/niminst/niminst"

proc csource(args: string) = 
  exec("$4 cc $1 -r $3 --var:version=$2 csource compiler/nimrod.ini $1" %
       [args, NimrodVersion, compileNimInst, findNim()])

proc zip(args: string) = 
  exec("$3 cc -r $2 --var:version=$1 zip compiler/nimrod.ini" %
       [NimrodVersion, compileNimInst, findNim()])
  
proc buildTool(toolname, args: string) = 
  exec("$# cc $# $#" % [findNim(), args, toolname])
  copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe)

proc inno(args: string) =
  # make sure we have generated the c2nim and niminst executables:
  buildTool("tools/niminst/niminst", args)
  buildTool("tools/nimgrep", args)
  buildTool("compiler/c2nim/c2nim", args)  
  exec("tools" / "niminst" / "niminst --var:version=$# inno compiler/nimrod" % 
       NimrodVersion)

proc install(args: string) = 
  exec("$# cc -r $# --var:version=$# scripts compiler/nimrod.ini" %
       [findNim(), compileNimInst, NimrodVersion])
  exec("sh ./install.sh $#" % args)

proc web(args: string) =
  exec("$# cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" % 
       [findNim(), NimrodVersion])

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

const
  bootOptions = "" # options to pass to the bootstrap process

proc findStartNimrod: string = 
  # we try several things before giving up:
  # * bin/nimrod
  # * $PATH/nimrod
  # If these fail, we try to build nimrod with the "build.(sh|bat)" script.
  var nimrod = "nimrod".exe
  result = "bin" / nimrod
  if existsFile(result): return
  for dir in split(getEnv("PATH"), PathSep):
    if existsFile(dir / nimrod): return dir / nimrod
  when defined(Posix):
    const buildScript = "build.sh"
    if existsFile(buildScript): 
      if tryExec("./" & buildScript): return "bin" / nimrod
  else:
    const buildScript = "build.bat"
    if existsFile(buildScript): 
      if tryExec(buildScript): return "bin" / nimrod
  
  echo("Found no nimrod compiler and every attempt to build one failed!")
  quit("FAILURE")

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

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

proc copyExe(source, dest: string) =
  safeRemove(dest)
  copyFile(dest=dest, source=source)
  inclFilePermissions(dest, {fpUserExec})
  
proc boot(args: string) =
  var output = "compiler" / "nimrod".exe
  var finalDest = "bin" / "nimrod".exe
  
  copyExe(findStartNimrod(), 0.thVersion)
  for i in 0..2:
    echo "iteration: ", i+1
    exec i.thVersion & " cc $# $# compiler" / "nimrod.nim" % [bootOptions, args]
    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"
  ]
  ignore = [
    ".bzrignore", "nimrod", "nimrod.exe", "koch", "koch.exe", ".gitignore"
  ]

proc cleanAux(dir: string) = 
  for kind, path in walkDir(dir):
    case kind
    of pcFile:
      var (dir, name, ext) = splitFile(path)
      if ext == "" or cleanExt.contains(ext):
        if not ignore.contains(name):
          echo "removing: ", path
          removeFile(path)
    of pcDir:<commit_msg>added .ilk files to the koch clean list, these are incremental link information files for MSVC<commit_after>#
#
#         Maintenance program for Nimrod  
#        (c) Copyright 2014 <NAME>
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

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

import
  os, strutils, parseopt, osproc, streams

when defined(withUpdate):
  import httpclient
when defined(haveZipLib):
  import zipfiles

const
  HelpText = """
+-----------------------------------------------------------------+
|         Maintenance program for Nimrod                          |
|             Version $1|
|             (c) 2014 <NAME>                              |
+-----------------------------------------------------------------+
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
  install [dir]            installs to given directory
  clean                    cleans Nimrod project; removes generated files
  web                      generates the website
  csource [options]        builds the C sources for installation
  zip                      builds the installation ZIP package
  inno [options]           builds the Inno Setup installer (for Windows)
  tests [options]          run the testsuite
  update                   updates nimrod to the latest version from github
                           (compile koch with -d:withUpdate to enable)
  temp options             creates a temporary compiler for testing
Boot options:
  -d:release               produce a release version of the compiler
  -d:tinyc                 include the Tiny C backend (not supported on Windows)
  -d:useGnuReadline        use the GNU readline library for interactive mode
                           (not needed on Windows)
  -d:nativeStacktrace      use native stack traces (only for Mac OS X or Linux)
  -d:noCaas                build Nimrod without CAAS support
  -d:avoidTimeMachine      only for Mac OS X, excludes nimcache dir from backups
"""

proc exe(f: string): string = return addFileExt(f, ExeExt)

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

proc exec(cmd: string) =
  echo(cmd)
  if execShellCmd(cmd) != 0: quit("FAILURE")

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

const
  compileNimInst = "-d:useLibzipSrc tools/niminst/niminst"

proc csource(args: string) = 
  exec("$4 cc $1 -r $3 --var:version=$2 csource compiler/nimrod.ini $1" %
       [args, NimrodVersion, compileNimInst, findNim()])

proc zip(args: string) = 
  exec("$3 cc -r $2 --var:version=$1 zip compiler/nimrod.ini" %
       [NimrodVersion, compileNimInst, findNim()])
  
proc buildTool(toolname, args: string) = 
  exec("$# cc $# $#" % [findNim(), args, toolname])
  copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe)

proc inno(args: string) =
  # make sure we have generated the c2nim and niminst executables:
  buildTool("tools/niminst/niminst", args)
  buildTool("tools/nimgrep", args)
  buildTool("compiler/c2nim/c2nim", args)  
  exec("tools" / "niminst" / "niminst --var:version=$# inno compiler/nimrod" % 
       NimrodVersion)

proc install(args: string) = 
  exec("$# cc -r $# --var:version=$# scripts compiler/nimrod.ini" %
       [findNim(), compileNimInst, NimrodVersion])
  exec("sh ./install.sh $#" % args)

proc web(args: string) =
  exec("$# cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" % 
       [findNim(), NimrodVersion])

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

const
  bootOptions = "" # options to pass to the bootstrap process

proc findStartNimrod: string = 
  # we try several things before giving up:
  # * bin/nimrod
  # * $PATH/nimrod
  # If these fail, we try to build nimrod with the "build.(sh|bat)" script.
  var nimrod = "nimrod".exe
  result = "bin" / nimrod
  if existsFile(result): return
  for dir in split(getEnv("PATH"), PathSep):
    if existsFile(dir / nimrod): return dir / nimrod
  when defined(Posix):
    const buildScript = "build.sh"
    if existsFile(buildScript): 
      if tryExec("./" & buildScript): return "bin" / nimrod
  else:
    const buildScript = "build.bat"
    if existsFile(buildScript): 
      if tryExec(buildScript): return "bin" / nimrod
  
  echo("Found no nimrod compiler and every attempt to build one failed!")
  quit("FAILURE")

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

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

proc copyExe(source, dest: string) =
  safeRemove(dest)
  copyFile(dest=dest, source=source)
  inclFilePermissions(dest, {fpUserExec})
  
proc boot(args: string) =
  var output = "compiler" / "nimrod".exe
  var finalDest = "bin" / "nimrod".exe
  
  copyExe(findStartNimrod(), 0.thVersion)
  for i in 0..2:
    echo "iteration: ", i+1
    exec i.thVersion & " cc $# $# compiler" / "nimrod.nim" % [bootOptions, args]
    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", "nimrod", "nimrod.exe", "koch", "koch.exe", ".gitignore"
  ]

proc cleanAux(dir: string) = 
  for kind, path in walkDir(dir):
    case kind
    of pcFile:
      var (dir, name, ext) = splitFile(path)
      if ext == "" or cleanExt.contains(ext):
        if not ignore.contains(name):
          echo "removing: ", path
          removeFile(path)
    of pcDir:
{'fraction_non_alphanumeric': 0.16170548756415318, 'fraction_numerical': 0.004895380971180419, 'mean_word_length': 2.407586763518967, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2379802', 'n_tokens_mistral': 4010, 'n_tokens_neox': 3934, 'n_words': 1205}