bindings, build: fix bindings and visualizer build

Includes:
- scan python scripts
- run python scripts from ns3
- replace visualizer file copy with configure_file to prevent cmake refreshes
- replace ns__init__.py file copy with configure_file to prevent cmake refreshes
- fix bindings scanning with cmake
- pass include directories to modulegen for castxml consumption
- add missing parameters of Recv in python-unit-tests.py
- change apiscan targets from apiscan-module to libmodule-apiscan
- change bindings targets from module-bingings to libmodule-bindings
- scanning and bindings build tests
- scan scratch python scripts
- replace FindPython3 with FindPython to be compatible with CMake 3.10
- do not export private visual-simulator-impl.h
- do not export udp-socket-impl.h
- use .so suffix for bindings on Mac instead of .dylib
This commit is contained in:
Gabriel Ferreira
2022-01-26 01:53:28 -03:00
parent 192019ce94
commit 9342082c53
16 changed files with 330 additions and 102 deletions

View File

@@ -87,7 +87,7 @@ option(NS3_STATIC "Build a static ns-3 library and link it against executables"
OFF OFF
) )
option(NS3_VERBOSE "Print additional build system messages" OFF) option(NS3_VERBOSE "Print additional build system messages" OFF)
option(NS3_VISUALIZER "Build visualizer module" OFF) option(NS3_VISUALIZER "Build visualizer module" ON)
option(NS3_WARNINGS "Enable compiler warnings" ON) option(NS3_WARNINGS "Enable compiler warnings" ON)
option(NS3_WARNINGS_AS_ERRORS option(NS3_WARNINGS_AS_ERRORS
"Treat warnings as errors. Requires NS3_WARNINGS=ON" ON "Treat warnings as errors. Requires NS3_WARNINGS=ON" ON
@@ -144,6 +144,7 @@ generate_c4che_cachepy()
generate_buildstatus() generate_buildstatus()
generate_fakewaflock() generate_fakewaflock()
write_fakewaf_config() write_fakewaf_config()
write_header_to_modules_map()
# Export package targets when installing # Export package targets when installing
ns3_cmake_package() ns3_cmake_package()

View File

@@ -232,7 +232,7 @@ def ns3_module_scan(top_builddir, module_name, headers_map, output_file_name, cf
#module_parser.add_post_scan_hook(post_scan_hook) #module_parser.add_post_scan_hook(post_scan_hook)
castxml_options = dict( castxml_options = dict(
include_paths=[top_builddir], include_paths=[top_builddir, os.path.join(top_builddir, "include")],
define_symbols={ define_symbols={
#'NS3_ASSERT_ENABLE': None, #'NS3_ASSERT_ENABLE': None,
#'NS3_LOG_ENABLE': None, #'NS3_LOG_ENABLE': None,
@@ -256,6 +256,8 @@ def ns3_module_scan(top_builddir, module_name, headers_map, output_file_name, cf
scan_header = os.path.join(os.path.dirname(output_file_name), "scan-header.h") scan_header = os.path.join(os.path.dirname(output_file_name), "scan-header.h")
if not os.path.exists(scan_header): if not os.path.exists(scan_header):
scan_header = os.path.join(top_builddir, "ns3", "%s-module.h" % module_name) scan_header = os.path.join(top_builddir, "ns3", "%s-module.h" % module_name)
if not os.path.exists(scan_header):
scan_header = os.path.join(top_builddir, "include", "ns3", "%s-module.h" % module_name)
module_parser.parse_init([scan_header], module_parser.parse_init([scan_header],
None, whitelist_paths=[top_builddir], None, whitelist_paths=[top_builddir],
@@ -280,5 +282,11 @@ if __name__ == '__main__':
if len(sys.argv) != 6: if len(sys.argv) != 6:
print("ns3modulescan-modular.py top_builddir module_path module_headers output_file_name cflags") print("ns3modulescan-modular.py top_builddir module_path module_headers output_file_name cflags")
sys.exit(1) sys.exit(1)
ns3_module_scan(sys.argv[1], sys.argv[2], eval(sys.argv[3]), sys.argv[4], sys.argv[5]) if os.path.exists(sys.argv[3]):
import json
with open(sys.argv[3], "r") as f:
module_headers = json.load(f)
else:
module_headers = eval(sys.argv[3])
ns3_module_scan(sys.argv[1], sys.argv[2], module_headers, sys.argv[4], sys.argv[5])
sys.exit(0) sys.exit(0)

View File

@@ -30,7 +30,7 @@
# Hidden argument (this is not a function, so you don't really need to pass arguments explicitly) # Hidden argument (this is not a function, so you don't really need to pass arguments explicitly)
# deprecated_header_files = "list;of;deprecated;.h;files", copy won't get triggered if deprecated_header_files isn't set # deprecated_header_files = "list;of;deprecated;.h;files", copy won't get triggered if deprecated_header_files isn't set
# ignore_pch = TRUE or FALSE, prevents the PCH from including undesired system libraries (e.g. custom GLIBC for DCE) # ignore_pch = TRUE or FALSE, prevents the PCH from including undesired system libraries (e.g. custom GLIBC for DCE)
# module_enabled_features = "list;of;enabled;features;for;this;module" (used by fd-net-device)
macro( macro(
build_lib_impl build_lib_impl
@@ -42,6 +42,7 @@ macro(
test_sources test_sources
#deprecated_header_files #deprecated_header_files
#ignore_pch #ignore_pch
#module_enabled_features
) )
# cmake-format: on # cmake-format: on
@@ -215,24 +216,23 @@ macro(
# Build lib examples if requested # Build lib examples if requested
if(${ENABLE_EXAMPLES}) if(${ENABLE_EXAMPLES})
if((EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/examples) foreach(example_folder example;examples)
AND (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/examples/CMakeLists.txt) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${example_folder})
) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${example_folder}/CMakeLists.txt)
add_subdirectory(examples) add_subdirectory(${example_folder})
endif() endif()
if((EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/example) scan_python_examples(${CMAKE_CURRENT_SOURCE_DIR}/${example_folder})
AND (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/example/CMakeLists.txt) endif()
) endforeach()
add_subdirectory(example)
endif()
endif() endif()
# Get architecture pair for python bindings # Get architecture pair for python bindings
set(arch gcc_ILP32) if((${CMAKE_SIZEOF_VOID_P} EQUAL 8) AND (NOT APPLE))
set(arch_flag)
if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
set(arch gcc_LP64) set(arch gcc_LP64)
set(arch_flag -m64) set(arch_flags -m64)
else()
set(arch gcc_ILP32)
set(arch_flags)
endif() endif()
# Add target to scan python bindings # Add target to scan python bindings
@@ -243,49 +243,63 @@ macro(
${PROJECT_SOURCE_DIR}/${folder}/${libname}/bindings ${PROJECT_SOURCE_DIR}/${folder}/${libname}/bindings
) )
file(MAKE_DIRECTORY ${bindings_output_folder}) file(MAKE_DIRECTORY ${bindings_output_folder})
set(module_api ${bindings_output_folder}/modulegen__${arch}.py) set(module_api_ILP32 ${bindings_output_folder}/modulegen__gcc_ILP32.py)
set(module_api_LP64 ${bindings_output_folder}/modulegen__gcc_LP64.py)
set(modulescan_modular_command set(modulescan_modular_command
${Python3_EXECUTABLE} ${Python_EXECUTABLE}
${PROJECT_SOURCE_DIR}/bindings/python/ns3modulescan-modular.py ${PROJECT_SOURCE_DIR}/bindings/python/ns3modulescan-modular.py
) )
# To build the header map for the module, we start by copying the headers set(header_map "")
# and prepending the dictionary start # We need a python map that takes header.h to module e.g. "ptr.h": "core"
set(header_map "{\\\"${header_files};") foreach(header ${header_files})
# header is a relative path to the current working directory
get_filename_component(
header_name ${CMAKE_CURRENT_SOURCE_DIR}/${header} NAME
)
string(APPEND header_map "\"${header_name}\":\"${libname}\",")
endforeach()
# We then remove model/ helper/ prefixes e.g. set(ns3-headers-to-module-map "${ns3-headers-to-module-map}${header_map}"
# {'model/angles.h;model/antenna-model.h;... -> CACHE INTERNAL "Map connecting headers to their modules"
# {'angles.h;antenna-model.h;...)
string(REPLACE "model/" "" header_map "${header_map}")
string(REPLACE "helper/" "" header_map "${header_map}")
# Now we replace list entry separators (;) with ending of the string quote
# ("), followed by the relative module e.g.
# {"model/angles.h;model/antenna-model.h;... -> {"angles.h" : "antenna",
# "antenna-model.h": "antenna", "...)
string(REPLACE ";" "\\\": \\\"${libname}\\\", \\\"" header_map
"${header_map}"
) )
# We now remove the last character ("), which needs to be replaced with a # API scan needs the include directories to find a few headers (e.g. mpi.h)
# (}), to close the dictionary e.g. "antenna-model.h" : "antenna", " -> get_target_property(include_dirs ${lib${libname}} INCLUDE_DIRECTORIES)
# "antenna-model.h" : "antenna" set(modulegen_include_dirs)
string(LENGTH "${header_map}" header_map_len) foreach(include_dir ${include_dirs})
math(EXPR header_map_len "${header_map_len}-3") if(include_dir MATCHES "<")
string(SUBSTRING "${header_map}" 0 ${header_map_len} header_map) # Skip CMake build and install interface includes
continue()
else()
# Append the include directory to a list
set(modulegen_include_dirs ${modulegen_include_dirs} -I${include_dir})
endif()
endforeach()
# Append end of dictionary (}) set(module_to_generate_api ${module_api_ILP32})
string(APPEND header_map "}") set(LP64toILP32)
if("${arch}" STREQUAL "gcc_LP64")
set(module_to_generate_api ${module_api_LP64})
set(LP64toILP32
${Python_EXECUTABLE}
${PROJECT_SOURCE_DIR}/buildsupport/pybindings_LP64_to_ILP32.py
${module_api_LP64} ${module_api_ILP32}
)
endif()
add_custom_target( add_custom_target(
apiscan-${lib${libname}} ${lib${libname}}-apiscan
COMMAND ${modulescan_modular_command} ${CMAKE_OUTPUT_DIRECTORY} ${libname} COMMAND
${header_map} ${module_api} ${arch_flag} ${modulescan_modular_command} ${CMAKE_OUTPUT_DIRECTORY} ${libname}
${PROJECT_BINARY_DIR}/header_map.json ${module_to_generate_api}
\"${arch_flags} ${modulegen_include_dirs}\"
COMMAND ${LP64toILP32}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${lib${libname}} DEPENDS ${lib${libname}}
) )
add_dependencies(apiscan-all apiscan-${lib${libname}}) add_dependencies(apiscan-all ${lib${libname}}-apiscan)
endif() endif()
# Build pybindings if requested and if bindings subfolder exists in # Build pybindings if requested and if bindings subfolder exists in
@@ -316,10 +330,12 @@ macro(
if((NOT EXISTS ${module_hdr}) OR (NOT EXISTS ${module_src})) # OR TRUE) # to if((NOT EXISTS ${module_hdr}) OR (NOT EXISTS ${module_src})) # OR TRUE) # to
# force # force
# reprocessing # reprocessing
string(REPLACE ";" "," ENABLED_FEATURES "${ns3-libs}") string(REPLACE ";" "," ENABLED_FEATURES
"${ns3-libs};${module_enabled_features}"
)
set(modulegen_modular_command set(modulegen_modular_command
GCC_RTTI_ABI_COMPLETE=True NS3_ENABLED_FEATURES="${ENABLED_FEATURES}" GCC_RTTI_ABI_COMPLETE=True NS3_ENABLED_FEATURES="${ENABLED_FEATURES}"
${Python3_EXECUTABLE} ${Python_EXECUTABLE}
${PROJECT_SOURCE_DIR}/bindings/python/ns3modulegen-modular.py ${PROJECT_SOURCE_DIR}/bindings/python/ns3modulegen-modular.py
) )
execute_process( execute_process(
@@ -354,11 +370,12 @@ macro(
${CMAKE_CURRENT_SOURCE_DIR}/bindings/scan-header.h ${CMAKE_CURRENT_SOURCE_DIR}/bindings/scan-header.h
) )
endif() endif()
add_library(${libname}-bindings SHARED "${python_module_files}") set(bindings-name lib${libname}-bindings)
add_library(${bindings-name} SHARED "${python_module_files}")
target_include_directories( target_include_directories(
${libname}-bindings PUBLIC ${Python3_INCLUDE_DIRS} ${bindings-name} PUBLIC ${Python_INCLUDE_DIRS} ${bindings_output_folder}
${bindings_output_folder}
) )
target_compile_options(${bindings-name} PRIVATE -Wno-error)
# If there is any, remove the "lib" prefix of libraries (search for # If there is any, remove the "lib" prefix of libraries (search for
# "set(lib${libname}") # "set(lib${libname}")
@@ -367,35 +384,45 @@ macro(
string(REPLACE ";" "-bindings;" bindings_to_link string(REPLACE ";" "-bindings;" bindings_to_link
"${ns_libraries_to_link};" "${ns_libraries_to_link};"
) # add -bindings suffix to all lib${name} ) # add -bindings suffix to all lib${name}
string(REPLACE "lib" "" bindings_to_link "${bindings_to_link}"
)# remove lib prefix from all lib${name}-bindings
endif() endif()
target_link_libraries( target_link_libraries(
${libname}-bindings ${bindings-name}
PUBLIC ${LIB_AS_NEEDED_PRE} ${lib${libname}} "${bindings_to_link}" PUBLIC ${LIB_AS_NEEDED_PRE} ${lib${libname}} "${bindings_to_link}"
"${libraries_to_link}" ${LIB_AS_NEEDED_POST} "${libraries_to_link}" ${LIB_AS_NEEDED_POST}
PRIVATE ${Python_LIBRARIES}
) )
target_include_directories( target_include_directories(
${libname}-bindings PRIVATE ${PROJECT_SOURCE_DIR}/src/core/bindings ${bindings-name} PRIVATE ${PROJECT_SOURCE_DIR}/src/core/bindings
) )
set(suffix)
if(APPLE)
# Python doesn't like Apple's .dylib and will refuse to load bindings
# unless its an .so
set(suffix SUFFIX .so)
endif()
# Set binding library name and output folder # Set binding library name and output folder
set_target_properties( set_target_properties(
${libname}-bindings ${bindings-name}
PROPERTIES OUTPUT_NAME ${prefix}${libname_sub} PREFIX "" PROPERTIES OUTPUT_NAME ${prefix}${libname_sub}
PREFIX ""
${suffix}
LIBRARY_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY
${CMAKE_OUTPUT_DIRECTORY}/bindings/python/ns ${CMAKE_OUTPUT_DIRECTORY}/bindings/python/ns
) )
set(ns3-python-bindings-modules set(ns3-python-bindings-modules
"${libname}-bindings;${ns3-python-bindings-modules}" "${bindings-name};${ns3-python-bindings-modules}"
CACHE INTERNAL "list of modules python bindings" CACHE INTERNAL "list of modules python bindings"
) )
# Make sure all bindings are built before building the visualizer module # Make sure all bindings are built before building the visualizer module
# that makes use of them # that makes use of them
if(NOT (${name} STREQUAL visualizer)) if(${ENABLE_VISUALIZER})
add_dependencies(${libvisualizer} ${libname}-bindings) if(NOT (${name} STREQUAL visualizer))
add_dependencies(${libvisualizer} ${bindings-name})
endif()
endif() endif()
endif() endif()

View File

@@ -18,15 +18,17 @@
function(generate_buildstatus) function(generate_buildstatus)
# Build build-status.py file consumed by test.py # Build build-status.py file consumed by test.py
set(buildstatus_contents "#! /usr/bin/env python3\n\n") set(buildstatus_contents "#! /usr/bin/env python3\n\n")
string(APPEND buildstatus_contents "ns3_runnable_programs = [")
string(APPEND buildstatus_contents "ns3_runnable_programs = [")
foreach(executable ${ns3-execs}) foreach(executable ${ns3-execs})
string(APPEND buildstatus_contents "'${executable}', ") string(APPEND buildstatus_contents "'${executable}',\n")
endforeach() endforeach()
string(APPEND buildstatus_contents "]\n\n") string(APPEND buildstatus_contents "]\n\n")
string(APPEND buildstatus_contents "ns3_runnable_scripts = [") # missing string(APPEND buildstatus_contents "ns3_runnable_scripts = [")
# support foreach(executable ${ns3-execs-py})
string(APPEND buildstatus_contents "'${executable}',\n")
endforeach()
string(APPEND buildstatus_contents "]\n\n") string(APPEND buildstatus_contents "]\n\n")
file(WRITE ${CMAKE_OUTPUT_DIRECTORY}/build-status.py file(WRITE ${CMAKE_OUTPUT_DIRECTORY}/build-status.py

View File

@@ -65,6 +65,9 @@ function(generate_c4che_cachepy)
cache_cmake_flag(NS3_BRITE "ENABLE_BRITE" cache_contents) cache_cmake_flag(NS3_BRITE "ENABLE_BRITE" cache_contents)
cache_cmake_flag(NS3_ENABLE_SUDO "ENABLE_SUDO" cache_contents) cache_cmake_flag(NS3_ENABLE_SUDO "ENABLE_SUDO" cache_contents)
cache_cmake_flag(NS3_PYTHON_BINDINGS "ENABLE_PYTHON_BINDINGS" cache_contents) cache_cmake_flag(NS3_PYTHON_BINDINGS "ENABLE_PYTHON_BINDINGS" cache_contents)
cache_cmake_flag(
NS3_SCAN_PYTHON_BINDINGS "ENABLE_SCAN_PYTHON_BINDINGS" cache_contents
)
string(APPEND cache_contents "EXAMPLE_DIRECTORIES = [") string(APPEND cache_contents "EXAMPLE_DIRECTORIES = [")
foreach(example_folder ${ns3-example-folders}) foreach(example_folder ${ns3-example-folders})
@@ -75,7 +78,7 @@ function(generate_c4che_cachepy)
string(APPEND cache_contents "APPNAME = 'ns'\n") string(APPEND cache_contents "APPNAME = 'ns'\n")
string(APPEND cache_contents "BUILD_PROFILE = '${build_profile}'\n") string(APPEND cache_contents "BUILD_PROFILE = '${build_profile}'\n")
string(APPEND cache_contents "VERSION = '${NS3_VER}' \n") string(APPEND cache_contents "VERSION = '${NS3_VER}' \n")
string(APPEND cache_contents "PYTHON = ['${Python3_EXECUTABLE}']\n") string(APPEND cache_contents "PYTHON = ['${Python_EXECUTABLE}']\n")
mark_as_advanced(VALGRIND) mark_as_advanced(VALGRIND)
find_program(VALGRIND valgrind) find_program(VALGRIND valgrind)

View File

@@ -221,7 +221,9 @@ macro(clear_global_cached_variables)
unset(ns3-contrib-libs CACHE) unset(ns3-contrib-libs CACHE)
unset(ns3-example-folders CACHE) unset(ns3-example-folders CACHE)
unset(ns3-execs CACHE) unset(ns3-execs CACHE)
unset(ns3-execs-py CACHE)
unset(ns3-external-libs CACHE) unset(ns3-external-libs CACHE)
unset(ns3-headers-to-module-map CACHE)
unset(ns3-libs CACHE) unset(ns3-libs CACHE)
unset(ns3-libs-tests CACHE) unset(ns3-libs-tests CACHE)
unset(ns3-python-bindings-modules CACHE) unset(ns3-python-bindings-modules CACHE)
@@ -232,7 +234,9 @@ macro(clear_global_cached_variables)
ns3-contrib-libs ns3-contrib-libs
ns3-example-folders ns3-example-folders
ns3-execs ns3-execs
ns3-execs-py
ns3-external-libs ns3-external-libs
ns3-headers-to-module-map
ns3-libs ns3-libs
ns3-libs-tests ns3-libs-tests
ns3-python-bindings-modules ns3-python-bindings-modules
@@ -612,23 +616,28 @@ macro(process_options)
endif() endif()
endif() endif()
find_package(Python3 COMPONENTS Interpreter Development QUIET) find_package(Python COMPONENTS Interpreter Development QUIET)
# Check if python3 was found, and mark as not found if python2 is found
if(${Python_FOUND} AND (${Python_VERSION_MAJOR} LESS 3))
set(Python_FOUND FALSE)
message(
STATUS:
"bindings: an incompatible version of Python was found, bindings will be disabled"
)
endif()
set(ENABLE_PYTHON_BINDINGS OFF) set(ENABLE_PYTHON_BINDINGS OFF)
if(${NS3_PYTHON_BINDINGS}) if(${NS3_PYTHON_BINDINGS})
if(NOT ${Python3_FOUND}) if(NOT ${Python_FOUND})
message(FATAL_ERROR "NS3_PYTHON_BINDINGS requires Python3") message(FATAL_ERROR "NS3_PYTHON_BINDINGS requires Python3")
endif() endif()
set(ENABLE_PYTHON_BINDINGS ON) set(ENABLE_PYTHON_BINDINGS ON)
link_directories(${Python3_LIBRARY_DIRS})
include_directories(${Python3_INCLUDE_DIRS})
set(PYTHONDIR ${Python3_SITELIB})
set(PYTHONARCHDIR ${Python3_SITEARCH})
set(HAVE_PYEMBED TRUE)
set(HAVE_PYEXT TRUE)
set(HAVE_PYTHON_H TRUE)
set(destination_dir ${CMAKE_OUTPUT_DIRECTORY}/bindings/python/ns) set(destination_dir ${CMAKE_OUTPUT_DIRECTORY}/bindings/python/ns)
file(COPY bindings/python/ns__init__.py DESTINATION ${destination_dir}) configure_file(
file(RENAME ${destination_dir}/ns__init__.py ${destination_dir}/__init__.py) bindings/python/ns__init__.py ${destination_dir}/__init__.py COPYONLY
)
endif() endif()
if(${NS3_SCAN_PYTHON_BINDINGS}) if(${NS3_SCAN_PYTHON_BINDINGS})
@@ -637,6 +646,15 @@ macro(process_options)
add_custom_target(apiscan-all) add_custom_target(apiscan-all)
endif() endif()
set(ENABLE_VISUALIZER FALSE)
if(${NS3_VISUALIZER})
if((NOT ${ENABLE_PYTHON_BINDINGS}) OR (NOT ${Python_FOUND}))
message(STATUS "Visualizer requires NS3_PYTHON_BINDINGS and Python3")
else()
set(ENABLE_VISUALIZER TRUE)
endif()
endif()
if(${NS3_COVERAGE} AND (NOT ${ENABLE_TESTS} OR NOT ${ENABLE_EXAMPLES})) if(${NS3_COVERAGE} AND (NOT ${ENABLE_TESTS} OR NOT ${ENABLE_EXAMPLES}))
message( message(
FATAL_ERROR FATAL_ERROR
@@ -652,7 +670,7 @@ macro(process_options)
# produce code coverage output # produce code coverage output
add_custom_target( add_custom_target(
run_test_py run_test_py
COMMAND ${Python3_EXECUTABLE} test.py --no-build COMMAND ${Python_EXECUTABLE} test.py --no-build
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS all-test-targets DEPENDS all-test-targets
) )
@@ -685,14 +703,6 @@ macro(process_options)
set_property(GLOBAL PROPERTY TARGET_MESSAGES OFF) set_property(GLOBAL PROPERTY TARGET_MESSAGES OFF)
endif() endif()
set(ENABLE_VISUALIZER FALSE)
if(${NS3_VISUALIZER})
if((NOT ${NS3_PYTHON_BINDINGS}) OR (NOT ${Python3_FOUND}))
message(FATAL_ERROR "Visualizer requires NS3_PYTHON_BINDINGS and Python3")
endif()
set(ENABLE_VISUALIZER TRUE)
endif()
mark_as_advanced(Boost_INCLUDE_DIR) mark_as_advanced(Boost_INCLUDE_DIR)
find_package(Boost) find_package(Boost)
if(${Boost_FOUND}) if(${Boost_FOUND})
@@ -749,7 +759,7 @@ macro(process_options)
add_custom_target( add_custom_target(
run-introspected-command-line run-introspected-command-line
COMMAND ${CMAKE_COMMAND} -E env NS_COMMANDLINE_INTROSPECTION=.. COMMAND ${CMAKE_COMMAND} -E env NS_COMMANDLINE_INTROSPECTION=..
${Python3_EXECUTABLE} ./test.py --no-build --constrain=example ${Python_EXECUTABLE} ./test.py --no-build --constrain=example
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS all-test-targets # all-test-targets only exists if ENABLE_TESTS is DEPENDS all-test-targets # all-test-targets only exists if ENABLE_TESTS is
# set to ON # set to ON
@@ -1071,6 +1081,17 @@ function(set_runtime_outputdirectory target_name output_directory target_prefix)
endif() endif()
endfunction(set_runtime_outputdirectory) endfunction(set_runtime_outputdirectory)
function(scan_python_examples path)
file(GLOB_RECURSE python_examples ${path}/*.py)
foreach(python_example ${python_examples})
if(NOT (${python_example} MATCHES "examples-to-run"))
set(ns3-execs-py "${python_example};${ns3-execs-py}"
CACHE INTERNAL "list of python scripts"
)
endif()
endforeach()
endfunction()
add_custom_target(copy_all_headers) add_custom_target(copy_all_headers)
function(copy_headers_before_building_lib libname outputdir headers visibility) function(copy_headers_before_building_lib libname outputdir headers visibility)
foreach(header ${headers}) foreach(header ${headers})
@@ -1437,6 +1458,20 @@ function(find_external_library_header_and_library name header_name library_name
endif() endif()
endfunction() endfunction()
function(write_header_to_modules_map)
if(${NS3_SCAN_PYTHON_BINDINGS})
set(header_map ${ns3-headers-to-module-map})
# Trim last comma
string(LENGTH "${header_map}" header_map_len)
math(EXPR header_map_len "${header_map_len}-1")
string(SUBSTRING "${header_map}" 0 ${header_map_len} header_map)
# Then write to header_map.json for consumption of pybindgen
file(WRITE ${PROJECT_BINARY_DIR}/header_map.json "{${header_map}}")
endif()
endfunction()
# Waf workaround scripts # Waf workaround scripts
include(buildsupport/custom_modules/waf_workaround_c4cache.cmake) include(buildsupport/custom_modules/waf_workaround_c4cache.cmake)
include(buildsupport/custom_modules/waf_workaround_buildstatus.cmake) include(buildsupport/custom_modules/waf_workaround_buildstatus.cmake)

View File

@@ -0,0 +1,16 @@
#! /usr/bin/env python3
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
def lp64_to_ilp32(lp64path, ilp32path):
import re
lp64bindings = None
with open(lp64path, "r") as lp64file:
lp64bindings = lp64file.read()
with open(ilp32path, "w") as ilp32file:
ilp32bindings = re.sub("unsigned long(?!( long))", "unsigned long long", lp64bindings)
ilp32file.write(ilp32bindings)
if __name__ == "__main__":
import sys
print(sys.argv)
exit(lp64_to_ilp32(sys.argv[1], sys.argv[2]))

View File

@@ -9,4 +9,5 @@ if(${ENABLE_EXAMPLES})
CACHE INTERNAL "list of example folders" CACHE INTERNAL "list of example folders"
) )
endforeach() endforeach()
scan_python_examples(${CMAKE_CURRENT_SOURCE_DIR})
endif() endif()

45
ns3
View File

@@ -99,6 +99,7 @@ def parse_args(argv):
parser_configure = on_off_argument(parser_configure, "gtk", "GTK support in ConfigStore") parser_configure = on_off_argument(parser_configure, "gtk", "GTK support in ConfigStore")
parser_configure = on_off_argument(parser_configure, "logs", "the logs regardless of the compile mode") parser_configure = on_off_argument(parser_configure, "logs", "the logs regardless of the compile mode")
parser_configure = on_off_argument(parser_configure, "mpi", "the MPI support for distributed simulation") parser_configure = on_off_argument(parser_configure, "mpi", "the MPI support for distributed simulation")
parser_configure = on_off_argument(parser_configure, "python-bindings", "python bindings")
parser_configure = on_off_argument(parser_configure, "tests", "the ns-3 tests") parser_configure = on_off_argument(parser_configure, "tests", "the ns-3 tests")
parser_configure = on_off_argument(parser_configure, "sanitizers", parser_configure = on_off_argument(parser_configure, "sanitizers",
"address, memory leaks and undefined behavior sanitizers") "address, memory leaks and undefined behavior sanitizers")
@@ -109,7 +110,6 @@ def parse_args(argv):
parser_configure = on_off_argument(parser_configure, "warnings", "compiler warnings") parser_configure = on_off_argument(parser_configure, "warnings", "compiler warnings")
parser_configure = on_off_argument(parser_configure, "werror", "Treat compiler warnings as errors", parser_configure = on_off_argument(parser_configure, "werror", "Treat compiler warnings as errors",
"Treat compiler warnings as warnings") "Treat compiler warnings as warnings")
parser_configure = on_off_argument(parser_configure, "visualizer", "the visualizer module")
parser_configure.add_argument('--enable-modules', parser_configure.add_argument('--enable-modules',
help='List of modules to build (e.g. core;network;internet)', help='List of modules to build (e.g. core;network;internet)',
action="store", type=str, default=None) action="store", type=str, default=None)
@@ -286,6 +286,8 @@ def check_build_profile(output_directory):
ns3_version = None ns3_version = None
ns3_modules = None ns3_modules = None
ns3_modules_tests = [] ns3_modules_tests = []
ns3_modules_apiscan = []
ns3_modules_bindings = []
enable_sudo = False enable_sudo = False
if output_directory and os.path.exists(c4che_path): if output_directory and os.path.exists(c4che_path):
c4che_info = {} c4che_info = {}
@@ -293,10 +295,17 @@ def check_build_profile(output_directory):
build_profile = c4che_info["BUILD_PROFILE"] build_profile = c4che_info["BUILD_PROFILE"]
ns3_version = c4che_info["VERSION"] ns3_version = c4che_info["VERSION"]
ns3_modules = c4che_info["NS3_ENABLED_MODULES"] ns3_modules = c4che_info["NS3_ENABLED_MODULES"]
ns3_modules_tests = [x + "-test" for x in ns3_modules]
if "ENABLE_SUDO" in c4che_info: if "ENABLE_SUDO" in c4che_info:
enable_sudo = c4che_info["ENABLE_SUDO"] enable_sudo = c4che_info["ENABLE_SUDO"]
return build_profile, ns3_version, ns3_modules + ns3_modules_tests if ns3_modules else None, enable_sudo if ns3_modules:
if c4che_info["ENABLE_TESTS"]:
ns3_modules_tests = [x + "-test" for x in ns3_modules]
if c4che_info["ENABLE_PYTHON_BINDINGS"]:
ns3_modules_bindings = [x + "-bindings" for x in ns3_modules]
if "ENABLE_SCAN_PYTHON_BINDINGS" in c4che_info and c4che_info["ENABLE_SCAN_PYTHON_BINDINGS"]:
ns3_modules_apiscan = [x + "-apiscan" for x in ns3_modules]
ns3_modules = ns3_modules + ns3_modules_tests + ns3_modules_apiscan + ns3_modules_bindings
return build_profile, ns3_version, ns3_modules, enable_sudo
def print_and_buffer(message): def print_and_buffer(message):
@@ -453,11 +462,11 @@ def configure_cmake(cmake, args, current_cmake_cache_folder, current_cmake_gener
("GTK3", "gtk"), ("GTK3", "gtk"),
("LOG", "logs"), ("LOG", "logs"),
("MPI", "mpi"), ("MPI", "mpi"),
("PYTHON_BINDINGS", "python_bindings"),
("SANITIZE", "sanitizers"), ("SANITIZE", "sanitizers"),
("STATIC", "static"), ("STATIC", "static"),
("TESTS", "tests"), ("TESTS", "tests"),
("VERBOSE", "verbose"), ("VERBOSE", "verbose"),
("VISUALIZER", "visualizer"),
("WARNINGS", "warnings"), ("WARNINGS", "warnings"),
("WARNINGS_AS_ERRORS", "werror"), ("WARNINGS_AS_ERRORS", "werror"),
) )
@@ -553,6 +562,19 @@ def get_program_shortcuts(build_profile, ns3_version):
if source_shortcut: if source_shortcut:
ns3_program_map[shortcut_path + ".cc"] = program ns3_program_map[shortcut_path + ".cc"] = program
temp_path.pop(0) temp_path.pop(0)
if programs_dict["ns3_runnable_scripts"]:
scratch_scripts = glob.glob(os.path.join(ns3_path, "scratch", "*.py"), recursive=True)
programs_dict["ns3_runnable_scripts"].extend(scratch_scripts)
for program in programs_dict["ns3_runnable_scripts"]:
temp_path = program.replace(ns3_path, "").split(os.sep)
program = program.strip()
while len(temp_path):
shortcut_path = os.sep.join(temp_path)
ns3_program_map[shortcut_path] = program
temp_path.pop(0)
return ns3_program_map return ns3_program_map
@@ -658,6 +680,9 @@ def reconfigure_cmake_to_force_refresh(cmake, current_cmake_cache_folder, output
def get_target_to_build(program_path, ns3_version, build_profile): def get_target_to_build(program_path, ns3_version, build_profile):
if ".py" in program_path:
return None
build_profile_suffix = "" if build_profile in ["release"] else "-" + build_profile build_profile_suffix = "" if build_profile in ["release"] else "-" + build_profile
program_name = "".join(*re.findall("(.*)ns%s-(.*)%s" % (ns3_version, build_profile_suffix), program_path)) program_name = "".join(*re.findall("(.*)ns%s-(.*)%s" % (ns3_version, build_profile_suffix), program_path))
@@ -712,7 +737,8 @@ def build_step(args,
# If we are building specific targets, we build them one by one # If we are building specific targets, we build them one by one
if "build" in args: if "build" in args:
non_executable_targets = ["check-version", non_executable_targets = ["apiscan-all",
"check-version",
"cmake-format", "cmake-format",
"docs", "docs",
"doxygen", "doxygen",
@@ -787,6 +813,11 @@ def run_step(args, target_to_run, target_args):
target_to_run = "bash" target_to_run = "bash"
use_shell = True use_shell = True
else: else:
# running a python script?
if ".py" in target_to_run:
target_args = [target_to_run] + target_args
target_to_run = "python3"
# running from ns-3-dev (ns3_path) or cwd # running from ns-3-dev (ns3_path) or cwd
if args.cwd: if args.cwd:
working_dir = args.cwd working_dir = args.cwd
@@ -1060,7 +1091,7 @@ def main():
target_path = os.sep.join(target_path) target_path = os.sep.join(target_path)
return target_path return target_path
if not args.check and not args.shell and target_to_run: if not args.check and not args.shell and target_to_run and not ".py" in target_to_run:
target_to_run = remove_overlapping_path(out_dir, target_to_run) target_to_run = remove_overlapping_path(out_dir, target_to_run)
# Waf doesn't add version prefix and build type suffix to the scratches, so we remove them # Waf doesn't add version prefix and build type suffix to the scratches, so we remove them
@@ -1072,7 +1103,7 @@ def main():
target_to_run = os.sep.join([out_dir, target_to_run]) target_to_run = os.sep.join([out_dir, target_to_run])
# If we're only trying to run the target, we need to check if it actually exists first # If we're only trying to run the target, we need to check if it actually exists first
if (run_only or build_and_run) and not os.path.exists(target_to_run): if (run_only or build_and_run) and ".py" not in target_to_run and not os.path.exists(target_to_run):
raise Exception("Executable has not been built yet") raise Exception("Executable has not been built yet")
# Setup program as sudo # Setup program as sudo

View File

@@ -1,5 +1,7 @@
set(name fd-net-device) set(name fd-net-device)
set(module_enabled_features) # modulegen_customizations consumes this list
mark_as_advanced(ENABLE_THREADING) mark_as_advanced(ENABLE_THREADING)
set(ENABLE_THREADING ${HAVE_PTHREAD_H}) set(ENABLE_THREADING ${HAVE_PTHREAD_H})
@@ -57,6 +59,7 @@ endif()
if(${ENABLE_FDNETDEV}) if(${ENABLE_FDNETDEV})
set(fd-net-device_creators) set(fd-net-device_creators)
list(APPEND module_enabled_features FdNetDevice)
if(${ENABLE_EMUNETDEV}) if(${ENABLE_EMUNETDEV})
set(emu_sources helper/emu-fd-net-device-helper.cc) set(emu_sources helper/emu-fd-net-device-helper.cc)
@@ -74,6 +77,7 @@ if(${ENABLE_FDNETDEV})
) )
list(APPEND fd-net-device_creators raw-sock-creator) list(APPEND fd-net-device_creators raw-sock-creator)
list(APPEND module_enabled_features EmuFdNetDevice)
endif() endif()
if(${ENABLE_TAPNETDEV}) if(${ENABLE_TAPNETDEV})
@@ -93,6 +97,7 @@ if(${ENABLE_FDNETDEV})
) )
list(APPEND fd-net-device_creators tap-device-creator) list(APPEND fd-net-device_creators tap-device-creator)
list(APPEND module_enabled_features TapFdNetDevice)
endif() endif()
if(${ENABLE_NETMAP_EMU}) if(${ENABLE_NETMAP_EMU})

View File

@@ -251,7 +251,6 @@ set(header_files
model/udp-header.h model/udp-header.h
model/udp-l4-protocol.h model/udp-l4-protocol.h
model/udp-socket-factory.h model/udp-socket-factory.h
model/udp-socket-impl.h
model/udp-socket.h model/udp-socket.h
model/windowed-filter.h model/windowed-filter.h
) )

View File

@@ -2,12 +2,12 @@ set(name visualizer)
set(source_files model/pyviz.cc model/visual-simulator-impl.cc) set(source_files model/pyviz.cc model/visual-simulator-impl.cc)
set(header_files model/pyviz.h model/visual-simulator-impl.h) set(header_files model/pyviz.h)
include_directories(${Python3_INCLUDE_DIRS}) include_directories(${Python_INCLUDE_DIRS})
set(libraries_to_link set(libraries_to_link
${Python3_LIBRARIES} ${Python_LIBRARIES}
${libcore} ${libcore}
${libinternet} ${libinternet}
${libwifi} ${libwifi}
@@ -27,4 +27,11 @@ build_lib("${name}" "${source_files}" "${header_files}" "${libraries_to_link}"
# move visualizer folder to build/bindings/python, which allows us to add only # move visualizer folder to build/bindings/python, which allows us to add only
# PYTHONPATH=ns-3-dev/build/bindings/python # PYTHONPATH=ns-3-dev/build/bindings/python
file(COPY visualizer DESTINATION ${CMAKE_OUTPUT_DIRECTORY}/bindings/python/) file(GLOB_RECURSE visualizer_files ${CMAKE_CURRENT_SOURCE_DIR}/visualizer/*)
foreach(file ${visualizer_files})
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_OUTPUT_DIRECTORY}/bindings/python" destination
${file}
)
configure_file(${file} ${destination} COPYONLY)
endforeach()

View File

@@ -18,6 +18,8 @@
* Author: Gustavo Carneiro <gjcarneiro@gmail.com> <gjc@inescporto.pt> * Author: Gustavo Carneiro <gjcarneiro@gmail.com> <gjc@inescporto.pt>
*/ */
#include <Python.h> #include <Python.h>
#undef HAVE_PTHREAD_H
#undef HAVE_SYS_STAT_H
#include "visual-simulator-impl.h" #include "visual-simulator-impl.h"
#include "ns3/default-simulator-impl.h" #include "ns3/default-simulator-impl.h"
#include "ns3/log.h" #include "ns3/log.h"

View File

@@ -1120,6 +1120,7 @@ def run_tests():
if os.path.exists(build_status_file): if os.path.exists(build_status_file):
ns3_runnable_programs = get_list_from_file(build_status_file, "ns3_runnable_programs") ns3_runnable_programs = get_list_from_file(build_status_file, "ns3_runnable_programs")
ns3_runnable_scripts = get_list_from_file(build_status_file, "ns3_runnable_scripts") ns3_runnable_scripts = get_list_from_file(build_status_file, "ns3_runnable_scripts")
ns3_runnable_scripts = [os.path.basename(script) for script in ns3_runnable_scripts]
else: else:
print('The build status file was not found. You must do waf build before running test.py.', file=sys.stderr) print('The build status file was not found. You must do waf build before running test.py.', file=sys.stderr)
sys.exit(2) sys.exit(2)

View File

@@ -1,3 +1,5 @@
#! /usr/bin/env python3
# Copyright (C) 2008-2011 INESC Porto # Copyright (C) 2008-2011 INESC Porto
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@@ -25,6 +27,8 @@ import ns.mobility
import ns.csma import ns.csma
import ns.applications import ns.applications
UINT32_MAX = 0xFFFFFFFF
## TestSimulator class ## TestSimulator class
class TestSimulator(unittest.TestCase): class TestSimulator(unittest.TestCase):
@@ -170,7 +174,7 @@ class TestSimulator(unittest.TestCase):
@return none @return none
""" """
assert self._received_packet is None assert self._received_packet is None
self._received_packet = socket.Recv() self._received_packet = socket.Recv(maxSize=UINT32_MAX, flags=0)
sink = ns.network.Socket.CreateSocket(node, ns.core.TypeId.LookupByName("ns3::UdpSocketFactory")) sink = ns.network.Socket.CreateSocket(node, ns.core.TypeId.LookupByName("ns3::UdpSocketFactory"))
sink.Bind(ns.network.InetSocketAddress(ns.network.Ipv4Address.GetAny(), 80)) sink.Bind(ns.network.InetSocketAddress(ns.network.Ipv4Address.GetAny(), 80))

View File

@@ -1138,8 +1138,8 @@ class NS3BuildBaseTestCase(NS3BaseTestCase):
f.write(""" f.write("""
#include <ns3/core-module.h> #include <ns3/core-module.h>
using namespace ns3; using namespace ns3;
int main () int main ()
{ {
Simulator::Stop (Seconds (1.0)); Simulator::Stop (Seconds (1.0));
Simulator::Run (); Simulator::Run ();
Simulator::Destroy (); Simulator::Destroy ();
@@ -1246,6 +1246,92 @@ class NS3BuildBaseTestCase(NS3BaseTestCase):
stdout = stdout.replace("scratch_%s" % target_cmake, "") # remove build lines stdout = stdout.replace("scratch_%s" % target_cmake, "") # remove build lines
self.assertIn(target_to_run.split("/")[-1], stdout) self.assertIn(target_to_run.split("/")[-1], stdout)
NS3BuildBaseTestCase.cleaned_once = False
def test_10_PybindgenBindings(self):
"""!
Test if cmake is calling pybindgen through modulegen to generate
the bindings source files correctly
@return None
"""
# First we enable python bindings
return_code, stdout, stderr = run_ns3("configure --enable-examples --enable-tests --enable-python-bindings")
self.assertEqual(return_code, 0)
# Then look for python bindings sources
core_bindings_generated_sources_path = os.path.join(ns3_path, "build", "src", "core", "bindings")
core_bindings_sources_path = os.path.join(ns3_path, "src", "core", "bindings")
core_bindings_path = os.path.join(ns3_path, "build", "bindings", "python", "ns")
core_bindings_header = os.path.join(core_bindings_generated_sources_path, "ns3module.h")
core_bindings_source = os.path.join(core_bindings_generated_sources_path, "ns3module.cc")
self.assertTrue(os.path.exists(core_bindings_header))
self.assertTrue(os.path.exists(core_bindings_source))
# Then try to build the bindings for the core module
return_code, stdout, stderr = run_ns3("build core-bindings")
self.assertEqual(return_code, 0)
# Then check if it was built
self.assertGreater(len(list(filter(lambda x: "_core" in x, os.listdir(core_bindings_path)))), 0)
# Now enable python bindings scanning
return_code, stdout, stderr = run_ns3("configure -- -DNS3_SCAN_PYTHON_BINDINGS=ON")
self.assertEqual(return_code, 0)
# Get the file status for the current scanned bindings
bindings_sources = os.listdir(core_bindings_sources_path)
bindings_sources = [os.path.join(core_bindings_sources_path, y) for y in bindings_sources]
timestamps = {}
[timestamps.update({x: os.stat(x)}) for x in bindings_sources]
# Try to scan the bindings for the core module
return_code, stdout, stderr = run_ns3("build core-apiscan")
self.assertEqual(return_code, 0)
# Check if they exist, are not empty and have a different timestamp
generated_python_files = ["callbacks_list.py", "modulegen__gcc_LP64.py"]
for binding_file in timestamps.keys():
if os.path.basename(binding_file) in generated_python_files:
self.assertTrue(os.path.exists(binding_file))
self.assertGreater(os.stat(binding_file).st_size, 0)
new_fstat = os.stat(binding_file)
self.assertNotEqual(timestamps[binding_file].st_mtime, new_fstat.st_mtime)
# Then delete the old bindings sources
for f in os.listdir(core_bindings_generated_sources_path):
os.remove(os.path.join(core_bindings_generated_sources_path, f))
# Reconfigure to recreate the source files
return_code, stdout, stderr = run_ns3("configure")
self.assertEqual(return_code, 0)
# Check again if they exist
self.assertTrue(os.path.exists(core_bindings_header))
self.assertTrue(os.path.exists(core_bindings_source))
# Build the core bindings again
return_code, stdout, stderr = run_ns3("build core-bindings")
self.assertEqual(return_code, 0)
# Then check if it was built
self.assertGreater(len(list(filter(lambda x: "_core" in x, os.listdir(core_bindings_path)))), 0)
# We are on python anyways, so we can just try to load it from here
sys.path.insert(0, os.path.join(core_bindings_path, ".."))
try:
from ns import core
except ImportError:
self.assertTrue(True)
# Check if ns3 can find and run the python example
return_code, stdout, stderr = run_ns3("run sample-simulator.py")
self.assertEqual(return_code, 0)
# Check if test.py can find and run the python example
return_code, stdout, stderr = run_program("./test.py", "-p src/core/examples/sample-simulator.py", python=True)
self.assertEqual(return_code, 0)
class NS3ExpectedUseTestCase(NS3BaseTestCase): class NS3ExpectedUseTestCase(NS3BaseTestCase):
"""! """!