source: rtems-docs/common/waf.py @ f97be09

5
Last change on this file since f97be09 was f97be09, checked in by Chris Johns <chrisj@…>, on 03/20/17 at 01:18:53

Use a single top level version number.

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