build: derive version string at build-time

refs #2749

Change-Id: Id1d164ff4b1fd5c7e9440a241ab299661ed933c4
diff --git a/core/version.cpp b/core/version.cpp.in
similarity index 88%
rename from core/version.cpp
rename to core/version.cpp.in
index b007102..7c44aac 100644
--- a/core/version.cpp
+++ b/core/version.cpp.in
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016,  Arizona Board of Regents.
+ * Copyright (c) 2014-2017,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -17,12 +17,12 @@
  * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "version.hpp"
+#include "core/version.hpp"
 
 namespace ndn {
 namespace tools {
 
-const char VERSION[] = "0.3";
+const char VERSION[] = "@VERSION_BUILD@";
 
 } // namespace tools
 } // namespace ndn
diff --git a/wscript b/wscript
index 37259da..07a7524 100644
--- a/wscript
+++ b/wscript
@@ -2,8 +2,9 @@
 
 VERSION = '0.3'
 APPNAME = 'ndn-tools'
+GIT_TAG_PREFIX = 'ndn-tools-'
 
-from waflib import Utils
+from waflib import Utils, Context
 import os
 
 def options(opt):
@@ -37,15 +38,66 @@
     conf.load('sanitizers')
 
 def build(bld):
-    bld.env['VERSION'] = VERSION
+    version(bld)
+
+    bld(features='subst',
+        source='core/version.cpp.in',
+        target='core/version.cpp',
+        VERSION_BUILD=VERSION)
 
     bld(target='core-objects',
         name='core-objects',
         features='cxx',
-        source=bld.path.ant_glob(['core/*.cpp']),
+        source=bld.path.ant_glob(['core/*.cpp']) + ['core/version.cpp'],
         use='NDN_CXX BOOST',
+        includes='.',
         export_includes='.')
 
     bld.recurse('tools')
     bld.recurse('tests')
     bld.recurse('manpages')
+
+def version(bld):
+    # Modified from ndn-cxx wscript
+    try:
+        cmd = ['git', 'describe', '--always', '--match', '%s*' % GIT_TAG_PREFIX]
+        p = Utils.subprocess.Popen(cmd, stdout=Utils.subprocess.PIPE,
+                                   stderr=None, stdin=None)
+        out = str(p.communicate()[0].strip())
+        didGetVersion = (p.returncode == 0 and out != "")
+        if didGetVersion:
+            if out.startswith(GIT_TAG_PREFIX):
+                Context.g_module.VERSION = out[len(GIT_TAG_PREFIX):]
+            else:
+                Context.g_module.VERSION = "%s-commit-%s" % (Context.g_module.VERSION, out)
+    except OSError:
+        pass
+
+    versionFile = bld.path.find_node('VERSION')
+
+    if not didGetVersion and versionFile is not None:
+        try:
+            Context.g_module.VERSION = versionFile.read()
+            return
+        except (OSError, IOError):
+            pass
+
+    # version was obtained from git, update VERSION file if necessary
+    if versionFile is not None:
+        try:
+            version = versionFile.read()
+            if version == Context.g_module.VERSION:
+                return # no need to update
+        except (OSError, IOError):
+            Logs.warn("VERSION file exists, but not readable")
+    else:
+        versionFile = bld.path.make_node('VERSION')
+
+    # neither git describe nor VERSION file contain the version, so fall back to constant in wscript
+    if versionFile is None:
+        Context.g_module.VERSION = VERSION
+
+    try:
+        versionFile.write(Context.g_module.VERSION)
+    except (OSError, IOError):
+        Logs.warn("VERSION file is not writeable")