mirror of
https://github.com/Cantera/cantera.git
synced 2026-07-30 16:18:17 -05:00
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.
214 lines
9.2 KiB
Python
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')
|