build: additional CMake and ns3 fixes

Includes:
- add additional find_external_library default search paths
- add option to enable/disable DPDK and make it disable by default
- skip test-ns3.py test cases if dependencies are not installed
- improve searching in library parent directories
- replace FindPython with FindPythonInterp and FindPythonLibs
- stub targets for doxygen/sphinx when dependencies are missing
This commit is contained in:
Gabriel Ferreira
2022-02-09 20:59:27 -03:00
parent 23a802f4b6
commit 4b01479b04
5 changed files with 185 additions and 60 deletions

View File

@@ -125,7 +125,7 @@ macro(write_fakewaf_config)
check_on_or_off("${NS3_DES_METRICS}" "${NS3_DES_METRICS}")
string(APPEND out "DPDK NetDevice : ")
check_on_or_off("ON" "${ENABLE_DPDKDEVNET}")
check_on_or_off("${NS3_DPDK}" "${ENABLE_DPDKDEVNET}")
string(APPEND out "Emulation FdNetDevice : ")
check_on_or_off("${ENABLE_EMU}" "${ENABLE_EMUNETDEV}")

View File

@@ -415,8 +415,18 @@ macro(process_options)
mark_as_advanced(CMAKE_FORMAT_PROGRAM)
find_program(CMAKE_FORMAT_PROGRAM cmake-format HINTS ~/.local/bin)
if(CMAKE_FORMAT_PROGRAM)
file(GLOB_RECURSE MODULES_CMAKE_FILES src/**/CMakeLists.txt contrib/**/CMakeLists.txt examples/**/CMakeLists.txt)
file(GLOB INTERNAL_CMAKE_FILES CMakeLists.txt utils/**/CMakeLists.txt src/CMakeLists.txt buildsupport/**/*.cmake)
file(GLOB_RECURSE MODULES_CMAKE_FILES src/**/CMakeLists.txt
contrib/**/CMakeLists.txt examples/**/CMakeLists.txt
)
file(
GLOB
INTERNAL_CMAKE_FILES
CMakeLists.txt
utils/**/CMakeLists.txt
src/CMakeLists.txt
buildsupport/**/*.cmake
buildsupport/*.cmake
)
add_custom_target(
cmake-format
COMMAND
@@ -490,12 +500,10 @@ macro(process_options)
set(ENABLE_SQLITE False)
if(${NS3_SQLITE})
#find_package(SQLite3 QUIET) # unsupported in CMake 3.10
# We emulate the behavior of find_package below
# find_package(SQLite3 QUIET) # unsupported in CMake 3.10 We emulate the
# behavior of find_package below
find_external_library(
DEPENDENCY_NAME SQLite3
HEADER_NAME sqlite3.h
LIBRARY_NAME sqlite3
DEPENDENCY_NAME SQLite3 HEADER_NAME sqlite3.h LIBRARY_NAME sqlite3
)
if(${SQLite3_FOUND})
@@ -650,17 +658,25 @@ macro(process_options)
endif()
endif()
find_package(Python COMPONENTS Interpreter Development QUIET)
# Check if python3 was found, and mark as not found if python2 is found
if(${Python_FOUND})
if(${Python_VERSION_MAJOR} LESS 3)
set(Python_FOUND FALSE)
message(
STATUS
"Python: an incompatible version of Python was found, python bindings will be disabled"
)
# cmake-format: off
set(Python_ADDITIONAL_VERSIONS 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11)
# cmake-format: on
find_package(PythonInterp)
set(Python_EXECUTABLE)
set(Python_FOUND FALSE)
if(${PythonInterp_FOUND})
set(Python_EXECUTABLE ${PYTHON_EXECUTABLE})
find_package(PythonLibs)
if(${PythonLibs_FOUND})
set(Python_FOUND TRUE)
else()
message(STATUS "Python: development libraries were not found")
endif()
else()
message(
STATUS
"Python: an incompatible version of Python was found, python bindings will be disabled"
)
endif()
set(ENABLE_PYTHON_BINDINGS OFF)
@@ -668,7 +684,7 @@ macro(process_options)
if(NOT ${Python_FOUND})
message(
STATUS
"Bindings: python bindings require Python, but it could not be found"
"Bindings: python bindings require Python, but it could not be found"
)
else()
check_python_packages("pybindgen" missing_packages)
@@ -807,6 +823,26 @@ macro(process_options)
STATUS
"docs: doxygen documentation not enabled due to missing dependencies: ${doxygen_docs_missing_deps}"
)
# cmake-format: off
set(doxygen_missing_msg
echo The following Doxygen dependencies are missing: ${doxygen_docs_missing_deps}.
Reconfigure the project after installing them.
)
# cmake-format: on
# Create stub targets to inform users about missing dependencies
add_custom_target(
run-print-introspected-doxygen COMMAND ${doxygen_missing_msg}
)
add_custom_target(
run-introspected-command-line COMMAND ${doxygen_missing_msg}
)
add_custom_target(
assemble-introspected-command-line COMMAND ${doxygen_missing_msg}
)
add_custom_target(update_doxygen_version COMMAND ${doxygen_missing_msg})
add_custom_target(doxygen COMMAND ${doxygen_missing_msg})
add_custom_target(doxygen-no-build COMMAND ${doxygen_missing_msg})
else()
# We checked this already exists, but we need the path to the executable
find_package(Doxygen QUIET)
@@ -889,13 +925,26 @@ macro(process_options)
STATUS
"docs: sphinx documentation not enabled due to missing dependencies: ${sphinx_docs_missing_deps}"
)
# cmake-format: off
set(sphinx_missing_msg
echo The following Sphinx dependencies are missing: ${sphinx_docs_missing_deps}.
Reconfigure the project after installing them.
)
# cmake-format: on
# Create stub targets to inform users about missing dependencies
add_custom_target(sphinx COMMAND ${sphinx_missing_msg})
add_custom_target(sphinx_manual COMMAND ${sphinx_missing_msg})
add_custom_target(sphinx_models COMMAND ${sphinx_missing_msg})
add_custom_target(sphinx_tutorial COMMAND ${sphinx_missing_msg})
add_custom_target(sphinx_contributing COMMAND ${sphinx_missing_msg})
else()
add_custom_target(sphinx COMMENT "Building sphinx documents")
function(sphinx_target targetname)
add_custom_target(
sphinx_${targetname}
COMMAND make SPHINXOPTS=-N -k html singlehtml latexpdf
sphinx_${targetname} COMMAND make SPHINXOPTS=-N -k html singlehtml
latexpdf
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/doc/${targetname}
)
add_dependencies(sphinx sphinx_${targetname})
@@ -1101,8 +1150,7 @@ macro(process_options)
if(${NS3_NETANIM})
include(FetchContent)
FetchContent_Declare(
netanim
GIT_REPOSITORY https://gitlab.com/nsnam/netanim.git
netanim GIT_REPOSITORY https://gitlab.com/nsnam/netanim.git
GIT_TAG netanim-3.108
)
FetchContent_Populate(netanim)
@@ -1181,7 +1229,9 @@ macro(build_example)
set(options)
set(oneValueArgs NAME)
set(multiValueArgs SOURCE_FILES HEADER_FILES LIBRARIES_TO_LINK)
cmake_parse_arguments("EXAMPLE" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
cmake_parse_arguments(
"EXAMPLE" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}
)
set(missing_dependencies)
foreach(lib ${EXAMPLE_LIBRARIES_TO_LINK})
@@ -1199,7 +1249,9 @@ macro(build_example)
if(NOT missing_dependencies)
# Create shared library with sources and headers
add_executable(${EXAMPLE_NAME} "${EXAMPLE_SOURCE_FILES}" "${EXAMPLE_HEADER_FILES}")
add_executable(
${EXAMPLE_NAME} "${EXAMPLE_SOURCE_FILES}" "${EXAMPLE_HEADER_FILES}"
)
if(${NS3_STATIC})
target_link_libraries(
@@ -1207,7 +1259,8 @@ macro(build_example)
)
elseif(${NS3_MONOLIB})
target_link_libraries(
${EXAMPLE_NAME} ${LIB_AS_NEEDED_PRE} ${lib-ns3-monolib} ${LIB_AS_NEEDED_POST}
${EXAMPLE_NAME} ${LIB_AS_NEEDED_PRE} ${lib-ns3-monolib}
${LIB_AS_NEEDED_POST}
)
else()
# Link the shared library with the libraries passed
@@ -1222,7 +1275,8 @@ macro(build_example)
endif()
set_runtime_outputdirectory(
${EXAMPLE_NAME} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/examples/${examplefolder}/ ""
${EXAMPLE_NAME}
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/examples/${examplefolder}/ ""
)
endif()
endmacro()
@@ -1476,11 +1530,12 @@ function(parse_ns3rc enabled_modules examples_enabled tests_enabled)
endif()
endfunction(parse_ns3rc)
function(find_external_library
)
function(find_external_library)
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})
cmake_parse_arguments(
"FIND_LIB" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}
)
set(name ${FIND_LIB_DEPENDENCY_NAME})
set(library_names "${FIND_LIB_LIBRARY_NAME};${FIND_LIB_LIBRARY_NAMES}")
@@ -1495,28 +1550,50 @@ function(find_external_library
mark_as_advanced(${name}_library_internal_${library})
find_library(
${name}_library_internal_${library} ${library}
HINTS ${search_paths} ENV LD_LIBRARY_PATH
PATH_SUFFIXES /build /lib /build/lib / ${path_suffixes}
HINTS ${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
PATH_SUFFIXES /build /lib /build/lib / /bin ${path_suffixes}
)
if("${${name}_library_internal_${library}}" STREQUAL
"${name}_library_internal_${library}-NOTFOUND"
)
if("${${name}_library_internal_${library}}" STREQUAL "${name}_library_internal_${library}-NOTFOUND")
list(APPEND not_found_libraries ${library})
else()
get_filename_component(${name}_library_dir_internal ${${name}_library_internal_${library}} DIRECTORY
)# e.g. lib/openflow.(so|dll|dylib|a) -> lib
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 ${parent_libdir} ${parent_parent_libdir})
endforeach()
set(not_found_headers)
set(include_dirs)
foreach(header ${header_names})
mark_as_advanced(${name}_header_internal_${header})
string(REPLACE ";" "/../ " upper_dirs "${library_dirs};")
string(REPLACE ";" "/../../ " upper_upper_dirs "${library_dirs};")
find_file(
${name}_header_internal_${header} ${header}
HINTS ${search_paths} ${upper_dirs} ${upper_upper_dirs}
HINTS ${search_paths}
${parent_dirs}
${CMAKE_OUTPUT_DIRECTORY} # Search for headers in ns-3-dev/build
${CMAKE_INSTALL_PREFIX} # Search for headers in the install
# directory (e.g. /usr/)
PATH_SUFFIXES
/build
/include
@@ -1527,13 +1604,17 @@ function(find_external_library
/
${path_suffixes}
)
if("${${name}_header_internal_${header}}" STREQUAL "${name}_header_internal_${header}-NOTFOUND")
if("${${name}_header_internal_${header}}" STREQUAL
"${name}_header_internal_${header}-NOTFOUND"
)
list(APPEND not_found_headers ${header})
else()
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
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()
@@ -1590,8 +1671,7 @@ function(check_python_packages packages missing_packages)
foreach(package ${packages})
execute_process(
COMMAND ${Python_EXECUTABLE} -c "import ${package}"
RESULT_VARIABLE return_code
OUTPUT_QUIET ERROR_QUIET
RESULT_VARIABLE return_code OUTPUT_QUIET ERROR_QUIET
)
if(NOT (${return_code} EQUAL 0))
list(APPEND missing ${package})

2
ns3
View File

@@ -94,6 +94,7 @@ def parse_args(argv):
"(which must call CommandLine::Parse(argc, argv)")
parser_configure = on_off_argument(parser_configure, "build-version",
"embedding git changes as a build version during build")
parser_configure = on_off_argument(parser_configure, "dpdk", "the fd-net-device DPDK features")
parser_configure = on_off_argument(parser_configure, "examples", "the ns-3 examples")
parser_configure = on_off_argument(parser_configure, "gcov", "code coverage analysis")
parser_configure = on_off_argument(parser_configure, "gtk", "GTK support in ConfigStore")
@@ -460,6 +461,7 @@ def configure_cmake(cmake, args, current_cmake_cache_folder, current_cmake_gener
options = (("ASSERT", "asserts"),
("COVERAGE", "gcov"),
("DES_METRICS", "des_metrics"),
("DPDK", "dpdk"),
("ENABLE_BUILD_VERSION", "build_version"),
("ENABLE_SUDO", "sudo"),
("EXAMPLES", "examples"),

View File

@@ -1,3 +1,9 @@
option(
NS3_DPDK
"Enable fd-net-device DPDK features"
OFF
)
set(module_enabled_features) # modulegen_customizations consumes this list
mark_as_advanced(ENABLE_THREADING)
@@ -80,7 +86,8 @@ if(${THREADS_ENABLED}
""
)
if(PKG_CONFIG_FOUND
if(${NS3_DPDK}
AND PKG_CONFIG_FOUND
AND DPDK_FOUND
)
set(ENABLE_DPDKDEVNET

View File

@@ -522,7 +522,8 @@ class NS3ConfigureTestCase(NS3BaseTestCase):
return_code, stdout, stderr = run_ns3("build core-test")
# Then check if they went back to the original list
self.assertGreater(len(stderr), 0)
self.assertEqual(return_code, 1)
self.assertIn("Target to build does not exist: core-test", stdout)
def test_03_EnableModules(self):
"""!
@@ -938,7 +939,8 @@ class NS3BuildBaseTestCase(NS3BaseTestCase):
"""
# tests are not enabled, so the target isn't available
return_code, stdout, stderr = run_ns3("build core-test")
self.assertGreater(len(stderr), 0)
self.assertEqual(return_code, 1)
self.assertIn("Target to build does not exist: core-test", stdout)
def test_03_BuildProject(self):
"""!
@@ -1266,6 +1268,12 @@ class NS3BuildBaseTestCase(NS3BaseTestCase):
@return None
"""
# Skip this test if pybindgen is not available
try:
import pybindgen
except Exception:
self.skipTest("Pybindgen is not available")
# First we enable python bindings
return_code, stdout, stderr = run_ns3("configure --enable-examples --enable-tests --enable-python-bindings")
self.assertEqual(return_code, 0)
@@ -1499,26 +1507,27 @@ class NS3ExpectedUseTestCase(NS3BaseTestCase):
self.assertEqual(return_code, 1)
self.assertIn("Couldn't find the specified program: nonsense", stderr)
def test_08_RunNoBuildGdbAndLldb(self):
def test_08_RunNoBuildGdb(self):
"""!
Test if scratch simulator is executed through gdb and lldb
@return None
"""
if shutil.which("gdb") is None:
self.skipTest("Missing gdb")
return_code, stdout, stderr = run_ns3("run scratch-simulator --gdb --verbose --no-build")
self.assertEqual(return_code, 0)
self.assertIn("scratch-simulator", stdout)
self.assertIn("No debugging symbols found", stdout)
return_code, stdout, stderr = run_ns3("run scratch-simulator --lldb --verbose --no-build")
self.assertEqual(return_code, 0)
self.assertIn("scratch-simulator", stdout)
self.assertIn("(lldb) target create", stdout)
def test_09_RunNoBuildValgrind(self):
"""!
Test if scratch simulator is executed through valgrind
@return None
"""
Test if scratch simulator is executed through valgrind
@return None
"""
if shutil.which("valgrind") is None:
self.skipTest("Missing valgrind")
return_code, stdout, stderr = run_ns3("run scratch-simulator --valgrind --verbose --no-build")
self.assertEqual(return_code, 0)
self.assertIn("scratch-simulator", stderr)
@@ -1529,6 +1538,9 @@ class NS3ExpectedUseTestCase(NS3BaseTestCase):
Test the doxygen target that does trigger a full build
@return None
"""
if shutil.which("doxygen") is None:
self.skipTest("Missing doxygen")
doc_folder = os.path.abspath(os.sep.join([".", "doc"]))
doxygen_files = ["introspected-command-line.h", "introspected-doxygen.h"]
@@ -1552,6 +1564,9 @@ class NS3ExpectedUseTestCase(NS3BaseTestCase):
Test the doxygen target that doesn't trigger a full build
@return None
"""
if shutil.which("doxygen") is None:
self.skipTest("Missing doxygen")
# Rebuilding dot images is super slow, so not removing doxygen products
# doc_folder = os.path.abspath(os.sep.join([".", "doc"]))
# doxygen_build_folder = os.sep.join([doc_folder, "html"])
@@ -1568,6 +1583,9 @@ class NS3ExpectedUseTestCase(NS3BaseTestCase):
Test every individual target for Sphinx-based documentation
@return None
"""
if shutil.which("sphinx-build") is None:
self.skipTest("Missing sphinx")
doc_folder = os.path.abspath(os.sep.join([".", "doc"]))
# First we need to clean old docs, or it will not make any sense.
@@ -1598,6 +1616,11 @@ class NS3ExpectedUseTestCase(NS3BaseTestCase):
both doxygen and sphinx based documentation
@return None
"""
if shutil.which("doxygen") is None:
self.skipTest("Missing doxygen")
if shutil.which("sphinx-build") is None:
self.skipTest("Missing sphinx")
doc_folder = os.path.abspath(os.sep.join([".", "doc"]))
# First we need to clean old docs, or it will not make any sense.
@@ -1639,7 +1662,7 @@ class NS3ExpectedUseTestCase(NS3BaseTestCase):
# Skip test if variable containing sudo password is the default value
if sudo_password is None:
return
self.skipTest("SUDO_PASSWORD environment variable was not specified")
enable_sudo = read_c4che_entry("ENABLE_SUDO")
self.assertFalse(enable_sudo is True)
@@ -1670,7 +1693,7 @@ class NS3ExpectedUseTestCase(NS3BaseTestCase):
prev_fstat.st_uid == 0
if sys.platform == "win32" or likely_fuse_mount:
return
self.skipTest("Windows or likely a FUSE mount")
# If this is a valid platform, we can continue
self.assertEqual(fstat.st_uid, 0) # check the file was correctly chown'ed by root
@@ -1787,6 +1810,19 @@ class NS3ExpectedUseTestCase(NS3BaseTestCase):
self.assertIn("To forward configuration or runtime options, put them after '--'", stderr0)
self.assertIn("To forward configuration or runtime options, put them after '--'", stderr1)
def test_18_RunNoBuildLldb(self):
"""!
Test if scratch simulator is executed through lldb
@return None
"""
if shutil.which("lldb") is None:
self.skipTest("Missing lldb")
return_code, stdout, stderr = run_ns3("run scratch-simulator --lldb --verbose --no-build")
self.assertEqual(return_code, 0)
self.assertIn("scratch-simulator", stdout)
self.assertIn("(lldb) target create", stdout)
if __name__ == '__main__':
loader = unittest.TestLoader()