Make build information accessible by programs by extracting data from the local git repository (or a standalone file if a git repository is not present).

Changes

* Adds a new class named Version to the core module
* Adds a template file to the core module named version-defines.h.in
* Adds --PrintVersion and --version arguments to the CommandLine class.
* Creates a new waf tool which queries the local git repository and extracts build information from itCreate a waf task to extract version information from git repository
This commit is contained in:
Mathew Bielejeski
2020-08-11 20:20:05 +00:00
committed by Peter Barnes
parent e752b2e937
commit 32e95f8228
12 changed files with 1190 additions and 30 deletions

2
.gitignore vendored
View File

@@ -36,3 +36,5 @@ build-dir/
build/
/.cproject
/.project
version.cache

View File

@@ -1155,3 +1155,106 @@ except that the program and ns-3 libraries will not be rebuilt.
.. sourcecode:: bash
$ ./waf --run-no-build '<ns3-program> --arg1=value1 --arg2=value2 ...'
Build version
+++++++++++++
As of the ns-3.32 release, a new Waf option was introduced to print out the
version of the ns-3 build
.. sourcecode:: bash
$ ./waf --check-version
Waf will collect information about the build and print out something similar
to the output below.
.. sourcecode:: text
ns-3.31+26@g82e7f5d-debug
The output of ``--check-version`` depends on how ns-3 was installed. If ns-3
was installed from a tarball, the build information is included in one
of the files in the tarball. In this case, the output of ``--check-version``
will always be the same, except for the profile value which is based
on the ``--build-profile`` option passed to ``waf configure``.
If ns-3 was downloaded using git, the build information is generated by
examining the current state of the git repository. The output of
``--check-version`` will change whenever the state of the active branch
changes.
The output of ``--check-version`` has the following format:
.. sourcecode:: text
<version_tag>[+closest_tag][+distance_from_tag]@<commit_hash>[-tree_state]-<profile>
version_tag
version_tag contains the version of the ns-3 code. The version tag is
defined as a git tag with the format ns-3*. If multiple git tags match the
format, the tag on the active branch which is closest to the current commit
is chosen.
closest_tag
closest_tag is similar to version_tag except it is the first tag found,
regardless of format. The closest tag is not included in the output when
closest_tag and version_tag have the same value.
distance_from_tag
distance_from_tag contains the number of commits between the current commit
and closest_tag. distance_from_tag is not included in the output when the
value is 0 (i.e. when closest_tag points to the current commit)
commit_hash
commit_hash is the hash of the commit at the tip of the active branch. The
value is 'g' followed by the first 7 characters of the commit hash. The 'g'
prefix is used to indicate that this is a git hash.
tree_state
tree_state indicates the state of the working tree. When the working tree
has uncommitted changes this field has the value 'dirty'. The tree state is
not included in the version output when the working tree is clean (e.g. when
there are no uncommitted changes).
profile
The build profile specified in the ``--build-profile`` option passed to
``waf configure``
A new class, named Version, has been added to the core module. The Version class
contains functions to retrieve individual fields of the build version as well
as functions to print the full build version like ``--check-version``.
The ``build-version-example`` application provides an example of how to use
the Version class to retrieve the various build version fields. See the
documentation for the Version class for specifics on the output of the Version
class functions.
.. sourcecode:: text
build-version-example:
Program Version (according to CommandLine): ns-3.31+28@gce1eb40-dirty-debug
Version fields:
LongVersion: ns-3.32+28@gcefeb91-dirty-debug
ShortVersion: ns-3.32+*
BuildSummary: ns-3.32+*
VersionTag: ns-3.32
Major: 3
Minor: 32
Patch: 0
ReleaseCandidate:
ClosestAncestorTag: ns-3.32
TagDistance: 28
CommitHash: gce1eb40
BuildProfile: debug
WorkingTree: dirty
The CommandLine class has also been updated to support the ``--version``
option which will print the full build version and exit.
.. sourcecode:: text
./waf --run-no-build "command-line-example --version"
Waf: Entering directory `/g/g14/mdb/gitlab/mdb/ns-3-dev/build/debug'
ns-3.31+28@gce1eb40-dirty-debug

View File

@@ -0,0 +1,71 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2018 Lawrence Livermore National Laboratory
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Peter D. Barnes, Jr. <pdbarnes@llnl.gov>
*/
#include "ns3/command-line.h"
#include "ns3/version.h"
#include <iostream>
#include <iomanip>
#include <string>
/**
* \file
* \ingroup core-examples
* Example program illustrating use of ns3::Version.
*/
using namespace ns3;
int main (int argc, char *argv[])
{
CommandLine cmd (__FILE__);
cmd.Usage ("Version class example program.\n"
"\n"
"This program demonstrates the various outputs from the Version class");
cmd.Parse (argc, argv);
std::cout << std::endl;
std::cout << cmd.GetName () << ":" << std::endl;
//Print the source version used to build this example
std::cout << "Program Version (according to CommandLine): ";
cmd.PrintVersion (std::cout);
std::cout << std::endl;
Version version;
std::cout << "Version fields:\n"
<< "LongVersion: " << version.LongVersion () << "\n"
<< "ShortVersion: " << version.ShortVersion () << "\n"
<< "BuildSummary: " << version.BuildSummary () << "\n"
<< "VersionTag: " << version.VersionTag () << "\n"
<< "Major: " << version.Major () << "\n"
<< "Minor: " << version.Minor () << "\n"
<< "Patch: " << version.Patch () << "\n"
<< "ReleaseCandidate: " << version.ReleaseCandidate () << "\n"
<< "ClosestAncestorTag: " << version.ClosestAncestorTag () << "\n"
<< "TagDistance: " << version.TagDistance () << "\n"
<< "CommitHash: " << version.CommitHash () << "\n"
<< "BuildProfile: " << version.BuildProfile () << "\n"
<< "WorkingTree: " << (version.DirtyWorkingTree () ? "dirty" : "clean")
<< std::endl;
return 0;
}

View File

@@ -28,7 +28,7 @@
* \file
* \ingroup core-examples
* \ingroup commandline
* \brief Example program illustrating use of ns3::CommandLine.
* Example program illustrating use of ns3::CommandLine.
*/
using namespace ns3;
@@ -104,7 +104,13 @@ int main (int argc, char *argv[])
// Show initial values:
std::cout << std::endl;
std::cout << cmd.GetName () << std::endl;
std::cout << cmd.GetName () << ":" << std::endl;
//Print the source version used to build this example
std::cout << "Program Version: ";
cmd.PrintVersion (std::cout);
std::cout << std::endl;
std::cout << "Initial values:" << std::endl;
std::cout << std::left << std::setw (10) << "intArg:"

View File

@@ -51,6 +51,10 @@ def build(bld):
['core'])
obj.source = 'system-path-examples.cc'
obj = bld.create_ns3_program('build-version-example',
['core'])
obj.source = 'build-version-example.cc'
if bld.env['ENABLE_THREADING'] and bld.env["ENABLE_REAL_TIME"]:
obj = bld.create_ns3_program('main-test-sync', ['network'])
obj.source = 'main-test-sync.cc'

View File

@@ -27,6 +27,7 @@
#include "system-path.h"
#include "type-id.h"
#include "string.h"
#include "version.h"
#include <algorithm> // transform
#include <cctype> // tolower
@@ -64,7 +65,7 @@ CommandLine::CommandLine (const std::string filename)
std::string basename = SystemPath::Split (filename).back ();
m_shortName = basename.substr (0, basename.rfind (".cc"));
}
CommandLine::CommandLine (const CommandLine &cmd)
{
Copy (cmd);
@@ -137,7 +138,7 @@ CommandLine::Parse (std::vector<std::string> args)
NS_LOG_FUNCTION (this << args.size () << args);
PrintDoxygenUsage ();
m_nonOptionCount = 0;
if (args.size () > 0)
@@ -328,11 +329,23 @@ CommandLine::PrintHelp (std::ostream &os) const
<< " --PrintGroup=[group]: Print all TypeIds of group.\n"
<< " --PrintTypeIds: Print all TypeIds.\n"
<< " --PrintAttributes=[typeid]: Print all attributes of typeid.\n"
<< " --PrintVersion: Print the ns-3 version.\n"
<< " --PrintHelp: Print this help message.\n"
<< std::endl;
}
#include <unistd.h> // getcwd
std::string
CommandLine::GetVersion () const
{
return Version::LongVersion ();
}
void
CommandLine::PrintVersion (std::ostream & os) const
{
os << GetVersion () << std::endl;
}
void
CommandLine::PrintDoxygenUsage (void) const
@@ -344,7 +357,7 @@ CommandLine::PrintDoxygenUsage (void) const
{
return;
}
if (m_shortName.size () == 0)
{
NS_FATAL_ERROR ("No file name on example-to-run; forgot to use CommandLine var (__FILE__)?");
@@ -356,19 +369,19 @@ CommandLine::PrintDoxygenUsage (void) const
m_nonOptions.begin () + m_NNonOptions);
std::string outf = SystemPath::Append (std::string (envVar), m_shortName + ".command-line");
NS_LOG_INFO ("Writing CommandLine doxy to " << outf);
std::fstream os (outf, std::fstream::out);
os << "/**\n \\file " << m_shortName << ".cc\n"
<< "<h3>Usage</h3>\n"
<< "<code>$ ./waf --run \"" << m_shortName
<< (m_options.size () ? " [Program Options]" : "")
<< (nonOptions.size () ? " [Program Arguments]" : "")
<< "\"</code>\n";
if (m_usage.length ())
{
os << m_usage << std::endl;
@@ -577,6 +590,12 @@ CommandLine::HandleArgument (const std::string &name, const std::string &value)
PrintHelp (std::cout);
std::exit (0);
}
if (name == "PrintVersion" || name == "version")
{
//Print the version, then exit the program
PrintVersion (std::cout);
std::exit (0);
}
else if (name == "PrintGroups")
{
// method below never returns.

View File

@@ -64,9 +64,12 @@ namespace ns3 {
--PrintGroup=[group]: Print all TypeIds of group.
--PrintTypeIds: Print all TypeIds.
--PrintAttributes=[typeid]: Print all attributes of typeid.
--PrintVersion: Print the ns-3 version.
--PrintHelp: Print this help message. \endverbatim
*
* The more common \c --help is a synonym for \c --PrintHelp; an example
* The more common \c \--version is a synonym for \c \--PrintVersion.
*
* The more common \c \--help is a synonym for \c \--PrintHelp; an example
* is given below.
*
* CommandLine can also handle non-option arguments
@@ -82,7 +85,7 @@ namespace ns3 {
* In use, arguments are given in the form
* \verbatim
--arg=value --toggle first-non-option\endverbatim
* Most arguments expect a value, as in the first form, \c --arg=value.
* Most arguments expect a value, as in the first form, \c \--arg=value.
* Toggles, corresponding to boolean arguments, can be given in any of
* the forms
* \verbatim
@@ -98,28 +101,29 @@ namespace ns3 {
* will be the final value used. For example,
* \verbatim
--arg=one --toggle=f --arg=another --toggle \endverbatim
* The variable set by \c --arg will end up with the value \c "another";
* the boolean set by \c --toggle will end up as \c true.
* The variable set by \c \--arg will end up with the value \c "another";
* the boolean set by \c \--toggle will end up as \c true.
*
* Because arguments can be repeated it can be hard to decipher what
* value each variable ended up with, especially when using boolean toggles.
* Suggested best practice is for scripts to report the values of all items
* settable through CommandLine, as done by the example below.
*
*
* CommandLine can set the initial value of every attribute in the system
* with the \c --TypeIdName::AttributeName=value syntax, for example
* with the \c \--TypeIdName::AttributeName=value syntax, for example
* \verbatim
--Application::StartTime=3s \endverbatim
* In some cases you may want to highlight the use of a particular
* attribute for a simulation script. For example, you might want
* to make it easy to set the \c Application::StartTime using
* the argument \c --start, and have its help string show as part
* the argument \c \--start, and have its help string show as part
* of the help message. This can be done using the
* \link AddValue(const std::string&, const std::string&) AddValue (name, attributePath) \endlink
* method.
*
* CommandLine can also set the value of every GlobalValue
* in the system with the \c --GlobalValueName=value syntax, for example
* in the system with the \c \--GlobalValueName=value syntax, for example
* \verbatim
--SchedulerType=HeapScheduler \endverbatim
*
@@ -182,6 +186,7 @@ namespace ns3 {
--PrintGroup=[group]: Print all TypeIds of group.
--PrintTypeIds: Print all TypeIds.
--PrintAttributes=[typeid]: Print all attributes of typeid.
--PrintVersion: Print the ns-3 version.
--PrintHelp: Print this help message. \endverbatim
*
* Having parsed the arguments, some programs will need to perform
@@ -225,7 +230,7 @@ public:
CommandLine (void);
/**
* Construct and register the source file name.
* This would typically be called by
* This would typically be called by
* CommandLine cmd (__FILE__);
*
* This form is required to generate Doxygen documentation of the
@@ -253,7 +258,7 @@ public:
/**
* Supply the program usage and documentation.
*
* \param [in] usage Program usage message to write with \c --help.
* \param [in] usage Program usage message to write with \c \--help.
*/
void Usage (const std::string usage);
@@ -261,7 +266,7 @@ public:
* Add a program argument, assigning to POD
*
* \param [in] name The name of the program-supplied argument
* \param [in] help The help text used by \c \-\-PrintHelp
* \param [in] help The help text used by \c \--PrintHelp
* \param [out] value A reference to the variable where the
* value parsed will be stored (if no value
* is parsed, this variable is not modified).
@@ -283,7 +288,7 @@ public:
* Add a program argument, using a Callback to parse the value
*
* \param [in] name The name of the program-supplied argument
* \param [in] help The help text used by \c --help
* \param [in] help The help text used by \c \--help
* \param [in] callback A Callback function that will be invoked to parse and
* store the value.
* \param [in] defaultValue Optional default value for argument.
@@ -310,7 +315,7 @@ public:
* Add a non-option argument, assigning to POD
*
* \param [in] name The name of the program-supplied argument
* \param [in] help The help text used by \c \-\-PrintHelp
* \param [in] help The help text used by \c \--PrintHelp
* \param [out] value A reference to the variable where the
* value parsed will be stored (if no value
* is parsed, this variable is not modified).
@@ -378,7 +383,7 @@ public:
/**
* \brief Print program usage to the desired output stream
*
* Handler for \c \-\-PrintHelp and \c \-\-help: print Usage(), argument names, and help strings
* Handler for \c \--PrintHelp and \c \--help: print Usage(), argument names, and help strings
*
* Alternatively, an overloaded operator << can be used:
* \code
@@ -393,6 +398,22 @@ public:
*/
void PrintHelp (std::ostream &os) const;
/**
* Get the program version.
*
* \return The program version
*/
std::string GetVersion () const;
/**
* Print ns-3 version to the desired output stream
*
* Handler for \c \--PrintVersion and \c \--version.
*
* \param [in,out] os The output stream to print on.
*/
void PrintVersion (std::ostream &os) const;
private:
/**
@@ -402,7 +423,7 @@ private:
class Item
{
public:
std::string m_name; /**< Argument label: \c \-\--m_name=... */
std::string m_name; /**< Argument label: \c \--m_name=... */
std::string m_help; /**< Argument help string */
virtual ~Item (); /**< Destructor */
/**
@@ -511,32 +532,32 @@ private:
static bool HandleAttribute (const std::string name, const std::string value);
/**
* Handler for \c \-\-PrintGlobals: print all global variables and values
* Handler for \c \--PrintGlobals: print all global variables and values
* \param [in,out] os The output stream to print on.
*/
void PrintGlobals (std::ostream &os) const;
/**
* Handler for \c \-\-PrintAttributes: print the attributes for a given type.
* Handler for \c \--PrintAttributes: print the attributes for a given type.
*
* \param [in,out] os the output stream.
* \param [in] type The TypeId whose Attributes should be displayed
*/
void PrintAttributes (std::ostream &os, const std::string &type) const;
/**
* Handler for \c \-\-PrintGroup: print all types belonging to a given group.
* Handler for \c \--PrintGroup: print all types belonging to a given group.
*
* \param [in,out] os The output stream.
* \param [in] group The name of the TypeId group to display
*/
void PrintGroup (std::ostream &os, const std::string &group) const;
/**
* Handler for \c \-\-PrintTypeIds: print all TypeId names.
* Handler for \c \--PrintTypeIds: print all TypeId names.
*
* \param [in,out] os The output stream.
*/
void PrintTypeIds (std::ostream &os) const;
/**
* Handler for \c \-\-PrintGroups: print all TypeId group names
* Handler for \c \--PrintGroups: print all TypeId group names
*
* \param [in,out] os The output stream.
*/

View File

@@ -0,0 +1,145 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2018 Lawrence Livermore National Laboratory
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Authors: Mathew Bielejeski <bielejeski1@llnl.gov>
*/
#ifndef NS3_VERSION_DEFINES_H_
#define NS3_VERSION_DEFINES_H_
/**
* \file
* \ingroup buildversion
* Defines the macro values for printing the build version.
* These will be populated by the build system.
*/
/**
* \ingroup buildversion
* @{
*/
/**
* The first tag found which matches the pattern ns-3*.
*
* The expected format of the ns-3 version tag is: ns-3.<minor>[.patch][-release_candidate]
*
* The tag is found by starting at the tip of the current branch and walking
* back towards the root.
*
* Type: string literal
*/
#define NS3_VERSION_TAG @VERSION_TAG@
/**
* The tag closest to the tip of the current branch
*
* The tag is found by starting at the tip of the current branch and
* walking back towards the root.
*
* This value will be the same as #NS3_VERSION_TAG when #NS3_VERSION_TAG is
* the closest tag.
*
* Type: string literal
*/
#define NS3_VERSION_CLOSEST_TAG @CLOSEST_TAG@
/**
* The major version extracted from #NS3_VERSION_TAG
*
* For a tag with the format ns-<digit>.<digit>[.digit], the major
* version is the number after ns- and before the first period.
*
* Type: integer
*/
#define NS3_VERSION_MAJOR @VERSION_MAJOR@
/**
* The minor version extracted from #NS3_VERSION_TAG
*
* For a tag with the format ns-<digit>.<digit>[.digit], the minor
* version is the number after the first period.
*
* Type: integer
*/
#define NS3_VERSION_MINOR @VERSION_MINOR@
/**
* The patch number extracted from #NS3_VERSION_TAG
*
* For a tag with the format ns-<digit>.<digit>[.digit], the patch
* is the number after the last period.
*
* The patch value is optional and may not be present in the tag.
* In cases where the patch value is not present, the field will be set to 0
*
* Type: integer
*/
#define NS3_VERSION_PATCH @VERSION_PATCH@
/**
* The portion of the #NS3_VERSION_TAG indicating the version
* of the release candidate (if applicable).
*
* In order for this field to contain a value, the #NS3_VERSION_TAG
* must have the format ns-<digit>.<digit>[.digit][-RC<digit>].
* The contents of the release candidate will be the RC<digit>
* portion of the tag.
*
* Type: string literal
*/
#define NS3_VERSION_RELEASE_CANDIDATE @VERSION_RELEASE_CANDIDATE@
/**
* The number of repository commits between #NS3_VERSION_CLOSEST_TAG
* and the branch HEAD.
*
* Type: integer
*/
#define NS3_VERSION_TAG_DISTANCE @VERSION_TAG_DISTANCE@
/**
* Hash value which uniquely identifies the commit of the
* branch HEAD.
* The first character of the commit hash is 'g' to indicate this hash is
* a git hash
*
* Type: string literal
*/
#define NS3_VERSION_COMMIT_HASH @VERSION_COMMIT_HASH@
/**
* Flag indicating whether the repository working tree had uncommitted
* changes when the library was built.
*
* The flag will be 1 if there were uncommitted changes, 0 otherwise.
*
* Type: integer
*/
#define NS3_VERSION_DIRTY_FLAG @VERSION_DIRTY_FLAG@
/**
* Indicates the build profile that was specified by the --build-profile option
* of "waf configure"
*
* Type: string literal
*/
#define NS3_VERSION_BUILD_PROFILE "@BUILD_PROFILE@"
/** @} */
#endif

191
src/core/model/version.cc Normal file
View File

@@ -0,0 +1,191 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2018 Lawrence Livermore National Laboratory
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Authors: Mathew Bielejeski <bielejeski1@llnl.gov>
*/
#include "version.h"
#include "version-defines.h"
#include <sstream>
/**
* \file
* \ingroup buildversion
* ns3::Version implementation
*/
namespace ns3 {
std::string
Version::VersionTag (void)
{
return NS3_VERSION_TAG;
}
std::string
Version::ClosestAncestorTag (void)
{
return NS3_VERSION_CLOSEST_TAG;
}
uint32_t
Version::Major (void)
{
return NS3_VERSION_MAJOR;
}
uint32_t
Version::Minor (void)
{
return NS3_VERSION_MINOR;
}
uint32_t
Version::Patch (void)
{
return NS3_VERSION_PATCH;
}
std::string
Version::ReleaseCandidate (void)
{
return std::string{NS3_VERSION_RELEASE_CANDIDATE};
}
uint32_t
Version::TagDistance (void)
{
return NS3_VERSION_TAG_DISTANCE;
}
bool
Version::DirtyWorkingTree (void)
{
return static_cast<bool> (NS3_VERSION_DIRTY_FLAG);
}
std::string
Version::CommitHash (void)
{
return std::string{NS3_VERSION_COMMIT_HASH};
}
std::string
Version::BuildProfile (void)
{
return std::string{NS3_VERSION_BUILD_PROFILE};
}
std::string
Version::ShortVersion (void)
{
std::ostringstream ostream;
ostream << "ns-" << Major ()
<< "." << Minor ();
//only include patch if it is not 0
auto patch = Patch ();
if (patch > 0)
{
ostream << '.' << patch;
}
auto rc = ReleaseCandidate ();
if (!rc.empty ())
{
ostream << '-' << rc;
}
auto ancestorTag = ClosestAncestorTag ();
if ( ( !ancestorTag.empty () && (ancestorTag != VersionTag ()) )
|| TagDistance () > 0)
{
ostream << "+";
}
if (DirtyWorkingTree ())
{
ostream << "*";
}
return ostream.str ();
}
std::string
Version::BuildSummary (void)
{
std::ostringstream ostream;
ostream << ClosestAncestorTag ();
if (TagDistance () > 0)
{
ostream << "+";
}
if (DirtyWorkingTree ())
{
ostream << "*";
}
return ostream.str ();
}
std::string
Version::LongVersion (void)
{
std::ostringstream ostream;
ostream << "ns-" << Major ()
<< "." << Minor ();
//only include patch if it is not 0
auto patch = Patch ();
if (patch > 0)
{
ostream << '.' << patch;
}
auto rc = ReleaseCandidate ();
if (!rc.empty ())
{
ostream << '-' << rc;
}
auto ancestorTag = ClosestAncestorTag ();
if ( !ancestorTag.empty () && (ancestorTag != VersionTag ()) )
{
ostream << '+' << ancestorTag;
}
auto tagDistance = TagDistance ();
if ( tagDistance > 0 )
{
ostream << '+' << tagDistance;
}
ostream << '@' << CommitHash ();
if ( DirtyWorkingTree () )
{
ostream << "-dirty";
}
ostream << '-' << BuildProfile ();
return ostream.str ();
}
} // namespace ns3

275
src/core/model/version.h Normal file
View File

@@ -0,0 +1,275 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2018 Lawrence Livermore National Laboratory
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Authors: Mathew Bielejeski <bielejeski1@llnl.gov>
*/
#ifndef BUILD_VERSION_H_
#define BUILD_VERSION_H_
#include "ns3/int64x64.h"
#include <string>
/**
* \file
* \ingroup buildversion
* class ns3::Version definition
*/
namespace ns3 {
/**
* \ingroup core
* \defgroup buildversion Build version reporting
*
* Version information is pulled from the local git repository during the build
* process. If a git repository is not found, the build system will search for
* a file named version.cache under the src/core directory. The version.cache
* file must contain key/value pairs in the format key=value with one entry per
* line. The build system will use the data pulled from the git repository, or
* loaded from the version.cache file, to generate the file version-defines.h
*
* The build will fail if a local git repository is not present and
* a version.cache file can not be found.
*/
/**
* \ingroup buildversion
*
* Helper class providing functions to access various parts of the version
* string, as well as functions for composing short and long variants of the
* version string.
*
* See CommandLine::PrintVersion() for an example on how to
* use Version to output a version string. command-line-example has been updated
* to include CommandLine::PrintVersion() in its output
*
* build-version-example.cc illustrates using each of these functions.
*
* Below is a partial view of a git branch:
*
* \note Square nodes represent tags
* \note Circle nodes represent commits
*
* \dot
* digraph {
* vt [label="ns-3.32", shape="box"]
* t [label="mytag", shape="box"]
* h [label="HEAD", shape="box"]
* c1 [label="6ad7f05"]
* c2 [label="05fc891"]
* c3 [label="bd9ffac"]
* c4 [label="70fa23b"]
*
* h -> c1 -> c2 -> c3 -> c4
* t -> c2
* vt -> c4
* }
* \enddot
*
* Here are the values that will be assigned based on this example branch:
*
* | Component | Value | Notes |
* |--------------------|----------|-------|
* | VersionTag | ns-3.32 | |
* | ClosestAncestorTag | mytag | |
* | Major | 3 | |
* | Minor | 32 | |
* | Patch | 0 | This version tag does not have a patch field |
* | ReleaseCandidate | "" | This version tag does not have a release candidate field |
* | TagDistance | 1 | |
* | CommitHash | g6ad7f05 | g at front of hash indicates a git hash |
* | DirtyWorkingTree | Variable | Depends on status of git working and stage areas |
* | BuildProfile | Variable | Depends on the value of --build-profile option of waf configure |
*/
class Version
{
public:
/**
* Returns the ns-3 version tag of the closest ancestor commit.
*
* The format of the tag is
* \verbatim ns3-<major>.<minor>[.patch] \endverbatim
*
* The patch field is optional and may not be present. The value of
* patch defaults to 0 if the tag does not have a patch field.
*
* \return ns-3 version tag
*/
static std::string VersionTag (void);
/**
* Returns the closest tag that is attached to a commit that is an ancestor
* of the current branch head.
*
* The value returned by this function may be the same as VersionTag()
* if the ns-3 version tag is the closest ancestor tag.
*
* \return Closest tag attached to an ancestor of the current commit
*/
static std::string ClosestAncestorTag (void);
/**
* Major component of the build version
*
* The format of the build version string is
* \verbatim ns-<major>.<minor>[.patch][-RC<digit>] \endverbatim
*
* The major component is the number before the first period
*
* \return The major component of the build version
*/
static uint32_t Major (void);
/**
* Minor component of the build version
*
* The format of the build version string is
* \verbatim ns-<major>.<minor>[.patch][-RC<digit>] \endverbatim
*
* The minor component is the number after the first period
*
* \return The minor component of the build version
*/
static uint32_t Minor (void);
/**
* Patch component of the build version
*
* A build version with a patch component will have the format
* \verbatim ns-<major>.<minor>.<patch> \endverbatim
*
* The patch component is the number after the second period
*
* \return The patch component of the build version or 0 if the build version
* does not have a patch component
*/
static uint32_t Patch (void);
/**
* Release candidate component of the build version
*
* A build version with a release candidate will have the format
* \verbatim ns-<major>.<minor>[.patch]-RC<digit> \endverbatim
*
* The string returned by this function will have the format RC<digit>
*
* \return The release candidate component of the build version or an empty
* string if the build version does not have a release candidate component
*/
static std::string ReleaseCandidate (void);
/**
* The number of commits between the current
* commit and the tag returned by ClosestAncestorTag().
*
* \return The number of commits made since the last tagged commit
*/
static uint32_t TagDistance (void);
/**
* Indicates whether there were uncommitted changes during the build
*
* \return \c true if the working tree had uncommitted changes.
*/
static bool DirtyWorkingTree (void);
/**
* Hash of the most recent commit
*
* The hash component is the id of the most recent commit.
* The returned value is a hexadecimal string with enough data to
* uniquely identify the commit.
*
* The first character of the string is a letter indicating the type
* of repository that was in use: g=git
*
* Example of hash output: g6bfb0c9
*
* \return hexadecimal representation of the most recent commit id
*/
static std::string CommitHash (void);
/**
* Indicates the type of build that was performed (debug/release/optimized).
*
* This information is set by the --build-profile option of waf configure
*
* \return String containing the type of build
*/
static std::string BuildProfile (void);
/**
* Constructs a string containing the ns-3 major and minor version components,
* and indication of additional commits or dirty status.
*
* The format of the constructed string is
* \verbatim ns-<major>.<minor>[.patch][-rc]<flags> \endverbatim
*
* * [patch] is included when Patch() > 0.
* * [-rc] is included when ReleaseCandidate() contains a non-empty string
* * \c flags will contain `+` when TagDistance() > 0
* * \c flags will contain `*` when DirtyWorkingTree() == true.
*
* [flags] will contain none, one, or both characters depending on the state
* of the branch
*
* \return String containing the ns-3 major and minor components and flags.
*/
static std::string ShortVersion (void);
/**
* Constructs a string containing the most recent tag and status flags.
*
* In the case where closest-ancestor-tag == version-tag, the output of this
* function will be the same as ShortVersion()
*
* The format of the constructed string is `<closest-ancestor-tag>[flags]`.
*
* * \c flags will contain `+` when TagDistance() > 0
* * \c flags will contain `*` when DirtyWorkingTree() == true.
*
* [flags] will contain none, one, or both characters depending on the state
* of the branch
*
* \return String containing the closest ancestor tag and flags.
*/
static std::string BuildSummary (void);
/**
* Constructs a string containing all of the build details
*
* The format of the constructed string is
* \verbatim
* ns-<major>.<minor>[.patch][-rc][-closest-tag]-<tag-distance>@<hash>[-dirty]-<build-profile>
* \endverbatim
*
* [patch], [rc], [closest-tag], and [dirty] will only be present under certain circumstances:
* * [patch] is included when Patch() > 0
* * [rc] is included when ReleaseCandidate() is not an empty string
* * [closest-tag] is included when ClosestTag() != VersionTag()
* * [dirty] is included when DirtyWorkingTree() is \c true
*
* \return String containing full version
*/
static std::string LongVersion (void);
}; // class Version
} // namespace ns3
#endif

View File

@@ -1,7 +1,8 @@
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
import sys
import re
from waflib import Options, Utils
from waflib import Context, Configure, Options, Utils
import wutils
int64x64 = {
@@ -41,9 +42,15 @@ def options(opt):
action="store_true", default=False,
dest='disable_pthread')
opt.add_option('--check-version',
help=("Print the current build version"),
action="store_true", default=False,
dest='check_version')
def configure(conf):
conf.load('versioning', ['waf-tools'])
int64x64_impl = Options.options.int64x64_impl
if int64x64_impl == 'default' or int64x64_impl == 'int128':
@@ -150,8 +157,26 @@ int main ()
conf.write_config_header('ns3/core-config.h', top=True)
def build(bld):
bld.install_files('${INCLUDEDIR}/%s%s/ns3' % (wutils.APPNAME, wutils.VERSION), '../../ns3/core-config.h')
version_template = bld.path.find_node('model/version-defines.h.in')
version_header = bld.bldnode.make_node('version-defines.h')
vers_tg = bld(features='version-defines',
source=version_template,
target=version_header)
#silence errors about no mapping for version-defines.h.in
vers_tg.mappings['.h'] = lambda *a, **k: None
vers_tg.mappings['.h.in'] = lambda *a, **k: None
if Options.options.check_version:
print_version(bld, vers_tg)
raise SystemExit(0)
return
core = bld.create_ns3_module('core')
core.source = [
'model/time.cc',
@@ -214,6 +239,7 @@ def build(bld):
'model/time-printer.cc',
'model/show-progress.cc',
'model/system-wall-clock-timestamp.cc',
'model/version.cc',
]
if (bld.env['ENABLE_EXAMPLES']):
@@ -342,6 +368,7 @@ def build(bld):
'model/node-printer.h',
'model/time-printer.h',
'model/show-progress.h',
'model/version.h',
]
if (bld.env['ENABLE_EXAMPLES']):
@@ -415,3 +442,45 @@ def build(bld):
pymod = bld.ns3_python_bindings()
if pymod is not None:
pymod.source += ['bindings/module_helpers.cc']
def print_version(bld, tg):
tg.post()
found = False
for task in tg.tasks:
if task.__class__.__name__ == 'git_ns3_version_info':
found = True
#manually run task
task.run()
break
if not found:
print("Task to generate version information could not be found")
return
handlers = {
'VERSION_TAG': lambda s: s.strip('"'),
'CLOSEST_TAG': lambda s: s.strip('"'),
'VERSION_TAG_DISTANCE': lambda s: '' if s == '0' else "+" + s,
'VERSION_COMMIT_HASH': lambda s: "@" + s.strip('"'),
'VERSION_DIRTY_FLAG': lambda s: '' if s == '0' else '-dirty',
'BUILD_PROFILE': lambda s: "-" + s
}
fields=('VERSION_TAG', 'CLOSEST_TAG', 'VERSION_TAG_DISTANCE',
'VERSION_COMMIT_HASH', 'VERSION_DIRTY_FLAG', 'BUILD_PROFILE')
parts = dict()
for field in fields:
if field in bld.env:
parts[field] = handlers[field](bld.env[field])
else:
parts[field] = ''
if parts['CLOSEST_TAG'] != parts['VERSION_TAG']:
parts['CLOSEST_TAG'] = "+" + parts['CLOSEST_TAG']
else:
parts['CLOSEST_TAG'] = ""
print(''.join([parts[f] for f in fields]))

254
waf-tools/versioning.py Normal file
View File

@@ -0,0 +1,254 @@
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
import re
from waflib import ConfigSet, Configure, Context, Task, TaskGen
CACHE_FILE = 'version.cache'
class ns3_version_info(Task.Task):
'''Base task which implements functionality common to all inherited tasks
This class handles parsing the ns-3 version tag into component parts
as well as saving the version fields to a cache file
All version fields should be stored in the fields property
Derived classes should override _find_closest_tag() and
_find_closest_ns3_tag()
'''
def __init__(self, *args, **kwargs):
self._fields = ConfigSet.ConfigSet()
super(ns3_version_info, self).__init__(*args, **kwargs)
@property
def fields(self):
return self._fields
def _find_closest_tag(self, ctx):
"""Override in derived classes"""
pass
def _find_closest_ns3_tag(self, ctx):
"""Override in derived classes"""
pass
def _parse_version_tag(self, ctx, tag):
safe_tag = tag.strip()
matches = re.match("ns-(\d+)\.(\d+)(?:.(\d+))?(?:-(RC.+))?.*", safe_tag)
if not matches:
return False
self.fields['VERSION_TAG'] = '"{}"'.format(safe_tag)
self.fields['VERSION_MAJOR'] = matches.group(1)
self.fields['VERSION_MINOR'] = matches.group(2)
patch = matches.group(3)
if not patch:
patch = '0'
self.fields['VERSION_PATCH'] = patch
release_candidate = matches.group(4)
if not release_candidate:
release_candidate = ''
self.fields['VERSION_RELEASE_CANDIDATE'] = '"{}"'.format(release_candidate)
return True
def run(self):
ctx = self.generator.bld
try:
self._find_closest_tag(ctx)
if 'VERSION_TAG' not in self.fields:
self._find_closest_ns3_tag(ctx)
#Generate the path where the cache file will be stored
base_path = self.generator.path.make_node('model')
cache_path = base_path.make_node(CACHE_FILE)
#Write the version information out to the cache file
#The cache file is used to populate the version fields when a git
#repository can not be found
self.fields.store(cache_path.abspath())
#merge version info into main configset
ctx.env.update(self.fields)
except Exception as e:
ctx.to_log("Extracting version information from tags failed: {}\n".format(e))
return 1
return 0
class git_ns3_version_info(ns3_version_info):
'''Task to generate version fields from an ns-3 git repository'''
always_run = True
def _find_closest_tag(self, ctx):
cmd = [
'git',
'describe',
'--tags',
'--dirty',
'--long'
]
try:
out = ctx.cmd_and_log(cmd,
output=Context.STDOUT,
quiet=Context.BOTH)
except Exception as e:
raise Exception(e.stderr.strip())
matches = re.match('(.+)-(\d+)-(g[a-fA-F0-9]+)(?:-(dirty))?', out)
if not matches:
raise ValueError("Closest tag found in git log"
"does not match the expected format (tag='{}')"
.format(out))
tag = matches.group(1)
self.fields['CLOSEST_TAG'] = '"{}"'.format(tag)
self.fields['VERSION_TAG_DISTANCE'] = matches.group(2)
self.fields['VERSION_COMMIT_HASH'] = '"{}"'.format(matches.group(3))
self.fields['VERSION_DIRTY_FLAG'] = '1' if matches.group(4) else '0'
self._parse_version_tag(ctx, tag)
def _find_closest_ns3_tag(self, ctx):
cmd = [
'git',
'describe',
'--tags',
'--abbrev=0',
'--match',
'ns-3*',
'HEAD'
]
try:
out = ctx.cmd_and_log(cmd,
output=Context.STDOUT,
quiet=Context.BOTH)
except Exception as e:
raise Exception(e.stderr.strip())
tag = out.strip()
result = self._parse_version_tag(ctx, tag)
if not result:
raise ValueError("Closest ns3 tag found in git log"
"does not match the expected format (tag='{}')"
.format(tag))
@TaskGen.feature('version-defines')
def generate_version_defines(self):
#Create a substitution task to generate version-defines.h
#from fields stored in env
subst_task = self.create_task('subst', self.source, self.target)
if self.env['HAVE_GIT_REPO']:
#if a git repo is present, run the version task first to
#populate the appropriate fields with data from the git repo
version_task = self.create_task('git_ns3_version_info')
subst_task.set_run_after(version_task)
@Configure.conf
def check_git_repo(self):
'''Determine if a git repository is present'''
root = False
cmd = [
'git',
'rev-parse',
'--show-toplevel'
]
try:
#determine if the current directory is part of a git repository
self.find_program('git')
out = self.cmd_and_log(cmd, output=Context.STDOUT, quiet=Context.BOTH)
root = out.strip()
except Exception:
root = False
self.msg('Checking for local git repository', root)
return bool(root)
@Configure.conf
def check_git_repo_has_ns3_tags(self):
'''Determine if the git repository is an ns-3 repository
A repository is considered an ns-3 repository if it has at least one
tag that matches the regex ns-3*
'''
tag = False
cmd = [
'git',
'describe',
'--tags',
'--abbrev=0',
'--match',
'ns-3*'
]
try:
out = self.cmd_and_log(cmd, output=Context.STDOUT, quiet=Context.BOTH)
tag = out.strip()
except Exception:
tag = False
self.msg('Checking local git repository for ns3 tags', tag)
return bool(tag)
def configure(ctx):
has_ns3_tags = False
if ctx.check_git_repo():
has_ns3_tags = ctx.check_git_repo_has_ns3_tags()
ctx.env['HAVE_GIT_REPO'] = has_ns3_tags
if not has_ns3_tags:
#no ns-3 repository, look for a cache file containing the version info
ctx.start_msg('Searching for file {}'.format(CACHE_FILE))
glob_pattern = '**/{}'.format(CACHE_FILE)
cache_path = ctx.path.ant_glob(glob_pattern)
if len(cache_path) == 0:
ctx.end_msg(False)
ctx.fatal("Could not find {} under {}. This file must exist and contain "
"version information when a git repository is not "
"present.".format(CACHE_FILE, ctx.path))
#Found cache file
#Load it and merge the information into the main context environment
src_path = cache_path[0].srcpath()
ctx.end_msg(src_path)
version_cache = ConfigSet.ConfigSet ()
version_cache.load (src_path)
ctx.env.update(version_cache)