From 42e320d676e46b06f128afdf276e2d4c37531b90 Mon Sep 17 00:00:00 2001 From: Gabriel Ferreira Date: Wed, 22 Dec 2021 17:16:47 -0300 Subject: [PATCH] doc: CMake docs --- doc/manual/Makefile | 1 + doc/manual/source/develop.rst | 1 + doc/manual/source/new-modules.rst | 267 +-- doc/manual/source/working-with-cmake.rst | 2135 ++++++++++++++++++++++ doc/manual/source/working-with-git.rst | 2 + doc/tutorial/source/getting-started.rst | 447 +++-- utils/create-module.py | 17 +- 7 files changed, 2555 insertions(+), 315 deletions(-) create mode 100644 doc/manual/source/working-with-cmake.rst diff --git a/doc/manual/Makefile b/doc/manual/Makefile index 4caabce41..fcd7611a5 100644 --- a/doc/manual/Makefile +++ b/doc/manual/Makefile @@ -45,6 +45,7 @@ SOURCES = \ source/tracing.rst \ source/troubleshoot.rst \ source/utilities.rst \ + source/working-with-cmake.rst \ ${SRC}/stats/doc/data-collection.rst \ ${SRC}/stats/doc/data-collection-overview.rst \ ${SRC}/stats/doc/statistics.rst \ diff --git a/doc/manual/source/develop.rst b/doc/manual/source/develop.rst index 532ca4d24..02e1b8899 100644 --- a/doc/manual/source/develop.rst +++ b/doc/manual/source/develop.rst @@ -9,6 +9,7 @@ This chapter describes the development ecosystem generaly used to create new mod :maxdepth: 2 working-with-git + working-with-cmake logging tests new-models diff --git a/doc/manual/source/new-modules.rst b/doc/manual/source/new-modules.rst index a269ef0ec..8eb2b76b2 100644 --- a/doc/manual/source/new-modules.rst +++ b/doc/manual/source/new-modules.rst @@ -1,6 +1,9 @@ .. include:: replace.txt .. highlight:: cpp + +.. _Adding a New Module to ns3: + Adding a New Module to |ns3| ---------------------------- @@ -31,12 +34,12 @@ required files: bindings/ doc/ examples/ - wscript + CMakeLists.txt helper/ model/ test/ examples-to-run.py - wscript + CMakeLists.txt Not all directories will be present in each module. @@ -67,10 +70,10 @@ Let's assume we've created our new module in ``src``. $ cd new-module $ ls - doc examples helper model test wscript + doc examples helper model test CMakeLists.txt In more detail, the ``create-module.py`` script will create the -directories as well as initial skeleton ``wscript``, ``.h``, ``.cc`` +directories as well as initial skeleton ``CMakeLists.txt``, ``.h``, ``.cc`` and ``.rst`` files. The complete module with skeleton files looks like this: .. sourcecode:: text @@ -81,7 +84,7 @@ and ``.rst`` files. The complete module with skeleton files looks like this: new-module.rst examples/ new-module-example.cc - wscript + CMakeLists.txt helper/ new-module-helper.cc new-module-helper.h @@ -90,7 +93,7 @@ and ``.rst`` files. The complete module with skeleton files looks like this: new-module.h test/ new-module-test-suite.cc - wscript + CMakeLists.txt (If required the ``bindings/`` directory listed in :ref:`Step-0 ` will be created automatically during @@ -98,28 +101,45 @@ the build.) We next walk through how to customize this module. Informing ``ns3`` about the files which make up your module is done by editing the two -``wscript`` files. We will walk through the main steps in this chapter. +``CMakeLists.txt`` files. We will walk through the main steps in this chapter. All |ns3| modules depend on the ``core`` module and usually on -other modules. This dependency is specified in the ``wscript`` file -(at the top level of the module, not the separate ``wscript`` file -in the ``examples`` directory!). In the skeleton ``wscript`` +other modules. This dependency is specified in the ``CMakeLists.txt`` file +(at the top level of the module, not the separate ``CMakeLists.txt`` file +in the ``examples`` directory!). In the skeleton ``CMakeLists.txt`` the call that will declare your new module to ``ns3`` will look like this (before editing): -.. sourcecode:: python +.. sourcecode:: cmake - def build(bld): - module = bld.create_ns3_module('new-module', ['core']) + build_lib( + LIBNAME new-module + SOURCE_FILES helper/new-module-helper.cc + model/new-module.cc + HEADER_FILES helper/new-module-helper.h + model/new-module.h + LIBRARIES_TO_LINK ${libcore} + TEST_SOURCES test/new-module-test-suite.cc + ) Let's assume that ``new-module`` depends on the ``internet``, -``mobility``, and ``aodv`` modules. After editing it the ``wscript`` file +``mobility``, and ``aodv`` modules. After editing it the ``CMakeLists.txt`` file should look like: -.. sourcecode:: python +.. sourcecode:: cmake - def build(bld): - module = bld.create_ns3_module('new-module', ['internet', 'mobility', 'aodv']) + build_lib( + LIBNAME new-module + SOURCE_FILES helper/new-module-helper.cc + model/new-module.cc + HEADER_FILES helper/new-module-helper.h + model/new-module.h + LIBRARIES_TO_LINK + ${libinternet} + ${libmobility} + ${libaodv} + TEST_SOURCES test/new-module-test-suite.cc + ) Note that only first level module dependencies should be listed, which is why we removed ``core``; the ``internet`` module in turn depends on @@ -151,31 +171,49 @@ Step 3 - Declare Source Files ***************************** The public header and source code files for your new module -should be specified in the ``wscript`` file by modifying it with +should be specified in the ``CMakeLists.txt`` file by modifying it with your text editor. As an example, after declaring the ``spectrum`` module, -the ``src/spectrum/wscript`` specifies the source code files -with the following list: +the ``src/spectrum/CMakeLists.txt`` specifies the source code files +with the following: -.. sourcecode:: python +.. sourcecode:: cmake - def build(bld): + set(source_files + helper/adhoc-aloha-noack-ideal-phy-helper.cc + helper/spectrum-analyzer-helper.cc + helper/spectrum-helper.cc + ... + ) - module = bld.create_ns3_module('spectrum', ['internet', 'propagation', 'antenna', 'applications']) + set(header_files + helper/adhoc-aloha-noack-ideal-phy-helper.h + helper/spectrum-analyzer-helper.h + helper/spectrum-helper.h + ... + ) - module.source = [ - 'model/spectrum-model.cc', - 'model/spectrum-value.cc', - . - . - . - 'model/microwave-oven-spectrum-value-helper.cc', - 'helper/spectrum-helper.cc', - 'helper/adhoc-aloha-noack-ideal-phy-helper.cc', - 'helper/waveform-generator-helper.cc', - 'helper/spectrum-analyzer-helper.cc', - ] + build_lib( + LIBNAME spectrum + SOURCE_FILES ${source_files} + HEADER_FILES ${header_files} + LIBRARIES_TO_LINK ${libpropagation} + ${libantenna} + TEST_SOURCES + test/spectrum-ideal-phy-test.cc + test/spectrum-interference-test.cc + test/spectrum-value-test.cc + test/spectrum-waveform-generator-test.cc + test/three-gpp-channel-test-suite.cc + test/tv-helper-distribution-test.cc + test/tv-spectrum-transmitter-test.cc + ) + + +Note: the ``source_files`` and ``header_files`` lists are not necessary. +They are used keep the ``build_lib`` macro readable for modules with many +source files. The objects resulting from compiling these sources will be assembled into a link library, which will be linked to any programs relying on this @@ -187,31 +225,48 @@ Step 4 - Declare Public Header Files ************************************ The header files defining the public API of your model and helpers -also should be specified in the ``wscript`` file. +also should be specified in the ``CMakeLists.txt`` file. Continuing with the ``spectrum`` model illustration, the public header files are specified with the following stanza. -(Note that the argument to the ``bld`` function tells -``ns3`` to install this module's headers with the other |ns3| headers): +(Note that the variable ``header_files`` tells +``CMake`` to install this module's headers with the other |ns3| headers): -.. sourcecode:: python +.. sourcecode:: cmake - headers = bld(features='ns3header') + set(header_files + helper/adhoc-aloha-noack-ideal-phy-helper.h + helper/spectrum-analyzer-helper.h + ... + model/tv-spectrum-transmitter.h + model/waveform-generator.h + model/wifi-spectrum-value-helper.h + ) - headers.module = 'spectrum' + build_lib( + LIBNAME spectrum + ... + HEADER_FILES ${header_files} + ... + ) - headers.source = [ - 'model/spectrum-model.h', - 'model/spectrum-value.h', - . - . - . - 'model/microwave-oven-spectrum-value-helper.h', - 'helper/spectrum-helper.h', - 'helper/adhoc-aloha-noack-ideal-phy-helper.h', - 'helper/waveform-generator-helper.h', - 'helper/spectrum-analyzer-helper.h', - ] +If the list of headers is short, use the following instead: + +.. sourcecode:: cmake + + build_lib( + LIBNAME spectrum + ... + HEADER_FILES + helper/adhoc-aloha-noack-ideal-phy-helper.h + helper/spectrum-analyzer-helper.h + ... + model/tv-spectrum-transmitter.h + model/waveform-generator.h + model/wifi-spectrum-value-helper.h + ... + ) + Headers made public in this way will be accessible to users of your model with include statements like @@ -233,18 +288,24 @@ Step 5 - Declare Tests ********************** If your new module has tests, then they must be specified in your -``wscript`` file by modifying it with your text editor. +``CMakeLists.txt`` file by modifying it with your text editor. The ``spectrum`` model tests are specified with the following stanza: -.. sourcecode:: python +.. sourcecode:: cmake - module_test = bld.create_ns3_module_test_library('spectrum') - - module_test.source = [ - 'test/spectrum-interference-test.cc', - 'test/spectrum-value-test.cc', - ] + build_lib( + LIBNAME spectrum + ... + TEST_SOURCES + test/spectrum-ideal-phy-test.cc + test/spectrum-interference-test.cc + test/spectrum-value-test.cc + test/spectrum-waveform-generator-test.cc + test/three-gpp-channel-test-suite.cc + test/tv-helper-distribution-test.cc + test/tv-spectrum-transmitter-test.cc + ) See :doc:`Tests ` for more information on how to write test cases. @@ -252,43 +313,46 @@ Step 6 - Declare Examples ************************* If your new module has examples, then they must be specified in your -``examples/wscript`` file. (The skeleton top-level ``wscript`` will -recursively include ``examples/wscript`` only if the examples were +``examples/CMakeLists.txt`` file. (The skeleton top-level ``CMakeLists.txt`` will +recursively include ``examples/CMakeLists.txt`` only if the examples were enabled at configure time.) The ``spectrum`` model defines it's first example in -``src/spectrum/examples/wscript`` with +``src/spectrum/examples/CMakeLists.txt`` with -.. sourcecode:: python +.. sourcecode:: cmake - def build(bld): - obj = bld.create_ns3_program('adhoc-aloha-ideal-phy', - ['spectrum', 'mobility']) - obj.source = 'adhoc-aloha-ideal-phy.cc' + build_lib_example( + NAME adhoc-aloha-ideal-phy + SOURCE_FILES adhoc-aloha-ideal-phy.cc + LIBRARIES_TO_LINK + ${libspectrum} + ${libmobility} + ${libinternet} + ${libapplications} + ) -Note that the second argument to the function ``create_ns3_program()`` -is the list of modules that the program being created depends on; again, -don't forget to include ``new-module`` in the list. It's best practice -to list only the direct module dependencies, and let ``ns3`` deduce -the full dependency tree. + +Note that the variable ``libraries_to_link`` is the list of modules that +the program being created depends on; again, don't forget to include +``new-module`` in the list. It's best practice to list only the direct +module dependencies, and let ``CMake`` deduce the full dependency tree. Occasionally, for clarity, you may want to split the implementation for your example among several source files. In this case, just include those files as additional explicit sources of the example: -.. sourcecode:: python - - obj = bld.create_ns3_program('new-module-example', [new-module]) - obj.source = ['new-module-example.cc', 'new-module-example-part.cc'] - -Python examples are specified using the following -function call. Note that the second argument for the function -``register_ns3_script()`` is the list of modules that the Python example -depends on: - -.. sourcecode:: python - - bld.register_ns3_script('new-module-example.py', ['new-module']) +.. sourcecode:: cmake + + build_lib_example( + NAME new-module-example + SOURCE_FILES new-module-example.cc + LIBRARIES_TO_LINK + ${libspectrum} + ${libmobility} + ${libinternet} + ${libapplications} + ) Step 7 - Examples Run as Tests ****************************** @@ -330,11 +394,11 @@ two lists of C++ and Python examples: As indicated in the comment, each entry in the C++ list of examples to run contains the tuple ``(example_name, do_run, do_valgrind_run)``, where - * ``example_name`` is the executable to be run, - * ``do_run`` is a condition under which to run the example, and - * ``do_valgrind_run`` is a condition under which to run the example - under valgrind. (This is needed because NSC causes illegal instruction - crashes with some tests when they are run under valgrind.) +* ``example_name`` is the executable to be run, +* ``do_run`` is a condition under which to run the example, and +* ``do_valgrind_run`` is a condition under which to run the example + under valgrind. (This is needed because NSC causes illegal instruction + crashes with some tests when they are run under valgrind.) Note that the two conditions are Python statements that can depend on ``ns3`` configuration variables. For example, using the @@ -347,8 +411,8 @@ NSC_ENABLED variable that was defined up until ns-3.35: Each entry in the Python list of examples to run contains the tuple ``(example_name, do_run)``, where, as for the C++ examples, - * ``example_name`` is the Python script to be run, and - * ``do_run`` is a condition under which to run the example. +* ``example_name`` is the Python script to be run, and +* ``do_run`` is a condition under which to run the example. Again, the condition is a Python statement that can depend on ``ns3`` configuration variables. For example, @@ -363,7 +427,7 @@ Step 8 - Configure and Build You can now configure, build and test your module as normal. You must reconfigure the project as a first step so that ``ns3`` -caches the new information in your ``wscript`` files, +caches the new information in your ``CMakeLists.txt`` files, or else your new module will not be included in the build. .. sourcecode:: bash @@ -379,15 +443,10 @@ if your module has any enabled) in the test output. Step 9 - Python Bindings ************************ -Adding Python bindings to your module is optional, and the step is -commented out by default in the ``create-module.py`` script. - -.. sourcecode:: python - - # bld.ns3_python_bindings() +Adding Python bindings to your module is optional. If you want to include Python bindings (needed only if you want to write Python ns-3 programs instead of C++ ns-3 programs), you -should uncomment the above and install the Python API scanning -system (covered elsewhere in this manual) and scan your module to -generate new bindings. +should scan your module to generate new bindings for the Python +API (covered elsewhere in this manual), and they will be used +if NS3_PYTHON_BINDINGS is set to ON. diff --git a/doc/manual/source/working-with-cmake.rst b/doc/manual/source/working-with-cmake.rst new file mode 100644 index 000000000..91769f13d --- /dev/null +++ b/doc/manual/source/working-with-cmake.rst @@ -0,0 +1,2135 @@ +.. include:: replace.txt +.. highlight:: console + +.. Section Separators: + ---- + **** + ++++ + ==== + ~~~~ + +.. _Working with CMake: + +Working with CMake +------------------ + +The ns-3 project used Waf build system in the past, but it has moved to +CMake for the ns-3.36 release. + +CMake is very verbose and commands can be very long for basic operations. + +The wrapper script ``ns3`` hides most of verbosity from CMake and provide a +Waf-like interface for command-line users. + +.. _CLion : https://www.jetbrains.com/clion/ +.. _Visual Studio : https://visualstudio.microsoft.com/ +.. _Code : https://code.visualstudio.com/Download +.. _Xcode : https://developer.apple.com/xcode/ +.. _CodeBlocks : https://www.codeblocks.org/ +.. _Eclipse : https://www.eclipse.org/cdt/ +.. _CMake generated : https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html +.. _generator options : https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html + +It is the recommended way to work on ns-3, except if you are using an +IDE that supports projects that can be generated with CMake or CMake projects. + +Here is a non-exhaustive list of IDEs that can be used: + +* Support CMake projects: + + * JetBrains's `CLion`_ + * Microsoft `Visual Studio`_ and Visual Studio `Code`_ + +* Supported IDEs via `CMake generated`_ projects: + + * Apple's `XCode`_ : ``ns3 configure -G Xcode`` + * `CodeBlocks`_ : ``ns3 configure -G "CodeBlocks - Ninja"`` + * `Eclipse`_ CDT4 : ``ns3 configure -G "Eclipse CDT4 - Ninja"`` + +Note: Ninja was used for brevity. +Both CodeBlocks and Eclipse have additional `generator options`_. + +General instructions on how to setup and use IDEs are available +in the Tutorial and will not be detailed here. + +Configuring the project +*********************** + +After getting the code, either cloning the ns-3-dev repository or +downloading the release tarball, you will need to configure the +project to work on it. + +There are two ways to configure the project: the easiest way +is using the ``ns3`` script and the other way directly with CMake. + +Configuring the project with ns3 +++++++++++++++++++++++++++++++++ + +Navigate to the ns-3-dev directory, then run ``./ns3 configure --help`` to +print the configuration options: + +.. sourcecode:: console + + ~$ cd ns-3-dev + ~/ns-3-dev$ ./ns3 configure --help + usage: ns3 configure [-h] [-d {debug,release,optimized}] [-G G] + [--cxx-standard CXX_STANDARD] [--enable-asserts] + [--disable-asserts] [--enable-examples] + [--disable-examples] [--enable-logs] + [--disable-logs] [--enable-tests] + [--disable-tests] [--enable-verbose] + [--disable-verbose] + ... + + positional arguments: + configure + + optional arguments: + -h, --help show this help message and exit + -d {debug,release,optimized}, --build-profile {debug,release,optimized} + Build profile + -G G CMake generator (e.g. + https://cmake.org/cmake/help/latest/manual/cmake- + generators.7.html) + ... + +Note: the command output was trimmed to the most used options. + +To configure ns-3 in release mode, while enabling examples and tests, +run ``./ns3 configure -d release --enable-examples --enable-tests``. +To check what underlying commands dare being executed, add the +``--dry-run`` option: + +.. sourcecode:: console + + ~/ns-3-dev$ ./ns3 --dry-run configure -d release --enable-examples --enable-tests + The following commands would be executed: + mkdir cmake_cache + cd cmake_cache; /usr/bin/cmake -DCMAKE_BUILD_TYPE=release -DNS3_NATIVE_OPTIMIZATIONS=OFF -DNS3_EXAMPLES=ON -DNS3_TESTS=ON -G Unix Makefiles .. ; cd .. + +Now we run it for real: + +.. sourcecode:: console + + ~/ns-3-dev$ ./ns3 configure -d release --enable-examples --enable-tests + -- CCache is enabled. Precompiled headers are disabled by default. + -- The CXX compiler identification is GNU 11.2.0 + -- The C compiler identification is GNU 11.2.0 + -- Detecting CXX compiler ABI info + -- Detecting CXX compiler ABI info - done + -- Check for working CXX compiler: /usr/bin/c++ - skipped + -- Detecting CXX compile features + -- Detecting CXX compile features - done + ... + -- Processing src/wifi + -- Processing src/wimax + -- ---- Summary of optional NS-3 features: + Build profile : release + Build directory : /mnt/dev/tools/source/ns-3-dev/build + ... + Examples : ON + ... + Tests : ON + Threading Primitives : ON + + + Modules configured to be built: + antenna aodv applications + bridge buildings config-store + core csma csma-layout + ... + wifi wimax + + Modules that cannot be built: + brite click openflow + visualizer + + + -- Configuring done + -- Generating done + -- Build files have been written to: /mnt/dev/tools/source/ns-3-dev/cmake_cache + Finished executing the following commands: + mkdir cmake_cache + cd cmake_cache; /usr/bin/cmake -DCMAKE_BUILD_TYPE=release -DNS3_NATIVE_OPTIMIZATIONS=OFF -DNS3_EXAMPLES=ON -DNS3_TESTS=ON -G Unix Makefiles .. ; cd .. + +Notice that CCache is automatically used (if installed) for your convenience. + +The summary with enabled feature shows both the ``release`` build type, along with +enabled examples and tests. + +Below is a list of enabled modules and modules that cannot be built. + +At the end, notice we print the same commands from ``--dry-run``. This is done +to familiarize Waf users with CMake and how the options names changed. + +The mapping of the ns3 build profiles into the CMake build types is the following: + ++---------------------------+--------------------------------------------------+ +| Equivalent build profiles | ++---------------------------+--------------------------------------------------+ +| ns3 | CMake | +| +------------------+-------------------------------+ +| | CMAKE_BUILD_TYPE | Additional flags | ++===========================+==================+===============================+ +| debug | debug | | ++---------------------------+------------------+-------------------------------+ +| release | release | | ++---------------------------+------------------+-------------------------------+ +| optimized | release | -DNS3_NATIVE_OPTIMIZATIONS=ON | ++---------------------------+------------------+-------------------------------+ + +The ``-DNS3_NATIVE_OPTIMIZATIONS=ON`` CMake flag is equivalent to GCC's ``-march=native -mtune=native``. + +Configuring the project with CMake +++++++++++++++++++++++++++++++++++ + +.. _CMake: https://cmake.org/cmake/help/latest/manual/cmake.1.html +.. _CMAKE_BUILD_TYPE: https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html +.. _Effects (g++): https://github.com/Kitware/CMake/blob/master/Modules/Compiler/GNU.cmake +.. _generator: https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html + +Navigate to the ns-3-dev directory, create a CMake cache folder, +navigate to it and run `CMake`_ pointing to the ns-3-dev folder. + +.. sourcecode:: console + + ~$ cd ns-3-dev + ~/ns-3-dev$ mkdir cmake_cache + ~/ns-3-dev$ cd cmake_cache + ~/ns-3-dev/cmake_cache$ cmake .. + +You can pass additional arguments to the CMake command, to configure it. To change variable values, +you should use the -D option followed by the variable name. + +As an example, the build type is stored in the variable named `CMAKE_BUILD_TYPE`_. Setting it to one +of the CMake build types shown in the table below will change compiler settings associated with those +build types and output executable and libraries names, which will receive a suffix. + ++------------------+-------------------+ +| CMAKE_BUILD_TYPE | `Effects (g++)`_ | ++==================+===================+ +| DEBUG | -g | ++------------------+-------------------+ +| RELEASE | -O3 -DNDEBUG | ++------------------+-------------------+ +| RELWITHDEBINFO | -O2 -g -DNDEBUG | ++------------------+-------------------+ +| MINSIZEREL | -Os -DNDEBUG | ++------------------+-------------------+ + +You can set the build type with the following command, which assumes your terminal is inside the cache folder +created previously. + +.. sourcecode:: console + + ~/ns-3-dev/cmake_cache$ cmake -DCMAKE_BUILD_TYPE=DEBUG .. + +Another common option to change is the `generator`_, which is the real underlying build system called by CMake. +There are many generators supported by CMake, including the ones listed in the table below. + ++------------------------------------------------+ +| Generators | ++================================================+ +| MinGW Makefiles | ++------------------------------------------------+ +| Unix Makefiles | ++------------------------------------------------+ +| MSYS Makefiles | ++------------------------------------------------+ +| CodeBlocks - *one of the previous Makefiles* | ++------------------------------------------------+ +| Eclipse CDT4 - *one of the previous Makefiles* | ++------------------------------------------------+ +| Ninja | ++------------------------------------------------+ +| Xcode | ++------------------------------------------------+ + +To change the generator, you will need to pass one of these generators with the -G option. For example, if we +prefer Ninja to Makefiles, which are the default, we need to run the following command: + +.. sourcecode:: console + + ~/ns-3-dev/cmake_cache$ cmake -G Ninja .. + +This command may fail if there are different generator files in the same CMake cache folder. It is recommended to clean up +the CMake cache folder, then recreate it and reconfigure setting the generator in the first run. + +.. sourcecode:: console + + ~/ns-3-dev/cmake_cache$ cd .. + ~/ns-3-dev$ rm -R cmake_cache && mkdir cmake_cache && cd cmake_cache + ~/ns-3-dev/cmake_cache$ cmake -DCMAKE_BUILD_TYPE=release -G Ninja .. + +After configuring for the first time, settings will be initialized to their +default values, and then you can use the ``ccmake`` command to manually change them: + +.. sourcecode:: console + + ~/ns-3-dev/cmake_cache$ ccmake . + CMAKE_BUILD_TYPE release + CMAKE_INSTALL_PREFIX /usr/local + NS3_ASSERT OFF + ... + NS3_EXAMPLES ON + ... + NS3_LOG OFF + NS3_TESTS ON + NS3_VERBOSE OFF + ... + + CMAKE_BUILD_TYPE: Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel ... + Keys: [enter] Edit an entry [d] Delete an entry CMake Version 3.22.1 + [l] Show log output [c] Configure + [h] Help [q] Quit without generating + [t] Toggle advanced mode (currently off) + +After moving the cursor and setting the desired values, type ``c`` to configure CMake. + +If you prefer doing everything with a non-interactive command, look at the main ``CMakeLists.txt`` +file in the ns-3-dev directory. It contains most of the option flags and their default values. +To enable both examples and tests, run: + +.. sourcecode:: console + + ~/ns-3-dev/cmake_cache$ cmake -DNS3_EXAMPLES=ON -DNS3_TESTS=ON .. + + +.. _Manually refresh the CMake cache: + +Manually refresh the CMake cache +******************************** + +After the project has been configured, calling ``CMake`` will +:ref:`refresh the CMake cache`. +The refresh is required to discover new targets: libraries, executables and/or modules +that were created since the last run. + +The refresh is done by running the CMake command from the CMake cache folder. + +.. sourcecode:: console + + ~/ns-3-dev/cmake_cache$ cmake .. + +Previous settings stored in the CMakeCache.txt will be preserved, while new modules will be +scanned and targets will be added. + +The cache can also be refreshed with the ns3 wrapper script: + +.. sourcecode:: console + + ~/ns-3-dev$ ./ns3 configure + + +Building the project +******************** + +There are three ways of building the project: +using the ``ns3`` script, calling ``CMake`` and +calling the underlying build system (e.g. Ninja) directly. +The last way is omitted, since each underlying build system +has its own unique command-line syntax. + +Building the project with ns3 ++++++++++++++++++++++++++++++ + +The ns3 wrapper script makes life easier for command line users, accepting module names without +the ``lib`` prefix and scratch files without the ``scratch_`` prefix. The following command can +be used to build the entire project: + +.. sourcecode:: console + + ~/ns-3-dev$ ./ns3 build + + +To build specific targets, run: + +.. sourcecode:: console + + ~/ns-3-dev$ ./ns3 build target_name + + +Building the project with CMake ++++++++++++++++++++++++++++++++ + +The build process of targets (either libraries, executables or custom tasks) can be done +invoking CMake build. To build all the targets, run: + +.. sourcecode:: console + + ~/ns-3-dev/cmake_cache$ cmake --build . + +Notice the single dot now refers to the ``cmake_cache`` directory, where the underlying +build system files are stored (referred inside CMake as ``PROJECT_BINARY_DIR`` or +``CMAKE_BINARY_DIR``, which have slightly different uses if working with sub-projects). + +.. _PROJECT_BINARY_DIR: https://cmake.org/cmake/help/latest/variable/PROJECT_BINARY_DIR.html +.. _CMAKE_BINARY_DIR: https://cmake.org/cmake/help/latest/variable/CMAKE_BINARY_DIR.html + +To build specific targets, run: + +.. sourcecode:: console + + ~/ns-3-dev/cmake_cache$ cmake --build . --target target_name + +Where target_name is a valid target name. Module libraries are prefixed with ``lib`` (e.g. libcore), +executables from the scratch folder are prefixed with ``scratch_`` (e.g. scratch_scratch-simulator). +Executables targets have their source file name without the ".cc" prefix +(e.g. sample-simulator.cc => sample-simulator). + + +Adding a new module +******************* + +Adding a module is the only case where +:ref:`manually refreshing the CMake cache` is required. + +More information on how to create a new module are provided in :ref:`Adding a New Module to ns3`. + +Migrating a Waf module to CMake +******************************* + +If your module does not have external dependencies, porting is very easy. +Start by copying the module Wscript, rename them to CMakeLists.txt and then open it. + +We are going to use the aodv module as an example: + +.. sourcecode:: python3 + + ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- + + def build(bld): + module = bld.create_ns3_module('aodv', ['internet', 'wifi']) + module.includes = '.' + module.source = [ + 'model/aodv-id-cache.cc', + 'model/aodv-dpd.cc', + 'model/aodv-rtable.cc', + 'model/aodv-rqueue.cc', + 'model/aodv-packet.cc', + 'model/aodv-neighbor.cc', + 'model/aodv-routing-protocol.cc', + 'helper/aodv-helper.cc', + ] + + aodv_test = bld.create_ns3_module_test_library('aodv') + aodv_test.source = [ + 'test/aodv-id-cache-test-suite.cc', + 'test/aodv-test-suite.cc', + 'test/aodv-regression.cc', + 'test/bug-772.cc', + 'test/loopback.cc', + ] + + # Tests encapsulating example programs should be listed here + if (bld.env['ENABLE_EXAMPLES']): + aodv_test.source.extend([ + # 'test/aodv-examples-test-suite.cc', + ]) + + headers = bld(features='ns3header') + headers.module = 'aodv' + headers.source = [ + 'model/aodv-id-cache.h', + 'model/aodv-dpd.h', + 'model/aodv-rtable.h', + 'model/aodv-rqueue.h', + 'model/aodv-packet.h', + 'model/aodv-neighbor.h', + 'model/aodv-routing-protocol.h', + 'helper/aodv-helper.h', + ] + + if bld.env['ENABLE_EXAMPLES']: + bld.recurse('examples') + + bld.ns3_python_bindings() + + +We can see the module name is ``aodv`` and it depends on the ``internet`` and the ``wifi`` libraries, +plus the lists of files (``module.source``, ``headers.source`` and ``module_test.source``). + +This translates to the following CMake lines: + +.. sourcecode:: cmake + + build_lib( + LIBNAME aodv # aodv module, which can later be linked to examples and modules with ${libaodv} + SOURCE_FILES # equivalent to module.source + helper/aodv-helper.cc + model/aodv-dpd.cc + model/aodv-id-cache.cc + model/aodv-neighbor.cc + model/aodv-packet.cc + model/aodv-routing-protocol.cc + model/aodv-rqueue.cc + model/aodv-rtable.cc + HEADER_FILES # equivalent to headers.source + helper/aodv-helper.h + model/aodv-dpd.h + model/aodv-id-cache.h + model/aodv-neighbor.h + model/aodv-packet.h + model/aodv-routing-protocol.h + model/aodv-rqueue.h + model/aodv-rtable.h + LIBRARIES_TO_LINK ${libinternet} # depends on internet and wifi, + ${libwifi} # but both are prefixed with lib in CMake + TEST_SOURCES # equivalent to module_test.source + test/aodv-id-cache-test-suite.cc + test/aodv-regression.cc + test/aodv-test-suite.cc + test/loopback.cc + test/bug-772.cc + ) + +If your module depends on external libraries, check the section +`Linking third-party libraries`_. + +Python bindings will be picked up if there is a subdirectory bindings +and NS3_PYTHON_BINDINGS is enabled. + +Next, we need to port the examples wscript. Repeat the copy, rename and open +steps. We should have something like the following: + +.. sourcecode:: python3 + + ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- + + def build(bld): + obj = bld.create_ns3_program('aodv', + ['wifi', 'internet', 'aodv', 'internet-apps']) + obj.source = 'aodv.cc' + +This means we create an example named ``aodv`` which depends on ``wifi``, ``internet``, +``aodv`` and ``internet-apps`` module, and has a single source file ``aodv.cc``. +This translates into the following CMake: + +.. sourcecode:: cmake + + build_lib_example( + NAME aodv # example named aodv + SOURCE_FILES aodv.cc # single source file aodv.cc + LIBRARIES_TO_LINK # depends on wifi, internet, aodv and internet-apps + ${libwifi} + ${libinternet} + ${libaodv} + ${libinternet-apps} + ) + + +Running programs +**************** + +Running programs with the ns3 wrapper script is pretty simple. To run the +scratch program produced by ``scratch/scratch-simulator.cc``, you need the following: + +.. sourcecode:: console + + ~/ns-3-dev$ ./ns3 run scratch-simulator --no-build + +Notice the ``--no-build`` indicates that the program should only be executed, and not built +before execution. + +To familiarize users with CMake, ns3 can also print the underlying CMake +and command line commands used by adding the ``--dry-run`` flag. +Removing the ``--no-build`` flag and adding ``--dry-run`` to the same example, +produces the following: + +.. sourcecode:: console + + ~/ns-3-dev$ ./ns3 --dry-run run scratch-simulator + The following commands would be executed: + cd cmake_cache; cmake --build . -j 15 --target scratch_scratch-simulator ; cd .. + export PATH=$PATH:~/ns-3-dev/build/lib + export PYTHONPATH=~/ns-3-dev/build/bindings/python + export LD_LIBRARY_PATH=~/ns-3-dev/build/lib + ./build/scratch/ns3-dev-scratch-simulator + + +In the CMake build command line, notice the scratch-simulator has a ``scratch_`` prefix. +That is true for all the CMake scratch targets. This is done to guarantee globally unique names. +Similarly, library-related targets have ``lib`` as a prefix (e.g. ``libcore``, ``libnetwork``). + +.. _RPATH: https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_RPATH.html + +The next few lines exporting variables guarantee the executable can find python dependencies +(``PYTHONPATH``) and linked libraries (``LD_LIBRARY_PATH`` and ``PATH`` on Unix-like, and +``PATH`` on Windows). This is not necessary in platforms that support `RPATH`_. + +Notice that when the scratch-simulator program is called on the last line, it has +a ns3-version prefix and could also have a build type suffix. +This is valid for all libraries and executables, but ommited in ns-3 for simplicity. + +Debugging can be done with GDB. Again, we have the two ways to run the program. +Using the ns-3 wrapper: + +.. sourcecode:: console + + ~/ns-3-dev$ ./ns3 run scratch-simulator --no-build --gdb + +Or directly: + +.. sourcecode:: console + + ~/ns-3-dev/cmake_cache$ export PATH=$PATH:~/ns-3-dev/build/lib + ~/ns-3-dev/cmake_cache$ export PYTHONPATH=~/ns-3-dev/build/bindings/python + ~/ns-3-dev/cmake_cache$ export LD_LIBRARY_PATH=~/ns-3-dev/build/lib + ~/ns-3-dev/cmake_cache$ gdb ../build/scratch/ns3-dev-scratch-simulator + + +Modifying files +*************** + +As CMake is not a build system on itself, but a meta build system, it requires +frequent refreshes, also known as reconfigurations. Those refreshes are triggered +automatically in the following cases: + +* Changes in linked libraries +* Changes in the CMake code +* Header changes +* Header/source file name changes +* Module name changes + +The following sections will detail some of these cases assuming a hypothetical module defined below. +Notice that the build_lib is the fundamental piece of every ns-3 module, while user-settable +options and external libraries checking are optional. + +.. sourcecode:: cmake + + build_lib( + LIBNAME hypothetical + SOURCE_FILES helper/hypothetical-helper.cc + model/hypothetical.cc + HEADER_FILES + helper/hypothetical-helper.h + model/hypothetical.h + model/colliding-header.h + LIBRARIES_TO_LINK ${libcore} + ) + + +Module name changes ++++++++++++++++++++ + +Changing the module name requires changing the value of ``LIBNAME``. +In the following example the name of the module seen previously is +changed from ``hypothetical`` to ``new-hypothetical-name``: + +.. sourcecode:: cmake + + build_lib( + LIBNAME new-hypothetical-name + # ... + ) + +If the module was already scanned, saving the changes and trying to build will trigger the +automatic CMake refresh. Otherwise, reconfigure the project to +:ref:`manually refresh it`. + + +Header/source file name changes ++++++++++++++++++++++++++++++++ + +Assuming the hypothetical module defined previously has a header name that collides +with a header of a different module. + +The name of the ``colliding-header.h`` can be changed via the filesystem to +``non-colliding-header.h``, and the ``CMakeLists.txt`` path needs to be updated to match +the new name. Some IDEs can do this automatically through refactoring tools. + +.. sourcecode:: cmake + + build_lib( + LIBNAME new-hypothetical-name + # ... + HEADER_FILES + helper/hypothetical-helper.h + model/hypothetical.h + model/non-colliding-header.h + # ... + ) + + +Linking ns-3 modules +++++++++++++++++++++ + +Adding a dependency to another ns-3 module just requires adding ``${lib${modulename}}`` +to the ``LIBRARIES_TO_LINK`` list, where modulename contains the value of the ns-3 +module which will be depended upon. + +Note: All ns-3 module libraries are prefixed with ``lib``, +as CMake requires unique global target names. + +.. sourcecode:: cmake + + # now ${libnew-hypothetical-name} will depend on both core and internet modules + build_lib( + LIBNAME new-hypothetical-name + # ... + LIBRARIES_TO_LINK ${libcore} + ${libinternet} + # ... + ) + + +.. _Linking third-party libraries: + +Linking third-party libraries ++++++++++++++++++++++++++++++ + +Depending on a third-party library is a bit more complicated as we have multiple +ways to handle that within CMake. + +Linking third-party libraries without CMake or PkgConfig support +================================================================ + +When the third-party library you want to use do not export CMake files to use +``find_package`` or PkgConfig files to use ``pkg_check_modules``, we need to +search for the headers and libraries manually. To simplify this process, +we include the macro ``find_external_library`` that searches for libraries and header include directories, +exporting results similarly to ``find_package``. + +We use a commented version of the ``CMakeLists.txt`` file from the Openflow module as an example: + +.. sourcecode:: cmake + + # Export a user option to specify the path to a custom + # openflow build directory. + set(NS3_WITH_OPENFLOW + "" + CACHE PATH + "Build with Openflow support" + ) + # We use this variable later in the ns-3-dev scope, but + # the value would be lost if we saved it to the + # parent scope ns-3-dev/src or ns-3-dev/contrib. + # We set it as an INTERNAL CACHE variable to make it globally available. + set(NS3_OPENFLOW + "OFF" + CACHE INTERNAL + "ON if Openflow is found" + ) + + # This is the macro that searches for headers and libraries. + # The DEPENDENCY_NAME is the equivalent of the find_package package name. + # Resulting variables will be prefixed with DEPENDENCY_NAME. + # - openflow_FOUND will be set to True if both headers and libraries + # were found and False otherwise + # - openflow_LIBRARIES will contain a list of absolute paths to the + # libraries named in LIBRARY_NAME|LIBRARY_NAMES + # - openflow_INCLUDE_DIRS will contain a list of include directories that contain + # headers named in HEADER_NAME|HEADER_NAMES and directories that contain + # those directories. + # e.g. searching for core-module.h will return + # both ns-3-dev/build/include/ns3 and ns-3-dev/build/include, + # allowing users to include both and + # If a user-settable variable was created, it can be searched too by + # adding it to the SEARCH_PATHS + find_external_library( + DEPENDENCY_NAME openflow + HEADER_NAME openflow.h + LIBRARY_NAME openflow + SEARCH_PATHS ${NS3_WITH_OPENFLOW} + ) + + # Check if header and library were found, + # and stop processing the module in case they were not + if(NOT + ${openflow_FOUND} + ) + message(STATUS "Openflow was not found") + return() + endif() + + # Check for the Boost header used by the openflow module + check_include_file_cxx( + boost/static_assert.hpp + BOOST_STATIC_ASSERT + ) + + # Stop processing the module if it was not found + if(NOT + BOOST_STATIC_ASSERT + ) + message(STATUS "Openflow requires Boost static_assert.hpp") + return() + endif() + + # Here we consume the include directories found by + # find_external_library + include_directories(${openflow_INCLUDE_DIRS}) + + # Manually set definitions + add_definitions( + -DNS3_OPENFLOW + -DENABLE_OPENFLOW + ) + + # Set the cache variable indicating Openflow is enabled as + # all dependencies were met + set(NS3_OPENFLOW + "ON" + CACHE INTERNAL + "ON if Openflow is found in NS3_WITH_OPENFLOW" + ) + + # Additional compilation flag to ignore a specific warning + add_compile_options(-Wno-stringop-truncation) + + # Call macro to create the module target + build_lib( + LIBNAME openflow + SOURCE_FILES + helper/openflow-switch-helper.cc + model/openflow-interface.cc + model/openflow-switch-net-device.cc + HEADER_FILES + helper/openflow-switch-helper.h + model/openflow-interface.h + model/openflow-switch-net-device.h + LIBRARIES_TO_LINK ${libinternet} + # Here we consume the list of libraries + # exported by find_external_library + ${openflow_LIBRARIES} + TEST_SOURCES test/openflow-switch-test-suite.cc + ) + + +Linking third-party libraries using CMake's find_package +======================================================== + +Assume we have a module with optional features that rely on a third-party library +that provides a FindThirdPartyPackage.cmake. This Find.cmake file can be distributed +by `CMake itself`_, via library/package managers (APT, Pacman, +`VcPkg`_), or included to the project tree in the buildsupport/3rd_party directory. + +.. _CMake itself: https://github.com/Kitware/CMake/tree/master/Modules +.. _Vcpkg: https://github.com/Microsoft/vcpkg#using-vcpkg-with-cmake + +This CMake file can be used to import the third-party library into the ns-3 project +and used to enable optional features, add include directories and get a list of +libraries that we can link to our modules. + +We use a modified version of the ``CMakeLists.xt`` file from the Stats module as an example: + + +.. sourcecode:: cmake + + # Set enable flag to false before checking + set(ENABLE_SQLITE False) + + # In this case, SQLite presence is only checked if the user sets + # NS3_SQLITE to ON, but your case may be different + if(${NS3_SQLITE}) + # FindSQLite3.cmake is used by CMake to find SQLite3 + # QUIET flag silences most warnings from the module and let us write our own + find_package(SQLite3 QUIET) # FindSQLite3.cmake was included in CMake 3.14 + + # If SQLite3 was found, SQLite3_FOUND will be set to True, otherwise to False + if(${SQLite3_FOUND}) + set(ENABLE_SQLITE True) + else() + message(STATUS "SQLite was not found") + endif() + endif() + + # Here we declare empty lists, that only hold values if ENABLE_SQLITE is set to ON + set(sqlite_sources) + set(sqlite_header) + set(sqlite_libraries) + if(${ENABLE_SQLITE}) + # If SQLite was found, add the optional source files to the lists + set(sqlite_sources + model/sqlite-data-output.cc + ) + set(sqlite_headers + model/sqlite-data-output.h + ) + # Include the include directories containing the sqlite3.h header + include_directories(${SQLite3_INCLUDE_DIRS}) + # Copy the list of sqlite3 libraries + set(sqlite_libraries + ${SQLite3_LIBRARIES} + ) + + # If the semaphore header is also found, + # append additional optional source files to + # the sqlite sources and headers lists + if(HAVE_SEMAPHORE_H) + list( + APPEND + sqlite_sources + model/sqlite-output.cc + ) + list( + APPEND + sqlite_headers + model/sqlite-output.h + ) + endif() + endif() + + # Sources and headers file lists for stats are quite long, + # so we use these auxiliary lists + # The optional sqlite_sources and sqlite_headers can be empty or not + set(source_files + ${sqlite_sources} + # ... + model/uinteger-8-probe.cc + ) + + set(header_files + ${sqlite_headers} + # ... + model/uinteger-8-probe.h + ) + + # Create the stats module consuming source files + build_lib( + LIBNAME stats + SOURCE_FILES ${source_files} + HEADER_FILES ${header_files} + LIBRARIES_TO_LINK ${libcore} + # Here we either have an empty list or + # a list with the sqlite library + ${sqlite_libraries} + TEST_SOURCES + test/average-test-suite.cc + test/basic-data-calculators-test-suite.cc + test/double-probe-test-suite.cc + test/histogram-test-suite.cc + ) + + + +Linking third-party libraries with PkgConfig support +==================================================== + +Assume we have a module with optional features that rely on a third-party library +that uses PkgConfig. We can look for the PkgConfig module and add the optional +source files similarly to the previous cases, as shown in the example below: + +.. sourcecode:: cmake + + # Include CMake script to use pkg-config + include(FindPkgConfig) + + # If pkg-config was found, search for library you want + if(PKG_CONFIG_FOUND) + pkg_check_modules(THIRD_PARTY libthird-party) + endif() + + set(third_party_sources) + set(third_party_libs) + # Set cached variable if both pkg-config and libthird-party are found + if(PKG_CONFIG_FOUND AND THIRD_PARTY) + # Include third-party include directories for + # consumption of the current module and its examples + include_directories(${THIRD_PARTY_INCLUDE_DIRS}) + + # Use exported CFLAGS required by the third-party library + add_compile_options(${THIRD_PARTY_CFLAGS}) + + # Copy the list of third-party libraries + set(third_party_libs ${THIRD_PARTY_LIBRARIES}) + + # Add optional source files that depend on the third-party library + set(third_party_sources model/optional-feature.cc) + endif() + + # Create module using the optional source files and libraries + build_lib( + LIBNAME hypothetical + SOURCE_FILES model/hypothetical.cc + ${third_party_sources} + HEADER_FILES model/hypothetical.h + LIBRARIES_TO_LINK ${libcore} + # Here we either have an empty list or + # a list with the third-party library + ${third_party_libs} + TEST_SOURCES + test/hypothetical.cc + ) + + + +Inclusion of options +++++++++++++++++++++ + +There are two ways of managing module options: option switches or cached variables. +Both are present in the main CMakeLists.txt in the ns-3-dev directory and the +buildsupport/macros_and_definitions.cmake file. + + +.. sourcecode:: cmake + + # Here are examples of ON and OFF switches + # option( + # NS3_SWITCH # option switch prefixed with NS3\_ + # "followed by the description of what the option does" + # ON # and the default value for that option + # ) + option(NS3_EXAMPLES "Enable examples to be built" OFF) + option(NS3_TESTS "Enable tests to be built" OFF) + + # Now here is how to let the user indicate a path + # set( # declares a value + # NS3_PREFIXED_VALUE # stores the option value + # "" # default value is empty in this case + # CACHE # stores that NS3_PREFIXED_VALUE in the CMakeCache.txt file + # STRING # type of the cached variable + # "description of what this value is used for" + # ) + set(NS3_OUTPUT_DIRECTORY "" CACHE PATH "Directory to store built artifacts") + + # The last case are options that can only assume predefined values + # First we cache different values for that variable + set(NS3_INT64X64 "INT128" CACHE STRING "Int64x64 implementation") + set(NS3_INT64X64 "CAIRO" CACHE STRING "Int64x64 implementation") + set(NS3_INT64X64 "DOUBLE" CACHE STRING "Int64x64 implementation") + + # Then set a cache property for the variable indicating it can assume + # specific values + set_property(CACHE NS3_INT64X64 PROPERTY STRINGS INT128 CAIRO DOUBLE) + + +More details about these commands can be found in the following links: +`option`_, `set`_, `set_property`_. + +.. _option: https://cmake.org/cmake/help/latest/command/option.html +.. _set: https://cmake.org/cmake/help/latest/command/set.html +.. _set_property: https://cmake.org/cmake/help/latest/command/set_property.html + + +Changes in CMake macros and functions ++++++++++++++++++++++++++++++++++++++ + +In order for CMake to feel more familiar to Waf users, a few macros and functions +were created. + +The most frequently used macros them can be found in +``buildsupport/macros_and_definitions.cmake``. This file includes build type checking, +compiler family and version checking, enabling and disabling features based +on user options, checking for dependencies of enabled features, +pre-compiling headers, filtering enabled/disabled modules and dependencies, +and more. + +Module macros +============= + +Module macros are located in ``buildsupport/custom_modules/ns3_module_macros.cmake``. +This file contains macros defining a library (``build_lib``), the associated test library, +examples (``build_lib_example``) and more. It also contains the macro that builds the +module header (``write_module_header``) that includes all headers from the module +for user scripts. + +These macros are responsible for easing the porting of modules from Waf to CMake. + +Module macros: build_lib +~~~~~~~~~~~~~~~~~~~~~~~~ + +As ``build_lib`` is the most important of the macros, we detail what it does here, +block by block. + +The first block declares the arguments received by the macro (in CMake, the +only difference is that a function has its own scope). Notice that there are +different types of arguments. Options that can only be set to true/false +(``IGNORE_PCH``). + +One value arguments that receive a single value +(usually a string) and in this case used to receive the module name (``LIBNAME``). + +Multiple value arguments receive a list of values, which we use to parse lists +of source (for the module itself and for the module tests) and +header files, plus libraries that should be linked and module features. + +The call to ``cmake_parse_arguments`` will parse ``${ARGN}`` into these values. +The variables containing the parsing results will be prefixed with ``BLIB_`` +(e.g. ``LIBNAME`` -> ``BLIB_LIBNAME``). + +.. sourcecode:: cmake + + function(build_lib) + # Argument parsing + set(options IGNORE_PCH) + set(oneValueArgs LIBNAME) + set(multiValueArgs SOURCE_FILES HEADER_FILES LIBRARIES_TO_LINK TEST_SOURCES + DEPRECATED_HEADER_FILES MODULE_ENABLED_FEATURES + ) + cmake_parse_arguments( + "BLIB" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} + ) + # ... + endfunction() + +In the following block, we add modules in the src folder to a list +and modules in the contrib folder to a different list. + +.. sourcecode:: cmake + + function(build_lib) + # ... + # Get path src/module or contrib/module + string(REPLACE "${PROJECT_SOURCE_DIR}/" "" FOLDER + "${CMAKE_CURRENT_SOURCE_DIR}" + ) + + # Add library to a global list of libraries + if("${FOLDER}" MATCHES "src") + set(ns3-libs "${lib${BLIB_LIBNAME}};${ns3-libs}" + CACHE INTERNAL "list of processed upstream modules" + ) + else() + set(ns3-contrib-libs "${lib${BLIB_LIBNAME}};${ns3-contrib-libs}" + CACHE INTERNAL "list of processed contrib modules" + ) + endif() + +In the following block, we check if we are working with Xcode, which does +not handle correctly CMake object libraries (.o files). + +In other platforms, +we build an object file ``add_library(${lib${BLIB_LIBNAME}-obj} OBJECT "${BLIB_SOURCE_FILES}...)`` +and a shared library ``add_library(${lib${BLIB_LIBNAME}} SHARED ...)``. + +The object library contains the actual source files (``${BLIB_SOURCE_FILES}``), +but is not linked, which mean we can reuse the object to build the static version of the libraries. +Notice the shared library uses the object file as its source files +``$ + ) + else() + # Xcode and CMake don't play well when using object libraries, so we have a + # specific path for that + add_library(${lib${BLIB_LIBNAME}} SHARED "${BLIB_SOURCE_FILES}") + + if(${PRECOMPILE_HEADERS_ENABLED} AND (NOT ${IGNORE_PCH})) + target_precompile_headers(${lib${BLIB_LIBNAME}} REUSE_FROM stdlib_pch) + endif() + endif() + # ... + endfunction() + +In the next code block, we create an alias to ``libmodule``, ``ns3::libmodule``, +which can later be used when importing ns-3 with CMake's ``find_package(ns3)``. + +Then, we associate configured headers (``config-store-config``, ``core-config.h`` and +``version-defines.h``) to the core module. + +And finally associate all of the public headers of the module to that library, +to make sure CMake will be refreshed in case one of them changes. + +.. sourcecode:: cmake + + function(build_lib) + # ... + add_library(ns3::${lib${BLIB_LIBNAME}} ALIAS ${lib${BLIB_LIBNAME}}) + + # Associate public headers with library for installation purposes + if("${BLIB_LIBNAME}" STREQUAL "core") + set(config_headers ${CMAKE_HEADER_OUTPUT_DIRECTORY}/config-store-config.h + ${CMAKE_HEADER_OUTPUT_DIRECTORY}/core-config.h + ) + if(${NS3_ENABLE_BUILD_VERSION}) + list(APPEND config_headers + ${CMAKE_HEADER_OUTPUT_DIRECTORY}/version-defines.h + ) + endif() + endif() + set_target_properties( + ${lib${BLIB_LIBNAME}} + PROPERTIES + PUBLIC_HEADER + "${BLIB_HEADER_FILES};${BLIB_DEPRECATED_HEADER_FILES};${config_headers};${CMAKE_HEADER_OUTPUT_DIRECTORY}/${BLIB_LIBNAME}-module.h" + ) + # ... + endfunction() + +In the next code block, we make the library a dependency to the ClangAnalyzer's time trace report, +which measures which step of compilation took most time and which files were responsible for that. + +Then, the ns-3 libraries are separated from non-ns-3 libraries, that can be propagated or not +for libraries/executables linked to the current ns-3 module being processed. + +The default is propagating these third-party libraries and their include directories, but this +can be turned off by setting ``NS3_REEXPORT_THIRD_PARTY_LIBRARIES=OFF`` + +.. sourcecode:: cmake + + function(build_lib) + # ... + if(${NS3_CLANG_TIMETRACE}) + add_dependencies(timeTraceReport ${lib${BLIB_LIBNAME}}) + endif() + + # Split ns and non-ns libraries to manage their propagation properly + set(non_ns_libraries_to_link) + set(ns_libraries_to_link) + + foreach(library ${BLIB_LIBRARIES_TO_LINK}) + # Remove lib prefix from module name (e.g. libcore -> core) + string(REPLACE "lib" "" module_name "${library}") + if(${module_name} IN_LIST ns3-all-enabled-modules) + list(APPEND ns_libraries_to_link ${library}) + else() + list(APPEND non_ns_libraries_to_link ${library}) + endif() + unset(module_name) + endforeach() + + if(NOT ${NS3_REEXPORT_THIRD_PARTY_LIBRARIES}) + # ns-3 libraries are linked publicly, to make sure other modules can find + # each other without being directly linked + set(exported_libraries PUBLIC ${LIB_AS_NEEDED_PRE} ${ns_libraries_to_link} + ${LIB_AS_NEEDED_POST} + ) + + # non-ns-3 libraries are linked privately, not propagating unnecessary + # libraries such as pthread, librt, etc + set(private_libraries PRIVATE ${LIB_AS_NEEDED_PRE} + ${non_ns_libraries_to_link} ${LIB_AS_NEEDED_POST} + ) + + # we don't re-export included libraries from 3rd-party modules + set(exported_include_directories) + else() + # we export everything by default when NS3_REEXPORT_THIRD_PARTY_LIBRARIES=ON + set(exported_libraries PUBLIC ${LIB_AS_NEEDED_PRE} ${ns_libraries_to_link} + ${non_ns_libraries_to_link} ${LIB_AS_NEEDED_POST} + ) + set(private_libraries) + + # with NS3_REEXPORT_THIRD_PARTY_LIBRARIES, we export all 3rd-party library + # include directories, allowing consumers of this module to include and link + # the 3rd-party code with no additional setup + get_target_includes(${lib${BLIB_LIBNAME}} exported_include_directories) + string(REPLACE "-I" "" exported_include_directories + "${exported_include_directories}" + ) + string(REPLACE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/include" "" + exported_include_directories + "${exported_include_directories}" + ) + endif() + # ... + endfunction() + +After the lists of libraries to link that should be exported (``PUBLIC``) and +not exported (``PRIVATE``) are built, we can link them with ``target_link_libraries``. + +Next, we set the output name of the module library to n3version-modulename(optional build suffix). + +.. sourcecode:: cmake + + function(build_lib) + # ... + target_link_libraries( + ${lib${BLIB_LIBNAME}} ${exported_libraries} ${private_libraries} + ) + + # set output name of library + set_target_properties( + ${lib${BLIB_LIBNAME}} + PROPERTIES OUTPUT_NAME ns${NS3_VER}-${BLIB_LIBNAME}${build_profile_suffix} + ) + # ... + endfunction() + +Next we export include directories, to let library consumers importing ns-3 via CMake +use them just by linking to one of the ns-3 modules. + +.. sourcecode:: cmake + + function(build_lib) + # ... + # export include directories used by this library so that it can be used by + # 3rd-party consumers of ns-3 using find_package(ns3) this will automatically + # add the build/include path to them, so that they can ns-3 headers with + # + target_include_directories( + ${lib${BLIB_LIBNAME}} + PUBLIC $ + $ + INTERFACE ${exported_include_directories} + ) + # ... + endfunction() + +We append the list of third-party/external libraries for each processed module, +and append a list of object libraries that can be later used for the static ns-3 build. + +.. sourcecode:: cmake + + function(build_lib) + # ... + set(ns3-external-libs "${non_ns_libraries_to_link};${ns3-external-libs}" + CACHE INTERNAL + "list of non-ns libraries to link to NS3_STATIC and NS3_MONOLIB" + ) + if(${NS3_STATIC} OR ${NS3_MONOLIB}) + set(lib-ns3-static-objs + "$;${lib-ns3-static-objs}" + CACHE + INTERNAL + "list of object files from module used by NS3_STATIC and NS3_MONOLIB" + ) + endif() + # ... + endfunction() + +The following block creates the ``${BLIB_LIBNAME}-module.h`` header for user scripts, +and copies header files from src/module and contrib/module to the include/ns3 directory. + +.. sourcecode:: cmake + + function(build_lib) + # ... + # Write a module header that includes all headers from that module + write_module_header("${BLIB_LIBNAME}" "${BLIB_HEADER_FILES}") + + # Copy all header files to outputfolder/include before each build + copy_headers_before_building_lib( + ${BLIB_LIBNAME} ${CMAKE_HEADER_OUTPUT_DIRECTORY} "${BLIB_HEADER_FILES}" + public + ) + if(BLIB_DEPRECATED_HEADER_FILES) + copy_headers_before_building_lib( + ${BLIB_LIBNAME} ${CMAKE_HEADER_OUTPUT_DIRECTORY} + "${BLIB_DEPRECATED_HEADER_FILES}" deprecated + ) + endif() + # ... + endfunction() + +The following block creates the test library for the module currently being processed. + +.. sourcecode:: cmake + + function(build_lib) + # ... + # Build tests if requested + if(${ENABLE_TESTS}) + list(LENGTH BLIB_TEST_SOURCES test_source_len) + if(${test_source_len} GREATER 0) + # Create BLIB_LIBNAME of output library test of module + set(test${BLIB_LIBNAME} lib${BLIB_LIBNAME}-test CACHE INTERNAL "") + set(ns3-libs-tests "${test${BLIB_LIBNAME}};${ns3-libs-tests}" + CACHE INTERNAL "list of test libraries" + ) + + # Create shared library containing tests of the module + add_library(${test${BLIB_LIBNAME}} SHARED "${BLIB_TEST_SOURCES}") + + # Link test library to the module library + if(${NS3_MONOLIB}) + target_link_libraries( + ${test${BLIB_LIBNAME}} ${LIB_AS_NEEDED_PRE} ${lib-ns3-monolib} + ${LIB_AS_NEEDED_POST} + ) + else() + target_link_libraries( + ${test${BLIB_LIBNAME}} ${LIB_AS_NEEDED_PRE} ${lib${BLIB_LIBNAME}} + "${BLIB_LIBRARIES_TO_LINK}" ${LIB_AS_NEEDED_POST} + ) + endif() + set_target_properties( + ${test${BLIB_LIBNAME}} + PROPERTIES OUTPUT_NAME + ns${NS3_VER}-${BLIB_LIBNAME}-test${build_profile_suffix} + ) + + target_compile_definitions( + ${test${BLIB_LIBNAME}} PRIVATE NS_TEST_SOURCEDIR="${FOLDER}/test" + ) + if(${PRECOMPILE_HEADERS_ENABLED} AND (NOT ${IGNORE_PCH})) + target_precompile_headers(${test${BLIB_LIBNAME}} REUSE_FROM stdlib_pch) + endif() + endif() + endif() + # ... + endfunction() + +The following block checks for examples subdirectories and add them to parse their +CMakeLists.txt file, creating the examples. It also scans for python examples. + +.. sourcecode:: cmake + + function(build_lib) + # ... + # Build lib examples if requested + if(${ENABLE_EXAMPLES}) + foreach(example_folder example;examples) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${example_folder}) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${example_folder}/CMakeLists.txt) + add_subdirectory(${example_folder}) + endif() + scan_python_examples(${CMAKE_CURRENT_SOURCE_DIR}/${example_folder}) + endif() + endforeach() + endif() + # ... + endfunction() + +The following block creates the ``lib${BLIB_LIBNAME}-apiscan`` CMake target. +When the target is built, it runs modulescan-modular to extract the python +bindings for the module using pybindgen. + +.. sourcecode:: cmake + + function(build_lib) + # ... + # Get architecture pair for python bindings + if((${CMAKE_SIZEOF_VOID_P} EQUAL 8) AND (NOT APPLE)) + set(arch gcc_LP64) + set(arch_flags -m64) + else() + set(arch gcc_ILP32) + set(arch_flags) + endif() + + # Add target to scan python bindings + if(${ENABLE_SCAN_PYTHON_BINDINGS} + AND EXISTS ${CMAKE_HEADER_OUTPUT_DIRECTORY}/${BLIB_LIBNAME}-module.h + ) + set(bindings_output_folder ${PROJECT_SOURCE_DIR}/${FOLDER}/bindings) + file(MAKE_DIRECTORY ${bindings_output_folder}) + set(module_api_ILP32 ${bindings_output_folder}/modulegen__gcc_ILP32.py) + set(module_api_LP64 ${bindings_output_folder}/modulegen__gcc_LP64.py) + + set(modulescan_modular_command + ${Python_EXECUTABLE} + ${PROJECT_SOURCE_DIR}/bindings/python/ns3modulescan-modular.py + ) + + set(header_map "") + # We need a python map that takes header.h to module e.g. "ptr.h": "core" + foreach(header ${BLIB_HEADER_FILES}) + # header is a relative path to the current working directory + get_filename_component( + header_name ${CMAKE_CURRENT_SOURCE_DIR}/${header} NAME + ) + string(APPEND header_map "\"${header_name}\":\"${BLIB_LIBNAME}\",") + endforeach() + + set(ns3-headers-to-module-map "${ns3-headers-to-module-map}${header_map}" + CACHE INTERNAL "Map connecting headers to their modules" + ) + + # API scan needs the include directories to find a few headers (e.g. mpi.h) + get_target_includes(${lib${BLIB_LIBNAME}} modulegen_include_dirs) + + set(module_to_generate_api ${module_api_ILP32}) + set(LP64toILP32) + if("${arch}" STREQUAL "gcc_LP64") + set(module_to_generate_api ${module_api_LP64}) + set(LP64toILP32 + ${Python_EXECUTABLE} + ${PROJECT_SOURCE_DIR}/buildsupport/pybindings_LP64_to_ILP32.py + ${module_api_LP64} ${module_api_ILP32} + ) + endif() + + add_custom_target( + ${lib${BLIB_LIBNAME}}-apiscan + COMMAND + ${modulescan_modular_command} ${CMAKE_OUTPUT_DIRECTORY} ${BLIB_LIBNAME} + ${PROJECT_BINARY_DIR}/header_map.json ${module_to_generate_api} + \"${arch_flags} ${modulegen_include_dirs}\" + COMMAND ${LP64toILP32} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${lib${BLIB_LIBNAME}} + ) + add_dependencies(apiscan-all ${lib${BLIB_LIBNAME}}-apiscan) + endif() + + # ... + endfunction() + +The targets can be built to execute the scanning using one of the following commands: + +.. sourcecode:: console + + ~/cmake_cache$ cmake --build . --target libcore-apiscan + ~/ns-3-dev/$ ./ns3 build core-apiscan + +To re-scan all bindings, use ``./ns3 build apiscan-all``. + +The next block runs the ``modulegen-modular`` and consumes the results of the previous step. +However, this step runs at configuration time, while the previous runs at build time. +This means the output directory needs to be cleared after running ``apiscan``, to regenerate +up-to-date bindings source files. + +.. sourcecode:: cmake + + function(build_lib) + # ... + # Build pybindings if requested and if bindings subfolder exists in + # NS3/src/BLIB_LIBNAME + if(${ENABLE_PYTHON_BINDINGS} AND EXISTS + "${CMAKE_CURRENT_SOURCE_DIR}/bindings" + ) + set(bindings_output_folder ${CMAKE_OUTPUT_DIRECTORY}/${FOLDER}/bindings) + file(MAKE_DIRECTORY ${bindings_output_folder}) + set(module_src ${bindings_output_folder}/ns3module.cc) + set(module_hdr ${bindings_output_folder}/ns3module.h) + + string(REPLACE "-" "_" BLIB_LIBNAME_sub ${BLIB_LIBNAME}) # '-' causes + # problems (e.g. + # csma-layout), replace with '_' (e.g. csma_layout) + + # Set prefix of binding to _ if a ${BLIB_LIBNAME}.py exists, and copy the + # ${BLIB_LIBNAME}.py to the output folder + set(prefix) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/bindings/${BLIB_LIBNAME}.py) + set(prefix _) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/bindings/${BLIB_LIBNAME}.py + DESTINATION ${CMAKE_OUTPUT_DIRECTORY}/bindings/python/ns + ) + endif() + + # Run modulegen-modular to generate the bindings sources + if((NOT EXISTS ${module_hdr}) OR (NOT EXISTS ${module_src})) # OR TRUE) # to + # force + # reprocessing + string(REPLACE ";" "," ENABLED_FEATURES + "${ns3-libs};${BLIB_MODULE_ENABLED_FEATURES}" + ) + set(modulegen_modular_command + GCC_RTTI_ABI_COMPLETE=True NS3_ENABLED_FEATURES="${ENABLED_FEATURES}" + ${Python_EXECUTABLE} + ${PROJECT_SOURCE_DIR}/bindings/python/ns3modulegen-modular.py + ) + execute_process( + COMMAND + ${CMAKE_COMMAND} -E env + PYTHONPATH=${CMAKE_OUTPUT_DIRECTORY}:$ENV{PYTHONPATH} + ${modulegen_modular_command} ${CMAKE_CURRENT_SOURCE_DIR} ${arch} + ${prefix}${BLIB_LIBNAME_sub} ${module_src} + TIMEOUT 60 + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_FILE ${module_hdr} + ERROR_FILE ${bindings_output_folder}/ns3modulegen.log + RESULT_VARIABLE error_code + ) + if(${error_code} OR NOT (EXISTS ${module_hdr})) + message( + FATAL_ERROR + "Something went wrong during processing of the python bindings of module ${BLIB_LIBNAME}." + " Make sure you have the latest version of Pybindgen." + ) + if(EXISTS ${module_src}) + file(REMOVE ${module_src}) + endif() + endif() + endif() + + # Add core module helper sources + set(python_module_files ${module_hdr} ${module_src}) + if(${BLIB_LIBNAME} STREQUAL "core") + list(APPEND python_module_files + ${CMAKE_CURRENT_SOURCE_DIR}/bindings/module_helpers.cc + ${CMAKE_CURRENT_SOURCE_DIR}/bindings/scan-header.h + ) + endif() + set(bindings-name lib${BLIB_LIBNAME}-bindings) + add_library(${bindings-name} SHARED "${python_module_files}") + target_include_directories( + ${bindings-name} PUBLIC ${Python_INCLUDE_DIRS} ${bindings_output_folder} + ) + target_compile_options(${bindings-name} PRIVATE -Wno-error) + + # If there is any, remove the "lib" prefix of libraries (search for + # "set(lib${BLIB_LIBNAME}") + list(LENGTH ns_libraries_to_link num_libraries) + if(num_libraries GREATER "0") + string(REPLACE ";" "-bindings;" bindings_to_link + "${ns_libraries_to_link};" + ) # add -bindings suffix to all lib${name} + endif() + target_link_libraries( + ${bindings-name} + PUBLIC ${LIB_AS_NEEDED_PRE} ${lib${BLIB_LIBNAME}} "${bindings_to_link}" + "${BLIB_LIBRARIES_TO_LINK}" ${LIB_AS_NEEDED_POST} + PRIVATE ${Python_LIBRARIES} + ) + target_include_directories( + ${bindings-name} PRIVATE ${PROJECT_SOURCE_DIR}/src/core/bindings + ) + + set(suffix) + if(APPLE) + # Python doesn't like Apple's .dylib and will refuse to load bindings + # unless its an .so + set(suffix SUFFIX .so) + endif() + + # Set binding library name and output folder + set_target_properties( + ${bindings-name} + PROPERTIES OUTPUT_NAME ${prefix}${BLIB_LIBNAME_sub} + PREFIX "" + ${suffix} LIBRARY_OUTPUT_DIRECTORY + ${CMAKE_OUTPUT_DIRECTORY}/bindings/python/ns + ) + + set(ns3-python-bindings-modules + "${bindings-name};${ns3-python-bindings-modules}" + CACHE INTERNAL "list of modules python bindings" + ) + + # Make sure all bindings are built before building the visualizer module + # that makes use of them + if(${ENABLE_VISUALIZER} AND (visualizer IN_LIST libs_to_build)) + if(NOT (${BLIB_LIBNAME} STREQUAL visualizer)) + add_dependencies(${libvisualizer} ${bindings-name}) + endif() + endif() + endif() + # ... + endfunction() + +The recommended steps to scan the bindings and then use them is the following: + +.. sourcecode:: console + + ~/ns-3-dev$ ./ns3 clean + ~/ns-3-dev$ ./ns3 configure -- -DNS3_SCAN_PYTHON_BINDINGS=ON + ~/ns-3-dev$ ./ns3 build apiscan-all + ~/ns-3-dev$ ./ns3 configure --enable-python-bindings + ~/ns-3-dev$ ./ns3 build + + +In the next code block we add the library to the ns3ExportTargets, later used for installation. +We also print an additional message the folder just finished being processed if NS3_VERBOSE is set to ON. + +.. sourcecode:: cmake + + function(build_lib) + # ... + # Handle package export + install( + TARGETS ${lib${BLIB_LIBNAME}} + EXPORT ns3ExportTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/ + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/ + PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ns3" + ) + if(${NS3_VERBOSE}) + message(STATUS "Processed ${FOLDER}") + endif() + endfunction() + +Module macros: build_lib_example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The second most important macro from a module author perspective is the ``build_lib_example``, which +builds the examples for their module. As with ``build_lib`` we explain what it does block-by-block. + +In the first block, arguments are parsed and we check wether the current module is in the contrib +or the src folder. + +.. sourcecode:: cmake + + function(build_lib_example) + # Argument parsing + set(options IGNORE_PCH) + set(oneValueArgs NAME) + set(multiValueArgs SOURCE_FILES HEADER_FILES LIBRARIES_TO_LINK) + cmake_parse_arguments("BLIB_EXAMPLE" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + # Get path src/module or contrib/module + string(REPLACE "${PROJECT_SOURCE_DIR}/" "" FOLDER "${CMAKE_CURRENT_SOURCE_DIR}") + get_filename_component(FOLDER ${FOLDER} DIRECTORY) + # ... + endfunction() + +Then we check if the ns-3 modules required by the example are enabled to be built. +The ``if(EXISTS ${lib})`` skip external libraries which are listed with their absolute paths. + +.. sourcecode:: cmake + + function(build_lib_example) + # ... + # cmake-format: on + set(missing_dependencies) + foreach(lib ${BLIB_EXAMPLE_LIBRARIES_TO_LINK}) + # skip check for ns-3 modules if its a path to a library + if(EXISTS ${lib}) + continue() + endif() + + # check if the example depends on disabled modules + string(REPLACE "lib" "" lib ${lib}) + if(NOT (${lib} IN_LIST ns3-all-enabled-modules)) + list(APPEND missing_dependencies ${lib}) + endif() + endforeach() + # ... + endfunction() + +If the list ``missing_dependencies`` is empty, we create the example. Otherwise, we skip it. +The example can be linked to the current module (``${lib${BLIB_EXAMPLE_LIBNAME}}``) and +other libraries to link (``${BLIB_EXAMPLE_LIBRARIES_TO_LINK}``) and optionally to the visualizer +module (``${optional_visualizer_lib}``). +If the visualizer module is not enabled, ``optional_visualizer_lib`` is empty. + +The example can also be linked to a single ns-3 shared library (``lib-ns3-monolib``) or +a single ns-3 static library (``lib-ns3-static``), if either ``NS3_MONOLIB=ON`` or ``NS3_STATIC=ON``. + +.. sourcecode:: cmake + + function(build_lib_example) + # ... + if(NOT missing_dependencies) + # Create shared library with sources and headers + add_executable( + "${BLIB_EXAMPLE_NAME}" ${BLIB_EXAMPLE_SOURCE_FILES} + ${BLIB_EXAMPLE_HEADER_FILES} + ) + + if(${NS3_STATIC}) + target_link_libraries( + ${BLIB_EXAMPLE_NAME} ${LIB_AS_NEEDED_PRE_STATIC} ${lib-ns3-static} + ) + elseif(${NS3_MONOLIB}) + target_link_libraries( + ${BLIB_EXAMPLE_NAME} ${LIB_AS_NEEDED_PRE} ${lib-ns3-monolib} + ${LIB_AS_NEEDED_POST} + ) + else() + target_link_libraries( + ${BLIB_EXAMPLE_NAME} ${LIB_AS_NEEDED_PRE} ${lib${BLIB_EXAMPLE_LIBNAME}} + ${BLIB_EXAMPLE_LIBRARIES_TO_LINK} ${optional_visualizer_lib} + ${LIB_AS_NEEDED_POST} + ) + endif() + # ... + endif() + endfunction() + +As with the module libraries, we can also reuse precompiled headers here to speed up +the parsing step of compilation. + +Finally, we call another macro ``set_runtime_outputdirectory``, which indicates the +resulting folder where the example will end up after built (e.g. build/src/module/examples) +and adds the proper ns-3 version prefix and build type suffix to the executable. + +.. sourcecode:: cmake + + function(build_lib_example) + # ... + if(NOT missing_dependencies) + # ... + if(${PRECOMPILE_HEADERS_ENABLED} AND (NOT ${BLIB_EXAMPLE_IGNORE_PCH})) + target_precompile_headers(${BLIB_EXAMPLE_NAME} REUSE_FROM stdlib_pch_exec) + endif() + + set_runtime_outputdirectory( + ${BLIB_EXAMPLE_NAME} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${FOLDER}/examples/ "" + ) + endif() + endfunction() + +User options and header checking +================================ + +User-settable options should be prefixed with ``NS3_``, otherwise +they will not be preserved by ``./ns3 configure --force-refresh``. + +After checking if the pre-requisites of the user-settable options +are met, set the same option now prefixed with ``ENABLE_``. The +following example demonstrates this pattern: + +.. sourcecode:: cmake + + # Option() means the variable NS3_GSL will be set to ON/OFF + # The second argument is a comment explaining what this option does + # The last argument is the default value for the user-settable option + option(NS3_GSL "Enable GSL related features" OFF) + + # Set the ENABLE\_ counterpart to FALSE by default + set(ENABLE_GSL FALSE) + if(${NS3_GSL}) + # If the user enabled GSL, check if GSL is available + find_package(GSL) + if(${GSL_FOUND}) + set(ENABLE_GSL TRUE) + message(STATUS "GSL was requested by the user and was found") + else() + message(STATUS "GSL was not found and GSL features will continue disabled") + endif() + else() + message(STATUS "GSL features were not requested by the user") + endif() + + # Now the module can check for ENABLE_GSL before being processed + if(NOT ${ENABLE_GSL}) + return() + endif() + + # Or to enable optional features + set(gsl_sources) + if(${ENABLE_GSL}) + set(gsl_sources model/gsl_features.cc) + endif() + +Here are examples of how to do the options and header checking, +followed by a header configuration: + +.. sourcecode:: cmake + + # We always set the ENABLE\_ counterpart of NS3\_ option to FALSE before checking + # + # If this variable is created inside your module, use + # set(ENABLE_MPI FALSE CACHE INTERNAL "") + # instead, to make it globally available + set(ENABLE_MPI FALSE) + + # If the user option switch is set to ON, we check + if(${NS3_MPI}) + # Use find_package to look for MPI + find_package(MPI QUIET) + + # If the package is optional, which is the case for MPI, + # we can proceed if it is not found + if(NOT ${MPI_FOUND}) + message(STATUS "MPI was not found. Continuing without it.") + else() + # If it is false, we add necessary C++ definitions (e.g. NS3_MPI) + message(STATUS "MPI was found.") + add_definitions(-DNS3_MPI) + + # Then set ENABLE_MPI to TRUE, which can be used to check + # if NS3_MPI is enabled AND MPI was found + # + # If this variable is created inside your module, use + # set(ENABLE_MPI TRUE CACHE INTERNAL "") + # instead, to make it globally available + set(ENABLE_MPI TRUE) + endif() + endif() + + # ... + + # These two standard CMake modules allow for header and function checking + include(CheckIncludeFileCXX) + include(CheckFunctionExists) + + # Check for required headers and functions, + # set flags on the right argument if header in the first argument is found + # if they are not found, a warning is emitted + check_include_file_cxx("stdint.h" "HAVE_STDINT_H") + check_include_file_cxx("inttypes.h" "HAVE_INTTYPES_H") + check_include_file_cxx("sys/types.h" "HAVE_SYS_TYPES_H") + check_include_file_cxx("stat.h" "HAVE_SYS_STAT_H") + check_include_file_cxx("dirent.h" "HAVE_DIRENT_H") + check_include_file_cxx("stdlib.h" "HAVE_STDLIB_H") + check_include_file_cxx("signal.h" "HAVE_SIGNAL_H") + check_include_file_cxx("netpacket/packet.h" "HAVE_PACKETH") + check_function_exists("getenv" "HAVE_GETENV") + + # This is the CMake command to open up a file template (in this case a header + # passed as the first argument), then fill its fields with values stored in + # CMake variables and save the resulting file to the target destination + # (in the second argument) + configure_file( + buildsupport/core-config-template.h + ${CMAKE_HEADER_OUTPUT_DIRECTORY}/core-config.h + ) + +The configure_file command is not very clear by itself, as you do not know which +values are being used. So we need to check the template. + +.. sourcecode:: cpp + + #ifndef NS3_CORE_CONFIG_H + #define NS3_CORE_CONFIG_H + + // Defined if HAVE_UINT128_T is defined in CMake + #cmakedefine HAVE_UINT128_T + // Set to 1 if HAVE__UINT128_T is defined in CMake, 0 otherwise + #cmakedefine01 HAVE___UINT128_T + #cmakedefine INT64X64_USE_128 + #cmakedefine INT64X64_USE_DOUBLE + #cmakedefine INT64X64_USE_CAIRO + #cmakedefine01 HAVE_STDINT_H + #cmakedefine01 HAVE_INTTYPES_H + #cmakedefine HAVE_SYS_INT_TYPES_H + #cmakedefine01 HAVE_SYS_TYPES_H + #cmakedefine01 HAVE_SYS_STAT_H + #cmakedefine01 HAVE_DIRENT_H + #cmakedefine01 HAVE_STDLIB_H + #cmakedefine01 HAVE_GETENV + #cmakedefine01 HAVE_SIGNAL_H + #cmakedefine HAVE_PTHREAD_H + #cmakedefine HAVE_RT + + /* + * #cmakedefine turns into: + * //#define HAVE_FLAG // if HAVE_FLAG is not defined in CMake (e.g. unset(HAVE_FLAG)) + * #define HAVE_FLAG // if HAVE_FLAG is defined in CMake (e.g. set(HAVE_FLAG)) + * + * #cmakedefine01 turns into: + * #define HAVE_FLAG 0 // if HAVE_FLAG is not defined in CMake + * #define HAVE_FLAG 1 // if HAVE_FLAG is defined in CMake + */ + + #endif //NS3_CORE_CONFIG_H + +Custom targets +============== + +Another common thing to do is implement custom targets that run specific commands and +manage dependencies. Here is an example for Doxygen: + +.. sourcecode:: cmake + + # This command hides DOXYGEN from some CMake cache interfaces + mark_as_advanced(DOXYGEN) + + # This custom macro checks for dependencies CMake find_package and program + # dependencies and return the missing dependencies in the third argument + check_deps("" "doxygen;dot;dia" doxygen_docs_missing_deps) + + # If the variable contains missing dependencies, we stop processing doxygen targets + if(doxygen_docs_missing_deps) + message( + STATUS + "docs: doxygen documentation not enabled due to missing dependencies: ${doxygen_docs_missing_deps}" + ) + else() + # We checked this already exists, but we need the path to the executable + find_package(Doxygen QUIET) + + # Get introspected doxygen + add_custom_target( + run-print-introspected-doxygen + COMMAND + ${CMAKE_OUTPUT_DIRECTORY}/utils/ns${NS3_VER}-print-introspected-doxygen${build_profile_suffix} + > ${PROJECT_SOURCE_DIR}/doc/introspected-doxygen.h + COMMAND + ${CMAKE_OUTPUT_DIRECTORY}/utils/ns${NS3_VER}-print-introspected-doxygen${build_profile_suffix} + --output-text > ${PROJECT_SOURCE_DIR}/doc/ns3-object.txt + DEPENDS print-introspected-doxygen + ) + + # Run test.py with NS_COMMANDLINE_INTROSPECTION=.. to print examples + # introspected commandline + add_custom_target( + run-introspected-command-line + COMMAND ${CMAKE_COMMAND} -E env NS_COMMANDLINE_INTROSPECTION=.. + ${Python_EXECUTABLE} ./test.py --no-build --constrain=example + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS all-test-targets # all-test-targets only exists if ENABLE_TESTS is + # set to ON + ) + + # This file header is written during configuration + file( + WRITE ${PROJECT_SOURCE_DIR}/doc/introspected-command-line.h + "/* 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" + ) + # After running test.py for the introspected commandline above, + # merge outputs and concatenate to the header file created during + # configuration + add_custom_target( + assemble-introspected-command-line + # works on CMake 3.18 or newer > COMMAND ${CMAKE_COMMAND} -E cat + # ${PROJECT_SOURCE_DIR}/testpy-output/*.command-line > + # ${PROJECT_SOURCE_DIR}/doc/introspected-command-line.h + COMMAND ${cat_command} ${PROJECT_SOURCE_DIR}/testpy-output/*.command-line + > ${PROJECT_SOURCE_DIR}/doc/introspected-command-line.h 2> NULL + DEPENDS run-introspected-command-line + ) + + # Create a target that updates the doxygen version + add_custom_target( + update_doxygen_version + COMMAND ${PROJECT_SOURCE_DIR}/doc/ns3_html_theme/get_version.sh + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + ) + + # Create a doxygen target that builds the documentation and only runs + # after the version target above was executed, the introspected doxygen + # and command line were extracted + add_custom_target( + doxygen + COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_SOURCE_DIR}/doc/doxygen.conf + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS update_doxygen_version run-print-introspected-doxygen + assemble-introspected-command-line + ) + + # Create a doxygen target that only needs to run the version target + # which doesn't trigger compilation of examples neither the execution of test.py + # nor print-introspected-doxygen + add_custom_target( + doxygen-no-build + COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_SOURCE_DIR}/doc/doxygen.conf + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS update_doxygen_version + ) + endif() + + +Project-wide compiler and linker flags +====================================== + +Different compilers and links accept different flags, which must be +known during configuration time. Some of these flags are +handled directly by CMake: + +.. sourcecode:: cmake + + # equivalent to -fPIC for libraries and -fPIE for executables + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + + # link-time optimization flags such as -flto and -flto=thin + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + + # C++ standard flag to use + set(CMAKE_CXX_STANDARD_MINIMUM 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + + add_library(static_lib STATIC) # equivalent to -static flag + add_library(shared_lib SHARED) # equivalent to -shared flags + +Other flags need to be handled manually and change based on +the compiler used. The most commonly used are handled in +``buildsupport/macros_and_definitions.cmake``. + +.. sourcecode:: cmake + + set(LIB_AS_NEEDED_PRE) + set(LIB_AS_NEEDED_POST) + if(${GCC} AND NOT APPLE) + # using GCC + set(LIB_AS_NEEDED_PRE -Wl,--no-as-needed) + set(LIB_AS_NEEDED_POST -Wl,--as-needed) + set(LIB_AS_NEEDED_PRE_STATIC -Wl,--whole-archive,-Bstatic) + set(LIB_AS_NEEDED_POST_STATIC -Wl,--no-whole-archive) + set(LIB_AS_NEEDED_POST_STATIC_DYN -Wl,-Bdynamic,--no-whole-archive) + endif() + +The ``LIB_AS_NEEDED`` values are used to force linking of all symbols, +and not only those explicitly used by the applications, which is necessary +since simulation scripts don't directly use most of the symbols exported +by the modules. Their use can be seen in the ``utils/CMakeLists.txt``: + +.. sourcecode:: cmake + + target_link_libraries( + test-runner ${LIB_AS_NEEDED_PRE} ${ns3-libs-tests} ${LIB_AS_NEEDED_POST} + ${ns3-libs} ${ns3-contrib-libs} + ) + +This will ensure test-runner linking to ``ns3-libs-tests`` (list containing all +module test libraries) with all symbols, which will make it able to find and run +all tests. The other two lists ``ns3-libs`` (src modules) and ``ns3-contrib-libs`` +(contrib modules) don't need to be completely linked since the tests libraries are +already linked to them. + +Other project-wide compiler-dependent flags can be set during compiler checking. + +.. sourcecode:: cmake + + # Check if the compiler is GCC + set(GCC FALSE) + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") + # Check if GCC is a supported version + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${GNU_MinVersion}) + message( + FATAL_ERROR + "GNU ${CMAKE_CXX_COMPILER_VERSION} ${below_minimum_msg} ${GNU_MinVersion}" + ) + endif() + # If GCC is up-to-date, set flag to true and continue + set(GCC TRUE) + + # Disable semantic interposition + add_definitions(-fno-semantic-interposition) + endif() + +.. _disables semantic interposition: https://gitlab.com/nsnam/ns-3-dev/-/merge_requests/777 + +The ``-fno-semantic-interposition`` flag `disables semantic interposition`_, which can +reduce overhead of function calls in shared libraries built with ``-fPIC``. +This is the default behaviour for Clang. The inlined ns-3 calls will not be +correctly interposed by the ``LD_PRELOAD`` trick, which is not know to be used by ns-3 users. +To re-enable semantic interposition, comment out the line and reconfigure the project. + +Note: the most common use of the ``LD_PRELOAD`` trick is to use custom memory allocators, +and this continues to work since the interposed symbols are from the standard libraries, +which are compiled with semantic interposition. + +Some modules may require special flags. The Openflow module for example +may require ``-Wno-stringop-truncation`` flag to prevent an warning that +is treated as error to prevent the compilation from proceeding. The flag +can be passed to the entire module with the following: + +.. sourcecode:: cmake + + add_compile_options(-Wno-stringop-truncation) + + build_lib( + LIBNAME openflow + SOURCE_FILES + helper/openflow-switch-helper.cc + model/openflow-interface.cc + model/openflow-switch-net-device.cc + HEADER_FILES + helper/openflow-switch-helper.h + model/openflow-interface.h + model/openflow-switch-net-device.h + LIBRARIES_TO_LINK ${libinternet} + ${openflow_LIBRARIES} + TEST_SOURCES test/openflow-switch-test-suite.cc + ) + +If a flag prevents your compiler from compiling, wrap the flag inside a +compiler check. The currently checked compilers are ``GCC`` and ``CLANG`` +(includes both upstream LLVM Clang and Apple Clang). + +.. sourcecode:: cmake + + if(NOT ${FAILING_COMPILER}) + add_compile_options(-Wno-stringop-truncation) + endif() + + # or + + if(${ONLY_COMPILER_THAT_SUPPORTS_UNIQUE_FLAG}) + add_compile_options(-unique_flag) + endif() + diff --git a/doc/manual/source/working-with-git.rst b/doc/manual/source/working-with-git.rst index 992554ede..2e3b6f26c 100644 --- a/doc/manual/source/working-with-git.rst +++ b/doc/manual/source/working-with-git.rst @@ -42,6 +42,8 @@ Setup of a personal repository We will provide two ways, one anonymous (but will impede the creation of merge requests) and the other, preferred, that include forking the repository through the GitLab.com web interface. +.. _Directly cloning ns-3-dev: + Directly cloning ns-3-dev +++++++++++++++++++++++++ diff --git a/doc/tutorial/source/getting-started.rst b/doc/tutorial/source/getting-started.rst index e5c0e4239..a0e4d3b24 100644 --- a/doc/tutorial/source/getting-started.rst +++ b/doc/tutorial/source/getting-started.rst @@ -1,5 +1,5 @@ .. include:: replace.txt -.. highlight:: bash +.. highlight:: console .. _Getting Started: @@ -96,7 +96,7 @@ prerequisites. For example, do not use a directory path such as the below, because one of the parent directories contains a space in the directory name: -.. sourcecode:: bash +.. sourcecode:: console $ pwd /home/user/5G simulations/ns-3-allinone/ns-3-dev @@ -119,7 +119,7 @@ If you adopt the ``workspace`` directory approach, you can get a copy of a release by typing the following into your Linux shell (substitute the appropriate version numbers, of course) -.. sourcecode:: bash +.. sourcecode:: console $ cd $ mkdir workspace @@ -159,7 +159,7 @@ may be foreign to you; if so, we recommend that you simply ``clone`` (create your own replica) of the repository found on GitLab.com, as follows: -.. sourcecode:: bash +.. sourcecode:: console $ cd $ mkdir workspace @@ -171,7 +171,7 @@ At this point, your view of the ns-3-allinone directory is slightly different than described above with a release archive; it should look something like this: -.. sourcecode:: bash +.. sourcecode:: console $ ls build.py constants.py download.py README util.py @@ -180,14 +180,14 @@ Note the presence of the ``download.py`` script, which will further fetch the |ns3| and related sourcecode. At this point, you have a choice, to either download the most recent development snapshot of |ns3|: -.. sourcecode:: bash +.. sourcecode:: console $ python3 download.py or to specify a release of |ns3|, using the ``-n`` flag to specify a release number: -.. sourcecode:: bash +.. sourcecode:: console $ python3 download.py -n ns-3.35 @@ -204,7 +204,7 @@ for network animiations). The third repository provided by default in ns-3-allinone is called ``bake``. Bake is a tool for coordinated software building from multiple repositories, -developed for the |ns3| project.  Bake can be used to fetch development +developed for the |ns3| project. Bake can be used to fetch development versions of the |ns3| software, and to download and build extensions to the base |ns3| distribution, such as the Direct Code Execution environment, Network Simulation Cradle, ability to create new Python bindings, and @@ -232,7 +232,7 @@ following into your Linux shell (assuming you have installed Git):: As the git command executes, you should see something like the following displayed: -.. sourcecode:: bash +.. sourcecode:: console Cloning into 'bake'... remote: Enumerating objects: 2086, done. @@ -245,7 +245,7 @@ following displayed: After the clone command completes, you should have a directory called ``bake``, the contents of which should look something like the following: -.. sourcecode:: bash +.. sourcecode:: console $ cd bake $ ls @@ -291,7 +291,7 @@ to put bake into your path, such as follows (Linux bash shell example). First, change into the 'bake' directory, and then set the following environment variables: -.. sourcecode:: bash +.. sourcecode:: console $ export BAKE_HOME=`pwd` $ export PATH=$PATH:$BAKE_HOME:$BAKE_HOME/build/bin @@ -304,14 +304,14 @@ full builds of ns-3-allinone (with the optional packages) typically do. Step into the workspace directory and type the following into your shell: -.. sourcecode:: bash +.. sourcecode:: console $ ./bake.py configure -e ns-3.35 Next, we'll ask bake to check whether we have enough tools to download various components. Type: -.. sourcecode:: bash +.. sourcecode:: console $ ./bake.py check @@ -338,7 +338,7 @@ administrator as needed to install these tools. You can also Next, try to download the software: -.. sourcecode:: bash +.. sourcecode:: console $ ./bake.py download @@ -367,7 +367,7 @@ should yield something like: The above suggests that three sources have been downloaded. Check the ``source`` directory now and type ``ls``; one should see: -.. sourcecode:: bash +.. sourcecode:: console $ cd source $ ls @@ -404,7 +404,7 @@ using a tarball you should have a directory called something like ``ns-allinone-3.35`` under your ``~/workspace`` directory. Type the following: -.. sourcecode:: bash +.. sourcecode:: console $ ./build.py --enable-examples --enable-tests @@ -418,42 +418,7 @@ are not necessary for your work, if you wish. You will see lots of compiler output messages displayed as the build script builds the various pieces you downloaded. First, the script will attempt to build the netanim animator, then the pybindgen bindings generator, -and finally |ns3|. Eventually you should see the following:: - - Waf: Leaving directory '/path/to/workspace/ns-allinone-3.35/ns-3.35/build' - 'build' finished successfully (6m25.032s) - - Modules built: - antenna aodv applications - bridge buildings config-store - core csma csma-layout - dsdv dsr energy - fd-net-device flow-monitor internet - internet-apps lr-wpan lte - mesh mobility mpi - netanim (no Python) network nix-vector-routing - olsr point-to-point point-to-point-layout - propagation sixlowpan spectrum - stats tap-bridge test (no Python) - topology-read traffic-control uan - virtual-net-device visualizer wave - wifi wimax - - Modules not built (see ns-3 tutorial for explanation): - brite click openflow - - Leaving directory ./ns-3.35 - -Regarding the portion about modules not built:: - - Modules not built (see ns-3 tutorial for explanation): - brite click - -This just means that some |ns3| modules that have dependencies on -outside libraries may not have been built, or that the configuration -specifically asked not to build them. It does not mean that the -simulator did not build successfully or that it will provide wrong -results for the modules listed as being built. +and finally |ns3|. Building with bake ++++++++++++++++++ @@ -461,7 +426,7 @@ Building with bake If you used bake above to fetch source code from project repositories, you may continue to use it to build |ns3|. Type: -.. sourcecode:: bash +.. sourcecode:: console $ ./bake.py build @@ -492,7 +457,7 @@ for now. If there happens to be a failure, please have a look at what the following command tells you; it may give a hint as to a missing dependency: -.. sourcecode:: bash +.. sourcecode:: console $ ./bake.py show @@ -523,7 +488,7 @@ wrapper script for CMake, |ns3|. To tell |ns3| that it should do optimized builds that include the examples and tests, you will need to execute the following commands: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 clean $ ./ns3 configure --build-profile=optimized --enable-examples --enable-tests @@ -537,109 +502,192 @@ When the project is reconfigured and the build system checks for various dependencies, you should see output that looks similar to the following:: - Setting top to : /home/ns3user/workspace/bake/source/ns-3-dev - Setting out to : /home/ns3user/workspace/bake/source/ns-3-dev/build - Checking for 'gcc' (C compiler) : /usr/bin/gcc - Checking for cc version : 7.3.0 - Checking for 'g++' (C++ compiler) : /usr/bin/g++ - Checking for compilation flag -march=native support : ok - Checking for compilation flag -Wl,--soname=foo support : ok - Checking for compilation flag -std=c++11 support : ok - Checking boost includes : headers not found, please provide a --boost-includes argument (see help) - Checking boost includes : headers not found, please provide a --boost-includes argument (see help) - Checking for program 'python' : /usr/bin/python - Checking for python version >= 2.3 : 2.7.15 - python-config : /usr/bin/python-config - Asking python-config for pyembed '--cflags --libs --ldflags' flags : yes - Testing pyembed configuration : yes - Asking python-config for pyext '--cflags --libs --ldflags' flags : yes - Testing pyext configuration : yes - Checking for compilation flag -fvisibility=hidden support : ok - Checking for compilation flag -Wno-array-bounds support : ok - Checking for pybindgen location : ../pybindgen (guessed) - Checking for python module 'pybindgen' : 0.21.0 - Checking for pybindgen version : 0.21.0 - Checking for code snippet : yes - Checking for types uint64_t and unsigned long equivalence : no - Checking for code snippet : no - Checking for types uint64_t and unsigned long long equivalence : yes - Checking for the apidefs that can be used for Python bindings : gcc-LP64 - Checking for internal GCC cxxabi : complete - Checking for python module 'pygccxml' : not found - Checking for click location : not found - Checking for program 'pkg-config' : /usr/bin/pkg-config - Checking for 'gtk+-3.0' : not found - Checking for 'libxml-2.0' : yes - checking for uint128_t : not found - checking for __uint128_t : yes - Checking high precision implementation : 128-bit integer (default) - Checking for header stdint.h : yes - Checking for header inttypes.h : yes - Checking for header sys/inttypes.h : not found - Checking for header sys/types.h : yes - Checking for header sys/stat.h : yes - Checking for header dirent.h : yes - Checking for header stdlib.h : yes - Checking for header signal.h : yes - Checking for header pthread.h : yes - Checking for header stdint.h : yes - Checking for header inttypes.h : yes - Checking for header sys/inttypes.h : not found - Checking for library rt : yes - Checking for header sys/ioctl.h : yes - Checking for header net/if.h : yes - Checking for header net/ethernet.h : yes - Checking for header linux/if_tun.h : yes - Checking for header netpacket/packet.h : yes - Checking for 'sqlite3' : not found - Checking for header linux/if_tun.h : yes - Checking for python module 'gi' : 3.26.1 - Checking for python module 'gi.repository.GObject' : ok - Checking for python module 'cairo' : ok - Checking for python module 'pygraphviz' : 1.4rc1 - Checking for python module 'gi.repository.Gtk' : ok - Checking for python module 'gi.repository.Gdk' : ok - Checking for python module 'gi.repository.Pango' : ok - Checking for python module 'gi.repository.GooCanvas' : ok - Checking for program 'sudo' : /usr/bin/sudo - Checking for program 'valgrind' : not found - Checking for 'gsl' : not found - python-config : not found - Checking for compilation flag -fstrict-aliasing support : ok - Checking for compilation flag -fstrict-aliasing support : ok - Checking for compilation flag -Wstrict-aliasing support : ok - Checking for compilation flag -Wstrict-aliasing support : ok - Checking for program 'doxygen' : /usr/bin/doxygen - ---- Summary of optional NS-3 features: +.. sourcecode:: console + + -- CCache is enabled. Precompiled headers are disabled by default. + -- The CXX compiler identification is GNU 11.2.0 + -- The C compiler identification is GNU 11.2.0 + -- Detecting CXX compiler ABI info + -- Detecting CXX compiler ABI info - done + -- Check for working CXX compiler: /usr/bin/c++ - skipped + -- Detecting CXX compile features + -- Detecting CXX compile features - done + -- Detecting C compiler ABI info + -- Detecting C compiler ABI info - done + -- Check for working C compiler: /usr/bin/cc - skipped + -- Detecting C compile features + -- Detecting C compile features - done + -- Using default output directory /mnt/dev/tools/source/ns-3-dev/build + -- Found GTK3_GTK: /usr/lib/x86_64-linux-gnu/libgtk-3.so + -- GTK3 was found. + -- LibXML2 was found. + -- LibRT was found. + -- Visualizer requires Python bindings + -- Found Boost: /usr/lib/x86_64-linux-gnu/cmake/Boost-1.74.0/BoostConfig.cmake (found version "1.74.0") + -- Found PkgConfig: /usr/bin/pkg-config (found version "0.29.2") + -- GSL was found. + -- Found Sphinx: /usr/bin/sphinx-build + -- Looking for sys/types.h + -- Looking for sys/types.h - found + -- Looking for stdint.h + -- Looking for stdint.h - found + -- Looking for stddef.h + -- Looking for stddef.h - found + -- Check size of long long + -- Check size of long long - done + -- Check size of int128_t + -- Check size of int128_t - failed + -- Check size of __int128_t + -- Check size of __int128_t - done + -- Performing Test has_hash___int128_t + -- Performing Test has_hash___int128_t - Success + -- Check size of unsigned long long + -- Check size of unsigned long long - done + -- Check size of uint128_t + -- Check size of uint128_t - failed + -- Check size of __uint128_t + -- Check size of __uint128_t - done + -- Performing Test has_hash___uint128_t + -- Performing Test has_hash___uint128_t - Success + -- Looking for C++ include inttypes.h + -- Looking for C++ include inttypes.h - found + -- Looking for C++ include stat.h + -- Looking for C++ include stat.h - not found + -- Looking for C++ include dirent.h + -- Looking for C++ include dirent.h - found + -- Looking for C++ include stdlib.h + -- Looking for C++ include stdlib.h - found + -- Looking for C++ include signal.h + -- Looking for C++ include signal.h - found + -- Looking for C++ include netpacket/packet.h + -- Looking for C++ include netpacket/packet.h - found + -- Looking for C++ include semaphore.h + -- Looking for C++ include semaphore.h - found + -- Looking for getenv + -- Looking for getenv - found + -- Processing src/antenna + -- Processing src/aodv + -- Processing src/applications + -- Processing src/bridge + -- Processing src/brite + -- Brite was not found + -- Processing src/buildings + -- Processing src/click + -- Click was not found + -- Processing src/config-store + -- Processing src/core + -- Looking for C++ include boost/units/quantity.hpp + -- Looking for C++ include boost/units/quantity.hpp - found + -- Looking for C++ include boost/units/systems/si.hpp + -- Looking for C++ include boost/units/systems/si.hpp - found + -- Boost Units have been found. + -- Processing src/csma + -- Processing src/csma-layout + -- Processing src/dsdv + -- Processing src/dsr + -- Processing src/energy + -- Processing src/fd-net-device + -- Looking for C++ include net/ethernet.h + -- Looking for C++ include net/ethernet.h - found + -- Looking for C++ include netpacket/packet.h + -- Looking for C++ include netpacket/packet.h - found + -- Looking for C++ include net/if.h + -- Looking for C++ include net/if.h - found + -- Looking for C++ include linux/if_tun.h + -- Looking for C++ include linux/if_tun.h - found + -- Looking for C++ include net/netmap_user.h + -- Looking for C++ include net/netmap_user.h - not found + -- Looking for C++ include sys/ioctl.h + -- Looking for C++ include sys/ioctl.h - found + -- Checking for module 'libdpdk' + -- No package 'libdpdk' found + -- Processing src/flow-monitor + -- Processing src/internet + -- Processing src/internet-apps + -- Processing src/lr-wpan + -- Processing src/lte + -- Processing src/mesh + -- Processing src/mobility + -- Processing src/netanim + -- Processing src/network + -- Processing src/nix-vector-routing + -- Processing src/olsr + -- Processing src/openflow + -- Openflow was not found + -- Processing src/point-to-point + -- Processing src/point-to-point-layout + -- Processing src/propagation + -- Processing src/sixlowpan + -- Processing src/spectrum + -- Processing src/stats + -- Processing src/tap-bridge + -- Processing src/test + -- Processing src/topology-read + -- Processing src/traffic-control + -- Processing src/uan + -- Processing src/virtual-net-device + -- Processing src/wave + -- Processing src/wifi + -- Processing src/wimax + -- ---- Summary of optional NS-3 features: Build profile : optimized - Build directory : - BRITE Integration : not enabled (BRITE not enabled (see option --with-brite)) - DES Metrics event collection : not enabled (defaults to disabled) - Emulation FdNetDevice : enabled - Examples : enabled - File descriptor NetDevice : enabled - GNU Scientific Library (GSL) : not enabled (GSL not found) - GtkConfigStore : not enabled (library 'gtk+-3.0 >= 3.0' not found) - MPI Support : not enabled (option --enable-mpi not selected) - NS-3 Click Integration : not enabled (nsclick not enabled (see option --with-nsclick)) - NS-3 OpenFlow Integration : not enabled (Required boost libraries not found) - PyViz visualizer : enabled - Python API Scanning Support : not enabled (Missing 'pygccxml' Python module) - Python Bindings : enabled - Real Time Simulator : enabled - SQlite stats data output : not enabled (library 'sqlite3' not found) - Tap Bridge : enabled - Tap FdNetDevice : enabled - Tests : enabled - Threading Primitives : enabled - Use sudo to set suid bit : not enabled (option --enable-sudo not selected) - XmlIo : enabled - 'configure' finished successfully (6.387s) + Build directory : /mnt/dev/tools/source/ns-3-dev/build + BRITE Integration : OFF (missing dependency) + DES Metrics event collection : OFF (not requested) + DPDK NetDevice : OFF (missing dependency) + Emulation FdNetDevice : ON + Examples : ON + File descriptor NetDevice : ON + GNU Scientific Library (GSL) : ON + GtkConfigStore : ON + MPI Support : OFF (not requested) + NS-3 Click Integration : OFF (missing dependency) + NS-3 OpenFlow Integration : OFF (missing dependency) + Netmap emulation FdNetDevice : OFF (missing dependency) + PyViz visualizer : OFF (missing dependency) + Python Bindings : OFF (not requested) + Real Time Simulator : ON + SQLite stats support : ON + Tap Bridge : ON + Tap FdNetDevice : ON + Tests : ON + Threading Primitives : ON + + + Modules configured to be built: + antenna aodv applications + bridge buildings config-store + core csma csma-layout + dsdv dsr energy + fd-net-device flow-monitor internet + internet-apps lr-wpan lte + mesh mobility netanim + network nix-vector-routing olsr + point-to-point point-to-point-layout propagation + sixlowpan spectrum stats + tap-bridge test topology-read + traffic-control uan virtual-net-device + wave wifi wimax + + + Modules that cannot be built: + brite click mpi + openflow visualizer + + + -- Configuring done + -- Generating done + -- Build files have been written to: /mnt/dev/tools/source/ns-3-dev/cmake_cache + Finished executing the following commands: + mkdir cmake_cache + cd cmake_cache; /usr/bin/cmake -DCMAKE_BUILD_TYPE=release -DNS3_NATIVE_OPTIMIZATIONS=ON -DNS3_EXAMPLES=ON -DNS3_TESTS=ON -G Unix Makefiles .. ; cd .. + Note the last part of the above output. Some |ns3| options are not enabled by -default or require support from the underlying system to work properly. -For instance, to enable XmlTo, the library libxml-2.0 must be found on the -system. If this library were not found, the corresponding |ns3| feature +default or require support from the underlying system to work properly (``OFF (not requested)``). +Other options might depend on third-party libraries, which if not found will be disabled +(``OFF(missing dependency)``). +If this library were not found, the corresponding |ns3| feature would not be enabled and a message would be displayed. Note further that there is a feature to use the program ``sudo`` to set the suid bit of certain programs. This is not enabled by default and so this feature is reported as "not enabled." @@ -648,7 +696,7 @@ the ``--check-config`` option to ns3. Now go ahead and switch back to the debug build that includes the examples and tests. -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 clean $ ./ns3 configure --build-profile=debug --enable-examples --enable-tests @@ -656,7 +704,7 @@ Now go ahead and switch back to the debug build that includes the examples and t The build system is now configured and you can build the debug versions of the |ns3| programs by simply typing: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 build @@ -666,30 +714,29 @@ now you know how to change the configuration and build optimized code. A command exists for checking which profile is currently active for an already configured project: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 --check-profile - Waf: Entering directory \`/path/to/ns-allinone-3.35/ns-3.35/build\' Build profile: debug The build.py script discussed above supports also the ``--enable-examples`` and ``enable-tests`` arguments, but in general, does not directly support other ns3 options; for example, this will not work: -.. sourcecode:: bash +.. sourcecode:: console $ ./build.py --disable-python will result in: -.. sourcecode:: bash +.. sourcecode:: console build.py: error: no such option: --disable-python However, the special operator ``--`` can be used to pass additional options through to ns3, so instead of the above, the following will work: -.. sourcecode:: bash +.. sourcecode:: console $ ./build.py -- --disable-python @@ -720,7 +767,7 @@ around these issues. The option disables the inclusion of the '-Werror' flag to g++ and clang++. The option is '--disable-werror' and must be used at configure time; e.g.: -.. sourcecode:: bash +.. sourcecode:: console ./ns3 configure --disable-werror --enable-examples --enable-tests @@ -733,7 +780,7 @@ features of |ns3|, you might want to enable setting the suid bit using sudo as described above. This turns out to be a configuration-time command, and so you could reconfigure using the following command that also includes the examples and tests. -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 configure --enable-sudo --enable-examples --enable-tests @@ -743,7 +790,7 @@ emulation code to run as root. There are many other configure- and build-time options available in ns3. To explore these options, type: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 --help @@ -754,7 +801,7 @@ Build Profiles We already saw how you can configure CMake for ``debug`` or ``optimized`` builds: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 configure --build-profile=debug @@ -782,6 +829,7 @@ The build profile controls the use of logging, assertions, and compiler optimiza | Compiler | ``-O0 -ggdb -g3`` | ``-O3 -g0`` | ``-O3 -g`` | | Flags | | ``-fomit-frame-pointer`` | ``-fstrict-overflow`` | | | | | ``-march=native`` | + | | | | ``-mtune=native`` | +----------+---------------------------------+-------------------------------+---------------------------------+ As you can see, logging and assertions are only configured @@ -805,14 +853,14 @@ By default ns3 puts the build artifacts in the ``build`` directory. You can specify a different output directory with the ``--out`` option, e.g. -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 configure --out=my-build-dir Combining this with build profiles lets you switch between the different compile options in a clean way: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 configure --build-profile=debug --out=build/debug $ ./ns3 build @@ -829,7 +877,7 @@ When you do switch build profiles like this, you have to be careful to give the same configuration parameters each time. It may be convenient to define some environment variables to help you avoid mistakes: -.. sourcecode:: bash +.. sourcecode:: console $ export NS3CONFIG="--enable-examples --enable-tests" $ export NS3DEBUG="--build-profile=debug --out=build/debug" @@ -849,7 +897,7 @@ building |ns3|. However, it's possible to change the C++ compiler used by CMake by defining the ``CXX`` environment variable. For example, to use the Clang C++ compiler, ``clang++``, -.. sourcecode:: bash +.. sourcecode:: console $ CXX="clang++" ./ns3 configure $ ./ns3 build @@ -857,7 +905,7 @@ For example, to use the Clang C++ compiler, ``clang++``, One can also set up ns3 to do distributed compilation with ``distcc`` in a similar way: -.. sourcecode:: bash +.. sourcecode:: console $ CXX="distcc g++" ./ns3 configure $ ./ns3 build @@ -916,7 +964,7 @@ As you work, you may find yourself spending a lot of time in ``scratch/``, or deep in ``src/...``, and needing to invoke ns3. You could just remember where you are, and invoke ns3 like this: -.. sourcecode:: bash +.. sourcecode:: console $ ../../../ns3 ... @@ -928,7 +976,7 @@ edit source code. If you only have the tarball, an environment variable can help: -.. sourcecode:: bash +.. sourcecode:: console $ export NS3DIR="$PWD" $ function ns3f { cd $NS3DIR && ./ns3 $* ; } @@ -955,13 +1003,13 @@ Here is are a few examples showing why we suggest the use of the ns3 wrapper scr Configuration command ===================== -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 configure --enable-tests --enable-examples -d optimized Corresponds to -.. sourcecode:: bash +.. sourcecode:: console $ cd /ns-3-dev/cmake_cache/ $ cmake -DCMAKE_BUILD_TYPE=release -DNS3_NATIVE_OPTIMIZATIONS=ON -DNS3_ASSERT=OFF -DNS3_LOG=OFF -DNS3_TESTS=ON -DNS3_EXAMPLES=ON .. @@ -971,13 +1019,13 @@ Build command To build a specific target such as ``test-runner`` we use the following ns3 command: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 build test-runner Which corresponds to the following commands: -.. sourcecode:: bash +.. sourcecode:: console $ cd /ns-3-dev/cmake_cache/ $ cmake --build . -j 16 --target test-runner # This command builds the test-runner target with the underlying build system @@ -985,13 +1033,13 @@ Which corresponds to the following commands: To build all targets such as modules, examples and tests, we use the following ns3 command: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 build Which corresponds to: -.. sourcecode:: bash +.. sourcecode:: console $ cd /ns-3-dev/cmake_cache/ $ cmake --build . -j 16 # This command builds all the targets with the underlying build system @@ -999,13 +1047,13 @@ Which corresponds to: Run command =========== -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 run test-runner Corresponds to: -.. sourcecode:: bash +.. sourcecode:: console $ cd /ns-3-dev/cmake_cache/ $ cmake --build . -j 16 --target test-runner # This command builds the test-runner target calling the underlying build system @@ -1107,7 +1155,7 @@ Code::Blocks does not support CMake project natively, but we can use the corresp generator to generate a project in order to use it. The generator name depends on the operating system and underlying build system. https://cmake.org/cmake/help/latest/generator/CodeBlocks.html -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 configure -G"CodeBlocks - Ninja" --enable-examples @@ -1163,7 +1211,7 @@ XCode does not support CMake project natively, but we can use the corresponding generator to generate a project in order to use it. The generator name depends on the operating system and underlying build system. https://cmake.org/cmake/help/latest/generator/Xcode.html -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 configure -GXcode --enable-examples @@ -1212,9 +1260,9 @@ Testing ns-3 You can run the unit tests of the |ns3| distribution by running the ``./test.py`` script: -.. sourcecode:: bash +.. sourcecode:: console - $ ./test.py + $ ./test.py --no-build These tests are run in parallel by ns3. You should eventually see a report saying that @@ -1230,7 +1278,7 @@ tools and the code. You will also see the summary output from ns3 and the test runner executing each test, which will actually look something like: -.. sourcecode:: bash +.. sourcecode:: console -- CCache is enabled -- The CXX compiler identification is GNU 11.2.0 @@ -1285,7 +1333,7 @@ the libraries are available at run time. To run a program, simply use the ``--run`` option in ns3. Let's run the |ns3| equivalent of the ubiquitous hello world program by typing the following: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 run hello-simulator @@ -1312,7 +1360,7 @@ automatically disabled when you compile optimized code -- it is "optimized out." If you don't see the "Hello Simulator" output, type the following: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 configure --build-profile=debug --enable-examples --enable-tests @@ -1320,7 +1368,7 @@ to tell ns3 to build the debug versions of the |ns3| programs that includes the examples and tests. You must still build the actual debug version of the code by typing -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 @@ -1332,7 +1380,7 @@ Program Arguments To feed command line arguments to an |ns3| program use this pattern: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 run --command-template="%s " @@ -1348,7 +1396,7 @@ If you find the above to be syntactically complicated, a simpler variant exists, which is to include the |ns3| program and its arguments enclosed by single quotes, such as: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 run ' --arg1=value1 --arg2=value2 ...' @@ -1358,7 +1406,7 @@ Above, we used the ``./test.py`` script to run a whole slew of tests in parallel, by repeatedly invoking the real testing program, ``test-runner``. To invoke ``test-runner`` directly for a single test: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 run test-runner --command-template="%s --suite=mytest --verbose" @@ -1366,7 +1414,7 @@ This passes the arguments to the ``test-runner`` program. Since ``mytest`` does not exist, an error message will be generated. To print the available ``test-runner`` options: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 run test-runner --command-template="%s --help" @@ -1380,7 +1428,7 @@ you use a similar ``--command-template="..."`` form. For example, to run your |ns3| program ``hello-simulator`` with the arguments ```` under the ``gdb`` debugger: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 run=hello-simulator --command-template="gdb %s --args " @@ -1395,7 +1443,7 @@ and use the ``gdb`` command ``set args``.) We can combine this recipe and the previous one to run a test under the debugger: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 run test-runner --command-template="gdb %s --args --suite=mytest --verbose" @@ -1407,7 +1455,7 @@ This becomes the working directory where output files will be written. But what if you want to keep those files out of the |ns3| source tree? Use the ``--cwd`` argument: -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 run program-name --cwd=... @@ -1424,7 +1472,7 @@ through a shell script, or when demonstrating program execution. The option ``--no-build`` modifies the ``run`` option, skipping the build steps of the program and required ns-3 libraries. -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 run ' --arg1=value1 --arg2=value2 ...' --no-build @@ -1451,7 +1499,7 @@ When these prerequisites are met and ns-3 is configured with the ``--enable-build-version`` option, the ns3 command ``--check-version`` can be used to query the local git repository and display the current version metadata. -.. sourcecode:: bash +.. sourcecode:: console $ ./ns3 --check-version @@ -1548,7 +1596,6 @@ option which will print the full build version and exit. .. sourcecode:: text ./ns3 run "command-line-example --version" --no-build - Waf: Entering directory `/g/g14/mdb/gitlab/mdb/ns-3-dev/build/debug' ns-3.33+249@g80e0dd0-dirty-debug If the ``--enable-build-version`` option was not configured, ``--version`` @@ -1566,7 +1613,7 @@ with a patch of any changes to that revision if the repository is dirty. The resulting text file can then be saved with any corresponding |ns3| simulation results. -.. sourcecode:: bash +.. sourcecode:: console echo `git describe` > version.txt gitDiff=`git diff` diff --git a/utils/create-module.py b/utils/create-module.py index 0b4a0b3bf..5bb45f2d8 100755 --- a/utils/create-module.py +++ b/utils/create-module.py @@ -13,10 +13,7 @@ if(HAVE_STDINT_H) add_definitions(-DHAVE_STDINT_H) endif() -set(libraries_to_link - ${{libcore}} - ) - +set(examples_as_tests_sources) if(${{ENABLE_EXAMPLES}}) set(examples_as_tests_sources #test/{MODULE}-examples-test-suite.cc @@ -29,7 +26,7 @@ build_lib( helper/{MODULE}-helper.cc HEADER_FILES model/{MODULE}.h helper/{MODULE}-helper.h - LIBRARIES_TO_LINK ${{libraries_to_link}} + LIBRARIES_TO_LINK ${{libcore}} TEST_SOURCES test/{MODULE}-test-suite.cc ${{examples_as_tests_sources}} ) @@ -101,11 +98,10 @@ namespace ns3 {{ EXAMPLES_CMAKELISTS_TEMPLATE = '''\ -set(libraries_to_link ${{lib{MODULE}}}) build_lib_example( NAME {MODULE}-example SOURCE_FILES ${{name}}.cc - LIBRARIES_TO_LINK ${{libraries_to_link}} + LIBRARIES_TO_LINK ${{lib{MODULE}}} ) ''' @@ -374,8 +370,7 @@ def make_examples(moduledir, modname): examplespath.mkdir(parents=True) 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) + create_file(cmakelistspath, EXAMPLES_CMAKELISTS_TEMPLATE, MODULE=modname) examplesfile_path = examplespath.joinpath(modname+'-example').with_suffix('.cc') create_file(examplesfile_path, EXAMPLE_CC_TEMPLATE, MODULE=modname) @@ -433,7 +428,7 @@ def create_argument_parser(): 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 +out of the box. ns3 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: @@ -617,7 +612,7 @@ def main(argv): 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") + print("Run './ns3 configure' to include them in the build") return 0