8 from pathlib
import Path
10 CMAKELISTS_TEMPLATE =
'''\
11 check_include_file_cxx(stdint.h HAVE_STDINT_H)
13 add_definitions(-DHAVE_STDINT_H)
16 set(examples_as_tests_sources)
17 if(${{ENABLE_EXAMPLES}})
18 set(examples_as_tests_sources
19 #test/{MODULE}-examples-test-suite.cc
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}}
37 MODEL_CC_TEMPLATE =
'''/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
52 MODEL_H_TEMPLATE =
'''/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
53 #ifndef {INCLUDE_GUARD}
54 #define {INCLUDE_GUARD}
62 #endif /* {INCLUDE_GUARD} */
68 HELPER_CC_TEMPLATE =
'''/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
70 #include "{MODULE}-helper.h"
83 HELPER_H_TEMPLATE =
'''/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
84 #ifndef {INCLUDE_GUARD}
85 #define {INCLUDE_GUARD}
87 #include "ns3/{MODULE}.h"
95 #endif /* {INCLUDE_GUARD} */
100 EXAMPLES_CMAKELISTS_TEMPLATE =
'''\
102 NAME {MODULE}-example
103 SOURCE_FILES {MODULE}-example.cc
104 LIBRARIES_TO_LINK ${{lib{MODULE}}}
109 EXAMPLE_CC_TEMPLATE =
'''/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
111 #include "ns3/core-module.h"
112 #include "ns3/{MODULE}-helper.h"
118 main (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 ();
138 TEST_CC_TEMPLATE =
'''/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
140 // Include a header file from your module to test.
141 #include "ns3/{MODULE}.h"
143 // An essential include is test.h
144 #include "ns3/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.
151 class {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
190 class {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
204 static {CAPITALIZED}TestSuite s{COMPOUND}TestSuite;
209 DOC_RST_TEMPLATE =
'''Example Module Documentation
210 ----------------------------
212 .. include:: replace.txt
215 .. heading hierarchy:
216 ------------- Chapter
217 ************* Section (#.#)
218 ============= Subsection (#.#.#)
219 ############# Paragraph (no number)
221 This is a suggested outline for adding new module documentation to |ns3|.
222 See ``src/click/doc/click.rst`` for an example.
224 The introductory paragraph is for describing what this code is trying to
227 For consistency (italicized formatting), please use |ns3| to refer to
228 ns-3 in the documentation (and likewise, |ns2| for ns-2). These macros
229 are defined in the file ``replace.txt``.
234 The source code for the new module lives in the directory ``{MODULE_DIR}``.
236 Add here a basic description of what is being modeled.
241 Briefly describe the software design of the model and how it fits into
242 the existing ns-3 architecture.
244 Scope and Limitations
245 =====================
247 What can the model do? What can it not do? Please use this section to
248 describe the scope and limitations of the model.
253 Add academic citations here, such as if you published a paper on this
254 model, or if readers should read a particular specification or other work.
259 This section is principally concerned with the usage of your model, using
260 the public API. Focus first on most common usage patterns, then go
261 into more advanced topics.
266 Include this subsection only if there are special build instructions or
267 platform limitations.
272 What helper API will users typically use? Describe it here.
277 What classes hold attributes, and what are the key ones worth mentioning?
282 What kind of data does the model generate? What are the key trace
283 sources? What kind of logging output can be enabled?
288 Go into further details (such as using the API outside of the helpers)
289 in additional sections, as needed.
294 What examples using this new code are available? Describe them here.
299 Add any tips for avoiding pitfalls, etc.
304 Describe how the model has been tested/validated. What tests run in the
305 test suite? How much API and code is covered by the tests? Again,
306 references to outside published work may help here.
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
429 Generates the directory structure and skeleton files required for an ns-3
430 module. All of the generated files are valid C/C++ and will compile successfully
431 out of the box. ns3 configure must be run after creating new modules in order
432 to integrate them into the ns-3 build system.
434 The 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
453 character groups: letters, numbers, -, _
454 The script validates the module name and skips modules that have characters
455 outside of the above groups. One exception to the naming rule is that src/
456 or contrib/ may be added to the front of the module name to indicate where the
457 module scaffold should be created. If the module name starts with src/, then
458 the module is placed in the src directory. If the module name starts with
459 contrib/, then the module is placed in the contrib directory. If the module
460 name does not start with src/ or contrib/, then it defaults to contrib/.
461 See the examples section for use cases.
464 In some situations it can be useful to group multiple related modules under one
465 directory. Use the --project option to specify a common parent directory where
466 the modules should be generated. The value passed to --project is treated
467 as a relative path. The path components have the same naming requirements as
468 the module name: letters, numbers, -, _
469 The project directory is placed under the contrib directory and any parts of the
470 path that do not exist will be created. Creating projects in the src directory
471 is 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
473 as 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")
619 if __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()