diff options
author | 1138-4EB <1138-4EB@users.noreply.github.com> | 2019-02-23 23:03:41 +0100 |
---|---|---|
committer | tgingold <tgingold@users.noreply.github.com> | 2019-02-25 07:28:45 +0100 |
commit | fb6fa7ceced6e74ae02ed24b92a488c6e65d3404 (patch) | |
tree | e868b6cb14074b81618d148d9f58077fb308924d | |
parent | 5040ad31d9e7ac3f7d459e388b734a917ce412f2 (diff) | |
download | ghdl-fb6fa7ceced6e74ae02ed24b92a488c6e65d3404.tar.gz ghdl-fb6fa7ceced6e74ae02ed24b92a488c6e65d3404.tar.bz2 ghdl-fb6fa7ceced6e74ae02ed24b92a488c6e65d3404.zip |
doc: add custom directive 'exec', generate releases shields and tables
with json data retrieved from github api"
-rw-r--r-- | doc/build.sh | 27 | ||||
-rw-r--r-- | doc/conf.py | 8 | ||||
-rw-r--r-- | doc/exec.py | 36 | ||||
-rw-r--r-- | doc/getting/Releases.rst | 96 | ||||
-rw-r--r-- | doc/helpers.py | 216 | ||||
-rw-r--r-- | doc/requirements.txt | 1 |
6 files changed, 330 insertions, 54 deletions
diff --git a/doc/build.sh b/doc/build.sh new file mode 100644 index 000000000..98d9039b9 --- /dev/null +++ b/doc/build.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +set -e + +cd $(dirname $0)/.. + +$(command -v winpty) docker run --rm -t \ + -v /$(pwd)://src \ + -w //src/doc \ + btdi/sphinx:py3-featured sh -c "$(cat <<-EOF +pip install -r requirements.txt +sphinx-build -T -b html -D language=en . _build/html +sphinx-build -T -b latex -D language=en . _build/latex +EOF +)" + +$(command -v winpty) docker run --rm -t \ + -v /$(pwd)://src \ + -w //src/doc \ + btdi/latex sh -c "$(cat <<-EOF +cd _build/latex +FILE=\"\`ls *.tex | sed -e 's/\.tex//'\`\" +pdflatex -interaction=nonstopmode \$FILE.tex; +makeindex -s python.ist \$FILE.idx; +pdflatex -interaction=nonstopmode \$FILE.tex; +EOF +)" diff --git a/doc/conf.py b/doc/conf.py index 31b20c948..efa30f23e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -12,11 +12,7 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys -import os -import shlex -import re -import subprocess +import sys, os, shlex, re, subprocess # http://docs.readthedocs.io/en/latest/getting_started.html#in-markdown from recommonmark.parser import CommonMarkParser @@ -51,6 +47,8 @@ extensions = [ # SphinxContrib extensions # 'sphinxcontrib.textstyle', # 'sphinxcontrib.spelling', +# Other + 'exec', ] # Add any paths that contain templates here, relative to this directory. diff --git a/doc/exec.py b/doc/exec.py new file mode 100644 index 000000000..f6ad8a8e8 --- /dev/null +++ b/doc/exec.py @@ -0,0 +1,36 @@ +# https://stackoverflow.com/questions/7250659/how-to-use-python-to-programmatically-generate-part-of-sphinx-documentation/18143318 + +import sys +from os.path import basename + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +from docutils.parsers.rst import Directive +from docutils import nodes, statemachine + +class ExecDirective(Directive): + """Execute the specified python code and insert the output into the document""" + has_content = True + + def run(self): + oldStdout, sys.stdout = sys.stdout, StringIO() + + tab_width = self.options.get('tab-width', self.state.document.settings.tab_width) + source = self.state_machine.input_lines.source(self.lineno - self.state_machine.input_offset - 1) + + try: + exec('\n'.join(self.content)) + text = sys.stdout.getvalue() + lines = statemachine.string2lines(text, tab_width, convert_whitespace=True) + self.state_machine.insert_input(lines, source) + return [] + except Exception: + return [nodes.error(None, nodes.paragraph(text = "Unable to execute python code at %s:%d:" % (basename(source), self.lineno)), nodes.paragraph(text = str(sys.exc_info()[1])))] + finally: + sys.stdout = oldStdout + +def setup(app): + app.add_directive('exec', ExecDirective) diff --git a/doc/getting/Releases.rst b/doc/getting/Releases.rst index 6b85decb5..81286a14a 100644 --- a/doc/getting/Releases.rst +++ b/doc/getting/Releases.rst @@ -11,61 +11,51 @@ Releases and sources Downloading pre-built packages ****************************** -.. |Strect-mcode-gpl| image:: https://img.shields.io/github/downloads/ghdl/ghdl/v0.35/ghdl-gpl-0.35-mcode.tgz.svg?longCache=true&style=flat-square&logo=github - :target: https://github.com/ghdl/ghdl/releases/download/v0.35/ghdl-gpl-0.35-mcode.tgz - :alt: ghdl-gpl-0.35-mcode.tgz -.. |Strect-mcode| image:: https://img.shields.io/github/downloads/ghdl/ghdl/v0.35/ghdl-0.35-mcode-stretch.tgz.svg?longCache=true&style=flat-square&logo=github - :target: https:// github.com/ghdl/ghdl/releases/download/v0.35/ghdl-0.35-mcode-stretch.tgz - :alt: ghdl-0.35-mcode-stretch.tgz -.. |Ubu1404-mcode| image:: https://img.shields.io/github/downloads/ghdl/ghdl/v0.35/ghdl-0.35-mcode-ubuntu14.tgz.svg?longCache=true&style=flat-square&logo=github - :target: https://github.com/ghdl/ghdl/releases/download/v0.35/ghdl-0.35-mcode-ubuntu14.tgz - :alt: ghdl-0.35-mcode-ubuntu14.tgz -.. |Ubu1404-llvm38| image:: https://img.shields.io/github/downloads/ghdl/ghdl/v0.35/ghdl-0.35-llvm-3.8-ubuntu14.tgz.svg?longCache=true&style=flat-square&logo=github - :target: https://github.com/ghdl/ghdl/releases/download/v0.35/ghdl-0.35-llvm-3.8-ubuntu14.tgz - :alt: ghdl-0.35-llvm-3.8-ubuntu14.tgz -.. |Fedora26-mcode| image:: https://img.shields.io/github/downloads/ghdl/ghdl/v0.35/ghdl-0.35-mcode-fedora26.tgz.svg?longCache=true&style=flat-square&logo=github - :target: https://github.com/ghdl/ghdl/releases/download/v0.35/ghdl-0.35-mcode-fedora26.tgz - :alt: ghdl-0.35-mcode-fedora26.tgz -.. |Win32-mcode| image:: https://img.shields.io/github/downloads/ghdl/ghdl/v0.35/ghdl-v0.35-mingw32-mcode.zip.svg?longCache=true&style=flat-square&logo=github - :target: https://github.com/ghdl/ghdl/releases/download/v0.35/ghdl-v0.35-mingw32-mcode.zip - :alt: ghdl-v0.35-mingw32-mcode.zip -.. |Win64-llvm| image:: https://img.shields.io/github/downloads/ghdl/ghdl/v0.35/ghdl-v0.35-mingw64-llvm.zip.svg?longCache=true&style=flat-square&logo=github - :target: https://github.com/ghdl/ghdl/releases/download/v0.35/ghdl-v0.35-mingw64-llvm.zip - :alt: ghdl-v0.35-mingw64-llvm.zip -.. |MacOS-mcode| image:: https://img.shields.io/github/downloads/ghdl/ghdl/v0.35/ghdl-0.35-mcode-macosx.tgz.svg?longCache=true&style=flat-square&logo=github - :target: https://github.com/ghdl/ghdl/releases/download/v0.35/ghdl-0.35-mcode-macosx.tgz - :alt: ghdl-0.35-mcode-macosx.tgz -.. |Sum| image:: https://img.shields.io/github/downloads/ghdl/ghdl/v0.35/total.svg?longCache=true&style=flat-square&logo=github - :target: https://github.com/ghdl/ghdl/releases/tag/v0.35 - :alt: Downloads of GHDL v0.35 - -+------------------------+----------+-----------+--------------------+ -| OS | Backend | Filesize | Downloads | -+========================+==========+===========+====================+ -| Debian 9 (Stretch) GPL | mcode | 2.61 MB | |Strect-mcode-gpl| | -+------------------------+----------+-----------+--------------------+ -| Debian 9 (Stretch) | mcode | 2.93 MB | |Strect-mcode| | -+------------------------+----------+-----------+--------------------+ -| Ubuntu 14 (Trusty) | mcode | 3.15 MB | |Ubu1404-mcode| | -+------------------------+----------+-----------+--------------------+ -| Ubuntu 14 (Trusty) | LLVM | 14.11MB | |Ubu1404-llvm38| | -+------------------------+----------+-----------+--------------------+ -| Fedora 26 | mcode | 2.83 MB | |Fedora26-mcode| | -+------------------------+----------+-----------+--------------------+ -| Windows x86 (MinGW32) | mcode | 3.05 MB | |Win32-mcode| | -+------------------------+----------+-----------+--------------------+ -| Windows x86 (MinGW64) | LLVM | 12.7 MB | |Win64-llvm| | -+------------------------+----------+-----------+--------------------+ -| Mac OS X | mcode | 2.26 MB | |MacOS-mcode| | -+------------------------+----------+-----------+--------------------+ -| Sum: | | | |Sum| | -+------------------------+----------+-----------+--------------------+ +.. TODO How to extend this directive to use `.. only:: html` and `.. only:: html` in the python code passed to it? + +.. exec:: + from helpers import createReleasesShields, printReleaseTab, printReleasesList + + # Optionally, provide a <tag> name to get the assets table of a specific release. By default, the table of 'latest' is returned. + data = createReleasesShields() + +.. only:: html + + .. exec:: + from helpers import printReleaseTab + printReleaseTab('data') + +.. only:: latex + + .. exec:: + from helpers import printReleaseTab + printReleaseTab('data', latex=True) + +Pre-built packages of older releases +==================================== + +.. only:: html + + .. exec:: + from helpers import printReleasesList + printReleasesList('data') + +.. only:: latex + + .. exec:: + from helpers import printReleasesList + printReleasesList('data', latex=True) .. _RELEASE:Sources: Downloading Source Files ************************ +.. HINT:: + + All the following procedures will retrieve the latest development version of GHDL, i.e., the `master` branch at `github.com/ghdl/ghdl <https://github.com/ghdl/ghdl>`_. + We do our best to keep it stable, but bugs can seldom be published. See `HINT` boxes below for instructions to get older releases. + .. _RELEASE:Sources:Zip: Downloading from GitHub @@ -92,6 +82,10 @@ choose your desired format/version: | master | |tgz-master| | +----------+------------------------+ +.. HINT:: + + To download a specific version of GHDL, use these alternative URL, where ``<format>`` is ``tar`` or ``zip``: ``https://api.github.com/repos/ghdl/ghdl/<format>ball/<tag>``. + .. _RELEASE:Sources:GitClone: Downloading via ``git clone`` @@ -113,6 +107,10 @@ later instructions or on other pages in this documentation. | SSH | ssh://git@github.com:ghdl/ghdl.git | +----------+----------------------------------------+ +.. HINT:: + + Execute ``git checkout -b stable <tag>`` after ``git clone``, to checkout a specific version of GHDL. + On Linux -------- diff --git a/doc/helpers.py b/doc/helpers.py new file mode 100644 index 000000000..dd028037e --- /dev/null +++ b/doc/helpers.py @@ -0,0 +1,216 @@ +# Try to load JSON data from a file. If not found, use the argument as a tag name and retrieve the data from GitHub. +def getJSON(tag='all'): + f = tag + tag = '/'+tag + if f == 'all': + f = 'releases' + tag = '' + + import json + try: + d = json.loads(open(f+'.json', 'r').read()) + except: + from urllib.request import urlopen + d = json.loads(urlopen('https://api.github.com/repos/ghdl/ghdl/releases'+tag).read()) + json.dump(d, open(f+'.json', 'w'), indent=4) + return d + +# +# Functions to print table with format `[ [], [], [], ... ]` to reStructuredText +# + +# Print a row of data elements. +def printTabRow(l, r): + printTabItem(l, r, '| ') + +# Print a rule. Two characters in 'b' define the type of rule. Expected values are '+-', '+=' or '| '. +def printTabRule(l, b): + printTabItem(l, [b[1] for x in range(len(l))], b) + +# Print a full row, be it a rule or data. +# Extend the width of each field to the size specified in 'l'. +def printTabItem(l, a, b): + for y, z in enumerate(a): + print((b + z).ljust(l[y]+3, b[1]), end='') + print(b[0]) + +# Get number of cols from number of elements in row 0. +# Compute minimum number of characters required for each col. +def getTabColLens(t): + cl = [0 for _ in t[0]] + for row in t: + for y, z in enumerate(row): + cl[y] = max(cl[y], len(z)) + return cl + +# Print a table using the functions above. +# The first row contains the headers. +def printTab(t): + clens = getTabColLens(t) + + printTabRule(clens, '+-') + printTabRow(clens, t[0]) + printTabRule(clens, '+=') + for x in t[1:]: + printTabRow(clens, x) + printTabRule(clens, '+-') + print() + +# +# Print two versions of each shield. Onee for 'html' (`image::`) and one for 'latex' (`replace::`) +# + +# Strip all non-alphanumeric characters when creating the labels +def stripLabel(label): + import re + pattern = re.compile('[\W_]+') + return pattern.sub('', label) + +def printShieldSrc(label, alt, img, target, latex=False): + if latex: + i = stripLabel(label) + if label[-6:] == '/total': + label = label[:-6] + print('.. |l' + i + '| replace:: `' + label + '`_') + print('.. _' + label + ': ' + target + '\n') + else: + print('.. |' + label + '| image:: '+ img + '\n', + ' :target: ' + target + '\n', + ' :height: 22\n', + ' :alt: ' + alt + '\n') + +# +# Display better OS and Backend names than those represented in the tarball name +# + +def prettyOS(i): + if i == 'fedora28': + return 'Fedora 28' + elif i == 'macosx': + return 'Max OS X' + elif i == 'mingw32': + return 'Windows x86 (MinGW32)' + elif i == 'mingw64': + return 'Windows x86 (MinGW64)' + elif i == 'stretch': + return 'Debian 9 (Stretch)' + elif i == 'gpl': + return 'Debian 9 (Stretch) GPL' + elif i == 'ubuntu14': + return '14.04 LTS (Trusty Tahr)' + return i + +def prettyBackend(i): + if i == 'llvm': + return 'LLVM' + if i == 'llvm-3.8': + return 'LLVM (3.8)' + return i + +# +# Get, extract and process JSON data to create the shields and table with the assets of a release +# + +def createTagShields(data='latest'): + if isinstance(data, str): + data = getJSON(data) + + assets=[['OS', 'Backend', 'Size', 'Downloads']] + tag = data['tag_name'] + for x in data['assets']: + name = x['name'] + s = [] + + p = 'ghdl-gpl-'+tag[1:] + if name[0:len(p)] == p: + s = ['gpl', 'mcode'] + + p = 'ghdl-'+tag[1:] + if name[0:len(p)] == p: + s = name[len(p)+1:-4].split('-',1) + + if len(s) > 1: + assets.append([ + prettyOS(s[0]), + prettyBackend(s[1]), + (str(round(x['size']/1024**2, 2))+' MB').rjust(8), + '|' + tag + '/' + name + '|'] + ) + + assets.append(['Sum:', '', '', '|'+tag+'/total|']) + + for x in assets[1:-1]: + i = x[3][1:-1] + for latex in [False, True]: + printShieldSrc(i, i, + 'https://img.shields.io/github/downloads/ghdl/ghdl/' + i + '.svg?longCache=true&style=flat-square&logo=github&label=%7F', + 'https://github.com/ghdl/ghdl/releases/download/' + i, latex=latex) + + return assets + +# TODO: Is github.com/ghdl/ghdl/releases/download/<tag>/<file> subject to rate limit? Is there an alternative (not documented) domain? + + +# +# Get, extract and process JSON data to create the shields and list/table with all the releases except the latest +# + +def createReleasesShields(tag='latest'): + d = getJSON() + from dateutil.parser import parse as parseTime + releases = [['Date', 'Downloads']] + if tag == 'latest': + t = d[0] + for x in d: + name = x['tag_name'] + if tag == name: + t = x + date = parseTime( x['published_at'] ).strftime("%Y-%m-%d") + releases.append([date, '|'+name+'/total|']) + i = name + for l in [False, True]: + printShieldSrc(i+'/total', i+' Total', + 'https://img.shields.io/github/downloads/ghdl/ghdl/' + i + '/total.svg?longCache=true&style=flat-square&logo=github&label=%7F', + 'https://github.com/ghdl/ghdl/releases/' + i, l) + + out = {'releases': releases, 'assets': createTagShields(t)} + import json + json.dump(out, open('data.json', 'w'), indent=4) + return out + + +# +# Print the table with all the assets of a release/tag +# + +def printReleaseTab(assets, latex=False): + if isinstance(assets, str): + assets = getJSON(assets)["assets"] + + if latex: + for y, z in enumerate(assets[1:]): + assets[y+1] = z[0:3] + ['|l' + stripLabel(z[3][1:-1]) + '|'] + + printTab(assets) + +# +# Print list of releases, except the latest (second row) +# + +def printReleasesList(releases, latex=False): + if isinstance(releases, str): + releases = getJSON(releases)["releases"] + + rs = [releases[0]] + rs.extend(releases[2:]) + + if latex: + rs[0] = ['Release/Tag'] + [rs[0][0]] + for x, r in enumerate(rs[1:]): + rs[x+1] = ['|l' + stripLabel(r[1][1:-1]) + '|'] + [r[0]] + else: + rs[0] = ['Release/Tag'] + rs[0] + for x, r in enumerate(rs[1:]): + rs[x+1] = [r[1][1:-7]] + r + + printTab(rs) diff --git a/doc/requirements.txt b/doc/requirements.txt index 42a068479..7e7fe40ed 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -2,6 +2,7 @@ sphinx>=1.5.2 recommonmark sphinx_rtd_theme +python-dateutil # sphinxcontrib-textstyle>=0.2.1 # sphinxcontrib-spelling>=2.2.0 # changelog>=0.3.5 |