build+core: Add printing of version number in daemons and tools

This commit also changes how version number is handled.  Version is now
fully controlled from top-level wscript.  In addition to that, a new
NFD_VERSION_BUILD_STRING macro is set to include more detailed
information, including commit ID (e.g., "0.1.0-rc1-1-g5c86570").

Change-Id: I448eb627e0c42dc814de1107cf7bb0dc94fa2a89
Refs: #1575
diff --git a/daemon/main.cpp b/daemon/main.cpp
index d647936..ebea105 100644
--- a/daemon/main.cpp
+++ b/daemon/main.cpp
@@ -25,6 +25,7 @@
 #include <getopt.h>
 #include <boost/filesystem.hpp>
 
+#include "version.hpp"
 #include "core/logger.hpp"
 #include "core/global-io.hpp"
 #include "fw/forwarder.hpp"
@@ -42,6 +43,7 @@
 struct ProgramOptions
 {
   bool showUsage;
+  bool showVersion;
   bool showModules;
   std::string config;
 };
@@ -140,7 +142,8 @@
        << "Run NFD forwarding daemon\n"
        << "\n"
        << "Options:\n"
-       << "  [--help]   - print this help message\n"
+       << "  [--help]    - print this help message\n"
+       << "  [--version] - print version and exit\n"
        << "  [--modules] - list available logging modules\n"
        << "  [--config /path/to/nfd.conf] - path to configuration file "
        << "(default: " << DEFAULT_CONFIG_FILE << ")\n"
@@ -165,6 +168,7 @@
   parseCommandLine(int argc, char** argv, ProgramOptions& options)
   {
     options.showUsage = false;
+    options.showVersion = false;
     options.showModules = false;
     options.config = DEFAULT_CONFIG_FILE;
 
@@ -174,6 +178,7 @@
         { "help"   , no_argument      , 0, 0 },
         { "modules", no_argument      , 0, 0 },
         { "config" , required_argument, 0, 0 },
+        { "version", no_argument      , 0, 0 },
         { 0        , 0                , 0, 0 }
       };
       int c = getopt_long_only(argc, argv, "", longOptions, &optionIndex);
@@ -181,21 +186,24 @@
         break;
 
       switch (c) {
-        case 0:
-          switch (optionIndex) {
-            case 0: // help
-              options.showUsage = true;
-              break;
-            case 1: // modules
-              options.showModules = true;
-              break;
-            case 2: // config
-              options.config = ::optarg;
-              break;
-            default:
-              return false;
-          }
+      case 0:
+        switch (optionIndex) {
+        case 0: // help
+          options.showUsage = true;
           break;
+        case 1: // modules
+          options.showModules = true;
+          break;
+        case 2: // config
+          options.config = ::optarg;
+          break;
+        case 3: // version
+          options.showVersion = true;
+          break;
+        default:
+          return false;
+        }
+        break;
       }
     }
     return true;
@@ -246,11 +254,17 @@
     Nfd::printUsage(std::cerr, argv[0]);
     return 1;
   }
+
   if (options.showUsage) {
     Nfd::printUsage(std::cout, argv[0]);
     return 0;
   }
 
+  if (options.showVersion) {
+    std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+    return 0;
+  }
+
   if (options.showModules) {
     Nfd::printModules(std::cout);
     return 0;
diff --git a/rib/main.cpp b/rib/main.cpp
index 1593784..d74ed8d 100644
--- a/rib/main.cpp
+++ b/rib/main.cpp
@@ -25,6 +25,7 @@
 
 #include <getopt.h>
 
+#include "version.hpp"
 #include "common.hpp"
 #include "rib-manager.hpp"
 #include "core/config-file.hpp"
@@ -39,6 +40,7 @@
 struct ProgramOptions
 {
   bool showUsage;
+  bool showVersion;
   bool showModules;
   std::string config;
 };
@@ -107,7 +109,9 @@
        << "Run NRD daemon\n"
        << "\n"
        << "Options:\n"
-       << "  [--help]   - print this help message\n"
+       << "  [--help]    - print this help message\n"
+       << "  [--version] - print version and exit\n"
+       << "  [--modules] - list available logging modules\n"
        << "  [--config /path/to/nfd.conf] - path to configuration file "
        << "(default: " << DEFAULT_CONFIG_FILE << ")\n"
       ;
@@ -131,6 +135,7 @@
   parseCommandLine(int argc, char** argv, ProgramOptions& options)
   {
     options.showUsage = false;
+    options.showVersion = false;
     options.showModules = false;
     options.config = DEFAULT_CONFIG_FILE;
 
@@ -140,6 +145,7 @@
         { "help"   , no_argument      , 0, 0 },
         { "modules", no_argument      , 0, 0 },
         { "config" , required_argument, 0, 0 },
+        { "version", no_argument      , 0, 0 },
         { 0        , 0                , 0, 0 }
       };
       int c = getopt_long_only(argc, argv, "", longOptions, &optionIndex);
@@ -147,21 +153,24 @@
         break;
 
       switch (c) {
-        case 0:
-          switch (optionIndex) {
-            case 0: // help
-              options.showUsage = true;
-              break;
-            case 1: // modules
-              options.showModules = true;
-              break;
-            case 2: // config
-              options.config = ::optarg;
-              break;
-            default:
-              return false;
-          }
+      case 0:
+        switch (optionIndex) {
+        case 0: // help
+          options.showUsage = true;
           break;
+        case 1: // modules
+          options.showModules = true;
+          break;
+        case 2: // config
+          options.config = ::optarg;
+          break;
+        case 3: // version
+          options.showVersion = true;
+          break;
+        default:
+          return false;
+        }
+        break;
       }
     }
     return true;
@@ -218,6 +227,11 @@
     return 0;
   }
 
+  if (options.showVersion) {
+    std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+    return 0;
+  }
+
   Nrd nrdInstance;
 
   try {
diff --git a/tools/ndn-autoconfig-server.cpp b/tools/ndn-autoconfig-server.cpp
index abcdf5c..9873bde 100644
--- a/tools/ndn-autoconfig-server.cpp
+++ b/tools/ndn-autoconfig-server.cpp
@@ -22,6 +22,7 @@
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  **/
 
+#include "version.hpp"
 #include <ndn-cxx/face.hpp>
 #include <ndn-cxx/security/key-chain.hpp>
 
@@ -29,11 +30,12 @@
 void
 usage(const char* programName)
 {
-  std::cout << "Usage:\n" << programName  << " [-h] Uri \n"
-  "   -h print usage and exit\n"
-  "\n"
-  "   Uri - a FaceMgmt URI\n"
-  << std::endl;
+  std::cout << "Usage:\n" << programName  << " [-h] [-V] Uri \n"
+            << "   -h  - print usage and exit\n"
+            << "   -V  - print version number and exit\n"
+            << "\n"
+            << "   Uri - a FaceMgmt URI\n"
+            << std::endl;
 }
 
 using namespace ndn;
@@ -92,35 +94,33 @@
   int opt;
   const char* programName = argv[0];
 
-  while ((opt = getopt(argc, argv, "h")) != -1)
-  {
-    switch (opt)
-    {
-      case 'h':
-        usage(programName);
-        return 0;
-
-      default:
-        usage(programName);
-        return 1;
+  while ((opt = getopt(argc, argv, "hV")) != -1) {
+    switch (opt) {
+    case 'h':
+      usage(programName);
+      return 0;
+    case 'V':
+      std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+      return 0;
+    default:
+      usage(programName);
+      return 1;
     }
   }
 
-  if (argc != optind + 1)
-  {
+  if (argc != optind + 1) {
     usage(programName);
     return 1;
   }
-  // get the configured face managment uri
+  // get the configured face management uri
   NdnAutoconfigServer producer(argv[optind]);
 
-  try
-  {
+  try {
     producer.listen();
   }
-  catch (std::exception& error)
-  {
+  catch (const std::exception& error) {
     std::cerr << "ERROR: " << error.what() << std::endl;
+    return 1;
   }
   return 0;
 }
diff --git a/tools/ndn-autoconfig.cpp b/tools/ndn-autoconfig.cpp
index 84a73d3..fe8ce19 100644
--- a/tools/ndn-autoconfig.cpp
+++ b/tools/ndn-autoconfig.cpp
@@ -22,6 +22,8 @@
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  **/
 
+#include "version.hpp"
+
 #include <ndn-cxx/face.hpp>
 #include <ndn-cxx/management/nfd-controller.hpp>
 #include <ndn-cxx/security/key-chain.hpp>
@@ -37,6 +39,15 @@
 
 namespace tools {
 
+void
+usage(const char* programName)
+{
+  std::cout << "Usage:\n" << programName  << " [-h] [-V]\n"
+            << "   -h  - print usage and exit\n"
+            << "   -V  - print version number and exit\n"
+            << std::endl;
+}
+
 class NdnAutoconfig
 {
 public:
@@ -311,17 +322,30 @@
 } // namespace tools
 
 int
-main()
+main(int argc, char** argv)
 {
-  try
-    {
-      tools::NdnAutoconfig autoConfigInstance;
+  int opt;
+  const char* programName = argv[0];
 
-      autoConfigInstance.discoverHubStage1();
+  while ((opt = getopt(argc, argv, "hV")) != -1) {
+    switch (opt) {
+    case 'h':
+      tools::usage(programName);
+      return 0;
+    case 'V':
+      std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+      return 0;
     }
-  catch (const std::exception& error)
-    {
-      std::cerr << "ERROR: " << error.what() << std::endl;
-    }
+  }
+
+  try {
+    tools::NdnAutoconfig autoConfigInstance;
+
+    autoConfigInstance.discoverHubStage1();
+  }
+  catch (const std::exception& error) {
+    std::cerr << "ERROR: " << error.what() << std::endl;
+    return 1;
+  }
   return 0;
 }
diff --git a/tools/ndn-tlv-peek.cpp b/tools/ndn-tlv-peek.cpp
index ee0436f..ec0a9b0 100644
--- a/tools/ndn-tlv-peek.cpp
+++ b/tools/ndn-tlv-peek.cpp
@@ -25,6 +25,8 @@
  * @author Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
  */
 
+#include "version.hpp"
+
 #include <boost/asio.hpp>
 
 #include <ndn-cxx/face.hpp>
@@ -63,7 +65,9 @@
       "   [-l lifetime] - set InterestLifetime in time::milliseconds\n"
       "   [-p]          - print payload only, not full packet\n"
       "   [-w timeout]  - set Timeout in time::milliseconds\n"
-      "   [-h]          - print help and exit\n\n";
+      "   [-h]          - print help and exit\n"
+      "   [-V]          - print version and exit\n"
+      "\n";
     exit(1);
   }
 
@@ -228,37 +232,40 @@
 {
   int option;
   ndntlvpeek::NdnTlvPeek ndnTlvPeek (argv[0]);
-  while ((option = getopt(argc, argv, "hfrm:M:l:pw:")) != -1)
-    {
-      switch (option) {
-        case 'h':
-          ndnTlvPeek.usage();
-          break;
-        case 'f':
-          ndnTlvPeek.setMustBeFresh();
-          break;
-        case 'r':
-          ndnTlvPeek.setRightmostChildSelector();
-          break;
-        case 'm':
-          ndnTlvPeek.setMinSuffixComponents(atoi(optarg));
-          break;
-        case 'M':
-          ndnTlvPeek.setMaxSuffixComponents(atoi(optarg));
-          break;
-        case 'l':
-          ndnTlvPeek.setInterestLifetime(atoi(optarg));
-          break;
-        case 'p':
-          ndnTlvPeek.setPayloadOnly();
-          break;
-        case 'w':
-          ndnTlvPeek.setTimeout(atoi(optarg));
-          break;
-        default:
-          ndnTlvPeek.usage();
-      }
+  while ((option = getopt(argc, argv, "hfrm:M:l:pw:V")) != -1) {
+    switch (option) {
+    case 'h':
+      ndnTlvPeek.usage();
+      break;
+    case 'f':
+      ndnTlvPeek.setMustBeFresh();
+      break;
+    case 'r':
+      ndnTlvPeek.setRightmostChildSelector();
+      break;
+    case 'm':
+      ndnTlvPeek.setMinSuffixComponents(atoi(optarg));
+      break;
+    case 'M':
+      ndnTlvPeek.setMaxSuffixComponents(atoi(optarg));
+      break;
+    case 'l':
+      ndnTlvPeek.setInterestLifetime(atoi(optarg));
+      break;
+    case 'p':
+      ndnTlvPeek.setPayloadOnly();
+      break;
+    case 'w':
+      ndnTlvPeek.setTimeout(atoi(optarg));
+      break;
+    case 'V':
+      std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+      return 0;
+    default:
+      ndnTlvPeek.usage();
+      break;
     }
+  }
 
   argc -= optind;
   argv += optind;
diff --git a/tools/ndn-tlv-poke.cpp b/tools/ndn-tlv-poke.cpp
index 5ff5a50..f90b735 100644
--- a/tools/ndn-tlv-poke.cpp
+++ b/tools/ndn-tlv-poke.cpp
@@ -25,6 +25,8 @@
  * @author Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
  */
 
+#include "version.hpp"
+
 #include <boost/utility.hpp>
 
 #include <ndn-cxx/face.hpp>
@@ -63,7 +65,9 @@
       "   [-F]          - set FinalBlockId to the last component of Name\n"
       "   [-x]          - set FreshnessPeriod in time::milliseconds\n"
       "   [-w timeout]  - set Timeout in time::milliseconds\n"
-      "   [-h]          - print help and exit\n\n";
+      "   [-h]          - print help and exit\n"
+      "   [-V]          - print version and exit\n"
+      "\n";
     exit(1);
   }
 
@@ -228,35 +232,37 @@
 {
   int option;
   ndntlvpoke::NdnTlvPoke ndnTlvPoke(argv[0]);
-  while ((option = getopt(argc, argv, "hfDi:Fx:w:")) != -1)
-    {
-      switch (option) {
-        case 'h':
-          ndnTlvPoke.usage();
-          break;
-        case 'f':
-          ndnTlvPoke.setForceData();
-          break;
-        case 'D':
-          ndnTlvPoke.setUseDigestSha256();
-          break;
-        case 'i':
-          ndnTlvPoke.setIdentityName(optarg);
-          break;
-        case 'F':
-          ndnTlvPoke.setLastAsFinalBlockId();
-          break;
-        case 'x':
-          ndnTlvPoke.setFreshnessPeriod(atoi(optarg));
-          break;
-        case 'w':
-          ndnTlvPoke.setTimeout(atoi(optarg));
-          break;
-        default:
-          ndnTlvPoke.usage();
-          break;
-      }
+  while ((option = getopt(argc, argv, "hfDi:Fx:w:V")) != -1) {
+    switch (option) {
+    case 'h':
+      ndnTlvPoke.usage();
+      break;
+    case 'f':
+      ndnTlvPoke.setForceData();
+      break;
+    case 'D':
+      ndnTlvPoke.setUseDigestSha256();
+      break;
+    case 'i':
+      ndnTlvPoke.setIdentityName(optarg);
+      break;
+    case 'F':
+      ndnTlvPoke.setLastAsFinalBlockId();
+      break;
+    case 'x':
+      ndnTlvPoke.setFreshnessPeriod(atoi(optarg));
+      break;
+    case 'w':
+      ndnTlvPoke.setTimeout(atoi(optarg));
+      break;
+    case 'V':
+      std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+      return 0;
+    default:
+      ndnTlvPoke.usage();
+      break;
     }
+  }
 
   argc -= optind;
   argv += optind;
diff --git a/tools/nfd-autoreg.cpp b/tools/nfd-autoreg.cpp
index 1de90ab..40ea488 100644
--- a/tools/nfd-autoreg.cpp
+++ b/tools/nfd-autoreg.cpp
@@ -32,6 +32,7 @@
 #include <boost/program_options/variables_map.hpp>
 #include <boost/program_options/parsers.hpp>
 
+#include "version.hpp"
 #include "core/face-uri.hpp"
 #include "network.hpp"
 
@@ -248,6 +249,7 @@
        "Whitelisted network, e.g., 192.168.2.0/24 or ::1/128")
       ("blacklist,b", po::value<std::vector<Network> >(&m_blackList)->composing(),
        "Blacklisted network, e.g., 192.168.2.32/30 or ::1/128")
+      ("version,V", "show version and exit")
       ;
 
     po::variables_map options;
@@ -269,6 +271,12 @@
         return 0;
       }
 
+    if (options.count("version"))
+      {
+        std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+        return 0;
+      }
+
     if (m_autoregPrefixes.empty())
       {
         std::cerr << "ERROR: at least one --prefix must be specified" << std::endl << std::endl;
diff --git a/tools/nfd-start.sh b/tools/nfd-start.sh
index 9fe9424..4df12d7 100755
--- a/tools/nfd-start.sh
+++ b/tools/nfd-start.sh
@@ -1,5 +1,25 @@
 #!@BASH@
 
+VERSION="@VERSION@"
+
+case "$1" in
+  -h)
+    echo Usage
+    echo $0
+    echo "  Start NFD and RIB Management daemon"
+    exit 0
+    ;;
+  -V)
+    echo $VERSION
+    exit 0
+    ;;
+  "") ;; # do nothing
+  *)
+    echo "Unrecognized option $1"
+    exit 1
+    ;;
+esac
+
 hasProcess() {
   local processName=$1
 
diff --git a/tools/nfd-status-http-server.py b/tools/nfd-status-http-server.py
index 52acae6..6e0f167 100755
--- a/tools/nfd-status-http-server.py
+++ b/tools/nfd-status-http-server.py
@@ -164,8 +164,15 @@
                         help="Enable HTTP robots to crawl; disabled by default.")
     parser.add_argument("-v", default=False, dest="verbose", action="store_true",
                         help="Verbose mode.")
+    parser.add_argument("--version", default=False, dest="version", action="store_true",
+                        help="Show version and exit")
 
     args = vars(parser.parse_args())
+
+    if args['version']:
+        print "@VERSION@"
+        return
+
     localPort = args["port"]
     localAddr = args["addr"]
     verbose = args["verbose"]
diff --git a/tools/nfd-status.cpp b/tools/nfd-status.cpp
index 1c1f415..9e8fa28 100644
--- a/tools/nfd-status.cpp
+++ b/tools/nfd-status.cpp
@@ -25,6 +25,8 @@
  * @author Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
  */
 
+#include "version.hpp"
+
 #include <ndn-cxx/face.hpp>
 #include <ndn-cxx/name.hpp>
 #include <ndn-cxx/interest.hpp>
@@ -56,7 +58,10 @@
       "  [-h] - print this help message\n"
       "  [-v] - retrieve version information\n"
       "  [-f] - retrieve face status information\n"
-      "  [-b] - retrieve FIB information\n\n"
+      "  [-b] - retrieve FIB information\n"
+      "\n"
+      "  [-V] - show version information of nfd-status and exit\n"
+      "\n"
       "If no options are provided, all information is retrieved.\n"
       ;
   }
@@ -299,12 +304,12 @@
 
 }
 
-int main( int argc, char* argv[] )
+int main(int argc, char* argv[])
 {
   int option;
-  ndn::NfdStatus nfdStatus (argv[0]);
+  ndn::NfdStatus nfdStatus(argv[0]);
 
-  while ((option = getopt(argc, argv, "hvfb")) != -1) {
+  while ((option = getopt(argc, argv, "hvfbV")) != -1) {
     switch (option) {
     case 'h':
       nfdStatus.usage();
@@ -318,6 +323,9 @@
     case 'b':
       nfdStatus.enableFibEnumerationRetrieval();
       break;
+    case 'V':
+      std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+      return 0;
     default:
       nfdStatus.usage();
       return 1;
diff --git a/tools/nfd-stop.sh b/tools/nfd-stop.sh
index c0b46bd..ed55929 100755
--- a/tools/nfd-stop.sh
+++ b/tools/nfd-stop.sh
@@ -1,4 +1,24 @@
 #!@BASH@
 
+VERSION="@VERSION@"
+
+case "$1" in
+  -h)
+    echo Usage
+    echo $0
+    echo "  Stop NFD and RIB Management daemon"
+    exit 0
+    ;;
+  -V)
+    echo $VERSION
+    exit 0
+    ;;
+  "") ;; # do nothing
+  *)
+    echo "Unrecognized option $1"
+    exit 1
+    ;;
+esac
+
 sudo killall nrd
 sudo killall nfd
diff --git a/tools/nfdc.cpp b/tools/nfdc.cpp
index 729dcb8..1b3499b 100644
--- a/tools/nfdc.cpp
+++ b/tools/nfdc.cpp
@@ -22,7 +22,10 @@
  * You should have received a copy of the GNU General Public License along with
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  **/
+
 #include "nfdc.hpp"
+#include "version.hpp"
+
 #include <boost/lexical_cast.hpp>
 #include <boost/algorithm/string.hpp>
 #include <boost/algorithm/string/regex_find_format.hpp>
@@ -31,8 +34,9 @@
 void
 usage(const char* programName)
 {
-  std::cout << "Usage:\n" << programName  << " [-h] COMMAND [<Command Options>]\n"
+  std::cout << "Usage:\n" << programName  << " [-h] [-V] COMMAND [<Command Options>]\n"
     "       -h print usage and exit\n"
+    "       -V print version and exit\n"
     "\n"
     "   COMMAND can be one of the following:\n"
     "       register [-I] [-C] [-c cost] name <faceId | faceUri>\n"
@@ -402,6 +406,11 @@
     return 0;
   }
 
+  if (!strcmp(argv[1], "-V")) {
+    std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+    return 0;
+  }
+
   ::optind = 2; //start reading options from 2nd argument i.e. Command
   int opt;
   while ((opt = ::getopt(argc, argv, "ICc:")) != -1) {
diff --git a/version.hpp b/version.hpp.in
similarity index 68%
rename from version.hpp
rename to version.hpp.in
index 08dccaf..279962f 100644
--- a/version.hpp
+++ b/version.hpp.in
@@ -35,20 +35,36 @@
  *
  *  MAJOR*1000000 + MINOR*1000 + PATCH
  */
-#define NFD_VERSION 1000
+#define NFD_VERSION @VERSION@
 
 /** \brief NFD version represented as a string
  *
  *  MAJOR.MINOR.PATCH
  */
-#define NFD_VERSION_STRING "0.1.0"
+#define NFD_VERSION_STRING "@VERSION_STRING@"
+
+/** \brief NFD version string, including git commit information, if NFD is build from
+ *         specific git commit
+ *
+ * NFD_VERSION_BUILD_STRING is obtained using the following command (`NFD-` prefix is
+ * afterwards removed):
+ *
+ *    `git describe --match 'NFD-*'`
+ *
+ * When NFD is built not from git, NFD_VERSION_BUILD_STRING equals NFD_VERSION_STRING
+ *
+ * MAJOR.MINOR.PATCH(-release-candidate-tag)(-(number-of-commits-since-tag)-COMMIT-HASH)
+ *
+ * Example, 0.1.0-rc1-1-g5c86570
+ */
+#define NFD_VERSION_BUILD_STRING "@VERSION_BUILD@"
 
 /// MAJOR version
-#define NFD_VERSION_MAJOR (NFD_VERSION / 1000000)
+#define NFD_VERSION_MAJOR @VERSION_MAJOR@
 /// MINOR version
-#define NFD_VERSION_MINOR (NFD_VERSION % 1000000 / 1000)
+#define NFD_VERSION_MINOR @VERSION_MINOR@
 /// PATCH version
-#define NFD_VERSION_PATCH (NFD_VERSION % 1000)
+#define NFD_VERSION_PATCH @VERSION_PATCH@
 
 } // namespace nfd
 
diff --git a/wscript b/wscript
index 5b37759..2ba2dda 100644
--- a/wscript
+++ b/wscript
@@ -23,16 +23,12 @@
 NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
 """
 
-import re
-
-VERSION = re.search('^#define NFD_VERSION_STRING\\s+"(.*)"',
-                    open("version.hpp").read(), re.M).group(1)
+VERSION = "0.1.0"
 APPNAME = "nfd"
 BUGREPORT = "http://redmine.named-data.net/projects/nfd"
-URL = "https://github.com/named-data/NFD"
+URL = "http://named-data.net/doc/NFD/"
 
-from waflib import Logs
-import os
+from waflib import Logs, Utils, Context
 
 def options(opt):
     opt.load(['compiler_cxx', 'gnu_dirs'])
@@ -100,12 +96,29 @@
     conf.write_config_header('config.hpp')
 
 def build(bld):
+    version(bld)
+
+    bld(features="subst",
+        name='version',
+        source='version.hpp.in',
+        target='version.hpp',
+        install_path=None,
+        VERSION_STRING=VERSION_BASE,
+        VERSION_BUILD=VERSION,
+        VERSION=int(VERSION_SPLIT[0]) * 1000000 +
+                int(VERSION_SPLIT[1]) * 1000 +
+                int(VERSION_SPLIT[2]),
+        VERSION_MAJOR=VERSION_SPLIT[0],
+        VERSION_MINOR=VERSION_SPLIT[1],
+        VERSION_PATCH=VERSION_SPLIT[2],
+        )
+
     core = bld(
         target='core-objects',
         name='core-objects',
         features='cxx',
         source=bld.path.ant_glob(['core/**/*.cpp']),
-        use='BOOST NDN_CXX LIBRT',
+        use='version BOOST NDN_CXX LIBRT',
         includes='. core',
         export_includes='. core',
         )
@@ -170,7 +183,8 @@
         source='tools/nfd-status-http-server.py',
         target='bin/nfd-status-http-server',
         install_path="${BINDIR}",
-        chmod=0755)
+        chmod=0755,
+        VERSION=VERSION)
 
     if bld.env['SPHINX_BUILD']:
         bld(features="sphinx",
@@ -186,13 +200,16 @@
             source='tools/%s' % (str(script)),
             target='bin/%s' % (str(script.change_ext(''))),
             install_path="${BINDIR}",
-            chmod=0755)
+            chmod=0755,
+            VERSION=VERSION)
 
 def docs(bld):
     from waflib import Options
     Options.commands = ['doxygen', 'sphinx'] + Options.commands
 
 def doxygen(bld):
+    version(bld)
+
     if not bld.env.DOXYGEN:
         Logs.error("ERROR: cannot build documentation (`doxygen' is not found in $PATH)")
     else:
@@ -200,7 +217,7 @@
             name="doxygen-conf",
             source="docs/doxygen.conf.in",
             target="docs/doxygen.conf",
-            VERSION=VERSION,
+            VERSION=VERSION_BASE,
             )
 
         bld(features="doxygen",
@@ -208,6 +225,8 @@
             use="doxygen-conf")
 
 def sphinx(bld):
+    version(bld)
+
     if not bld.env.SPHINX_BUILD:
         bld.fatal("ERROR: cannot build documentation (`sphinx-build' is not found in $PATH)")
     else:
@@ -215,4 +234,24 @@
             outdir="docs",
             source=bld.path.ant_glob('docs/**/*.rst'),
             config="docs/conf.py",
-            VERSION=VERSION)
+            VERSION=VERSION_BASE)
+
+def version(ctx):
+    Context.g_module.VERSION_BASE = Context.g_module.VERSION
+    Context.g_module.VERSION_SPLIT = [v for v in VERSION_BASE.split('.')]
+
+    try:
+        cmd = ['git', 'describe', '--match', 'NFD-*']
+        p = Utils.subprocess.Popen(cmd, stdout=Utils.subprocess.PIPE,
+                                   stderr=None, stdin=None)
+        out = p.communicate()[0].strip()
+        if p.returncode == 0 and out != "":
+            Context.g_module.VERSION = out[4:]
+    except:
+        pass
+
+def dist(ctx):
+    version(ctx)
+
+def distcheck(ctx):
+    version(ctx)