manpages: build manpages

This commit also imports manpages of ndnpeek and ndnpoke from
NFD:commit:6ee6e07e6f1c068f7658a616982ab785f3bd8b22.

refs #2816

Change-Id: Ibc7bbbbd063af0107fa5883baccc12c7789bcc72
diff --git a/.waf-tools/sphinx_build.py b/.waf-tools/sphinx_build.py
new file mode 100644
index 0000000..e61da6e
--- /dev/null
+++ b/.waf-tools/sphinx_build.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+# inspired by code by Hans-Martin von Gaudecker, 2012
+
+import os
+from waflib import Node, Task, TaskGen, Errors, Logs, Build, Utils
+
+class sphinx_build(Task.Task):
+    color = 'BLUE'
+    run_str = '${SPHINX_BUILD} -D ${VERSION} -D ${RELEASE} -q -b ${BUILDERNAME} -d ${DOCTREEDIR} ${SRCDIR} ${OUTDIR}'
+
+    def __str__(self):
+        env = self.env
+        src_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.inputs])
+        tgt_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.outputs])
+        if self.outputs: sep = ' -> '
+        else: sep = ''
+        return'%s [%s]: %s%s%s\n'%(self.__class__.__name__.replace('_task',''),
+                                   self.env['BUILDERNAME'], src_str, sep, tgt_str)
+
+@TaskGen.extension('.py', '.rst')
+def sig_hook(self, node):
+    node.sig=Utils.h_file(node.abspath())
+
+@TaskGen.feature("sphinx")
+@TaskGen.before_method("process_source")
+def apply_sphinx(self):
+    """Set up the task generator with a Sphinx instance and create a task."""
+
+    inputs = []
+    for i in Utils.to_list(self.source):
+        if not isinstance(i, Node.Node):
+            node = self.path.find_node(node)
+        else:
+            node = i
+        if not node:
+            raise ValueError('[%s] file not found' % i)
+        inputs.append(node)
+
+    task = self.create_task('sphinx_build', inputs)
+
+    conf = self.path.find_node(self.config)
+    task.inputs.append(conf)
+
+    confdir = conf.parent.abspath()
+    buildername = getattr(self, "builder", "html")
+    srcdir = getattr(self, "srcdir", confdir)
+    outdir = self.path.find_or_declare(getattr(self, "outdir", buildername)).get_bld()
+    doctreedir = getattr(self, "doctreedir", os.path.join(outdir.abspath(), ".doctrees"))
+
+    task.env['BUILDERNAME'] = buildername
+    task.env['SRCDIR'] = srcdir
+    task.env['DOCTREEDIR'] = doctreedir
+    task.env['OUTDIR'] = outdir.abspath()
+    task.env['VERSION'] = "version=%s" % self.VERSION
+    task.env['RELEASE'] = "release=%s" % self.VERSION
+
+    import imp
+    confData = imp.load_source('sphinx_conf', conf.abspath())
+
+    if buildername == "man":
+        for i in confData.man_pages:
+            target = outdir.find_or_declare('%s.%d' % (i[1], i[4]))
+            task.outputs.append(target)
+
+            if self.install_path:
+                self.bld.install_files("%s/man%d/" % (self.install_path, i[4]), target)
+    else:
+        task.outputs.append(outdir)
+
+def configure(conf):
+    conf.find_program('sphinx-build', var='SPHINX_BUILD', mandatory=False)
+
+# sphinx docs
+from waflib.Build import BuildContext
+class sphinx(BuildContext):
+    cmd = "sphinx"
+    fun = "sphinx"
diff --git a/manpages/conf.py b/manpages/conf.py
new file mode 100644
index 0000000..20709a9
--- /dev/null
+++ b/manpages/conf.py
@@ -0,0 +1,11 @@
+# General information about the project.
+project = u'NDN Essential Tools'
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('ndnpeek', 'ndnpeek', 'simple consumer to send one Interest and expect one Data', None, 1),
+    ('ndnpoke', 'ndnpoke', 'simple producer to publish one Data', None, 1),
+]
diff --git a/manpages/ndnpeek.rst b/manpages/ndnpeek.rst
new file mode 100644
index 0000000..1f36263
--- /dev/null
+++ b/manpages/ndnpeek.rst
@@ -0,0 +1,56 @@
+ndnpeek
+=======
+
+Usage
+-----
+
+::
+
+    ndnpeek [-h] [-f] [-r] [-m min] [-M max] [-l lifetime] [-p] [-w timeout] name
+
+Description
+-----------
+
+``ndnpeek`` is a simple consumer program that sends one Interest and expects one Data
+packet in response.  The full Data packet (in TLV format) is written to stdout.  The
+program terminates with return code 0 if Data arrives, and with return code 1 when timeout
+occurs.
+
+``name`` is interpreted as the Interest name.
+
+Options
+-------
+
+``-h``
+  Print help and exit
+
+``-f``
+  If specified, set ``MustBeFresh`` selector in the Interest packet.
+
+``-r``
+  Set ``ChildSelector=1`` (the rightmost child) selector.
+
+``-m``
+  Set ``min`` as the ``MinSuffixComponents`` selector.
+
+``-M``
+  Set ``max`` as the ``MaxSuffixComponents`` selector.
+
+``-l``
+  Set ``lifetime`` (in milliseconds) as the ``InterestLifetime``.
+
+``-p``
+  If specified, print the received payload only, not the full packet.
+
+``-w``
+  Timeout after ``timeout`` milliseconds.
+
+
+Examples
+--------
+
+Send Interest for ``ndn:/app1/video`` and print the received payload only
+
+::
+
+    ndnpeek -p ndn:/app1/video
diff --git a/manpages/ndnpoke.rst b/manpages/ndnpoke.rst
new file mode 100644
index 0000000..c4c0a47
--- /dev/null
+++ b/manpages/ndnpoke.rst
@@ -0,0 +1,54 @@
+ndnpoke
+=======
+
+Usage
+-----
+
+::
+
+    ndnpoke [-h] [-f] [-D] [-i identity] [-F] [-x freshness] [-w timeout] name
+
+Description
+-----------
+
+``ndnpoke`` is a simple producer program that reads payload from stdin and publishes it
+as a single NDN Data packet.  The Data packet is published either as a response to the
+incoming Interest for the given ``name``, or forcefully pushed to the local NDN
+forwarder's cache if ``-f`` flag is specified.
+
+The program terminates with return code 0 if Data is sent and with return code 1 when
+timeout occurs.
+
+Options
+-------
+
+``-h``
+  Print usage and exit.
+
+``-f``
+  If specified, send Data without waiting for Interest.
+
+``-D``
+  If specified, use ``DigestSha256`` signature instead of default ``SignatureSha256WithRsa``.
+
+``-i``
+  Use ``identity`` to sign the created Data packet.
+
+``-F``
+  Set ``FinalBlockId`` to the last component of specified name.
+
+``-x``
+  Set ``FreshnessPeriod`` in milliseconds.
+
+``-w``
+  Wait at most ``timeout`` milliseconds for the incoming Interest.  If no Interest arrives
+  within the ``timeout``, the Data packet will not be published.
+
+
+Examples
+--------
+
+Create Data packet with content ``hello`` with the name ``ndn:/app/video`` and wait at
+most 3 seconds for the incoming Interest for it::
+
+    echo "Hello" | build/bin/ndnpoke -w 3000 ndn:/app/video
diff --git a/manpages/wscript b/manpages/wscript
new file mode 100644
index 0000000..e1093a4
--- /dev/null
+++ b/manpages/wscript
@@ -0,0 +1,10 @@
+def build(bld):
+    if not bld.env['SPHINX_BUILD']:
+        return
+    bld(features='sphinx',
+        builder='man',
+        outdir='.',
+        config='./conf.py',
+        source=bld.path.ant_glob('*.rst'),
+        install_path='${MANDIR}/',
+        VERSION=bld.env['VERSION'])
diff --git a/wscript b/wscript
index e846466..900bc17 100644
--- a/wscript
+++ b/wscript
@@ -2,15 +2,15 @@
 import os
 
 VERSION='0.1'
-APPNAME="ndn-tools"
+APPNAME='ndn-tools'
 
 def options(opt):
     opt.load(['compiler_cxx', 'gnu_dirs'])
-    opt.load(['default-compiler-flags'], tooldir=['.waf-tools'])
+    opt.load(['default-compiler-flags', 'sphinx_build'], tooldir=['.waf-tools'])
 
 def configure(conf):
     conf.load(['compiler_cxx', 'gnu_dirs',
-               'default-compiler-flags'])
+               'default-compiler-flags', 'sphinx_build'])
 
     if not os.environ.has_key('PKG_CONFIG_PATH'):
         os.environ['PKG_CONFIG_PATH'] = ':'.join([
@@ -23,6 +23,8 @@
     conf.recurse('tools')
 
 def build(bld):
+    bld.env['VERSION'] = VERSION
+
     bld(
         target='core-objects',
         name='core-objects',
@@ -33,3 +35,4 @@
         )
 
     bld.recurse('tools')
+    bld.recurse('manpages')