build: CMake bugfixes

Fixes include:
- Handling of relative paths used as output directories
- Accepting comma separated lists of modules to enable/disable
- Echoing all CMake commands at the end of the execution of ns3 script
- More informative optional features summary output
- Replace absolute paths with relative paths in the printed CMake output for less verbose output
- Set debug build type on the CMake side to ensure ccmake doesn't crash
- Prefix INT64X64 with NS3 to indicate user switch
- Compiler version check
- Removal of verbose option
- Suppress empty enabled/disabled module tables

Features include:
- Test for installation/uninstallation of ns-3 as a CMake package
- Test importing ns-3 as a CMake package and use in a 3rd-party project
- Test to check VERSION usage
- ns3 option to enable/disable sanitizers
- Associate headers to libraries targets
This commit is contained in:
Gabriel Ferreira
2021-12-10 02:13:43 +00:00
parent 0e32b5304b
commit f6ef415392
20 changed files with 730 additions and 265 deletions

View File

@@ -25,6 +25,11 @@ project(NS3 CXX C)
file(STRINGS VERSION NS3_VER)
# minimum compiler versions
set(AppleClang_MinVersion 7.0.0)
set(Clang_MinVersion 3.6.0)
set(GNU_MinVersion 7.0.0)
# common options
option(NS3_ASSERT "Enable assert on failure" OFF)
option(NS3_DES_METRICS "Enable DES Metrics event collection" OFF)

View File

@@ -27,8 +27,8 @@
# Try to find Harfbuzz include and library directories.
#
# After successful discovery, this will set for inclusion where needed:
# HarfBuzz_INCLUDE_DIRS - containg the HarfBuzz headers HarfBuzz_LIBRARIES -
# containg the HarfBuzz library
# HarfBuzz_INCLUDE_DIRS - contains the HarfBuzz headers HarfBuzz_LIBRARIES -
# contains the HarfBuzz library
#[=======================================================================[.rst:
FindHarfBuzz

View File

@@ -15,7 +15,18 @@
#
# Author: Gabriel Ferreira <gabrielcarvfer@gmail.com>
macro(ns3_cmake_package)
function(ns3_cmake_package)
# Only create configuration to export if there is an module configured to be
# built
set(enabled_modules "${ns3-libs};${ns3-contrib-libs}")
if(enabled_modules STREQUAL ";")
message(
STATUS
"No modules were configured, so we cannot create installation artifacts"
)
return()
endif()
install(
EXPORT ns3ExportTargets
NAMESPACE ns3::
@@ -30,8 +41,11 @@ macro(ns3_cmake_package)
PATH_VARS CMAKE_INSTALL_LIBDIR
)
# CMake does not support '-' separated versions in config packages, so replace
# them with dots
string(REPLACE "-" "." ns3_version "${NS3_VER}")
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/ns3ConfigVersion.cmake VERSION ${NS3_VER}
${CMAKE_CURRENT_BINARY_DIR}/ns3ConfigVersion.cmake VERSION ${ns3_version}
COMPATIBILITY ExactVersion
)
@@ -39,29 +53,13 @@ macro(ns3_cmake_package)
"${CMAKE_CURRENT_BINARY_DIR}/ns3ConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ns3
)
install(DIRECTORY ${CMAKE_HEADER_OUTPUT_DIRECTORY}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
# Hack to get the install_manifest.txt file before finishing the installation,
# which we can then install along with ns3 to make uninstallation trivial
set(sep "/")
install(
CODE "string(REPLACE \";\" \"\\n\" MY_CMAKE_INSTALL_MANIFEST_CONTENT \"\$\{CMAKE_INSTALL_MANIFEST_FILES\}\")\n\
string(REPLACE \"/\" \"${sep}\" MY_CMAKE_INSTALL_MANIFEST_CONTENT_FINAL \"\$\{MY_CMAKE_INSTALL_MANIFEST_CONTENT\}\")\n\
file(WRITE manifest.txt \"\$\{MY_CMAKE_INSTALL_MANIFEST_CONTENT_FINAL\}\")"
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.txt
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ns3
)
endmacro()
endfunction()
# You will need administrative privileges to run this
add_custom_target(
uninstall
COMMAND
rm -R `cat ${CMAKE_INSTALL_FULL_LIBDIR}/cmake/ns3/manifest.txt` && rm -R
rm `ls ${CMAKE_INSTALL_FULL_LIBDIR}/libns3*` && rm -R
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/ns3 && rm -R
${CMAKE_INSTALL_FULL_INCLUDEDIR}/ns3
)

View File

@@ -81,6 +81,19 @@ macro(
add_library(ns3::${lib${libname}} ALIAS ${lib${libname}})
# Associate public headers with library for installation purposes
if("${libname}" STREQUAL "core")
set(config_headers ${CMAKE_HEADER_OUTPUT_DIRECTORY}/config-store-config.h
${CMAKE_HEADER_OUTPUT_DIRECTORY}/core-config.h
)
endif()
set_target_properties(
${lib${libname}}
PROPERTIES
PUBLIC_HEADER
"${header_files};${deprecated_header_files};${config_headers};${CMAKE_HEADER_OUTPUT_DIRECTORY}/${libname}-module.h"
)
if(${NS3_CLANG_TIMETRACE})
add_dependencies(timeTraceReport ${lib${libname}})
endif()
@@ -148,7 +161,7 @@ macro(
endif()
# Build tests if requested
if(${TESTS_ENABLED})
if(${ENABLE_TESTS})
list(LENGTH test_sources test_source_len)
if(${test_source_len} GREATER 0)
# Create libname of output library test of module
@@ -188,7 +201,7 @@ macro(
endif()
# Build lib examples if requested
if(${EXAMPLES_ENABLED})
if(${ENABLE_EXAMPLES})
if((EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/examples)
AND (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/examples/CMakeLists.txt)
)
@@ -376,8 +389,9 @@ macro(
install(
TARGETS ${lib${name}}
EXPORT ns3ExportTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/
PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ns3"
)
endmacro()

View File

@@ -59,8 +59,8 @@ function(generate_c4che_cachepy)
cache_cmake_flag(NS3_NSC "NSC_ENABLED" cache_contents) # missing support
cache_cmake_flag(ENABLE_REALTIME "ENABLE_REAL_TIME" cache_contents)
cache_cmake_flag(NS3_PTHREAD "ENABLE_THREADING" cache_contents)
cache_cmake_flag(EXAMPLES_ENABLED "ENABLE_EXAMPLES" cache_contents)
cache_cmake_flag(TESTS_ENABLED "ENABLE_TESTS" cache_contents)
cache_cmake_flag(ENABLE_EXAMPLES "ENABLE_EXAMPLES" cache_contents)
cache_cmake_flag(ENABLE_TESTS "ENABLE_TESTS" cache_contents)
cache_cmake_flag(NS3_OPENFLOW "ENABLE_OPENFLOW" cache_contents)
cache_cmake_flag(NS3_CLICK "NSCLICK" cache_contents)
cache_cmake_flag(NS3_BRITE "ENABLE_BRITE" cache_contents)

View File

@@ -63,10 +63,10 @@ macro(check_on_or_off user_config_switch confirmation_flag)
if(${confirmation_flag})
string(APPEND out "ON\n")
else()
string(APPEND out "OFF (not found)\n")
string(APPEND out "OFF (missing dependency)\n")
endif()
else()
string(APPEND out "OFF\n")
string(APPEND out "OFF (not requested)\n")
endif()
endmacro()
@@ -111,14 +111,18 @@ macro(write_fakewaf_config)
string(APPEND out "BRITE Integration : ")
check_on_or_off("ON" "${NS3_BRITE}")
string(APPEND out "DES Metrics event collection : ${NS3_DES_METRICS}\n")
string(APPEND out "DES Metrics event collection : ")
check_on_or_off("${NS3_DES_METRICS}" "${NS3_DES_METRICS}")
string(APPEND out "DPDK NetDevice : ")
check_on_or_off("ON" "${ENABLE_DPDKDEVNET}")
string(APPEND out "Emulation FdNetDevice : ")
check_on_or_off("${NS3_EMU}" "${ENABLE_EMU}")
check_on_or_off("${ENABLE_EMU}" "${ENABLE_EMUNETDEV}")
string(APPEND out "Examples : ")
check_on_or_off("${ENABLE_EXAMPLES}" "${ENABLE_EXAMPLES}")
string(APPEND out "Examples : ${EXAMPLES_ENABLED}\n")
string(APPEND out "File descriptor NetDevice : ")
check_on_or_off("ON" "${ENABLE_FDNETDEV}")
@@ -141,7 +145,7 @@ macro(write_fakewaf_config)
check_on_or_off("ON" "${NS3_OPENFLOW}")
string(APPEND out "Netmap emulation FdNetDevice : ")
check_on_or_off("${NS3_EMU}" "${ENABLE_NETMAP_EMU}")
check_on_or_off("${ENABLE_EMU}" "${ENABLE_NETMAP_EMU}")
string(
APPEND
@@ -153,21 +157,29 @@ macro(write_fakewaf_config)
out
"PlanetLab FdNetDevice : flag is set to ${NS3_PLANETLAB}, but currently not supported\n"
)
string(APPEND out "PyViz visualizer : ${NS3_VISUALIZER}\n")
string(APPEND out "PyViz visualizer : ")
check_on_or_off("${NS3_VISUALIZER}" "${ENABLE_VISUALIZER}")
# string(APPEND out "Python API Scanning Support : not enabled (castxml too
# old)
string(APPEND out "Python Bindings : ${NS3_PYTHON_BINDINGS}\n")
string(APPEND out "Python Bindings : ")
check_on_or_off("${NS3_PYTHON_BINDINGS}" "${ENABLE_PYTHON_BINDINGS}")
string(APPEND out "Real Time Simulator : ")
check_on_or_off("${NS3_REALTIME}" "${ENABLE_REALTIME}")
string(APPEND out "SQLite stats support : ")
check_on_or_off("${NS3_SQLITE}" "${ENABLE_SQLITE}")
string(APPEND out "Tap Bridge : ${NS3_TAP}\n")
string(APPEND out "Tap FdNetDevice : ")
check_on_or_off("${NS3_TAP}" "${ENABLE_TAP}")
string(APPEND out "Tap Bridge : ")
check_on_or_off("${ENABLE_TAP}" "${ENABLE_TAP}")
string(APPEND out "Tap FdNetDevice : ")
check_on_or_off("${ENABLE_TAP}" "${ENABLE_TAPNETDEV}")
string(APPEND out "Tests : ")
check_on_or_off("${ENABLE_TESTS}" "${ENABLE_TESTS}")
string(APPEND out "Tests : ${TESTS_ENABLED}\n")
string(APPEND out "Threading Primitives : ")
check_on_or_off("${NS3_PTHREAD}" "${THREADS_ENABLED}")
@@ -176,10 +188,16 @@ macro(write_fakewaf_config)
string(APPEND out "\n\n")
set(really-enabled-modules ${ns3-libs};${ns3-contrib-libs})
print_formatted_table_with_modules(
"Modules that can be built" "${really-enabled-modules}" "out"
)
string(APPEND out "\n")
if(${ENABLE_TESTS})
list(APPEND really-enabled-modules libtest) # test is an object library and
# is treated differently
endif()
if(really-enabled-modules)
print_formatted_table_with_modules(
"Modules configured to be built" "${really-enabled-modules}" "out"
)
string(APPEND out "\n")
endif()
set(disabled-modules)
foreach(module ${ns3-all-enabled-modules})
@@ -187,10 +205,13 @@ macro(write_fakewaf_config)
list(APPEND disabled-modules ${module})
endif()
endforeach()
print_formatted_table_with_modules(
"Modules that cannot be built" "${disabled-modules}" "out"
)
string(APPEND out "\n")
if(disabled-modules)
print_formatted_table_with_modules(
"Modules that cannot be built" "${disabled-modules}" "out"
)
string(APPEND out "\n")
endif()
file(WRITE ${PROJECT_BINARY_DIR}/ns3wafconfig.txt ${out})
message(STATUS ${out})

View File

@@ -18,11 +18,11 @@
# Export compile time variable setting the directory to the NS3 root folder
add_definitions(-DPROJECT_SOURCE_PATH="${PROJECT_SOURCE_DIR}")
# Cache options for INT64X64
set(INT64X64 "INT128" CACHE STRING "Int64x64 implementation")
set(INT64X64 "CAIRO" CACHE STRING "Int64x64 implementation")
set(INT64X64 "DOUBLE" CACHE STRING "Int64x64 implementation")
set_property(CACHE INT64X64 PROPERTY STRINGS INT128 CAIRO DOUBLE)
# Cache options for NS3_INT64X64
set(NS3_INT64X64 "INT128" CACHE STRING "Int64x64 implementation")
set(NS3_INT64X64 "CAIRO" CACHE STRING "Int64x64 implementation")
set(NS3_INT64X64 "DOUBLE" CACHE STRING "Int64x64 implementation")
set_property(CACHE NS3_INT64X64 PROPERTY STRINGS INT128 CAIRO DOUBLE)
# WSLv1 doesn't support tap features
if(EXISTS "/proc/version")
@@ -63,36 +63,42 @@ if("${NS3_OUTPUT_DIRECTORY}" STREQUAL "")
set(CMAKE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/build) # default output
# folder
else()
# Check if we can create that directory
if(NOT (EXISTS ${NS3_OUTPUT_DIRECTORY}))
# Check if NS3_OUTPUT_DIRECTORY is a relative path
set(absolute_ns3_output_directory "${NS3_OUTPUT_DIRECTORY}")
if(NOT IS_ABSOLUTE ${NS3_OUTPUT_DIRECTORY})
set(absolute_ns3_output_directory
"${PROJECT_SOURCE_DIR}/${NS3_OUTPUT_DIRECTORY}"
)
endif()
if(NOT (EXISTS ${absolute_ns3_output_directory}))
message(
STATUS
"User-defined output directory \"${NS3_OUTPUT_DIRECTORY}\" doesn't exist. Trying to create it"
)
file(MAKE_DIRECTORY ${NS3_OUTPUT_DIRECTORY})
if(NOT (EXISTS ${NS3_OUTPUT_DIRECTORY}))
file(MAKE_DIRECTORY ${absolute_ns3_output_directory})
if(NOT (EXISTS ${absolute_ns3_output_directory}))
message(
FATAL_ERROR
"User-defined output directory \"${NS3_OUTPUT_DIRECTORY}\" could not be created. "
"User-defined output directory \"${absolute_ns3_output_directory}\" could not be created. "
"Try changing the value of NS3_OUTPUT_DIRECTORY"
)
endif()
# If this directory is not inside the ns-3-dev folder, alert users tests may
# break
if(NOT ("${NS3_OUTPUT_DIRECTORY}" MATCHES "${PROJECT_SOURCE_DIR}"))
if(NOT ("${absolute_ns3_output_directory}" MATCHES "${PROJECT_SOURCE_DIR}"))
message(
WARNING
"User-defined output directory \"${NS3_OUTPUT_DIRECTORY}\" is outside "
"User-defined output directory \"${absolute_ns3_output_directory}\" is outside "
" of the ns-3 directory ${PROJECT_SOURCE_DIR}, which will break some tests"
)
endif()
endif()
message(
STATUS
"User-defined output directory \"${NS3_OUTPUT_DIRECTORY}\" will be used"
"User-defined output directory \"${absolute_ns3_output_directory}\" will be used"
)
set(CMAKE_OUTPUT_DIRECTORY ${NS3_OUTPUT_DIRECTORY})
set(CMAKE_OUTPUT_DIRECTORY ${absolute_ns3_output_directory})
endif()
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/lib)
@@ -125,16 +131,40 @@ endif()
# fPIC (position-independent code) and fPIE (position-independent executable)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# Identify compiler
# Identify compiler and check version
set(below_minimum_msg "compiler is below the minimum required version")
set(CLANG FALSE)
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${AppleClang_MinVersion})
message(
FATAL_ERROR
"Apple Clang ${CMAKE_CXX_COMPILER_VERSION} ${below_minimum_msg} ${AppleClang_MinVersion}"
)
endif()
set(CLANG TRUE)
endif()
if((NOT CLANG) AND ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${Clang_MinVersion})
message(
FATAL_ERROR
"Clang ${CMAKE_CXX_COMPILER_VERSION} ${below_minimum_msg} ${Clang_MinVersion}"
)
endif()
set(CLANG TRUE)
endif()
set(GCC FALSE)
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${GNU_MinVersion})
message(
FATAL_ERROR
"GNU ${CMAKE_CXX_COMPILER_VERSION} ${below_minimum_msg} ${GNU_MinVersion}"
)
endif()
set(GCC TRUE)
endif()
unset(below_minimum_msg)
# Set compiler options and get command to force unused function linkage (useful
# for libraries)
@@ -202,6 +232,11 @@ endmacro()
macro(process_options)
clear_global_cached_variables()
# make sure to default to debug if no build type is specified
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
set(CMAKE_BUILD_TYPE debug)
endif()
# process debug switch Used in build-profile-test-suite
string(TOLOWER ${CMAKE_BUILD_TYPE} cmakeBuildType)
set(build_profile "${cmakeBuildType}" CACHE INTERNAL "")
@@ -220,16 +255,16 @@ macro(process_options)
# Enable examples if activated via command line (NS3_EXAMPLES) or ns3rc config
# file
set(EXAMPLES_ENABLED OFF)
set(ENABLE_EXAMPLES OFF)
if(${NS3_EXAMPLES} OR ${ns3rc_examples_enabled})
set(EXAMPLES_ENABLED ON)
set(ENABLE_EXAMPLES ON)
endif()
# Enable examples if activated via command line (NS3_TESTS) or ns3rc config
# file
set(TESTS_ENABLED OFF)
set(ENABLE_TESTS OFF)
if(${NS3_TESTS} OR ${ns3rc_tests_enabled})
set(TESTS_ENABLED ON)
set(ENABLE_TESTS ON)
endif()
set(profiles_without_suffixes release)
@@ -593,10 +628,12 @@ macro(process_options)
endif()
find_package(Python3 COMPONENTS Interpreter Development)
set(ENABLE_PYTHON_BINDINGS OFF)
if(${NS3_PYTHON_BINDINGS})
if(NOT ${Python3_FOUND})
message(FATAL_ERROR "NS3_PYTHON_BINDINGS requires Python3")
endif()
set(ENABLE_PYTHON_BINDINGS ON)
link_directories(${Python3_LIBRARY_DIRS})
include_directories(${Python3_INCLUDE_DIRS})
set(PYTHONDIR ${Python3_SITELIB})
@@ -615,7 +652,7 @@ macro(process_options)
add_custom_target(apiscan-all)
endif()
if(${NS3_COVERAGE} AND (NOT ${TESTS_ENABLED} OR NOT ${EXAMPLES_ENABLED}))
if(${NS3_COVERAGE} AND (NOT ${ENABLE_TESTS} OR NOT ${ENABLE_EXAMPLES}))
message(
FATAL_ERROR
"Code coverage requires examples and tests.\n"
@@ -623,7 +660,7 @@ macro(process_options)
)
endif()
if(${TESTS_ENABLED})
if(${ENABLE_TESTS})
add_custom_target(all-test-targets)
# Create a custom target to run test.py --nowaf Target is also used to
@@ -634,7 +671,7 @@ macro(process_options)
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS all-test-targets
)
if(${EXAMPLES_ENABLED})
if(${ENABLE_EXAMPLES})
include(buildsupport/custom_modules/ns3_coverage.cmake)
endif()
endif()
@@ -719,8 +756,8 @@ macro(process_options)
COMMAND ${CMAKE_COMMAND} -E env NS_COMMANDLINE_INTROSPECTION=..
${Python3_EXECUTABLE} ./test.py --nowaf --constrain=example
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS all-test-targets # all-test-targets only exists if TESTS_ENABLED
# is set to ON
DEPENDS all-test-targets # all-test-targets only exists if ENABLE_TESTS is
# set to ON
)
file(
@@ -801,7 +838,7 @@ CommandLine configuration in those files instead.
endif()
# Process core-config
if(${INT64X64} MATCHES "INT128")
if(${NS3_INT64X64} MATCHES "INT128")
include(buildsupport/3rd_party/FindInt128.cmake)
find_int128_types()
if(UINT128_FOUND)
@@ -809,15 +846,15 @@ CommandLine configuration in those files instead.
set(INT64X64_USE_128 TRUE)
else()
message(STATUS "Int128 was not found. Falling back to Cairo.")
set(INT64X64 "CAIRO")
set(NS3_INT64X64 "CAIRO")
endif()
endif()
if(${INT64X64} MATCHES "CAIRO")
if(${NS3_INT64X64} MATCHES "CAIRO")
set(INT64X64_USE_CAIRO TRUE)
endif()
if(${INT64X64} MATCHES "DOUBLE")
if(${NS3_INT64X64} MATCHES "DOUBLE")
# WSLv1 has a long double issue that will result in a few tests failing
# https://github.com/microsoft/WSL/issues/830
include(CheckTypeSize)
@@ -829,7 +866,7 @@ CommandLine configuration in those files instead.
STATUS
"Long double has the wrong size: LD ${SIZEOF_LONG_DOUBLE} vs D ${SIZEOF_DOUBLE}. Falling back to CAIRO."
)
set(INT64X64 "CAIRO")
set(NS3_INT64X64 "CAIRO")
else()
set(INT64X64_USE_DOUBLE TRUE)
endif()
@@ -867,18 +904,28 @@ CommandLine configuration in those files instead.
endif()
# Enable examples as tests suites
if(${EXAMPLES_ENABLED})
if(${ENABLE_EXAMPLES})
set(NS3_ENABLE_EXAMPLES "1")
add_definitions(-DNS3_ENABLE_EXAMPLES -DCMAKE_EXAMPLE_AS_TEST)
endif()
set(ENABLE_TAP OFF)
if(${NS3_TAP})
set(ENABLE_TAP ON)
endif()
set(ENABLE_EMU OFF)
if(${NS3_EMU})
set(ENABLE_EMU ON)
endif()
set(PLATFORM_UNSUPPORTED_PRE "Platform doesn't support")
set(PLATFORM_UNSUPPORTED_POST "features. Continuing without them.")
# Remove from libs_to_build all incompatible libraries or the ones that
# dependencies couldn't be installed
if(APPLE OR WSLv1)
set(NS3_TAP OFF)
set(NS3_EMU OFF)
set(ENABLE_TAP OFF)
set(ENABLE_EMU OFF)
list(REMOVE_ITEM libs_to_build fd-net-device)
message(
STATUS
@@ -894,7 +941,7 @@ CommandLine configuration in those files instead.
list(REMOVE_ITEM libs_to_build visualizer)
endif()
if(NOT ${NS3_TAP})
if(NOT ${ENABLE_TAP})
list(REMOVE_ITEM libs_to_build tap-bridge)
endif()
@@ -1021,7 +1068,7 @@ function(set_runtime_outputdirectory target_name output_directory target_prefix)
endforeach(OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES)
endif()
if(${TESTS_ENABLED})
if(${ENABLE_TESTS})
add_dependencies(all-test-targets ${target_prefix}${target_name})
endif()
@@ -1120,7 +1167,7 @@ endmacro()
function(filter_modules modules_to_filter all_modules_list filter_in)
set(new_modules_to_build)
# We are receiveing variable names as arguments, so we have to "dereference"
# We are receiving variable names as arguments, so we have to "dereference"
# them first That is why we have the duplicated ${${}}
foreach(module ${${all_modules_list}})
if(${filter_in} (${module} IN_LIST ${modules_to_filter}))
@@ -1153,6 +1200,8 @@ endfunction()
function(filter_libraries cmakelists_contents libraries)
string(REGEX MATCHALL "{lib[^}]*[^obj]}" matches "${cmakelists_content}")
list(REMOVE_ITEM matches "{libraries_to_link}")
string(REPLACE "{lib\${name" "" matches "${matches}") # special case for
# src/test
string(REPLACE "{lib" "" matches "${matches}")
string(REPLACE "}" "" matches "${matches}")
set(${libraries} ${matches} PARENT_SCOPE)
@@ -1183,20 +1232,20 @@ function(recursive_dependency module_name)
set(contrib_dependency_visited
"${contrib_dependency_visited};${module_name}" CACHE INTERNAL ""
)
set(examples_cmakelist ${contrib_cmakelist})
set(examples_cmakelists ${contrib_cmakelist})
else()
set(dependency_visited "${dependency_visited};${module_name}" CACHE INTERNAL
""
)
set(examples_cmakelist ${src_cmakelist})
set(examples_cmakelists ${src_cmakelist})
endif()
# cmake-format: off
# Scan dependencies required by this module examples
#if(${EXAMPLES_ENABLED})
# string(REPLACE "${module_name}" "${module_name}/examples" examples_cmakelist ${examples_cmakelist})
# if(EXISTS ${examples_cmakelist})
# file(READ ${examples_cmakelist} cmakelists_content)
#if(${ENABLE_EXAMPLES})
# string(REPLACE "${module_name}" "${module_name}/examples" examples_cmakelists ${examples_cmakelists})
# if(EXISTS ${examples_cmakelists})
# file(READ ${examples_cmakelists} cmakelists_content)
# filter_libraries(${cmakelists_content} example_matches)
# endif()
#endif()
@@ -1218,16 +1267,20 @@ macro(filter_enabled_and_disabled_modules libs_to_build contrib_libs_to_build
)
mark_as_advanced(ns3-all-enabled-modules)
# Before filtering, we set a variable with all scanned moduled in the src
# Before filtering, we set a variable with all scanned modules in the src
# directory
set(scanned_modules ${${libs_to_build}})
# Ensure enabled and disable modules lists are using semicolons
string(REPLACE "," ";" ${NS3_ENABLED_MODULES} "${${NS3_ENABLED_MODULES}}")
string(REPLACE "," ";" ${NS3_DISABLED_MODULES} "${${NS3_DISABLED_MODULES}}")
# Now that scanning modules finished, we can remove the disabled modules or
# replace the modules list with the ones in the enabled list
if(${NS3_ENABLED_MODULES} OR ${ns3rc_enabled_modules})
# List of enabled modules passed by the command line overwrites list read
# from ns3rc
if(NS3_ENABLED_MODULES)
if(${NS3_ENABLED_MODULES})
set(ns3rc_enabled_modules ${${NS3_ENABLED_MODULES}})
endif()
@@ -1235,9 +1288,9 @@ macro(filter_enabled_and_disabled_modules libs_to_build contrib_libs_to_build
filter_modules(ns3rc_enabled_modules libs_to_build "")
filter_modules(ns3rc_enabled_modules contrib_libs_to_build "")
# Recursively build a list of module dependendencies
# Use recursion to automatically determine dependencies required by the
# manually enabled modules
foreach(lib ${${contrib_libs_to_build}})
message(WARNING "contrib ${lib}")
resolve_dependencies(${lib} dependencies contrib_dependencies)
list(APPEND ${contrib_libs_to_build} "${contrib_dependencies}")
list(APPEND ${libs_to_build} "${dependencies}")
@@ -1252,15 +1305,51 @@ macro(filter_enabled_and_disabled_modules libs_to_build contrib_libs_to_build
unset(contrib_dependencies)
endforeach()
if(${TESTS_ENABLED})
list(APPEND ${libs_to_build} test) # force enable test module if
# TESTS_ENABLED is enabled
if(core IN_LIST ${libs_to_build})
list(APPEND ${libs_to_build} test) # include test module
endif()
endif()
if(NS3_DISABLED_MODULES)
filter_modules(NS3_DISABLED_MODULES libs_to_build "NOT")
filter_modules(NS3_DISABLED_MODULES contrib_libs_to_build "NOT")
if(${NS3_DISABLED_MODULES})
set(all_libs ${${libs_to_build}};${${contrib_libs_to_build}})
# We then use the recursive dependency finding to get all dependencies of
# all modules
foreach(lib ${all_libs})
resolve_dependencies(${lib} dependencies contrib_dependencies)
set(${lib}_dependencies "${dependencies};${contrib_dependencies}")
unset(dependencies)
unset(contrib_dependencies)
endforeach()
# Now we can begin removing libraries that require disabled dependencies
set(disabled_libs "${${NS3_DISABLED_MODULES}}")
foreach(libo ${all_libs})
foreach(lib ${all_libs})
foreach(disabled_lib ${disabled_libs})
if(${lib} STREQUAL ${disabled_lib})
continue()
endif()
if(${disabled_lib} IN_LIST ${lib}_dependencies)
list(APPEND disabled_libs ${lib})
break() # proceed to the next lib in all_libs
endif()
endforeach()
endforeach()
endforeach()
# Clean dependencies lists
foreach(lib ${all_libs})
unset(${lib}_dependencies)
endforeach()
# We can filter out disabled modules
filter_modules(disabled_libs libs_to_build "NOT")
filter_modules(disabled_libs contrib_libs_to_build "NOT")
if(core IN_LIST ${libs_to_build})
list(APPEND ${libs_to_build} test) # include test module
endif()
endif()
# Older CMake versions require this workaround for empty lists
@@ -1300,7 +1389,7 @@ function(parse_ns3rc enabled_modules examples_enabled tests_enabled)
set(${enabled_modules})
else()
# If modules are listed, remove quotes and replace commas with
# semicollons transforming a string into a cmake list
# semicolons transforming a string into a cmake list
string(REPLACE "," ";" ${enabled_modules} "${${enabled_modules}}")
string(REPLACE "'" "" ${enabled_modules} "${${enabled_modules}}")
string(REPLACE "\"" "" ${enabled_modules} "${${enabled_modules}}")

View File

@@ -1,4 +1,4 @@
if(${EXAMPLES_ENABLED})
if(${ENABLE_EXAMPLES})
subdirlist(examples_to_build ${CMAKE_CURRENT_SOURCE_DIR})
# Process subdirectories

204
ns3
View File

@@ -1,5 +1,6 @@
#! /usr/bin/env python3
import atexit
import argparse
import glob
import os
@@ -8,13 +9,26 @@ import shutil
import subprocess
import sys
from utils import read_config_file
out_dir = ""
build_separator = ""
ns3_path = os.path.dirname(os.path.abspath(__file__))
out_dir = os.sep.join([ns3_path, "build"])
lock_file = os.sep.join([ns3_path, ".lock-waf_%s_build" % sys.platform])
print_buffer = ""
run_only = False
# Prints everything in the print_buffer on exit
def exit_handler(dry_run):
global print_buffer, run_only
# We should not print anything in run_only a.k.a. run-no-build cases, except if dry_run
if not dry_run and run_only:
return
if dry_run:
print("The following commands would be executed:")
elif not run_only:
print("Finished executing the following commands:")
print(print_buffer[1:])
def on_off_argument(parser, option_name, help_on, help_off=None):
parser.add_argument('--enable-%s' % option_name,
@@ -79,6 +93,8 @@ def parse_args(argv):
parser_configure = on_off_argument(parser_configure, "werror", "Treat compiler warnings as errors",
"Treat compiler warnings as warnings")
parser_configure = on_off_argument(parser_configure, "documentation", "documentation targets")
parser_configure = on_off_argument(parser_configure, "sanitizers",
"address, memory leaks and undefined behavior sanitizers")
parser_configure.add_argument('--enable-modules',
help='List of modules to build (e.g. core;network;internet)',
@@ -114,24 +130,43 @@ def parse_args(argv):
help='Force refresh the CMake cache by deleting'
'the cache and reconfiguring the project',
action="store_true", default=None)
parser_configure.add_argument('--prefix',
help='Target output directory to install',
action="store", default=None)
parser_configure.add_argument('--dry-run',
help="Do not execute the commands",
action="store_true", default=None, dest="configure_dry_run")
parser_build = sub_parser.add_parser('build',
help='Accepts a list of targets to build, or builds the entire project if no target is given')
help=('Accepts a list of targets to build,',
' or builds the entire project if no target is given'))
parser_build.add_argument('build',
help='Build the entire project or the specified target and dependencies',
action="store", nargs='*', default=None)
parser_build.add_argument('--dry-run',
help="Do not execute the commands",
action="store_true", default=None, dest="build_dry_run")
parser_clean = sub_parser.add_parser('clean', help='Removes files created by waf and ns3')
parser_clean.add_argument('clean',
nargs="?",
action="store", default=True)
parser_clean.add_argument('--dry-run',
help="Do not execute the commands",
action="store_true", default=None, dest="clean_dry_run")
parser_install = sub_parser.add_parser('install', help='Install ns-3')
parser_install.add_argument('install',
nargs="?",
action="store", default=True)
parser_uninstall = sub_parser.add_parser('uninstall', help='Uninstall ns-3')
parser_uninstall.add_argument('uninstall',
nargs="?",
action="store", default=True)
parser.add_argument('--dry-run',
help="Do not execute the commands",
action="store_true", default=None)
parser.add_argument('-v', '--verbose',
help="Print underlying commands",
action="store_true", default=None)
action="store_true", default=None, dest="dry_run")
parser.add_argument('--check-config',
help='Print the current configuration.',
action="store_true", default=None)
@@ -206,7 +241,14 @@ def parse_args(argv):
# action="store_true", default=None,
# dest="docset_build")
return parser.parse_args(argv)
args = parser.parse_args(argv)
# Merge dry_runs
dry_run_args = [(args.__getattribute__(name) if name in args else None) for name in
["build_dry_run", "clean_dry_run", "configure_dry_run", "dry_run"]]
args.dry_run = dry_run_args.count(True) > 0
return args
def check_build_profile(output_directory):
@@ -226,9 +268,14 @@ def check_build_profile(output_directory):
return build_profile, ns3_version, ns3_modules + ns3_modules_tests if ns3_modules else None
def clean_cmake_artifacts(verbose=False, dry_run=False):
if verbose:
print("rm -R %s" % out_dir)
def print_and_buffer(message):
global print_buffer
# print(message)
print_buffer += "\n" + message
def clean_cmake_artifacts(dry_run=False):
print_and_buffer("rm -R %s" % os.path.relpath(out_dir, ns3_path))
if not dry_run:
shutil.rmtree(out_dir, ignore_errors=True)
@@ -236,15 +283,14 @@ def clean_cmake_artifacts(verbose=False, dry_run=False):
cmake_cache_files = glob.glob("%s/**/CMakeCache.txt" % ns3_path, recursive=True)
for cmake_cache_file in cmake_cache_files:
dirname = os.path.dirname(cmake_cache_file)
if verbose:
print("rm -R %s" % dirname)
print_and_buffer("rm -R %s" % os.path.relpath(dirname, ns3_path))
if not dry_run:
shutil.rmtree(dirname, ignore_errors=True)
if os.path.exists(lock_file):
if verbose:
print("rm %s" % lock_file)
os.remove(lock_file)
print_and_buffer("rm %s" % os.path.relpath(lock_file, ns3_path))
if not dry_run:
os.remove(lock_file)
def search_cmake_cache(build_profile):
@@ -313,8 +359,7 @@ def check_config(current_cmake_cache_folder):
print(f.read())
def configure_cmake(cmake, args, current_cmake_cache_folder, current_cmake_generator, output, verbose=False,
dry_run=False):
def configure_cmake(cmake, args, current_cmake_cache_folder, current_cmake_generator, output, dry_run=False):
# Aggregate all flags to configure CMake
cmake_args = [cmake]
@@ -359,7 +404,8 @@ def configure_cmake(cmake, args, current_cmake_cache_folder, current_cmake_gener
("GTK3", "gtk"),
("WARNINGS", "warnings"),
("WARNINGS_AS_ERRORS", "werror"),
("DOCS", "documentation")
("DOCS", "documentation"),
("SANITIZE", "sanitizers")
)
for (cmake_flag, option_name) in options:
arg = on_off_condition(args, cmake_flag, option_name)
@@ -382,6 +428,9 @@ def configure_cmake(cmake, args, current_cmake_cache_folder, current_cmake_gener
if args.with_openflow is not None:
cmake_args.append("-DNS3_WITH_OPENFLOW=%s" % args.with_openflow)
if args.prefix is not None:
cmake_args.append("-DCMAKE_INSTALL_PREFIX=%s" % args.prefix)
# Build and link visualizer
if args.visualize is not None:
cmake_args.append("-DNS3_VISUALIZER=%s" % on_off(args.visualize))
@@ -393,21 +442,25 @@ def configure_cmake(cmake, args, current_cmake_cache_folder, current_cmake_gener
if args.disable_modules:
cmake_args.append("-DNS3_DISABLED_MODULES=%s" % args.disable_modules)
# Try to set specified generator (will probably fail if there is an old cache)
if args.G:
cmake_args.append("-G%s" % args.G)
# Try to set specified generator (will probably fail if there is an old cache)
if "configure" in args and args.G:
cmake_args.append("-G")
cmake_args.append(args.G)
# Configure cmake
cmake_args.append("..") # for now, assuming the cmake_cache directory is inside the ns-3-dev folder
if "configure" not in args:
if first_run:
print("You need to configure ns-3 first: try ./ns3 configure")
exit(0)
# Configure cmake
cmake_args.append("..") # for now, assuming the cmake_cache directory is inside the ns-3-dev folder
# Echo out the configure command
if verbose:
print("cd %s; %s" % (current_cmake_cache_folder, " ".join(cmake_args)))
print_and_buffer("cd %s; %s ; cd %s" % (os.path.relpath(current_cmake_cache_folder, ns3_path),
" ".join(cmake_args),
os.path.relpath(ns3_path, current_cmake_cache_folder)
)
)
# Run cmake
if not dry_run:
@@ -430,7 +483,7 @@ def get_program_shortcuts(build_profile, ns3_version):
if "pch_exec" in program:
continue
temp_path = program.split(out_dir_name)[-1].split(os.sep)
# Remove version prefix and buildtype suffix from shortcuts (or keep them too?)
# Remove version prefix and build type suffix from shortcuts (or keep them too?)
temp_path[-1] = temp_path[-1].replace("-" + build_profile, "").replace("ns" + ns3_version + "-", "")
# Check if there is a .cc file for that specific program
@@ -450,7 +503,7 @@ def get_program_shortcuts(build_profile, ns3_version):
return ns3_program_map
def cmake_build(current_cmake_cache_folder, output, jobs, target=None, verbose=False, dry_run=False):
def cmake_build(current_cmake_cache_folder, output, jobs, target=None, dry_run=False):
# Check CMake version
cmake = shutil.which("cmake")
if not cmake:
@@ -461,8 +514,12 @@ def cmake_build(current_cmake_cache_folder, output, jobs, target=None, verbose=F
jobs_part = ("-j %d" % jobs) if version >= "3.12.0" else ""
target_part = (" --target %s" % target) if target else ""
cmake_build_command = "cmake --build . %s%s" % (jobs_part, target_part)
if verbose:
print("cd %s; %s" % (current_cmake_cache_folder, cmake_build_command))
print_and_buffer("cd %s; %s ; cd %s" % (os.path.relpath(current_cmake_cache_folder, ns3_path),
cmake_build_command,
os.path.relpath(ns3_path, current_cmake_cache_folder)
)
)
if not dry_run:
subprocess.run(cmake_build_command.split(), cwd=current_cmake_cache_folder, stdout=output)
@@ -474,13 +531,13 @@ def extract_cmakecache_settings(current_cmake_cache_folder):
except FileNotFoundError as e:
raise e
current_settings = re.findall("(NS3_.*):.*=(.*)", contents) # extract NS3 specific settings
current_settings.extend(re.findall("(CMAKE_BUILD_TYPE):.*=(.*)", contents)) # extract buildtype
current_settings.extend(re.findall("(CMAKE_BUILD_TYPE):.*=(.*)", contents)) # extract build type
current_settings.extend(re.findall("(CMAKE_GENERATOR):.*=(.*)", contents)) # extract generator
return dict(current_settings)
def reconfigure_cmake_to_force_refresh(cmake, current_cmake_cache_folder, output, verbose=False, dry_run=False):
def reconfigure_cmake_to_force_refresh(cmake, current_cmake_cache_folder, output, dry_run=False):
import json
settings_bak_file = "settings.json"
@@ -492,8 +549,8 @@ def reconfigure_cmake_to_force_refresh(cmake, current_cmake_cache_folder, output
settings = json.load(f)
# Delete cache folder and then recreate it
if verbose:
print("rm -R %s; mkdir %s" % current_cmake_cache_folder * 2)
cache_path = os.path.relpath(current_cmake_cache_folder, ns3_path)
print_and_buffer("rm -R %s; mkdir %s" % (cache_path, cache_path))
if not dry_run:
shutil.rmtree(current_cmake_cache_folder)
os.mkdir(current_cmake_cache_folder)
@@ -510,8 +567,11 @@ def reconfigure_cmake_to_force_refresh(cmake, current_cmake_cache_folder, output
cmake_args.append("..")
# Echo out the configure command
if verbose:
print("cd %s; %s" % (current_cmake_cache_folder, " ".join(cmake_args)))
print_and_buffer("cd %s; %s ; cd %s" % (os.path.relpath(ns3_path, current_cmake_cache_folder),
" ".join(cmake_args),
os.path.relpath(current_cmake_cache_folder, ns3_path)
)
)
# Call cmake
if not dry_run:
@@ -538,7 +598,7 @@ def get_target_to_build(program_path, ns3_version, build_profile):
return program_name.split("/")[-1]
def configuration_step(current_cmake_cache_folder, current_cmake_generator, args, run_only, configure_and_run,
def configuration_step(current_cmake_cache_folder, current_cmake_generator, args, configure_and_run,
output, dry_run=False):
# There are a few cases where we want to reconfigure/refresh the cmake cache
# MANUALLY (does require ./ns3 configure)
@@ -552,8 +612,7 @@ def configuration_step(current_cmake_cache_folder, current_cmake_generator, args
if not current_cmake_cache_folder:
current_cmake_cache_folder = os.sep.join([ns3_path, "cmake_cache"])
if not os.path.exists(current_cmake_cache_folder):
if args.verbose:
print("mkdir %s" % current_cmake_cache_folder)
print_and_buffer("mkdir %s" % os.path.relpath(current_cmake_cache_folder, ns3_path))
if not dry_run:
os.mkdir(current_cmake_cache_folder)
current_cmake_cache_folder = os.path.abspath(current_cmake_cache_folder)
@@ -567,7 +626,7 @@ def configuration_step(current_cmake_cache_folder, current_cmake_generator, args
# If --force-refresh, we load settings from the CMakeCache, delete it, then reconfigure CMake to
# force refresh cached packages/libraries that were installed/removed, without losing the current settings
if "configure" in args and args.force_refresh:
reconfigure_cmake_to_force_refresh(cmake, current_cmake_cache_folder, output, args.verbose, dry_run)
reconfigure_cmake_to_force_refresh(cmake, current_cmake_cache_folder, output, dry_run)
exit(0)
# Call cmake to configure/reconfigure/refresh the project
@@ -576,7 +635,6 @@ def configuration_step(current_cmake_cache_folder, current_cmake_generator, args
current_cmake_cache_folder,
current_cmake_generator,
output,
args.verbose,
dry_run
)
@@ -601,7 +659,6 @@ def build_step(args,
cmake_build(current_cmake_cache_folder,
jobs=args.jobs,
output=output,
verbose=args.verbose,
dry_run=args.dry_run
)
if "build" in args:
@@ -610,7 +667,7 @@ def build_step(args,
# If we are building specific targets, we build them one by one
if "build" in args:
non_executable_targets = ["doxygen", "doxygen-no-build"]
non_executable_targets = ["doxygen", "doxygen-no-build", "install", "uninstall"]
# Build targets in the list
for target in args.build:
if target in ns3_modules:
@@ -621,7 +678,6 @@ def build_step(args,
jobs=args.jobs,
target=target,
output=output,
verbose=args.verbose,
dry_run=args.dry_run)
# We can also exit earlier in this case
exit(0)
@@ -632,34 +688,29 @@ def build_step(args,
jobs=args.jobs,
target=get_target_to_build(target_to_run, ns3_version, build_profile),
output=output,
verbose=args.verbose,
dry_run=args.dry_run
)
def main():
global out_dir
global out_dir, run_only
# Parse arguments
args = parse_args(sys.argv[1:])
atexit.register(exit_handler, dry_run=args.dry_run)
output = subprocess.DEVNULL if args.no_task_lines else None
# no arguments were passed, so can't possibly be reconfiguring anything, then we refresh and rebuild
if len(sys.argv) == 1:
args.build = []
# If Dry-run, do not execute the commands, only print the underlying commands from --verbose
if args.dry_run:
args.verbose = True
# Read contents from lock (output directory is important)
if os.path.exists(lock_file):
exec(open(lock_file).read(), globals())
else:
out_dir = os.sep.join([ns3_path, "build"])
# Clean project if needed
if "clean" in args and args.clean:
clean_cmake_artifacts(verbose=args.verbose, dry_run=args.dry_run)
clean_cmake_artifacts(dry_run=args.dry_run)
# We end things earlier when cleaning
return
@@ -671,6 +722,12 @@ def main():
if args.doxygen_no_build:
args.build = ['doxygen-no-build']
# Installation and uninstallation options become cmake targets
if "install" in args:
args.build = ['install']
if 'uninstall' in args:
args.build = ['uninstall']
# Get build profile
build_profile, ns3_version, ns3_modules = check_build_profile(out_dir)
@@ -678,6 +735,8 @@ def main():
run_only = args.run_no_build or args.pyrun_no_build
configure_and_run = args.run or args.pyrun
target_to_run = None
target_args = []
current_cmake_cache_folder = None
if not args.check and (run_only or configure_and_run):
target_to_run = max(args.run_no_build, args.pyrun_no_build, args.run, args.pyrun)
if len(target_to_run) > 0:
@@ -702,7 +761,6 @@ def main():
first_run, current_cmake_cache_folder = configuration_step(current_cmake_cache_folder,
current_cmake_generator,
args,
run_only,
configure_and_run,
output,
args.dry_run
@@ -749,7 +807,7 @@ def main():
# we can just check if the out_dir matches the base folder from the target_to_run
if out_dir.split(os.sep)[-1] == target_to_run.split(os.sep)[0]:
target_to_run = target_to_run.replace(out_dir.split(os.sep)[-1] + os.sep, "")
# Waf doesn't add version prefix and build type suffix to scratches, so we remove them
# Waf doesn't add version prefix and build type suffix to the scratches, so we remove them
if "scratch" in target_to_run and run_only:
target_to_run = target_to_run.replace(os.path.basename(target_to_run), run_only)
target_to_run = os.sep.join([out_dir, target_to_run])
@@ -760,15 +818,13 @@ def main():
# Finally, we try to run it
if args.check or run_only or configure_and_run:
path_sep = ";" if os.name == "nt" else ":"
paths_to_add = path_sep.join([out_dir,
"%s/lib" % out_dir,
"%s/bindings/python" % out_dir,
])
proc_env = {"PATH": os.getenv("PATH") + path_sep + paths_to_add,
"LD_LIBRARY_PATH": paths_to_add,
"PYTHON_PATH": paths_to_add,
libdir = "%s/lib" % out_dir
path_sep = ";" if sys.platform == "win32" else ":"
proc_env = {"PATH": os.getenv("PATH") + path_sep + libdir,
"PYTHON_PATH": "%s/bindings/python" % out_dir,
}
if sys.platform != "win32":
proc_env["LD_LIBRARY_PATH"] = libdir
# running from ns-3-dev (ns3_path) or cwd
working_dir = args.cwd if args.cwd else ns3_path
@@ -799,18 +855,24 @@ def main():
program_arguments = [*debugging_software, target_to_run, *target_args]
if args.verbose:
if not run_only or args.dry_run:
exported_variables = "export "
for item in proc_env.items():
exported_variables += "=".join(item) + " "
print("cd %s; %s; %s" % (working_dir, exported_variables, " ".join(program_arguments)))
for (variable, value) in proc_env.items():
if variable == "PATH":
value = "$PATH" + path_sep + libdir
exported_variables += "%s=%s " % (variable, value)
print_and_buffer("cd %s; %s; %s" % (os.path.relpath(ns3_path, working_dir),
exported_variables,
" ".join(program_arguments)
)
)
if not args.dry_run:
try:
subprocess.run(program_arguments, env=proc_env, cwd=working_dir)
except KeyboardInterrupt:
print("Process was interrupted by the user")
return 0
return
main()

View File

@@ -42,12 +42,12 @@ set(osclock_sources model/unix-system-wall-clock-ms.cc)
set(int64x64_sources)
set(int64x64_headers)
if(${INT64X64} MATCHES "INT128")
if(${NS3_INT64X64} MATCHES "INT128")
set(int64x64_sources model/int64x64-128.cc)
set(int64x64_headers model/int64x64-128.h)
elseif(${INT64X64} MATCHES "DOUBLE")
elseif(${NS3_INT64X64} MATCHES "DOUBLE")
set(int64x64_headers model/int64x64-double.h)
elseif(${INT64X64} MATCHES "CAIRO")
elseif(${NS3_INT64X64} MATCHES "CAIRO")
set(int64x64_sources model/int64x64-cairo.cc)
set(int64x64_headers model/int64x64-cairo.h model/cairo-wideint-private.h)
endif()
@@ -73,7 +73,7 @@ if(${NS3_PTHREAD})
endif()
endif()
if(${EXAMPLES_ENABLED})
if(${ENABLE_EXAMPLES})
set(example_as_test_sources model/example-as-test.cc)
set(example_as_test_headers model/example-as-test.h)
set(example_as_test_suite test/examples-as-tests-test-suite.cc)

View File

@@ -16,12 +16,13 @@ if(PKG_CONFIG_FOUND)
endif()
mark_as_advanced(
ENABLE_FDNETDEV ENABLE_DPDKDEVNET ENABLE_TAP ENABLE_EMU ENABLE_NETMAP_EMU
ENABLE_FDNETDEV ENABLE_DPDKDEVNET ENABLE_TAPNETDEV ENABLE_EMUNETDEV
ENABLE_NETMAP_EMU
)
set(ENABLE_FDNETDEV False CACHE INTERNAL "")
set(ENABLE_DPDKDEVNET False CACHE INTERNAL "")
set(ENABLE_TAP False CACHE INTERNAL "")
set(ENABLE_EMU False CACHE INTERNAL "")
set(ENABLE_TAPNETDEV False CACHE INTERNAL "")
set(ENABLE_EMUNETDEV False CACHE INTERNAL "")
set(ENABLE_NETMAP_EMU False CACHE INTERNAL "")
if(${THREADS_ENABLED} AND HAVE_NET_ETHERNET_H)
@@ -34,17 +35,17 @@ if(${THREADS_ENABLED} AND HAVE_NET_ETHERNET_H)
if(HAVE_IF_NETS_H
AND HAVE_IF_TUN_H
AND HAVE_SYS_IOCTL_H
AND ${NS3_TAP}
AND ${ENABLE_TAP}
)
set(ENABLE_TAP True CACHE INTERNAL "")
set(ENABLE_TAPNETDEV True CACHE INTERNAL "")
endif()
if(HAVE_IF_NETS_H
AND HAVE_PACKET_H
AND HAVE_SYS_IOCTL_H
AND ${NS3_EMU}
AND ${ENABLE_EMU}
)
set(ENABLE_EMU True CACHE INTERNAL "")
set(ENABLE_EMUNETDEV True CACHE INTERNAL "")
add_definitions(-DHAVE_PACKET_H)
endif()
@@ -57,7 +58,7 @@ endif()
if(${ENABLE_FDNETDEV})
set(fd-net-device_creators)
if(${ENABLE_EMU})
if(${ENABLE_EMUNETDEV})
set(emu_sources helper/emu-fd-net-device-helper.cc)
set(emu_headers helper/emu-fd-net-device-helper.h)
@@ -75,7 +76,7 @@ if(${ENABLE_FDNETDEV})
list(APPEND fd-net-device_creators raw-sock-creator)
endif()
if(${ENABLE_TAP})
if(${ENABLE_TAPNETDEV})
set(tap_sources helper/tap-fd-net-device-helper.cc)
set(tap_headers helper/tap-fd-net-device-helper.h)
@@ -94,7 +95,7 @@ if(${ENABLE_FDNETDEV})
list(APPEND fd-net-device_creators tap-device-creator)
endif()
if(${ENABLE_TAP} AND ${NS3_PLANETLAB}) # todo: find planetlab libraries
if(${ENABLE_TAPNETDEV} AND ${NS3_PLANETLAB}) # todo: find planetlab libraries
set(planetlab_sources helper/planetlab-fd-net-device-helper.cc)
set(planetlab_headers helper/planetlab-fd-net-device-helper.h)

View File

@@ -1,6 +1,6 @@
set(name lte)
if(${NS3_EMU})
if(${ENABLE_EMU})
set(emu_sources helper/emu-epc-helper.cc)
set(emu_headers helper/emu-epc-helper.h)
set(emu_libraries ${libfd-net-device})

View File

@@ -166,7 +166,7 @@ build_lib_example(
"${name}" "${source_files}" "${header_files}" "${libraries_to_link}"
)
if(${NS3_EMU})
if(${ENABLE_EMU})
set(name lena-simple-epc-emu)
set(source_files ${name}.cc)
set(header_files)

View File

@@ -21,7 +21,7 @@ set(header_files model/mpi-interface.h model/mpi-receiver.h
set(libraries_to_link ${libcore} ${libnetwork} ${MPI_CXX_LIBRARIES})
set(example_as_test_suite)
if(${EXAMPLES_ENABLED})
if(${ENABLE_EXAMPLES})
set(example_as_test_suite test/mpi-test-suite.cc)
endif()

View File

@@ -11,7 +11,7 @@ set(header_files helper/sixlowpan-helper.h model/sixlowpan-header.h
set(libraries_to_link ${libinternet} ${libinternet} ${libcore})
set(example_as_test_suite)
if(${EXAMPLES_ENABLED})
if(${ENABLE_EXAMPLES})
set(example_as_test_suite test/sixlowpan-examples-test-suite.cc)
endif()

View File

@@ -1,4 +1,4 @@
if(${NS3_TAP})
if(${ENABLE_TAP})
set(name tap-csma)
set(source_files ${name}.cc)
set(header_files)

View File

@@ -1,4 +1,4 @@
if(${TESTS_ENABLED} AND (test IN_LIST libs_to_build))
if(${ENABLE_TESTS} AND (test IN_LIST libs_to_build))
add_executable(test-runner $<TARGET_OBJECTS:${libtest}> test-runner.cc)
if(${NS3_MONOLIB})

View File

@@ -29,7 +29,7 @@ set(libraries_to_link
${{libcore}}
)
if(${{EXAMPLES_ENABLED}})
if(${{ENABLE_EXAMPLES}})
set(examples_as_tests_sources
#test/{MODULE}-examples-test-suite.cc
)

View File

@@ -108,8 +108,8 @@ per-commit-compile-optimized:
- mkdir -p $CCACHE_BASEDIR_VALUE
- export CCACHE_BASEDIR=${PWD}
- export CCACHE_DIR=${PWD}/$CCACHE_BASEDIR_VALUE
- ./ns3 --verbose configure -d $MODE -GNinja --enable-examples --enable-tests
- ./ns3 --verbose build
- ./ns3 configure -d $MODE -GNinja --enable-examples --enable-tests
- ./ns3 build
- ./test.py --nowaf
cache:
paths:

View File

@@ -18,31 +18,31 @@
#
# Author: Gabriel Ferreira <gabrielcarvfer@gmail.com>
import itertools
import glob
import os
import re
import shutil
from functools import partial
import subprocess
import sys
import unittest
import os
import subprocess
import re
import glob
from functools import partial
# Get path containing ns3
ns3_path = os.path.dirname(os.path.abspath(os.sep.join([__file__, "../../"])))
ns3_script = os.sep.join([ns3_path, "ns3"])
ns3rc_script = os.sep.join([ns3_path, ".ns3rc"])
build_status_script = os.sep.join([ns3_path, "build", "build-status.py"])
c4che_script = os.sep.join([ns3_path, "build", "c4che", "_cache.py"])
usual_outdir = os.sep.join([ns3_path, "build"])
usual_build_status_script = os.sep.join([usual_outdir, "build-status.py"])
usual_c4che_script = os.sep.join([usual_outdir, "c4che", "_cache.py"])
usual_lib_outdir = os.sep.join([usual_outdir, "lib"])
# Move the current working directory to the ns-3-dev/utils/tests folder
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# Cmake commands
cmake_refresh_cache_command = "cd {ns3_path}/cmake_cache; /usr/bin/cmake ..".format(ns3_path=ns3_path)
cmake_build_project_command = "cd {ns3_path}/cmake_cache; cmake --build . -j".format(ns3_path=ns3_path)
cmake_build_target_command = partial("cd {ns3_path}/cmake_cache; cmake --build . -j {jobs} --target {target}".format,
ns3_path=ns3_path,
cmake_refresh_cache_command = "/usr/bin/cmake ..".format(ns3_path=ns3_path)
cmake_build_project_command = "cmake --build . -j".format(ns3_path=ns3_path)
cmake_build_target_command = partial("cmake --build . -j {jobs} --target {target}".format,
jobs=max(1, os.cpu_count() - 1)
)
@@ -52,7 +52,7 @@ def run_ns3(args):
# Adapted from https://github.com/metabrainz/picard/blob/master/picard/util/__init__.py
def run_program(program, args, python=False):
def run_program(program, args, python=False, cwd=ns3_path):
if type(args) != str:
raise Exception("args should be a string")
@@ -71,24 +71,31 @@ def run_program(program, args, python=False):
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
timeout=None,
cwd=ns3_path # run process from the ns-3-dev path
cwd=cwd # run process from the ns-3-dev path
)
# Return (error code, stdout and stderr)
return ret.returncode, ret.stdout.decode(sys.stdout.encoding), ret.stderr.decode(sys.stderr.encoding)
def get_programs_list():
def get_programs_list(build_status_script_path=usual_build_status_script):
values = {}
with open(build_status_script) as f:
with open(build_status_script_path) as f:
exec(f.read(), globals(), values)
return values["ns3_runnable_programs"]
def read_c4che_entry(entry):
def get_libraries_list(lib_outdir=usual_lib_outdir):
return glob.glob(lib_outdir + '/*', recursive=True)
def get_headers_list(outdir=usual_outdir):
return glob.glob(outdir + '/**/*.h', recursive=True)
def read_c4che_entry(entry, c4cache_script_path=usual_c4che_script):
values = {}
with open(c4che_script) as f:
with open(c4cache_script_path) as f:
exec(f.read(), globals(), values)
return values[entry]
@@ -119,13 +126,13 @@ class NS3RunWafTargets(unittest.TestCase):
def test_01_loadExecutables(self):
# Check if build-status.py exists, then read to get list of executables
self.assertTrue(os.path.exists(build_status_script))
self.assertTrue(os.path.exists(usual_build_status_script))
self.ns3_executables = get_programs_list()
self.assertGreater(len(self.ns3_executables), 0)
def test_02_loadModules(self):
# Check if c4che.py exists than read to get the list of enabled modules
self.assertTrue(os.path.exists(c4che_script))
self.assertTrue(os.path.exists(usual_c4che_script))
self.ns3_modules = get_enabled_modules()
self.assertGreater(len(self.ns3_modules), 0)
@@ -161,17 +168,12 @@ class NS3CommonSettingsTestCase(unittest.TestCase):
self.assertEqual(return_code, 0)
self.assertIn("You need to configure ns-3 first: try ./ns3 configure", stdout)
def test_02_Verbose(self):
return_code, stdout, stderr = run_ns3("--verbose")
self.assertEqual(return_code, 0)
self.assertIn("You need to configure ns-3 first: try ./ns3 configure", stdout)
def test_03_NoTaskLines(self):
def test_02_NoTaskLines(self):
return_code, stdout, stderr = run_ns3("--no-task-lines")
self.assertEqual(return_code, 0)
self.assertIn("You need to configure ns-3 first: try ./ns3 configure", stdout)
def test_04_CheckConfig(self):
def test_03_CheckConfig(self):
return_code, stdout, stderr = run_ns3("--check-config")
self.assertEqual(return_code, 1)
self.assertIn("Project was not configured", stderr)
@@ -194,9 +196,9 @@ class NS3ConfigureBuildProfileTestCase(unittest.TestCase):
self.assertEqual(return_code, 0)
self.assertIn("Built target libcore", stdout)
libcore = glob.glob(os.sep.join([ns3_path, "build", "lib"]) + '/*', recursive=True)
self.assertGreater(len(libcore), 0)
self.assertIn("core-debug", libcore[0])
libraries = get_libraries_list()
self.assertGreater(len(libraries), 0)
self.assertIn("core-debug", libraries[0])
def test_02_Release(self):
return_code, stdout, stderr = run_ns3("configure -d release")
@@ -215,9 +217,9 @@ class NS3ConfigureBuildProfileTestCase(unittest.TestCase):
self.assertEqual(return_code, 0)
self.assertIn("Built target libcore", stdout)
libcore = glob.glob(os.sep.join([ns3_path, "build", "lib"]) + '/*', recursive=True)
self.assertGreater(len(libcore), 0)
self.assertIn("core-optimized", libcore[0])
libraries = get_libraries_list()
self.assertGreater(len(libraries), 0)
self.assertIn("core-optimized", libraries[0])
def test_04_Typo(self):
return_code, stdout, stderr = run_ns3("configure -d Optimized")
@@ -253,11 +255,11 @@ class NS3BaseTestCase(unittest.TestCase):
self.config_ok(return_code, stdout)
# Check if build-status.py exists, then read to get list of executables
self.assertTrue(os.path.exists(build_status_script))
self.assertTrue(os.path.exists(usual_build_status_script))
self.ns3_executables = get_programs_list()
# Check if c4che.py exists than read to get the list of enabled modules
self.assertTrue(os.path.exists(c4che_script))
self.assertTrue(os.path.exists(usual_c4che_script))
self.ns3_modules = get_enabled_modules()
@@ -347,7 +349,43 @@ class NS3ConfigureTestCase(NS3BaseTestCase):
# At this point we should have the same amount of modules that we had when we started
self.assertEqual(len(get_enabled_modules()), len(self.ns3_modules))
def test_05_Ns3rc(self):
def test_05_EnableModulesComma(self):
# Try filtering enabled modules to network+wifi and their dependencies
return_code, stdout, stderr = run_ns3("configure --enable-modules='network,wifi'")
self.config_ok(return_code, stdout)
# At this point we should have fewer modules
enabled_modules = get_enabled_modules()
self.assertLess(len(get_enabled_modules()), len(self.ns3_modules))
self.assertIn("ns3-network", enabled_modules)
self.assertIn("ns3-wifi", enabled_modules)
# Try cleaning the list of enabled modules to reset to the normal configuration
return_code, stdout, stderr = run_ns3("configure --enable-modules=''")
self.config_ok(return_code, stdout)
# At this point we should have the same amount of modules that we had when we started
self.assertEqual(len(get_enabled_modules()), len(self.ns3_modules))
def test_06_DisableModulesComma(self):
# Try filtering disabled modules to disable lte and modules that depend on it
return_code, stdout, stderr = run_ns3("configure --disable-modules='lte,mpi'")
self.config_ok(return_code, stdout)
# At this point we should have fewer modules
enabled_modules = get_enabled_modules()
self.assertLess(len(enabled_modules), len(self.ns3_modules))
self.assertNotIn("ns3-lte", enabled_modules)
self.assertNotIn("ns3-mpi", enabled_modules)
# Try cleaning the list of enabled modules to reset to the normal configuration
return_code, stdout, stderr = run_ns3("configure --disable-modules=''")
self.config_ok(return_code, stdout)
# At this point we should have the same amount of modules that we had when we started
self.assertEqual(len(get_enabled_modules()), len(self.ns3_modules))
def test_07_Ns3rc(self):
ns3rc_template = "# ! /usr/bin/env python\
\
# A list of the modules that will be enabled when ns-3 is run.\
@@ -376,7 +414,7 @@ class NS3ConfigureTestCase(NS3BaseTestCase):
self.assertLess(len(get_enabled_modules()), len(self.ns3_modules))
self.assertIn("ns3-lte", enabled_modules)
self.assertTrue(get_test_enabled())
self.assertEqual(len(get_programs_list()) + 1, len(self.ns3_executables)) # the +1 is due to test-runner
self.assertEqual(len(get_programs_list()), len(self.ns3_executables))
# Replace the ns3rc file
with open(ns3rc_script, "w") as f:
@@ -405,9 +443,55 @@ class NS3ConfigureTestCase(NS3BaseTestCase):
self.assertFalse(get_test_enabled())
self.assertEqual(len(get_programs_list()), len(self.ns3_executables))
def test_06_DryRun(self):
# todo: collect commands from CMake and test them
pass
def test_08_DryRun(self):
run_ns3("clean")
# Try dry-run before and after the positional commands (outputs should match)
for positional_command in ["configure", "build", "clean"]:
return_code, stdout, stderr = run_ns3("--dry-run %s" % positional_command)
return_code1, stdout1, stderr1 = run_ns3("%s --dry-run" % positional_command)
self.assertEqual(return_code, return_code1)
self.assertEqual(stdout, stdout1)
self.assertEqual(stderr, stderr1)
# Build target before using below
run_ns3("configure -d release")
run_ns3("build scratch-simulator")
# Run all cases and then check outputs
return_code0, stdout0, stderr0 = run_ns3("--dry-run --run scratch-simulator")
return_code1, stdout1, stderr1 = run_ns3("--run scratch-simulator")
return_code2, stdout2, stderr2 = run_ns3("--dry-run --run-no-build scratch-simulator")
return_code3, stdout3, stderr3 = run_ns3("--run-no-build scratch-simulator")
# Return code and stderr should be the same for all of them
self.assertEqual(sum([return_code0, return_code1, return_code2, return_code3]), 0)
self.assertEqual([stderr0, stderr1, stderr2, stderr3], [""]*4)
scratch_path = None
for program in get_programs_list():
if "scratch-simulator" in program and "subdir" not in program:
scratch_path = program
break
# Scratches currently have a 'scratch_' prefix in their CMake targets
# Case 0: dry-run + run (should print commands to refresh cache, build target and then run)
self.assertIn(cmake_refresh_cache_command, stdout0)
self.assertIn(cmake_build_target_command(target="scratch_scratch-simulator"), stdout0)
self.assertIn(scratch_path, stdout0)
# Case 1: run (should print all the commands of case 1 plus CMake output from build)
self.assertIn(cmake_refresh_cache_command, stdout1)
self.assertIn(cmake_build_target_command(target="scratch_scratch-simulator"), stdout1)
self.assertIn("Built target", stdout1)
self.assertIn(scratch_path, stdout1)
# Case 2: dry-run + run-no-build (should print commands to run the target)
self.assertIn(scratch_path, stdout2)
# Case 3: run-no-build (should print the target output only)
self.assertEqual("", stdout3)
class NS3BuildBaseTestCase(NS3BaseTestCase):
@@ -419,6 +503,8 @@ class NS3BuildBaseTestCase(NS3BaseTestCase):
NS3BaseTestCase.cleaned_once = False
super().setUp()
self.ns3_libraries = get_libraries_list()
def test_01_BuildExistingTargets(self):
return_code, stdout, stderr = run_ns3("build core")
self.assertEqual(return_code, 0)
@@ -435,28 +521,219 @@ class NS3BuildBaseTestCase(NS3BaseTestCase):
self.assertIn("Built target", stdout)
for program in get_programs_list():
self.assertTrue(os.path.exists(program))
def test_04_BuildProjectVerbose(self):
return_code, stdout, stderr = run_ns3("--verbose build")
self.assertEqual(return_code, 0)
self.assertIn("Built target", stdout)
for program in get_programs_list():
self.assertTrue(os.path.exists(program))
self.assertIn(cmake_refresh_cache_command, stdout)
self.assertIn(cmake_build_project_command, stdout)
def test_05_BuildProjectNoTaskLines(self):
def test_04_BuildProjectNoTaskLines(self):
return_code, stdout, stderr = run_ns3("--no-task-lines build")
self.assertEqual(return_code, 0)
self.assertEqual(len(stderr), 0)
self.assertEqual(len(stdout), 0)
def test_06_BuildProjectVerboseAndNoTaskLines(self):
return_code, stdout, stderr = run_ns3("--no-task-lines --verbose build")
self.assertEqual(return_code, 0)
self.assertIn(cmake_refresh_cache_command, stdout)
self.assertIn(cmake_build_project_command, stdout)
def test_05_TestVersionFile(self):
version_file = os.sep.join([ns3_path, "VERSION"])
with open(version_file, "w") as f:
f.write("3-00\n")
# Reconfigure
return_code, stdout, stderr = run_ns3("configure")
self.config_ok(return_code, stdout)
# Build
return_code, stdout, stderr = run_ns3("build")
self.assertEqual(return_code, 0)
self.assertIn("Built target", stdout)
# Programs with new versions
new_programs = get_programs_list()
# Check if they exist
for program in new_programs:
self.assertTrue(os.path.exists(program))
# Check if we still have the same number of binaries
self.assertEqual(len(new_programs), len(self.ns3_executables))
# Check if versions changed from 3-dev to 3-00
libraries = get_libraries_list()
new_libraries = list(set(libraries).difference(set(self.ns3_libraries)))
self.assertEqual(len(new_libraries), len(self.ns3_libraries))
for library in new_libraries:
self.assertNotIn("libns3-dev", library)
self.assertIn("libns3-00", library)
self.assertTrue(os.path.exists(library))
# Restore version file
with open(version_file, "w") as f:
f.write("3-dev\n")
# Reset flag to let it clean the build
NS3BuildBaseTestCase.cleaned_once = False
def test_06_OutputDirectory(self):
# Re-build to return to the original state
return_code, stdout, stderr = run_ns3("build")
self.config_ok(return_code, stdout)
self.ns3_libraries = get_libraries_list()
self.ns3_executables = get_programs_list()
# Delete built programs and libraries to check if they were restored later
for program in self.ns3_executables:
os.remove(program)
for library in self.ns3_libraries:
os.remove(library)
# Reconfigure setting the output folder to ns-3-dev/build/release (both as an absolute path or relative)
absolute_path = os.sep.join([ns3_path, "build", "release"])
relative_path = os.sep.join(["build", "release"])
for different_out_dir in [absolute_path, relative_path]:
return_code, stdout, stderr = run_ns3("configure --out=%s" % different_out_dir)
self.config_ok(return_code, stdout)
self.assertIn("Build directory : %s" % absolute_path, stdout)
# Build
return_code, stdout, stderr = run_ns3("build")
self.config_ok(return_code, stdout)
# Check if we have the same number of binaries and that they were built correctly
new_programs = get_programs_list(os.sep.join([absolute_path, "build-status.py"]))
self.assertEqual(len(new_programs), len(self.ns3_executables))
for program in new_programs:
self.assertTrue(os.path.exists(program))
# Check if we have the same number of libraries and that they were built correctly
libraries = get_libraries_list(os.sep.join([absolute_path, "lib"]))
new_libraries = list(set(libraries).difference(set(self.ns3_libraries)))
self.assertEqual(len(new_libraries), len(self.ns3_libraries))
for library in new_libraries:
self.assertTrue(os.path.exists(library))
# Remove files in the different output dir
shutil.rmtree(absolute_path)
# Restore original output directory
return_code, stdout, stderr = run_ns3("configure --out=''")
self.config_ok(return_code, stdout)
self.assertIn("Build directory : %s" % usual_outdir, stdout)
# Try re-building
return_code, stdout, stderr = run_ns3("build")
self.config_ok(return_code, stdout)
# Check if we have the same binaries we had at the beginning
new_programs = get_programs_list()
self.assertEqual(len(new_programs), len(self.ns3_executables))
for program in new_programs:
self.assertTrue(os.path.exists(program))
# Check if we have the same libraries we had at the beginning
libraries = get_libraries_list()
self.assertEqual(len(libraries), len(self.ns3_libraries))
for library in libraries:
self.assertTrue(os.path.exists(library))
def test_07_InstallationAndUninstallation(self):
# Remove existing libraries from the previous step
libraries = get_libraries_list()
for library in libraries:
os.remove(library)
# 3-dev version format is not supported by CMake, so we use 3.01
version_file = os.sep.join([ns3_path, "VERSION"])
with open(version_file, "w") as f:
f.write("3-01\n")
# Reconfigure setting the installation folder to ns-3-dev/build/install
install_prefix = os.sep.join([ns3_path, "build", "install"])
return_code, stdout, stderr = run_ns3("configure --prefix=%s" % install_prefix)
self.config_ok(return_code, stdout)
# Build
return_code, stdout, stderr = run_ns3("build")
self.config_ok(return_code, stdout)
libraries = get_libraries_list()
headers = get_headers_list()
# Install
return_code, stdout, stderr = run_ns3("install")
self.config_ok(return_code, stdout)
# Find out if libraries were installed to lib or lib64 (Fedora thing)
lib64 = os.path.exists(os.sep.join([install_prefix, "lib64"]))
installed_libdir = os.sep.join([install_prefix, ("lib64" if lib64 else "lib")])
# Make sure all libraries were installed
installed_libraries = get_libraries_list(installed_libdir)
installed_libraries_list = ";".join(installed_libraries)
for library in libraries:
library_name = os.path.basename(library)
self.assertIn(library_name, installed_libraries_list)
# Make sure all headers were installed
installed_headers = get_headers_list(install_prefix)
missing_headers = list(set([os.path.basename(x) for x in headers])
- (set([os.path.basename(x) for x in installed_headers]))
)
self.assertEqual(len(missing_headers), 0)
# Now create a test CMake project and try to find_package ns-3
test_main_file = os.sep.join([install_prefix, "main.cpp"])
with open(test_main_file, "w") as f:
f.write("# include <iostream>\nint main() { return 0; }\n")
# We try to use this library without specifying a version,
# specifying ns3-01 (text version with 'dev' is not supported)
# and specifying ns3-00 (a wrong version)
for version in ["", "3.01", "3.00"]:
test_cmake_project = """
cmake_minimum_required(VERSION 3.10..3.10)
project(ns3_consumer CXX)
list(APPEND CMAKE_PREFIX_PATH ./{lib}/cmake/ns3)
find_package(ns3 {version} COMPONENTS libcore)
add_executable(test main.cpp)
target_link_libraries(test PRIVATE ns3::libcore)
""".format(lib=("lib64" if lib64 else "lib"), version=version)
test_cmake_project_file = os.sep.join([install_prefix, "CMakeLists.txt"])
with open(test_cmake_project_file, "w") as f:
f.write(test_cmake_project)
# Configure the test project and build
cmake = shutil.which("cmake")
return_code, stdout, stderr = run_program(cmake,
"-DCMAKE_BUILD_TYPE=debug .",
cwd=install_prefix)
if version == "3.00":
self.assertEqual(return_code, 1)
self.assertIn('Could not find a configuration file for package "ns3" that is compatible',
stderr.replace("\n", ""))
else:
self.assertEqual(return_code, 0)
self.assertIn("Build files", stdout)
return_code, stdout, stderr = run_program("cmake", "--build .", cwd=install_prefix)
if version == "3.00":
self.assertEqual(return_code, 2)
self.assertIn("cannot", stderr)
else:
self.assertEqual(return_code, 0)
self.assertIn("Built target", stdout)
# Uninstall
return_code, stdout, stderr = run_ns3("uninstall")
self.config_ok(return_code, stdout)
self.assertIn("Built target uninstall", stdout)
# Restore 3-dev version file
with open(version_file, "w") as f:
f.write("3-dev\n")
# Reset flag to let it clean the build
NS3BuildBaseTestCase.cleaned_once = False
class NS3ExpectedUseTestCase(NS3BaseTestCase):
cleaned_once = False
@@ -472,27 +749,27 @@ class NS3ExpectedUseTestCase(NS3BaseTestCase):
self.config_ok(return_code, stdout)
# Check if build-status.py exists, then read to get list of executables
self.assertTrue(os.path.exists(build_status_script))
self.assertTrue(os.path.exists(usual_build_status_script))
self.ns3_executables = get_programs_list()
# Check if c4che.py exists than read to get the list of enabled modules
self.assertTrue(os.path.exists(c4che_script))
self.assertTrue(os.path.exists(usual_c4che_script))
self.ns3_modules = get_enabled_modules()
def test_01_BuildProjectVerbose(self):
return_code, stdout, stderr = run_ns3("--verbose build")
def test_01_BuildProject(self):
return_code, stdout, stderr = run_ns3("build")
self.assertEqual(return_code, 0)
self.assertIn("Built target", stdout)
for program in get_programs_list():
self.assertTrue(os.path.exists(program))
libraries = ";".join(glob.glob(os.sep.join([ns3_path, "build", "lib"]) + '/*', recursive=True))
libraries = get_libraries_list()
for module in get_enabled_modules():
self.assertIn(module.replace("ns3-", ""), libraries)
self.assertIn(module.replace("ns3-", ""), ";".join(libraries))
self.assertIn(cmake_refresh_cache_command, stdout)
self.assertIn(cmake_build_project_command, stdout)
def test_02_BuildAndRunExistingExecutableTarget(self):
return_code, stdout, stderr = run_ns3('--verbose --run "test-runner --list"')
return_code, stdout, stderr = run_ns3('--run "test-runner --list"')
self.assertEqual(return_code, 0)
self.assertIn("Built target test-runner", stdout)
self.assertIn(cmake_refresh_cache_command, stdout)
@@ -509,21 +786,19 @@ class NS3ExpectedUseTestCase(NS3BaseTestCase):
self.assertIn("Couldn't find the specified program: nonsense", stderr)
def test_05_RunNoBuildExistingExecutableTarget(self):
return_code, stdout, stderr = run_ns3('--verbose --run-no-build "test-runner --list"')
return_code, stdout, stderr = run_ns3('--run-no-build "test-runner --list"')
self.assertEqual(return_code, 0)
self.assertNotIn("Built target test-runner", stdout)
self.assertNotIn(cmake_refresh_cache_command, stdout)
self.assertNotIn(cmake_build_target_command(target="test-runner"), stdout)
self.assertIn("test-runner", stdout)
self.assertIn("--list", stdout)
def test_06_RunNoBuildExistingLibraryTarget(self):
return_code, stdout, stderr = run_ns3("--verbose --run-no-build core") # this should not work
return_code, stdout, stderr = run_ns3("--run-no-build core") # this should not work
self.assertEqual(return_code, 1)
self.assertIn("Couldn't find the specified program: core", stderr)
def test_07_RunNoBuildNonExistingExecutableTarget(self):
return_code, stdout, stderr = run_ns3("--verbose --run-no-build nonsense") # this should not work
return_code, stdout, stderr = run_ns3("--run-no-build nonsense") # this should not work
self.assertEqual(return_code, 1)
self.assertIn("Couldn't find the specified program: nonsense", stderr)
@@ -540,14 +815,14 @@ class NS3ExpectedUseTestCase(NS3BaseTestCase):
self.assertIn("Memcheck", stderr)
def test_10_DoxygenWithBuild(self):
return_code, stdout, stderr = run_ns3("--verbose --doxygen")
return_code, stdout, stderr = run_ns3("--doxygen")
self.assertEqual(return_code, 0)
self.assertIn(cmake_refresh_cache_command, stdout)
self.assertIn(cmake_build_target_command(target="doxygen"), stdout)
self.assertIn("Built target doxygen", stdout)
def test_11_DoxygenWithoutBuild(self):
return_code, stdout, stderr = run_ns3("--verbose --doxygen-no-build")
return_code, stdout, stderr = run_ns3("--doxygen-no-build")
self.assertEqual(return_code, 0)
self.assertIn(cmake_refresh_cache_command, stdout)
self.assertIn(cmake_build_target_command(target="doxygen-no-build"), stdout)