remove deprecated and broken nimweb tools (#20442)
* remove deprecated and broken nimweb tools * readme
This commit is contained in:
parent
89f76bb448
commit
95614089ac
5 changed files with 2 additions and 842 deletions
|
|
@ -463,11 +463,7 @@ You can edit ``config/nimdoc.cfg`` and modify the ``doc.item.seesrc`` value
|
||||||
with a hyperlink to your own code repository.
|
with a hyperlink to your own code repository.
|
||||||
|
|
||||||
In the case of Nim's own documentation, the `commit` value is just a commit
|
In the case of Nim's own documentation, the `commit` value is just a commit
|
||||||
hash to append to a formatted URL to https://github.com/nim-lang/Nim. The
|
hash to append to a formatted URL to https://github.com/nim-lang/Nim.
|
||||||
``tools/nimweb.nim`` helper queries the current git commit hash during the doc
|
|
||||||
generation, but since you might be working on an unpublished repository, it
|
|
||||||
also allows specifying a `githash` value in ``web/website.ini`` to force a
|
|
||||||
specific commit in the output.
|
|
||||||
|
|
||||||
|
|
||||||
Other Input Formats
|
Other Input Formats
|
||||||
|
|
|
||||||
14
koch.nim
14
koch.nim
|
|
@ -251,20 +251,6 @@ proc install(args: string) =
|
||||||
geninstall()
|
geninstall()
|
||||||
exec("sh ./install.sh $#" % args)
|
exec("sh ./install.sh $#" % args)
|
||||||
|
|
||||||
when false:
|
|
||||||
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().quoteShell(), args, VersionAsString], additionalPATH=findNim().splitFile.dir)
|
|
||||||
|
|
||||||
# -------------- boot ---------------------------------------------------------
|
# -------------- boot ---------------------------------------------------------
|
||||||
|
|
||||||
proc findStartNim: string =
|
proc findStartNim: string =
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ you should familiarize yourself with the following repository structure:
|
||||||
dependencies written in other languages.
|
dependencies written in other languages.
|
||||||
* ``wrappers/`` - modules that wrap dependencies written in other languages.
|
* ``wrappers/`` - modules that wrap dependencies written in other languages.
|
||||||
* ``tests/`` - contains categorized tests for the compiler and standard library.
|
* ``tests/`` - contains categorized tests for the compiler and standard library.
|
||||||
* ``tools/`` - the tools including ``niminst`` and ``nimweb`` (mostly invoked via
|
* ``tools/`` - the tools including ``niminst`` (mostly invoked via
|
||||||
``koch``).
|
``koch``).
|
||||||
* ``koch.nim`` - the tool used to bootstrap Nim, generate C sources, build the website,
|
* ``koch.nim`` - the tool used to bootstrap Nim, generate C sources, build the website,
|
||||||
and generate the documentation.
|
and generate the documentation.
|
||||||
|
|
|
||||||
556
tools/nimweb.nim
556
tools/nimweb.nim
|
|
@ -1,556 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nim Website Generator
|
|
||||||
# (c) Copyright 2015 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
import
|
|
||||||
os, strutils, times, parseopt, parsecfg, streams, strtabs, tables,
|
|
||||||
re, htmlgen, macros, md5, osproc, parsecsv, algorithm
|
|
||||||
|
|
||||||
from xmltree import escape
|
|
||||||
|
|
||||||
type
|
|
||||||
TKeyValPair = tuple[key, id, val: string]
|
|
||||||
TConfigData = object of RootObj
|
|
||||||
tabs, links: seq[TKeyValPair]
|
|
||||||
doc, srcdoc, srcdoc2, webdoc, pdf: seq[string]
|
|
||||||
authors, projectName, projectTitle, logo, infile, ticker: string
|
|
||||||
vars: StringTableRef
|
|
||||||
nimCompiler: string
|
|
||||||
nimArgs: string
|
|
||||||
gitURL: string
|
|
||||||
docHTMLOutput: string
|
|
||||||
webUploadOutput: string
|
|
||||||
quotations: Table[string, tuple[quote, author: string]]
|
|
||||||
numProcessors: int # Set by parallelBuild:n, only works for values > 0.
|
|
||||||
gaId: string # google analytics ID, nil means analytics are disabled
|
|
||||||
TRssItem = object
|
|
||||||
year, month, day, title, url, content: string
|
|
||||||
TAction = enum
|
|
||||||
actAll, actOnlyWebsite, actPdf, actJson2, actOnlyDocs
|
|
||||||
|
|
||||||
Sponsor = object
|
|
||||||
logo: string
|
|
||||||
name: string
|
|
||||||
url: string
|
|
||||||
thisMonth: int
|
|
||||||
allTime: int
|
|
||||||
since: string
|
|
||||||
level: int
|
|
||||||
|
|
||||||
var action: TAction
|
|
||||||
|
|
||||||
proc initConfigData(c: var TConfigData) =
|
|
||||||
c.tabs = @[]
|
|
||||||
c.links = @[]
|
|
||||||
c.doc = @[]
|
|
||||||
c.srcdoc = @[]
|
|
||||||
c.srcdoc2 = @[]
|
|
||||||
c.webdoc = @[]
|
|
||||||
c.pdf = @[]
|
|
||||||
c.infile = ""
|
|
||||||
c.nimArgs = "--hint:Conf:off --hint:Path:off --hint:Processing:off -d:boot "
|
|
||||||
c.gitURL = "https://github.com/nim-lang/Nim"
|
|
||||||
c.docHTMLOutput = "doc/html"
|
|
||||||
c.webUploadOutput = "web/upload"
|
|
||||||
c.authors = ""
|
|
||||||
c.projectTitle = ""
|
|
||||||
c.projectName = ""
|
|
||||||
c.logo = ""
|
|
||||||
c.ticker = ""
|
|
||||||
c.vars = newStringTable(modeStyleInsensitive)
|
|
||||||
c.numProcessors = countProcessors()
|
|
||||||
# Attempts to obtain the git current commit.
|
|
||||||
when false:
|
|
||||||
let (output, code) = execCmdEx("git log -n 1 --format=%H")
|
|
||||||
if code == 0 and output.strip.len == 40:
|
|
||||||
c.gitCommit = output.strip
|
|
||||||
c.quotations = initTable[string, tuple[quote, author: string]]()
|
|
||||||
|
|
||||||
include "website.nimf"
|
|
||||||
|
|
||||||
# ------------------------- configuration file -------------------------------
|
|
||||||
|
|
||||||
const
|
|
||||||
version = "0.8"
|
|
||||||
usage = "nimweb - Nim Website Generator Version " & version & """
|
|
||||||
|
|
||||||
(c) 2015 Andreas Rumpf
|
|
||||||
Usage:
|
|
||||||
nimweb [options] ini-file[.ini] [compile_options]
|
|
||||||
Options:
|
|
||||||
-h, --help shows this help
|
|
||||||
-v, --version shows the version
|
|
||||||
-o, --output overrides output directory instead of default
|
|
||||||
web/upload and doc/html
|
|
||||||
--nimCompiler overrides nim compiler; default = bin/nim
|
|
||||||
--var:name=value set the value of a variable
|
|
||||||
--website only build the website, not the full documentation
|
|
||||||
--pdf build the PDF version of the documentation
|
|
||||||
--json2 build JSON of the documentation
|
|
||||||
--onlyDocs build only the documentation
|
|
||||||
--git.url override base url in generated doc links
|
|
||||||
--git.commit override commit/branch in generated doc links 'source'
|
|
||||||
--git.devel override devel branch in generated doc links 'edit'
|
|
||||||
Compile_options:
|
|
||||||
will be passed to the Nim compiler
|
|
||||||
"""
|
|
||||||
|
|
||||||
rYearMonthDay = r"on\s+(\d{2})\/(\d{2})\/(\d{4})"
|
|
||||||
rssUrl = "http://nim-lang.org/news.xml"
|
|
||||||
rssNewsUrl = "http://nim-lang.org/news.html"
|
|
||||||
activeSponsors = "web/sponsors.csv"
|
|
||||||
inactiveSponsors = "web/inactive_sponsors.csv"
|
|
||||||
validAnchorCharacters = Letters + Digits
|
|
||||||
|
|
||||||
|
|
||||||
macro id(e: untyped): untyped =
|
|
||||||
## generates the rss xml ``id`` element.
|
|
||||||
let e = callsite()
|
|
||||||
result = xmlCheckedTag(e, "id")
|
|
||||||
|
|
||||||
macro updated(e: varargs[untyped]): untyped =
|
|
||||||
## generates the rss xml ``updated`` element.
|
|
||||||
let e = callsite()
|
|
||||||
result = xmlCheckedTag(e, "updated")
|
|
||||||
|
|
||||||
proc updatedDate(year, month, day: string): string =
|
|
||||||
## wrapper around the update macro with easy input.
|
|
||||||
result = updated("$1-$2-$3T00:00:00Z" % [year,
|
|
||||||
repeat("0", 2 - len(month)) & month,
|
|
||||||
repeat("0", 2 - len(day)) & day])
|
|
||||||
|
|
||||||
macro entry(e: varargs[untyped]): untyped =
|
|
||||||
## generates the rss xml ``entry`` element.
|
|
||||||
let e = callsite()
|
|
||||||
result = xmlCheckedTag(e, "entry")
|
|
||||||
|
|
||||||
macro content(e: varargs[untyped]): untyped =
|
|
||||||
## generates the rss xml ``content`` element.
|
|
||||||
let e = callsite()
|
|
||||||
result = xmlCheckedTag(e, "content", reqAttr = "type")
|
|
||||||
|
|
||||||
proc parseCmdLine(c: var TConfigData) =
|
|
||||||
var p = initOptParser()
|
|
||||||
while true:
|
|
||||||
next(p)
|
|
||||||
var kind = p.kind
|
|
||||||
var key = p.key
|
|
||||||
var val = p.val
|
|
||||||
case kind
|
|
||||||
of cmdArgument:
|
|
||||||
c.infile = addFileExt(key, "ini")
|
|
||||||
c.nimArgs.add(cmdLineRest(p))
|
|
||||||
break
|
|
||||||
of cmdLongOption, cmdShortOption:
|
|
||||||
case normalize(key)
|
|
||||||
of "help", "h":
|
|
||||||
stdout.write(usage)
|
|
||||||
quit(0)
|
|
||||||
of "version", "v":
|
|
||||||
stdout.write(version & "\n")
|
|
||||||
quit(0)
|
|
||||||
of "output", "o":
|
|
||||||
c.webUploadOutput = val
|
|
||||||
c.docHTMLOutput = val / "docs"
|
|
||||||
of "nimcompiler":
|
|
||||||
c.nimCompiler = val
|
|
||||||
of "parallelbuild":
|
|
||||||
try:
|
|
||||||
let num = parseInt(val)
|
|
||||||
if num != 0: c.numProcessors = num
|
|
||||||
except ValueError:
|
|
||||||
quit("invalid numeric value for --parallelBuild")
|
|
||||||
of "var":
|
|
||||||
var idx = val.find('=')
|
|
||||||
if idx < 0: quit("invalid command line")
|
|
||||||
c.vars[substr(val, 0, idx-1)] = substr(val, idx+1)
|
|
||||||
of "website": action = actOnlyWebsite
|
|
||||||
of "pdf": action = actPdf
|
|
||||||
of "json2": action = actJson2
|
|
||||||
of "onlydocs": action = actOnlyDocs
|
|
||||||
of "googleanalytics":
|
|
||||||
c.gaId = val
|
|
||||||
c.nimArgs.add("--doc.googleAnalytics:" & val & " ")
|
|
||||||
of "git.url":
|
|
||||||
c.gitURL = val
|
|
||||||
of "git.commit":
|
|
||||||
c.nimArgs.add("--git.commit:" & val & " ")
|
|
||||||
of "git.devel":
|
|
||||||
c.nimArgs.add("--git.devel:" & val & " ")
|
|
||||||
else:
|
|
||||||
echo("Invalid argument '$1'" % [key])
|
|
||||||
quit(usage)
|
|
||||||
of cmdEnd: break
|
|
||||||
if c.infile.len == 0: quit(usage)
|
|
||||||
|
|
||||||
proc walkDirRecursively(s: var seq[string], root, ext: string) =
|
|
||||||
for k, f in walkDir(root):
|
|
||||||
case k
|
|
||||||
of pcFile, pcLinkToFile:
|
|
||||||
if cmpIgnoreCase(ext, splitFile(f).ext) == 0:
|
|
||||||
add(s, f)
|
|
||||||
of pcDir: walkDirRecursively(s, f, ext)
|
|
||||||
of pcLinkToDir: discard
|
|
||||||
|
|
||||||
proc addFiles(s: var seq[string], dir, ext: string, patterns: seq[string]) =
|
|
||||||
for p in items(patterns):
|
|
||||||
if fileExists(dir / addFileExt(p, ext)):
|
|
||||||
s.add(dir / addFileExt(p, ext))
|
|
||||||
if dirExists(dir / p):
|
|
||||||
walkDirRecursively(s, dir / p, ext)
|
|
||||||
|
|
||||||
proc parseIniFile(c: var TConfigData) =
|
|
||||||
var
|
|
||||||
p: CfgParser
|
|
||||||
section: string # current section
|
|
||||||
var input = newFileStream(c.infile, fmRead)
|
|
||||||
if input == nil: quit("cannot open: " & c.infile)
|
|
||||||
open(p, input, c.infile)
|
|
||||||
while true:
|
|
||||||
var k = next(p)
|
|
||||||
case k.kind
|
|
||||||
of cfgEof: break
|
|
||||||
of cfgSectionStart:
|
|
||||||
section = normalize(k.section)
|
|
||||||
case section
|
|
||||||
of "project", "links", "tabs", "ticker", "documentation", "var": discard
|
|
||||||
else: echo("[Warning] Skipping unknown section: " & section)
|
|
||||||
|
|
||||||
of cfgKeyValuePair:
|
|
||||||
var v = k.value % c.vars
|
|
||||||
c.vars[k.key] = v
|
|
||||||
|
|
||||||
case section
|
|
||||||
of "project":
|
|
||||||
case normalize(k.key)
|
|
||||||
of "name": c.projectName = v
|
|
||||||
of "title": c.projectTitle = v
|
|
||||||
of "logo": c.logo = v
|
|
||||||
of "authors": c.authors = v
|
|
||||||
else: quit(errorStr(p, "unknown variable: " & k.key))
|
|
||||||
of "var": discard
|
|
||||||
of "links":
|
|
||||||
let valID = v.split(';')
|
|
||||||
add(c.links, (k.key.replace('_', ' '), valID[1], valID[0]))
|
|
||||||
of "tabs": add(c.tabs, (k.key, "", v))
|
|
||||||
of "ticker": c.ticker = v
|
|
||||||
of "documentation":
|
|
||||||
case normalize(k.key)
|
|
||||||
of "doc": addFiles(c.doc, "doc", ".rst", split(v, {';'}))
|
|
||||||
of "pdf": addFiles(c.pdf, "doc", ".rst", split(v, {';'}))
|
|
||||||
of "srcdoc": addFiles(c.srcdoc, "lib", ".nim", split(v, {';'}))
|
|
||||||
of "srcdoc2": addFiles(c.srcdoc2, "lib", ".nim", split(v, {';'}))
|
|
||||||
of "webdoc": addFiles(c.webdoc, "lib", ".nim", split(v, {';'}))
|
|
||||||
of "parallelbuild":
|
|
||||||
try:
|
|
||||||
let num = parseInt(v)
|
|
||||||
if num != 0: c.numProcessors = num
|
|
||||||
except ValueError:
|
|
||||||
quit("invalid numeric value for --parallelBuild in config")
|
|
||||||
else: quit(errorStr(p, "unknown variable: " & k.key))
|
|
||||||
of "quotations":
|
|
||||||
let vSplit = v.split('-')
|
|
||||||
doAssert vSplit.len == 2
|
|
||||||
c.quotations[k.key.normalize] = (vSplit[0], vSplit[1])
|
|
||||||
else: discard
|
|
||||||
of cfgOption: quit(errorStr(p, "syntax error"))
|
|
||||||
of cfgError: quit(errorStr(p, k.msg))
|
|
||||||
close(p)
|
|
||||||
if c.projectName.len == 0:
|
|
||||||
c.projectName = changeFileExt(extractFilename(c.infile), "")
|
|
||||||
|
|
||||||
# ------------------- main ----------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
proc exe(f: string): string = return addFileExt(f, ExeExt)
|
|
||||||
|
|
||||||
proc findNim(c: TConfigData): string =
|
|
||||||
if c.nimCompiler.len > 0: return c.nimCompiler
|
|
||||||
var nim = "nim".exe
|
|
||||||
result = "bin" / nim
|
|
||||||
if fileExists(result): return
|
|
||||||
for dir in split(getEnv("PATH"), PathSep):
|
|
||||||
if fileExists(dir / nim): return dir / nim
|
|
||||||
# assume there is a symlink to the exe or something:
|
|
||||||
return nim
|
|
||||||
|
|
||||||
proc exec(cmd: string) =
|
|
||||||
echo(cmd)
|
|
||||||
let (outp, exitCode) = osproc.execCmdEx(cmd)
|
|
||||||
if exitCode != 0: quit outp
|
|
||||||
|
|
||||||
proc sexec(cmds: openarray[string]) =
|
|
||||||
## Serial queue wrapper around exec.
|
|
||||||
for cmd in cmds: exec(cmd)
|
|
||||||
|
|
||||||
proc mexec(cmds: openarray[string], processors: int) =
|
|
||||||
## Multiprocessor version of exec
|
|
||||||
doAssert processors > 0, "nimweb needs at least one processor"
|
|
||||||
if processors == 1:
|
|
||||||
sexec(cmds)
|
|
||||||
return
|
|
||||||
let r = execProcesses(cmds, {poStdErrToStdOut, poParentStreams, poEchoCmd},
|
|
||||||
n = processors)
|
|
||||||
if r != 0:
|
|
||||||
echo "external program failed, retrying serial work queue for logs!"
|
|
||||||
sexec(cmds)
|
|
||||||
|
|
||||||
proc buildDocSamples(c: var TConfigData, destPath: string) =
|
|
||||||
## Special case documentation sample proc.
|
|
||||||
##
|
|
||||||
## TODO: consider integrating into the existing generic documentation builders
|
|
||||||
## now that we have a single `doc` command.
|
|
||||||
exec(findNim(c) & " doc $# -o:$# $#" %
|
|
||||||
[c.nimArgs, destPath / "docgen_sample.html", "doc" / "docgen_sample.nim"])
|
|
||||||
|
|
||||||
proc pathPart(d: string): string = splitFile(d).dir.replace('\\', '/')
|
|
||||||
|
|
||||||
proc buildDoc(c: var TConfigData, destPath: string) =
|
|
||||||
# call nim for the documentation:
|
|
||||||
var
|
|
||||||
commands = newSeq[string](len(c.doc) + len(c.srcdoc) + len(c.srcdoc2))
|
|
||||||
i = 0
|
|
||||||
for d in items(c.doc):
|
|
||||||
commands[i] = findNim(c) & " rst2html $# --git.url:$# -o:$# --index:on $#" %
|
|
||||||
[c.nimArgs, c.gitURL,
|
|
||||||
destPath / changeFileExt(splitFile(d).name, "html"), d]
|
|
||||||
i.inc
|
|
||||||
for d in items(c.srcdoc):
|
|
||||||
commands[i] = findNim(c) & " doc0 $# --git.url:$# -o:$# --index:on $#" %
|
|
||||||
[c.nimArgs, c.gitURL,
|
|
||||||
destPath / changeFileExt(splitFile(d).name, "html"), d]
|
|
||||||
i.inc
|
|
||||||
for d in items(c.srcdoc2):
|
|
||||||
commands[i] = findNim(c) & " doc $# --git.url:$# -o:$# --index:on $#" %
|
|
||||||
[c.nimArgs, c.gitURL,
|
|
||||||
destPath / changeFileExt(splitFile(d).name, "html"), d]
|
|
||||||
i.inc
|
|
||||||
|
|
||||||
mexec(commands, c.numProcessors)
|
|
||||||
exec(findNim(c) & " buildIndex -o:$1/theindex.html $1" % [destPath])
|
|
||||||
|
|
||||||
proc buildPdfDoc(c: var TConfigData, destPath: string) =
|
|
||||||
createDir(destPath)
|
|
||||||
if os.execShellCmd("pdflatex -version") != 0:
|
|
||||||
echo "pdflatex not found; no PDF documentation generated"
|
|
||||||
else:
|
|
||||||
const pdflatexcmd = "pdflatex -interaction=nonstopmode "
|
|
||||||
for d in items(c.pdf):
|
|
||||||
exec(findNim(c) & " rst2tex $# $#" % [c.nimArgs, d])
|
|
||||||
# call LaTeX twice to get cross references right:
|
|
||||||
exec(pdflatexcmd & changeFileExt(d, "tex"))
|
|
||||||
exec(pdflatexcmd & changeFileExt(d, "tex"))
|
|
||||||
# delete all the crappy temporary files:
|
|
||||||
let pdf = splitFile(d).name & ".pdf"
|
|
||||||
let dest = destPath / pdf
|
|
||||||
removeFile(dest)
|
|
||||||
moveFile(dest=dest, source=pdf)
|
|
||||||
removeFile(changeFileExt(pdf, "aux"))
|
|
||||||
if fileExists(changeFileExt(pdf, "toc")):
|
|
||||||
removeFile(changeFileExt(pdf, "toc"))
|
|
||||||
removeFile(changeFileExt(pdf, "log"))
|
|
||||||
removeFile(changeFileExt(pdf, "out"))
|
|
||||||
removeFile(changeFileExt(d, "tex"))
|
|
||||||
|
|
||||||
proc buildAddDoc(c: var TConfigData, destPath: string) =
|
|
||||||
# build additional documentation (without the index):
|
|
||||||
var commands = newSeq[string](c.webdoc.len)
|
|
||||||
for i, doc in pairs(c.webdoc):
|
|
||||||
commands[i] = findNim(c) & " doc $# --git.url:$# -o:$# $#" %
|
|
||||||
[c.nimArgs, c.gitURL,
|
|
||||||
destPath / changeFileExt(splitFile(doc).name, "html"), doc]
|
|
||||||
mexec(commands, c.numProcessors)
|
|
||||||
|
|
||||||
proc parseNewsTitles(inputFilename: string): seq[TRssItem] =
|
|
||||||
# Goes through each news file, returns its date/title.
|
|
||||||
result = @[]
|
|
||||||
var matches: array[3, string]
|
|
||||||
let reYearMonthDay = re(rYearMonthDay)
|
|
||||||
for kind, path in walkDir(inputFilename):
|
|
||||||
let (dir, name, ext) = path.splitFile
|
|
||||||
if ext == ".rst":
|
|
||||||
let content = readFile(path)
|
|
||||||
let title = content.splitLines()[0]
|
|
||||||
let urlPath = "news/" & name & ".html"
|
|
||||||
if content.find(reYearMonthDay, matches) >= 0:
|
|
||||||
result.add(TRssItem(year: matches[2], month: matches[1], day: matches[0],
|
|
||||||
title: title, url: "http://nim-lang.org/" & urlPath,
|
|
||||||
content: content))
|
|
||||||
result.reverse()
|
|
||||||
|
|
||||||
proc genUUID(text: string): string =
|
|
||||||
# Returns a valid RSS uuid, which is basically md5 with dashes and a prefix.
|
|
||||||
result = getMD5(text)
|
|
||||||
result.insert("-", 20)
|
|
||||||
result.insert("-", 16)
|
|
||||||
result.insert("-", 12)
|
|
||||||
result.insert("-", 8)
|
|
||||||
result.insert("urn:uuid:")
|
|
||||||
|
|
||||||
proc genNewsLink(title: string): string =
|
|
||||||
# Mangles a title string into an expected news.html anchor.
|
|
||||||
result = title
|
|
||||||
result.insert("Z")
|
|
||||||
for i in 1..len(result)-1:
|
|
||||||
let letter = result[i].toLowerAscii()
|
|
||||||
if letter in validAnchorCharacters:
|
|
||||||
result[i] = letter
|
|
||||||
else:
|
|
||||||
result[i] = '-'
|
|
||||||
result.insert(rssNewsUrl & "#")
|
|
||||||
|
|
||||||
proc generateRss(outputFilename: string, news: seq[TRssItem]) =
|
|
||||||
# Given a list of rss items generates an rss overwriting destination.
|
|
||||||
var
|
|
||||||
output: File
|
|
||||||
|
|
||||||
if not open(output, outputFilename, mode = fmWrite):
|
|
||||||
quit("Could not write to $1 for rss generation" % [outputFilename])
|
|
||||||
defer: output.close()
|
|
||||||
|
|
||||||
output.write("""<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
|
||||||
""")
|
|
||||||
output.write(title("Nim website news"))
|
|
||||||
output.write(link(href = rssUrl, rel = "self"))
|
|
||||||
output.write(link(href = rssNewsUrl))
|
|
||||||
output.write(id(rssNewsUrl))
|
|
||||||
|
|
||||||
let now = utc(getTime())
|
|
||||||
output.write(updatedDate($now.year, $(int(now.month) + 1), $now.monthday))
|
|
||||||
|
|
||||||
for rss in news:
|
|
||||||
output.write(entry(
|
|
||||||
title(xmltree.escape(rss.title)),
|
|
||||||
id(genUUID(rss.title)),
|
|
||||||
link(`type` = "text/html", rel = "alternate",
|
|
||||||
href = rss.url),
|
|
||||||
updatedDate(rss.year, rss.month, rss.day),
|
|
||||||
"<author><name>Nim</name></author>",
|
|
||||||
content(xmltree.escape(rss.content), `type` = "text")
|
|
||||||
))
|
|
||||||
|
|
||||||
output.write("""</feed>""")
|
|
||||||
|
|
||||||
proc buildNewsRss(c: var TConfigData, destPath: string) =
|
|
||||||
# generates an xml feed from the web/news.rst file
|
|
||||||
let
|
|
||||||
srcFilename = "web" / "news"
|
|
||||||
destFilename = destPath / changeFileExt(splitFile(srcFilename).name, "xml")
|
|
||||||
|
|
||||||
generateRss(destFilename, parseNewsTitles(srcFilename))
|
|
||||||
|
|
||||||
proc readSponsors(sponsorsFile: string): seq[Sponsor] =
|
|
||||||
result = @[]
|
|
||||||
var fileStream = newFileStream(sponsorsFile, fmRead)
|
|
||||||
if fileStream == nil: quit("Cannot open sponsors.csv file: " & sponsorsFile)
|
|
||||||
var parser: CsvParser
|
|
||||||
open(parser, fileStream, sponsorsFile)
|
|
||||||
discard readRow(parser) # Skip the header row.
|
|
||||||
while readRow(parser):
|
|
||||||
result.add(Sponsor(logo: parser.row[0], name: parser.row[1],
|
|
||||||
url: parser.row[2], thisMonth: parser.row[3].parseInt,
|
|
||||||
allTime: parser.row[4].parseInt,
|
|
||||||
since: parser.row[5], level: parser.row[6].parseInt))
|
|
||||||
parser.close()
|
|
||||||
|
|
||||||
proc buildSponsors(c: var TConfigData, outputDir: string) =
|
|
||||||
let sponsors = generateSponsorsPage(readSponsors(activeSponsors),
|
|
||||||
readSponsors(inactiveSponsors))
|
|
||||||
let outFile = outputDir / "sponsors.html"
|
|
||||||
var f: File
|
|
||||||
if open(f, outFile, fmWrite):
|
|
||||||
writeLine(f, generateHtmlPage(c, "", "Our Sponsors", sponsors, ""))
|
|
||||||
close(f)
|
|
||||||
else:
|
|
||||||
quit("[Error] Cannot write file: " & outFile)
|
|
||||||
|
|
||||||
const
|
|
||||||
cmdRst2Html = " rst2html --compileonly $1 -o:web/$2.temp web/$2.rst"
|
|
||||||
|
|
||||||
proc buildPage(c: var TConfigData, file, title, rss: string, assetDir = "") =
|
|
||||||
exec(findNim(c) & cmdRst2Html % [c.nimArgs, file])
|
|
||||||
var temp = "web" / changeFileExt(file, "temp")
|
|
||||||
var content: string
|
|
||||||
try:
|
|
||||||
content = readFile(temp)
|
|
||||||
except IOError:
|
|
||||||
quit("[Error] cannot open: " & temp)
|
|
||||||
var f: File
|
|
||||||
var outfile = c.webUploadOutput / "$#.html" % file
|
|
||||||
if not dirExists(outfile.splitFile.dir):
|
|
||||||
createDir(outfile.splitFile.dir)
|
|
||||||
if open(f, outfile, fmWrite):
|
|
||||||
writeLine(f, generateHTMLPage(c, file, title, content, rss, assetDir))
|
|
||||||
close(f)
|
|
||||||
else:
|
|
||||||
quit("[Error] cannot write file: " & outfile)
|
|
||||||
removeFile(temp)
|
|
||||||
|
|
||||||
proc buildNews(c: var TConfigData, newsDir: string, outputDir: string) =
|
|
||||||
for kind, path in walkDir(newsDir):
|
|
||||||
let (dir, name, ext) = path.splitFile
|
|
||||||
if ext == ".rst":
|
|
||||||
let title = readFile(path).splitLines()[0]
|
|
||||||
buildPage(c, tailDir(dir) / name, title, "", "../")
|
|
||||||
else:
|
|
||||||
echo("Skipping file in news directory: ", path)
|
|
||||||
|
|
||||||
proc buildWebsite(c: var TConfigData) =
|
|
||||||
if c.ticker.len > 0:
|
|
||||||
try:
|
|
||||||
c.ticker = readFile("web" / c.ticker)
|
|
||||||
except IOError:
|
|
||||||
quit("[Error] cannot open: " & c.ticker)
|
|
||||||
for i in 0..c.tabs.len-1:
|
|
||||||
var file = c.tabs[i].val
|
|
||||||
let rss = if file in ["news", "index"]: extractFilename(rssUrl) else: ""
|
|
||||||
if '.' in file: continue
|
|
||||||
buildPage(c, file, if file == "question": "FAQ" else: file, rss)
|
|
||||||
copyDir("web/assets", c.webUploadOutput / "assets")
|
|
||||||
buildNewsRss(c, c.webUploadOutput)
|
|
||||||
buildSponsors(c, c.webUploadOutput)
|
|
||||||
buildNews(c, "web/news", c.webUploadOutput / "news")
|
|
||||||
|
|
||||||
proc onlyDocs(c: var TConfigData) =
|
|
||||||
createDir(c.docHTMLOutput)
|
|
||||||
buildDocSamples(c, c.docHTMLOutput)
|
|
||||||
buildDoc(c, c.docHTMLOutput)
|
|
||||||
|
|
||||||
proc main(c: var TConfigData) =
|
|
||||||
buildWebsite(c)
|
|
||||||
let docup = c.webUploadOutput / NimVersion
|
|
||||||
createDir(docup)
|
|
||||||
buildAddDoc(c, docup)
|
|
||||||
buildDocSamples(c, docup)
|
|
||||||
buildDoc(c, docup)
|
|
||||||
onlyDocs(c)
|
|
||||||
|
|
||||||
proc json2(c: var TConfigData) =
|
|
||||||
const destPath = "web/json2"
|
|
||||||
var commands = newSeq[string](c.srcdoc2.len)
|
|
||||||
var i = 0
|
|
||||||
for d in items(c.srcdoc2):
|
|
||||||
createDir(destPath / splitFile(d).dir)
|
|
||||||
commands[i] = findNim(c) & " jsondoc $# --git.url:$# -o:$# --index:on $#" %
|
|
||||||
[c.nimArgs, c.gitURL,
|
|
||||||
destPath / changeFileExt(d, "json"), d]
|
|
||||||
i.inc
|
|
||||||
|
|
||||||
mexec(commands, c.numProcessors)
|
|
||||||
|
|
||||||
var c: TConfigData
|
|
||||||
initConfigData(c)
|
|
||||||
parseCmdLine(c)
|
|
||||||
parseIniFile(c)
|
|
||||||
case action
|
|
||||||
of actOnlyWebsite: buildWebsite(c)
|
|
||||||
of actPdf: buildPdfDoc(c, "doc/pdf")
|
|
||||||
of actOnlyDocs: onlyDocs(c)
|
|
||||||
of actAll: main(c)
|
|
||||||
of actJson2: json2(c)
|
|
||||||
|
|
@ -1,266 +0,0 @@
|
||||||
#? stdtmpl | standard
|
|
||||||
#proc generateHTMLPage(c: var TConfigData, currentTab, title, content, rss,
|
|
||||||
# rootDir = ""): string =
|
|
||||||
# result = ""
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
|
||||||
<title>${title} - $c.projectTitle</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="${rootDir}assets/style.css?t=2320" />
|
|
||||||
<link rel="shortcut icon" href="${rootDir}assets/images/favicon.ico">
|
|
||||||
#if len(rss) > 0:
|
|
||||||
<link href="$rss" title="Recent changes" type="application/atom+xml" rel="alternate">
|
|
||||||
#end if
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="bountysource">
|
|
||||||
<a href="https://salt.bountysource.com/teams/nim">
|
|
||||||
<div class="page-layout" style="padding: 2pt 2pt 2pt 30pt">
|
|
||||||
<img src="${rootDir}assets/bountysource/bountysource.png" style="width: 20px; float: left;">
|
|
||||||
<span style="margin-left: 10pt; float: left; margin-top: 2pt;">Fund Nim and help us develop it further!</span>
|
|
||||||
<img src="https://api.bountysource.com/badge/team?team_id=19072&style=raised" style="margin-top: 2pt; margin-left: 10pt"/>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<header id="head">
|
|
||||||
<div class="page-layout tall">
|
|
||||||
<div id="head-logo"></div>
|
|
||||||
<a id="head-logo-link" href="${rootDir}index.html"></a>
|
|
||||||
<nav id="head-links">
|
|
||||||
#for i in 0.. c.tabs.len-1:
|
|
||||||
# let t = c.tabs[i].val
|
|
||||||
# if t != "index" and t != "community" and t != "news":
|
|
||||||
# let name = c.tabs[i].key
|
|
||||||
# if currentTab == t:
|
|
||||||
<a class="active"
|
|
||||||
# else:
|
|
||||||
<a
|
|
||||||
# end if
|
|
||||||
# if t.contains('.'):
|
|
||||||
href="${t}" title = "$c.projectName - $name">$name</a>
|
|
||||||
# else:
|
|
||||||
href="${rootDir}${t}.html" title = "$c.projectName - $name">$name</a>
|
|
||||||
# end if
|
|
||||||
# end if
|
|
||||||
#end for
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
# if currentTab == "index":
|
|
||||||
<section id="neck" class="home">
|
|
||||||
# else:
|
|
||||||
<section id="neck">
|
|
||||||
# end
|
|
||||||
<div class="page-layout tall">
|
|
||||||
<div id="glow-arrow"></div>
|
|
||||||
|
|
||||||
# if currentTab == "index":
|
|
||||||
<div id="slideshow">
|
|
||||||
<!-- slides -->
|
|
||||||
<div id="slide0" class="active codeslide2">
|
|
||||||
<div>
|
|
||||||
<h2>Nim is simple..</h2>
|
|
||||||
<pre>
|
|
||||||
<span class="cmt"># compute average line length</span>
|
|
||||||
<span class="kwd">var</span>
|
|
||||||
<span class="tab"> </span>sum = <span class="val">0</span>
|
|
||||||
<span class="tab end"> </span>count = <span class="val">0</span>
|
|
||||||
|
|
||||||
<span class="kwd">for</span> line <span class="kwd">in</span> stdin.lines:
|
|
||||||
<span class="tab"> </span>sum += line.len
|
|
||||||
<span class="tab end"> </span>count += <span class="val">1</span>
|
|
||||||
|
|
||||||
echo(<span class="val">"Average line length: "</span>,
|
|
||||||
<span class="kwd">if</span> count > <span class="val">0</span>: sum / count <span class="kwd">else</span>: <span class="val">0</span>)
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h2>..and type safe...</h2>
|
|
||||||
<pre>
|
|
||||||
<span class="cmt"># create and greet someone</span>
|
|
||||||
<span class="kwd">type</span> <span class="def">Person</span> = <span class="kwd">object</span>
|
|
||||||
<span class="tab"> </span>name: <span class="typ">string</span>
|
|
||||||
<span class="tab end"> </span>age: <span class="typ">int</span>
|
|
||||||
|
|
||||||
<span class="kwd">proc</span> <span class="def">greet</span>(p: <span class="typ">Person</span>) =
|
|
||||||
<span class="tab"> </span>echo <span class="val">"Hi, I'm "</span>, p.name, <span class="val">"."</span>
|
|
||||||
<span class="tab end"> </span>echo <span class="val">"I am "</span>, p.age, <span class="val">" years old."</span>
|
|
||||||
|
|
||||||
<span class="kwd">let</span> p = <span class="typ">Person</span>(name:<span class="val">"Jon"</span>, age:<span class="val">18</span>)
|
|
||||||
p.greet() <span class="cmt"># or greet(p)</span>
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="slide1" class="codeslide3">
|
|
||||||
<div>
|
|
||||||
<h2>C FFI is easy in Nim..</h2>
|
|
||||||
<pre>
|
|
||||||
<span class="cmt"># declare a C procedure..</span>
|
|
||||||
<span class="kwd">proc</span> <span class="def">unsafeScanf</span>(f: <span class="typ">File</span>, s: <span class="typ">cstring</span>)
|
|
||||||
<span class="tab"> </span>{.varargs,
|
|
||||||
<span class="tab"> </span>importc: <span class="val">"fscanf"</span>,
|
|
||||||
<span class="tab end"> </span>header: <span class="val">"<stdio.h>"</span>.}
|
|
||||||
|
|
||||||
<span class="cmt"># ..and use it...</span>
|
|
||||||
<span class="kwd">var</span> x: <span class="typ">cint</span>
|
|
||||||
stdin.unsafeScanf(<span class="val">"%d"</span>, <span class="kwd">addr</span> x)
|
|
||||||
</pre>
|
|
||||||
<p><span class="desc"><b>Compile and run with:</b><br> $ nim c -r example.nim</span></p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h2>..and DSLs are too...</h2>
|
|
||||||
<pre>
|
|
||||||
<span class="cmt"># a simple html server</span>
|
|
||||||
<span class="kwd">import</span>
|
|
||||||
jester, asyncdispatch, htmlgen
|
|
||||||
|
|
||||||
<span class="kwd">routes</span>:
|
|
||||||
<span class="tab"> </span><span class="kwd">get</span> <span class="val">"/"</span>:
|
|
||||||
<span class="tab end"> <span class="tab end"> </span></span><span class="kwd">resp</span> h1(<span class="val">"Hello world"</span>)
|
|
||||||
|
|
||||||
runForever()
|
|
||||||
</pre>
|
|
||||||
<p><span class="desc"><b>View in browser at:</b><br> localhost:5000</span></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="slide2" class="niaslide">
|
|
||||||
<a href="news/e030_nim_in_action_in_production.html">
|
|
||||||
<img src="${rootDir}assets/niminaction/banner2.png" alt="A printed copy of Nim in Action should be available in March 2017!"/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div id="slide3" class="niaslide">
|
|
||||||
<a href="sponsors.html">
|
|
||||||
<img src="${rootDir}assets/bountysource/meet_sponsors.png" alt="Meet our BountySource sponsors!"/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="slideshow-nav">
|
|
||||||
<div id="slideControl0" onclick="slideshow_click(0)" class="active"></div>
|
|
||||||
<div id="slideControl1" onclick="slideshow_click(1)"></div>
|
|
||||||
<div id="slideControl2" onclick="slideshow_click(2)"></div>
|
|
||||||
<div id="slideControl3" onclick="slideshow_click(3)"></div>
|
|
||||||
</div>
|
|
||||||
# end
|
|
||||||
<aside id="sidebar">
|
|
||||||
|
|
||||||
# if len(c.links) > 0:
|
|
||||||
<h3>More Links</h3>
|
|
||||||
<div id="sidebar-links">
|
|
||||||
# for i in 0..c.links.len-1:
|
|
||||||
<a href="${c.links[i].val}" id="${c.links[i].id}">${c.links[i].key}</a>
|
|
||||||
# end for
|
|
||||||
</div>
|
|
||||||
# end if
|
|
||||||
# if len(c.ticker) > 0:
|
|
||||||
<h3 class="blue">Latest News</h3>
|
|
||||||
<div id="sidebar-news">
|
|
||||||
${c.ticker % rootDir}
|
|
||||||
</div>
|
|
||||||
# end if
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="body">
|
|
||||||
<div id="body-border"></div>
|
|
||||||
<div id="glow-line"></div>
|
|
||||||
<div class="page-layout">
|
|
||||||
<article id="content" class="page">
|
|
||||||
$content
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!--- #foot --->
|
|
||||||
<footer id="foot" class="home">
|
|
||||||
<div class="page-layout tall">
|
|
||||||
<div id="foot-links">
|
|
||||||
<div>
|
|
||||||
<h4>Documentation</h4>
|
|
||||||
<a href="${rootDir}documentation.html">Stable Documentation</a>
|
|
||||||
<a href="${rootDir}learn.html">Learning Resources</a>
|
|
||||||
<!-- <a href="">Development Documentation</a> -->
|
|
||||||
<a href="https://github.com/nim-lang/nim">Issues & Requests</a>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h4>Community</h4>
|
|
||||||
<a href="https://forum.nim-lang.org">User Forum</a>
|
|
||||||
<a href="https://web.libera.chat/#nim">Online IRC</a>
|
|
||||||
<a href="https://irclogs.nim-lang.org/">IRC Logs</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="foot-legal">
|
|
||||||
<h4>Written in Nim - Powered by <a href="https://github.com/dom96/jester">Jester</a></h4>
|
|
||||||
Web Design by <a href="http://reign-studios.net/philipwitte/">Philip Witte</a> & <a href="http://picheta.me/">Dominik Picheta</a><br>
|
|
||||||
Copyright © 2017 - <a href="https://nim-lang.org/blog/">Andreas Rumpf</a> & <a href="https://github.com/nim-lang/nim/graphs/contributors">Contributors</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
# if currentTab == "index":
|
|
||||||
<script src="${rootDir}assets/index.js"></script>
|
|
||||||
# end if
|
|
||||||
# if c.gaId.len != 0:
|
|
||||||
<script>
|
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
||||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
||||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
|
||||||
|
|
||||||
ga('create', '${c.gaId}', 'nim-lang.org');
|
|
||||||
ga('send', 'pageview');
|
|
||||||
</script>
|
|
||||||
# end if
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
#end proc
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#proc generateSponsors(sponsors: seq[Sponsor]): string =
|
|
||||||
#result = ""
|
|
||||||
#for sponsor in sponsors:
|
|
||||||
<dt class="level-${sponsor.level}">
|
|
||||||
#if sponsor.url.len > 0:
|
|
||||||
<a href="${sponsor.url}" target="_blank">${sponsor.name}</a>
|
|
||||||
#else:
|
|
||||||
${sponsor.name}
|
|
||||||
#end if
|
|
||||||
</dt>
|
|
||||||
<dd class="logo">
|
|
||||||
#if sponsor.logo.len > 0:
|
|
||||||
<a href="${sponsor.url}" target="_blank">
|
|
||||||
<img alt="${sponsor.name}'s logo" src="${sponsor.logo}"/>
|
|
||||||
</a>
|
|
||||||
#end if
|
|
||||||
</dd>
|
|
||||||
<dd class="this_month">
|
|
||||||
Donated <b>$$${sponsor.thisMonth}</b> this month
|
|
||||||
</dd>
|
|
||||||
<dd class="legend">
|
|
||||||
Donated $$${sponsor.allTime} in total since ${sponsor.since}
|
|
||||||
</dd>
|
|
||||||
#end for
|
|
||||||
#end proc
|
|
||||||
#proc generateSponsorsPage(activeSponsors, inactiveSponsors: seq[Sponsor]): string =
|
|
||||||
#result = ""
|
|
||||||
<h1 id="our-current-sponsors">Our Current Sponsors</h1>
|
|
||||||
<p>This section lists the companies and individuals that are, very kindly, contributing a
|
|
||||||
monthly amount to help sustain Nim's development. For more details take a
|
|
||||||
look at the <a href="https://salt.bountysource.com/teams/nim">Bountysource campaign</a>.</p>
|
|
||||||
<p class="lastUpdate">Last updated: ${getTime().utc().format("dd/MM/yyyy")}</p>
|
|
||||||
<dl>
|
|
||||||
${generateSponsors(activeSponsors)}
|
|
||||||
</dl>
|
|
||||||
#
|
|
||||||
<h1 id="our-past-sponsors">Our Past Sponsors</h1>
|
|
||||||
<p>This section lists the companies and individuals that have contributed
|
|
||||||
money in the past to help sustain Nim's development. For more details take a
|
|
||||||
look at the <a href="https://salt.bountysource.com/teams/nim">Bountysource campaign</a>.</p>
|
|
||||||
<dl>
|
|
||||||
${generateSponsors(inactiveSponsors)}
|
|
||||||
</dl>
|
|
||||||
#
|
|
||||||
#end proc
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue