source: rtems-docs/common/waf.py @ 2a06644

5am
Last change on this file since 2a06644 was 2a06644, checked in by Richard Campbell <richard.campbell@…>, on Feb 22, 2018 at 4:23:08 PM

RTEMS Docs: Check sphinx version

Sphinx.util.compat module was removed at Sphinx version 1.7.
Imported module was not being used.

Closes #3311.

  • Property mode set to 100644
File size: 16.1 KB
Line 
1import sys, os, re
2from waflib.Build import BuildContext
3
4import latex
5
6sphinx_min_version = (1, 3)
7
8def build_date():
9    import datetime
10    now = datetime.date.today()
11    m = now.strftime('%B')
12    y = now.strftime('%Y')
13    if now.day % 10 == 1:
14        s = 'st'
15    elif now.day % 10 == 2:
16        s = 'nd'
17    elif now.day == 3:
18        s = 'rd'
19    else:
20        s = 'th'
21    d = '%2d%s' % (now.day, s)
22    return '%s %s %s' % (d, m, y)
23
24def version_cmdline(ctx):
25    return '-Drelease="%s" -Dversion="%s"' % (ctx.env.VERSION, ctx.env.VERSION)
26
27def sphinx_cmdline(ctx, build_type, conf_dir, doctrees, source_dir, output_dir):
28    rule = "${BIN_SPHINX_BUILD} %s -b %s -c %s %s -d %s %s %s ${SRC}" % \
29           (sphinx_verbose(ctx), build_type, conf_dir, version_cmdline(ctx),
30            doctrees, source_dir, output_dir)
31    return rule
32
33def cmd_spell(ctx):
34    from waflib import Options
35    from sys import argv
36    from subprocess import call
37
38    Options.commands = None # stop warnings about knowing commands.
39
40    if not ctx.env.BIN_ASPELL:
41        ctx.fatal("'aspell' is required please install and re-run configure.")
42
43    if len(argv) < 3:
44        ctx.fatal("Please supply at least one file name")
45
46    files = argv[2:]
47
48    path = ctx.path.parent.abspath()
49
50    # XXX: add error checking eg check if file exists.
51    for file in files:
52        cmd = ctx.env.BIN_ASPELL + \
53              ["-c",
54               "--personal=%s/common/spell/dict/rtems" % path,
55               "--extra-dicts=%s/common/spell/en_GB-ise-w_accents.multi" % path,
56               file]
57        print("running:", cmd)
58        call(cmd)
59
60def cmd_linkcheck(ctx):
61    conf_dir = ctx.path.get_src()
62    source_dir = ctx.path.get_src()
63    buildtype = 'linkcheck'
64    build_dir, output_node, output_dir, doctrees = build_dir_setup(ctx, buildtype)
65    rule = sphinx_cmdline(ctx, buildtype, conf_dir, doctrees, source_dir, output_dir)
66    ctx(
67        rule   = rule,
68        cwd    = ctx.path.abspath(),
69        source = ctx.path.ant_glob('**/*.rst'),
70        target = "linkcheck/output.txt"
71    )
72
73class spell(BuildContext):
74    __doc__ = "Check spelling.  Supply a list of files or a glob (*.rst)"
75    cmd = 'spell'
76    fun = 'cmd_spell'
77
78class linkcheck(BuildContext):
79    __doc__ = "Check all external URL references."
80    cmd = 'linkcheck'
81    fun = 'cmd_linkcheck'
82
83def check_sphinx_version(ctx, minver):
84    try:
85        import sphinx
86        # sphinx.version_info was introduced in sphinx ver 1.2
87        version = sphinx.version_info
88        # version looks like (1, 7, 0, 'final', 0))
89        ver = version[0:2]
90    except:
91        try:
92            # sphinx-build returns its version info in stderr
93            (out, err) = ctx.cmd_and_log(ctx.env.BIN_SPHINX_BUILD +
94                              ['--version'], output=Context.BOTH)
95            # err looks like 'sphinx-build 1.7.0\n'
96            version = err.split(" ")[-1:][0].strip()
97            ver = tuple(map(int, re.split('[\D]', version)))
98        except:
99            try:
100                # sphinx-build returns its version info in stdout
101                version = ctx.cmd_and_log(ctx.env.BIN_SPHINX_BUILD +
102                              ['--version']).split(" ")[-1:][0].strip()
103                try:
104                    ver = tuple(map(int, re.split('[\D]', version)))
105                except:
106                    ctx.fatal("Sphinx version cannot be checked")
107            except:
108                ctx.fatal("Sphinx version cannot be checked: %s" % version)
109    if ver < minver:
110        ctx.fatal("Sphinx version is too old: %s" % ".".join(map(str, ver)))
111    return ver
112
113def sphinx_verbose(ctx):
114    return ' '.join(ctx.env.SPHINX_VERBOSE)
115
116def is_top_build(ctx):
117    from_top = False
118    if ctx.env['BUILD_FROM_TOP'] and ctx.env['BUILD_FROM_TOP'] == 'yes':
119        from_top = True
120    return from_top
121
122def build_dir_setup(ctx, buildtype):
123    where = buildtype
124    if is_top_build(ctx):
125        where = os.path.join(ctx.path.name, where)
126    bnode = ctx.bldnode.find_node(where)
127    if bnode is None:
128        ctx.bldnode.make_node(where).mkdir()
129    build_dir = ctx.path.get_bld().relpath()
130    output_node = ctx.path.get_bld().make_node(buildtype)
131    output_dir = output_node.abspath()
132    doctrees = os.path.join(os.path.dirname(output_dir), 'doctrees', buildtype)
133    return build_dir, output_node, output_dir, doctrees
134
135def pdf_resources(ctx, buildtype):
136    packages_base = ctx.path.parent.find_dir('common/latex')
137    if packages_base is None:
138        ctx.fatal('Latex package directory not found')
139    base = packages_base.path_from(ctx.path)
140    fnode = ctx.path.get_bld().make_node(buildtype)
141    fnode.mkdir()
142    local_packages = latex.local_packages()
143    if local_packages is not None:
144        srcs = [os.path.join(base, p) for p in local_packages]
145        ctx(
146            features = "subst",
147            is_copy  = True,
148            source   = srcs,
149            target   = [fnode.make_node(p) for p in local_packages]
150        )
151    ctx(
152        features = "subst",
153        is_copy  = True,
154        source   = os.path.join(base, ctx.env.RTEMSEXTRAFONTS),
155        target   = fnode.make_node('rtemsextrafonts.sty')
156    )
157
158def html_resources(ctx, buildtype):
159    for dir_name in ["_static", "_templates"]:
160        files = ctx.path.parent.find_node("common").ant_glob("%s/*" % dir_name)
161        fnode = ctx.path.get_bld().make_node(os.path.join(buildtype, dir_name))
162        fnode.mkdir() # dirs
163        ctx(
164            features = "subst",
165            is_copy  = True,
166            source   = files,
167            target   = [fnode.make_node(x.name) for x in files]
168        )
169
170    # copy images
171#    ctx.path.get_bld().make_node("images").mkdir()
172#    files = ctx.path.parent.ant_glob("images/**")
173#    ctx(
174#        features    = "subst",
175#        is_copy     = True,
176#        source      = files,
177#        target      = [x.srcpath().replace("../", "") for x in files]
178#    )
179
180
181def check_sphinx_extension(ctx, extension):
182    def run_sphinx(bld):
183        rst_node = bld.srcnode.make_node('testbuild/contents.rst')
184        rst_node.parent.mkdir()
185        rst_node.write('.. COMMENT test sphinx\n')
186        bld(rule = bld.kw['rule'], source = rst_node)
187
188    ctx.start_msg("Checking for '%s'" % (extension))
189    try:
190        ctx.run_build(fragment = 'xx',
191                      rule = "${BIN_SPHINX_BUILD} -b html -D extensions=%s -C . out" % (extension),
192                      build_fun = run_sphinx,
193                      env = ctx.env)
194    except ctx.errors.ConfigurationError:
195        ctx.end_msg('not found (see README.txt)', 'RED')
196        ctx.fatal('The configuration failed')
197    ctx.end_msg('found')
198
199
200def cmd_configure(ctx):
201    check_sphinx = not ctx.env.BIN_SPHINX_BUILD
202    if check_sphinx:
203        ctx.msg('Checking version', ctx.env.VERSION)
204
205        ctx.find_program("sphinx-build", var="BIN_SPHINX_BUILD", mandatory = True)
206        ctx.find_program("aspell", var = "BIN_ASPELL", mandatory = False)
207
208        ctx.start_msg("Checking if Sphinx is at least %s.%s" % sphinx_min_version)
209        ver = check_sphinx_version(ctx, sphinx_min_version)
210        ctx.end_msg("yes (%s)" % ".".join(map(str, ver)))
211
212        ctx.start_msg("Checking Sphinx Verbose ")
213        if 'SPHINX_VERBOSE' not in ctx.env:
214            ctx.env.append_value('SPHINX_VERBOSE', ctx.options.sphinx_verbose)
215            level = sphinx_verbose(ctx)
216            if level == '-Q':
217                level = 'quiet'
218            ctx.end_msg(level)
219        #
220        # Check extensions.
221        #
222        check_sphinx_extension(ctx, 'sphinx.ext.autodoc')
223        check_sphinx_extension(ctx, 'sphinx.ext.coverage')
224        check_sphinx_extension(ctx, 'sphinx.ext.doctest')
225        check_sphinx_extension(ctx, 'sphinx.ext.graphviz')
226        check_sphinx_extension(ctx, 'sphinx.ext.intersphinx')
227        check_sphinx_extension(ctx, 'sphinx.ext.mathjax')
228        check_sphinx_extension(ctx, 'sphinxcontrib.bibtex')
229
230    #
231    # Optional builds.
232    #
233    ctx.env.BUILD_PDF = 'no'
234    if ctx.options.pdf:
235        check_tex = not ctx.env.PDFLATEX
236        if check_tex:
237            ctx.load('tex')
238            if not ctx.env.PDFLATEX or not ctx.env.MAKEINDEX:
239                ctx.fatal('The programs pdflatex and makeindex are required for PDF output')
240            if 'PDFLATEXFLAGS' not in ctx.env or \
241               '-shell-escape' not in ctx.env['PDFLATEXFLAGS']:
242                ctx.env.append_value('PDFLATEXFLAGS', '-shell-escape')
243            latex.configure_tests(ctx)
244        ctx.env.BUILD_PDF = 'yes'
245
246    ctx.envBUILD_SINGLEHTML = 'no'
247    if ctx.options.singlehtml:
248        check_inliner = not ctx.env.BIN_INLINER
249        if check_inliner:
250            ctx.env.BUILD_SINGLEHTML = 'yes'
251            ctx.find_program("inliner", var = "BIN_INLINER", mandatory = False)
252            if not ctx.env.BIN_INLINER:
253                ctx.fatal("Node inliner is required install with 'npm install -g inliner' " +
254                          "(https://github.com/remy/inliner)")
255
256def doc_pdf(ctx, source_dir, conf_dir, extra_source):
257    buildtype = 'latex'
258    build_dir, output_node, output_dir, doctrees = build_dir_setup(ctx, buildtype)
259    pdf_resources(ctx, buildtype)
260    rule = sphinx_cmdline(ctx, buildtype, conf_dir, doctrees, source_dir, output_dir)
261    ctx(
262        rule         = rule,
263        cwd          = ctx.path,
264        source       = ctx.path.ant_glob('**/*.rst') + extra_source,
265        target       = ctx.path.find_or_declare("%s/%s.tex" % (buildtype,
266                                                               ctx.path.name))
267    )
268    ctx(
269        features     = 'tex',
270        cwd          = output_dir,
271        type         = 'pdflatex',
272        source       = "%s/%s.tex" % (buildtype, ctx.path.name),
273        prompt       = 0
274    )
275    ctx.install_files('${PREFIX}',
276                      '%s/%s.pdf' % (buildtype, ctx.path.name),
277                      cwd = output_node,
278                      quiet = True)
279
280def doc_singlehtml(ctx, source_dir, conf_dir, extra_source):
281    #
282    # Use a run command to handle stdout and stderr output from inliner. Using
283    # a standard rule in the build context locks up.
284    #
285    def run(task):
286        src = task.inputs[0].abspath()
287        tgt = task.outputs[0].abspath()
288        cmd = '%s %s' % (task.env.BIN_INLINER[0], src)
289        so = open(tgt, 'w')
290        se = open(tgt + '.err', 'w')
291        r = task.exec_command(cmd, stdout = so, stderr = se)
292        so.close()
293        se.close()
294        #
295        # The inliner does not handle internal href's correctly and places the
296        # input's file name in the href. Strip these.
297        #
298        with open(tgt, 'r') as i:
299            before = i.read()
300            after = before.replace('index.html', '')
301        i.close()
302        with open(tgt, 'w') as o:
303            o.write(after)
304        o.close()
305        return r
306
307    buildtype = 'singlehtml'
308    build_dir, output_node, output_dir, doctrees = build_dir_setup(ctx, buildtype)
309    html_resources(ctx, buildtype)
310    rule = sphinx_cmdline(ctx, buildtype, conf_dir, doctrees, source_dir, output_dir)
311    ctx(
312        rule         = rule,
313        cwd          = ctx.path,
314        source       = ctx.path.ant_glob('**/*.rst') + extra_source,
315        target       = ctx.path.find_or_declare("%s/index.html" % (buildtype)),
316        install_path = None
317    )
318    ctx(
319        rule         = run,
320        inliner      = ctx.env.BIN_INLINER,
321        source       = "%s/index.html" % buildtype,
322        target       = "%s/%s.html" % (buildtype, ctx.path.name),
323        install_path = '${PREFIX}'
324    )
325
326def doc_html(ctx, source_dir, conf_dir, extra_source):
327    buildtype = 'html'
328    build_dir, output_node, output_dir, doctrees = build_dir_setup(ctx, buildtype)
329    html_resources(ctx, buildtype)
330    rule = sphinx_cmdline(ctx, buildtype, conf_dir, doctrees, source_dir, output_dir)
331    ctx(
332        rule         = rule,
333        cwd          = ctx.path,
334        source       = ctx.path.ant_glob('**/*.rst') + extra_source,
335        target       = ctx.path.find_or_declare('%s/index.html' % buildtype),
336        install_path = None
337    )
338    ctx.install_files('${PREFIX}/%s' % (ctx.path.name),
339                      output_node.ant_glob('**/*', quiet = True),
340                      cwd = output_node,
341                      relative_trick = True,
342                      quiet = True)
343
344def cmd_build(ctx, extra_source = []):
345    conf_dir = ctx.path.get_src()
346    source_dir = ctx.path.get_src()
347
348    if ctx.env.BUILD_PDF == 'yes':
349        doc_pdf(ctx, source_dir, conf_dir, extra_source)
350
351    if ctx.env.BUILD_SINGLEHTML == 'yes':
352        doc_singlehtml(ctx, source_dir, conf_dir, extra_source)
353
354    doc_html(ctx, source_dir, conf_dir, extra_source)
355
356def cmd_options(ctx):
357    ctx.add_option('--disable-extra-fonts',
358                   action = 'store_true',
359                   default = False,
360                   help = "Disable building with extra fonts for better quality (lower quality).")
361    ctx.add_option('--sphinx-verbose',
362                   action = 'store',
363                   default = "-Q",
364                   help = "Sphinx verbose.")
365    ctx.add_option('--pdf',
366                   action = 'store_true',
367                   default = False,
368                   help = "Build PDF.")
369    ctx.add_option('--singlehtml',
370                   action = 'store_true',
371                   default = False,
372                   help = "Build Single HTML file, requires Node Inliner")
373
374def cmd_options_path(ctx):
375    cmd_options(ctx)
376    ctx.add_option('--rtems-path-py',
377                   type = 'string',
378                   help = "Full path to py/ in RTEMS source repository.")
379
380def cmd_configure_path(ctx):
381    if not ctx.options.rtems_path_py:
382        ctx.fatal("--rtems-path-py is required")
383
384    ctx.env.RTEMS_PATH = ctx.options.rtems_path_py
385
386    cmd_configure(ctx)
387
388def xml_catalogue(ctx, building):
389    #
390    # The following is a hack to find the top_dir because the task does
391    # provided a reference to top_dir like a build context.
392    #
393    top_dir = ctx.get_cwd().find_node('..')
394    #
395    # Read the conf.py files in each directory to gather the doc details.
396    #
397    catalogue = {}
398    sp = sys.path[:]
399    for doc in building:
400        #
401        # Import using the imp API so the module is reloaded for us.
402        #
403        import imp
404        sys.path = [top_dir.find_node(doc).abspath()]
405        mf = imp.find_module('conf')
406        sys.path = sp[:]
407        try:
408            bconf = imp.load_module('bconf', mf[0], mf[1], mf[2])
409        finally:
410            mf[0].close()
411        catalogue[doc] = {
412            'title': bconf.project,
413            'version': str(ctx.env.VERSION),
414            'release': str(ctx.env.VERSION),
415            'pdf': bconf.latex_documents[0][1].replace('.tex', '.pdf'),
416            'html': '%s/index.html' % (doc),
417            'singlehtml': '%s.html' % (doc)
418        }
419        bconf = None
420
421    import xml.dom.minidom as xml
422    cat = xml.Document()
423
424    root = cat.createElement('rtems-docs')
425    root.setAttribute('date', build_date())
426    cat.appendChild(root)
427
428    heading = cat.createElement('catalogue')
429    text = cat.createTextNode(str(ctx.env.VERSION))
430    heading.appendChild(text)
431    root.appendChild(heading)
432
433    builds = ['html']
434    if ctx.env.BUILD_PDF == 'yes':
435        builds += ['pdf']
436    if ctx.env.BUILD_SINGLEHTML == 'yes':
437        builds += ['singlehtml']
438
439    for d in building:
440        doc = cat.createElement('doc')
441        name = cat.createElement('name')
442        text = cat.createTextNode(d)
443        name.appendChild(text)
444        title = cat.createElement('title')
445        text = cat.createTextNode(catalogue[d]['title'])
446        title.appendChild(text)
447        release = cat.createElement('release')
448        text = cat.createTextNode(catalogue[d]['release'])
449        release.appendChild(text)
450        version = cat.createElement('version')
451        text = cat.createTextNode(catalogue[d]['version'])
452        version.appendChild(text)
453        doc.appendChild(name)
454        doc.appendChild(title)
455        doc.appendChild(release)
456        doc.appendChild(version)
457        for b in builds:
458            output = cat.createElement(b)
459            text = cat.createTextNode(catalogue[d][b])
460            output.appendChild(text)
461            doc.appendChild(output)
462        root.appendChild(doc)
463
464    catnode = ctx.get_cwd().make_node('catalogue.xml')
465    catnode.write(cat.toprettyxml(indent = ' ' * 2, newl = os.linesep))
466
467    cat.unlink()
Note: See TracBrowser for help on using the repository browser.