7from pathlib
import Path
9CMAKELISTS_TEMPLATE =
"""\
10check_include_file_cxx(stdint.h HAVE_STDINT_H)
12 add_definitions(-DHAVE_STDINT_H)
15set(examples_as_tests_sources)
16if(${{ENABLE_EXAMPLES}})
17 set(examples_as_tests_sources
24 SOURCE_FILES model/{MODULE}.cc
25 helper/{MODULE}-helper.cc
26 HEADER_FILES model/{MODULE}.h
27 helper/{MODULE}-helper.h
28 LIBRARIES_TO_LINK ${{libcore}}
29 TEST_SOURCES test/{MODULE}-test-suite.cc
30 ${{examples_as_tests_sources}}
35MODEL_CC_TEMPLATE = """\
47MODEL_H_TEMPLATE = """\
51// Add a doxygen group
for this module.
52// If you have more than one file, this should be
in only one of them.
54 * \defgroup {MODULE} Description of the {MODULE}
60// Each
class should be documented using Doxygen,
61//
and have an \ingroup {MODULE} directive
71HELPER_CC_TEMPLATE = """\
83HELPER_H_TEMPLATE = """\
92// Each
class should be documented using Doxygen,
93//
and have an \ingroup {MODULE} directive
103EXAMPLES_CMAKELISTS_TEMPLATE = """\
105 NAME {MODULE}-example
106 SOURCE_FILES {MODULE}-example.cc
107 LIBRARIES_TO_LINK ${{lib{MODULE}}}
111EXAMPLE_CC_TEMPLATE = """\
118 * Explain here what the example does.
124main(int argc, char* argv[])
128 CommandLine cmd(__FILE__);
129 cmd.AddValue(
"verbose",
"Tell application to log if true", verbose);
131 cmd.Parse(argc, argv);
136 Simulator::Destroy();
142TEST_CC_TEMPLATE = """\
144// Include a header file
from your module to test.
147// An essential include
is test.h
150// Do
not put your test classes
in namespace ns3. You may find it useful
151// to use the using directive to access the ns3 namespace directly
154// Add a doxygen group
for tests.
155// If you have more than one test, this should be
in only one of them.
157 * \defgroup {MODULE}-tests Tests
for {MODULE}
162// This
is an example TestCase.
164 * \ingroup {MODULE}-tests
165 * Test case
for feature 1
167class {CAPITALIZED}TestCase1 : public TestCase
170 {CAPITALIZED}TestCase1();
171 virtual ~{CAPITALIZED}TestCase1();
174 void DoRun() override;
177// Add some help text to this case to describe what it
is intended to test
178{CAPITALIZED}TestCase1::{CAPITALIZED}TestCase1()
179 : TestCase(
"{CAPITALIZED} test case (does nothing)")
183// This destructor does nothing but we include it
as a reminder that
184// the test case should clean up after itself
185{CAPITALIZED}TestCase1::~{CAPITALIZED}TestCase1()
190// This method
is the pure virtual method
from class TestCase that every
191// TestCase must implement
194{CAPITALIZED}TestCase1::DoRun()
196 // A wide variety of test macros are available
in src/core/test.h
198 // Use this one
for floating point comparisons
202// The TestSuite
class names the TestSuite, identifies what type of TestSuite,
203//
and enables the TestCases to be run. Typically, only the constructor
for
204// this
class must be defined
207 * \ingroup {MODULE}-tests
208 * TestSuite
for module {MODULE}
210class {CAPITALIZED}TestSuite : public TestSuite
213 {CAPITALIZED}TestSuite();
216{CAPITALIZED}TestSuite::{CAPITALIZED}TestSuite()
217 : TestSuite(
"{MODULE}", UNIT)
219 // TestDuration
for TestCase can be QUICK, EXTENSIVE
or TAKES_FOREVER
220 AddTestCase(new {CAPITALIZED}TestCase1, TestCase::QUICK);
223// Do
not forget to allocate an instance of this TestSuite
225 * \ingroup {MODULE}-tests
226 * Static variable
for test initialization
228static {CAPITALIZED}TestSuite s{COMPOUND}TestSuite;
232DOC_RST_TEMPLATE = """Example Module Documentation
233----------------------------
235.. include:: replace.txt
239 ------------- Chapter
240 ************* Section (
241 ============= Subsection (
244This
is a suggested outline
for adding new module documentation to |ns3|.
245See ``src/click/doc/click.rst``
for an example.
247The introductory paragraph
is for describing what this code
is trying to
250For consistency (italicized formatting), please use |ns3| to refer to
251ns-3
in the documentation (
and likewise, |ns2|
for ns-2). These macros
252are defined
in the file ``replace.txt``.
257The source code
for the new module lives
in the directory ``{MODULE_DIR}``.
259Add here a basic description of what
is being modeled.
264Briefly describe the software design of the model
and how it fits into
265the existing ns-3 architecture.
270What can the model do? What can it
not do? Please use this section to
271describe the scope
and limitations of the model.
276Add academic citations here, such
as if you published a paper on this
277model,
or if readers should read a particular specification
or other work.
282This section
is principally concerned
with the usage of your model, using
283the public API. Focus first on most common usage patterns, then go
284into more advanced topics.
289Include this subsection only
if there are special build instructions
or
295What helper API will users typically use? Describe it here.
300What classes hold attributes,
and what are the key ones worth mentioning?
305What kind of data does the model generate? What are the key trace
306sources? What kind of logging output can be enabled?
311Go into further details (such
as using the API outside of the helpers)
312in additional sections,
as needed.
317What examples using this new code are available? Describe them here.
322Add any tips
for avoiding pitfalls, etc.
327Describe how the model has been tested/validated. What tests run
in the
328test suite? How much API
and code
is covered by the tests? Again,
329references to outside published work may help here.
333def create_file(path, template, **kwargs):
334 artifact_path = Path(path)
337 with artifact_path.open(
"wt", encoding=
"utf-8")
as f:
338 f.write(template.format(**kwargs))
342 path = Path(moduledir,
"CMakeLists.txt")
344 create_file(path, CMAKELISTS_TEMPLATE, MODULE=modname)
350 modelpath = Path(moduledir,
"model")
351 modelpath.mkdir(parents=
True)
353 srcfile_path = modelpath.joinpath(modname).with_suffix(
".cc")
354 create_file(srcfile_path, MODEL_CC_TEMPLATE, MODULE=modname)
356 hfile_path = modelpath.joinpath(modname).with_suffix(
".h")
357 guard =
"{}_H".format(modname.replace(
"-",
"_").upper())
358 create_file(hfile_path, MODEL_H_TEMPLATE, MODULE=modname, INCLUDE_GUARD=guard)
364 testpath = Path(moduledir,
"test")
365 testpath.mkdir(parents=
True)
367 file_path = testpath.joinpath(modname +
"-test-suite").with_suffix(
".cc")
368 name_parts = modname.split(
"-")
373 CAPITALIZED=
"".join([word.capitalize()
for word
in name_parts]),
375 [word.capitalize()
if index > 0
else word
for index, word
in enumerate(name_parts)]
383 helperpath = Path(moduledir,
"helper")
384 helperpath.mkdir(parents=
True)
386 srcfile_path = helperpath.joinpath(modname +
"-helper").with_suffix(
".cc")
387 create_file(srcfile_path, HELPER_CC_TEMPLATE, MODULE=modname)
389 h_file_path = helperpath.joinpath(modname +
"-helper").with_suffix(
".h")
390 guard =
"{}_HELPER_H".format(modname.replace(
"-",
"_").upper())
391 create_file(h_file_path, HELPER_H_TEMPLATE, MODULE=modname, INCLUDE_GUARD=guard)
397 examplespath = Path(moduledir,
"examples")
398 examplespath.mkdir(parents=
True)
400 cmakelistspath = Path(examplespath,
"CMakeLists.txt")
401 create_file(cmakelistspath, EXAMPLES_CMAKELISTS_TEMPLATE, MODULE=modname)
403 examplesfile_path = examplespath.joinpath(modname +
"-example").with_suffix(
".cc")
404 create_file(examplesfile_path, EXAMPLE_CC_TEMPLATE, MODULE=modname)
410 docpath = Path(moduledir,
"doc")
411 docpath.mkdir(parents=
True)
415 mod_relpath = os.path.relpath(str(moduledir))
417 file_name =
"{}.rst".format(modname)
418 file_path = Path(docpath, file_name)
419 create_file(file_path, DOC_RST_TEMPLATE, MODULE=modname, MODULE_DIR=mod_relpath)
425 modulepath = Path(modpath, modname)
427 if modulepath.exists():
428 print(
"Module {!r} already exists".format(modname), file=sys.stderr)
431 print(
"Creating module {}".format(modulepath))
433 functions = (make_cmakelists, make_model, make_test, make_helper, make_examples, make_doc)
436 modulepath.mkdir(parents=
True)
438 success = all(func(modulepath, modname)
for func
in functions)
441 raise ValueError(
"Generating module artifacts failed")
443 except Exception
as e:
444 if modulepath.exists():
445 shutil.rmtree(modulepath)
447 print(
"Creating module {!r} failed: {}".format(modname, str(e)), file=sys.stderr)
455 description =
"""Generate scaffolding for ns-3 modules
457Generates the directory structure and skeleton files required for an ns-3
458module. All of the generated files are valid C/C++ and will compile successfully
459out of the box. ns3 configure must be run after creating new modules
in order
460to integrate them into the ns-3 build system.
462The following directory structure
is generated under the contrib directory:
468 |-- <modname>-example.cc
471 |-- <modname>-helper.cc
472 |-- <modname>-helper.h
477 |-- <modname>-test-suite.cc
480<modname>
is the name of the module
and is restricted to the following
481character groups: letters, numbers, -, _
482The script validates the module name
and skips modules that have characters
483outside of the above groups. One exception to the naming rule
is that src/
484or contrib/ may be added to the front of the module name to indicate where the
485module scaffold should be created. If the module name starts
with src/, then
486the module
is placed
in the src directory. If the module name starts
with
487contrib/, then the module
is placed
in the contrib directory. If the module
488name does
not start
with src/
or contrib/, then it defaults to contrib/.
489See the examples section
for use cases.
492In some situations it can be useful to group multiple related modules under one
493directory. Use the --project option to specify a common parent directory where
494the modules should be generated. The value passed to --project
is treated
495as a relative path. The path components have the same naming requirements
as
496the module name: letters, numbers, -, _
497The project directory
is placed under the contrib directory
and any parts of the
498path that do
not exist will be created. Creating projects
in the src directory
499is not supported. Module names that start
with src/ are
not allowed when
500--project
is used. Module names that start
with contrib/ are treated the same
501as module names that don
't start with contrib/ and are generated under the
505 epilog = """Examples:
507 %(prog)s contrib/module1
509 Creates a new module named module1 under the contrib directory
513 Creates a new module named module1 under the src directory
515 %(prog)s src/module1 contrib/module2, module3
517 Creates three modules, one under the src directory and two under the
520 %(prog)s --project myproject module1 module2
522 Creates two modules under contrib/myproject
524 %(prog)s --project myproject/sub_project module1 module2
526 Creates two modules under contrib/myproject/sub_project
530 formatter = argparse.RawDescriptionHelpFormatter
532 parser = argparse.ArgumentParser(
533 description=description, epilog=epilog, formatter_class=formatter
540 "Specify a relative path under the contrib directory "
541 "where the new modules will be generated. The path "
542 "will be created if it does not exist."
550 "One or more modules to generate. Module names "
551 "are limited to the following: letters, numbers, -, "
552 "_. Modules are generated under the contrib directory "
553 "except when the module name starts with src/. Modules "
554 "that start with src/ are generated under the src "
565 args = parser.parse_args(argv[1:])
567 project = args.project
568 modnames = args.modnames
570 base_path = Path.cwd()
572 src_path = base_path.joinpath(
"src")
573 contrib_path = base_path.joinpath(
"contrib")
575 for p
in (src_path, contrib_path):
578 "Cannot find the directory '{}'.\nPlease run this "
579 "script from the top level of the ns3 directory".format(p)
587 allowedRE = re.compile(
"^(\w|-)+$")
594 project_path = Path(project)
596 if project_path.is_absolute():
598 project_path = project_path.relative_to(os.sep)
600 if not all(allowedRE.match(part)
for part
in project_path.parts):
601 parser.error(
"Project path may only contain the characters [a-zA-Z0-9_-].")
606 for name
in modnames:
609 name = name.strip(os.sep)
615 name_path = Path(name)
617 if len(name_path.parts) > 2:
618 print(
"Skipping {}: module name can not be a path".format(name))
622 modpath = contrib_path
624 if name_path.parts[0] ==
"src":
627 "{}: Cannot specify src/ in a module name when --project option is used".format(
635 name_path = name_path.relative_to(
"src")
637 elif name_path.parts[0] ==
"contrib":
638 modpath = contrib_path
641 name_path = name_path.relative_to(
"contrib")
646 modpath = contrib_path.joinpath(project_path)
648 modname = name_path.parts[0]
650 if not allowedRE.match(modname):
652 "Skipping {}: module name may only contain the characters [a-zA-Z0-9_-]".format(
658 modules.append((modpath, modname))
660 if all(
make_module(*module)
for module
in modules):
662 print(
"Successfully created new modules")
663 print(
"Run './ns3 configure' to include them in the build")
668if __name__ ==
"__main__":
671 return_value = main(sys.argv)
672 except Exception
as e:
673 print(
"Exception: '{}'".format(e), file=sys.stderr)
676 sys.exit(return_value)
#define NS_TEST_ASSERT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report and abort if not.
#define NS_TEST_ASSERT_MSG_EQ_TOL(actual, limit, tol, msg)
Test that actual and expected (limit) values are equal to plus or minus some tolerance and report and...
def make_module(modpath, modname)
def make_helper(moduledir, modname)
def make_test(moduledir, modname)
def create_file(path, template, **kwargs)
def make_model(moduledir, modname)
def make_examples(moduledir, modname)
def make_cmakelists(moduledir, modname)
def make_doc(moduledir, modname)
def create_argument_parser()