3425 lines
130 KiB
ReStructuredText
3425 lines
130 KiB
ReStructuredText
.. include:: replace.txt
|
|
.. highlight:: console
|
|
|
|
.. Section Separators:
|
|
----
|
|
****
|
|
++++
|
|
====
|
|
~~~~
|
|
|
|
.. _Working with CMake:
|
|
|
|
Working with CMake
|
|
------------------
|
|
|
|
The |ns3| 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 |ns3|, 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 |ns3| 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 : /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: /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 --build-profile`` | CMake | Equivalent GCC compiler flags |
|
|
| +------------------------+-------------------------------+---------------------------------+
|
|
| | CMAKE_BUILD_TYPE | Additional flags | |
|
|
+=========================+========================+===============================+=================================+
|
|
| debug | debug | | -g |
|
|
+-------------------------+------------------------+-------------------------------+---------------------------------+
|
|
| default | default | | -Os -g |
|
|
+-------------------------+------------------------+-------------------------------+---------------------------------+
|
|
| release | release | | -O3 |
|
|
+-------------------------+------------------------+-------------------------------+---------------------------------+
|
|
| optimized | release | -DNS3_NATIVE_OPTIMIZATIONS=ON | -O3 -march=native -mtune=native |
|
|
+-------------------------+------------------------+-------------------------------+---------------------------------+
|
|
| minsizerel | minsizerel | | -Os |
|
|
+-------------------------+------------------------+-------------------------------+---------------------------------+
|
|
|
|
In addition to setting compiler flags each build type also controls whether certain features are enabled or not:
|
|
|
|
+-------------------------+-----------------+-------------+----------------------------+
|
|
| ``ns3 --build-profile`` | ``NS3_ASSERT`` | ``NS3_LOG`` | ``NS3_WARNINGS_AS_ERRORS`` |
|
|
+=========================+=================+=============+============================+
|
|
| debug | ON | ON | ON |
|
|
+-------------------------+-----------------+-------------+----------------------------+
|
|
| default | ON | ON | OFF |
|
|
+-------------------------+-----------------+-------------+----------------------------+
|
|
| release | OFF | OFF | OFF |
|
|
+-------------------------+-----------------+-------------+----------------------------+
|
|
| optimized | OFF | OFF | OFF |
|
|
+-------------------------+-----------------+-------------+----------------------------+
|
|
| minsizerel | OFF | OFF | OFF |
|
|
+-------------------------+-----------------+-------------+----------------------------+
|
|
|
|
``NS3_ASSERT`` and ``NS_LOG`` control whether the assert or logging macros
|
|
are functional or compiled out.
|
|
``NS3_WARNINGS_AS_ERRORS`` controls whether compiler warnings are treated
|
|
as errors and stop the build, or whether they are only warnings and
|
|
allow the build to continue.
|
|
|
|
|
|
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 |
|
|
+------------------+-------------------+
|
|
| DEFAULT | -Os -g -DNDEBUG |
|
|
+------------------+-------------------+
|
|
| RELWITHDEBINFO | -Os -g -DNDEBUG |
|
|
+------------------+-------------------+
|
|
| RELEASE | -O3 -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<Manually 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.
|
|
Since ns-3.43, module libraries CMake targets are named the same as the module name (e.g. core, wifi, lte).
|
|
From ns-3.36 to 3.42, module library targets were prefixed with ``lib`` (e.g. libcore, libwifi, liblte).
|
|
Executables from the scratch folder are prefixed with ``scratch_`` (e.g. scratch_scratch-simulator).
|
|
Executables targets are named the same as their source file containing the `main` function,
|
|
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<Manually refresh 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
|
|
|
|
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 are generated at runtime for all built modules
|
|
if 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
|
|
|
|
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}
|
|
)
|
|
|
|
Migrating definitions, compilation and linking options
|
|
++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
If your Waf modules had additional definitions, compilation or linking flags,
|
|
you also need to translate them to CMake. The easiest way to accomplish that
|
|
is using the CMake counterparts *BEFORE* defining your target.
|
|
|
|
If you, for example, had the following:
|
|
|
|
.. sourcecode:: python
|
|
|
|
conf.env.append_value("CXXFLAGS", ["-fopenmp", "-I/usr/local/include/e2sim"])
|
|
conf.env.append_value("CXXDEFINES", ["LAPACK", "LUSOLVER=LAPACK"])
|
|
conf.env.append_value("LINKFLAGS", ["-llapack", "-L/usr/local/src/GoToBLAS2", "-lblas", "-Lsrc/common", "-lthyme"])
|
|
conf.env.append_value("LIB", ["e2sim"])
|
|
|
|
You would need to replace it with the following counterparts:
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
# The settings below will impact all future target declarations
|
|
# in the current subdirectory and its subdirectories
|
|
#
|
|
# a.k.a. the module, its examples and tests will have the definitions,
|
|
# compilation options and will be linked to the specified libraries
|
|
add_compile_options(-fopenmp) # CXXFLAGS counterpart
|
|
include_directories(/usr/local/include/e2sim) # CXXFLAGS -I counterpart
|
|
add_definitions(-DLAPACK -DLUSOLVER=LAPACK) # CXXDEFINES counterpart
|
|
link_directories(/usr/local/src/GoToBLAS2 src/common) # LINKFLAGS -L counterpart
|
|
link_libraries(lapack blas thyme e2sim) # LINKFLAGS -l or LIB counterpart
|
|
|
|
# Target definition after changing settings
|
|
build_lib_example(
|
|
NAME hypothetical-module
|
|
SOURCE_FILES hypothetical-module-source.cc
|
|
LIBRARIES_TO_LINK
|
|
# depends on wifi, internet, aodv and internet-apps modules
|
|
${libwifi}
|
|
${libinternet}
|
|
${libaodv}
|
|
${libinternet-apps}
|
|
# and lapack, blas, thyme, e2sim external libraries
|
|
)
|
|
|
|
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.
|
|
|
|
.. _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 omitted in ``ns3`` for simplicity.
|
|
|
|
Debugging can be done with GDB. Again, we have the two ways to run the program.
|
|
Using the ``ns3`` 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 |ns3| 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<Manually refresh the CMake cache>`.
|
|
|
|
|
|
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 |ns3| modules
|
|
+++++++++++++++++++++
|
|
|
|
Adding a dependency to another |ns3| module just requires adding ``${lib${modulename}}``
|
|
to the ``LIBRARIES_TO_LINK`` list, where modulename contains the value of the |ns3|
|
|
module which will be depended upon.
|
|
|
|
Note: All |ns3| 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.
|
|
|
|
Here is a short version on how to find and use third-party libraries
|
|
that should work in most cases:
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
# DEPENDENCY_NAME is used as a prefix to variables set by the find_external_library macro
|
|
# HEADER_NAME(S) is(are) the name(s) of the header(s) you want to include
|
|
# LIBRARY_NAME(S) is(are) the name(s) of the library(ies) you want to link
|
|
# SEARCH_PATHS are the custom paths you can give if your library is not on a system path
|
|
find_external_library(DEPENDENCY_NAME SQLite3
|
|
HEADER_NAME sqlite3.h
|
|
LIBRARY_NAME sqlite3
|
|
SEARCH_PATHS /optional/search/path/to/custom/sqlite3/library)
|
|
|
|
# If the header(s) and library(ies) are not found, a message will be printed during the configuration
|
|
# If the header(s) and the library(ies) are found, we can use the information found by the buildsystem
|
|
if(${SQLite3_FOUND}) # Notice that the contents of DEPENDENCY_NAME became a prefix for the _FOUND variable
|
|
# The compiler will not be able to find the include that is not on
|
|
# a system include path, unless we explicitly inform it
|
|
|
|
# This is the equivalent of -I/optional/search/path/to/custom/sqlite3/include
|
|
# and AFFECTS ALL the targets in the CURRENT DIRECTORY and ITS SUBDIRECTORIES
|
|
include_directories(${SQLite3_INCLUDE_DIRS})
|
|
|
|
# The compiler should be able to locate the headers, but it still needs to be
|
|
# informed of the libraries that should be linked
|
|
|
|
# This is the equivalent of -l/optional/search/path/to/custom/sqlite3/library/libsqlite3.so
|
|
# and AFFECTS ALL the targets in the CURRENT DIRECTORY and ITS SUBDIRECTORIES
|
|
link_libraries(${SQLite3_LIBRARIES})
|
|
endif()
|
|
|
|
If you do not want to link the library against all the targets
|
|
(executables and other libraries) use one of the following patterns.
|
|
|
|
**If the third-party library is required**
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
# if the third-party library is required
|
|
if(${SQLite3_FOUND})
|
|
# define your target
|
|
build_lib(
|
|
LIBNAME example
|
|
LIBRARIES_TO_LINK ${SQLite3_LIBRARIES}
|
|
...
|
|
)
|
|
|
|
# The LIBRARIES_TO_LINK will be translated into CMake's
|
|
# target_link_libraries(${libexample} PUBLIC ${SQLite3_LIBRARIES})
|
|
# which is equivalent to -l${SQLite3_LIBRARIES}
|
|
endif()
|
|
|
|
**If the third-party library is optional**
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
set(sqlite_libraries)
|
|
if(${SQLite3_FOUND})
|
|
set(sqlite_libraries ${SQLite3_LIBRARIES})
|
|
endif()
|
|
|
|
# And then define your target
|
|
build_lib(
|
|
LIBNAME example
|
|
LIBRARIES_TO_LINK ${sqlite_libraries} # variable can be empty
|
|
...
|
|
)
|
|
|
|
More details on how find_external_library works and the other
|
|
ways to import third-party libraries are presented next.
|
|
|
|
It is recommended to use a system package managers to install libraries,
|
|
but ns-3 also supports vcpkg and CPM. More information on how to
|
|
use them is available in `Using C++ library managers`_.
|
|
|
|
|
|
.. _Linking third-party libraries without CMake or PkgConfig support:
|
|
|
|
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``.
|
|
|
|
Here is how it works:
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
function(find_external_library)
|
|
# Parse arguments
|
|
set(options QUIET)
|
|
set(oneValueArgs DEPENDENCY_NAME HEADER_NAME LIBRARY_NAME)
|
|
set(multiValueArgs HEADER_NAMES LIBRARY_NAMES PATH_SUFFIXES SEARCH_PATHS)
|
|
cmake_parse_arguments(
|
|
"FIND_LIB" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}
|
|
)
|
|
|
|
# Set the external package/dependency name
|
|
set(name ${FIND_LIB_DEPENDENCY_NAME})
|
|
|
|
# We process individual and list of headers and libraries by transforming them
|
|
# into lists
|
|
set(library_names "${FIND_LIB_LIBRARY_NAME};${FIND_LIB_LIBRARY_NAMES}")
|
|
set(header_names "${FIND_LIB_HEADER_NAME};${FIND_LIB_HEADER_NAMES}")
|
|
|
|
# Just changing the parsed argument name back to something shorter
|
|
set(search_paths ${FIND_LIB_SEARCH_PATHS})
|
|
set(path_suffixes "${FIND_LIB_PATH_SUFFIXES}")
|
|
|
|
set(not_found_libraries)
|
|
set(library_dirs)
|
|
set(libraries)
|
|
# Paths and suffixes where libraries will be searched on
|
|
set(library_search_paths
|
|
${search_paths}
|
|
${CMAKE_OUTPUT_DIRECTORY} # Search for libraries in ns-3-dev/build
|
|
${CMAKE_INSTALL_PREFIX} # Search for libraries in the install directory (e.g. /usr/)
|
|
$ENV{LD_LIBRARY_PATH} # Search for libraries in LD_LIBRARY_PATH directories
|
|
$ENV{PATH} # Search for libraries in PATH directories
|
|
)
|
|
set(suffixes /build /lib /build/lib / /bin ${path_suffixes})
|
|
|
|
# For each of the library names in LIBRARY_NAMES or LIBRARY_NAME
|
|
foreach(library ${library_names})
|
|
# We mark this value is advanced not to pollute the configuration with
|
|
# ccmake with the cache variables used internally
|
|
mark_as_advanced(${name}_library_internal_${library})
|
|
|
|
# We search for the library named ${library} and store the results in
|
|
# ${name}_library_internal_${library}
|
|
find_library(
|
|
${name}_library_internal_${library} ${library}
|
|
HINTS ${library_search_paths}
|
|
PATH_SUFFIXES ${suffixes}
|
|
)
|
|
# cmake-format: off
|
|
# Note: the PATH_SUFFIXES above apply to *ALL* PATHS and HINTS Which
|
|
# translates to CMake searching on standard library directories
|
|
# CMAKE_SYSTEM_PREFIX_PATH, user-settable CMAKE_PREFIX_PATH or
|
|
# CMAKE_LIBRARY_PATH and the directories listed above
|
|
#
|
|
# e.g. from Ubuntu 22.04 CMAKE_SYSTEM_PREFIX_PATH =
|
|
# /usr/local;/usr;/;/usr/local;/usr/X11R6;/usr/pkg;/opt
|
|
#
|
|
# Searched directories without suffixes
|
|
#
|
|
# ${CMAKE_SYSTEM_PREFIX_PATH}[0] = /usr/local/
|
|
# ${CMAKE_SYSTEM_PREFIX_PATH}[1] = /usr
|
|
# ${CMAKE_SYSTEM_PREFIX_PATH}[2] = /
|
|
# ...
|
|
# ${CMAKE_SYSTEM_PREFIX_PATH}[6] = /opt
|
|
# ${LD_LIBRARY_PATH}[0]
|
|
# ...
|
|
# ${LD_LIBRARY_PATH}[m]
|
|
# ...
|
|
#
|
|
# Searched directories with suffixes include all of the directories above
|
|
# plus all suffixes
|
|
# PATH_SUFFIXES /build /lib /build/lib / /bin # ${path_suffixes}
|
|
#
|
|
# /usr/local/build
|
|
# /usr/local/lib
|
|
# /usr/local/build/lib
|
|
# /usr/local/bin
|
|
# ...
|
|
#
|
|
# cmake-format: on
|
|
# Or enable NS3_VERBOSE to print the searched paths
|
|
|
|
# Print tested paths to the searched library and if it was found
|
|
if(${NS3_VERBOSE})
|
|
log_find_searched_paths(
|
|
TARGET_TYPE Library
|
|
TARGET_NAME ${library}
|
|
SEARCH_RESULT ${name}_library_internal_${library}
|
|
SEARCH_PATHS ${library_search_paths}
|
|
SEARCH_SUFFIXES ${suffixes}
|
|
)
|
|
endif()
|
|
|
|
# After searching the library, the internal variable should have either the
|
|
# absolute path to the library or the name of the variable appended with
|
|
# -NOTFOUND
|
|
if("${${name}_library_internal_${library}}" STREQUAL
|
|
"${name}_library_internal_${library}-NOTFOUND"
|
|
)
|
|
# We keep track of libraries that were not found
|
|
list(APPEND not_found_libraries ${library})
|
|
else()
|
|
# We get the name of the parent directory of the library and append the
|
|
# library to a list of found libraries
|
|
get_filename_component(
|
|
${name}_library_dir_internal ${${name}_library_internal_${library}}
|
|
DIRECTORY
|
|
) # e.g. lib/openflow.(so|dll|dylib|a) -> lib
|
|
list(APPEND library_dirs ${${name}_library_dir_internal})
|
|
list(APPEND libraries ${${name}_library_internal_${library}})
|
|
endif()
|
|
endforeach()
|
|
|
|
# For each library that was found (e.g. /usr/lib/pthread.so), get their parent
|
|
# directory (/usr/lib) and its parent (/usr)
|
|
set(parent_dirs)
|
|
foreach(libdir ${library_dirs})
|
|
get_filename_component(parent_libdir ${libdir} DIRECTORY)
|
|
get_filename_component(parent_parent_libdir ${parent_libdir} DIRECTORY)
|
|
list(APPEND parent_dirs ${libdir} ${parent_libdir} ${parent_parent_libdir})
|
|
endforeach()
|
|
|
|
# If we already found a library somewhere, limit the search paths for the header
|
|
if(parent_dirs)
|
|
set(header_search_paths ${parent_dirs})
|
|
set(header_skip_system_prefix NO_CMAKE_SYSTEM_PATH)
|
|
else()
|
|
set(header_search_paths
|
|
${search_paths}
|
|
${CMAKE_OUTPUT_DIRECTORY} # Search for headers in ns-3-dev/build
|
|
${CMAKE_INSTALL_PREFIX} # Search for headers in the install
|
|
)
|
|
endif()
|
|
|
|
set(not_found_headers)
|
|
set(include_dirs)
|
|
foreach(header ${header_names})
|
|
# The same way with libraries, we mark the internal variable as advanced not
|
|
# to pollute ccmake configuration with variables used internally
|
|
mark_as_advanced(${name}_header_internal_${header})
|
|
set(suffixes
|
|
/build
|
|
/include
|
|
/build/include
|
|
/build/include/${name}
|
|
/include/${name}
|
|
/${name}
|
|
/
|
|
${path_suffixes}
|
|
)
|
|
# cmake-format: off
|
|
# Here we search for the header file named ${header} and store the result in
|
|
# ${name}_header_internal_${header}
|
|
#
|
|
# The same way we did with libraries, here we search on
|
|
# CMAKE_SYSTEM_PREFIX_PATH, along with user-settable ${search_paths}, the
|
|
# parent directories from the libraries, CMAKE_OUTPUT_DIRECTORY and
|
|
# CMAKE_INSTALL_PREFIX
|
|
#
|
|
# And again, for each of them, for every suffix listed /usr/local/build
|
|
# /usr/local/include
|
|
# /usr/local/build/include
|
|
# /usr/local/build/include/${name}
|
|
# /usr/local/include/${name}
|
|
# ...
|
|
#
|
|
# cmake-format: on
|
|
# Or enable NS3_VERBOSE to get the searched paths printed while configuring
|
|
|
|
find_file(
|
|
${name}_header_internal_${header} ${header}
|
|
HINTS ${header_search_paths} # directory (e.g. /usr/)
|
|
${header_skip_system_prefix}
|
|
PATH_SUFFIXES ${suffixes}
|
|
)
|
|
|
|
# Print tested paths to the searched header and if it was found
|
|
if(${NS3_VERBOSE})
|
|
log_find_searched_paths(
|
|
TARGET_TYPE Header
|
|
TARGET_NAME ${header}
|
|
SEARCH_RESULT ${name}_header_internal_${header}
|
|
SEARCH_PATHS ${header_search_paths}
|
|
SEARCH_SUFFIXES ${suffixes}
|
|
SEARCH_SYSTEM_PREFIX ${header_skip_system_prefix}
|
|
)
|
|
endif()
|
|
|
|
# If the header file was not found, append to the not-found list
|
|
if("${${name}_header_internal_${header}}" STREQUAL
|
|
"${name}_header_internal_${header}-NOTFOUND"
|
|
)
|
|
list(APPEND not_found_headers ${header})
|
|
else()
|
|
# If the header file was found, get their directories and the parent of
|
|
# their directories to add as include directories
|
|
get_filename_component(
|
|
header_include_dir ${${name}_header_internal_${header}} DIRECTORY
|
|
) # e.g. include/click/ (simclick.h) -> #include <simclick.h> should work
|
|
get_filename_component(
|
|
header_include_dir2 ${header_include_dir} DIRECTORY
|
|
) # e.g. include/(click) -> #include <click/simclick.h> should work
|
|
list(APPEND include_dirs ${header_include_dir} ${header_include_dir2})
|
|
endif()
|
|
endforeach()
|
|
|
|
# Remove duplicate include directories
|
|
if(include_dirs)
|
|
list(REMOVE_DUPLICATES include_dirs)
|
|
endif()
|
|
|
|
# If we find both library and header, we export their values
|
|
if((NOT not_found_libraries}) AND (NOT not_found_headers))
|
|
set(${name}_INCLUDE_DIRS "${include_dirs}" PARENT_SCOPE)
|
|
set(${name}_LIBRARIES "${libraries}" PARENT_SCOPE)
|
|
set(${name}_HEADER ${${name}_header_internal} PARENT_SCOPE)
|
|
set(${name}_FOUND TRUE PARENT_SCOPE)
|
|
set(status_message "find_external_library: ${name} was found.")
|
|
else()
|
|
set(${name}_INCLUDE_DIRS PARENT_SCOPE)
|
|
set(${name}_LIBRARIES PARENT_SCOPE)
|
|
set(${name}_HEADER PARENT_SCOPE)
|
|
set(${name}_FOUND FALSE PARENT_SCOPE)
|
|
set(status_message
|
|
"find_external_library: ${name} was not found. Missing headers: \"${not_found_headers}\" and missing libraries: \"${not_found_libraries}\"."
|
|
)
|
|
endif()
|
|
|
|
if(NOT ${FIND_LIB_QUIET})
|
|
message(STATUS "${status_message}")
|
|
endif()
|
|
endfunction()
|
|
|
|
Debugging why a header or a library cannot be found is fairly tricky.
|
|
For ``find_external_library`` users, enabling the ``NS3_VERBOSE`` switch
|
|
will enable the logging of search path directories for both headers and
|
|
libraries.
|
|
|
|
.. _CMAKE_FIND_DEBUG_MODE: https://cmake.org/cmake/help/latest/variable/CMAKE_FIND_DEBUG_MODE.html
|
|
|
|
Note: The logging provided by find_external_library is an alternative to
|
|
CMake's own ``CMAKE_FIND_DEBUG_MODE=true`` introduced in CMake 3.17,
|
|
which gets used by *ALL* ``find_file``, ``find_library``, ``find_header``,
|
|
``find_package`` and ``find_path`` calls throughout CMake and its modules.
|
|
If you are using a recent version of CMake, it is recommended to use
|
|
`CMAKE_FIND_DEBUG_MODE`_ instead.
|
|
|
|
A commented version of the Openflow module ``CMakeLists.txt`` has an
|
|
example of ``find_external_library`` usage.
|
|
|
|
.. 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 <core-module.h> and <ns3/core-module.h>
|
|
# 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} # user-settable search path, empty by default
|
|
)
|
|
|
|
# Before testing if the header and library were found ${openflow_FOUND},
|
|
# test if openflow_FOUND was defined
|
|
# If openflow_FOUND was not defined, the dependency name above doesn't match
|
|
# the tested values below
|
|
# If openflow_FOUND is set to FALSE, stop processing the module by returning
|
|
# to the parent directory with return()
|
|
if((NOT
|
|
openflow_FOUND)
|
|
AND (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
|
|
#
|
|
# This will make the following work:
|
|
# include<openflow/openflow.h>
|
|
# include<openflow.h>
|
|
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${Package}.cmake`` file can be distributed
|
|
by `CMake itself`_, via library/package managers (APT, Pacman,
|
|
`vcpkg`_), or included to the project tree in the build-support/3rd-party directory.
|
|
|
|
.. _CMake itself: https://github.com/Kitware/CMake/tree/master/Modules
|
|
.. _Vcpkg: https://github.com/Microsoft/vcpkg#using-vcpkg-with-cmake
|
|
|
|
When ``find_package(${Package})`` is called, the ``Find${Package}.cmake`` file gets processed,
|
|
and multiple variables are set. There is no hard standard in the name of those variables, nor if
|
|
they should follow the modern CMake usage, where just linking to the library will include
|
|
associated header directories, forward compile flags and so on.
|
|
|
|
We assume the old CMake style is the one being used, which means we need to include the include
|
|
directories provided by the ``Find${Package}.cmake module``, usually exported as a variable
|
|
``${Package}_INCLUDE_DIRS``, and get a list of libraries for that module so that they can be
|
|
added to the list of libraries to link of the |ns3| modules. Libraries are usually exported as
|
|
the variable ``${Package}_LIBRARIES``.
|
|
|
|
As an example for the above, we use the Boost library
|
|
(excerpt from ``macros-and-definitions.cmake`` and ``src/core/CMakeLists.txt``):
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
# https://cmake.org/cmake/help/v3.10/module/FindBoost.html?highlight=module%20find#module:FindBoost
|
|
find_package(Boost)
|
|
|
|
# It is recommended to create either an empty list that is conditionally filled
|
|
# and later included in the LIBRARIES_TO_LINK list unconditionally
|
|
set(boost_libraries)
|
|
|
|
# If Boost is found, Boost_FOUND will be set to true, which we can then test
|
|
if(${Boost_FOUND})
|
|
# This will export Boost include directories to ALL subdirectories
|
|
# of the current CMAKE_CURRENT_SOURCE_DIR
|
|
#
|
|
# If calling this from the top-level directory (ns-3-dev), it will
|
|
# be used by all contrib/src modules, examples, etc
|
|
include_directories(${Boost_INCLUDE_DIRS})
|
|
|
|
# This is a trick for Boost
|
|
# Sometimes you want to check if specific Boost headers are available,
|
|
# but they would not be found if they're not in system include directories
|
|
set(CMAKE_REQUIRED_INCLUDES ${Boost_INCLUDE_DIRS})
|
|
|
|
# We get the list of Boost libraries and save them in the boost_libraries list
|
|
set(boost_libraries ${Boost_LIBRARIES})
|
|
endif()
|
|
|
|
# If Boost was found earlier, we will be able to check if Boost headers are available
|
|
check_include_file_cxx(
|
|
"boost/units/quantity.hpp"
|
|
HAVE_BOOST_UNITS_QUANTITY
|
|
)
|
|
check_include_file_cxx(
|
|
"boost/units/systems/si.hpp"
|
|
HAVE_BOOST_UNITS_SI
|
|
)
|
|
if(${HAVE_BOOST_UNITS_QUANTITY}
|
|
AND ${HAVE_BOOST_UNITS_SI}
|
|
)
|
|
# Activate optional features that rely on Boost
|
|
add_definitions(
|
|
-DHAVE_BOOST
|
|
-DHAVE_BOOST_UNITS
|
|
)
|
|
# In this case, the Boost libraries are header-only,
|
|
# but in case we needed real libraries, we could add
|
|
# boost_libraries to either the auxiliary libraries_to_link list
|
|
# or the build_lib's LIBRARIES_TO_LINK list
|
|
message(STATUS "Boost Units have been found.")
|
|
else()
|
|
message(
|
|
STATUS
|
|
"Boost Units are an optional feature of length.cc."
|
|
)
|
|
endif()
|
|
|
|
|
|
If ``Find${Package}.cmake`` does not exist in your module path, CMake will warn you that is the case.
|
|
If ``${Package_FOUND}`` is set to ``False``, other variables such as the ones related to libraries and
|
|
include directories might not be set, and can result in CMake failures to configure if used.
|
|
|
|
In case the ``Find${Package}.cmake`` you need is not distributed by the upstream CMake project,
|
|
you can create your own and add it to ``build-support/3rd-party``. This directory is included
|
|
to the ``CMAKE_MODULE_PATH`` variable, making it available for calls without needing to include
|
|
the file with the absolute path to it. To add more directories to the ``CMAKE_MODULE_PATH``,
|
|
use the following:
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
# Excerpt from build-support/macros-and-definitions.cmake
|
|
|
|
# Add ns-3 custom modules to the module path
|
|
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/build-support/custom-modules")
|
|
|
|
# Add the 3rd-party modules to the module path
|
|
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/build-support/3rd-party")
|
|
|
|
# Add your new modules directory to the module path
|
|
# (${PROJECT_SOURCE_DIR} is /path/to/ns-3-dev/)
|
|
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/build-support/new-modules")
|
|
|
|
|
|
One of the custom Find files currently shipped by |ns3| is the ``FindGTK3.cmake`` file.
|
|
GTK3 requires Harfbuzz, which has its own ``FindHarfBuzz.cmake`` file. Both of them
|
|
are in the ``build-support/3rd-party`` directory.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
# You don't need to keep adding this, this is just a demonstration
|
|
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/build-support/3rd-party")
|
|
|
|
# If the user-settable NS3_GTK3 is set, look for HarfBuzz and GTK
|
|
if(${NS3_GTK3})
|
|
# Use FindHarfBuzz.cmake to find HarfBuzz
|
|
find_package(HarfBuzz QUIET)
|
|
|
|
# If HarfBuzz is not found
|
|
if(NOT ${HarfBuzz_FOUND})
|
|
message(STATUS "Harfbuzz is required by GTK3 and was not found.")
|
|
else()
|
|
# FindGTK3.cmake does some weird tricks and results in warnings,
|
|
# that we can only suppress this way
|
|
set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE BOOL "")
|
|
|
|
# If HarfBuzz is found, search for GTK
|
|
find_package(GTK3 QUIET)
|
|
|
|
# Remove suppressions needed for quiet operations
|
|
unset(CMAKE_SUPPRESS_DEVELOPER_WARNINGS CACHE)
|
|
|
|
# If GTK3 is not found, inform the user
|
|
if(NOT ${GTK3_FOUND})
|
|
message(STATUS "GTK3 was not found. Continuing without it.")
|
|
else()
|
|
# If an incompatible version is found, set the GTK3_FOUND flag to false,
|
|
# to make sure it won't be used later
|
|
if(${GTK3_VERSION} VERSION_LESS 3.22)
|
|
set(GTK3_FOUND FALSE)
|
|
message(STATUS "GTK3 found with incompatible version ${GTK3_VERSION}")
|
|
else()
|
|
# A compatible GTK3 version was found
|
|
message(STATUS "GTK3 was found.")
|
|
endif()
|
|
endif()
|
|
endif()
|
|
endif()
|
|
|
|
The Stats module can use the same ``find_package`` macro to search for SQLite3.
|
|
|
|
Note: we currently use a custom macro to find Python3 and SQLite3 since
|
|
``FindPython3.cmake`` and ``FindSQLite3.cmake`` were included in CMake 3.12 and 3.14.
|
|
More details on how to use the macro are listed in
|
|
`Linking third-party libraries without CMake or PkgConfig support`_.
|
|
|
|
.. 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
|
|
)
|
|
|
|
.. _Using C++ library managers:
|
|
|
|
Using C++ library managers
|
|
++++++++++++++++++++++++++
|
|
|
|
It is not rare to try using a library that is not available
|
|
on a certain platform or does not have a CMake-friendly
|
|
interface for us to use.
|
|
|
|
.. _CPM: https://github.com/cpm-cmake/CPM.cmake
|
|
|
|
Some C++ package managers are fairly easy to use with CMake,
|
|
such as `Vcpkg`_ and `CPM`_.
|
|
|
|
vcpkg
|
|
=====
|
|
|
|
Vcpkg requires ``git``, ``curl``, ``zip``, ``unzip`` and ``tar``,
|
|
along with the default ns-3 dependencies. The setup downloads
|
|
and builds vcpkg from their Git repository. Telemetry is disabled
|
|
by default.
|
|
|
|
.. sourcecode:: console
|
|
|
|
~$ ./ns3 configure -- -DNS3_VCPKG=ON
|
|
...
|
|
-- vcpkg: setting up support
|
|
Cloning into 'vcpkg'...
|
|
Updating files: 100% (10376/10376), done.
|
|
Downloading vcpkg-glibc...
|
|
vcpkg package management program version 2023-07-19-814b7ec837b59f1c8778f72351c1dd7605983cd2
|
|
...
|
|
|
|
Configuration will finish successfully.
|
|
For example, now we can try using the Armadillo library.
|
|
To do that, we use the following CMake statements:
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
# Check this is not a fluke
|
|
find_package(Armadillo)
|
|
message(STATUS "Armadillo was found? ${ARMADILLO_FOUND}")
|
|
|
|
Reconfigure ns-3 to check if Armadillo is available.
|
|
|
|
.. sourcecode:: console
|
|
|
|
~$ ./ns3 configure
|
|
...
|
|
-- vcpkg: setting up support
|
|
-- vcpkg: folder already exists, skipping git download
|
|
-- vcpkg: already bootstrapped
|
|
...
|
|
-- Could NOT find Armadillo (missing: ARMADILLO_INCLUDE_DIR)
|
|
-- Armadillo was found? FALSE
|
|
|
|
As you can see, no Armadillo found.
|
|
We can now use vcpkg to install it, using the CMake
|
|
function ``add_package(package_name)``. CMake
|
|
will then be able to find the installed package using ``find_package``.
|
|
|
|
Note: some packages may require additional dependencies.
|
|
The Armadillo package requires ``pkg-config`` and a fortran compiler.
|
|
You will be prompted with a CMake error when a missing dependency is found.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
# Install Armadillo and search for it again
|
|
add_package(Armadillo) # Installs Armadillo with vcpkg
|
|
find_package(Armadillo) # Loads vcpkg installation of Armadillo
|
|
message(STATUS "Armadillo was found? ${ARMADILLO_FOUND}")
|
|
|
|
Sadly, we will need to reconfigure ns-3 from the scratch,
|
|
since CMake ``find_package`` caches are problematic.
|
|
Installing the packages can take a while, and it can look like it hanged.
|
|
|
|
.. sourcecode:: console
|
|
|
|
~$ ./ns3 clean
|
|
~$ ./ns3 configure -- -DNS3_VCPKG=ON
|
|
...
|
|
-- vcpkg: setting up support
|
|
-- vcpkg: folder already exists, skipping git download
|
|
-- vcpkg: already bootstrapped
|
|
...
|
|
-- vcpkg: Armadillo will be installed
|
|
-- vcpkg: Armadillo was installed
|
|
-- Armadillo was found? TRUE
|
|
|
|
As shown above, the Armadillo library gets installed by vcpkg
|
|
and it can be found by CMake's ``find_package`` function.
|
|
We can then use it for our targets.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
# Install Armadillo
|
|
add_package(Armadillo) # Installs Armadillo with vcpkg
|
|
find_package(Armadillo) # Loads vcpkg installation of Armadillo
|
|
message(STATUS "Armadillo was found? ${ARMADILLO_FOUND}")
|
|
|
|
# Include and link Armadillo to targets
|
|
include_directories(${ARMADILLO_INCLUDE_DIRS})
|
|
link_libraries(${ARMADILLO_LIBRARIES})
|
|
|
|
.. _vcpkg manifests: https://learn.microsoft.com/en-us/vcpkg/users/manifests
|
|
|
|
An alternative to manually installing packages with ``add_package`` is
|
|
placing all packages into a ``vcpkg.json`` file in the ns-3 main directory.
|
|
This mode is known as the "manifest mode" in the Vcpkg manual.
|
|
Packages there will be automatically installed at the beginning of the configuration.
|
|
More information about the manifest mode can be found in `vcpkg manifests`_ website.
|
|
|
|
Let us see an example of this mode starting with the ``vcpkg.json`` file.
|
|
|
|
.. sourcecode:: json
|
|
|
|
{
|
|
"dependencies": [
|
|
"sqlite3",
|
|
"eigen3",
|
|
"libxml2",
|
|
"gsl",
|
|
"boost-units"
|
|
]
|
|
}
|
|
|
|
These are some of the optional dependencies used by the upstream ns-3 modules.
|
|
When configuring ns-3 with the Vcpkg support, we will see the following.
|
|
|
|
.. sourcecode:: console
|
|
|
|
/ns-3-dev$ ./ns3 clean
|
|
/ns-3-dev$ ./ns3 configure -- -DNS3_VCPKG=ON
|
|
...
|
|
-- vcpkg: setting up support
|
|
Cloning into 'vcpkg'...
|
|
Updating files: 100% (10434/10434), done.
|
|
Downloading vcpkg-glibc...
|
|
vcpkg package management program version 2023-08-02-6d13efa755f9b5e101712d210199e4139b4c29f6
|
|
|
|
See LICENSE.txt for license information.
|
|
-- vcpkg: detected a vcpkg manifest file: /ns-3-dev/vcpkg.json
|
|
A suitable version of cmake was not found (required v3.27.1) Downloading portable cmake 3.27.1...
|
|
Downloading cmake...
|
|
https://github.com/Kitware/CMake/releases/download/v3.27.1/cmake-3.27.1-linux-x86_64.tar.gz->/ns-3-dev/vcpkg/downloads/cmake-3.27.1-linux-x86_64.tar.gz
|
|
Extracting cmake...
|
|
Detecting compiler hash for triplet x64-linux...
|
|
The following packages will be built and installed:
|
|
* boost-array:x64-linux -> 1.82.0#2
|
|
...
|
|
* boost-winapi:x64-linux -> 1.82.0#2
|
|
eigen3:x64-linux -> 3.4.0#2
|
|
gsl:x64-linux -> 2.7.1#3
|
|
* libiconv:x64-linux -> 1.17#1
|
|
* liblzma:x64-linux -> 5.4.3#1
|
|
libxml2[core,iconv,lzma,zlib]:x64-linux -> 2.10.3#1
|
|
sqlite3[core,json1]:x64-linux -> 3.42.0#1
|
|
* vcpkg-cmake:x64-linux -> 2023-05-04
|
|
* vcpkg-cmake-config:x64-linux -> 2022-02-06#1
|
|
* vcpkg-cmake-get-vars:x64-linux -> 2023-03-02
|
|
* zlib:x64-linux -> 1.2.13
|
|
Additional packages (*) will be modified to complete this operation.
|
|
Restored 0 package(s) from /root/.cache/vcpkg/archives in 98.7 us. Use --debug to see more details.
|
|
Installing 1/58 boost-uninstall:x64-linux...
|
|
...
|
|
Installing 50/58 boost-units:x64-linux...
|
|
Building boost-units:x64-linux...
|
|
-- Downloading https://github.com/boostorg/units/archive/boost-1.82.0.tar.gz -> boostorg-units-boost-1.82.0.tar.gz...
|
|
-- Extracting source /ns-3-dev/vcpkg/downloads/boostorg-units-boost-1.82.0.tar.gz
|
|
-- Using source at /ns-3-dev/vcpkg/buildtrees/boost-units/src/ost-1.82.0-a9fdcc40b2.clean
|
|
-- Copying headers
|
|
-- Copying headers done
|
|
-- Installing: /ns-3-dev/vcpkg/packages/boost-units_x64-linux/share/boost-units/usage
|
|
-- Installing: /ns-3-dev/vcpkg/packages/boost-units_x64-linux/share/boost-units/copyright
|
|
-- Performing post-build validation
|
|
Stored binaries in 1 destinations in 276 ms.
|
|
Elapsed time to handle boost-units:x64-linux: 3.8 s
|
|
Installing 51/58 vcpkg-cmake-config:x64-linux...
|
|
Building vcpkg-cmake-config:x64-linux...
|
|
-- Installing: /ns-3-dev/vcpkg/packages/vcpkg-cmake-config_x64-linux/share/vcpkg-cmake-config/vcpkg_cmake_config_fixup.cmake
|
|
-- Installing: /ns-3-dev/vcpkg/packages/vcpkg-cmake-config_x64-linux/share/vcpkg-cmake-config/vcpkg-port-config.cmake
|
|
-- Installing: /ns-3-dev/vcpkg/packages/vcpkg-cmake-config_x64-linux/share/vcpkg-cmake-config/copyright
|
|
-- Performing post-build validation
|
|
Stored binaries in 1 destinations in 8.58 ms.
|
|
Elapsed time to handle vcpkg-cmake-config:x64-linux: 144 ms
|
|
Installing 52/58 eigen3:x64-linux...
|
|
Building eigen3:x64-linux...
|
|
-- Downloading https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz -> libeigen-eigen-3.4.0.tar.gz...
|
|
-- Extracting source /ns-3-dev/vcpkg/downloads/libeigen-eigen-3.4.0.tar.gz
|
|
-- Applying patch remove_configure_checks.patch
|
|
-- Applying patch fix-vectorized-reductions-half.patch
|
|
-- Using source at /ns-3-dev/vcpkg/buildtrees/eigen3/src/3.4.0-74a8d62212.clean
|
|
-- Configuring x64-linux
|
|
-- Building x64-linux-dbg
|
|
-- Building x64-linux-rel
|
|
-- Fixing pkgconfig file: /ns-3-dev/vcpkg/packages/eigen3_x64-linux/lib/pkgconfig/eigen3.pc
|
|
CMake Error at scripts/cmake/vcpkg_find_acquire_program.cmake:163 (message):
|
|
Could not find pkg-config. Please install it via your package manager:
|
|
|
|
sudo apt-get install pkg-config
|
|
Call Stack (most recent call first):
|
|
scripts/cmake/vcpkg_fixup_pkgconfig.cmake:203 (vcpkg_find_acquire_program)
|
|
ports/eigen3/portfile.cmake:30 (vcpkg_fixup_pkgconfig)
|
|
scripts/ports.cmake:147 (include)
|
|
|
|
|
|
error: building eigen3:x64-linux failed with: BUILD_FAILED
|
|
Elapsed time to handle eigen3:x64-linux: 19 s
|
|
Please ensure you're using the latest port files with `git pull` and `vcpkg update`.
|
|
Then check for known issues at:
|
|
https://github.com/microsoft/vcpkg/issues?q=is%3Aissue+is%3Aopen+in%3Atitle+eigen3
|
|
You can submit a new issue at:
|
|
https://github.com/microsoft/vcpkg/issues/new?title=[eigen3]+Build+error&body=Copy+issue+body+from+%2Fns-3-dev%2Fvcpkg%2Finstalled%2Fvcpkg%2Fissue_body.md
|
|
|
|
CMake Error at build-support/3rd-party/colored-messages.cmake:82 (_message):
|
|
vcpkg: packages defined in the manifest failed to be installed
|
|
Call Stack (most recent call first):
|
|
build-support/custom-modules/ns3-vcpkg-hunter.cmake:138 (message)
|
|
build-support/custom-modules/ns3-vcpkg-hunter.cmake:183 (setup_vcpkg)
|
|
build-support/macros-and-definitions.cmake:743 (include)
|
|
CMakeLists.txt:149 (process_options)
|
|
|
|
As we can see above, the setup failed during the eigen3 setup due to a missing dependency.
|
|
In this case, pkg-config. We can install it using the system package manager and then
|
|
resume the ns-3 configuration.
|
|
|
|
|
|
.. sourcecode:: console
|
|
|
|
/ns-3-dev$ apt install -y pkg-config
|
|
/ns-3-dev$ ./ns3 configure -- -DNS3_VCPKG=ON
|
|
...
|
|
-- vcpkg: folder already exists, skipping git download
|
|
-- vcpkg: already bootstrapped
|
|
-- vcpkg: detected a vcpkg manifest file: /ns-3-dev/vcpkg.json
|
|
Detecting compiler hash for triplet x64-linux...
|
|
The following packages will be built and installed:
|
|
eigen3:x64-linux -> 3.4.0#2
|
|
gsl:x64-linux -> 2.7.1#3
|
|
* libiconv:x64-linux -> 1.17#1
|
|
* liblzma:x64-linux -> 5.4.3#1
|
|
libxml2[core,iconv,lzma,zlib]:x64-linux -> 2.10.3#1
|
|
sqlite3[core,json1]:x64-linux -> 3.42.0#1
|
|
* zlib:x64-linux -> 1.2.13
|
|
Additional packages (*) will be modified to complete this operation.
|
|
Restored 0 package(s) from /root/.cache/vcpkg/archives in 97.6 us. Use --debug to see more details.
|
|
Installing 1/7 eigen3:x64-linux...
|
|
Building eigen3:x64-linux...
|
|
-- Using cached libeigen-eigen-3.4.0.tar.gz.
|
|
-- Cleaning sources at /ns-3-dev/vcpkg/buildtrees/eigen3/src/3.4.0-74a8d62212.clean. Use --editable to skip cleaning for the packages you specify.
|
|
-- Extracting source /ns-3-dev/vcpkg/downloads/libeigen-eigen-3.4.0.tar.gz
|
|
-- Applying patch remove_configure_checks.patch
|
|
-- Applying patch fix-vectorized-reductions-half.patch
|
|
-- Using source at /ns-3-dev/vcpkg/buildtrees/eigen3/src/3.4.0-74a8d62212.clean
|
|
-- Configuring x64-linux
|
|
-- Building x64-linux-dbg
|
|
-- Building x64-linux-rel
|
|
-- Fixing pkgconfig file: /ns-3-dev/vcpkg/packages/eigen3_x64-linux/lib/pkgconfig/eigen3.pc
|
|
-- Fixing pkgconfig file: /ns-3-dev/vcpkg/packages/eigen3_x64-linux/debug/lib/pkgconfig/eigen3.pc
|
|
-- Installing: /ns-3-dev/vcpkg/packages/eigen3_x64-linux/share/eigen3/copyright
|
|
-- Performing post-build validation
|
|
Stored binaries in 1 destinations in 1.7 s.
|
|
Elapsed time to handle eigen3:x64-linux: 28 s
|
|
Installing 2/7 gsl:x64-linux...
|
|
...
|
|
Installing 7/7 sqlite3:x64-linux...
|
|
Building sqlite3[core,json1]:x64-linux...
|
|
-- Downloading https://sqlite.org/2023/sqlite-amalgamation-3420000.zip -> sqlite-amalgamation-3420000.zip...
|
|
-- Extracting source /ns-3-dev/vcpkg/downloads/sqlite-amalgamation-3420000.zip
|
|
-- Applying patch fix-arm-uwp.patch
|
|
-- Applying patch add-config-include.patch
|
|
-- Using source at /ns-3-dev/vcpkg/buildtrees/sqlite3/src/on-3420000-e624a7f335.clean
|
|
-- Configuring x64-linux
|
|
-- Building x64-linux-dbg
|
|
-- Building x64-linux-rel
|
|
-- Fixing pkgconfig file: /ns-3-dev/vcpkg/packages/sqlite3_x64-linux/lib/pkgconfig/sqlite3.pc
|
|
-- Fixing pkgconfig file: /ns-3-dev/vcpkg/packages/sqlite3_x64-linux/debug/lib/pkgconfig/sqlite3.pc
|
|
-- Installing: /ns-3-dev/vcpkg/packages/sqlite3_x64-linux/share/sqlite3/usage
|
|
-- Performing post-build validation
|
|
Stored binaries in 1 destinations in 430 ms.
|
|
Elapsed time to handle sqlite3:x64-linux: 42 s
|
|
Total install time: 2.5 min
|
|
The package boost is compatible with built-in CMake targets:
|
|
|
|
find_package(Boost REQUIRED [COMPONENTS <libs>...])
|
|
target_link_libraries(main PRIVATE Boost::boost Boost::<lib1> Boost::<lib2> ...)
|
|
|
|
eigen3 provides CMake targets:
|
|
|
|
# this is heuristically generated, and may not be correct
|
|
find_package(Eigen3 CONFIG REQUIRED)
|
|
target_link_libraries(main PRIVATE Eigen3::Eigen)
|
|
|
|
The package gsl is compatible with built-in CMake targets:
|
|
|
|
find_package(GSL REQUIRED)
|
|
target_link_libraries(main PRIVATE GSL::gsl GSL::gslcblas)
|
|
|
|
The package libxml2 is compatible with built-in CMake targets:
|
|
|
|
find_package(LibXml2 REQUIRED)
|
|
target_link_libraries(main PRIVATE LibXml2::LibXml2)
|
|
|
|
sqlite3 provides pkgconfig bindings.
|
|
sqlite3 provides CMake targets:
|
|
|
|
find_package(unofficial-sqlite3 CONFIG REQUIRED)
|
|
target_link_libraries(main PRIVATE unofficial::sqlite3::sqlite3)
|
|
|
|
-- vcpkg: packages defined in the manifest were installed
|
|
-- find_external_library: SQLite3 was found.
|
|
...
|
|
-- LibXML2 was found.
|
|
...
|
|
-- Found Boost: /ns-3-dev/vcpkg/installed/x64-linux/include (found version "1.82.0")
|
|
...
|
|
-- Looking for include files boost/units/quantity.hpp, boost/units/systems/si.hpp
|
|
-- Looking for include files boost/units/quantity.hpp, boost/units/systems/si.hpp - found
|
|
-- Boost Units have been found.
|
|
...
|
|
-- ---- Summary of ns-3 settings:
|
|
Build profile : default
|
|
Build directory : /ns-3-dev/build
|
|
Build with runtime asserts : ON
|
|
...
|
|
GNU Scientific Library (GSL) : ON
|
|
...
|
|
LibXml2 support : ON
|
|
...
|
|
SQLite support : ON
|
|
Eigen3 support : ON
|
|
...
|
|
|
|
From the above, we can see that the headers and libraries installed by the packages
|
|
were correctly found by CMake and the optional features were successfully enabled.
|
|
|
|
.. _Armadillo's port on vcpkg: https://github.com/microsoft/vcpkg/blob/master/ports/armadillo/usage
|
|
|
|
Note: not every vcpkg package (also known as a port) obeys
|
|
the same pattern for usage. The user of the package needs
|
|
to look into the usage file of said port for instructions.
|
|
In the case of Armadillo, the corresponding file can be found
|
|
in `Armadillo's port on vcpkg`_.
|
|
|
|
Vcpkg is installed to a ``vcpkg`` directory inside the ns-3 main directory (e.g. ``ns-3-dev``).
|
|
Packages installed via vcpkg are installed to ``ns-3-dev/vcpkg/installed/${VCPKG_TRIPLET}``,
|
|
which is automatically added to the ``CMAKE_PREFIX_PATH``, making headers, libraries, and
|
|
pkg-config and CMake packages discoverable via ``find_file``, ``find_library``, ``find_package``
|
|
and ``pkg_check_modules``.
|
|
|
|
CPM
|
|
===
|
|
|
|
`CPM`_ is a package manager made for CMake projects consuming CMake projects.
|
|
Some CMake projects however, create files during the installation step, which
|
|
is not supported by CPM, which treats the package as a CMake subproject that
|
|
we can then depend upon. CPM may require dependencies such as ``git`` and ``tar``,
|
|
depending on the package sources used.
|
|
|
|
Let's see an example trying to find the Armadillo library via CMake.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
# Check this is not a fluke
|
|
find_package(Armadillo)
|
|
message(STATUS "Armadillo was found? ${ARMADILLO_FOUND}")
|
|
|
|
Reconfigure ns-3 to check if Armadillo is available.
|
|
|
|
.. sourcecode:: console
|
|
|
|
~$ ./ns3 configure
|
|
...
|
|
-- Could NOT find Armadillo (missing: ARMADILLO_INCLUDE_DIR)
|
|
-- Armadillo was found? FALSE
|
|
|
|
As you can see, no Armadillo found.
|
|
We can now use CPM to install it, using the CMake
|
|
function ``CPMAddPackage(package_info)``.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
# Install Armadillo and search for it again
|
|
CPMAddPackage(
|
|
NAME ARMADILLO
|
|
GIT_TAG 6cada351248c9a967b137b9fcb3d160dad7c709b
|
|
GIT_REPOSITORY https://gitlab.com/conradsnicta/armadillo-code.git
|
|
)
|
|
find_package(Armadillo) # Loads CPM installation of Armadillo
|
|
message(STATUS "Armadillo was found? ${ARMADILLO_FOUND}")
|
|
|
|
Sadly, we will need to reconfigure ns-3 from the scratch,
|
|
since CMake ``find_package`` caches are problematic.
|
|
Installing the packages can take a while, and it can look like it hanged.
|
|
|
|
.. sourcecode:: console
|
|
|
|
~$ ./ns3 clean
|
|
~$ ./ns3 configure -- -DNS3_CPM=ON
|
|
...
|
|
-- CPM: Adding package ARMADILLO@0 (6cada351248c9a967b137b9fcb3d160dad7c709b)
|
|
-- *** set cmake policy CMP0025 to NEW
|
|
-- CMAKE_CXX_STANDARD = 11
|
|
-- Configuring Armadillo 12.6.1
|
|
--
|
|
-- *** WARNING: variable 'CMAKE_CXX_FLAGS' is not empty; this may cause problems!
|
|
--
|
|
-- Detected Clang 6.0 or newer
|
|
-- ARMA_USE_EXTERN_RNG = true
|
|
-- CMAKE_SYSTEM_NAME = Linux
|
|
-- CMAKE_CXX_COMPILER_ID = Clang
|
|
-- CMAKE_CXX_COMPILER_VERSION = 15.0.7
|
|
-- CMAKE_COMPILER_IS_GNUCXX =
|
|
--
|
|
-- *** Options:
|
|
-- BUILD_SHARED_LIBS = ON
|
|
-- OPENBLAS_PROVIDES_LAPACK = OFF
|
|
-- ALLOW_FLEXIBLAS_LINUX = ON
|
|
-- ALLOW_OPENBLAS_MACOS = OFF
|
|
-- ALLOW_BLAS_LAPACK_MACOS = OFF
|
|
-- BUILD_SMOKE_TEST = ON
|
|
--
|
|
-- *** Looking for external libraries
|
|
-- Found OpenBLAS: /usr/lib/x86_64-linux-gnu/libopenblas.so
|
|
-- Found BLAS: /usr/lib/x86_64-linux-gnu/libblas.so
|
|
-- Found LAPACK: /usr/lib/x86_64-linux-gnu/liblapack.so
|
|
-- FlexiBLAS_FOUND = NO
|
|
-- MKL_FOUND = NO
|
|
-- OpenBLAS_FOUND = YES
|
|
-- ATLAS_FOUND = NO
|
|
-- BLAS_FOUND = YES
|
|
-- LAPACK_FOUND = YES
|
|
--
|
|
-- *** NOTE: found both OpenBLAS and BLAS; BLAS will not be used
|
|
--
|
|
-- *** NOTE: if OpenBLAS is known to provide LAPACK functions, recommend to
|
|
-- *** NOTE: rerun cmake with the OPENBLAS_PROVIDES_LAPACK option enabled:
|
|
-- *** NOTE: cmake -D OPENBLAS_PROVIDES_LAPACK=true .
|
|
--
|
|
-- *** If the OpenBLAS library is installed in
|
|
-- *** /usr/local/lib or /usr/local/lib64
|
|
-- *** make sure the run-time linker can find it.
|
|
-- *** On Linux systems this can be done by editing /etc/ld.so.conf
|
|
-- *** or modifying the LD_LIBRARY_PATH environment variable.
|
|
--
|
|
-- Found ARPACK: /usr/lib/x86_64-linux-gnu/libarpack.so
|
|
-- ARPACK_FOUND = YES
|
|
-- Looking for SuperLU version 5
|
|
-- Found SuperLU: /usr/lib/x86_64-linux-gnu/libsuperlu.so
|
|
-- SuperLU_FOUND = YES
|
|
-- SuperLU_INCLUDE_DIR = /usr/include/superlu
|
|
--
|
|
-- *** Result of configuration:
|
|
-- *** ARMA_USE_WRAPPER = true
|
|
-- *** ARMA_USE_LAPACK = true
|
|
-- *** ARMA_USE_BLAS = true
|
|
-- *** ARMA_USE_ATLAS = false
|
|
-- *** ARMA_USE_ARPACK = true
|
|
-- *** ARMA_USE_EXTERN_RNG = true
|
|
-- *** ARMA_USE_SUPERLU = true
|
|
--
|
|
-- *** Armadillo wrapper library will use the following libraries:
|
|
-- *** ARMA_LIBS = /usr/lib/x86_64-linux-gnu/libopenblas.so;/usr/lib/x86_64-linux-gnu/liblapack.so;/usr/lib/x86_64-linux-gnu/libarpack.so;/usr/lib/x86_64-linux-gnu/libsuperlu.so
|
|
--
|
|
-- Copying /ns-3-dev/cmake-build-release/_deps/armadillo-src/include/ to /ns-3-dev/cmake-build-release/_deps/armadillo-build/tmp/include/
|
|
-- Generating /ns-3-dev/cmake-build-release/_deps/armadillo-build/tmp/include/config.hpp
|
|
-- CMAKE_CXX_FLAGS = -fsanitize=address,leak,undefined -Os
|
|
-- CMAKE_SHARED_LINKER_FLAGS = -Wl,--no-as-needed
|
|
-- CMAKE_REQUIRED_INCLUDES = /usr/include;/usr/include/superlu
|
|
--
|
|
-- CMAKE_INSTALL_PREFIX = /usr
|
|
-- CMAKE_INSTALL_LIBDIR = lib/x86_64-linux-gnu
|
|
-- CMAKE_INSTALL_INCLUDEDIR = include
|
|
-- CMAKE_INSTALL_DATADIR = share
|
|
-- CMAKE_INSTALL_BINDIR = bin
|
|
-- Generating '/ns-3-dev/cmake-build-release/_deps/armadillo-build/ArmadilloConfig.cmake'
|
|
-- Generating '/ns-3-dev/cmake-build-release/_deps/armadillo-build/ArmadilloConfigVersion.cmake'
|
|
-- Generating '/ns-3-dev/cmake-build-release/_deps/armadillo-build/InstallFiles/ArmadilloConfig.cmake'
|
|
-- Generating '/ns-3-dev/cmake-build-release/_deps/armadillo-build/InstallFiles/ArmadilloConfigVersion.cmake'
|
|
-- Copying /ns-3-dev/cmake-build-release/_deps/armadillo-src/misc/ to /ns-3-dev/cmake-build-release/_deps/armadillo-build/tmp/misc/
|
|
-- Generating '/ns-3-dev/cmake-build-release/_deps/armadillo-build/tmp/misc/armadillo.pc'
|
|
-- *** configuring smoke_test
|
|
-- Armadillo was found? TRUE
|
|
...
|
|
|
|
As shown above, the Armadillo library gets installed by CPM
|
|
and it can be found by CMake's ``find_package`` function.
|
|
Differently from other packages found via ``find_package``,
|
|
CPM creates native CMake targets from the subprojects.
|
|
In the case of Armadillo, the target is called ``armadillo``,
|
|
which we can link to our targets.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
# Install Armadillo
|
|
CPMAddPackage(
|
|
NAME ARMADILLO
|
|
GIT_TAG 6cada351248c9a967b137b9fcb3d160dad7c709b
|
|
GIT_REPOSITORY https://gitlab.com/conradsnicta/armadillo-code.git
|
|
)
|
|
find_package(Armadillo) # Loads CPM installation of Armadillo
|
|
message(STATUS "Armadillo was found? ${ARMADILLO_FOUND}")
|
|
|
|
# CPM is kind of jenky. It could get the ARMADILLO_INCLUDE_DIRS
|
|
# from the ArmadilloConfig.cmake file in ${CMAKE_BINARY_DIR}/_deps/armadillo-build,
|
|
# but it doesn't... So add its include directories directly from the source directory
|
|
include_directories(${CMAKE_BINARY_DIR}/_deps/armadillo-src/include)
|
|
|
|
# Link to Armadillo and
|
|
link_libraries(armadillo)
|
|
|
|
.. _`CPM's examples`: https://github.com/cpm-cmake/CPM.cmake/wiki/More-Snippets
|
|
|
|
Note: using CPM can be challenging. Users are recommended
|
|
to look at `CPM's examples`_.
|
|
|
|
For example, the libraries of installed packages will be placed by default in
|
|
the ``ns-3-dev/build/lib`` directory. On the other hand, header placement depends
|
|
on how the CMake project was setup.
|
|
|
|
If the package CMakeLists.txt was made to build in-source, headers will be
|
|
along the source files, which will be placed in
|
|
``${PROJECT_BINARY_DIR}/_deps/packageName-src``. When configured with the
|
|
ns3 script, ``PROJECT_BINARY_DIR corresponds`` to ``ns-3-dev/cmake-cache``.
|
|
|
|
If the package CMakeLists.txt copies the headers to an output directory
|
|
(like ns-3 does), it will be placed in
|
|
``${PROJECT_BINARY_DIR}/_deps/packageName-build``,
|
|
possibly in an ``include`` subdirectory.
|
|
|
|
In case it was configured to copy the headers to ``${CMAKE_BINARY_DIR}/include``,
|
|
the headers will land on ``${PROJECT_BINARY_DIR}/include`` of the most top-level
|
|
project. In our case, the top-level project is the NS3 project.
|
|
|
|
Since the packages get installed into the ns-3 cache directory
|
|
(``PROJECT_BINARY_DIR``), using ``./ns3 clean`` will delete them,
|
|
requiring them to be rebuilt.
|
|
|
|
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
|
|
build-support/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 the default option
|
|
set(NS3_INT64X64 "INT128" 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
|
|
``build-support/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.
|
|
|
|
Executable macros
|
|
=================
|
|
|
|
Creating an executable in CMake requires a few different macro calls.
|
|
Some of these calls are related to setting the target and built executable name,
|
|
indicating which libraries that should be linked to the executable,
|
|
where the executable should be placed after being built and installed.
|
|
|
|
Note that if you are trying to add a new example to your module, you should
|
|
look at the `build_lib_example`_ macro section.
|
|
|
|
If you are trying to add a new example to ``~/ns-3-dev/examples``, you should
|
|
look at the `build_example`_ macro section.
|
|
|
|
While both of the previously mentioned macros are meant to be used for examples,
|
|
in some cases additional utilities are required. Those utilities can be helpers,
|
|
such as the ``raw-sock-creator`` in the ``fd-net-device`` module, or benchmark
|
|
tools in the ``~/ns-3-dev/utils`` directory. In those cases, the `build_exec`_
|
|
macro is recommended instead of direct CMake calls.
|
|
|
|
.. _build_exec:
|
|
|
|
Executable macros: build_exec
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The ``build_exec`` macro bundles a series of direct CMake calls into a single macro.
|
|
The example below shows the creation of an executable named ``example``, that will
|
|
later receive a version prefix (e.g. ``ns3.37-``) and a build type suffix
|
|
(e.g. ``-debug``), resulting in an executable file named ``ns3.37-example-debug``.
|
|
|
|
The list of source and header files can be passed in the ``SOURCE_FILES`` and
|
|
``HEADER_FILES`` arguments, followed by the ``LIBRARIES_TO_LINK`` that will be linked
|
|
to the executable.
|
|
|
|
That executable will be saved by default to the ``CMAKE_RUNTIME_OUTPUT_DIRECTORY``
|
|
(e.g. /ns-3-dev/build/bin). To change its destination, set ``EXECUTABLE_DIRECTORY_PATH``
|
|
to the desired path. The path is relative to the ``CMAKE_OUTPUT_DIRECTORY``
|
|
(e.g. /ns-3-dev/build).
|
|
|
|
In case this executable should be installed, set ``INSTALL_DIRECTORY_PATH`` to the
|
|
desired destination. In case this value is empty, the executable will not be installed.
|
|
The path is relative to the ``CMAKE_INSTALL_PREFIX`` (e.g. /usr).
|
|
|
|
To set custom compiler defines for that specific executable, defines can be passed
|
|
to the ``DEFINITIONS`` argument.
|
|
|
|
Add the ``STANDALONE`` option to prevent linking the |ns3| static library
|
|
(``NS3_STATIC``) and single shared library (``NS3_MONOLIB``) to the executable.
|
|
This may be necessary in case the executable redefine symbols which are part
|
|
of the |ns3| library. This is the case for the fd-net-device creators and the tap-creator,
|
|
which include the source file ``encode-decode.cc``, which is also part of fd-net-device module
|
|
and tap-bridge module, respectively.
|
|
|
|
Finally, to ignore precompiled headers, include ``IGNORE_PCH`` to the list of parameters.
|
|
You can find more information about ``IGNORE_PCH`` at the `PCH side-effects`_ section.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
build_exec(
|
|
# necessary
|
|
EXECNAME example # executable name = example (plus version prefix and build type suffix)
|
|
SOURCE_FILES example.cc example-complement.cc
|
|
HEADER_FILES example.h
|
|
LIBRARIES_TO_LINK ${libcore} # links to core
|
|
EXECUTABLE_DIRECTORY_PATH scratch # build/scratch
|
|
# optional
|
|
EXECNAME_PREFIX scratch_subdir_prefix_ # target name = scratch_subdir_prefix_example
|
|
INSTALL_DIRECTORY_PATH ${CMAKE_INSTALL_BIN}/ # e.g. /usr/bin/ns3.37-scratch_subdir_prefix_example-debug
|
|
DEFINITIONS -DHAVE_FEATURE=1 # defines for this specific target
|
|
[STANDALONE] # set in case you don't want the executable to be linked to ns3-static/ns3-monolib
|
|
IGNORE_PCH
|
|
)
|
|
|
|
The same executable can be built by directly calling the following CMake macros:
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
set(target_prefix scratch_subdir_prefix_)
|
|
set(target_name example)
|
|
set(output_directory scratch)
|
|
|
|
# Creates a target named "example" (target_name) prefixed with "scratch_subdir_prefix_" (target_prefix)
|
|
# e.g. scratch_subdir_prefix_example
|
|
add_executable(${target_prefix}${target_name} example.cc example-complement.cc)
|
|
target_link_libraries(${target_prefix}${target_name} PUBLIC ${libcore})
|
|
|
|
# Create a variable with the target name prefixed with
|
|
# the version and suffixed with the build profile suffix
|
|
# e.g. ns3.37-scratch_subdir_prefix_example-debug
|
|
set(ns3-exec-outputname ns${NS3_VER}-${target_prefix}${target_name}${build_profile_suffix})
|
|
|
|
# Append the binary name to the executables list later written to the lock file,
|
|
# which is consumed by the ns3 script and test.py
|
|
set(ns3-execs "${output_directory}${ns3-exec-outputname};${ns3-execs}"
|
|
CACHE INTERNAL "list of c++ executables"
|
|
)
|
|
# Modify the target properties to change the binary name to ns3-exec-outputname contents
|
|
# and modify its output directory (e.g. scratch). The output directory is relative to the build directory.
|
|
set_target_properties(
|
|
${target_prefix}${target_name}
|
|
PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_directory}
|
|
RUNTIME_OUTPUT_NAME ${ns3-exec-outputname}
|
|
)
|
|
# Create a dependency between the target and the all-test-targets
|
|
# (used by ctest, coverage and doxygen targets)
|
|
add_dependencies(all-test-targets ${target_prefix}${target_name})
|
|
|
|
# Create a dependency between the target and the timeTraceReport
|
|
# (used by Clang TimeTrace to collect compilation statistics)
|
|
add_dependencies(timeTraceReport ${target_prefix}${target_name}) # target used to track compilation time
|
|
|
|
# Set target-specific compile definitions
|
|
target_compile_definitions(${target_prefix}${target_name} PUBLIC definitions)
|
|
|
|
# Check whether the target should reuse or not the precompiled headers
|
|
if(NOT ${IGNORE_PCH})
|
|
target_precompile_headers(
|
|
${target_prefix}${target_name} REUSE_FROM stdlib_pch_exec
|
|
)
|
|
endif()
|
|
|
|
|
|
.. _build_example:
|
|
|
|
Executable macros: build_example
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The ``build_example`` macro sets some of ``build_exec``'s arguments based on the current
|
|
example directory (output directory) and adds the optional visualizer module as a dependency
|
|
in case it is enabled. It also performs dependency checking on the libraries passed.
|
|
|
|
In case one of the dependencies listed is not found, the example target will not be created.
|
|
If you are trying to add an example or a dependency to an existing example and it is not
|
|
listed by ``./ns3 show targets`` or your IDE, check if all its dependencies were found.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
macro(build_example)
|
|
set(options IGNORE_PCH)
|
|
set(oneValueArgs NAME)
|
|
set(multiValueArgs SOURCE_FILES HEADER_FILES LIBRARIES_TO_LINK)
|
|
# Parse arguments
|
|
cmake_parse_arguments(
|
|
"EXAMPLE" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}
|
|
)
|
|
|
|
# Filter examples out if they don't contain one of the filtered in modules
|
|
set(filtered_in ON)
|
|
if(NS3_FILTER_MODULE_EXAMPLES_AND_TESTS)
|
|
set(filtered_in OFF)
|
|
foreach(filtered_module NS3_FILTER_MODULE_EXAMPLES_AND_TESTS)
|
|
if(${filtered_module} IN_LIST EXAMPLE_LIBRARIES_TO_LINK)
|
|
set(filtered_in ON)
|
|
endif()
|
|
endforeach()
|
|
endif()
|
|
|
|
# Check if any of the LIBRARIES_TO_LINK is missing to prevent configuration errors
|
|
check_for_missing_libraries(
|
|
missing_dependencies "${EXAMPLE_LIBRARIES_TO_LINK}"
|
|
)
|
|
|
|
if((NOT missing_dependencies) AND ${filtered_in})
|
|
# Convert boolean into text to forward argument
|
|
if(${EXAMPLE_IGNORE_PCH})
|
|
set(IGNORE_PCH IGNORE_PCH)
|
|
endif()
|
|
# Create example library with sources and headers
|
|
# cmake-format: off
|
|
build_exec(
|
|
EXECNAME ${EXAMPLE_NAME}
|
|
SOURCE_FILES ${EXAMPLE_SOURCE_FILES}
|
|
HEADER_FILES ${EXAMPLE_HEADER_FILES}
|
|
LIBRARIES_TO_LINK ${EXAMPLE_LIBRARIES_TO_LINK} ${ns3-optional-visualizer-lib}
|
|
EXECUTABLE_DIRECTORY_PATH
|
|
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/examples/${examplefolder}/
|
|
${IGNORE_PCH}
|
|
)
|
|
# cmake-format: on
|
|
endif()
|
|
endmacro()
|
|
|
|
An example on how it is used can be found in ``~/ns-3-dev/examples/tutorial/CMakeLists.txt``:
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
build_example(
|
|
NAME first
|
|
SOURCE_FILES first.cc
|
|
LIBRARIES_TO_LINK
|
|
${libcore}
|
|
${libpoint-to-point}
|
|
${libinternet}
|
|
${libapplications}
|
|
# If visualizer is available, the macro will add the module to this list automatically
|
|
# build_exec's EXECUTABLE_DIRECTORY_PATH will be set to build/examples/tutorial/
|
|
)
|
|
|
|
Module macros
|
|
=============
|
|
|
|
Module macros are located in ``build-support/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.
|
|
|
|
.. _build_lib:
|
|
|
|
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 ON/OFF.
|
|
Options are ``OFF`` by default, and are set to ``ON`` if the option name is added to
|
|
the arguments list (e.g. ``build_lib(... IGNORE_PCH)``).
|
|
|
|
Note: You can find more information about ``IGNORE_PCH`` at the `PCH side-effects`_ section.
|
|
|
|
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 "${BLIB_LIBNAME};${ns3-libs}"
|
|
CACHE INTERNAL "list of processed upstream modules"
|
|
)
|
|
else()
|
|
set(ns3-contrib-libs "${BLIB_LIBNAME};${ns3-contrib-libs}"
|
|
CACHE INTERNAL "list of processed contrib modules"
|
|
)
|
|
endif()
|
|
|
|
We build a shared library using CMake's ``add_library(${BLIB_LIBNAME} SHARED ...)``.
|
|
|
|
Notice that we can also reuse precompiled headers created previously to speed up the
|
|
parsing phase of the compilation.
|
|
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
function(build_lib)
|
|
# ...
|
|
add_library(${BLIB_LIBNAME} SHARED "${BLIB_SOURCE_FILES}")
|
|
|
|
if(${PRECOMPILE_HEADERS_ENABLED} AND (NOT ${IGNORE_PCH}))
|
|
target_precompile_headers(${BLIB_LIBNAME} REUSE_FROM stdlib_pch)
|
|
endif()
|
|
# ...
|
|
endfunction()
|
|
|
|
In the next code block, we create an alias to ``module``, ``ns3::module``,
|
|
which can later be used when importing |ns3| with CMake's ``find_package(ns3)``.
|
|
|
|
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::${BLIB_LIBNAME} ALIAS ${BLIB_LIBNAME})
|
|
|
|
# Associate public headers with library for installation purposes
|
|
set_target_properties(
|
|
${BLIB_LIBNAME}
|
|
PROPERTIES
|
|
PUBLIC_HEADER
|
|
"${BLIB_HEADER_FILES};${BLIB_DEPRECATED_HEADER_FILES};${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.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
function(build_lib)
|
|
# ...
|
|
|
|
build_lib_reexport_third_party_libraries(
|
|
"${BLIB_LIBNAME}" "${BLIB_LIBRARIES_TO_LINK}"
|
|
)
|
|
|
|
# ...
|
|
endfunction(build_lib)
|
|
|
|
The ``build_lib_reexport_third_party_libraries`` macro then separates |ns3| libraries from non-|ns3| libraries.
|
|
The non-|ns3| can be automatically propagated, or not, to libraries/executables linked to the current |ns3| module being processed.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
function(build_lib_reexport_third_party_libraries libname libraries_to_link)
|
|
# Separate ns-3 and non-ns-3 libraries to manage their propagation properly
|
|
separate_ns3_from_non_ns3_libs(
|
|
"${libname}" "${libraries_to_link}" ns_libraries_to_link
|
|
non_ns_libraries_to_link
|
|
)
|
|
|
|
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"
|
|
)
|
|
# ...
|
|
endfunction()
|
|
|
|
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_reexport_third_party_libraries libname libraries_to_link)
|
|
# ...
|
|
|
|
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(${libname} exported_include_directories)
|
|
|
|
string(REPLACE "-I" "" exported_include_directories
|
|
"${exported_include_directories}"
|
|
)
|
|
|
|
# include directories prefixed in the source or binary directory need to be
|
|
# treated differently
|
|
set(new_exported_include_directories)
|
|
foreach(directory ${exported_include_directories})
|
|
string(FIND "${directory}" "${PROJECT_SOURCE_DIR}" is_prefixed_in_subdir)
|
|
if(${is_prefixed_in_subdir} GREATER_EQUAL 0)
|
|
string(SUBSTRING "${directory}" ${is_prefixed_in_subdir} -1
|
|
directory_path
|
|
)
|
|
list(APPEND new_exported_include_directories
|
|
$<BUILD_INTERFACE:${directory_path}>
|
|
)
|
|
else()
|
|
list(APPEND new_exported_include_directories ${directory})
|
|
endif()
|
|
endforeach()
|
|
set(exported_include_directories ${new_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``.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
function(build_lib_reexport_third_party_libraries libname libraries_to_link)
|
|
# ...
|
|
|
|
# Set public and private headers linked to the module library
|
|
target_link_libraries(${libname} ${exported_libraries} ${private_libraries})
|
|
|
|
# ...
|
|
endfunction()
|
|
|
|
Next we export include directories, to let library consumers importing |ns3| via CMake
|
|
use them just by linking to one of the |ns3| modules.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
function(build_lib_reexport_third_party_libraries libname libraries_to_link)
|
|
# ...
|
|
# 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
|
|
# <ns3/something.h>
|
|
target_include_directories(
|
|
${libname} PUBLIC $<BUILD_INTERFACE:${CMAKE_OUTPUT_DIRECTORY}/include>
|
|
$<INSTALL_INTERFACE:include>
|
|
INTERFACE ${exported_include_directories}
|
|
)
|
|
# ...
|
|
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(
|
|
PUBLIC_HEADER_OUTPUT_DIR ${CMAKE_HEADER_OUTPUT_DIRECTORY}
|
|
PUBLIC_HEADER_FILES ${BLIB_HEADER_FILES}
|
|
DEPRECATED_HEADER_OUTPUT_DIR ${CMAKE_HEADER_OUTPUT_DIRECTORY}
|
|
DEPRECATED_HEADER_FILES ${BLIB_DEPRECATED_HEADER_FILES}
|
|
PRIVATE_HEADER_OUTPUT_DIR ${CMAKE_HEADER_OUTPUT_DIRECTORY}
|
|
PRIVATE_HEADER_FILES ${BLIB_PRIVATE_HEADER_FILES}
|
|
)
|
|
# ...
|
|
endfunction()
|
|
|
|
The following block creates the test library for the module currently being processed.
|
|
Note that it handles Windows vs non-Windows differently, since Windows requires all
|
|
used linked symbols to be used, which results in our test-runner being unable to
|
|
be dynamically linked to ns-3 modules. To solve that, we create the test libraries
|
|
for the different modules as object libraries instead, and statically link to the
|
|
test-runner executable.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
function(build_lib)
|
|
# ...
|
|
build_lib_tests(
|
|
"${BLIB_LIBNAME}" "${BLIB_IGNORE_PCH}" "${FOLDER}" "${BLIB_TEST_SOURCES}"
|
|
)
|
|
# ...
|
|
endfunction()
|
|
|
|
# This macro builds the test library for the module library
|
|
#
|
|
# Arguments: libname (e.g. core), ignore_pch (TRUE/FALSE), folder (src/contrib),
|
|
# sources (list of .cc's)
|
|
function(build_lib_tests libname ignore_pch folder test_sources)
|
|
if(${ENABLE_TESTS})
|
|
# Check if the module tests should be built
|
|
build_lib_check_examples_and_tests_filtered_in(${libname} filtered_in)
|
|
if(NOT ${filtered_in})
|
|
return()
|
|
endif()
|
|
list(LENGTH test_sources test_source_len)
|
|
if(${test_source_len} GREATER 0)
|
|
# Create libname of output library test of module
|
|
set(test${libname} ${libname}-test CACHE INTERNAL "")
|
|
|
|
# Create shared library containing tests of the module on UNIX and just
|
|
# the object file that will be part of test-runner on Windows
|
|
if(WIN32)
|
|
set(ns3-libs-tests
|
|
"$<TARGET_OBJECTS:${test${libname}}>;${ns3-libs-tests}"
|
|
CACHE INTERNAL "list of test libraries"
|
|
)
|
|
add_library(${test${libname}} OBJECT "${test_sources}")
|
|
else()
|
|
set(ns3-libs-tests "${test${libname}};${ns3-libs-tests}"
|
|
CACHE INTERNAL "list of test libraries"
|
|
)
|
|
add_library(${test${libname}} SHARED "${test_sources}")
|
|
|
|
# Link test library to the module library
|
|
if(${NS3_MONOLIB})
|
|
target_link_libraries(
|
|
${test${libname}} ${LIB_AS_NEEDED_PRE} ${lib-ns3-monolib}
|
|
${LIB_AS_NEEDED_POST}
|
|
)
|
|
else()
|
|
target_link_libraries(
|
|
${test${libname}} ${LIB_AS_NEEDED_PRE} ${libname}
|
|
"${BLIB_LIBRARIES_TO_LINK}" ${LIB_AS_NEEDED_POST}
|
|
)
|
|
endif()
|
|
set_target_properties(
|
|
${test${libname}}
|
|
PROPERTIES OUTPUT_NAME
|
|
ns${NS3_VER}-${libname}-test${build_profile_suffix}
|
|
)
|
|
endif()
|
|
target_compile_definitions(
|
|
${test${libname}} PRIVATE NS_TEST_SOURCEDIR="${folder}/test"
|
|
)
|
|
if(${PRECOMPILE_HEADERS_ENABLED} AND (NOT ${ignore_pch}))
|
|
target_precompile_headers(${test${libname}} REUSE_FROM stdlib_pch)
|
|
endif()
|
|
|
|
# Add dependency between tests and examples used as tests
|
|
examples_as_tests_dependencies("${module_examples}" "${test_sources}")
|
|
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)
|
|
# ...
|
|
|
|
# Scan for C++ and Python examples and return a list of C++ examples (which
|
|
# can be set as dependencies of examples-as-test test suites)
|
|
build_lib_scan_examples(module_examples)
|
|
|
|
# ...
|
|
endfunction()
|
|
|
|
# This macro scans for C++ and Python examples for a given module and return a
|
|
# list of C++ examples
|
|
#
|
|
# Arguments: module_cpp_examples = return list of C++ examples
|
|
function(build_lib_scan_examples module_cpp_examples)
|
|
# Build lib examples if requested
|
|
set(examples_before ${ns3-execs-clean})
|
|
foreach(example_folder example;examples)
|
|
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${example_folder})
|
|
if(${ENABLE_EXAMPLES})
|
|
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${example_folder}/CMakeLists.txt)
|
|
add_subdirectory(${example_folder})
|
|
endif()
|
|
endif()
|
|
scan_python_examples(${CMAKE_CURRENT_SOURCE_DIR}/${example_folder})
|
|
endif()
|
|
endforeach()
|
|
set(module_examples ${ns3-execs-clean})
|
|
|
|
# Return a list of module c++ examples (current examples - previous examples)
|
|
list(REMOVE_ITEM module_examples ${examples_before})
|
|
set(${module_cpp_examples} ${module_examples} PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
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 ${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()
|
|
|
|
|
|
.. _build_lib_example:
|
|
|
|
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 whether 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 |ns3| modules required by the example are enabled to be built.
|
|
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 (``${ns3-optional-visualizer-lib}``).
|
|
If the visualizer module is not enabled, ``ns3-optional-visualizer-lib`` is empty.
|
|
|
|
The example can also be linked to a single |ns3| shared library (``lib-ns3-monolib``) or
|
|
a single |ns3| static library (``lib-ns3-static``), if either ``NS3_MONOLIB=ON`` or ``NS3_STATIC=ON``.
|
|
Note that both of these options are handled by the ``build_exec`` macro.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
function(build_lib_example)
|
|
# ...
|
|
check_for_missing_libraries(missing_dependencies "${BLIB_EXAMPLE_LIBRARIES_TO_LINK}")
|
|
|
|
# Check if a module example should be built
|
|
set(filtered_in ON)
|
|
if(NS3_FILTER_MODULE_EXAMPLES_AND_TESTS)
|
|
set(filtered_in OFF)
|
|
if(${BLIB_LIBNAME} IN_LIST NS3_FILTER_MODULE_EXAMPLES_AND_TESTS)
|
|
set(filtered_in ON)
|
|
endif()
|
|
endif()
|
|
|
|
if((NOT missing_dependencies) AND ${filtered_in})
|
|
# Convert boolean into text to forward argument
|
|
if(${BLIB_EXAMPLE_IGNORE_PCH})
|
|
set(IGNORE_PCH IGNORE_PCH)
|
|
endif()
|
|
# Create executable with sources and headers
|
|
# cmake-format: off
|
|
build_exec(
|
|
EXECNAME ${BLIB_EXAMPLE_NAME}
|
|
SOURCE_FILES ${BLIB_EXAMPLE_SOURCE_FILES}
|
|
HEADER_FILES ${BLIB_EXAMPLE_HEADER_FILES}
|
|
LIBRARIES_TO_LINK
|
|
${lib${BLIB_EXAMPLE_LIBNAME}} ${BLIB_EXAMPLE_LIBRARIES_TO_LINK}
|
|
${ns3-optional-visualizer-lib}
|
|
EXECUTABLE_DIRECTORY_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${FOLDER}/
|
|
${IGNORE_PCH}
|
|
)
|
|
# cmake-format: on
|
|
endif()
|
|
endfunction()
|
|
|
|
The `build_exec`_ macro will also set resulting folder where the example will end up
|
|
after built (e.g. build/src/module/examples). It does that by forwarding the
|
|
``EXECUTABLE_DIRECTORY_PATH`` to the macro ``set_runtime_outputdirectory``, which also
|
|
adds the proper |ns3| version prefix and build type suffix to the executable.
|
|
|
|
As with the module libraries, we can also reuse precompiled headers here to speed up
|
|
the parsing step of compilation.
|
|
You can find more information about ``IGNORE_PCH`` at the `PCH side-effects`_ section.
|
|
|
|
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(
|
|
build-support/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 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_docs_missing_deps EXECUTABLES doxygen dot dia python3)
|
|
|
|
# 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
|
|
``build-support/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 |ns3| calls will not be
|
|
correctly interposed by the ``LD_PRELOAD`` trick, which is not know to be used by |ns3| 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()
|
|
|
|
|
|
|
|
CCache and Precompiled Headers
|
|
******************************
|
|
|
|
.. _CCache: https://ccache.dev/
|
|
.. _PCHs: https://gcc.gnu.org/onlinedocs/gcc/Precompiled-Headers.html
|
|
|
|
|
|
There are a few ways of speeding up the build of |ns3| and its modules.
|
|
Partially rebuilding only changed modules is one of the ways, and
|
|
this is already handled by the build system.
|
|
|
|
However, cleaning up the build and cmake cache directories removes
|
|
the intermediate and final files that could be used to skip the build
|
|
of unchanged modules.
|
|
|
|
In this case, `CCache`_ is recommended. It acts as a compiler and stores the
|
|
intermediate and final object files and libraries on a cache artifact directory.
|
|
|
|
Note: for ease of use, CCache is enabled by default if found by the build system.
|
|
|
|
The cache artifact directory of CCACHE can be set by changing the
|
|
``CCACHE_BASEDIR`` environment variable.
|
|
|
|
The CCache artifact cache is separated per directory, to prevent incompatible
|
|
artifacts, which may depend on different working directories ``CWD`` to work properly
|
|
from getting mixed and producing binaries that will start running from a different directory.
|
|
|
|
Note: to reuse CCache artifacts from different directories,
|
|
set the ``CCACHE_NOHASHDIR`` environment variable to ``true``.
|
|
|
|
A different way of speeding up builds is by using Precompiled Headers (`PCHs`_).
|
|
PCHs drastically reduce parsing times of C and C++ headers by precompiling their symbols,
|
|
which are imported instead of re-parsing the same headers again and again,
|
|
for each compilation unit (.cc file).
|
|
|
|
Note: for ease of use, PCH is enabled by default if supported. It can be manually disabled
|
|
by setting ``NS3_PRECOMPILE_HEADERS`` to ``OFF``.
|
|
|
|
Setting up and adding new headers to the PCH
|
|
++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
When both CCache and PCH are used together, there is a set of settings that must be
|
|
properly configured, otherwise timestamps built into the PCH can invalidate the CCache
|
|
artifacts, forcing a new build of unmodified modules/programs.
|
|
|
|
Compiler settings required by PCH and CCache are set in the PCH block in macros-and-definitions.cmake.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
if(${PRECOMPILE_HEADERS_ENABLED})
|
|
if(CLANG)
|
|
# Clang adds a timestamp to the PCH, which prevents ccache from working
|
|
# correctly
|
|
# https://github.com/ccache/ccache/issues/539#issuecomment-664198545
|
|
add_definitions(-Xclang -fno-pch-timestamp)
|
|
endif()
|
|
|
|
if(${XCODE})
|
|
# XCode is weird and messes up with the PCH, requiring this flag
|
|
# https://github.com/ccache/ccache/issues/156
|
|
add_definitions(-Xclang -fno-validate-pch)
|
|
endif()
|
|
|
|
# Headers that will be compiled into the PCH
|
|
# Only worth for frequently included headers
|
|
set(precompiled_header_libraries
|
|
<algorithm>
|
|
<cstdlib>
|
|
<cstring>
|
|
<exception>
|
|
<fstream>
|
|
<iostream>
|
|
<limits>
|
|
<list>
|
|
<map>
|
|
<math.h>
|
|
<ostream>
|
|
<set>
|
|
<sstream>
|
|
<stdint.h>
|
|
<stdlib.h>
|
|
<string>
|
|
<unordered_map>
|
|
<vector>
|
|
)
|
|
|
|
# PCHs can be reused by similar targets (libraries or executables)
|
|
# We have a PCH for libraries, compiled with the -fPIC flag
|
|
add_library(stdlib_pch OBJECT ${PROJECT_SOURCE_DIR}/build-support/empty.cc)
|
|
target_precompile_headers(
|
|
stdlib_pch PUBLIC "${precompiled_header_libraries}"
|
|
)
|
|
|
|
# And another PCH for executables, compiled with the -fPIE flag
|
|
add_executable(
|
|
stdlib_pch_exec ${PROJECT_SOURCE_DIR}/build-support/empty-main.cc
|
|
)
|
|
target_precompile_headers(
|
|
stdlib_pch_exec PUBLIC "${precompiled_header_libraries}"
|
|
)
|
|
set_runtime_outputdirectory(stdlib_pch_exec ${CMAKE_BINARY_DIR}/ "")
|
|
endif()
|
|
|
|
The CCache settings required to work with PCH are set in the main CMakeLists.txt file:
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
# Use ccache if available
|
|
mark_as_advanced(CCACHE)
|
|
find_program(CCACHE ccache)
|
|
if(NOT ("${CCACHE}" STREQUAL "CCACHE-NOTFOUND"))
|
|
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
|
|
message(STATUS "CCache is enabled.")
|
|
|
|
# Changes user-wide settings from CCache to make it ignore:
|
|
# - PCH definitions,
|
|
# - time related macros that could bake timestamps into cached artifacts,
|
|
# - source file creation and modification timestamps,
|
|
# forcing it to check for content changes instead
|
|
execute_process(
|
|
COMMAND
|
|
${CCACHE} --set-config
|
|
sloppiness=pch_defines,time_macros,include_file_mtime,include_file_ctime
|
|
)
|
|
endif()
|
|
|
|
Note: you can use the following commands to manually check
|
|
and restore the CCache sloppiness settings.
|
|
|
|
.. sourcecode:: console
|
|
|
|
~$ ccache --get-config sloppiness
|
|
include_file_mtime, include_file_ctime, time_macros, pch_defines
|
|
~$ ccache --set-config sloppiness=""
|
|
~$ ccache --get-config sloppiness
|
|
|
|
~$
|
|
|
|
The PCHs can be reused later with one of the following.
|
|
|
|
.. sourcecode:: cmake
|
|
|
|
add_library(example_lib example_lib.cc)
|
|
target_precompile_headers(example_lib REUSE_FROM stdlib_pch)
|
|
|
|
add_executable(example_exec example_exec.cc)
|
|
target_precompile_headers(example_exec REUSE_FROM stdlib_pch_exec)
|
|
|
|
If if you have problems with the build times when the PCH is enabled,
|
|
you can diagnose issues with CCache by clearing the cache statistics (``ccache -z``),
|
|
then cleaning, configuring, building, and finally printing the CCache statistics (``ccache -s``).
|
|
|
|
.. sourcecode:: console
|
|
|
|
ccache -z
|
|
./ns3 clean
|
|
./ns3 configure
|
|
./ns3 build
|
|
ccache -s
|
|
|
|
If you have changed any compiler flag, the cache hit rate should be very low.
|
|
Repeat the same commands once more.
|
|
If the cache hit rate is at 100%, it means everything is working as it should.
|
|
|
|
.. _PCH side-effects:
|
|
|
|
Possible side-effects, fixes and IGNORE_PCH
|
|
+++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Precompiled headers can cause symbol collisions due to includes reordering or unwanted includes,
|
|
which can lead to attempts to redefine functions, macros, types or variables.
|
|
An example of such side-effect is shown below.
|
|
|
|
In order to exemplify how precompiled headers can cause issues, assume the following inclusion order from
|
|
``ns-3-dev/src/aodv/model/aodv-routing-protocol.cc``:
|
|
|
|
.. sourcecode:: cpp
|
|
|
|
...
|
|
#define NS_LOG_APPEND_CONTEXT \
|
|
if (m_ipv4) { std::clog << "[node " << m_ipv4->GetObject<Node> ()->GetId () << "] "; }
|
|
|
|
#include "aodv-routing-protocol.h"
|
|
#include "ns3/log.h"
|
|
...
|
|
|
|
The ``NS_LOG_APPEND_CONTEXT`` macro definition comes before the ``ns3/log.h`` inclusion,
|
|
and that is the expected way of using ``NS_LOG_APPEND_CONTEXT``, since we have the following
|
|
guards on ``ns3/log-macros-enabled.h``, which is included by ``ns3/log.h`` when logs are enabled.
|
|
|
|
.. sourcecode:: cpp
|
|
|
|
...
|
|
#ifndef NS_LOG_APPEND_CONTEXT
|
|
#define NS_LOG_APPEND_CONTEXT
|
|
#endif /* NS_LOG_APPEND_CONTEXT */
|
|
...
|
|
|
|
By adding ``<ns3/log.h>`` to the list of headers to precompile (``precompiled_header_libraries``)
|
|
in ``ns-3-dev/build-support/macros-and-definitions.cmake``, the ``ns3/log.h`` header will
|
|
now be part of the PCH, which gets included before any parsing of the code is done.
|
|
This means the equivalent inclusion order would be different than what was originally intended,
|
|
as shown below:
|
|
|
|
.. sourcecode:: cpp
|
|
|
|
|
|
#include "cmake_pch.hxx" // PCH includes ns3/log.h before defining ``NS_LOG_APPEND_CONTEXT`` below
|
|
...
|
|
#define NS_LOG_APPEND_CONTEXT \
|
|
if (m_ipv4) { std::clog << "[node " << m_ipv4->GetObject<Node> ()->GetId () << "] "; }
|
|
|
|
#include "aodv-routing-protocol.h"
|
|
#include "ns3/log.h" // isn't processed since ``NS3_LOG_H`` was already defined by the PCH
|
|
...
|
|
|
|
While trying to build with the redefined symbols in the debug build, where warnings are treated
|
|
as errors, the build may fail with an error similar to the following from GCC 11.2:
|
|
|
|
.. sourcecode:: console
|
|
|
|
FAILED: src/aodv/CMakeFiles/libaodv-obj.dir/model/aodv-routing-protocol.cc.o
|
|
ccache /usr/bin/c++ ... -DNS3_LOG_ENABLE -Wall -Werror -include /ns-3-dev/cmake-build-debug/CMakeFiles/stdlib_pch.dir/cmake_pch.hxx
|
|
/ns-3-dev/src/aodv/model/aodv-routing-protocol.cc:28: error: "NS_LOG_APPEND_CONTEXT" redefined [-Werror]
|
|
28 | #define NS_LOG_APPEND_CONTEXT \
|
|
|
|
|
In file included from /ns-3-dev/src/core/model/log.h:32,
|
|
from /ns-3-dev/src/core/model/fatal-error.h:29,
|
|
from /ns-3-dev/build/include/ns3/assert.h:56,
|
|
from /ns-3-dev/build/include/ns3/buffer.h:26,
|
|
from /ns-3-dev/build/include/ns3/packet.h:24,
|
|
from /ns-3-dev/cmake-build-debug/CMakeFiles/stdlib_pch.dir/cmake_pch.hxx:23,
|
|
from <command-line>:
|
|
/ns-3-dev/src/core/model/log-macros-enabled.h:146: note: this is the location of the previous definition
|
|
146 | #define NS_LOG_APPEND_CONTEXT
|
|
|
|
|
cc1plus: all warnings being treated as errors
|
|
|
|
One of the ways to fix this issue in particular is undefining ``NS_LOG_APPEND_CONTEXT`` before redefining it in
|
|
``/ns-3-dev/src/aodv/model/aodv-routing-protocol.cc``.
|
|
|
|
.. sourcecode:: cpp
|
|
|
|
#include "cmake_pch.hxx" // PCH includes ns3/log.h before defining NS_LOG_APPEND_CONTEXT below
|
|
...
|
|
#undef NS_LOG_APPEND_CONTEXT // undefines symbol previously defined in the PCH
|
|
#define NS_LOG_APPEND_CONTEXT \
|
|
if (m_ipv4) { std::clog << "[node " << m_ipv4->GetObject<Node> ()->GetId () << "] "; }
|
|
|
|
#include "aodv-routing-protocol.h"
|
|
#include "ns3/log.h" // isn't processed since ``NS3_LOG_H`` was already defined by the PCH
|
|
...
|
|
|
|
If the ``IGNORE_PCH`` option is set in the `build_lib`_, `build_lib_example`_, `build_exec`_ and the `build_example`_
|
|
macros, the PCH is not included in their targets, continuing to build as we normally would without the PCH.
|
|
This is only a workaround for the issue, which can be helpful when the same macro names, class names,
|
|
global variables and others are redefined by different components that can't be modified safely.
|