doc: Add CommandLine::Usage to the doxygen for every example

This commit is contained in:
Peter D. Barnes, Jr
2020-04-27 15:22:11 -07:00
parent e070724fe4
commit 20f5e442ed
6 changed files with 204 additions and 22 deletions

View File

@@ -81,6 +81,7 @@ transmitted.</li>
<li>The internal TCP API for <b>TcpCongestionOps</b> has been extended to support the <b>CongControl</b> method to allow for delivery rate estimation feedback to the congestion control mechanism.</li>
<li>Functions <b>LteEnbPhy::ReceiveUlHarqFeedback</b> and <b>LteUePhy::ReceiveLteDlHarqFeedback</b> are renamed to <b>LteEnbPhy::ReportUlHarqFeedback</b> and <b>LteUePhy::EnqueueDlHarqFeedback</b>, respectively to avoid confusion about their functionality. <b>LteHelper</b> is updated accordingly.</li>
<li>Now on, instead of <b>uint8_t</b>, <b>uint16_t</b> would be used to store a bandwidth value in LTE.</li>
<li>The preferred way to declare instances of <b>CommandLine</b> is now through a macro: <b>COMMANDLINE (cmd)</b>. This enables us to add the <b>CommandLine::Usage()</b> message to the Doxygen for the program.</li>
</ul>
<h2>Changes to build system:</h2>
<ul>

View File

@@ -28,6 +28,7 @@ New user-visible features
- (propagation) 3GPP TR 38.901 pathloss and channel condition models added
- (spectrum) Addition three-gpp-channel-model (part of Integration of the 3GPP TR 38.901 fast fading model)
- (antenna) Addition of three-gpp-antenna-array-model (part of Integration of the 3GPP TR 38.901 fast fading model)
- (core) CommandLine can now add the Usage message to the Doxygen for the program; see CommandLine for details.
Bugs fixed
----------

1
doc/.gitignore vendored
View File

@@ -5,6 +5,7 @@
latex/
ns3-object.txt
introspected-doxygen.h
introspected-command-line.h
doxygen.docset.conf
doxygen.log
doxygen.warnings.log

View File

@@ -18,12 +18,6 @@
* Authors: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
*/
#include <algorithm> // for transform
#include <cctype> // for tolower
#include <cstdlib> // for exit
#include <iomanip> // for setw, boolalpha
#include <set>
#include <sstream>
#include "command-line.h"
#include "des-metrics.h"
@@ -34,6 +28,14 @@
#include "type-id.h"
#include "string.h"
#include <algorithm> // transform
#include <cctype> // tolower
#include <cstdlib> // exit, getenv
#include <cstring> // strlen
#include <iomanip> // setw, boolalpha
#include <set>
#include <sstream>
/**
* \file
@@ -47,10 +49,22 @@ NS_LOG_COMPONENT_DEFINE ("CommandLine");
CommandLine::CommandLine ()
: m_NNonOptions (0),
m_nonOptionCount (0)
m_nonOptionCount (0),
m_usage (),
m_shortName ()
{
NS_LOG_FUNCTION (this);
}
CommandLine::CommandLine (const std::string filename)
: m_NNonOptions (0),
m_nonOptionCount (0),
m_usage ()
{
NS_LOG_FUNCTION (this << filename);
std::string basename = SystemPath::Split (filename).back ();
m_shortName = basename.substr (0, basename.rfind (".cc"));
}
CommandLine::CommandLine (const CommandLine &cmd)
{
Copy (cmd);
@@ -76,8 +90,9 @@ CommandLine::Copy (const CommandLine &cmd)
std::copy (cmd.m_nonOptions.begin (), cmd.m_nonOptions.end (), m_nonOptions.end ());
m_NNonOptions = cmd.m_NNonOptions;
m_usage = cmd.m_usage;
m_name = cmd.m_name;
m_nonOptionCount = 0;
m_usage = cmd.m_usage;
m_shortName = cmd.m_shortName;
}
void
CommandLine::Clear (void)
@@ -95,8 +110,8 @@ CommandLine::Clear (void)
m_options.clear ();
m_nonOptions.clear ();
m_NNonOptions = 0;
m_usage = "";
m_name = "";
m_usage = "";
m_shortName = "";
}
void
@@ -108,7 +123,7 @@ CommandLine::Usage (const std::string usage)
std::string
CommandLine::GetName () const
{
return m_name;
return m_shortName;
}
CommandLine::Item::~Item ()
@@ -121,12 +136,12 @@ CommandLine::Parse (std::vector<std::string> args)
{
NS_LOG_FUNCTION (this << args.size () << args);
PrintDoxygenUsage ();
m_nonOptionCount = 0;
m_name = "";
if (args.size () > 0)
{
m_name = SystemPath::Split (args[0]).back ();
args.erase (args.begin ()); // discard the program name
for (auto param : args)
@@ -218,6 +233,7 @@ CommandLine::HandleNonOption (const std::string &value)
std::cerr << "Invalid non-option argument value "
<< value << " for " << i->m_name
<< std::endl;
PrintHelp (std::cerr);
std::exit (1);
}
++m_nonOptionCount;
@@ -242,7 +258,7 @@ CommandLine::PrintHelp (std::ostream &os) const
// Hack to show just the declared non-options
Items nonOptions (m_nonOptions.begin (),
m_nonOptions.begin () + m_NNonOptions);
os << m_name
os << m_shortName
<< (m_options.size () ? " [Program Options]" : "")
<< (nonOptions.size () ? " [Program Arguments]" : "")
<< " [General Arguments]"
@@ -316,6 +332,98 @@ CommandLine::PrintHelp (std::ostream &os) const
<< std::endl;
}
#include <unistd.h> // getcwd
void
CommandLine::PrintDoxygenUsage (void) const
{
NS_LOG_FUNCTION (this);
{
char buf[1024];
std::string cwd= getcwd (buf, 1024);
}
const char * envVar = std::getenv ("NS_COMMANDLINE_INTROSPECTION");
if (envVar == 0 || std::strlen (envVar) == 0)
{
return;
}
if (m_shortName.size () == 0)
{
NS_FATAL_ERROR ("No file name on example-to-run; forgot to use COMMANDLINE (var)?");
return;
}
// Hack to show just the declared non-options
Items nonOptions (m_nonOptions.begin (),
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;
}
if (!m_options.empty ())
{
os << std::endl;
os << "<h3>Program Options</h3>\n"
<< "<dl>\n";
for (auto i : m_options)
{
os << " <dt>\\c --" << i->m_name << "</dt>\n"
<< " <dd>" << i->m_help;
if ( i->HasDefault ())
{
os << " [" << i->GetDefault () << "]";
}
os << "</dd>\n";
}
os << "</dl>\n";
}
if (!nonOptions.empty ())
{
os << std::endl;
os << "<h3>Program Arguments</h3>\n"
<< "<dl>\n";
for (auto i : nonOptions)
{
os << " <dt> \\c " << i->m_name << "</dt>\n"
<< " <dd>" << i->m_help;
if ( i->HasDefault ())
{
os << " [" << i->GetDefault () << "]";
}
os << "</dd>\n";
}
os << "</dl>\n";
}
os << "*/" << std::endl;
// All done, don't need to actually run the example
os.close ();
std::exit (0);
}
void
CommandLine::PrintGlobals (std::ostream &os) const
{
@@ -514,6 +622,7 @@ CommandLine::HandleArgument (const std::string &name, const std::string &value)
{
std::cerr << "Invalid argument value: "
<< name << "=" << value << std::endl;
PrintHelp (std::cerr);
std::exit (1);
}
else

View File

@@ -45,6 +45,17 @@ namespace ns3 {
*
* The main entry point is CommandLine
*/
/**
* \ingroup commandline
* \brief Declare a CommandLine instance.
*
* This form is preferred since it supports creating Doxygen
* documentation for programs from the CommandLine configuration.
*/
#define COMMANDLINE(var) \
CommandLine var ( __FILE__ )
/**
* \ingroup commandline
* \brief Parse command-line arguments
@@ -105,7 +116,6 @@ namespace ns3 {
* 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
* \verbatim
@@ -133,7 +143,7 @@ namespace ns3 {
* bool boolArg = false;
* std::string strArg = "strArg default";
*
* CommandLine cmd;
* COMMANDLINE (cmd);
* cmd.Usage ("CommandLine example program.\n"
* "\n"
* "This little program demonstrates how to use CommandLine.");
@@ -194,7 +204,7 @@ namespace ns3 {
* int value1;
* int value2;
*
* CommandLine cmd;
* COMMANDLINE (cmd);
* cmd.Usage ("...");
* cmd.AddValue ("value1", "first value", value1);
* cmd.AddValue ("value2", "second value", value1);
@@ -208,12 +218,33 @@ namespace ns3 {
* exit (-1);
* }
* \endcode
*
* Finally, note that for examples which will be run by \c test.py
* the preferred declaration of a CommandLine instance is
* to use the \c COMMANDLINE macro to declare the instance name:
*
* \code
* COMMANDLINE (cmd);
* \endcode
* This will ensure that the program usage and arguments can be added to
* the Doxygen documentation automatically.
*/
class CommandLine
{
public:
/** Constructor */
CommandLine ();
CommandLine (void);
/**
* Construct and register the source file name.
* This would typically be called by using the \c COMMANDLINE macro:
* COMMANDLINE (cmd);
*
* This is just syntactic sugar for
* COMMANDLINE cmd (__FILE__);
* This form is required to generate Doxygen documentation of the
* arguments and options.
*/
CommandLine (const std::string filename);
/**
* Copy constructor
*
@@ -359,7 +390,7 @@ public:
*
* Alternatively, an overloaded operator << can be used:
* \code
* CommandLine cmd;
* COMMANDLINE (cmd);
* cmd.Parse (argc, argv);
* ...
*
@@ -520,6 +551,11 @@ private:
void Copy (const CommandLine &cmd);
/** Remove all arguments, Usage(), name */
void Clear (void);
/**
* Append usage message in Doxygen format to the file indicated
* by the NS_COMMANDLINE_INTROSPECTION environment variable.
*/
void PrintDoxygenUsage (void) const;
typedef std::vector<Item *> Items; /**< Argument list container */
Items m_options; /**< The list of option arguments */
@@ -527,7 +563,7 @@ private:
std::size_t m_NNonOptions; /**< The expected number of non-option arguments */
std::size_t m_nonOptionCount; /**< The number of actual non-option arguments seen so far. */
std::string m_usage; /**< The Usage string */
std::string m_name; /**< The program name */
std::string m_shortName; /**< The source file name (without `.cc`), as would be given to `waf --run` */
}; // class CommandLine
@@ -669,7 +705,7 @@ CommandLineHelper::UserItemParse (const std::string value, T & val)
*
* Example usage:
* \code
* CommandLine cmd;
* COMMANDLINE (cmd);
* cmd.Parse (argc, argv);
* ...
*

34
wscript
View File

@@ -11,6 +11,8 @@ import re
import shlex
import subprocess
import textwrap
import fileinput
import glob
from utils import read_config_file
@@ -1259,6 +1261,38 @@ def _print_introspected_doxygen(bld):
raise SystemExit(1)
text_out.close()
# Gather the CommandLine doxy
# test.py appears not to create or keep the output directory
# if no real tests are run, so we just stuff all the
# .command-line output files into testpy-output/
# NS_COMMANDLINE_INTROSPECTION=".." test.py --nowaf --constrain=example
Logs.info("Running CommandLine introspection")
proc_env['NS_COMMANDLINE_INTROSPECTION'] = '..'
subprocess.run(["test.py", "--nowaf", "--constrain=example"],
env=proc_env, stdout=subprocess.DEVNULL)
doxygen_out = os.path.join('doc', 'introspected-command-line.h')
try:
os.remove(doxygen_out)
except OSError as e:
pass
with open(doxygen_out, 'w') as out_file:
lines="""
/* This file is automatically generated by
CommandLine::PrintDoxygenUsage() from the CommandLine configuration
in various example programs. Do not edit this file! Edit the
CommandLine configuration in those files instead.
*/\n
"""
out_file.write(lines)
out_file.close()
with open(doxygen_out,'a') as outfile:
for in_file in glob.glob('testpy-output/*.command-line'):
with open(in_file,'r') as infile:
outfile.write(infile.read())
def _doxygen(bld, skip_pid=False):
env = wutils.bld.env
proc_env = wutils.get_proc_env()