8from pathlib
import Path
10CMAKELISTS_TEMPLATE =
'''\
11check_include_file_cxx(stdint.h HAVE_STDINT_H)
13 add_definitions(-DHAVE_STDINT_H)
16set(examples_as_tests_sources)
17if(${{ENABLE_EXAMPLES}})
18 set(examples_as_tests_sources
25 SOURCE_FILES model/{MODULE}.cc
26 helper/{MODULE}-helper.cc
27 HEADER_FILES model/{MODULE}.h
28 helper/{MODULE}-helper.h
29 LIBRARIES_TO_LINK ${{libcore}}
30 TEST_SOURCES test/{MODULE}-test-suite.cc
31 ${{examples_as_tests_sources}}
37MODEL_CC_TEMPLATE = '''/* -*- Mode:C++; c-file-style:
"gnu"; indent-tabs-mode:nil; -*- */
52MODEL_H_TEMPLATE = '''/* -*- Mode:C++; c-file-style:
"gnu"; indent-tabs-mode:nil; -*- */
68HELPER_CC_TEMPLATE = '''/* -*- Mode:C++; c-file-style:
"gnu"; indent-tabs-mode:nil; -*- */
83HELPER_H_TEMPLATE = '''/* -*- Mode:C++; c-file-style:
"gnu"; indent-tabs-mode:nil; -*- */
100EXAMPLES_CMAKELISTS_TEMPLATE = '''\
102 NAME {MODULE}-example
103 SOURCE_FILES {MODULE}-example.cc
104 LIBRARIES_TO_LINK ${{lib{MODULE}}}
109EXAMPLE_CC_TEMPLATE = '''/* -*- Mode:C++; c-file-style:
"gnu"; indent-tabs-mode:nil; -*- */
118main (int argc, char *argv[])
122 CommandLine cmd (__FILE__);
123 cmd.AddValue (
"verbose",
"Tell application to log if true", verbose);
125 cmd.Parse (argc,argv);
130 Simulator::Destroy ();
138TEST_CC_TEMPLATE = '''/* -*- Mode:C++; c-file-style:
"gnu"; indent-tabs-mode:nil; -*- */
140// Include a header file
from your module to test.
143// An essential include
is test.h
146// Do
not put your test classes
in namespace ns3. You may find it useful
147// to use the using directive to access the ns3 namespace directly
150// This
is an example TestCase.
151class {CAPITALIZED}TestCase1 : public TestCase
154 {CAPITALIZED}TestCase1 ();
155 virtual ~{CAPITALIZED}TestCase1 ();
158 virtual void DoRun (void);
161// Add some help text to this case to describe what it
is intended to test
162{CAPITALIZED}TestCase1::{CAPITALIZED}TestCase1 ()
163 : TestCase (
"{CAPITALIZED} test case (does nothing)")
167// This destructor does nothing but we include it
as a reminder that
168// the test case should clean up after itself
169{CAPITALIZED}TestCase1::~{CAPITALIZED}TestCase1 ()
174// This method
is the pure virtual method
from class TestCase that every
175// TestCase must implement
178{CAPITALIZED}TestCase1::DoRun (void)
180 // A wide variety of test macros are available
in src/core/test.h
181 NS_TEST_ASSERT_MSG_EQ (true, true,
"true doesn\'t equal true for some reason");
182 // Use this one
for floating point comparisons
183 NS_TEST_ASSERT_MSG_EQ_TOL (0.01, 0.01, 0.001,
"Numbers are not equal within tolerance");
186// The TestSuite
class names the TestSuite, identifies what type of TestSuite,
187//
and enables the TestCases to be run. Typically, only the constructor
for
188// this
class must be defined
190class {CAPITALIZED}TestSuite : public TestSuite
193 {CAPITALIZED}TestSuite ();
196{CAPITALIZED}TestSuite::{CAPITALIZED}TestSuite ()
197 : TestSuite (
"{MODULE}", UNIT)
199 // TestDuration
for TestCase can be QUICK, EXTENSIVE
or TAKES_FOREVER
200 AddTestCase (new {CAPITALIZED}TestCase1, TestCase::QUICK);
203// Do
not forget to allocate an instance of this TestSuite
204static {CAPITALIZED}TestSuite s{COMPOUND}TestSuite;
209DOC_RST_TEMPLATE = '''Example Module Documentation
210----------------------------
212.. include:: replace.txt
216 ------------- Chapter
217 ************* Section (
218 ============= Subsection (
221This
is a suggested outline
for adding new module documentation to |ns3|.
222See ``src/click/doc/click.rst``
for an example.
224The introductory paragraph
is for describing what this code
is trying to
227For consistency (italicized formatting), please use |ns3| to refer to
228ns-3
in the documentation (
and likewise, |ns2|
for ns-2). These macros
229are defined
in the file ``replace.txt``.
234The source code
for the new module lives
in the directory ``{MODULE_DIR}``.
236Add here a basic description of what
is being modeled.
241Briefly describe the software design of the model
and how it fits into
242the existing ns-3 architecture.
247What can the model do? What can it
not do? Please use this section to
248describe the scope
and limitations of the model.
253Add academic citations here, such
as if you published a paper on this
254model,
or if readers should read a particular specification
or other work.
259This section
is principally concerned
with the usage of your model, using
260the public API. Focus first on most common usage patterns, then go
261into more advanced topics.
266Include this subsection only
if there are special build instructions
or
272What helper API will users typically use? Describe it here.
277What classes hold attributes,
and what are the key ones worth mentioning?
282What kind of data does the model generate? What are the key trace
283sources? What kind of logging output can be enabled?
288Go into further details (such
as using the API outside of the helpers)
289in additional sections,
as needed.
294What examples using this new code are available? Describe them here.
299Add any tips
for avoiding pitfalls, etc.
304Describe how the model has been tested/validated. What tests run
in the
305test suite? How much API
and code
is covered by the tests? Again,
306references to outside published work may help here.
309def create_file(path, template, **kwargs):
310 artifact_path = Path(path)
313 with artifact_path.open(
"wt")
as f:
314 f.write(template.format(**kwargs))
318 path = Path(moduledir,
'CMakeLists.txt')
320 create_file(path, CMAKELISTS_TEMPLATE, MODULE=modname)
326 modelpath = Path(moduledir,
"model")
327 modelpath.mkdir(parents=
True)
329 srcfile_path = modelpath.joinpath(modname).with_suffix(
'.cc')
330 create_file(srcfile_path, MODEL_CC_TEMPLATE, MODULE=modname)
332 hfile_path = modelpath.joinpath(modname).with_suffix(
'.h')
333 guard =
"{}_H".format(modname.replace(
'-',
'_').upper())
342 testpath = Path(moduledir,
"test")
343 testpath.mkdir(parents=
True)
345 file_path = testpath.joinpath(modname+
'-test-suite').with_suffix(
'.cc')
346 name_parts = modname.split(
'-')
347 create_file(file_path, TEST_CC_TEMPLATE, MODULE=modname,
348 CAPITALIZED=
''.join([word.capitalize()
for word
in name_parts]),
349 COMPOUND=
''.join([word.capitalize()
if index > 0
else word
for index, word
in enumerate(name_parts)]))
355 helperpath = Path(moduledir,
"helper")
356 helperpath.mkdir(parents=
True)
358 srcfile_path = helperpath.joinpath(modname+
'-helper').with_suffix(
'.cc')
359 create_file(srcfile_path, HELPER_CC_TEMPLATE, MODULE=modname)
361 h_file_path = helperpath.joinpath(modname+
'-helper').with_suffix(
'.h')
362 guard =
"{}_HELPER_H".format(modname.replace(
'-',
'_').upper())
363 create_file(h_file_path, HELPER_H_TEMPLATE, MODULE=modname, INCLUDE_GUARD=guard)
369 examplespath = Path(moduledir,
"examples")
370 examplespath.mkdir(parents=
True)
372 cmakelistspath = Path(examplespath,
'CMakeLists.txt')
373 create_file(cmakelistspath, EXAMPLES_CMAKELISTS_TEMPLATE, MODULE=modname)
375 examplesfile_path = examplespath.joinpath(modname+
'-example').with_suffix(
'.cc')
376 create_file(examplesfile_path, EXAMPLE_CC_TEMPLATE, MODULE=modname)
382 docpath = Path(moduledir,
"doc")
383 docpath.mkdir(parents=
True)
387 mod_relpath = os.path.relpath(str(moduledir))
389 file_name =
'{}.rst'.format(modname)
390 file_path = Path(docpath, file_name)
391 create_file(file_path, DOC_RST_TEMPLATE, MODULE=modname, MODULE_DIR=mod_relpath)
397 modulepath = Path(modpath, modname)
399 if modulepath.exists():
400 print(
"Module {!r} already exists".format(modname), file=sys.stderr)
403 print(
"Creating module {}".format(modulepath))
405 functions = (make_cmakelists, make_model, make_test,
406 make_helper, make_examples, make_doc)
409 modulepath.mkdir(parents=
True)
411 success = all(func(modulepath, modname)
for func
in functions)
414 raise ValueError(
"Generating module artifacts failed")
416 except Exception
as e:
417 if modulepath.exists():
418 shutil.rmtree(modulepath)
420 print(
"Creating module {!r} failed: {}".format(modname, str(e)), file=sys.stderr)
427 description =
"""Generate scaffolding for ns-3 modules
429Generates the directory structure and skeleton files required for an ns-3
430module. All of the generated files are valid C/C++ and will compile successfully
431out of the box. ns3 configure must be run after creating new modules
in order
432to integrate them into the ns-3 build system.
434The following directory structure
is generated under the contrib directory:
440 |-- <modname>-example.cc
443 |-- <modname>-helper.cc
444 |-- <modname>-helper.h
449 |-- <modname>-test-suite.cc
452<modname>
is the name of the module
and is restricted to the following
453character groups: letters, numbers, -, _
454The script validates the module name
and skips modules that have characters
455outside of the above groups. One exception to the naming rule
is that src/
456or contrib/ may be added to the front of the module name to indicate where the
457module scaffold should be created. If the module name starts
with src/, then
458the module
is placed
in the src directory. If the module name starts
with
459contrib/, then the module
is placed
in the contrib directory. If the module
460name does
not start
with src/
or contrib/, then it defaults to contrib/.
461See the examples section
for use cases.
464In some situations it can be useful to group multiple related modules under one
465directory. Use the --project option to specify a common parent directory where
466the modules should be generated. The value passed to --project
is treated
467as a relative path. The path components have the same naming requirements
as
468the module name: letters, numbers, -, _
469The project directory
is placed under the contrib directory
and any parts of the
470path that do
not exist will be created. Creating projects
in the src directory
471is not supported. Module names that start
with src/ are
not allowed when
472--project
is used. Module names that start
with contrib/ are treated the same
473as module names that don
't start with contrib/ and are generated under the
477 epilog = """Examples:
479 %(prog)s contrib/module1
481 Creates a new module named module1 under the contrib directory
485 Creates a new module named module1 under the src directory
487 %(prog)s src/module1 contrib/module2, module3
489 Creates three modules, one under the src directory and two under the
492 %(prog)s --project myproject module1 module2
494 Creates two modules under contrib/myproject
496 %(prog)s --project myproject/sub_project module1 module2
498 Creates two modules under contrib/myproject/sub_project
502 formatter = argparse.RawDescriptionHelpFormatter
504 parser = argparse.ArgumentParser(description=description,
506 formatter_class=formatter)
508 parser.add_argument('--project', default=
'',
509 help=(
"Specify a relative path under the contrib directory "
510 "where the new modules will be generated. The path "
511 "will be created if it does not exist."))
513 parser.add_argument(
'modnames', nargs=
'+',
514 help=(
"One or more modules to generate. Module names "
515 "are limited to the following: letters, numbers, -, "
516 "_. Modules are generated under the contrib directory "
517 "except when the module name starts with src/. Modules "
518 "that start with src/ are generated under the src "
526 args = parser.parse_args(argv[1:])
528 project = args.project
529 modnames = args.modnames
531 base_path = Path.cwd()
533 src_path = base_path.joinpath(
'src')
534 contrib_path = base_path.joinpath(
'contrib')
536 for p
in (src_path, contrib_path):
538 parser.error(
"Cannot find the directory '{}'.\nPlease run this "
539 "script from the top level of the ns3 directory".format(
547 allowedRE = re.compile(
'^(\w|-)+$')
554 project_path = Path(project)
556 if project_path.is_absolute():
558 project_path = project_path.relative_to(os.sep)
560 if not all(allowedRE.match(part)
for part
in project_path.parts):
561 parser.error(
'Project path may only contain the characters [a-zA-Z0-9_-].')
566 for name
in modnames:
569 name = name.strip(os.sep)
575 name_path = Path(name)
577 if len(name_path.parts) > 2:
578 print(
"Skipping {}: module name can not be a path".format(name))
582 modpath = contrib_path
584 if name_path.parts[0] ==
'src':
586 parser.error(
"{}: Cannot specify src/ in a module name when --project option is used".format(name))
591 name_path = name_path.relative_to(
'src')
593 elif name_path.parts[0] ==
'contrib':
594 modpath = contrib_path
597 name_path = name_path.relative_to(
'contrib')
602 modpath = contrib_path.joinpath(project_path)
604 modname = name_path.parts[0]
606 if not allowedRE.match(modname):
607 print(
"Skipping {}: module name may only contain the characters [a-zA-Z0-9_-]".format(modname))
610 modules.append((modpath, modname))
612 if all(
make_module(*module)
for module
in modules):
614 print(
"Successfully created new modules")
615 print(
"Run './ns3 configure' to include them in the build")
619if __name__ ==
'__main__':
622 return_value = main(sys.argv)
623 except Exception
as e:
624 print(
"Exception: '{}'".format(e), file=sys.stderr)
627 sys.exit(return_value)
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()