From 6a6b785c5fcc56365a1f8afb2e987f81b5341749 Mon Sep 17 00:00:00 2001 From: Gabriel Ferreira Date: Wed, 16 Mar 2022 22:44:30 -0300 Subject: [PATCH] build,test: fixing CMake and ns3 issues and adding a test case Includes: - refactoring lib prefix removal and library dependency checking (fixes #598) - add new test case for test-ns3.py to test library names - fix ns3 issue, not accepting to build contrib libraries - ignore gitlab-ci-local directory in test-ns3.py --- .../custom-modules/ns3-module-macros.cmake | 21 +--- build-support/macros-and-definitions.cmake | 47 ++++++--- ns3 | 4 +- utils/tests/test-ns3.py | 99 +++++++++++++++++++ 4 files changed, 141 insertions(+), 30 deletions(-) diff --git a/build-support/custom-modules/ns3-module-macros.cmake b/build-support/custom-modules/ns3-module-macros.cmake index 500205d40..dc57cdcd8 100644 --- a/build-support/custom-modules/ns3-module-macros.cmake +++ b/build-support/custom-modules/ns3-module-macros.cmake @@ -114,8 +114,10 @@ function(build_lib) set(ns_libraries_to_link) foreach(library ${BLIB_LIBRARIES_TO_LINK}) - # Remove lib prefix from module name (e.g. libcore -> core) - string(REPLACE "lib" "" module_name "${library}") + remove_lib_prefix("${library}" module_name) + + # Check if the module exists in the ns-3 modules list + # or if it is a 3rd-party library if(${module_name} IN_LIST ns3-all-enabled-modules) list(APPEND ns_libraries_to_link ${library}) else() @@ -483,20 +485,7 @@ function(build_lib_example) get_filename_component(FOLDER ${FOLDER} DIRECTORY) # cmake-format: on - set(missing_dependencies) - foreach(lib ${BLIB_EXAMPLE_LIBRARIES_TO_LINK}) - # 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 - string(REPLACE "lib" "" lib ${lib}) - if(NOT (${lib} IN_LIST ns3-all-enabled-modules)) - list(APPEND missing_dependencies ${lib}) - endif() - endforeach() - + check_for_missing_libraries(missing_dependencies "${BLIB_EXAMPLE_LIBRARIES_TO_LINK}") if(NOT missing_dependencies) # Create shared library with sources and headers add_executable( diff --git a/build-support/macros-and-definitions.cmake b/build-support/macros-and-definitions.cmake index 895478c12..e3904c0ce 100644 --- a/build-support/macros-and-definitions.cmake +++ b/build-support/macros-and-definitions.cmake @@ -1299,6 +1299,39 @@ function(copy_headers_before_building_lib libname outputdir headers visibility) endforeach() endfunction(copy_headers_before_building_lib) +function(remove_lib_prefix prefixed_library library) + # 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 lib) + set(${library} ${lib} 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() + # Import macros used for modules and define specialized versions for src modules include(build-support/custom-modules/ns3-module-macros.cmake) @@ -1314,19 +1347,7 @@ macro(build_example) "EXAMPLE" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) - set(missing_dependencies) - foreach(lib ${EXAMPLE_LIBRARIES_TO_LINK}) - # 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 - string(REPLACE "lib" "" lib ${lib}) - if(NOT (${lib} IN_LIST ns3-all-enabled-modules)) - list(APPEND missing_dependencies ${lib}) - endif() - endforeach() + check_for_missing_libraries(missing_dependencies "${EXAMPLE_LIBRARIES_TO_LINK}") if(NOT missing_dependencies) # Create shared library with sources and headers diff --git a/ns3 b/ns3 index 6d7cfef17..ad0134cef 100755 --- a/ns3 +++ b/ns3 @@ -344,7 +344,8 @@ def check_lock_data(output_directory): ns3_modules_bindings = [] ns3_modules = None - build_info = {"BUILD_PROFILE": None, + build_info = {"NS3_ENABLED_MODULES": None, + "BUILD_PROFILE": None, "VERSION": None, "ENABLE_EXAMPLES": False, "ENABLE_SUDO": False, @@ -354,6 +355,7 @@ def check_lock_data(output_directory): exec(open(lock_file).read(), globals(), build_info) ns3_modules = build_info["NS3_ENABLED_MODULES"] if ns3_modules: + ns3_modules.extend(build_info["NS3_ENABLED_CONTRIBUTED_MODULES"]) if build_info["ENABLE_TESTS"]: ns3_modules_tests = [x + "-test" for x in ns3_modules] if build_info["ENABLE_PYTHON_BINDINGS"]: diff --git a/utils/tests/test-ns3.py b/utils/tests/test-ns3.py index b9e6754f0..94774d88c 100644 --- a/utils/tests/test-ns3.py +++ b/utils/tests/test-ns3.py @@ -56,6 +56,11 @@ def run_ns3(args, env=None): @param env: environment variables dictionary @return tuple containing (error code, stdout and stderr) """ + if "clean" in args: + possible_leftovers = ["contrib/borked"] + for leftover in possible_leftovers: + if os.path.exists(leftover): + shutil.rmtree(leftover, ignore_errors=True) return run_program(ns3_script, args, python=True, env=env) @@ -176,6 +181,8 @@ class NS3UnusedSourcesTestCase(unittest.TestCase): @return None """ for root, dirs, files in os.walk(ns3_path): + if "gitlab-ci-local" in root: + continue for name in files: if name.endswith(".cc"): path = os.path.join(root, name) @@ -915,6 +922,98 @@ class NS3ConfigureTestCase(NS3BaseTestCase): self.assertEqual(return_code, 0) self.assertIn("echo %s" % sample_simulator_path, stdout) + def test_15_InvalidLibrariesToLink(self): + """! + Test if CMake and ns3 fail in the expected ways when: + - examples from modules or general examples fail if they depend on a + library with a name shorter than 4 characters or are disabled when + a library is non-existant + - a module library passes the configuration but fails to build due to + a missing library + @return None + """ + os.makedirs("contrib/borked", exist_ok=True) + os.makedirs("contrib/borked/examples", exist_ok=True) + + # Test if configuration succeeds and building the module library fails + with open("contrib/borked/examples/CMakeLists.txt", "w") as f: + f.write("") + for invalid_or_non_existant_library in ["", "fee", "fi", "fogh", "calibre"]: + with open("contrib/borked/CMakeLists.txt", "w") as f: + f.write(""" + build_lib( + LIBNAME borked + SOURCE_FILES ${PROJECT_SOURCE_DIR}/build-support/empty.cc + LIBRARIES_TO_LINK ${libcore} %s + ) + """ % invalid_or_non_existant_library) + + return_code, stdout, stderr = run_ns3("configure -G \"Unix Makefiles\"") + if invalid_or_non_existant_library in ["", "fogh", "calibre"]: + self.assertEqual(return_code, 0) + elif invalid_or_non_existant_library in ["fee", "fi"]: + self.assertEqual(return_code, 1) + self.assertIn("Invalid library name: %s" % invalid_or_non_existant_library, stderr) + else: + pass + + return_code, stdout, stderr = run_ns3("build borked") + if invalid_or_non_existant_library in [""]: + self.assertEqual(return_code, 0) + elif invalid_or_non_existant_library in ["fee", "fi"]: + self.assertEqual(return_code, 2) # should fail due to invalid library name + self.assertIn("Invalid library name: %s" % invalid_or_non_existant_library, stderr) + elif invalid_or_non_existant_library in ["fogh", "calibre"]: + self.assertEqual(return_code, 2) # should fail due to missing library + self.assertIn("cannot find -l%s" % invalid_or_non_existant_library, stderr) + else: + pass + + # Now test if the example can be built with: + # - no additional library (should work) + # - invalid library names (should fail to configure) + # - valid library names but inexistent libraries (should not create a target) + with open("contrib/borked/CMakeLists.txt", "w") as f: + f.write(""" + build_lib( + LIBNAME borked + SOURCE_FILES ${PROJECT_SOURCE_DIR}/build-support/empty.cc + LIBRARIES_TO_LINK ${libcore} + ) + """) + for invalid_or_non_existant_library in ["", "fee", "fi", "fogh", "calibre"]: + with open("contrib/borked/examples/CMakeLists.txt", "w") as f: + f.write(""" + build_lib_example( + NAME borked-example + SOURCE_FILES ${PROJECT_SOURCE_DIR}/build-support/empty-main.cc + LIBRARIES_TO_LINK ${libborked} %s + ) + """ % invalid_or_non_existant_library) + + return_code, stdout, stderr = run_ns3("configure -G \"Unix Makefiles\"") + if invalid_or_non_existant_library in ["", "fogh", "calibre"]: + self.assertEqual(return_code, 0) # should be able to configure + elif invalid_or_non_existant_library in ["fee", "fi"]: + self.assertEqual(return_code, 1) # should fail to even configure + self.assertIn("Invalid library name: %s" % invalid_or_non_existant_library, stderr) + else: + pass + + return_code, stdout, stderr = run_ns3("build borked-example") + if invalid_or_non_existant_library in [""]: + self.assertEqual(return_code, 0) # should be able to build + elif invalid_or_non_existant_library in ["fee", "fi"]: + self.assertEqual(return_code, 2) # should fail due to missing configuration + self.assertIn("Invalid library name: %s" % invalid_or_non_existant_library, stderr) + elif invalid_or_non_existant_library in ["fogh", "calibre"]: + self.assertEqual(return_code, 1) # should fail to find target + self.assertIn("Target to build does not exist: borked-example", stdout) + else: + pass + + shutil.rmtree("contrib/borked", ignore_errors=True) + class NS3BuildBaseTestCase(NS3BaseTestCase): """!