2020-03-13 17:12:57 -07:00
|
|
|
#! /usr/bin/env python3
|
|
|
|
|
import sys
|
|
|
|
|
import argparse
|
|
|
|
|
import os
|
|
|
|
|
import re
|
|
|
|
|
import shutil
|
|
|
|
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
2021-11-10 22:28:44 -03:00
|
|
|
CMAKELISTS_TEMPLATE = '''\
|
|
|
|
|
check_include_file_cxx(stdint.h HAVE_STDINT_H)
|
|
|
|
|
if(HAVE_STDINT_H)
|
|
|
|
|
add_definitions(-DHAVE_STDINT_H)
|
|
|
|
|
endif()
|
|
|
|
|
|
|
|
|
|
set(name {MODULE})
|
|
|
|
|
|
|
|
|
|
set(source_files
|
|
|
|
|
model/{MODULE}.cc
|
|
|
|
|
helper/{MODULE}-helper.cc
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
set(header_files
|
|
|
|
|
model/{MODULE}.h
|
|
|
|
|
helper/{MODULE}-helper.h
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
set(libraries_to_link
|
|
|
|
|
${{libcore}}
|
|
|
|
|
)
|
|
|
|
|
|
2021-12-05 21:53:49 +00:00
|
|
|
if(${{EXAMPLES_ENABLED}})
|
2021-11-10 22:28:44 -03:00
|
|
|
set(examples_as_tests_sources
|
|
|
|
|
#test/{MODULE}-examples-test-suite.cc
|
|
|
|
|
)
|
|
|
|
|
endif()
|
|
|
|
|
|
|
|
|
|
set(test_sources
|
|
|
|
|
test/{MODULE}-test-suite.cc
|
|
|
|
|
${{examples_as_tests_sources}}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
{BUILD_LIB_MACRO}("${{name}}" "${{source_files}}" "${{header_files}}" "${{libraries_to_link}}" "${{test_sources}}")
|
|
|
|
|
|
2020-03-13 17:12:57 -07:00
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MODEL_CC_TEMPLATE = '''/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
|
|
|
|
|
|
|
|
#include "{MODULE}.h"
|
|
|
|
|
|
|
|
|
|
namespace ns3 {{
|
|
|
|
|
|
|
|
|
|
/* ... */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MODEL_H_TEMPLATE = '''/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
|
|
|
#ifndef {INCLUDE_GUARD}
|
|
|
|
|
#define {INCLUDE_GUARD}
|
|
|
|
|
|
|
|
|
|
namespace ns3 {{
|
|
|
|
|
|
|
|
|
|
/* ... */
|
|
|
|
|
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
#endif /* {INCLUDE_GUARD} */
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HELPER_CC_TEMPLATE = '''/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
|
|
|
|
|
|
|
|
#include "{MODULE}-helper.h"
|
|
|
|
|
|
|
|
|
|
namespace ns3 {{
|
|
|
|
|
|
|
|
|
|
/* ... */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HELPER_H_TEMPLATE = '''/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
|
|
|
#ifndef {INCLUDE_GUARD}
|
|
|
|
|
#define {INCLUDE_GUARD}
|
|
|
|
|
|
|
|
|
|
#include "ns3/{MODULE}.h"
|
|
|
|
|
|
|
|
|
|
namespace ns3 {{
|
|
|
|
|
|
|
|
|
|
/* ... */
|
|
|
|
|
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
#endif /* {INCLUDE_GUARD} */
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
2021-11-10 22:28:44 -03:00
|
|
|
EXAMPLES_CMAKELISTS_TEMPLATE = '''\
|
|
|
|
|
set(name {MODULE}-example)
|
|
|
|
|
set(source_files ${{name}}.cc)
|
|
|
|
|
set(header_files)
|
|
|
|
|
set(libraries_to_link ${{lib{MODULE}}})
|
|
|
|
|
{BUILD_EXAMPLE_MACRO}("${{name}}" "${{source_files}}" "${{header_files}}" "${{libraries_to_link}}")
|
2020-03-13 17:12:57 -07:00
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
EXAMPLE_CC_TEMPLATE = '''/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
|
|
|
|
|
|
|
|
#include "ns3/core-module.h"
|
|
|
|
|
#include "ns3/{MODULE}-helper.h"
|
|
|
|
|
|
|
|
|
|
using namespace ns3;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
main (int argc, char *argv[])
|
|
|
|
|
{{
|
|
|
|
|
bool verbose = true;
|
|
|
|
|
|
2020-06-23 22:53:23 +02:00
|
|
|
CommandLine cmd (__FILE__);
|
2020-03-13 17:12:57 -07:00
|
|
|
cmd.AddValue ("verbose", "Tell application to log if true", verbose);
|
|
|
|
|
|
|
|
|
|
cmd.Parse (argc,argv);
|
|
|
|
|
|
|
|
|
|
/* ... */
|
|
|
|
|
|
|
|
|
|
Simulator::Run ();
|
|
|
|
|
Simulator::Destroy ();
|
|
|
|
|
return 0;
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TEST_CC_TEMPLATE = '''/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
|
|
|
|
|
|
|
|
// Include a header file from your module to test.
|
|
|
|
|
#include "ns3/{MODULE}.h"
|
|
|
|
|
|
|
|
|
|
// An essential include is test.h
|
|
|
|
|
#include "ns3/test.h"
|
|
|
|
|
|
|
|
|
|
// Do not put your test classes in namespace ns3. You may find it useful
|
|
|
|
|
// to use the using directive to access the ns3 namespace directly
|
|
|
|
|
using namespace ns3;
|
|
|
|
|
|
|
|
|
|
// This is an example TestCase.
|
|
|
|
|
class {CAPITALIZED}TestCase1 : public TestCase
|
|
|
|
|
{{
|
|
|
|
|
public:
|
|
|
|
|
{CAPITALIZED}TestCase1 ();
|
|
|
|
|
virtual ~{CAPITALIZED}TestCase1 ();
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
virtual void DoRun (void);
|
|
|
|
|
}};
|
|
|
|
|
|
|
|
|
|
// Add some help text to this case to describe what it is intended to test
|
|
|
|
|
{CAPITALIZED}TestCase1::{CAPITALIZED}TestCase1 ()
|
|
|
|
|
: TestCase ("{CAPITALIZED} test case (does nothing)")
|
|
|
|
|
{{
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
// This destructor does nothing but we include it as a reminder that
|
|
|
|
|
// the test case should clean up after itself
|
|
|
|
|
{CAPITALIZED}TestCase1::~{CAPITALIZED}TestCase1 ()
|
|
|
|
|
{{
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// This method is the pure virtual method from class TestCase that every
|
|
|
|
|
// TestCase must implement
|
|
|
|
|
//
|
|
|
|
|
void
|
|
|
|
|
{CAPITALIZED}TestCase1::DoRun (void)
|
|
|
|
|
{{
|
|
|
|
|
// A wide variety of test macros are available in src/core/test.h
|
|
|
|
|
NS_TEST_ASSERT_MSG_EQ (true, true, "true doesn\'t equal true for some reason");
|
|
|
|
|
// Use this one for floating point comparisons
|
|
|
|
|
NS_TEST_ASSERT_MSG_EQ_TOL (0.01, 0.01, 0.001, "Numbers are not equal within tolerance");
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
// The TestSuite class names the TestSuite, identifies what type of TestSuite,
|
|
|
|
|
// and enables the TestCases to be run. Typically, only the constructor for
|
|
|
|
|
// this class must be defined
|
|
|
|
|
//
|
|
|
|
|
class {CAPITALIZED}TestSuite : public TestSuite
|
|
|
|
|
{{
|
|
|
|
|
public:
|
|
|
|
|
{CAPITALIZED}TestSuite ();
|
|
|
|
|
}};
|
|
|
|
|
|
|
|
|
|
{CAPITALIZED}TestSuite::{CAPITALIZED}TestSuite ()
|
|
|
|
|
: TestSuite ("{MODULE}", UNIT)
|
|
|
|
|
{{
|
|
|
|
|
// TestDuration for TestCase can be QUICK, EXTENSIVE or TAKES_FOREVER
|
|
|
|
|
AddTestCase (new {CAPITALIZED}TestCase1, TestCase::QUICK);
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
// Do not forget to allocate an instance of this TestSuite
|
|
|
|
|
static {CAPITALIZED}TestSuite s{COMPOUND}TestSuite;
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DOC_RST_TEMPLATE = '''Example Module Documentation
|
|
|
|
|
----------------------------
|
|
|
|
|
|
|
|
|
|
.. include:: replace.txt
|
|
|
|
|
.. highlight:: cpp
|
|
|
|
|
|
|
|
|
|
.. heading hierarchy:
|
|
|
|
|
------------- Chapter
|
|
|
|
|
************* Section (#.#)
|
|
|
|
|
============= Subsection (#.#.#)
|
|
|
|
|
############# Paragraph (no number)
|
|
|
|
|
|
|
|
|
|
This is a suggested outline for adding new module documentation to |ns3|.
|
|
|
|
|
See ``src/click/doc/click.rst`` for an example.
|
|
|
|
|
|
|
|
|
|
The introductory paragraph is for describing what this code is trying to
|
|
|
|
|
model.
|
|
|
|
|
|
|
|
|
|
For consistency (italicized formatting), please use |ns3| to refer to
|
|
|
|
|
ns-3 in the documentation (and likewise, |ns2| for ns-2). These macros
|
|
|
|
|
are defined in the file ``replace.txt``.
|
|
|
|
|
|
|
|
|
|
Model Description
|
|
|
|
|
*****************
|
|
|
|
|
|
|
|
|
|
The source code for the new module lives in the directory ``{MODULE_DIR}``.
|
|
|
|
|
|
|
|
|
|
Add here a basic description of what is being modeled.
|
|
|
|
|
|
|
|
|
|
Design
|
|
|
|
|
======
|
|
|
|
|
|
|
|
|
|
Briefly describe the software design of the model and how it fits into
|
|
|
|
|
the existing ns-3 architecture.
|
|
|
|
|
|
|
|
|
|
Scope and Limitations
|
|
|
|
|
=====================
|
|
|
|
|
|
|
|
|
|
What can the model do? What can it not do? Please use this section to
|
|
|
|
|
describe the scope and limitations of the model.
|
|
|
|
|
|
|
|
|
|
References
|
|
|
|
|
==========
|
|
|
|
|
|
|
|
|
|
Add academic citations here, such as if you published a paper on this
|
|
|
|
|
model, or if readers should read a particular specification or other work.
|
|
|
|
|
|
|
|
|
|
Usage
|
|
|
|
|
*****
|
|
|
|
|
|
|
|
|
|
This section is principally concerned with the usage of your model, using
|
|
|
|
|
the public API. Focus first on most common usage patterns, then go
|
|
|
|
|
into more advanced topics.
|
|
|
|
|
|
|
|
|
|
Building New Module
|
|
|
|
|
===================
|
|
|
|
|
|
|
|
|
|
Include this subsection only if there are special build instructions or
|
|
|
|
|
platform limitations.
|
|
|
|
|
|
|
|
|
|
Helpers
|
|
|
|
|
=======
|
|
|
|
|
|
|
|
|
|
What helper API will users typically use? Describe it here.
|
|
|
|
|
|
|
|
|
|
Attributes
|
|
|
|
|
==========
|
|
|
|
|
|
|
|
|
|
What classes hold attributes, and what are the key ones worth mentioning?
|
|
|
|
|
|
|
|
|
|
Output
|
|
|
|
|
======
|
|
|
|
|
|
|
|
|
|
What kind of data does the model generate? What are the key trace
|
|
|
|
|
sources? What kind of logging output can be enabled?
|
|
|
|
|
|
|
|
|
|
Advanced Usage
|
|
|
|
|
==============
|
|
|
|
|
|
|
|
|
|
Go into further details (such as using the API outside of the helpers)
|
|
|
|
|
in additional sections, as needed.
|
|
|
|
|
|
|
|
|
|
Examples
|
|
|
|
|
========
|
|
|
|
|
|
|
|
|
|
What examples using this new code are available? Describe them here.
|
|
|
|
|
|
|
|
|
|
Troubleshooting
|
|
|
|
|
===============
|
|
|
|
|
|
|
|
|
|
Add any tips for avoiding pitfalls, etc.
|
|
|
|
|
|
|
|
|
|
Validation
|
|
|
|
|
**********
|
|
|
|
|
|
|
|
|
|
Describe how the model has been tested/validated. What tests run in the
|
|
|
|
|
test suite? How much API and code is covered by the tests? Again,
|
|
|
|
|
references to outside published work may help here.
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
def create_file(path, template, **kwargs):
|
|
|
|
|
artifact_path = Path(path)
|
|
|
|
|
|
|
|
|
|
#open file for (w)rite and in (t)ext mode
|
|
|
|
|
with artifact_path.open("wt") as f:
|
|
|
|
|
f.write(template.format(**kwargs))
|
|
|
|
|
|
2021-11-10 22:28:44 -03:00
|
|
|
|
|
|
|
|
def make_cmakelists(moduledir, modname):
|
|
|
|
|
path = Path(moduledir, 'CMakeLists.txt')
|
|
|
|
|
macro = "build_lib" if "contrib" not in str(path) else "build_contrib_lib"
|
|
|
|
|
create_file(path, CMAKELISTS_TEMPLATE, MODULE=modname, BUILD_LIB_MACRO=macro)
|
2020-03-13 17:12:57 -07:00
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
2021-11-10 22:28:44 -03:00
|
|
|
|
2020-03-13 17:12:57 -07:00
|
|
|
def make_model(moduledir, modname):
|
|
|
|
|
modelpath = Path(moduledir, "model")
|
|
|
|
|
modelpath.mkdir(parents=True)
|
|
|
|
|
|
|
|
|
|
srcfile_path = modelpath.joinpath(modname).with_suffix('.cc')
|
|
|
|
|
create_file(srcfile_path, MODEL_CC_TEMPLATE, MODULE=modname)
|
|
|
|
|
|
|
|
|
|
hfile_path = modelpath.joinpath(modname).with_suffix('.h')
|
|
|
|
|
guard = "{}_H".format(modname.replace('-', '_').upper())
|
|
|
|
|
create_file(hfile_path, MODEL_H_TEMPLATE,
|
|
|
|
|
MODULE=modname,
|
|
|
|
|
INCLUDE_GUARD=guard)
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def make_test(moduledir, modname):
|
|
|
|
|
testpath = Path(moduledir, "test")
|
|
|
|
|
testpath.mkdir(parents=True)
|
|
|
|
|
|
|
|
|
|
file_path = testpath.joinpath(modname+'-test-suite').with_suffix('.cc')
|
|
|
|
|
name_parts = modname.split('-')
|
|
|
|
|
create_file(file_path, TEST_CC_TEMPLATE, MODULE=modname,
|
|
|
|
|
CAPITALIZED=''.join([word.capitalize() for word in name_parts]),
|
|
|
|
|
COMPOUND=''.join([word.capitalize() if index > 0 else word for index, word in enumerate(name_parts)]))
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def make_helper(moduledir, modname):
|
2020-06-25 08:03:39 -07:00
|
|
|
helperpath = Path(moduledir, "helper")
|
|
|
|
|
helperpath.mkdir(parents=True)
|
|
|
|
|
|
|
|
|
|
srcfile_path = helperpath.joinpath(modname+'-helper').with_suffix('.cc')
|
|
|
|
|
create_file(srcfile_path, HELPER_CC_TEMPLATE, MODULE=modname)
|
|
|
|
|
|
|
|
|
|
h_file_path = helperpath.joinpath(modname+'-helper').with_suffix('.h')
|
|
|
|
|
guard = "{}_HELPER_H".format(modname.replace('-', '_').upper())
|
|
|
|
|
create_file(h_file_path, HELPER_H_TEMPLATE, MODULE=modname, INCLUDE_GUARD=guard)
|
2020-03-13 17:12:57 -07:00
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def make_examples(moduledir, modname):
|
2020-06-25 08:03:39 -07:00
|
|
|
examplespath = Path(moduledir, "examples")
|
|
|
|
|
examplespath.mkdir(parents=True)
|
2020-03-13 17:12:57 -07:00
|
|
|
|
2021-11-10 22:28:44 -03:00
|
|
|
cmakelistspath = Path(examplespath, 'CMakeLists.txt')
|
|
|
|
|
macro = "build_lib_example" if "contrib" not in str(moduledir) else "build_contrib_example"
|
|
|
|
|
create_file(cmakelistspath, EXAMPLES_CMAKELISTS_TEMPLATE, MODULE=modname, BUILD_EXAMPLE_MACRO=macro)
|
2020-03-13 17:12:57 -07:00
|
|
|
|
2020-06-25 08:03:39 -07:00
|
|
|
examplesfile_path = examplespath.joinpath(modname+'-example').with_suffix('.cc')
|
|
|
|
|
create_file(examplesfile_path, EXAMPLE_CC_TEMPLATE, MODULE=modname)
|
2020-03-13 17:12:57 -07:00
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def make_doc(moduledir, modname):
|
2020-06-25 08:03:39 -07:00
|
|
|
docpath = Path(moduledir, "doc")
|
|
|
|
|
docpath.mkdir(parents=True)
|
2020-03-13 17:12:57 -07:00
|
|
|
|
|
|
|
|
#the module_dir template parameter must be a relative path
|
|
|
|
|
#instead of an absolute path
|
2020-06-25 08:03:39 -07:00
|
|
|
mod_relpath = os.path.relpath(str(moduledir))
|
2020-03-13 17:12:57 -07:00
|
|
|
|
|
|
|
|
file_name = '{}.rst'.format(modname)
|
2020-06-25 08:03:39 -07:00
|
|
|
file_path = Path(docpath, file_name)
|
2021-11-10 22:28:44 -03:00
|
|
|
create_file(file_path, DOC_RST_TEMPLATE, MODULE=modname, MODULE_DIR=mod_relpath)
|
2020-03-13 17:12:57 -07:00
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
2021-11-10 22:28:44 -03:00
|
|
|
|
2020-03-13 17:12:57 -07:00
|
|
|
def make_module(modpath, modname):
|
|
|
|
|
modulepath = Path(modpath, modname)
|
|
|
|
|
|
|
|
|
|
if modulepath.exists():
|
|
|
|
|
print("Module {!r} already exists".format(modname), file=sys.stderr)
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
print("Creating module {}".format(modulepath))
|
|
|
|
|
|
2021-11-10 22:28:44 -03:00
|
|
|
functions = (make_cmakelists, make_model, make_test,
|
2020-03-13 17:12:57 -07:00
|
|
|
make_helper, make_examples, make_doc)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
modulepath.mkdir(parents=True)
|
|
|
|
|
|
|
|
|
|
success = all(func(modulepath, modname) for func in functions)
|
|
|
|
|
|
|
|
|
|
if not success:
|
|
|
|
|
raise ValueError("Generating module artifacts failed")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
if modulepath.exists():
|
|
|
|
|
shutil.rmtree(modulepath)
|
|
|
|
|
|
|
|
|
|
print("Creating module {!r} failed: {}".format(modname, str(e)), file=sys.stderr)
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def create_argument_parser():
|
|
|
|
|
description = """Generate scaffolding for ns-3 modules
|
|
|
|
|
|
|
|
|
|
Generates the directory structure and skeleton files required for an ns-3
|
|
|
|
|
module. All of the generated files are valid C/C++ and will compile successfully
|
|
|
|
|
out of the box. waf configure must be run after creating new modules in order
|
|
|
|
|
to integrate them into the ns-3 build system.
|
|
|
|
|
|
|
|
|
|
The following directory structure is generated under the contrib directory:
|
|
|
|
|
<modname>
|
2021-11-10 22:28:44 -03:00
|
|
|
|-- CMakeLists.txt
|
2020-03-13 17:12:57 -07:00
|
|
|
|-- doc
|
|
|
|
|
|-- <modname>.rst
|
|
|
|
|
|-- examples
|
|
|
|
|
|-- <modname>-example.cc
|
2021-11-10 22:28:44 -03:00
|
|
|
|-- CMakeLists.txt
|
2020-03-13 17:12:57 -07:00
|
|
|
|-- helper
|
|
|
|
|
|-- <modname>-helper.cc
|
|
|
|
|
|-- <modname>-helper.h
|
|
|
|
|
|-- model
|
|
|
|
|
|-- <modname>.cc
|
|
|
|
|
|-- <modname>.h
|
|
|
|
|
|-- test
|
|
|
|
|
|-- <modname>-test-suite.cc
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<modname> is the name of the module and is restricted to the following
|
|
|
|
|
character groups: letters, numbers, -, _
|
|
|
|
|
The script validates the module name and skips modules that have characters
|
|
|
|
|
outside of the above groups. One exception to the naming rule is that src/
|
|
|
|
|
or contrib/ may be added to the front of the module name to indicate where the
|
|
|
|
|
module scaffold should be created. If the module name starts with src/, then
|
|
|
|
|
the module is placed in the src directory. If the module name starts with
|
|
|
|
|
contrib/, then the module is placed in the contrib directory. If the module
|
|
|
|
|
name does not start with src/ or contrib/, then it defaults to contrib/.
|
|
|
|
|
See the examples section for use cases.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In some situations it can be useful to group multiple related modules under one
|
|
|
|
|
directory. Use the --project option to specify a common parent directory where
|
|
|
|
|
the modules should be generated. The value passed to --project is treated
|
|
|
|
|
as a relative path. The path components have the same naming requirements as
|
|
|
|
|
the module name: letters, numbers, -, _
|
|
|
|
|
The project directory is placed under the contrib directory and any parts of the
|
|
|
|
|
path that do not exist will be created. Creating projects in the src directory
|
|
|
|
|
is not supported. Module names that start with src/ are not allowed when
|
|
|
|
|
--project is used. Module names that start with contrib/ are treated the same
|
|
|
|
|
as module names that don't start with contrib/ and are generated under the
|
|
|
|
|
project directory.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
epilog = """Examples:
|
|
|
|
|
%(prog)s module1
|
|
|
|
|
%(prog)s contrib/module1
|
|
|
|
|
|
|
|
|
|
Creates a new module named module1 under the contrib directory
|
|
|
|
|
|
|
|
|
|
%(prog)s src/module1
|
|
|
|
|
|
|
|
|
|
Creates a new module named module1 under the src directory
|
|
|
|
|
|
|
|
|
|
%(prog)s src/module1 contrib/module2, module3
|
|
|
|
|
|
|
|
|
|
Creates three modules, one under the src directory and two under the
|
|
|
|
|
contrib directory
|
|
|
|
|
|
|
|
|
|
%(prog)s --project myproject module1 module2
|
|
|
|
|
|
|
|
|
|
Creates two modules under contrib/myproject
|
|
|
|
|
|
|
|
|
|
%(prog)s --project myproject/sub_project module1 module2
|
|
|
|
|
|
|
|
|
|
Creates two modules under contrib/myproject/sub_project
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
formatter = argparse.RawDescriptionHelpFormatter
|
|
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description=description,
|
|
|
|
|
epilog=epilog,
|
|
|
|
|
formatter_class=formatter)
|
|
|
|
|
|
|
|
|
|
parser.add_argument('--project', default='',
|
|
|
|
|
help=("Specify a relative path under the contrib directory "
|
|
|
|
|
"where the new modules will be generated. The path "
|
|
|
|
|
"will be created if it does not exist."))
|
|
|
|
|
|
|
|
|
|
parser.add_argument('modnames', nargs='+',
|
|
|
|
|
help=("One or more modules to generate. Module names "
|
|
|
|
|
"are limited to the following: letters, numbers, -, "
|
|
|
|
|
"_. Modules are generated under the contrib directory "
|
|
|
|
|
"except when the module name starts with src/. Modules "
|
|
|
|
|
"that start with src/ are generated under the src "
|
|
|
|
|
"directory."))
|
|
|
|
|
|
|
|
|
|
return parser
|
|
|
|
|
|
|
|
|
|
def main(argv):
|
|
|
|
|
parser = create_argument_parser()
|
|
|
|
|
|
|
|
|
|
args = parser.parse_args(argv[1:])
|
|
|
|
|
|
|
|
|
|
project = args.project
|
|
|
|
|
modnames = args.modnames
|
|
|
|
|
|
|
|
|
|
base_path = Path.cwd()
|
|
|
|
|
|
|
|
|
|
src_path = base_path.joinpath('src')
|
|
|
|
|
contrib_path = base_path.joinpath('contrib')
|
|
|
|
|
|
|
|
|
|
for p in (src_path, contrib_path):
|
|
|
|
|
if not p.is_dir():
|
|
|
|
|
parser.error("Cannot find the directory '{}'.\nPlease run this "
|
|
|
|
|
"script from the top level of the ns3 directory".format(
|
|
|
|
|
p))
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Error check the arguments
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
# Alphanumeric and '-' only
|
|
|
|
|
allowedRE = re.compile('^(\w|-)+$')
|
|
|
|
|
|
|
|
|
|
project_path = None
|
|
|
|
|
|
|
|
|
|
if project:
|
|
|
|
|
#project may be a path in the form a/b/c
|
|
|
|
|
#remove any leading or trailing path separators
|
|
|
|
|
project_path = Path(project)
|
|
|
|
|
|
|
|
|
|
if project_path.is_absolute():
|
|
|
|
|
#remove leading separator
|
|
|
|
|
project_path = project_path.relative_to(os.sep)
|
|
|
|
|
|
|
|
|
|
if not all(allowedRE.match(part) for part in project_path.parts):
|
|
|
|
|
parser.error('Project path may only contain the characters [a-zA-Z0-9_-].')
|
|
|
|
|
#
|
|
|
|
|
# Create each module, if it doesn't exist
|
|
|
|
|
#
|
|
|
|
|
modules = []
|
|
|
|
|
for name in modnames:
|
|
|
|
|
if name:
|
|
|
|
|
#remove any leading or trailing directory separators
|
|
|
|
|
name = name.strip(os.sep)
|
|
|
|
|
|
|
|
|
|
if not name:
|
|
|
|
|
#skip empty modules
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
name_path = Path(name)
|
|
|
|
|
|
|
|
|
|
if len(name_path.parts) > 2:
|
|
|
|
|
print("Skipping {}: module name can not be a path".format(name))
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
#default target directory is contrib
|
|
|
|
|
modpath = contrib_path
|
|
|
|
|
|
|
|
|
|
if name_path.parts[0] == 'src':
|
|
|
|
|
if project:
|
|
|
|
|
parser.error("{}: Cannot specify src/ in a module name when --project option is used".format(name))
|
|
|
|
|
|
|
|
|
|
modpath = src_path
|
|
|
|
|
|
|
|
|
|
#create a new path without the src part
|
|
|
|
|
name_path = name_path.relative_to('src')
|
|
|
|
|
|
|
|
|
|
elif name_path.parts[0] == 'contrib':
|
|
|
|
|
modpath = contrib_path
|
|
|
|
|
|
|
|
|
|
#create a new path without the contrib part
|
|
|
|
|
name_path = name_path.relative_to('contrib')
|
|
|
|
|
|
|
|
|
|
if project_path:
|
|
|
|
|
#if a project path was specified, that overrides other paths
|
|
|
|
|
#project paths are always relative to the contrib path
|
|
|
|
|
modpath = contrib_path.joinpath(project_path)
|
|
|
|
|
|
|
|
|
|
modname = name_path.parts[0]
|
|
|
|
|
|
|
|
|
|
if not allowedRE.match(modname):
|
|
|
|
|
print("Skipping {}: module name may only contain the characters [a-zA-Z0-9_-]".format(modname))
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
modules.append((modpath, modname))
|
|
|
|
|
|
|
|
|
|
if all(make_module(*module) for module in modules):
|
|
|
|
|
print()
|
|
|
|
|
print("Successfully created new modules")
|
|
|
|
|
print("Run './waf configure' to include them in the build")
|
|
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
return_value = 0
|
|
|
|
|
try:
|
|
|
|
|
return_value = main(sys.argv)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print("Exception: '{}'".format(e), file=sys.stderr)
|
|
|
|
|
return_value = 1
|
|
|
|
|
|
|
|
|
|
sys.exit(return_value)
|