summaryrefslogblamecommitdiffstats
path: root/common/waf.py
blob: 25f3d74cf08282164bf3d1aa7ad3eac1cec8d746 (plain) (tree)
1
2
3
4
5
6
7
8
9
                  
                                     
 
                           
 
                   


                               
 
                                                                   
 

                                                                              
 

                                                         
 
                    
 
                                    
 








                                                                                

 
                                                     









                                                                          
 
                          


                                                                         
 
                              


                                                  
 
                                      








                                                                            
 
                        











                                                                        
                                        




                                                         

                                                                               





















                                                                               
 






























































































                                                                                    
                       



















                                                                              










                                                                                            
                                 







                                                                                         
 
                                       
                       

                                                                                  
                                                     
                                             

















                                                                              
 
                                              
 


























                                                                              
                                                                                  
                                  
                                                               
                                                     
                                             













                                                                               
 
                                        
                      
                                                                                  
                                  
                                                               
                                                     
                                             











                                                                             
 
                                                     
                                   
 

                                          
 

                                                 
 
                                       
 












                                                                          
 
                          



                                                                         

                            

                                                
 
                                                  
 
                      








                                                 

                                                   
                  
 


                                                                                        
 

                                                                      
 




                                                              

     
                                                            
import sys, os, re
from waflib.Build import BuildContext

sphinx_min_version = (1, 3)

def cmd_spell(ctx):
    from waflib import Options
    from sys import argv
    from subprocess import call

    Options.commands = None # stop warnings about knowing commands.

    if not ctx.env.BIN_ASPELL:
        ctx.fatal("'aspell' is required please install and re-run configure.")

    if len(argv) < 3:
        ctx.fatal("Please supply at least one file name")

    files = argv[2:]

    path = ctx.path.parent.abspath()

    # XXX: add error checking eg check if file exists.
    for file in files:
        cmd = ctx.env.BIN_ASPELL + \
              ["-c",
               "--personal=%s/common/spell/dict/rtems" % path,
               "--extra-dicts=%s/common/spell/en_GB-ise-w_accents.multi" % path,
               file]
        print("running:", cmd)
        call(cmd)


def cmd_linkcheck(ctx, conf_dir=".", source_dir="."):
    ctx_rule = "${BIN_SPHINX_BUILD} -b linkcheck -c %s -j %d " + \
               "-d build/doctrees %s build/linkcheck" % (conf_dir,
                                                         ctx.options.jobs,
                                                             source_dir)
    ctx(
        rule   = ctx_rule,
        cwd    = ctx.path.abspath(),
        source = ctx.path.ant_glob('**/*.rst'),
        target = "linkcheck/output.txt"
    )

class spell(BuildContext):
    __doc__ = "Check spelling.  Supply a list of files or a glob (*.rst)"
    cmd = 'spell'
    fun = 'cmd_spell'

class linkcheck(BuildContext):
    __doc__ = "Check all external URL references."
    cmd = 'linkcheck'
    fun = 'cmd_linkcheck'

def check_sphinx_version(ctx, minver):
    version = ctx.cmd_and_log(ctx.env.BIN_SPHINX_BUILD +
                              ['--version']).split(" ")[-1:][0].strip()
    try:
        ver = tuple(map(int, re.split('[\D]', version)))
    except:
        ctx.fatal("Sphinx version cannot be checked: %s" % version)
    if ver < minver:
        ctx.fatal("Sphinx version is too old: %s" % ".".join(map(str, ver)))
    return ver

def sphinx_verbose(ctx):
    return ' '.join(ctx.env.SPHINX_VERBOSE)

def is_top_build(ctx):
    from_top = False
    if ctx.env['BUILD_FROM_TOP'] and ctx.env['BUILD_FROM_TOP'] == 'yes':
        from_top = True
    return from_top

def build_dir_setup(ctx, buildtype):
    where = buildtype
    if is_top_build(ctx):
        where = os.path.join(ctx.path.name, where)
    bnode = ctx.bldnode.find_node(where)
    if bnode is None:
        ctx.bldnode.make_node(where).mkdir()
    build_dir = ctx.path.get_bld().relpath()
    output_node = ctx.path.get_bld().make_node(buildtype)
    output_dir = output_node.abspath()
    doctrees = os.path.join(os.path.dirname(output_dir), 'doctrees', buildtype)
    return build_dir, output_node, output_dir, doctrees

def html_resources(ctx, buildtype):
    for dir_name in ["_static", "_templates"]:
        files = ctx.path.parent.find_node("common").ant_glob("%s/*" % dir_name)
        fnode = ctx.path.get_bld().make_node(os.path.join(buildtype, dir_name))
        fnode.mkdir() # dirs
        ctx(
            features = "subst",
            is_copy  = True,
            source   = files,
            target   = [fnode.make_node(x.name) for x in files]
        )

    # copy images
#    ctx.path.get_bld().make_node("images").mkdir()
#    files = ctx.path.parent.ant_glob("images/**")
#    ctx(
#        features    = "subst",
#        is_copy     = True,
#        source      = files,
#        target      = [x.srcpath().replace("../", "") for x in files]
#    )

def latex_configure_tests(conf):

    #
    # Using a hint from ita (thank you) :
    #  https://github.com/waf-project/waf/blob/master/demos/tex/wscript
    #
    latex_test_preamble = ['\\newif\\ifsphinxKeepOldNames \\sphinxKeepOldNamestrue',
                           '\documentclass[a4paper,11pt,english]{report}']
    latex_test_postamble = ['\\begin{document} test \\end{document}']
    latex_tests = {
        'Bjarne'         : ['\\usepackage[Bjarne]{fncychap}'],
        'alltt'          : ['\\usepackage{alltt}'],
        'amsmath'        : ['\\usepackage{amsmath}'],
        'amssymb'        : ['\\usepackage{amssymb}'],
        'amstext'        : ['\\usepackage{amstext}'],
        'array'          : ['\\usepackage{array}'],
        'atbegshi'       : ['\\usepackage{atbegshi}'],
        'babel'          : ['\\usepackage{babel}'],
        'babel'          : ['\\usepackage{babel}'],
        'calc'           : ['\\usepackage{calc}'],
        'capt-of'        : ['\\usepackage{capt-of}'],
        'charter'        : ['\\usepackage{charter}'],
        'cmap'           : ['\\usepackage{cmap}'],
        'color'          : ['\\usepackage{color}'],
        'eqparbox'       : ['\\usepackage{eqparbox}'],
        'etoolbox'       : ['\\usepackage{etoolbox}'],
        'fancybox'       : ['\\usepackage{fancybox}'],
        'fancyhdr'       : ['\\usepackage{fancyhdr}'],
        'fancyvrb'       : ['\\usepackage{fancyvrb}'],
        'float'          : ['\\usepackage{float}'],
        'fncychap'       : ['\\usepackage{fncychap}'],
        'fontenc'        : ['\\usepackage[T1]{fontenc}'],
        'footnote'       : ['\\usepackage{footnote}'],
        'framed'         : ['\\usepackage{framed}'],
        'graphicx'       : ['\\usepackage{graphicx}'],
        'hypcap'         : ['\\usepackage{hyperref}',
                            '\\usepackage{hypcap}'],
        'hyperref'       : ['\\usepackage{hyperref}'],
        'ifplatform'     : ['\\usepackage{ifplatform}'],
        'ifthen'         : ['\\usepackage{ifthen}'],
        'inconsolata'    : ['\\usepackage{inconsolata}'],
        'inputenc'       : ['\\usepackage{inputenc}'],
        'keyval'         : ['\\usepackage{keyval}'],
        'kvoptions'      : ['\\usepackage{kvoptions}'],
        'lato'           : ['\\usepackage{lato}'],
        'lineno'         : ['\\usepackage{lineno}'],
        'longtable'      : ['\\usepackage{longtable}'],
        'makeidx'        : ['\\usepackage{makeidx}'],
        'multirow'       : ['\\usepackage{multirow}'],
        'parskip'        : ['\\usepackage{parskip}'],
        'pdftexcmds'     : ['\\usepackage{pdftexcmds}'],
        'textcomp'       : ['\\usepackage{textcomp}'],
        'threeparttable' : ['\\usepackage{threeparttable}'],
        'times'          : ['\\usepackage{times}'],
        'titlesec'       : ['\\usepackage{titlesec}'],
        'upquote'        : ['\\usepackage{upquote}'],
        'utf8'           : ['\\usepackage[utf8]{inputenc}'],
        'wrapfig'        : ['\\usepackage{wrapfig}'],
        'xcolor'         : ['\\usepackage{xcolor}'],
        'xstring'        : ['\\usepackage{xstring}'],
    }

    def build_latex_test(bld):
        def create_tex(test_data):
            return os.linesep.join(latex_test_preamble +
                                   test_data +
                                   latex_test_postamble)
        def write_tex_test(tsk):
            tex = create_tex(tsk.env.TEST_DATA)
            tsk.outputs[0].write(tex)

        test = bld.kw['tex_test']
        bld.env.TEST = test
        bld.env.TEST_DATA = latex_tests[test]

        bld.to_log('%s.tex %s' % (test, '=' * (40 - len(test) + 5)))
        bld.to_log(create_tex(latex_tests[test]))
        bld.to_log('=' * 40)

        bld(rule = write_tex_test, target = 'main.tex')
        bld(features = 'tex', type = 'pdflatex', source = 'main.tex', prompt = 0)

    fails = 0
    for t in sorted(latex_tests.keys()):
        r = conf.test(build_fun = build_latex_test,
                      msg = "Checking for Tex package '%s'" % (t),
                      tex_test = t,
                      okmsg = 'ok',
                      errmsg = 'not found (please install)',
                      mandatory = False)
        if r is None:
            fails += 1
    if fails > 0:
        conf.fatal('There are %d Tex package failures. Please fix.' % (fails))

def cmd_configure(ctx):
    ctx.find_program("sphinx-build", var="BIN_SPHINX_BUILD", mandatory = True)
    ctx.find_program("aspell", var = "BIN_ASPELL", mandatory = False)

    ctx.start_msg("Checking if Sphinx is at least %s.%s" % sphinx_min_version)
    ver = check_sphinx_version(ctx, sphinx_min_version)
    ctx.end_msg("yes (%s)" % ".".join(map(str, ver)))

    ctx.start_msg("Sphinx Verbose: ")
    if 'SPHINX_VERBOSE' not in ctx.env:
        ctx.env.append_value('SPHINX_VERBOSE', ctx.options.sphinx_verbose)
    level = sphinx_verbose(ctx)
    if level == '-Q':
        level = 'quiet'
    ctx.end_msg(level)

    #
    # Optional builds.
    #
    ctx.env.BUILD_PDF = 'no'
    if ctx.options.pdf:
        check_tex = not ctx.env.PDFLATEX
        if check_tex:
            ctx.load('tex')
            if not ctx.env.PDFLATEX or not ctx.env.MAKEINDEX:
                ctx.fatal('The programs pdflatex and makeindex are required for PDF output')
            if 'PDFLATEXFLAGS' not in ctx.env or \
               '-shell-escape' not in ctx.env['PDFLATEXFLAGS']:
                ctx.env.append_value('PDFLATEXFLAGS', '-shell-escape')
            latex_configure_tests(ctx)
        else:
            ctx.msg('Check for Tex packages', 'skipping, already checked')
        ctx.env.BUILD_PDF = 'yes'

    ctx.envBUILD_SINGLEHTML = 'no'
    if ctx.options.singlehtml:
        ctx.env.BUILD_SINGLEHTML = 'yes'
        ctx.find_program("inliner", var = "BIN_INLINER", mandatory = False)
        if not ctx.env.BIN_INLINER:
            ctx.fatal("Node inliner is required install with 'npm install -g inliner' " +
                      "(https://github.com/remy/inliner)")

def doc_pdf(ctx, source_dir, conf_dir):
    buildtype = 'latex'
    build_dir, output_node, output_dir, doctrees = build_dir_setup(ctx, buildtype)
    rule = "${BIN_SPHINX_BUILD} %s -b %s -c %s -d %s %s %s" % \
           (sphinx_verbose(ctx), buildtype, conf_dir,
            doctrees, source_dir, output_dir)
    ctx(
        rule         = rule,
        cwd          = ctx.path,
        source       = ctx.path.ant_glob('**/*.rst'),
        target       = ctx.path.find_or_declare("%s/%s.tex" % (buildtype,
                                                               ctx.path.name))
    )
    ctx(
        features     = 'tex',
        cwd          = output_dir,
        type         = 'pdflatex',
        source       = "%s/%s.tex" % (buildtype, ctx.path.name),
        prompt       = 0
    )
    ctx.install_files('${PREFIX}',
                      '%s/%s.pdf' % (buildtype, ctx.path.name),
                      cwd = output_node,
                      quiet = True)

def doc_singlehtml(ctx, source_dir, conf_dir):

    #
    # Use a run command to handle stdout and stderr output from inliner. Using
    # a standard rule in the build context locks up.
    #
    def run(task):
        src = task.inputs[0].abspath()
        tgt = task.outputs[0].abspath()
        cmd = '%s %s' % (task.env.BIN_INLINER[0], src)
        so = open(tgt, 'w')
        se = open(tgt + '.err', 'w')
        r = task.exec_command(cmd, stdout = so, stderr = se)
        so.close()
        se.close()
        #
        # The inliner does not handle internal href's correctly and places the
        # input's file name in the href. Strip these.
        #
        with open(tgt, 'r') as i:
            before = i.read()
            after = before.replace('index.html', '')
        i.close()
        with open(tgt, 'w') as o:
            o.write(after)
        o.close()
        return r

    buildtype = 'singlehtml'
    build_dir, output_node, output_dir, doctrees = build_dir_setup(ctx, buildtype)
    html_resources(ctx, buildtype)
    rule = "${BIN_SPHINX_BUILD} %s -b %s -c %s -d %s %s %s" % \
           (sphinx_verbose(ctx), buildtype, conf_dir,
            doctrees, source_dir, output_dir)
    ctx(
        rule         = rule,
        cwd          = ctx.path,
        source       = ctx.path.ant_glob('**/*.rst'),
        target       = ctx.path.find_or_declare("%s/index.html" % (buildtype)),
        install_path = None
    )
    ctx(
        rule         = run,
        inliner      = ctx.env.BIN_INLINER,
        source       = "%s/index.html" % buildtype,
        target       = "%s/%s.html" % (buildtype, ctx.path.name),
        install_path = '${PREFIX}'
    )

def doc_html(ctx, conf_dir, source_dir):
    buildtype = 'html'
    build_dir, output_node, output_dir, doctrees = build_dir_setup(ctx, buildtype)
    html_resources(ctx, buildtype)
    rule = "${BIN_SPHINX_BUILD} %s -b %s -c %s -d %s %s %s" % \
           (sphinx_verbose(ctx), buildtype, conf_dir,
            doctrees, source_dir, output_dir)
    ctx(
        rule         = rule,
        cwd          = ctx.path,
        source       = ctx.path.ant_glob('**/*.rst'),
        target       = ctx.path.find_or_declare('%s/index.html' % buildtype),
        install_path = None
    )
    ctx.install_files('${PREFIX}/%s' % (ctx.path.name),
                      output_node.ant_glob('**/*', quiet = True),
                      cwd = output_node,
                      relative_trick = True,
                      quiet = True)

def cmd_build(ctx, conf_dir = ".", source_dir = "."):
    srcnode = ctx.srcnode.abspath()

    if ctx.env.BUILD_PDF == 'yes':
        doc_pdf(ctx, source_dir, conf_dir)

    if ctx.env.BUILD_SINGLEHTML == 'yes':
        doc_singlehtml(ctx, source_dir, conf_dir)

    doc_html(ctx, source_dir, conf_dir)

def cmd_options(ctx):
    ctx.add_option('--sphinx-verbose',
                   action = 'store',
                   default = "-Q",
                   help = "Sphinx verbose.")
    ctx.add_option('--pdf',
                   action='store_true',
                   default = False,
                   help = "Build PDF.")
    ctx.add_option('--singlehtml',
                   action='store_true',
                   default = False,
                   help = "Build Single HTML file, requires Node Inliner")

def cmd_options_path(ctx):
    cmd_options(ctx)
    ctx.add_option('--rtems-path-py',
                   type = 'string',
                   help = "Full path to py/ in RTEMS source repository.")

def cmd_configure_path(ctx):
    if not ctx.options.rtems_path_py:
        ctx.fatal("--rtems-path-py is required")

    ctx.env.RTEMS_PATH = ctx.options.rtems_path_py

    cmd_configure(ctx)


CONF_FRAG = """
sys.path.append(os.path.abspath('../../common/'))
sys.path.append('%s')
templates_path = ['_templates']
html_static_path = ['_static']
"""

# XXX: fix this ugly hack.  No time to waste on it.
def cmd_build_path(ctx):
    def run(task):

        with open("conf.py") as fp:
            conf = "import sys, os\nsys.path.append(os.path.abspath('../../common/'))\n"
            conf += fp.read()

        task.inputs[0].abspath()
        task.outputs[0].write(conf + (CONF_FRAG % ctx.env.RTEMS_PATH))

    ctx(
        rule   = run,
        source = [ctx.path.parent.find_node("common/conf.py"),
                  ctx.path.find_node("./conf.py")],
        target = ctx.path.get_bld().make_node('conf.py')
    )

    cmd_build(ctx, conf_dir = "build", source_dir = "build")