diff --git a/build-support/cmake-format.yaml b/build-support/cmake-format.yaml index 58d38a3dd..7ccb9fde3 100644 --- a/build-support/cmake-format.yaml +++ b/build-support/cmake-format.yaml @@ -48,6 +48,13 @@ additional_commands: LIBRARY_NAMES : '*' PATH_SUFFIXES : '*' SEARCH_PATHS : '*' + check_deps: + pargs: + nargs: 1 + kwargs: + CMAKE_PACKAGES : '*' + EXECUTABLES : '*' + PYTHON_PACKAGES : '*' format: tab_size: 2 diff --git a/build-support/custom-modules/ns3-check-dependencies.cmake b/build-support/custom-modules/ns3-check-dependencies.cmake new file mode 100644 index 000000000..c415dab3c --- /dev/null +++ b/build-support/custom-modules/ns3-check-dependencies.cmake @@ -0,0 +1,61 @@ +# Copyright (c) 2023 Universidade de Brasília +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License version 2 as published by the Free +# Software Foundation; +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA +# +# Author: Gabriel Ferreira + +# function used to search for package and program dependencies than store list +# of missing dependencies in the list whose name is stored in missing_deps +function(check_deps missing_deps) + set(multiValueArgs CMAKE_PACKAGES EXECUTABLES PYTHON_PACKAGES) + cmake_parse_arguments( + "DEPS" "" "${OUTPUT_VAR_NAME}" "${multiValueArgs}" ${ARGN} + ) + + set(local_missing_deps) + # Search for package dependencies + foreach(package ${DEPS_CMAKE_PACKAGES}) + find_package(${package}) + if(NOT ${${package}_FOUND}) + list(APPEND local_missing_deps ${package}) + endif() + endforeach() + + # And for program dependencies + foreach(program ${DEPS_EXECUTABLES}) + # CMake likes to cache find_* to speed things up, so we can't reuse names + # here or it won't check other dependencies + string(TOUPPER ${program} upper_${program}) + mark_as_advanced(${upper_${program}}) + find_program( + ${upper_${program}} ${program} HINTS ${3RD_PARTY_FIND_PROGRAM_HINTS} + ) + if("${${upper_${program}}}" STREQUAL "${upper_${program}}-NOTFOUND") + list(APPEND local_missing_deps ${program}) + endif() + endforeach() + + foreach(package ${DEPS_PYTHON_PACKAGES}) + execute_process( + COMMAND ${Python3_EXECUTABLE} -c "import ${package}" + RESULT_VARIABLE return_code OUTPUT_QUIET ERROR_QUIET + ) + if(NOT (${return_code} EQUAL 0)) + list(APPEND local_missing_deps ${package}) + endif() + endforeach() + + # Store list of missing dependencies in the parent scope + set(${missing_deps} ${local_missing_deps} PARENT_SCOPE) +endfunction() diff --git a/build-support/custom-modules/ns3-cmake-package.cmake b/build-support/custom-modules/ns3-cmake-package.cmake index e441281d7..a8edab357 100644 --- a/build-support/custom-modules/ns3-cmake-package.cmake +++ b/build-support/custom-modules/ns3-cmake-package.cmake @@ -15,6 +15,27 @@ # # Author: Gabriel Ferreira +include(GNUInstallDirs) + +# Set RPATH not too need LD_LIBRARY_PATH after installing +set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib:$ORIGIN/:$ORIGIN/../lib") + +# Add the 64 suffix to the library path when manually requested with the +# -DNS3_USE_LIB64=ON flag. May be necessary depending on the target platform. +# This is used to properly build the manylinux pip wheel. +if(${NS3_USE_LIB64}) + link_directories(${CMAKE_OUTPUT_DIRECTORY}/lib64) + set(CMAKE_INSTALL_RPATH + "${CMAKE_INSTALL_RPATH}:${CMAKE_INSTALL_PREFIX}/lib64:$ORIGIN/:$ORIGIN/../lib64" + ) +endif() + +# cmake-format: off +# You are a wizard, Harry! +# source: https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/RPATH-handling +# cmake-format: on +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + function(build_required_and_libs_lists module_name visibility libraries all_ns3_libraries ) diff --git a/build-support/custom-modules/ns3-compiler-and-linker-support.cmake b/build-support/custom-modules/ns3-compiler-and-linker-support.cmake new file mode 100644 index 000000000..105ba4c8b --- /dev/null +++ b/build-support/custom-modules/ns3-compiler-and-linker-support.cmake @@ -0,0 +1,128 @@ +# Copyright (c) 2023 Universidade de Brasília +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License version 2 as published by the Free +# Software Foundation; +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA +# +# Author: Gabriel Ferreira + +# Compiler-specific version checks and flag/feature setting should be done here + +# 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 "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() + +if(CLANG) + if(${NS3_COLORED_OUTPUT} OR "$ENV{CLICOLOR}") + add_definitions(-fcolor-diagnostics) # colorize clang++ output + endif() +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() + if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.2.0")) + # PCH causes weird errors on certain versions of GCC when C++20 is enabled + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106799 + set(NS3_PRECOMPILE_HEADERS OFF + CACHE BOOL "Precompile module headers to speed up compilation" FORCE + ) + endif() + if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.1.0") + AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "12.3.2") + ) + # Disable warnings-as-errors for certain versions of GCC when C++20 is + # enabled https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105545 + add_compile_options(-Wno-restrict) + endif() + set(GCC TRUE) + add_definitions(-fno-semantic-interposition) + if(${NS3_COLORED_OUTPUT} OR "$ENV{CLICOLOR}") + add_definitions(-fdiagnostics-color=always) # colorize g++ output + endif() +endif() +unset(below_minimum_msg) + +set(LIB_AS_NEEDED_PRE) +set(LIB_AS_NEEDED_POST) +set(STATIC_LINK_FLAGS -static -static-libstdc++ -static-libgcc) +if(${GCC} AND NOT APPLE) + # using GCC + set(LIB_AS_NEEDED_PRE -Wl,--no-as-needed) + set(LIB_AS_NEEDED_POST -Wl,--as-needed) + set(LIB_AS_NEEDED_PRE_STATIC -Wl,--whole-archive,-Bstatic) + set(LIB_AS_NEEDED_POST_STATIC -Wl,--no-whole-archive) + set(LIB_AS_NEEDED_POST_STATIC_DYN -Wl,-Bdynamic,--no-whole-archive) +endif() + +if(${CLANG} AND APPLE) + # using Clang set(LIB_AS_NEEDED_PRE -all_load) + set(LIB_AS_NEEDED_POST) + set(LIB_AS_NEEDED_PRE_STATIC -Wl,-all_load) + set(STATIC_LINK_FLAGS) +endif() + +if(${NS3_FAST_LINKERS}) + # Search for faster linkers mold and lld, and use them if available + mark_as_advanced(MOLD LLD) + find_program(MOLD mold) + find_program(LLD ld.lld) + + # USING_FAST_LINKER will be defined if a fast linker is being used and its + # content will correspond to the fast linker name + + # Mold support was added in GCC 12.1.0 + if(NOT USING_FAST_LINKER + AND NOT (${MOLD} STREQUAL "MOLD-NOTFOUND") + AND LINUX + AND ${GCC} + AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12.1.0) + ) + set(USING_FAST_LINKER MOLD) + add_link_options("-fuse-ld=mold") + endif() + + if(NOT USING_FAST_LINKER AND NOT (${LLD} STREQUAL "LLD-NOTFOUND") + AND (${GCC} OR ${CLANG}) + ) + set(USING_FAST_LINKER LLD) + add_link_options("-fuse-ld=lld") + if(WIN32) + # Clear unsupported linker flags on Windows + set(LIB_AS_NEEDED_PRE) + endif() + endif() +endif() diff --git a/build-support/custom-modules/ns3-executables.cmake b/build-support/custom-modules/ns3-executables.cmake new file mode 100644 index 000000000..92ccea1a8 --- /dev/null +++ b/build-support/custom-modules/ns3-executables.cmake @@ -0,0 +1,162 @@ +function(set_runtime_outputdirectory target_name output_directory target_prefix) + # Prevent duplicate '/' in EXECUTABLE_DIRECTORY_PATH, since it gets translated + # to doubled underlines and will cause the ns3 script to fail + string(REPLACE "//" "/" output_directory "${output_directory}") + + set(ns3-exec-outputname ns${NS3_VER}-${target_name}${build_profile_suffix}) + set(ns3-execs "${output_directory}${ns3-exec-outputname};${ns3-execs}" + CACHE INTERNAL "list of c++ executables" + ) + set(ns3-execs-clean "${target_prefix}${target_name};${ns3-execs-clean}" + CACHE INTERNAL + "list of c++ executables without version prefix and build suffix" + ) + + set_target_properties( + ${target_prefix}${target_name} + PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_directory} + RUNTIME_OUTPUT_NAME ${ns3-exec-outputname} + ) + if(${XCODE}) + # Is that so hard not to break people's CI, AAPL?? Why would you output the + # targets to a Debug/Release subfolder? Why? + foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG) + set_target_properties( + ${target_prefix}${target_name} + PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${output_directory} + RUNTIME_OUTPUT_NAME_${OUTPUTCONFIG} ${ns3-exec-outputname} + ) + endforeach(OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES) + endif() + + if(${ENABLE_TESTS}) + add_dependencies(all-test-targets ${target_prefix}${target_name}) + # Create a CTest entry for each executable + if(WIN32) + # Windows require this workaround to make sure the DLL files are located + add_test( + NAME ctest-${target_prefix}${target_name} + COMMAND + ${CMAKE_COMMAND} -E env + "PATH=$ENV{PATH};${CMAKE_RUNTIME_OUTPUT_DIRECTORY};${CMAKE_LIBRARY_OUTPUT_DIRECTORY}" + ${ns3-exec-outputname} + WORKING_DIRECTORY ${output_directory} + ) + else() + add_test(NAME ctest-${target_prefix}${target_name} + COMMAND ${ns3-exec-outputname} + WORKING_DIRECTORY ${output_directory} + ) + endif() + endif() + + if(${NS3_CLANG_TIMETRACE}) + add_dependencies(timeTraceReport ${target_prefix}${target_name}) + endif() +endfunction(set_runtime_outputdirectory) + +function(get_scratch_prefix prefix) + # /path/to/ns-3-dev/scratch/nested-subdir + set(temp ${CMAKE_CURRENT_SOURCE_DIR}) + # remove /path/to/ns-3-dev/ to get scratch/nested-subdir + string(REPLACE "${PROJECT_SOURCE_DIR}/" "" temp "${temp}") + # replace path separators with underlines + string(REPLACE "/" "_" temp "${temp}") + # save the prefix value to the passed variable + set(${prefix} ${temp}_ PARENT_SCOPE) +endfunction() + +function(build_exec) + # Argument parsing + set(options IGNORE_PCH STANDALONE) + set(oneValueArgs EXECNAME EXECNAME_PREFIX EXECUTABLE_DIRECTORY_PATH + INSTALL_DIRECTORY_PATH + ) + set(multiValueArgs SOURCE_FILES HEADER_FILES LIBRARIES_TO_LINK DEFINITIONS) + cmake_parse_arguments( + "BEXEC" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} + ) + + # Resolve nested scratch prefixes without user intervention + string(REPLACE "${PROJECT_SOURCE_DIR}" "" relative_path + "${CMAKE_CURRENT_SOURCE_DIR}" + ) + if("${relative_path}" MATCHES "scratch" AND "${BEXEC_EXECNAME_PREFIX}" + STREQUAL "" + ) + get_scratch_prefix(BEXEC_EXECNAME_PREFIX) + endif() + + add_executable( + ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} "${BEXEC_SOURCE_FILES}" + ) + + target_compile_definitions( + ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} PUBLIC ${BEXEC_DEFINITIONS} + ) + + if(${PRECOMPILE_HEADERS_ENABLED} AND (NOT ${BEXEC_IGNORE_PCH})) + target_precompile_headers( + ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} REUSE_FROM stdlib_pch_exec + ) + endif() + + if(${NS3_STATIC} AND (NOT BEXEC_STANDALONE)) + target_link_libraries( + ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} ${LIB_AS_NEEDED_PRE_STATIC} + ${lib-ns3-static} + ) + elseif(${NS3_MONOLIB} AND (NOT BEXEC_STANDALONE)) + target_link_libraries( + ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} ${LIB_AS_NEEDED_PRE} + ${lib-ns3-monolib} ${LIB_AS_NEEDED_POST} + ) + else() + target_link_libraries( + ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} ${LIB_AS_NEEDED_PRE} + "${BEXEC_LIBRARIES_TO_LINK}" ${LIB_AS_NEEDED_POST} + ) + endif() + + set_runtime_outputdirectory( + "${BEXEC_EXECNAME}" "${BEXEC_EXECUTABLE_DIRECTORY_PATH}/" + "${BEXEC_EXECNAME_PREFIX}" + ) + + if(BEXEC_INSTALL_DIRECTORY_PATH) + install(TARGETS ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} + EXPORT ns3ExportTargets + RUNTIME DESTINATION ${BEXEC_INSTALL_DIRECTORY_PATH} + ) + get_property( + filename TARGET ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} + PROPERTY RUNTIME_OUTPUT_NAME + ) + add_custom_target( + uninstall_${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} + COMMAND + rm ${CMAKE_INSTALL_PREFIX}/${BEXEC_INSTALL_DIRECTORY_PATH}/${filename} + ) + add_dependencies( + uninstall uninstall_${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} + ) + endif() +endfunction(build_exec) + +function(scan_python_examples path) + # Skip python examples search in case the bindings are disabled + if(NOT ${ENABLE_PYTHON_BINDINGS}) + return() + endif() + + # Search for python examples + 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() diff --git a/build-support/custom-modules/ns3-filter-modules.cmake b/build-support/custom-modules/ns3-filter-modules.cmake new file mode 100644 index 000000000..fe1daaf7d --- /dev/null +++ b/build-support/custom-modules/ns3-filter-modules.cmake @@ -0,0 +1,232 @@ +# Copyright (c) 2023 Universidade de Brasília +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License version 2 as published by the Free +# Software Foundation; +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA +# +# Author: Gabriel Ferreira + +# This is where recursive module dependency checking is implemented for +# filtering enabled and disabled modules + +function(filter_modules modules_to_filter all_modules_list filter_in) + set(new_modules_to_build) + # 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})) + list(APPEND new_modules_to_build ${module}) + endif() + endforeach() + set(${all_modules_list} ${new_modules_to_build} PARENT_SCOPE) +endfunction() + +function(resolve_dependencies module_name dependencies contrib_dependencies) + # Create cache variables to hold dependencies list and visited + set(dependency_visited "" CACHE INTERNAL "") + set(contrib_dependency_visited "" CACHE INTERNAL "") + recursive_dependency(${module_name}) + if(${module_name} IN_LIST dependency_visited) + set(temp ${dependency_visited}) + list(REMOVE_AT temp 0) + set(${dependencies} ${temp} PARENT_SCOPE) + set(${contrib_dependencies} ${contrib_dependency_visited} PARENT_SCOPE) + else() + set(temp ${contrib_dependency_visited}) + list(REMOVE_AT temp 0) + set(${dependencies} ${dependency_visited} PARENT_SCOPE) + set(${contrib_dependencies} ${temp} PARENT_SCOPE) + endif() + unset(dependency_visited CACHE) + unset(contrib_dependency_visited CACHE) +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) +endfunction() + +function(recursive_dependency module_name) + # Read module CMakeLists.txt and search for ns-3 modules + set(src_cmakelist ${PROJECT_SOURCE_DIR}/src/${module_name}/CMakeLists.txt) + set(contrib_cmakelist + ${PROJECT_SOURCE_DIR}/contrib/${module_name}/CMakeLists.txt + ) + set(contrib FALSE) + if(EXISTS ${src_cmakelist}) + file(READ ${src_cmakelist} cmakelists_content) + elseif(EXISTS ${contrib_cmakelist}) + file(READ ${contrib_cmakelist} cmakelists_content) + set(contrib TRUE) + else() + set(cmakelists_content "") + message(${HIGHLIGHTED_STATUS} + "The CMakeLists.txt file for module ${module_name} was not found." + ) + endif() + + filter_libraries("${cmakelists_content}" matches) + + # Add this visited module dependencies to the dependencies list + if(contrib) + set(contrib_dependency_visited + "${contrib_dependency_visited};${module_name}" CACHE INTERNAL "" + ) + set(examples_cmakelists ${contrib_cmakelist}) + else() + set(dependency_visited "${dependency_visited};${module_name}" CACHE INTERNAL + "" + ) + set(examples_cmakelists ${src_cmakelist}) + endif() + + # cmake-format: off + # Scan dependencies required by this module examples + #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() + # cmake-format: on + + # For each dependency, call this same function + set(matches "${matches};${example_matches}") + foreach(match ${matches}) + if(NOT ((${match} IN_LIST dependency_visited) + OR (${match} IN_LIST contrib_dependency_visited)) + ) + recursive_dependency(${match}) + endif() + endforeach() +endfunction() + +macro( + filter_enabled_and_disabled_modules + libs_to_build + contrib_libs_to_build + NS3_ENABLED_MODULES + NS3_DISABLED_MODULES + ns3rc_enabled_modules + ns3rc_disabled_modules +) + mark_as_advanced(ns3-all-enabled-modules) + + # 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}) + set(ns3rc_enabled_modules ${${NS3_ENABLED_MODULES}}) + endif() + + # Filter enabled modules + filter_modules(ns3rc_enabled_modules libs_to_build "") + filter_modules(ns3rc_enabled_modules contrib_libs_to_build "") + + # Use recursion to automatically determine dependencies required by the + # manually enabled modules + foreach(lib ${${contrib_libs_to_build}}) + resolve_dependencies(${lib} dependencies contrib_dependencies) + list(APPEND ${contrib_libs_to_build} "${contrib_dependencies}") + list(APPEND ${libs_to_build} "${dependencies}") + unset(dependencies) + unset(contrib_dependencies) + endforeach() + + foreach(lib ${${libs_to_build}}) + resolve_dependencies(${lib} dependencies contrib_dependencies) + list(APPEND ${libs_to_build} "${dependencies}") + unset(dependencies) + unset(contrib_dependencies) + endforeach() + endif() + + if(${NS3_DISABLED_MODULES} OR ${ns3rc_disabled_modules}) + # List of disabled modules passed by the command line overwrites list read + # from ns3rc + + if(${NS3_DISABLED_MODULES}) + set(ns3rc_disabled_modules ${${NS3_DISABLED_MODULES}}) + endif() + + 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 "${${ns3rc_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 + if(NOT ${contrib_libs_to_build}) + set(${contrib_libs_to_build} "") + endif() + + # Filter out any eventual duplicates + list(REMOVE_DUPLICATES ${libs_to_build}) + list(REMOVE_DUPLICATES ${contrib_libs_to_build}) + + # Export list with all enabled modules (used to separate ns libraries from + # non-ns libraries in ns3_module_macros) + set(ns3-all-enabled-modules "${${libs_to_build}};${${contrib_libs_to_build}}" + CACHE INTERNAL "list with all enabled modules" + ) +endmacro() diff --git a/build-support/custom-modules/ns3-find-external-library.cmake b/build-support/custom-modules/ns3-find-external-library.cmake new file mode 100644 index 000000000..9996a5944 --- /dev/null +++ b/build-support/custom-modules/ns3-find-external-library.cmake @@ -0,0 +1,328 @@ +# Copyright (c) 2023 Universidade de Brasília +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License version 2 as published by the Free +# Software Foundation; +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA +# +# Author: Gabriel Ferreira + +# This is our own variant of find_package, for CMake and non-CMake based +# projects +function(log_find_searched_paths) + # Parse arguments + set(options) + set(oneValueArgs TARGET_TYPE TARGET_NAME SEARCH_RESULT SEARCH_SYSTEM_PREFIX) + set(multiValueArgs SEARCH_PATHS SEARCH_SUFFIXES) + cmake_parse_arguments( + "LOGFIND" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} + ) + + # Get searched paths and add cmake_system_prefix_path if not explicitly marked + # not to include it + set(tsearch_paths ${LOGFIND_SEARCH_PATHS}) + if("${LOGFIND_SEARCH_SYSTEM_PREFIX}" STREQUAL "") + list(APPEND tsearch_paths "${CMAKE_SYSTEM_PREFIX_PATH}") + endif() + + set(log_find + "Looking for ${LOGFIND_TARGET_TYPE} ${LOGFIND_TARGET_NAME} in:\n" + ) + # For each search path and suffix combination, print a line + foreach(tsearch_path ${tsearch_paths}) + foreach(suffix ${LOGFIND_SEARCH_SUFFIXES}) + string(APPEND log_find + "\t${tsearch_path}${suffix}/${LOGFIND_TARGET_NAME}\n" + ) + endforeach() + endforeach() + + # Add a final line saying if the file was found and where, or if it was not + # found + if("${${LOGFIND_SEARCH_RESULT}}" STREQUAL "${LOGFIND_SEARCH_RESULT}-NOTFOUND") + string(APPEND log_find + "\n\t${LOGFIND_TARGET_TYPE} ${LOGFIND_TARGET_NAME} was not found\n" + ) + else() + string( + APPEND + log_find + "\n\t${LOGFIND_TARGET_TYPE} ${LOGFIND_TARGET_NAME} was found in ${${LOGFIND_SEARCH_RESULT}}\n" + ) + endif() + + # Replace duplicate path separators + string(REPLACE "//" "/" log_find "${log_find}") + + # Print find debug message similar to the one produced by + # CMAKE_FIND_DEBUG_MODE=true in CMake >= 3.17 + message(STATUS ${log_find}) +endfunction() + +function(find_external_library) + # Parse arguments + set(options QUIET) + set(oneValueArgs DEPENDENCY_NAME HEADER_NAME LIBRARY_NAME) + set(multiValueArgs HEADER_NAMES LIBRARY_NAMES PATH_SUFFIXES SEARCH_PATHS) + cmake_parse_arguments( + "FIND_LIB" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} + ) + + # Set the external package/dependency name + set(name ${FIND_LIB_DEPENDENCY_NAME}) + + # We process individual and list of headers and libraries by transforming them + # into lists + set(library_names "${FIND_LIB_LIBRARY_NAME};${FIND_LIB_LIBRARY_NAMES}") + set(header_names "${FIND_LIB_HEADER_NAME};${FIND_LIB_HEADER_NAMES}") + + # Just changing the parsed argument name back to something shorter + set(search_paths ${FIND_LIB_SEARCH_PATHS}) + set(path_suffixes "${FIND_LIB_PATH_SUFFIXES}") + + set(not_found_libraries) + set(library_dirs) + set(libraries) + + # Include parent directories in the search paths to handle Bake cases + get_filename_component(parent_project_dir ${PROJECT_SOURCE_DIR} DIRECTORY) + get_filename_component( + grandparent_project_dir ${parent_project_dir} DIRECTORY + ) + set(project_parent_dirs ${parent_project_dir} ${grandparent_project_dir}) + + # Paths and suffixes where libraries will be searched on + set(library_search_paths + ${search_paths} + ${project_parent_dirs} + ${CMAKE_OUTPUT_DIRECTORY} # Search for libraries in ns-3-dev/build + ${CMAKE_INSTALL_PREFIX} # Search for libraries in the install directory + # (e.g. /usr/) + $ENV{LD_LIBRARY_PATH} # Search for libraries in LD_LIBRARY_PATH + # directories + $ENV{PATH} # Search for libraries in PATH directories + ) + # cmake-format: off + # + # Split : separated entries from environment variables + # by replacing separators with ; + # + # cmake-format: on + string(REPLACE ":" ";" library_search_paths "${library_search_paths}") + + set(suffixes /build /lib /build/lib / /bin ${path_suffixes}) + + # For each of the library names in LIBRARY_NAMES or LIBRARY_NAME + foreach(library ${library_names}) + # We mark this value is advanced not to pollute the configuration with + # ccmake with the cache variables used internally + mark_as_advanced(${name}_library_internal_${library}) + + # We search for the library named ${library} and store the results in + # ${name}_library_internal_${library} + find_library( + ${name}_library_internal_${library} ${library} + HINTS ${library_search_paths} PATH_SUFFIXES ${suffixes} + ) + # cmake-format: off + # Note: the PATH_SUFFIXES above apply to *ALL* PATHS and HINTS Which + # translates to CMake searching on standard library directories + # CMAKE_SYSTEM_PREFIX_PATH, user-settable CMAKE_PREFIX_PATH or + # CMAKE_LIBRARY_PATH and the directories listed above + # + # e.g. from Ubuntu 22.04 CMAKE_SYSTEM_PREFIX_PATH = + # /usr/local;/usr;/;/usr/local;/usr/X11R6;/usr/pkg;/opt + # + # Searched directories without suffixes + # + # ${CMAKE_SYSTEM_PREFIX_PATH}[0] = /usr/local/ + # ${CMAKE_SYSTEM_PREFIX_PATH}[1] = /usr + # ${CMAKE_SYSTEM_PREFIX_PATH}[2] = / + # ... + # ${CMAKE_SYSTEM_PREFIX_PATH}[6] = /opt + # ${LD_LIBRARY_PATH}[0] + # ... + # ${LD_LIBRARY_PATH}[m] + # ${PATH}[0] + # ... + # ${PATH}[m] + # + # Searched directories with suffixes include all of the directories above + # plus all suffixes + # PATH_SUFFIXES /build /lib /build/lib / /bin # ${path_suffixes} + # + # /usr/local/build + # /usr/local/lib + # /usr/local/build/lib + # /usr/local/bin + # ... + # + # cmake-format: on + # Or enable NS3_VERBOSE to print the searched paths + + # Print tested paths to the searched library and if it was found + if(${NS3_VERBOSE} AND (${CMAKE_VERSION} VERSION_LESS "3.17.0")) + log_find_searched_paths( + TARGET_TYPE + Library + TARGET_NAME + ${library} + SEARCH_RESULT + ${name}_library_internal_${library} + SEARCH_PATHS + ${library_search_paths} + SEARCH_SUFFIXES + ${suffixes} + ) + endif() + + # After searching the library, the internal variable should have either the + # absolute path to the library or the name of the variable appended with + # -NOTFOUND + if("${${name}_library_internal_${library}}" STREQUAL + "${name}_library_internal_${library}-NOTFOUND" + ) + # We keep track of libraries that were not found + list(APPEND not_found_libraries ${library}) + else() + # We get the name of the parent directory of the library and append the + # library to a list of found libraries + get_filename_component( + ${name}_library_dir_internal ${${name}_library_internal_${library}} + DIRECTORY + ) # e.g. lib/openflow.(so|dll|dylib|a) -> lib + list(APPEND library_dirs ${${name}_library_dir_internal}) + list(APPEND libraries ${${name}_library_internal_${library}}) + endif() + endforeach() + + # For each library that was found (e.g. /usr/lib/pthread.so), get their parent + # directory (/usr/lib) and its parent (/usr) + set(parent_dirs) + foreach(libdir ${library_dirs}) + get_filename_component(parent_libdir ${libdir} DIRECTORY) + get_filename_component(parent_parent_libdir ${parent_libdir} DIRECTORY) + list(APPEND parent_dirs ${libdir} ${parent_libdir} ${parent_parent_libdir}) + endforeach() + + set(header_search_paths + ${search_paths} + ${parent_dirs} + ${project_parent_dirs} + ${CMAKE_OUTPUT_DIRECTORY} # Search for headers in + # ns-3-dev/build + ${CMAKE_INSTALL_PREFIX} # Search for headers in the install + ) + + set(not_found_headers) + set(include_dirs) + foreach(header ${header_names}) + # The same way with libraries, we mark the internal variable as advanced not + # to pollute ccmake configuration with variables used internally + mark_as_advanced(${name}_header_internal_${header}) + set(suffixes + /build + /include + /build/include + /build/include/${name} + /include/${name} + /${name} + / + ${path_suffixes} + ) + + # cmake-format: off + # Here we search for the header file named ${header} and store the result in + # ${name}_header_internal_${header} + # + # The same way we did with libraries, here we search on + # CMAKE_SYSTEM_PREFIX_PATH, along with user-settable ${search_paths}, the + # parent directories from the libraries, CMAKE_OUTPUT_DIRECTORY and + # CMAKE_INSTALL_PREFIX + # + # And again, for each of them, for every suffix listed /usr/local/build + # /usr/local/include + # /usr/local/build/include + # /usr/local/build/include/${name} + # /usr/local/include/${name} + # ... + # + # cmake-format: on + # Or enable NS3_VERBOSE to get the searched paths printed while configuring + + find_file(${name}_header_internal_${header} ${header} + HINTS ${header_search_paths} # directory (e.g. /usr/) + ${header_skip_system_prefix} PATH_SUFFIXES ${suffixes} + ) + + # Print tested paths to the searched header and if it was found + if(${NS3_VERBOSE} AND (${CMAKE_VERSION} VERSION_LESS "3.17.0")) + log_find_searched_paths( + TARGET_TYPE + Header + TARGET_NAME + ${header} + SEARCH_RESULT + ${name}_header_internal_${header} + SEARCH_PATHS + ${header_search_paths} + SEARCH_SUFFIXES + ${suffixes} + SEARCH_SYSTEM_PREFIX + ${header_skip_system_prefix} + ) + endif() + + # If the header file was not found, append to the not-found list + if("${${name}_header_internal_${header}}" STREQUAL + "${name}_header_internal_${header}-NOTFOUND" + ) + list(APPEND not_found_headers ${header}) + else() + # If the header file was found, get their directories and the parent of + # their directories to add as include directories + get_filename_component( + header_include_dir ${${name}_header_internal_${header}} DIRECTORY + ) # e.g. include/click/ (simclick.h) -> #include should work + get_filename_component( + header_include_dir2 ${header_include_dir} DIRECTORY + ) # e.g. include/(click) -> #include should work + list(APPEND include_dirs ${header_include_dir} ${header_include_dir2}) + endif() + endforeach() + + # Remove duplicate include directories + if(include_dirs) + list(REMOVE_DUPLICATES include_dirs) + endif() + + # If we find both library and header, we export their values + if((NOT not_found_libraries) AND (NOT not_found_headers)) + set(${name}_INCLUDE_DIRS "${include_dirs}" PARENT_SCOPE) + set(${name}_LIBRARIES "${libraries}" PARENT_SCOPE) + set(${name}_HEADER ${${name}_header_internal} PARENT_SCOPE) + set(${name}_FOUND TRUE PARENT_SCOPE) + if(NOT ${FIND_LIB_QUIET}) + message(STATUS "find_external_library: ${name} was found.") + endif() + else() + set(${name}_INCLUDE_DIRS PARENT_SCOPE) + set(${name}_LIBRARIES PARENT_SCOPE) + set(${name}_HEADER PARENT_SCOPE) + set(${name}_FOUND FALSE PARENT_SCOPE) + if(NOT ${FIND_LIB_QUIET}) + message( + ${HIGHLIGHTED_STATUS} + "find_external_library: ${name} was not found. Missing headers: \"${not_found_headers}\" and missing libraries: \"${not_found_libraries}\"." + ) + endif() + endif() +endfunction() diff --git a/build-support/custom-modules/ns3-hidden-settings.cmake b/build-support/custom-modules/ns3-hidden-settings.cmake new file mode 100644 index 000000000..e10976f51 --- /dev/null +++ b/build-support/custom-modules/ns3-hidden-settings.cmake @@ -0,0 +1,49 @@ +# Copyright (c) 2023 Universidade de Brasília +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License version 2 as published by the Free +# Software Foundation; +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA +# +# Author: Gabriel Ferreira + +# Set INT128 as the default option for INT64X64 and register alternative +# implementations +set(NS3_INT64X64 "INT128" CACHE STRING "Int64x64 implementation") +set_property(CACHE NS3_INT64X64 PROPERTY STRINGS INT128 CAIRO DOUBLE) + +# Purposefully hidden options: + +# For ease of use, export all libraries and include directories to ns-3 module +# consumers by default +option(NS3_REEXPORT_THIRD_PARTY_LIBRARIES "Export all third-party libraries +and include directories to ns-3 module consumers" ON +) + +# Since we can't really do that safely from the CMake side +option(NS3_ENABLE_SUDO + "Set executables ownership to root and enable the SUID flag" OFF +) + +# A flag that controls some aspects related to pip packaging +option(NS3_PIP_PACKAGING "Control aspects related to pip wheel packaging" OFF) + +# fPIC (position-independent code) and fPIE (position-independent executable) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# Do not create a file-level dependency with shared libraries reducing +# unnecessary relinking +set(CMAKE_LINK_DEPENDS_NO_SHARED TRUE) + +# Honor CMAKE_CXX_STANDARD in check_cxx_source_compiles +# https://cmake.org/cmake/help/latest/policy/CMP0067.html +cmake_policy(SET CMP0066 NEW) +cmake_policy(SET CMP0067 NEW) diff --git a/build-support/custom-modules/ns3-module-macros.cmake b/build-support/custom-modules/ns3-module-macros.cmake index 82e428720..c9c0054d7 100644 --- a/build-support/custom-modules/ns3-module-macros.cmake +++ b/build-support/custom-modules/ns3-module-macros.cmake @@ -15,6 +15,80 @@ # # Author: Gabriel Ferreira +add_custom_target(copy_all_headers) +function(copy_headers_before_building_lib libname outputdir headers visibility) + foreach(header ${headers}) + # Copy header to output directory on changes -> too darn slow + # configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${header} ${outputdir}/ + # COPYONLY) + + get_filename_component( + header_name ${CMAKE_CURRENT_SOURCE_DIR}/${header} NAME + ) + + # If output directory does not exist, create it + if(NOT (EXISTS ${outputdir})) + file(MAKE_DIRECTORY ${outputdir}) + endif() + + # If header already exists, skip symlinking/stub header creation + if(EXISTS ${outputdir}/${header_name}) + continue() + endif() + + # Create a stub header in the output directory, including the real header + # inside their respective module + get_filename_component( + ABSOLUTE_HEADER_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${header}" ABSOLUTE + ) + file(WRITE ${outputdir}/${header_name} + "#include \"${ABSOLUTE_HEADER_PATH}\"\n" + ) + endforeach() +endfunction(copy_headers_before_building_lib) + +function(remove_lib_prefix prefixed_library library) + # Check if there is a lib prefix + string(FIND "${prefixed_library}" "lib" lib_pos) + + # If there is a lib prefix, try to remove it + if(${lib_pos} EQUAL 0) + # Check if we still have something remaining after removing the "lib" prefix + string(LENGTH ${prefixed_library} len) + if(${len} LESS 4) + message(FATAL_ERROR "Invalid library name: ${prefixed_library}") + endif() + + # Remove lib prefix from module name (e.g. libcore -> core) + string(SUBSTRING "${prefixed_library}" 3 -1 unprefixed_library) + else() + set(unprefixed_library ${prefixed_library}) + endif() + + # Save the unprefixed library name to the parent scope + set(${library} ${unprefixed_library} PARENT_SCOPE) +endfunction() + +function(check_for_missing_libraries output_variable_name libraries) + set(missing_dependencies) + foreach(lib ${libraries}) + # skip check for ns-3 modules if its a path to a library + if(EXISTS ${lib}) + continue() + endif() + + # check if the example depends on disabled modules + remove_lib_prefix("${lib}" lib) + + # Check if the module exists in the ns-3 modules list or if it is a + # 3rd-party library + if(NOT (${lib} IN_LIST ns3-all-enabled-modules)) + list(APPEND missing_dependencies ${lib}) + endif() + endforeach() + set(${output_variable_name} ${missing_dependencies} PARENT_SCOPE) +endfunction() + # cmake-format: off # # This macro processes a ns-3 module diff --git a/build-support/custom-modules/ns3-ns3rc-parser.cmake b/build-support/custom-modules/ns3-ns3rc-parser.cmake new file mode 100644 index 000000000..a42fd8f25 --- /dev/null +++ b/build-support/custom-modules/ns3-ns3rc-parser.cmake @@ -0,0 +1,110 @@ +# Copyright (c) 2023 Universidade de Brasília +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License version 2 as published by the Free +# Software Foundation; +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA +# +# Author: Gabriel Ferreira + +# Parse .ns3rc +macro(parse_ns3rc enabled_modules disabled_modules examples_enabled + tests_enabled +) + # Try to find .ns3rc + find_file(NS3RC .ns3rc PATHS /etc $ENV{HOME} $ENV{USERPROFILE} + ${PROJECT_SOURCE_DIR} NO_CACHE + ) + + # Set variables with default values (all modules, no examples nor tests) + set(${enabled_modules} "") + set(${disabled_modules} "") + set(${examples_enabled} "FALSE") + set(${tests_enabled} "FALSE") + + if(NOT (${NS3RC} STREQUAL "NS3RC-NOTFOUND")) + message(${HIGHLIGHTED_STATUS} + "Configuration file .ns3rc being used : ${NS3RC}" + ) + file(READ ${NS3RC} ns3rc_contents) + # Check if ns3rc file is CMake or Python based and act accordingly + if(ns3rc_contents MATCHES "ns3rc_*") + include(${NS3RC}) + else() + parse_python_ns3rc( + "${ns3rc_contents}" ${enabled_modules} ${examples_enabled} + ${tests_enabled} ${NS3RC} + ) + endif() + endif() +endmacro(parse_ns3rc) + +function(parse_python_ns3rc ns3rc_contents enabled_modules examples_enabled + tests_enabled ns3rc_location +) + # Save .ns3rc backup + file(WRITE ${ns3rc_location}.backup ${ns3rc_contents}) + + # Match modules_enabled list + if(ns3rc_contents MATCHES "modules_enabled.*\\[(.*).*\\]") + set(${enabled_modules} ${CMAKE_MATCH_1}) + if(${enabled_modules} MATCHES "all_modules") + # If all modules, just clean the filter and all modules will get built by + # default + set(${enabled_modules}) + else() + # If modules are listed, remove quotes and replace commas with 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}}") + string(REPLACE " " "" ${enabled_modules} "${${enabled_modules}}") + string(REPLACE "\n" ";" ${enabled_modules} "${${enabled_modules}}") + list(SORT ${enabled_modules}) + + # Remove possibly empty entry + list(REMOVE_ITEM ${enabled_modules} "") + foreach(element ${${enabled_modules}}) + # Inspect each element for comments + if(${element} MATCHES "#.*") + list(REMOVE_ITEM ${enabled_modules} ${element}) + endif() + endforeach() + endif() + endif() + + string(REPLACE "True" "ON" ns3rc_contents ${ns3rc_contents}) + string(REPLACE "False" "OFF" ns3rc_contents ${ns3rc_contents}) + + # Match examples_enabled flag + if(ns3rc_contents MATCHES "examples_enabled = (ON|OFF)") + set(${examples_enabled} ${CMAKE_MATCH_1}) + endif() + + # Match tests_enabled flag + if(ns3rc_contents MATCHES "tests_enabled = (ON|OFF)") + set(${tests_enabled} ${CMAKE_MATCH_1}) + endif() + + # Save variables to parent scope + set(${enabled_modules} "${${enabled_modules}}" PARENT_SCOPE) + set(${examples_enabled} "${${examples_enabled}}" PARENT_SCOPE) + set(${tests_enabled} "${${tests_enabled}}" PARENT_SCOPE) + + # Save updated .ns3rc file + message( + ${HIGHLIGHTED_STATUS} + "The python-based .ns3rc file format is deprecated and was updated to the CMake format" + ) + configure_file( + ${PROJECT_SOURCE_DIR}/build-support/.ns3rc-template ${ns3rc_location} @ONLY + ) +endfunction(parse_python_ns3rc) diff --git a/build-support/custom-modules/ns3-output-directory.cmake b/build-support/custom-modules/ns3-output-directory.cmake new file mode 100644 index 000000000..e2171fae8 --- /dev/null +++ b/build-support/custom-modules/ns3-output-directory.cmake @@ -0,0 +1,76 @@ +# Copyright (c) 2023 Universidade de Brasília +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License version 2 as published by the Free +# Software Foundation; +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA +# +# Author: Gabriel Ferreira + +# This is where we define where executables, libraries and headers will end up + +if("${NS3_OUTPUT_DIRECTORY}" STREQUAL "") + message(STATUS "Using default output directory ${PROJECT_SOURCE_DIR}/build") + set(CMAKE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/build) # default output + # folder +else() + # 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() + + # Transform backward slash into forward slash Not the best way to do it since + # \ is a scape thing and can be used before whitespaces + string(REPLACE "\\" "/" absolute_ns3_output_directory + "${absolute_ns3_output_directory}" + ) + + 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 ${absolute_ns3_output_directory}) + if(NOT (EXISTS ${absolute_ns3_output_directory})) + message( + FATAL_ERROR + "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 ("${absolute_ns3_output_directory}" MATCHES "${PROJECT_SOURCE_DIR}")) + message( + WARNING + "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 \"${absolute_ns3_output_directory}\" will be used" + ) + 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) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}) +set(CMAKE_HEADER_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/include/ns3) +set(THIRD_PARTY_DIRECTORY ${PROJECT_SOURCE_DIR}/3rd-party) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +link_directories(${CMAKE_OUTPUT_DIRECTORY}/lib) +file(MAKE_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}) diff --git a/build-support/custom-modules/ns3-platform-support.cmake b/build-support/custom-modules/ns3-platform-support.cmake new file mode 100644 index 000000000..bc83344e5 --- /dev/null +++ b/build-support/custom-modules/ns3-platform-support.cmake @@ -0,0 +1,81 @@ +# Copyright (c) 2023 Universidade de Brasília +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License version 2 as published by the Free +# Software Foundation; +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA +# +# Author: Gabriel Ferreira + +# This is where we check for platform specifics + +# WSLv1 doesn't support tap features +if(EXISTS "/proc/version") + file(READ "/proc/version" CMAKE_LINUX_DISTRO) + string(FIND "${CMAKE_LINUX_DISTRO}" "Microsoft" res) + if(res EQUAL -1) + set(WSLv1 False) + else() + set(WSLv1 True) + endif() +endif() + +# Set Linux flag if on Linux +if(UNIX AND NOT APPLE) + set(LINUX TRUE) + add_definitions(-D__LINUX__) +endif() + +if(APPLE) + add_definitions(-D__APPLE__) + # cmake-format: off + # Configure find_program to search for AppBundles only if programs are not found in PATH. + # This prevents Doxywizard from being launched when the Doxygen.app is installed. + # https://gitlab.kitware.com/cmake/cmake/-/blob/master/Modules/FindDoxygen.cmake + # cmake-format: on + set(CMAKE_FIND_APPBUNDLE "LAST") +endif() + +if(WIN32) + set(NS3_PRECOMPILE_HEADERS OFF + CACHE BOOL "Precompile module headers to speed up compilation" FORCE + ) + + # For whatever reason getting M_PI and other math.h definitions from cmath + # requires this definition + # https://docs.microsoft.com/en-us/cpp/c-runtime-library/math-constants?view=vs-2019 + add_definitions(/D_USE_MATH_DEFINES) +endif() + +set(cat_command cat) + +if(CMAKE_XCODE_BUILD_SYSTEM) + set(XCODE True) +else() + set(XCODE False) +endif() + +if(${XCODE}) + # Is that so hard not to break people's CI, AAPL? Why would you output the + # targets to a Debug/Release subfolder? Why? + foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + ) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} + ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} + ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + endforeach() +endif() diff --git a/build-support/macros-and-definitions.cmake b/build-support/macros-and-definitions.cmake index 18825092b..97a3d967f 100644 --- a/build-support/macros-and-definitions.cmake +++ b/build-support/macros-and-definitions.cmake @@ -18,246 +18,40 @@ # Export compile time variable setting the directory to the NS3 root folder add_definitions(-DPROJECT_SOURCE_PATH="${PROJECT_SOURCE_DIR}") -# Set INT128 as the default option for INT64X64 and register alternative -# implementations -set(NS3_INT64X64 "INT128" CACHE STRING "Int64x64 implementation") -set_property(CACHE NS3_INT64X64 PROPERTY STRINGS INT128 CAIRO DOUBLE) - -# Purposefully hidden options: - -# for ease of use, export all libraries and include directories to ns-3 module -# consumers by default -option(NS3_REEXPORT_THIRD_PARTY_LIBRARIES "Export all third-party libraries -and include directories to ns-3 module consumers" ON +# Add custom and 3rd-part cmake modules to the path +list(APPEND CMAKE_MODULE_PATH + "${PROJECT_SOURCE_DIR}/build-support/custom-modules" ) +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/build-support/3rd-party") -# since we can't really do that safely from the CMake side -option(NS3_ENABLE_SUDO - "Set executables ownership to root and enable the SUID flag" OFF -) - -# a flag that controls some aspects related to pip packaging -option(NS3_PIP_PACKAGING "Control aspects related to pip wheel packaging" OFF) +# Set options that are not really meant to be changed +include(ns3-hidden-settings) # Replace default CMake messages (logging) with custom colored messages as early # as possible -include(${PROJECT_SOURCE_DIR}/build-support/3rd-party/colored-messages.cmake) +include(colored-messages) -# WSLv1 doesn't support tap features -if(EXISTS "/proc/version") - file(READ "/proc/version" CMAKE_LINUX_DISTRO) - string(FIND "${CMAKE_LINUX_DISTRO}" "Microsoft" res) - if(res EQUAL -1) - set(WSLv1 False) - else() - set(WSLv1 True) - endif() -endif() - -# Set Linux flag if on Linux -if(UNIX AND NOT APPLE) - set(LINUX TRUE) - add_definitions(-D__LINUX__) -endif() - -if(APPLE) - add_definitions(-D__APPLE__) - # cmake-format: off - # Configure find_program to search for AppBundles only if programs are not found in PATH. - # This prevents Doxywizard from being launched when the Doxygen.app is installed. - # https://gitlab.kitware.com/cmake/cmake/-/blob/master/Modules/FindDoxygen.cmake - # cmake-format: on - set(CMAKE_FIND_APPBUNDLE "LAST") -endif() - -if(WIN32) - set(NS3_PRECOMPILE_HEADERS OFF - CACHE BOOL "Precompile module headers to speed up compilation" FORCE - ) - - # For whatever reason getting M_PI and other math.h definitions from cmath - # requires this definition - # https://docs.microsoft.com/en-us/cpp/c-runtime-library/math-constants?view=vs-2019 - add_definitions(/D_USE_MATH_DEFINES) -endif() - -set(cat_command cat) - -if(CMAKE_XCODE_BUILD_SYSTEM) - set(XCODE True) -else() - set(XCODE False) -endif() - -# Check the number of threads -include(ProcessorCount) -ProcessorCount(NumThreads) - -# Output folders -if("${NS3_OUTPUT_DIRECTORY}" STREQUAL "") - message(STATUS "Using default output directory ${PROJECT_SOURCE_DIR}/build") - set(CMAKE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/build) # default output - # folder -else() - # 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() - - # Transform backward slash into forward slash Not the best way to do it since - # \ is a scape thing and can be used before whitespaces - string(REPLACE "\\" "/" absolute_ns3_output_directory - "${absolute_ns3_output_directory}" - ) - - 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 ${absolute_ns3_output_directory}) - if(NOT (EXISTS ${absolute_ns3_output_directory})) - message( - FATAL_ERROR - "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 ("${absolute_ns3_output_directory}" MATCHES "${PROJECT_SOURCE_DIR}")) - message( - WARNING - "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 \"${absolute_ns3_output_directory}\" will be used" - ) - 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) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}) -set(CMAKE_HEADER_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/include/ns3) -set(THIRD_PARTY_DIRECTORY ${PROJECT_SOURCE_DIR}/3rd-party) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -link_directories(${CMAKE_OUTPUT_DIRECTORY}/lib) -file(MAKE_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}) +# Define output directories +include(ns3-output-directory) # Get installation folder default values for each platform and include package # configuration macro -include(GNUInstallDirs) -include(build-support/custom-modules/ns3-cmake-package.cmake) +include(ns3-cmake-package) -# Set RPATH not too need LD_LIBRARY_PATH after installing -set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib:$ORIGIN/:$ORIGIN/../lib") +# Windows, Linux and Mac related checks +include(ns3-platform-support) -# Add the 64 suffix to the library path when manually requested with the -# -DNS3_USE_LIB64=ON flag. May be necessary depending on the target platform. -# This is used to properly build the manylinux pip wheel. -if(${NS3_USE_LIB64}) - link_directories(${CMAKE_OUTPUT_DIRECTORY}/lib64) - set(CMAKE_INSTALL_RPATH - "${CMAKE_INSTALL_RPATH}:${CMAKE_INSTALL_PREFIX}/lib64:$ORIGIN/:$ORIGIN/../lib64" - ) -endif() +# Perform compiler and linker checks +include(ns3-compiler-and-linker-support) -# cmake-format: off -# You are a wizard, Harry! -# source: https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/RPATH-handling -# cmake-format: on -set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +# Include CMake file with common find_program HINTS +include(find-program-hints) -if(${XCODE}) - # Is that so hard not to break people's CI, AAPL? Why would you output the - # targets to a Debug/Release subfolder? Why? - foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) - string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} - ) - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} - ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} - ) - set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} - ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} - ) - endforeach() -endif() +# Custom find_package alternative +include(ns3-find-external-library) -# fPIC (position-independent code) and fPIE (position-independent executable) -set(CMAKE_POSITION_INDEPENDENT_CODE ON) - -# do not create a file-level dependency with shared libraries reducing -# unnecessary relinking -set(CMAKE_LINK_DEPENDS_NO_SHARED TRUE) - -# 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 "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() - -if(CLANG) - if(${NS3_COLORED_OUTPUT} OR "$ENV{CLICOLOR}") - add_definitions(-fcolor-diagnostics) # colorize clang++ output - endif() -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() - if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.2.0")) - # PCH causes weird errors on certain versions of GCC when C++20 is enabled - # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106799 - set(NS3_PRECOMPILE_HEADERS OFF - CACHE BOOL "Precompile module headers to speed up compilation" FORCE - ) - endif() - if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.1.0") - AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "12.3.2") - ) - # Disable warnings-as-errors for certain versions of GCC when C++20 is - # enabled https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105545 - add_compile_options(-Wno-restrict) - endif() - set(GCC TRUE) - add_definitions(-fno-semantic-interposition) - if(${NS3_COLORED_OUTPUT} OR "$ENV{CLICOLOR}") - add_definitions(-fdiagnostics-color=always) # colorize g++ output - endif() -endif() -unset(below_minimum_msg) +# Custom check_deps to search for packages, executables and python dependencies +include(ns3-check-dependencies) # Set compiler options and get command to force unused function linkage (useful # for libraries) @@ -265,56 +59,6 @@ set(CXX_UNSUPPORTED_STANDARDS 98 11 14 17) set(CMAKE_CXX_STANDARD_MINIMUM 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -set(LIB_AS_NEEDED_PRE) -set(LIB_AS_NEEDED_POST) -set(STATIC_LINK_FLAGS -static -static-libstdc++ -static-libgcc) -if(${GCC} AND NOT APPLE) - # using GCC - set(LIB_AS_NEEDED_PRE -Wl,--no-as-needed) - set(LIB_AS_NEEDED_POST -Wl,--as-needed) - set(LIB_AS_NEEDED_PRE_STATIC -Wl,--whole-archive,-Bstatic) - set(LIB_AS_NEEDED_POST_STATIC -Wl,--no-whole-archive) - set(LIB_AS_NEEDED_POST_STATIC_DYN -Wl,-Bdynamic,--no-whole-archive) -endif() - -if(${CLANG} AND APPLE) - # using Clang set(LIB_AS_NEEDED_PRE -all_load) - set(LIB_AS_NEEDED_POST) - set(LIB_AS_NEEDED_PRE_STATIC -Wl,-all_load) - set(STATIC_LINK_FLAGS) -endif() - -if(${NS3_FAST_LINKERS}) - # Search for faster linkers mold and lld, and use them if available - mark_as_advanced(MOLD LLD) - find_program(MOLD mold) - find_program(LLD ld.lld) - - # USING_FAST_LINKER will be defined if a fast linker is being used and its - # content will correspond to the fast linker name - - # Mold support was added in GCC 12.1.0 - if(NOT USING_FAST_LINKER - AND NOT (${MOLD} STREQUAL "MOLD-NOTFOUND") - AND LINUX - AND ${GCC} - AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12.1.0) - ) - set(USING_FAST_LINKER MOLD) - add_link_options("-fuse-ld=mold") - endif() - - if(NOT USING_FAST_LINKER AND NOT (${LLD} STREQUAL "LLD-NOTFOUND") - AND (${GCC} OR ${CLANG}) - ) - set(USING_FAST_LINKER LLD) - add_link_options("-fuse-ld=lld") - if(WIN32) - # Clear unsupported linker flags on Windows - set(LIB_AS_NEEDED_PRE) - endif() - endif() -endif() # Include CMake files used for compiler checks include(CheckIncludeFile) # Used to check a single C header at a time @@ -322,6 +66,10 @@ include(CheckIncludeFileCXX) # Used to check a single C++ header at a time include(CheckIncludeFiles) # Used to check multiple headers at once include(CheckFunctionExists) +# Check the number of threads +include(ProcessorCount) +ProcessorCount(NumThreads) + macro(SUBDIRLIST result curdir) file(GLOB children RELATIVE ${curdir} ${curdir}/*) set(dirlist "") @@ -367,39 +115,6 @@ macro(clear_global_cached_variables) ) endmacro() -# Include CMake file with common find_program HINTS -include(build-support/3rd-party/find-program-hints.cmake) - -# function used to search for package and program dependencies than store list -# of missing dependencies in the list whose name is stored in missing_deps -function(check_deps package_deps program_deps missing_deps) - set(local_missing_deps) - # Search for package dependencies - foreach(package ${package_deps}) - find_package(${package}) - if(NOT ${${package}_FOUND}) - list(APPEND local_missing_deps ${package}) - endif() - endforeach() - - # And for program dependencies - foreach(program ${program_deps}) - # CMake likes to cache find_* to speed things up, so we can't reuse names - # here or it won't check other dependencies - string(TOUPPER ${program} upper_${program}) - mark_as_advanced(${upper_${program}}) - find_program( - ${upper_${program}} ${program} HINTS ${3RD_PARTY_FIND_PROGRAM_HINTS} - ) - if("${${upper_${program}}}" STREQUAL "${upper_${program}}-NOTFOUND") - list(APPEND local_missing_deps ${program}) - endif() - endforeach() - - # Store list of missing dependencies in the parent scope - set(${missing_deps} ${local_missing_deps} PARENT_SCOPE) -endfunction() - # process all options passed in main cmakeLists macro(process_options) clear_global_cached_variables() @@ -486,7 +201,7 @@ macro(process_options) endif() endif() - include(build-support/custom-modules/ns3-versioning.cmake) + include(ns3-versioning) set(ENABLE_BUILD_VERSION False) configure_embedded_version() @@ -610,14 +325,9 @@ macro(process_options) ) endif() - # Honor CMAKE_CXX_STANDARD in check_cxx_source_compiles - # https://cmake.org/cmake/help/latest/policy/CMP0067.html - cmake_policy(SET CMP0066 NEW) - cmake_policy(SET CMP0067 NEW) - # After setting the correct CXX version, we can proceed to check for compiler # workarounds - include(build-support/custom-modules/ns3-compiler-workarounds.cmake) + include(ns3-compiler-workarounds) if(${NS3_DES_METRICS}) add_definitions(-DENABLE_DES_METRICS) @@ -724,12 +434,6 @@ macro(process_options) # Set common include folder (./build/include, where we find ns3/core-module.h) include_directories(${CMAKE_OUTPUT_DIRECTORY}/include) - # find required dependencies - list(APPEND CMAKE_MODULE_PATH - "${PROJECT_SOURCE_DIR}/build-support/custom-modules" - ) - list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/build-support/3rd-party") - # Include our package managers # cmake-format: off # Starting with a custom cmake file that provides a Hunter-like interface to vcpkg @@ -877,7 +581,7 @@ macro(process_options) find_package(Python3 COMPONENTS Interpreter Development) else() # If Python was not set yet, use the version found by check_deps - check_deps("" "python3" python3_deps) + check_deps(python3_deps EXECUTABLES python3) if(python3_deps) message(FATAL_ERROR "Python3 was not found") else() @@ -939,7 +643,7 @@ macro(process_options) "macOS silicon detected -- see issue 930" ) else() - check_python_packages("cppyy" missing_packages) + check_deps(missing_packages PYTHON_PACKAGES cppyy) if(missing_packages) message( ${HIGHLIGHTED_STATUS} @@ -1072,7 +776,7 @@ macro(process_options) DEPENDS all-test-targets ) if(${ENABLE_EXAMPLES}) - include(build-support/custom-modules/ns3-coverage.cmake) + include(ns3-coverage) endif() endif() @@ -1117,7 +821,7 @@ macro(process_options) # First we check for doxygen dependencies mark_as_advanced(DOXYGEN) - check_deps("" "doxygen;dot;dia;python3" doxygen_docs_missing_deps) + check_deps(doxygen_docs_missing_deps EXECUTABLES doxygen dot dia python3) if(doxygen_docs_missing_deps) message( ${HIGHLIGHTED_STATUS} @@ -1233,8 +937,8 @@ macro(process_options) # Check deps accepts a list of packages, list of programs and name of the # return variable check_deps( - "Sphinx" "epstopdf;pdflatex;latexmk;convert;dvipng" - sphinx_docs_missing_deps + sphinx_docs_missing_deps CMAKE_PACKAGES Sphinx + EXECUTABLES epstopdf pdflatex latexmk convert dvipng ) if(sphinx_docs_missing_deps) message( @@ -1575,254 +1279,40 @@ macro(process_options) endif() if(${NS3_FETCH_OPTIONAL_COMPONENTS}) - include( - build-support/custom-modules/ns3-fetch-optional-modules-dependencies.cmake - ) + include(ns3-fetch-optional-modules-dependencies) endif() endmacro() -function(set_runtime_outputdirectory target_name output_directory target_prefix) - # Prevent duplicate '/' in EXECUTABLE_DIRECTORY_PATH, since it gets translated - # to doubled underlines and will cause the ns3 script to fail - string(REPLACE "//" "/" output_directory "${output_directory}") - - set(ns3-exec-outputname ns${NS3_VER}-${target_name}${build_profile_suffix}) - set(ns3-execs "${output_directory}${ns3-exec-outputname};${ns3-execs}" - CACHE INTERNAL "list of c++ executables" - ) - set(ns3-execs-clean "${target_prefix}${target_name};${ns3-execs-clean}" - CACHE INTERNAL - "list of c++ executables without version prefix and build suffix" - ) - - set_target_properties( - ${target_prefix}${target_name} - PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_directory} - RUNTIME_OUTPUT_NAME ${ns3-exec-outputname} - ) - if(${XCODE}) - # Is that so hard not to break people's CI, AAPL?? Why would you output the - # targets to a Debug/Release subfolder? Why? - foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) - string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG) - set_target_properties( - ${target_prefix}${target_name} - PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${output_directory} - RUNTIME_OUTPUT_NAME_${OUTPUTCONFIG} ${ns3-exec-outputname} - ) - endforeach(OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES) - endif() - - if(${ENABLE_TESTS}) - add_dependencies(all-test-targets ${target_prefix}${target_name}) - # Create a CTest entry for each executable - if(WIN32) - # Windows require this workaround to make sure the DLL files are located - add_test( - NAME ctest-${target_prefix}${target_name} - COMMAND - ${CMAKE_COMMAND} -E env - "PATH=$ENV{PATH};${CMAKE_RUNTIME_OUTPUT_DIRECTORY};${CMAKE_LIBRARY_OUTPUT_DIRECTORY}" - ${ns3-exec-outputname} - WORKING_DIRECTORY ${output_directory} - ) +function(get_target_includes target output) + set(include_directories) + get_target_property(include_dirs ${target} INCLUDE_DIRECTORIES) + list(REMOVE_DUPLICATES include_dirs) + foreach(include_dir ${include_dirs}) + if(include_dir MATCHES "<") + # Skip CMake build and install interface includes + continue() else() - add_test(NAME ctest-${target_prefix}${target_name} - COMMAND ${ns3-exec-outputname} - WORKING_DIRECTORY ${output_directory} - ) - endif() - endif() - - if(${NS3_CLANG_TIMETRACE}) - add_dependencies(timeTraceReport ${target_prefix}${target_name}) - endif() -endfunction(set_runtime_outputdirectory) - -function(get_scratch_prefix prefix) - # /path/to/ns-3-dev/scratch/nested-subdir - set(temp ${CMAKE_CURRENT_SOURCE_DIR}) - # remove /path/to/ns-3-dev/ to get scratch/nested-subdir - string(REPLACE "${PROJECT_SOURCE_DIR}/" "" temp "${temp}") - # replace path separators with underlines - string(REPLACE "/" "_" temp "${temp}") - # save the prefix value to the passed variable - set(${prefix} ${temp}_ PARENT_SCOPE) -endfunction() - -function(build_exec) - # Argument parsing - set(options IGNORE_PCH STANDALONE) - set(oneValueArgs EXECNAME EXECNAME_PREFIX EXECUTABLE_DIRECTORY_PATH - INSTALL_DIRECTORY_PATH - ) - set(multiValueArgs SOURCE_FILES HEADER_FILES LIBRARIES_TO_LINK DEFINITIONS) - cmake_parse_arguments( - "BEXEC" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} - ) - - # Resolve nested scratch prefixes without user intervention - string(REPLACE "${PROJECT_SOURCE_DIR}" "" relative_path - "${CMAKE_CURRENT_SOURCE_DIR}" - ) - if("${relative_path}" MATCHES "scratch" AND "${BEXEC_EXECNAME_PREFIX}" - STREQUAL "" - ) - get_scratch_prefix(BEXEC_EXECNAME_PREFIX) - endif() - - add_executable( - ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} "${BEXEC_SOURCE_FILES}" - ) - - target_compile_definitions( - ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} PUBLIC ${BEXEC_DEFINITIONS} - ) - - if(${PRECOMPILE_HEADERS_ENABLED} AND (NOT ${BEXEC_IGNORE_PCH})) - target_precompile_headers( - ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} REUSE_FROM stdlib_pch_exec - ) - endif() - - if(${NS3_STATIC} AND (NOT BEXEC_STANDALONE)) - target_link_libraries( - ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} ${LIB_AS_NEEDED_PRE_STATIC} - ${lib-ns3-static} - ) - elseif(${NS3_MONOLIB} AND (NOT BEXEC_STANDALONE)) - target_link_libraries( - ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} ${LIB_AS_NEEDED_PRE} - ${lib-ns3-monolib} ${LIB_AS_NEEDED_POST} - ) - else() - target_link_libraries( - ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} ${LIB_AS_NEEDED_PRE} - "${BEXEC_LIBRARIES_TO_LINK}" ${LIB_AS_NEEDED_POST} - ) - endif() - - set_runtime_outputdirectory( - "${BEXEC_EXECNAME}" "${BEXEC_EXECUTABLE_DIRECTORY_PATH}/" - "${BEXEC_EXECNAME_PREFIX}" - ) - - if(BEXEC_INSTALL_DIRECTORY_PATH) - install(TARGETS ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} - EXPORT ns3ExportTargets - RUNTIME DESTINATION ${BEXEC_INSTALL_DIRECTORY_PATH} - ) - get_property( - filename TARGET ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} - PROPERTY RUNTIME_OUTPUT_NAME - ) - add_custom_target( - uninstall_${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} - COMMAND - rm ${CMAKE_INSTALL_PREFIX}/${BEXEC_INSTALL_DIRECTORY_PATH}/${filename} - ) - add_dependencies( - uninstall uninstall_${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} - ) - endif() -endfunction(build_exec) - -function(scan_python_examples path) - # Skip python examples search in case the bindings are disabled - if(NOT ${ENABLE_PYTHON_BINDINGS}) - return() - endif() - - # Search for python examples - 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" - ) + # Append the include directory to a list + set(include_directories ${include_directories} -I${include_dir}) endif() endforeach() + set(${output} ${include_directories} PARENT_SCOPE) endfunction() -add_custom_target(copy_all_headers) -function(copy_headers_before_building_lib libname outputdir headers visibility) - foreach(header ${headers}) - # Copy header to output directory on changes -> too darn slow - # configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${header} ${outputdir}/ - # COPYONLY) - - get_filename_component( - header_name ${CMAKE_CURRENT_SOURCE_DIR}/${header} NAME - ) - - # If output directory does not exist, create it - if(NOT (EXISTS ${outputdir})) - file(MAKE_DIRECTORY ${outputdir}) - endif() - - # If header already exists, skip symlinking/stub header creation - if(EXISTS ${outputdir}/${header_name}) - continue() - endif() - - # Create a stub header in the output directory, including the real header - # inside their respective module - get_filename_component( - ABSOLUTE_HEADER_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${header}" ABSOLUTE - ) - file(WRITE ${outputdir}/${header_name} - "#include \"${ABSOLUTE_HEADER_PATH}\"\n" - ) - endforeach() -endfunction(copy_headers_before_building_lib) - -function(remove_lib_prefix prefixed_library library) - # Check if there is a lib prefix - string(FIND "${prefixed_library}" "lib" lib_pos) - - # If there is a lib prefix, try to remove it - if(${lib_pos} EQUAL 0) - # Check if we still have something remaining after removing the "lib" prefix - string(LENGTH ${prefixed_library} len) - if(${len} LESS 4) - message(FATAL_ERROR "Invalid library name: ${prefixed_library}") - endif() - - # Remove lib prefix from module name (e.g. libcore -> core) - string(SUBSTRING "${prefixed_library}" 3 -1 unprefixed_library) - else() - set(unprefixed_library ${prefixed_library}) - endif() - - # Save the unprefixed library name to the parent scope - set(${library} ${unprefixed_library} PARENT_SCOPE) -endfunction() - -function(check_for_missing_libraries output_variable_name libraries) - set(missing_dependencies) - foreach(lib ${libraries}) - # skip check for ns-3 modules if its a path to a library - if(EXISTS ${lib}) - continue() - endif() - - # check if the example depends on disabled modules - remove_lib_prefix("${lib}" lib) - - # Check if the module exists in the ns-3 modules list or if it is a - # 3rd-party library - if(NOT (${lib} IN_LIST ns3-all-enabled-modules)) - list(APPEND missing_dependencies ${lib}) - endif() - endforeach() - set(${output_variable_name} ${missing_dependencies} PARENT_SCOPE) -endfunction() +# Macros related to the definition of executables +include(ns3-executables) # Import macros used for modules and define specialized versions for src modules -include(build-support/custom-modules/ns3-module-macros.cmake) +include(ns3-module-macros) # Contrib modules counterparts of macros above -include(build-support/custom-modules/ns3-contributions.cmake) +include(ns3-contributions) + +# Macros for enabled/disabled module filtering +include(ns3-filter-modules) + +# .ns3rc configuration file parsers +include(ns3-ns3rc-parser) # Macro to build examples in ns-3-dev/examples/ macro(build_example) @@ -1856,664 +1346,20 @@ macro(build_example) # Create example library with sources and headers # cmake-format: off build_exec( - EXECNAME ${EXAMPLE_NAME} - SOURCE_FILES ${EXAMPLE_SOURCE_FILES} - HEADER_FILES ${EXAMPLE_HEADER_FILES} - LIBRARIES_TO_LINK ${EXAMPLE_LIBRARIES_TO_LINK} ${optional_visualizer_lib} - EXECUTABLE_DIRECTORY_PATH - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/examples/${examplefolder}/ - ${IGNORE_PCH} + EXECNAME ${EXAMPLE_NAME} + SOURCE_FILES ${EXAMPLE_SOURCE_FILES} + HEADER_FILES ${EXAMPLE_HEADER_FILES} + LIBRARIES_TO_LINK ${EXAMPLE_LIBRARIES_TO_LINK} ${optional_visualizer_lib} + EXECUTABLE_DIRECTORY_PATH + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/examples/${examplefolder}/ + ${IGNORE_PCH} ) # cmake-format: on endif() endmacro() -function(filter_modules modules_to_filter all_modules_list filter_in) - set(new_modules_to_build) - # 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})) - list(APPEND new_modules_to_build ${module}) - endif() - endforeach() - set(${all_modules_list} ${new_modules_to_build} PARENT_SCOPE) -endfunction() +# Macros to write the lock file +include(ns3-lock) -function(resolve_dependencies module_name dependencies contrib_dependencies) - # Create cache variables to hold dependencies list and visited - set(dependency_visited "" CACHE INTERNAL "") - set(contrib_dependency_visited "" CACHE INTERNAL "") - recursive_dependency(${module_name}) - if(${module_name} IN_LIST dependency_visited) - set(temp ${dependency_visited}) - list(REMOVE_AT temp 0) - set(${dependencies} ${temp} PARENT_SCOPE) - set(${contrib_dependencies} ${contrib_dependency_visited} PARENT_SCOPE) - else() - set(temp ${contrib_dependency_visited}) - list(REMOVE_AT temp 0) - set(${dependencies} ${dependency_visited} PARENT_SCOPE) - set(${contrib_dependencies} ${temp} PARENT_SCOPE) - endif() - unset(dependency_visited CACHE) - unset(contrib_dependency_visited CACHE) -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) -endfunction() - -function(recursive_dependency module_name) - # Read module CMakeLists.txt and search for ns-3 modules - set(src_cmakelist ${PROJECT_SOURCE_DIR}/src/${module_name}/CMakeLists.txt) - set(contrib_cmakelist - ${PROJECT_SOURCE_DIR}/contrib/${module_name}/CMakeLists.txt - ) - set(contrib FALSE) - if(EXISTS ${src_cmakelist}) - file(READ ${src_cmakelist} cmakelists_content) - elseif(EXISTS ${contrib_cmakelist}) - file(READ ${contrib_cmakelist} cmakelists_content) - set(contrib TRUE) - else() - set(cmakelists_content "") - message(${HIGHLIGHTED_STATUS} - "The CMakeLists.txt file for module ${module_name} was not found." - ) - endif() - - filter_libraries("${cmakelists_content}" matches) - - # Add this visited module dependencies to the dependencies list - if(contrib) - set(contrib_dependency_visited - "${contrib_dependency_visited};${module_name}" CACHE INTERNAL "" - ) - set(examples_cmakelists ${contrib_cmakelist}) - else() - set(dependency_visited "${dependency_visited};${module_name}" CACHE INTERNAL - "" - ) - set(examples_cmakelists ${src_cmakelist}) - endif() - - # cmake-format: off - # Scan dependencies required by this module examples - #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() - # cmake-format: on - - # For each dependency, call this same function - set(matches "${matches};${example_matches}") - foreach(match ${matches}) - if(NOT ((${match} IN_LIST dependency_visited) - OR (${match} IN_LIST contrib_dependency_visited)) - ) - recursive_dependency(${match}) - endif() - endforeach() -endfunction() - -macro( - filter_enabled_and_disabled_modules - libs_to_build - contrib_libs_to_build - NS3_ENABLED_MODULES - NS3_DISABLED_MODULES - ns3rc_enabled_modules - ns3rc_disabled_modules -) - mark_as_advanced(ns3-all-enabled-modules) - - # 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}) - set(ns3rc_enabled_modules ${${NS3_ENABLED_MODULES}}) - endif() - - # Filter enabled modules - filter_modules(ns3rc_enabled_modules libs_to_build "") - filter_modules(ns3rc_enabled_modules contrib_libs_to_build "") - - # Use recursion to automatically determine dependencies required by the - # manually enabled modules - foreach(lib ${${contrib_libs_to_build}}) - resolve_dependencies(${lib} dependencies contrib_dependencies) - list(APPEND ${contrib_libs_to_build} "${contrib_dependencies}") - list(APPEND ${libs_to_build} "${dependencies}") - unset(dependencies) - unset(contrib_dependencies) - endforeach() - - foreach(lib ${${libs_to_build}}) - resolve_dependencies(${lib} dependencies contrib_dependencies) - list(APPEND ${libs_to_build} "${dependencies}") - unset(dependencies) - unset(contrib_dependencies) - endforeach() - endif() - - if(${NS3_DISABLED_MODULES} OR ${ns3rc_disabled_modules}) - # List of disabled modules passed by the command line overwrites list read - # from ns3rc - - if(${NS3_DISABLED_MODULES}) - set(ns3rc_disabled_modules ${${NS3_DISABLED_MODULES}}) - endif() - - 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 "${${ns3rc_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 - if(NOT ${contrib_libs_to_build}) - set(${contrib_libs_to_build} "") - endif() - - # Filter out any eventual duplicates - list(REMOVE_DUPLICATES ${libs_to_build}) - list(REMOVE_DUPLICATES ${contrib_libs_to_build}) - - # Export list with all enabled modules (used to separate ns libraries from - # non-ns libraries in ns3_module_macros) - set(ns3-all-enabled-modules "${${libs_to_build}};${${contrib_libs_to_build}}" - CACHE INTERNAL "list with all enabled modules" - ) -endmacro() - -# Parse .ns3rc -macro(parse_ns3rc enabled_modules disabled_modules examples_enabled - tests_enabled -) - # Try to find .ns3rc - find_file(NS3RC .ns3rc PATHS /etc $ENV{HOME} $ENV{USERPROFILE} - ${PROJECT_SOURCE_DIR} NO_CACHE - ) - - # Set variables with default values (all modules, no examples nor tests) - set(${enabled_modules} "") - set(${disabled_modules} "") - set(${examples_enabled} "FALSE") - set(${tests_enabled} "FALSE") - - if(NOT (${NS3RC} STREQUAL "NS3RC-NOTFOUND")) - message(${HIGHLIGHTED_STATUS} - "Configuration file .ns3rc being used : ${NS3RC}" - ) - file(READ ${NS3RC} ns3rc_contents) - # Check if ns3rc file is CMake or Python based and act accordingly - if(ns3rc_contents MATCHES "ns3rc_*") - include(${NS3RC}) - else() - parse_python_ns3rc( - "${ns3rc_contents}" ${enabled_modules} ${examples_enabled} - ${tests_enabled} ${NS3RC} - ) - endif() - endif() -endmacro(parse_ns3rc) - -function(parse_python_ns3rc ns3rc_contents enabled_modules examples_enabled - tests_enabled ns3rc_location -) - # Save .ns3rc backup - file(WRITE ${ns3rc_location}.backup ${ns3rc_contents}) - - # Match modules_enabled list - if(ns3rc_contents MATCHES "modules_enabled.*\\[(.*).*\\]") - set(${enabled_modules} ${CMAKE_MATCH_1}) - if(${enabled_modules} MATCHES "all_modules") - # If all modules, just clean the filter and all modules will get built by - # default - set(${enabled_modules}) - else() - # If modules are listed, remove quotes and replace commas with 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}}") - string(REPLACE " " "" ${enabled_modules} "${${enabled_modules}}") - string(REPLACE "\n" ";" ${enabled_modules} "${${enabled_modules}}") - list(SORT ${enabled_modules}) - - # Remove possibly empty entry - list(REMOVE_ITEM ${enabled_modules} "") - foreach(element ${${enabled_modules}}) - # Inspect each element for comments - if(${element} MATCHES "#.*") - list(REMOVE_ITEM ${enabled_modules} ${element}) - endif() - endforeach() - endif() - endif() - - string(REPLACE "True" "ON" ns3rc_contents ${ns3rc_contents}) - string(REPLACE "False" "OFF" ns3rc_contents ${ns3rc_contents}) - - # Match examples_enabled flag - if(ns3rc_contents MATCHES "examples_enabled = (ON|OFF)") - set(${examples_enabled} ${CMAKE_MATCH_1}) - endif() - - # Match tests_enabled flag - if(ns3rc_contents MATCHES "tests_enabled = (ON|OFF)") - set(${tests_enabled} ${CMAKE_MATCH_1}) - endif() - - # Save variables to parent scope - set(${enabled_modules} "${${enabled_modules}}" PARENT_SCOPE) - set(${examples_enabled} "${${examples_enabled}}" PARENT_SCOPE) - set(${tests_enabled} "${${tests_enabled}}" PARENT_SCOPE) - - # Save updated .ns3rc file - message( - ${HIGHLIGHTED_STATUS} - "The python-based .ns3rc file format is deprecated and was updated to the CMake format" - ) - configure_file( - ${PROJECT_SOURCE_DIR}/build-support/.ns3rc-template ${ns3rc_location} @ONLY - ) -endfunction(parse_python_ns3rc) - -function(log_find_searched_paths) - # Parse arguments - set(options) - set(oneValueArgs TARGET_TYPE TARGET_NAME SEARCH_RESULT SEARCH_SYSTEM_PREFIX) - set(multiValueArgs SEARCH_PATHS SEARCH_SUFFIXES) - cmake_parse_arguments( - "LOGFIND" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} - ) - - # Get searched paths and add cmake_system_prefix_path if not explicitly marked - # not to include it - set(tsearch_paths ${LOGFIND_SEARCH_PATHS}) - if("${LOGFIND_SEARCH_SYSTEM_PREFIX}" STREQUAL "") - list(APPEND tsearch_paths "${CMAKE_SYSTEM_PREFIX_PATH}") - endif() - - set(log_find - "Looking for ${LOGFIND_TARGET_TYPE} ${LOGFIND_TARGET_NAME} in:\n" - ) - # For each search path and suffix combination, print a line - foreach(tsearch_path ${tsearch_paths}) - foreach(suffix ${LOGFIND_SEARCH_SUFFIXES}) - string(APPEND log_find - "\t${tsearch_path}${suffix}/${LOGFIND_TARGET_NAME}\n" - ) - endforeach() - endforeach() - - # Add a final line saying if the file was found and where, or if it was not - # found - if("${${LOGFIND_SEARCH_RESULT}}" STREQUAL "${LOGFIND_SEARCH_RESULT}-NOTFOUND") - string(APPEND log_find - "\n\t${LOGFIND_TARGET_TYPE} ${LOGFIND_TARGET_NAME} was not found\n" - ) - else() - string( - APPEND - log_find - "\n\t${LOGFIND_TARGET_TYPE} ${LOGFIND_TARGET_NAME} was found in ${${LOGFIND_SEARCH_RESULT}}\n" - ) - endif() - - # Replace duplicate path separators - string(REPLACE "//" "/" log_find "${log_find}") - - # Print find debug message similar to the one produced by - # CMAKE_FIND_DEBUG_MODE=true in CMake >= 3.17 - message(STATUS ${log_find}) -endfunction() - -function(find_external_library) - # Parse arguments - set(options QUIET) - set(oneValueArgs DEPENDENCY_NAME HEADER_NAME LIBRARY_NAME) - set(multiValueArgs HEADER_NAMES LIBRARY_NAMES PATH_SUFFIXES SEARCH_PATHS) - cmake_parse_arguments( - "FIND_LIB" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} - ) - - # Set the external package/dependency name - set(name ${FIND_LIB_DEPENDENCY_NAME}) - - # We process individual and list of headers and libraries by transforming them - # into lists - set(library_names "${FIND_LIB_LIBRARY_NAME};${FIND_LIB_LIBRARY_NAMES}") - set(header_names "${FIND_LIB_HEADER_NAME};${FIND_LIB_HEADER_NAMES}") - - # Just changing the parsed argument name back to something shorter - set(search_paths ${FIND_LIB_SEARCH_PATHS}) - set(path_suffixes "${FIND_LIB_PATH_SUFFIXES}") - - set(not_found_libraries) - set(library_dirs) - set(libraries) - - # Include parent directories in the search paths to handle Bake cases - get_filename_component(parent_project_dir ${PROJECT_SOURCE_DIR} DIRECTORY) - get_filename_component( - grandparent_project_dir ${parent_project_dir} DIRECTORY - ) - set(project_parent_dirs ${parent_project_dir} ${grandparent_project_dir}) - - # Paths and suffixes where libraries will be searched on - set(library_search_paths - ${search_paths} - ${project_parent_dirs} - ${CMAKE_OUTPUT_DIRECTORY} # Search for libraries in ns-3-dev/build - ${CMAKE_INSTALL_PREFIX} # Search for libraries in the install directory - # (e.g. /usr/) - $ENV{LD_LIBRARY_PATH} # Search for libraries in LD_LIBRARY_PATH - # directories - $ENV{PATH} # Search for libraries in PATH directories - ) - # cmake-format: off - # - # Split : separated entries from environment variables - # by replacing separators with ; - # - # cmake-format: on - string(REPLACE ":" ";" library_search_paths "${library_search_paths}") - - set(suffixes /build /lib /build/lib / /bin ${path_suffixes}) - - # For each of the library names in LIBRARY_NAMES or LIBRARY_NAME - foreach(library ${library_names}) - # We mark this value is advanced not to pollute the configuration with - # ccmake with the cache variables used internally - mark_as_advanced(${name}_library_internal_${library}) - - # We search for the library named ${library} and store the results in - # ${name}_library_internal_${library} - find_library( - ${name}_library_internal_${library} ${library} - HINTS ${library_search_paths} PATH_SUFFIXES ${suffixes} - ) - # cmake-format: off - # Note: the PATH_SUFFIXES above apply to *ALL* PATHS and HINTS Which - # translates to CMake searching on standard library directories - # CMAKE_SYSTEM_PREFIX_PATH, user-settable CMAKE_PREFIX_PATH or - # CMAKE_LIBRARY_PATH and the directories listed above - # - # e.g. from Ubuntu 22.04 CMAKE_SYSTEM_PREFIX_PATH = - # /usr/local;/usr;/;/usr/local;/usr/X11R6;/usr/pkg;/opt - # - # Searched directories without suffixes - # - # ${CMAKE_SYSTEM_PREFIX_PATH}[0] = /usr/local/ - # ${CMAKE_SYSTEM_PREFIX_PATH}[1] = /usr - # ${CMAKE_SYSTEM_PREFIX_PATH}[2] = / - # ... - # ${CMAKE_SYSTEM_PREFIX_PATH}[6] = /opt - # ${LD_LIBRARY_PATH}[0] - # ... - # ${LD_LIBRARY_PATH}[m] - # ${PATH}[0] - # ... - # ${PATH}[m] - # - # Searched directories with suffixes include all of the directories above - # plus all suffixes - # PATH_SUFFIXES /build /lib /build/lib / /bin # ${path_suffixes} - # - # /usr/local/build - # /usr/local/lib - # /usr/local/build/lib - # /usr/local/bin - # ... - # - # cmake-format: on - # Or enable NS3_VERBOSE to print the searched paths - - # Print tested paths to the searched library and if it was found - if(${NS3_VERBOSE} AND (${CMAKE_VERSION} VERSION_LESS "3.17.0")) - log_find_searched_paths( - TARGET_TYPE - Library - TARGET_NAME - ${library} - SEARCH_RESULT - ${name}_library_internal_${library} - SEARCH_PATHS - ${library_search_paths} - SEARCH_SUFFIXES - ${suffixes} - ) - endif() - - # After searching the library, the internal variable should have either the - # absolute path to the library or the name of the variable appended with - # -NOTFOUND - if("${${name}_library_internal_${library}}" STREQUAL - "${name}_library_internal_${library}-NOTFOUND" - ) - # We keep track of libraries that were not found - list(APPEND not_found_libraries ${library}) - else() - # We get the name of the parent directory of the library and append the - # library to a list of found libraries - get_filename_component( - ${name}_library_dir_internal ${${name}_library_internal_${library}} - DIRECTORY - ) # e.g. lib/openflow.(so|dll|dylib|a) -> lib - list(APPEND library_dirs ${${name}_library_dir_internal}) - list(APPEND libraries ${${name}_library_internal_${library}}) - endif() - endforeach() - - # For each library that was found (e.g. /usr/lib/pthread.so), get their parent - # directory (/usr/lib) and its parent (/usr) - set(parent_dirs) - foreach(libdir ${library_dirs}) - get_filename_component(parent_libdir ${libdir} DIRECTORY) - get_filename_component(parent_parent_libdir ${parent_libdir} DIRECTORY) - list(APPEND parent_dirs ${libdir} ${parent_libdir} ${parent_parent_libdir}) - endforeach() - - set(header_search_paths - ${search_paths} - ${parent_dirs} - ${project_parent_dirs} - ${CMAKE_OUTPUT_DIRECTORY} # Search for headers in - # ns-3-dev/build - ${CMAKE_INSTALL_PREFIX} # Search for headers in the install - ) - - set(not_found_headers) - set(include_dirs) - foreach(header ${header_names}) - # The same way with libraries, we mark the internal variable as advanced not - # to pollute ccmake configuration with variables used internally - mark_as_advanced(${name}_header_internal_${header}) - set(suffixes - /build - /include - /build/include - /build/include/${name} - /include/${name} - /${name} - / - ${path_suffixes} - ) - - # cmake-format: off - # Here we search for the header file named ${header} and store the result in - # ${name}_header_internal_${header} - # - # The same way we did with libraries, here we search on - # CMAKE_SYSTEM_PREFIX_PATH, along with user-settable ${search_paths}, the - # parent directories from the libraries, CMAKE_OUTPUT_DIRECTORY and - # CMAKE_INSTALL_PREFIX - # - # And again, for each of them, for every suffix listed /usr/local/build - # /usr/local/include - # /usr/local/build/include - # /usr/local/build/include/${name} - # /usr/local/include/${name} - # ... - # - # cmake-format: on - # Or enable NS3_VERBOSE to get the searched paths printed while configuring - - find_file(${name}_header_internal_${header} ${header} - HINTS ${header_search_paths} # directory (e.g. /usr/) - ${header_skip_system_prefix} PATH_SUFFIXES ${suffixes} - ) - - # Print tested paths to the searched header and if it was found - if(${NS3_VERBOSE} AND (${CMAKE_VERSION} VERSION_LESS "3.17.0")) - log_find_searched_paths( - TARGET_TYPE - Header - TARGET_NAME - ${header} - SEARCH_RESULT - ${name}_header_internal_${header} - SEARCH_PATHS - ${header_search_paths} - SEARCH_SUFFIXES - ${suffixes} - SEARCH_SYSTEM_PREFIX - ${header_skip_system_prefix} - ) - endif() - - # If the header file was not found, append to the not-found list - if("${${name}_header_internal_${header}}" STREQUAL - "${name}_header_internal_${header}-NOTFOUND" - ) - list(APPEND not_found_headers ${header}) - else() - # If the header file was found, get their directories and the parent of - # their directories to add as include directories - get_filename_component( - header_include_dir ${${name}_header_internal_${header}} DIRECTORY - ) # e.g. include/click/ (simclick.h) -> #include should work - get_filename_component( - header_include_dir2 ${header_include_dir} DIRECTORY - ) # e.g. include/(click) -> #include should work - list(APPEND include_dirs ${header_include_dir} ${header_include_dir2}) - endif() - endforeach() - - # Remove duplicate include directories - if(include_dirs) - list(REMOVE_DUPLICATES include_dirs) - endif() - - # If we find both library and header, we export their values - if((NOT not_found_libraries) AND (NOT not_found_headers)) - set(${name}_INCLUDE_DIRS "${include_dirs}" PARENT_SCOPE) - set(${name}_LIBRARIES "${libraries}" PARENT_SCOPE) - set(${name}_HEADER ${${name}_header_internal} PARENT_SCOPE) - set(${name}_FOUND TRUE PARENT_SCOPE) - if(NOT ${FIND_LIB_QUIET}) - message(STATUS "find_external_library: ${name} was found.") - endif() - else() - set(${name}_INCLUDE_DIRS PARENT_SCOPE) - set(${name}_LIBRARIES PARENT_SCOPE) - set(${name}_HEADER PARENT_SCOPE) - set(${name}_FOUND FALSE PARENT_SCOPE) - if(NOT ${FIND_LIB_QUIET}) - message( - ${HIGHLIGHTED_STATUS} - "find_external_library: ${name} was not found. Missing headers: \"${not_found_headers}\" and missing libraries: \"${not_found_libraries}\"." - ) - endif() - endif() -endfunction() - -function(get_target_includes target output) - set(include_directories) - get_target_property(include_dirs ${target} INCLUDE_DIRECTORIES) - list(REMOVE_DUPLICATES include_dirs) - foreach(include_dir ${include_dirs}) - if(include_dir MATCHES "<") - # Skip CMake build and install interface includes - continue() - else() - # Append the include directory to a list - set(include_directories ${include_directories} -I${include_dir}) - endif() - endforeach() - set(${output} ${include_directories} PARENT_SCOPE) -endfunction() - -function(check_python_packages packages missing_packages) - set(missing) - foreach(package ${packages}) - execute_process( - COMMAND ${Python3_EXECUTABLE} -c "import ${package}" - RESULT_VARIABLE return_code OUTPUT_QUIET ERROR_QUIET - ) - if(NOT (${return_code} EQUAL 0)) - list(APPEND missing ${package}) - endif() - endforeach() - set(${missing_packages} "${missing}" PARENT_SCOPE) -endfunction() - -include(build-support/custom-modules/ns3-lock.cmake) -include(build-support/custom-modules/ns3-configtable.cmake) +# Macros to build the config table file +include(ns3-configtable) diff --git a/doc/manual/source/working-with-cmake.rst b/doc/manual/source/working-with-cmake.rst index 562ecd063..41c14e0b7 100644 --- a/doc/manual/source/working-with-cmake.rst +++ b/doc/manual/source/working-with-cmake.rst @@ -2950,7 +2950,7 @@ manage dependencies. Here is an example for Doxygen: # This custom macro checks for dependencies CMake find_package and program # dependencies and return the missing dependencies in the third argument - check_deps("" "doxygen;dot;dia" doxygen_docs_missing_deps) + check_deps(doxygen_docs_missing_deps EXECUTABLES doxygen dot dia python3) # If the variable contains missing dependencies, we stop processing doxygen targets if(doxygen_docs_missing_deps)