|
| 1 | +import macros, strformat, strutils, sequtils, sets, tables, algorithm |
| 2 | + |
| 3 | +from os import parentDir, getCurrentCompilerExe, DirSep, extractFilename, `/`, setCurrentDir |
| 4 | + |
| 5 | +# NOTE: |
| 6 | +# for some time on devel 1.3.x `paramCount` and `paramStr` had to be imported |
| 7 | +# os, because they were removed for nimscript. This was reverted in: |
| 8 | +# https://github.com/nim-lang/Nim/pull/14658 |
| 9 | +# For `nimdoc` we still have to import those from `os`! |
| 10 | +when defined(nimdoc): |
| 11 | + from os import getCurrentDir, paramCount, paramStr |
| 12 | + |
| 13 | +#[ |
| 14 | +This file is a slightly modified version of the same file of `nimterop`: |
| 15 | +https://github.com/nimterop/nimterop/blob/master/nimterop/docs.nim |
| 16 | +]# |
| 17 | + |
| 18 | + |
| 19 | +proc getNimRootDir(): string = |
| 20 | + #[ |
| 21 | + hack, but works |
| 22 | + alternatively (but more complex), use (from a nim file, not nims otherwise |
| 23 | + you get Error: ambiguous call; both system.fileExists): |
| 24 | + import "$nim/testament/lib/stdtest/specialpaths.nim" |
| 25 | + nimRootDir |
| 26 | + ]# |
| 27 | + fmt"{currentSourcePath}".parentDir.parentDir.parentDir |
| 28 | + |
| 29 | +const |
| 30 | + DirSep = when defined(windows): '\\' else: '/' |
| 31 | + |
| 32 | +proc execAction(cmd: string): string = |
| 33 | + var |
| 34 | + ccmd = "" |
| 35 | + ret = 0 |
| 36 | + when defined(Windows): |
| 37 | + ccmd = "cmd /c " & cmd |
| 38 | + elif defined(posix): |
| 39 | + ccmd = cmd |
| 40 | + else: |
| 41 | + doAssert false |
| 42 | + |
| 43 | + (result, ret) = gorgeEx(ccmd) |
| 44 | + doAssert ret == 0, "Command failed: " & $ret & "\ncmd: " & ccmd & "\nresult:\n" & result |
| 45 | + |
| 46 | +template genRemove(name: untyped): untyped = |
| 47 | + proc `name`(s, toRemove: string): string = |
| 48 | + result = s |
| 49 | + result.`name`(toRemove) |
| 50 | +genRemove(removePrefix) |
| 51 | +genRemove(removeSuffix) |
| 52 | + |
| 53 | +proc getFiles*(path: string): seq[string] = |
| 54 | + # Add files and dirs here, which should be skipped. |
| 55 | + #const excludeDirs = [] |
| 56 | + #let ExcludeDirSet = toSet(excludeDirs) |
| 57 | + #if path.extractFilename in ExcludeDirSet: return |
| 58 | + # The files below are not valid by themselves, they are only included |
| 59 | + # from other files |
| 60 | + const excludeFiles = [ "formula.nim" ] |
| 61 | + let ExcludeFileSet = toSet(excludeFiles) |
| 62 | + |
| 63 | + for file in listFiles(path): |
| 64 | + if file.endsWith(".nim") and file.extractFilename notin ExcludeFileSet: |
| 65 | + result.add file |
| 66 | + for dir in listDirs(path): |
| 67 | + result.add getFiles(dir) |
| 68 | + |
| 69 | +proc buildDocs*(path: string, docPath: string, |
| 70 | + defaultFlags = "", |
| 71 | + masterBranch = "master", |
| 72 | + defines: openArray[string] = @[]) = |
| 73 | + ## Generate docs for all nim files in `path` and output all HTML files to the |
| 74 | + ## `docPath` in a flattened form (subdirectories are removed). |
| 75 | + ## |
| 76 | + ## If duplicate filenames are detected, they will be printed at the end. |
| 77 | + ## |
| 78 | + ## WARNING: not in use! `baseDir` is the project path by default and `files` and `path` are relative |
| 79 | + ## to that directory. Set to "" if using absolute paths. |
| 80 | + ## |
| 81 | + ## `masterBranch` is the name of the default branch to which the docs should link |
| 82 | + ## when clicking the `Source` button below a procedure etc. |
| 83 | + ## |
| 84 | + ## `defines` is a list of `-d:xxx` define flags (the `xxx` part) that should be passed |
| 85 | + ## to `nim doc` so that `getHeader()` is invoked correctly. |
| 86 | + ## |
| 87 | + ## Use the `--publish` flag with nimble to publish docs contained in |
| 88 | + ## `path` to Github in the `gh-pages` branch. This requires the ghp-import |
| 89 | + ## package for Python: `pip install ghp-import` |
| 90 | + ## |
| 91 | + ## WARNING: `--publish` will destroy any existing content in this branch. |
| 92 | + ## |
| 93 | + ## NOTE: `buildDocs()` only works correctly on Windows with Nim 1.0+ since |
| 94 | + ## https://github.com/nim-lang/Nim/pull/11814 is required. |
| 95 | + ## |
| 96 | + ## |
| 97 | + const gitUrl = "https://github.com/Vindaar/ggplotnim" |
| 98 | + ## WARNING: this means `gen_docs` *only* works if you use `nimble develop` on |
| 99 | + ## the repository. Nimble cannot deal with ****. This is frustrating. Thanks. |
| 100 | + let baseDir = execAction("nimble path ggplotnim").parentDir & $DirSep |
| 101 | + when defined(windows) and (NimMajor, NimMinor, NimPatch) < (1, 0, 0): |
| 102 | + echo "buildDocs() unsupported on Windows for Nim < 1.0 - requires PR #11814" |
| 103 | + else: |
| 104 | + let |
| 105 | + docPath = baseDir & docPath |
| 106 | + path = baseDir & path |
| 107 | + defStr = block: |
| 108 | + var defStr = " " & defaultFlags |
| 109 | + for def in defines: |
| 110 | + defStr &= " -d:" & def |
| 111 | + defStr |
| 112 | + nim = getCurrentCompilerExe() |
| 113 | + |
| 114 | + # now we walk the whole `path` and build the documentation for each `.nim` file. |
| 115 | + # While doing that we flatten the directory structure for the generated HTML files. |
| 116 | + # `src/foo/bar/baz.nim` just becomes |
| 117 | + # `docPath/baz.html`. |
| 118 | + # This allows for all files to be in the `docPath` directory, which means each |
| 119 | + # file will be able to find the `dochack.js` file, which will be put into |
| 120 | + # the `docPath` directory, too (the inclusion of the `dochack.js` is done statically |
| 121 | + # via our generated nimdoc.cfg file and is fixed for each generated HTML). |
| 122 | + let files = getFiles(path) |
| 123 | + var idx = 0 |
| 124 | + var fileSet = initHashSet[string]() |
| 125 | + var duplSet = initHashSet[string]() |
| 126 | + for file in files: |
| 127 | + let baseName = file.extractFilename() |
| 128 | + let relPath = file.removePrefix(path).removeSuffix(baseName) |
| 129 | + let prefix = relPath.strip(chars = {'/'}) # remove possible trailing `/` |
| 130 | + .split('/') # split path parts |
| 131 | + .join(".") # concat by `.` instead |
| 132 | + var outfile = baseName.replace(".nim", ".html") |
| 133 | + if outfile in fileSet: |
| 134 | + duplSet.incl outfile |
| 135 | + else: |
| 136 | + fileSet.incl outfile |
| 137 | + outfile = docPath / outfile |
| 138 | + echo "Processing: ", outfile, " [", idx, "/", files.len, "]" |
| 139 | + # NOTE: Changing the current working directory to the project path is required in order for |
| 140 | + # `git.commit:` to work! Otherwise we sit in `docs` and for some reason the relative path |
| 141 | + # will eat one piece of the resulting `source` links and thereby removing the actual branch |
| 142 | + # and we end up with a broken link! |
| 143 | + echo execAction(&"cd {baseDir} && {nim} doc {defStr} --git.url:{gitUrl} --git.commit:{masterBranch} --git.devel:{masterBranch} -o:{outfile} --index:on {file}") |
| 144 | + inc idx |
| 145 | + ## now build the index |
| 146 | + echo execAction(&"{nim} buildIndex -o:{docPath}/theindex.html {docPath}") |
| 147 | + when declared(getNimRootDir): |
| 148 | + #[ |
| 149 | + NOTE: running it locally doesn't work anymore on modern chromium browser, |
| 150 | + because they block "access from origin 'null' due to CORS policy". |
| 151 | + this enables doc search, works at least locally with: |
| 152 | + cd {docPath} && python -m SimpleHTTPServer 9009 |
| 153 | + ]# |
| 154 | + echo execAction(&"{nim} js -o:{docPath}/dochack.js {getNimRootDir()}/tools/dochack/dochack.nim") |
| 155 | + |
| 156 | + # echo "Processed files: ", fileSet |
| 157 | + if duplSet.card > 0: |
| 158 | + echo "WARNING: Duplicate filenames detected: ", duplSet |
0 commit comments