# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. """A sphnix-doc plugin to build mxnet docs""" import subprocess import re import os import json import sys from recommonmark import transform import pypandoc import contextlib # Use six for Python 2 / 3 compatibility from six import StringIO from six.moves import configparser _BUILD_VER = os.getenv('BUILD_VER', 'default') print("Building version {}".format(_BUILD_VER)) _DOC_SET = 'document_sets_' + _BUILD_VER parser = configparser.SafeConfigParser() parser.read('settings.ini') if _DOC_SET not in parser.sections(): _DOC_SET = 'document_sets_default' for section in [ _DOC_SET ]: print("Document sets to generate:") for candidate in [ 'scala_docs', 'clojure_docs', 'doxygen_docs', 'r_docs' ]: print('%-12s : %s' % (candidate, parser.get(section, candidate))) _MXNET_DOCS_BUILD_MXNET = parser.getboolean('mxnet', 'build_mxnet') _SCALA_DOCS = parser.getboolean(_DOC_SET, 'scala_docs') _CLOJURE_DOCS = parser.getboolean(_DOC_SET, 'clojure_docs') _DOXYGEN_DOCS = parser.getboolean(_DOC_SET, 'doxygen_docs') _R_DOCS = parser.getboolean(_DOC_SET, 'r_docs') # white list to evaluate the code block output, such as ['tutorials/gluon'] _EVAL_WHILTELIST = [] # start or end of a code block _CODE_MARK = re.compile('^([ ]*)```([\w]*)') # language names and the according file extensions and comment symbol _LANGS = {'python' : ('py', '#'), 'r' : ('R','#'), 'scala' : ('scala', '#'), 'julia' : ('jl', '#'), 'perl' : ('pl', '#'), 'cpp' : ('cc', '//'), 'bash' : ('sh', '#')} _LANG_SELECTION_MARK = 'INSERT SELECTION BUTTONS' _SRC_DOWNLOAD_MARK = 'INSERT SOURCE DOWNLOAD BUTTONS' def _run_cmd(cmds): """Run commands, raise exception if failed""" if not isinstance(cmds, str): cmds = "".join(cmds) print("Execute \"%s\"" % cmds) try: subprocess.check_call(cmds, shell=True) except subprocess.CalledProcessError as err: print(err) raise err def generate_doxygen(app): """Run the doxygen make commands""" _run_cmd("cd %s/.. && make doxygen" % app.builder.srcdir) _run_cmd("cp -rf doxygen/html %s/doxygen" % app.builder.outdir) def build_mxnet(app): """Build mxnet .so lib""" if not os.path.exists(os.path.join(app.builder.srcdir, '..', 'config.mk')): _run_cmd("cd %s/.. && cp make/config.mk config.mk && make -j$(nproc) DEBUG=1" % app.builder.srcdir) else: _run_cmd("cd %s/.. && make -j$(nproc) DEBUG=1" % app.builder.srcdir) def build_r_docs(app): """build r pdf""" r_root = app.builder.srcdir + '/../R-package' pdf_path = app.builder.srcdir + '/api/r/mxnet-r-reference-manual.pdf' _run_cmd('cd ' + r_root + '; R -e "roxygen2::roxygenize()"; R CMD Rd2pdf . --no-preview -o ' + pdf_path) dest_path = app.builder.outdir + '/api/r/' _run_cmd('mkdir -p ' + dest_path + '; mv ' + pdf_path + ' ' + dest_path) def build_scala(app): """build scala for scala docs and clojure docs to use""" _run_cmd("cd %s/.. && make scalapkg" % app.builder.srcdir) _run_cmd("cd %s/.. && make scalainstall" % app.builder.srcdir) def build_scala_docs(app): """build scala doc and then move the outdir""" scala_path = app.builder.srcdir + '/../scala-package' # scaldoc fails on some apis, so exit 0 to pass the check _run_cmd('cd ' + scala_path + '; scaladoc `find . -type f -name "*.scala" | egrep \"\/core|\/infer\" | egrep -v \"Suite\"`; exit 0') dest_path = app.builder.outdir + '/api/scala/docs' _run_cmd('rm -rf ' + dest_path) _run_cmd('mkdir -p ' + dest_path) scaladocs = ['index', 'index.html', 'org', 'lib', 'index.js', 'package.html'] for doc_file in scaladocs: _run_cmd('cd ' + scala_path + ' && mv -f ' + doc_file + ' ' + dest_path) def build_clojure_docs(app): """build clojure doc and then move the outdir""" clojure_path = app.builder.srcdir + '/../contrib/clojure-package' _run_cmd('cd ' + clojure_path + '; lein codox') dest_path = app.builder.outdir + '/api/clojure/docs' _run_cmd('rm -rf ' + dest_path) _run_cmd('mkdir -p ' + dest_path) clojure_doc_path = app.builder.srcdir + '/../contrib/clojure-package/target/doc' _run_cmd('cd ' + clojure_doc_path + ' && cp -r * ' + dest_path) def _convert_md_table_to_rst(table): """Convert a markdown table to rst format""" if len(table) < 3: return '' out = '```eval_rst\n.. list-table::\n :header-rows: 1\n\n' for i,l in enumerate(table): cols = l.split('|')[1:-1] if i == 0: ncol = len(cols) else: if len(cols) != ncol: return '' if i == 1: for c in cols: if len(c) is not 0 and '---' not in c: return '' else: for j,c in enumerate(cols): out += ' * - ' if j == 0 else ' - ' out += pypandoc.convert_text( c, 'rst', format='md').replace('\n', ' ').replace('\r', '') + '\n' out += '```\n' return out def convert_table(app, docname, source): """Find tables in a markdown and then convert them into the rst format""" num_tables = 0 for i,j in enumerate(source): table = [] output = '' in_table = False for l in j.split('\n'): r = l.strip() if r.startswith('|'): table.append(r) in_table = True else: if in_table is True: converted = _convert_md_table_to_rst(table) if converted is '': print("Failed to convert the markdown table") print(table) else: num_tables += 1 output += converted in_table = False table = [] output += l + '\n' source[i] = output if num_tables > 0: print('Converted %d tables in %s' % (num_tables, docname)) def _parse_code_lines(lines): """A iterator that returns if a line is within a code block Returns ------- iterator of (str, bool, str, int) - line: the line - in_code: if this line is in a code block - lang: the code block langunage - indent: the code indent """ in_code = False lang = None indent = None for l in lines: m = _CODE_MARK.match(l) if m is not None: if not in_code: if m.groups()[1].lower() in _LANGS: lang = m.groups()[1].lower() indent = len(m.groups()[0]) in_code = True yield (l, in_code, lang, indent) else: yield (l, in_code, lang, indent) lang = None indent = None in_code = False else: yield (l, in_code, lang, indent) def _get_lang_selection_btn(langs): active = True btngroup = '