Files
cantera/doc/SConscript
T
Bryan W. Weber daa36668e7 [SCons] Fix Sphinx docs build in parallel
The Sphinx build requires both the Cython module to have been built and
the doxygen run to have finished. When running the build in parallel
with scons build -j#, there was the possibility that Sphinx could run
before all its dependencies were built. This change adds dependencies
for the Sphinx builder on Doxygen and the Cython module, resolving the
problem.
2021-02-12 16:57:15 -05:00

214 lines
9.2 KiB
Python

from __future__ import print_function
from buildutils import *
Import('env', 'build', 'install')
localenv = env.Clone()
from collections import namedtuple
Page = namedtuple('Page', ['name', 'title', 'objects'])
# Set up functions to pseudo-autodoc the MATLAB toolbox
def extract_matlab_docstring(mfile, level):
"""
Return the docstring from mfile, assuming that it consists of the
first uninterrupted comment block.
:param mfile:
File name of the matlab file from which the documentation will be
read
:param level:
Level of documentation. Class = 0, Function = 1
"""
# Set the start of the docstring based on the level passed in. This is only
# necessary for the old-style MATLAB classes, where each method is its own
# file.
if level == 0:
docstring = ".. mat:class:: "
elif level == 1:
docstring = " .. mat:function:: "
else:
print("Unknown level for MATLAB documentation.")
sys.exit(1)
# The leader is the number of spaces at the beginning of a regular line
# of documentation.
leader = ' '*4*(level + 1)
with open(mfile, 'r') as in_file:
# The function name is read from the first line
docstring += get_function_name(in_file.readline()) + '\n'
# By convention, the second line (called H1 in the MATLAB documentation)
# is read by various MATLAB functions, so it should be in the format
# MATLAB expects - FUNCTIONNAME Summary. We read in this line and
# add the Summary to the docstring. If the line doesn't match the
# format, just write it to the docstring as is.
line = in_file.readline()
try:
docstring += leader + line.split(' ')[1] + '\n'
except IndexError:
docstring += line + '\n'
# Skip the next line, which is a duplicate of the first. It is here
# because MATLAB doesn't show the function definition in its help.
in_file.readline()
# For the rest of the lines in the file, get the line if it is
# in the first unbroken comment section and add it to the docstring.
for line in in_file.readlines():
try:
if line.lstrip().startswith('%'):
docstring += leader + line.lstrip()[2:-1] + '\n'
else:
break
except IndexError:
docstring += '\n'
return docstring + '\n'
def get_function_name(function_string):
"""
Return the Matlab function or classdef signature, assuming that
the string starts with either 'function ' or 'classdef '.
"""
if function_string.startswith('function '):
sig = function_string[len('function '):]
elif function_string.startswith('classdef '):
sig = function_string[len('classdef '):]
else:
print("Unknown function declaration in MATLAB document", function_string)
sys.exit(1)
# Split the function signature on the equals sign, if it exists.
# We don't care about what comes before the equals sign, since
# if a function returns, the docs will tell us. If there is no
# =, return the whole signature.
if '=' in sig:
idx = sig.index('=')
return sig[idx+2:]
else:
return sig
if localenv['doxygen_docs']:
docs = build(localenv.Command('#build/docs/doxygen/html/index.html',
'doxygen/Doxyfile', 'doxygen $SOURCE'))
env.Depends(docs, env.Glob('#doc/doxygen/*') +
mglob(env, '#include/cantera', 'h') +
mglob(env, '#include/cantera/*', 'h') +
mglob(env, '#src/cantera/*', 'h', 'cpp'))
env.Alias('doxygen', docs)
install(localenv.RecursiveInstall, '$inst_docdir/doxygen/html',
'#/build/docs/doxygen/html', exclude=['\\.map', '\\.md5'])
if localenv['sphinx_docs']:
localenv['SPHINXBUILD'] = Dir('#build/docs/sphinx')
localenv['SPHINXSRC'] = Dir('sphinx')
sphinxdocs = build(localenv.Command('${SPHINXBUILD}/html/index.html',
'sphinx/conf.py',
'${sphinx_cmd} -b html -d ${SPHINXBUILD}/doctrees ${SPHINXSRC} ${SPHINXBUILD}/html'))
env.Alias('sphinx', sphinxdocs)
env.Depends(sphinxdocs, env['python_module'])
# Create a list of MATLAB classes to document. This uses the NamedTuple
# structure defined at the top of the file. The @Data and @Utilities
# classes are fake classes for the purposes of documentation only. Each
# Page represents one html page of the documentation.
pages = [
Page('importing', 'Objects Representing Phases',
['@Solution', '@Mixture', '@Interface', '@Pure Fluid Phases']),
Page('thermodynamics', 'Thermodynamic Properties',
['@ThermoPhase']),
Page('kinetics', 'Chemical Kinetics', ['@Kinetics']),
Page('transport', 'Transport Properties', ['@Transport']),
Page('zero-dim', 'Zero-Dimensional Reactor Networks',
['@Func', '@Reactor', '@ReactorNet', '@FlowDevice', '@Wall']),
Page('one-dim', 'One-Dimensional Reacting Flows', ['1D/@Domain1D', '1D/@Stack']),
Page('data', 'Physical Constants', ['@Data']),
Page('utilities', 'Utility Functions', ['@Utilities', '@XML_Node']),
]
# Create a dictionary of extra files associated with each class. These
# files are listed relative to the top directory interfaces/matlab/cantera
extra = {
'@Solution': ['IdealGasMix.m', 'GRI30.m', 'Air.m'],
'@Pure Fluid Phases': ['CarbonDioxide.m', 'HFC134a.m', 'Hydrogen.m',
'Methane.m', 'Nitrogen.m', 'Oxygen.m', 'Water.m'],
'@Func': ['gaussian.m', 'polynom.m'],
'@Reactor': ['ConstPressureReactor.m',
'FlowReactor.m', 'IdealGasConstPressureReactor.m',
'IdealGasReactor.m', 'Reservoir.m'],
'@FlowDevice': ['MassFlowController.m', 'Valve.m'],
'1D/@Domain1D': ['1D/AxiStagnFlow.m', '1D/AxisymmetricFlow.m',
'1D/Inlet.m', '1D/Outlet.m', '1D/OutletRes.m',
'1D/Surface.m', '1D/SymmPlane.m'],
'1D/@Stack': ['1D/FreeFlame.m', '1D/CounterFlowDiffusionFlame.m'],
'@Interface': ['importEdge.m', 'importInterface.m'],
'@Data': ['gasconstant.m', 'oneatm.m'],
'@Utilities': ['adddir.m', 'ck2cti.m', 'cleanup.m', 'geterr.m',
'getDataDirectories.m', 'canteraVersion.m',
'canteraGitCommit.m']
}
# These files do not need to be documented in the MATLAB classes because they
# are generics that are overloaded per-class. Since the loop checks for these
# strings in each file name, hndl.m is the same as *hndl.m* (to use globbing
# notation).
nodoc_matlab_files = ['clear.m', 'display.m', 'hndl.m', 'private', 'subsref.m']
# Loop through the pages list to document each class
for page in pages:
tempenv = env.Clone()
# Set the title header
title = page.title
tempenv['title'] = '='*len(title) + '\n' + title + '\n' + '='*len(title)
doc = ''
# The base directory of the MATLAB toolbox relative to the sphinx build directory
base = '../interfaces/matlab/toolbox'
for obj in page.objects:
all_files = []
# Set the subheader based on the class name
doc += obj.split('@')[1] + '\n' + '-'*len(obj.split('@')[1]) + '\n\n'
if os.path.isdir(pjoin(base,obj)):
# Get a list of the functions in this class as long as its a file we care about
functions = [name for name in os.listdir(pjoin(base,obj)) if not any(x in name for x in nodoc_matlab_files)]
# Add the docstring for the class name at level 0
class_file = functions.pop(functions.index(obj.split('@')[1]+'.m'))
doc += extract_matlab_docstring(os.path.relpath(pjoin(base,obj,class_file)), 0)
# Get the extra files from the extra dictionary and sort them with
# the regular functions.
extra_files = extra.get(obj,[])
all_files += sorted(functions + extra_files)
else:
all_files = extra.get(obj,[])
for file in all_files:
if file in functions:
doc += extract_matlab_docstring(os.path.relpath(pjoin(base,obj,file)), 1)
else:
doc += extract_matlab_docstring(os.path.relpath(pjoin(base,file)), 1)
tempenv['matlab_docstrings'] = doc
# Substitute the docstrings into the proper file. Since the docs change
# every time the source is changed, we don't want to have to commit the
# change in the rst file as well as the source - too much code churn. So
# we use a template and a SubstFile directive.
c = tempenv.SubstFile('#doc/sphinx/matlab/%s.rst' % page.name,
'#doc/sphinx/matlab/matlab-template.rst.in')
build(c)
localenv.Depends(sphinxdocs, c)
localenv.AlwaysBuild(sphinxdocs)
if localenv['doxygen_docs']:
localenv.Depends(sphinxdocs, docs)
install(localenv.RecursiveInstall, '$inst_docdir/sphinx/html',
'#/build/docs/sphinx/html')