source: rtems-docs/common/waf.py @ 91d6c96

5
Last change on this file since 91d6c96 was 91d6c96, checked in by Chris Johns <chrisj@…>, on 03/15/17 at 04:03:10

waf: Configure check the inliner only once.

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