WIP waf-manpages: Rework manpages generation with waf
authorMatthieu Patou <mat@matws.net>
Mon, 22 Oct 2012 04:15:19 +0000 (21:15 -0700)
committerStefan Metzmacher <metze@samba.org>
Tue, 16 Jan 2018 06:47:01 +0000 (07:47 +0100)
We now use features for generating the file with the includes for the
smb.conf.5 and also each xsltproc is split in a separate task.

The parameters.all.xml is now a dependency of smb.conf.5 but didn't
appear on the command line (it's listed both in source and dependencies
so source get cleaned before running)

This based on the work of Alexander Bokovoy (ab@samba.org)

buildtools/wafsamba/samba_manpages.py [new file with mode: 0644]

diff --git a/buildtools/wafsamba/samba_manpages.py b/buildtools/wafsamba/samba_manpages.py
new file mode 100644 (file)
index 0000000..95c20f1
--- /dev/null
@@ -0,0 +1,253 @@
+import Build, Utils, Task
+from TaskGen import feature, before, after
+import os
+from samba_utils import save_file
+
+# define the gen_xincludes feature that is used to generate a xml file with
+# includes pointing to file to be merge during the xslt processing
+@feature('gen_xincludes')
+@before('apply_core')
+def create_xinclude(self):
+    tree = self.bld
+    output = Utils.to_list(getattr(self, 'output', []))
+    node_out = self.path.find_or_declare(output[0])
+    node_lst = []
+
+    self.source = self.to_list(self.source)
+
+    for filename in self.source:
+        base, ext = os.path.splitext(filename)
+
+        node = self.path.find_resource(filename)
+        if not node: raise Utils.WafError('cannot find %s' % filename)
+        node_lst.append(node)
+
+    self.create_task('do_generate_xinclude', node_lst, node_out)
+    self.source = []
+
+class do_generate_xinclude(Task.Task):
+    """
+    Generate an xml file with a separate xi:include for each article
+    contained in the inputs
+    """
+    color = 'PINK'
+    quiet = True
+    name = 'do_generate_xinclude'
+
+    def run(self):
+        output = self.outputs[0].bldpath(self.env)
+        articles = self.inputs
+
+        t = '<section xmlns:xi="http://www.w3.org/2003/XInclude">\n'
+        for article in articles:
+            t += "<xi:include href='file://" + article.abspath(self.env) + "' parse='xml'/>\n"
+        t += "</section>\n"
+        save_file(output, t , create_dir=True)
+        return 0
+
+# define the manpage feature that is used to generate a manpage
+# out of an xml file, this feature expect that their will be one
+# input file and one output file.
+# This feature can generate multiple kind of manpages.
+# For the moment only 2 different kind are possible:
+# * samba, used for pure samba things (commands, config, ...)
+# * standard, usually used in support libraries (talloc, tdb, ldb, ...)
+@feature('manpage')
+@before('apply_core')
+def gen_manpage(self):
+    output = Utils.to_list(getattr(self, 'output', []))
+    node_out = self.path.find_or_declare(output[0])
+    node_lst = []
+
+    self.source = self.to_list(self.source)
+    for filename in self.source:
+        base, ext = os.path.splitext(filename)
+
+        node = self.path.find_resource(filename)
+        if not node: raise Utils.WafError('cannot find %s' % filename)
+        node_lst.append(node)
+
+    page_type = getattr(self, 'manpage_type', 'standard')
+    if page_type == 'standard':
+        self.create_task('generate_manpage', src=node_lst, tgt=node_out)
+    else:
+        self.gen_manpage = self.create_task('generate_samba_manpage',
+                                            src=node_lst, tgt=node_out)
+
+    # This is important otherwise waf complain about not being able to
+    # handle xml files
+    self.source = []
+
+class generate_manpage(Task.Task):
+    """
+    Generate man page out of XML source with the same name
+    using standard xml to manpage XSL
+    """
+    color = 'PINK'
+    name = 'generate_manpage'
+
+    def run(self):
+        bld = self.generator.bld
+        bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
+
+        function = getattr(self, 'function', None)
+        if not function:
+            (function,line) = Task.compile_fun('xsltproc-man',
+                                               '''${XSLTPROC} --xinclude -o ${TGT} --nonet ${MAN_XSL} ${SRC}''',
+                                               shell=True)
+            function.code = line
+            self.function = function
+        function(self)
+
+class generate_samba_manpage(Task.Task):
+    """
+    Generate man page out of XML source with the same name
+    using samba's custom XSL and rules
+    """
+    color = 'PINK'
+    name = 'generate_samba_manpage'
+
+    def run(self):
+        bld = self.generator.bld
+        self.env.SAMBA_MAN_XSL =  bld.srcnode.abspath() + '/docs-xml/xslt/man.xsl'
+        SAMBA_CATALOGS = 'file:///etc/xml/catalog file://' + bld.srcnode.abspath() + '/bin/default/docs-xml/build/catalog.xml'
+        self.env['env'] = {'XML_CATALOG_FILES': SAMBA_CATALOGS}
+
+        function = getattr(self, 'function', None)
+        if not function:
+            (function,line) = Task.compile_fun('xsltproc-man',
+                                               '''${XSLTPROC} --nonet -o ${TGT} ${SAMBA_MAN_XSL} ${SRC}''',
+                                               shell=True)
+            function.code = line
+            self.function = function
+        function(self)
+
+# It might sound strange but we want to operate after the gen_manpage has
+# created its tasks for something that will actually generate a step before
+# the task associated with gen_manage but we need it in order to be able to
+# modify the inputs of this task.
+@feature('manpage')
+@after('gen_manpage')
+def preprocess_manpages_sources(self):
+    """
+    Add a preproccess task for manpages that requires it
+    """
+    if getattr(self, 'manpage_preprocess', False):
+        # Insert a task only if needed
+        # But it seems so far that all samba's manpage require preprocess
+        deps = getattr(self, 'manpage_deps', [])
+
+        o = self.gen_manpage.outputs[0]
+        s = Utils.to_list(self.gen_manpage.inputs)
+        p = o.path_to_parent(self.path)
+
+        deps_nodes = []
+        for d in Utils.to_list(deps):
+            n = self.path.find_or_declare(d)
+            deps_nodes.append(n)
+            s.append(n)
+
+        preproc_node_out = self.path.find_or_declare("%s_preproc.xml" % p)
+
+        t = self.create_task('preproc_samba_manpage',
+                             src=s, tgt=preproc_node_out)
+        t.deps = deps_nodes
+        self.gen_manpage.inputs = [preproc_node_out]
+        # Important if we don't want to have complains about missing signature
+        self.gen_manpage.set_run_after(t)
+
+class preproc_samba_manpage(Task.Task):
+    """
+    Preprocess manpage's xml source
+    """
+    color = 'PINK'
+    name = 'preproc_samba_manpage'
+
+    def run(self):
+        bld = self.generator.bld
+        self.env.SAMBA_EXPAND_XSL = bld.srcnode.abspath() + '/docs-xml/xslt/expand-sambadoc.xsl'
+        # TODO is it still needed for this step ?
+        SAMBA_CATALOGS = 'file:///etc/xml/catalog file://' + bld.srcnode.abspath() + '/bin/default/docs-xml/build/catalog.xml'
+        self.env['env'] = {'XML_CATALOG_FILES': SAMBA_CATALOGS}
+
+        # We remove from our source the deps that were stored in the 
+        # deps attribute of this task
+        deps = Utils.to_list(getattr(self, 'deps', []))
+        inputs = []
+        for i in getattr(self, 'inputs', []):
+            if i not in deps:
+                inputs.append(i)
+
+        self.inputs = inputs
+
+        preproc_function = getattr(self, 'preproc_function', None)
+        if not preproc_function:
+            (f,line) = Task.compile_fun('xsltproc-preprocman',
+                                        '''${XSLTPROC} --xinclude --stringparam noreference 0 -o ${TGT} --nonet ${SAMBA_EXPAND_XSL} ${SRC}''',
+                                        shell=True)
+            f.code = line
+            self.preproc_function = f
+            preproc_function = f
+        preproc_function(self)
+
+def SMBDOTCONF_MANPAGE(bld, target):
+    """
+    assemble and build smb.conf.5 manual page
+    """
+
+    articles = bld.path.ant_glob("smbdotconf/**/*.xml", excl="parameters*.xml")
+    parameter_all = 'smbdotconf/parameters.all.xml'
+    bld.set_group('generators')
+    bld(name=parameter_all,
+        features='gen_xincludes',
+        output=parameter_all,
+        source=articles)
+
+    bld.set_group('final')
+    param_obj = bld.name_to_obj(parameter_all, bld.env)
+    t = bld(name=target,
+            features='manpage',
+            manpage_type = 'samba',
+            manpage_deps = parameter_all,
+            manpage_preprocess = True,
+            source=' %s.xml' % target,
+            output=target)
+    bld.INSTALL_FILES('${MANDIR}/man%s' % target[-1], target, flat=True)
+
+# So that we can do bld.SMBDOTCONF_MANPAGE(...)
+Build.BuildContext.SMBDOTCONF_MANPAGE = SMBDOTCONF_MANPAGE
+
+def MANPAGE(bld, page):
+    """
+    build and install standard manual pages
+    """
+
+    source = page + '.xml'
+    bld.set_group('final')
+    bld(name=page,
+        features='manpage',
+        source=source,
+        manpage_type = 'standard',
+        output=page)
+    bld.INSTALL_FILES('${MANDIR}/man%s' % page[-1], page, flat=True)
+# So that we can do bld.MANPAGE(...)
+Build.BuildContext.MANPAGE = MANPAGE
+
+def SAMBAMANPAGE(bld, page, **kw):
+    """
+    build and install samba styled manual pages
+    """
+
+    source = page + '.xml'
+    bld.set_group('final')
+    bld(name=page,
+        features='manpage',
+        manpage_preprocess = True,
+        source=source,
+        manpage_type = 'samba',
+        output=page)
+    bld.INSTALL_FILES('${MANDIR}/man%s' % page[-1], page, flat=True)
+
+# So that we can do bld.SAMBAMANPAGE(...)
+Build.BuildContext.SAMBAMANPAGE = SAMBAMANPAGE
+