kochdocs.nim 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. ## Part of 'koch' responsible for the documentation generation.
  2. import os, strutils, osproc, sets, pathnorm
  3. const
  4. gaCode* = " --doc.googleAnalytics:UA-48159761-1"
  5. # --warning[LockLevel]:off pending #13218
  6. nimArgs = "--warning[LockLevel]:off --hint[Conf]:off --hint[Path]:off --hint[Processing]:off -d:boot --putenv:nimversion=$#" % system.NimVersion
  7. gitUrl = "https://github.com/nim-lang/Nim"
  8. docHtmlOutput = "doc/html"
  9. webUploadOutput = "web/upload"
  10. docHackDir = "tools/dochack"
  11. var nimExe*: string
  12. proc exe*(f: string): string =
  13. result = addFileExt(f, ExeExt)
  14. when defined(windows):
  15. result = result.replace('/','\\')
  16. proc findNimImpl*(): tuple[path: string, ok: bool] =
  17. if nimExe.len > 0: return (nimExe, true)
  18. let nim = "nim".exe
  19. result.path = "bin" / nim
  20. result.ok = true
  21. if existsFile(result.path): return
  22. for dir in split(getEnv("PATH"), PathSep):
  23. result.path = dir / nim
  24. if existsFile(result.path): return
  25. # assume there is a symlink to the exe or something:
  26. return (nim, false)
  27. proc findNim*(): string = findNimImpl().path
  28. proc exec*(cmd: string, errorcode: int = QuitFailure, additionalPath = "") =
  29. let prevPath = getEnv("PATH")
  30. if additionalPath.len > 0:
  31. var absolute = additionalPath
  32. if not absolute.isAbsolute:
  33. absolute = getCurrentDir() / absolute
  34. echo("Adding to $PATH: ", absolute)
  35. putEnv("PATH", (if prevPath.len > 0: prevPath & PathSep else: "") & absolute)
  36. echo(cmd)
  37. if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
  38. putEnv("PATH", prevPath)
  39. template inFold*(desc, body) =
  40. if existsEnv("TRAVIS"):
  41. echo "travis_fold:start:" & desc.replace(" ", "_")
  42. body
  43. if existsEnv("TRAVIS"):
  44. echo "travis_fold:end:" & desc.replace(" ", "_")
  45. proc execFold*(desc, cmd: string, errorcode: int = QuitFailure, additionalPath = "") =
  46. ## Execute shell command. Add log folding on Travis CI.
  47. # https://github.com/travis-ci/travis-ci/issues/2285#issuecomment-42724719
  48. inFold(desc):
  49. exec(cmd, errorcode, additionalPath)
  50. proc execCleanPath*(cmd: string,
  51. additionalPath = ""; errorcode: int = QuitFailure) =
  52. # simulate a poor man's virtual environment
  53. let prevPath = getEnv("PATH")
  54. when defined(windows):
  55. let cleanPath = r"$1\system32;$1;$1\System32\Wbem" % getEnv"SYSTEMROOT"
  56. else:
  57. const cleanPath = r"/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin"
  58. putEnv("PATH", cleanPath & PathSep & additionalPath)
  59. echo(cmd)
  60. if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
  61. putEnv("PATH", prevPath)
  62. proc nimexec*(cmd: string) =
  63. # Consider using `nimCompile` instead
  64. exec findNim().quoteShell() & " " & cmd
  65. proc nimCompile*(input: string, outputDir = "bin", mode = "c", options = "") =
  66. let output = outputDir / input.splitFile.name.exe
  67. let cmd = findNim().quoteShell() & " " & mode & " -o:" & output & " " & options & " " & input
  68. exec cmd
  69. proc nimCompileFold*(desc, input: string, outputDir = "bin", mode = "c", options = "") =
  70. let output = outputDir / input.splitFile.name.exe
  71. let cmd = findNim().quoteShell() & " " & mode & " -o:" & output & " " & options & " " & input
  72. execFold(desc, cmd)
  73. const
  74. pdf = """
  75. doc/manual.rst
  76. doc/lib.rst
  77. doc/tut1.rst
  78. doc/tut2.rst
  79. doc/tut3.rst
  80. doc/nimc.rst
  81. doc/niminst.rst
  82. doc/gc.rst
  83. """.splitWhitespace()
  84. rst2html = """
  85. doc/intern.rst
  86. doc/apis.rst
  87. doc/lib.rst
  88. doc/manual.rst
  89. doc/manual_experimental.rst
  90. doc/destructors.rst
  91. doc/tut1.rst
  92. doc/tut2.rst
  93. doc/tut3.rst
  94. doc/nimc.rst
  95. doc/hcr.rst
  96. doc/overview.rst
  97. doc/filters.rst
  98. doc/tools.rst
  99. doc/niminst.rst
  100. doc/nimgrep.rst
  101. doc/gc.rst
  102. doc/estp.rst
  103. doc/idetools.rst
  104. doc/docgen.rst
  105. doc/koch.rst
  106. doc/backends.rst
  107. doc/nimsuggest.rst
  108. doc/nep1.rst
  109. doc/nims.rst
  110. doc/contributing.rst
  111. doc/codeowners.rst
  112. doc/packaging.rst
  113. doc/manual/var_t_return.rst
  114. """.splitWhitespace()
  115. doc0 = """
  116. lib/system/threads.nim
  117. lib/system/channels.nim
  118. """.splitWhitespace() # ran by `nim doc0` instead of `nim doc`
  119. withoutIndex = """
  120. lib/wrappers/mysql.nim
  121. lib/wrappers/iup.nim
  122. lib/wrappers/sqlite3.nim
  123. lib/wrappers/postgres.nim
  124. lib/wrappers/tinyc.nim
  125. lib/wrappers/odbcsql.nim
  126. lib/wrappers/pcre.nim
  127. lib/wrappers/openssl.nim
  128. lib/posix/posix.nim
  129. lib/posix/linux.nim
  130. lib/posix/termios.nim
  131. lib/js/jscore.nim
  132. """.splitWhitespace()
  133. # some of these are include files so shouldn't be docgen'd
  134. ignoredModules = """
  135. lib/prelude.nim
  136. lib/pure/future.nim
  137. lib/pure/collections/hashcommon.nim
  138. lib/pure/collections/tableimpl.nim
  139. lib/pure/collections/setimpl.nim
  140. lib/pure/ioselects/ioselectors_kqueue.nim
  141. lib/pure/ioselects/ioselectors_select.nim
  142. lib/pure/ioselects/ioselectors_poll.nim
  143. lib/pure/ioselects/ioselectors_epoll.nim
  144. lib/posix/posix_macos_amd64.nim
  145. lib/posix/posix_other.nim
  146. lib/posix/posix_nintendoswitch.nim
  147. lib/posix/posix_nintendoswitch_consts.nim
  148. lib/posix/posix_linux_amd64.nim
  149. lib/posix/posix_linux_amd64_consts.nim
  150. lib/posix/posix_other_consts.nim
  151. lib/posix/posix_openbsd_amd64.nim
  152. """.splitWhitespace()
  153. when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo):
  154. proc isRelativeTo(path, base: string): bool =
  155. let path = path.normalizedPath
  156. let base = base.normalizedPath
  157. let ret = relativePath(path, base)
  158. result = path.len > 0 and not ret.startsWith ".."
  159. proc getDocList(): seq[string] =
  160. var docIgnore: HashSet[string]
  161. for a in doc0: docIgnore.incl a
  162. for a in withoutIndex: docIgnore.incl a
  163. for a in ignoredModules: docIgnore.incl a
  164. # don't ignore these even though in lib/system
  165. const goodSystem = """
  166. lib/system/io.nim
  167. lib/system/nimscript.nim
  168. lib/system/assertions.nim
  169. lib/system/iterators.nim
  170. lib/system/dollars.nim
  171. lib/system/widestrs.nim
  172. """.splitWhitespace()
  173. for a in walkDirRec("lib"):
  174. if a.splitFile.ext != ".nim" or
  175. a.isRelativeTo("lib/pure/includes") or
  176. a.isRelativeTo("lib/genode") or
  177. a.isRelativeTo("lib/deprecated") or
  178. (a.isRelativeTo("lib/system") and a.replace('\\', '/') notin goodSystem) or
  179. a.replace('\\', '/') in docIgnore:
  180. continue
  181. result.add a
  182. result.add normalizePath("nimsuggest/sexp.nim")
  183. let doc = getDocList()
  184. proc sexec(cmds: openArray[string]) =
  185. ## Serial queue wrapper around exec.
  186. for cmd in cmds:
  187. echo(cmd)
  188. let (outp, exitCode) = osproc.execCmdEx(cmd)
  189. if exitCode != 0: quit outp
  190. proc mexec(cmds: openArray[string]) =
  191. ## Multiprocessor version of exec
  192. let r = execProcesses(cmds, {poStdErrToStdOut, poParentStreams, poEchoCmd})
  193. if r != 0:
  194. echo "external program failed, retrying serial work queue for logs!"
  195. sexec(cmds)
  196. proc buildDocSamples(nimArgs, destPath: string) =
  197. ## Special case documentation sample proc.
  198. ##
  199. ## TODO: consider integrating into the existing generic documentation builders
  200. ## now that we have a single `doc` command.
  201. exec(findNim().quoteShell() & " doc $# -o:$# $#" %
  202. [nimArgs, destPath / "docgen_sample.html", "doc" / "docgen_sample.nim"])
  203. proc buildDoc(nimArgs, destPath: string) =
  204. # call nim for the documentation:
  205. var
  206. commands = newSeq[string](rst2html.len + len(doc0) + len(doc) + withoutIndex.len)
  207. i = 0
  208. let nim = findNim().quoteShell()
  209. for d in items(rst2html):
  210. commands[i] = nim & " rst2html $# --git.url:$# -o:$# --index:on $#" %
  211. [nimArgs, gitUrl,
  212. destPath / changeFileExt(splitFile(d).name, "html"), d]
  213. i.inc
  214. for d in items(doc0):
  215. commands[i] = nim & " doc0 $# --git.url:$# -o:$# --index:on $#" %
  216. [nimArgs, gitUrl,
  217. destPath / changeFileExt(splitFile(d).name, "html"), d]
  218. i.inc
  219. for d in items(doc):
  220. var nimArgs2 = nimArgs
  221. if d.isRelativeTo("compiler"):
  222. nimArgs2.add " --docroot"
  223. commands[i] = nim & " doc $# --git.url:$# --outdir:$# --index:on $#" %
  224. [nimArgs2, gitUrl, destPath, d]
  225. i.inc
  226. for d in items(withoutIndex):
  227. commands[i] = nim & " doc2 $# --git.url:$# -o:$# $#" %
  228. [nimArgs, gitUrl,
  229. destPath / changeFileExt(splitFile(d).name, "html"), d]
  230. i.inc
  231. mexec(commands)
  232. exec(nim & " buildIndex -o:$1/theindex.html $1" % [destPath])
  233. proc buildPdfDoc*(nimArgs, destPath: string) =
  234. createDir(destPath)
  235. if os.execShellCmd("pdflatex -version") != 0:
  236. echo "pdflatex not found; no PDF documentation generated"
  237. else:
  238. const pdflatexcmd = "pdflatex -interaction=nonstopmode "
  239. for d in items(pdf):
  240. exec(findNim().quoteShell() & " rst2tex $# $#" % [nimArgs, d])
  241. # call LaTeX twice to get cross references right:
  242. exec(pdflatexcmd & changeFileExt(d, "tex"))
  243. exec(pdflatexcmd & changeFileExt(d, "tex"))
  244. # delete all the crappy temporary files:
  245. let pdf = splitFile(d).name & ".pdf"
  246. let dest = destPath / pdf
  247. removeFile(dest)
  248. moveFile(dest=dest, source=pdf)
  249. removeFile(changeFileExt(pdf, "aux"))
  250. if existsFile(changeFileExt(pdf, "toc")):
  251. removeFile(changeFileExt(pdf, "toc"))
  252. removeFile(changeFileExt(pdf, "log"))
  253. removeFile(changeFileExt(pdf, "out"))
  254. removeFile(changeFileExt(d, "tex"))
  255. proc buildJS() =
  256. exec(findNim().quoteShell() & " js -d:release --out:$1 tools/nimblepkglist.nim" %
  257. [webUploadOutput / "nimblepkglist.js"])
  258. exec(findNim().quoteShell() & " js " & (docHackDir / "dochack.nim"))
  259. proc buildDocs*(args: string) =
  260. const
  261. docHackJs = "dochack.js"
  262. let
  263. a = nimArgs & " " & args
  264. docHackJsSource = docHackDir / docHackJs
  265. docHackJsDest = docHtmlOutput / docHackJs
  266. buildJS() # This call generates docHackJsSource
  267. let docup = webUploadOutput / NimVersion
  268. createDir(docup)
  269. buildDocSamples(a, docup)
  270. buildDoc(a, docup)
  271. # 'nimArgs' instead of 'a' is correct here because we don't want
  272. # that the offline docs contain the 'gaCode'!
  273. createDir(docHtmlOutput)
  274. buildDocSamples(nimArgs, docHtmlOutput)
  275. buildDoc(nimArgs, docHtmlOutput)
  276. copyFile(docHackJsSource, docHackJsDest)
  277. copyFile(docHackJsSource, docup / docHackJs)