diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d23aa22d..4b0efb3d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,7 @@ option(NS3_SQLITE "Build with SQLite support" ON) option(NS3_STATIC "Build a static ns-3 library and link it against executables" OFF ) +option(NS3_VERBOSE "Print additional build system messages" OFF) option(NS3_VISUALIZER "Build visualizer module" OFF) option(NS3_WARNINGS "Enable compiler warnings" ON) option(NS3_WARNINGS_AS_ERRORS diff --git a/buildsupport/custom_modules/ns3_contributions.cmake b/buildsupport/custom_modules/ns3_contributions.cmake index 1e9d3587b..f604b1816 100644 --- a/buildsupport/custom_modules/ns3_contributions.cmake +++ b/buildsupport/custom_modules/ns3_contributions.cmake @@ -27,6 +27,7 @@ macro(process_contribution contribution_list) foreach(contribname ${contribution_list}) set(folder "contrib/${contribname}") if(EXISTS ${PROJECT_SOURCE_DIR}/${folder}/CMakeLists.txt) + message(STATUS "Processing ${folder}") add_subdirectory(${folder}) else() message( diff --git a/buildsupport/custom_modules/ns3_module_macros.cmake b/buildsupport/custom_modules/ns3_module_macros.cmake index 02baf232d..bb93366a0 100644 --- a/buildsupport/custom_modules/ns3_module_macros.cmake +++ b/buildsupport/custom_modules/ns3_module_macros.cmake @@ -44,7 +44,6 @@ macro( #ignore_pch ) # cmake-format: on - message(STATUS "Processing ${folder}/${libname}") # Add library to a global list of libraries if("${folder}" MATCHES "src") @@ -393,6 +392,9 @@ macro( LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/ PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ns3" ) + if(${NS3_VERBOSE}) + message(STATUS "Processed ${folder}/${libname}") + endif() endmacro() # cmake-format: off diff --git a/buildsupport/macros_and_definitions.cmake b/buildsupport/macros_and_definitions.cmake index ce990418b..3755d469b 100644 --- a/buildsupport/macros_and_definitions.cmake +++ b/buildsupport/macros_and_definitions.cmake @@ -698,6 +698,12 @@ macro(process_options) endif() endif() + if(${NS3_VERBOSE}) + set_property(GLOBAL PROPERTY TARGET_MESSAGES TRUE) + else() + set_property(GLOBAL PROPERTY TARGET_MESSAGES OFF) + endif() + set(ENABLE_VISUALIZER FALSE) if(${NS3_VISUALIZER}) if((NOT ${NS3_PYTHON_BINDINGS}) OR (NOT ${Python3_FOUND})) diff --git a/ns3 b/ns3 index 5758dd4ed..72355d3b5 100755 --- a/ns3 +++ b/ns3 @@ -80,23 +80,24 @@ def parse_args(argv): type=str, default=None) parser_configure = on_off_argument(parser_configure, "asserts", "the asserts regardless of the compile mode") - parser_configure = on_off_argument(parser_configure, "logs", "the logs regardless of the compile mode") - parser_configure = on_off_argument(parser_configure, "tests", "the ns-3 tests") - parser_configure = on_off_argument(parser_configure, "examples", "the ns-3 examples") - parser_configure = on_off_argument(parser_configure, "static", "Build a single static library with all ns-3", - "Restore the shared libraries") - parser_configure = on_off_argument(parser_configure, "mpi", "the MPI support for distributed simulation") parser_configure = on_off_argument(parser_configure, "des-metrics", "Logging all events in a json file with the name of the executable " "(which must call CommandLine::Parse(argc, argv)") + parser_configure = on_off_argument(parser_configure, "documentation", "documentation targets") + parser_configure = on_off_argument(parser_configure, "examples", "the ns-3 examples") parser_configure = on_off_argument(parser_configure, "gcov", "code coverage analysis") parser_configure = on_off_argument(parser_configure, "gtk", "GTK support in ConfigStore") + parser_configure = on_off_argument(parser_configure, "logs", "the logs regardless of the compile mode") + parser_configure = on_off_argument(parser_configure, "mpi", "the MPI support for distributed simulation") + parser_configure = on_off_argument(parser_configure, "tests", "the ns-3 tests") + parser_configure = on_off_argument(parser_configure, "sanitizers", + "address, memory leaks and undefined behavior sanitizers") + parser_configure = on_off_argument(parser_configure, "static", "Build a single static library with all ns-3", + "Restore the shared libraries") + parser_configure = on_off_argument(parser_configure, "verbose", "printing of additional build system messages") parser_configure = on_off_argument(parser_configure, "warnings", "compiler warnings") parser_configure = on_off_argument(parser_configure, "werror", "Treat compiler warnings as errors", "Treat compiler warnings as warnings") - parser_configure = on_off_argument(parser_configure, "documentation", "documentation targets") - parser_configure = on_off_argument(parser_configure, "sanitizers", - "address, memory leaks and undefined behavior sanitizers") parser_configure.add_argument('--enable-modules', help='List of modules to build (e.g. core;network;internet)', @@ -411,18 +412,19 @@ def configure_cmake(cmake, args, current_cmake_cache_folder, current_cmake_gener cmake_args.append("-DNS3_NATIVE_OPTIMIZATIONS=%s" % on_off((args.build_profile == "optimized"))) options = (("ASSERT", "asserts"), - ("LOG", "logs"), - ("TESTS", "tests"), - ("EXAMPLES", "examples"), ("COVERAGE", "gcov"), ("DES_METRICS", "des_metrics"), - ("STATIC", "static"), - ("MPI", "mpi"), + ("DOCS", "documentation"), + ("EXAMPLES", "examples"), ("GTK3", "gtk"), + ("LOG", "logs"), + ("MPI", "mpi"), + ("SANITIZE", "sanitizers"), + ("STATIC", "static"), + ("TESTS", "tests"), + ("VERBOSE", "verbose"), ("WARNINGS", "warnings"), ("WARNINGS_AS_ERRORS", "werror"), - ("DOCS", "documentation"), - ("SANITIZE", "sanitizers") ) for (cmake_flag, option_name) in options: arg = on_off_condition(args, cmake_flag, option_name) @@ -531,7 +533,10 @@ def cmake_build(current_cmake_cache_folder, output, jobs, target=None, dry_run=F ) ) if not dry_run: - subprocess.run(cmake_build_command.split(), cwd=current_cmake_cache_folder, stdout=output) + ret = subprocess.run(cmake_build_command.split(), cwd=current_cmake_cache_folder, stdout=output) + # In case of failure, exit prematurely with the return code from the build + if ret.returncode != 0: + exit(ret.returncode) def extract_cmakecache_settings(current_cmake_cache_folder): diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0376862e3..c4736dad7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,7 @@ set(libs "${libs_to_build}") # Process the visualizer module first if enabled if(${ENABLE_VISUALIZER}) + message(STATUS "Processing src/visualizer") add_subdirectory(visualizer) list(REMOVE_ITEM libs visualizer) endif() @@ -9,6 +10,7 @@ endif() # Process subdirectories foreach(libname ${libs}) if(EXISTS ${PROJECT_SOURCE_DIR}/src/${libname}/CMakeLists.txt) + message(STATUS "Processing src/${libname}") add_subdirectory(${libname}) else() message( diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 467ea6148..fffbd6d94 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -24,14 +24,17 @@ set_runtime_outputdirectory( bench-simulator ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/utils/ "" ) -add_executable(bench-packets bench-packets.cc) -target_link_libraries(bench-packets ${libnetwork}) -set_runtime_outputdirectory( - bench-packets ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/utils/ "" -) +if (network IN_LIST libs_to_build) + add_executable(bench-packets bench-packets.cc) + target_link_libraries(bench-packets ${libnetwork}) + set_runtime_outputdirectory( + bench-packets ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/utils/ "" + ) + + add_executable(print-introspected-doxygen print-introspected-doxygen.cc) + target_link_libraries(print-introspected-doxygen ${libnetwork}) + set_runtime_outputdirectory( + print-introspected-doxygen ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/utils/ "" + ) +endif() -add_executable(print-introspected-doxygen print-introspected-doxygen.cc) -target_link_libraries(print-introspected-doxygen ${ns3-libs}) -set_runtime_outputdirectory( - print-introspected-doxygen ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/utils/ "" -) diff --git a/utils/tests/test-ns3.py b/utils/tests/test-ns3.py index 4ab4c52f2..c090bf368 100644 --- a/utils/tests/test-ns3.py +++ b/utils/tests/test-ns3.py @@ -47,11 +47,24 @@ cmake_build_target_command = partial("cmake --build . -j {jobs} --target {target def run_ns3(args): + """ + Runs the ns3 wrapper script with arguments + :param args: string containing arguments that will get split before calling ns3 + :return: tuple containing (error code, stdout and stderr) + """ return run_program(ns3_script, args, True) # Adapted from https://github.com/metabrainz/picard/blob/master/picard/util/__init__.py def run_program(program, args, python=False, cwd=ns3_path): + """ + Runs a program with the given arguments and returns a tuple containing (error code, stdout and stderr) + :param program: program to execute (or python script) + :param args: string containing arguments that will get split before calling the program + :param python: flag indicating whether the program is a python script + :param cwd: working directory used that will be the root folder for the execution + :return: tuple containing (error code, stdout and stderr) + """ if type(args) != str: raise Exception("args should be a string") @@ -78,6 +91,11 @@ def run_program(program, args, python=False, cwd=ns3_path): def get_programs_list(build_status_script_path=usual_build_status_script): + """ + Extracts the programs list from build-status.py + :param build_status_script_path: path containing build-status.py + :return: list of programs + """ values = {} with open(build_status_script_path) as f: exec(f.read(), globals(), values) @@ -85,25 +103,49 @@ def get_programs_list(build_status_script_path=usual_build_status_script): def get_libraries_list(lib_outdir=usual_lib_outdir): + """ + Gets a list of built libraries + :param lib_outdir: path containing libraries + :return: list of built libraries + """ return glob.glob(lib_outdir + '/*', recursive=True) def get_headers_list(outdir=usual_outdir): + """ + Gets a list of header files + :param outdir: path containing headers + :return: list of headers + """ return glob.glob(outdir + '/**/*.h', recursive=True) -def read_c4che_entry(entry, c4cache_script_path=usual_c4che_script): +def read_c4che_entry(entry, c4che_script_path=usual_c4che_script): + """ + Read interesting entries from the c4che/_cache.py file + :param entry: entry to read from c4che/_cache.py + :param c4che_script_path: path containing _cache.py + :return: value of the requested entry + """ values = {} - with open(c4cache_script_path) as f: + with open(c4che_script_path) as f: exec(f.read(), globals(), values) return values[entry] def get_test_enabled(): + """ + Check if tests are enabled in the c4che/_cache.py + :return: bool + """ return read_c4che_entry("ENABLE_TESTS") def get_enabled_modules(): + """ + Check if tests are enabled in the c4che/_cache.py + :return: list of enabled modules (prefixed with 'ns3-') + """ return read_c4che_entry("NS3_ENABLED_MODULES") @@ -112,6 +154,9 @@ class NS3RunWafTargets(unittest.TestCase): cleaned_once = False def setUp(self): + """ + Clean the default build directory, then configure and build ns-3 with waf + """ if not NS3RunWafTargets.cleaned_once: NS3RunWafTargets.cleaned_once = True run_ns3("clean") @@ -158,6 +203,9 @@ class NS3RunWafTargets(unittest.TestCase): class NS3CommonSettingsTestCase(unittest.TestCase): def setUp(self): + """ + Clean configuration/build artifacts before common commands + """ super().setUp() # No special setup for common test cases other than making sure we are working on a clean directory run_ns3("clean") @@ -180,12 +228,15 @@ class NS3CommonSettingsTestCase(unittest.TestCase): class NS3ConfigureBuildProfileTestCase(unittest.TestCase): def setUp(self): + """ + Clean configuration/build artifacts before testing configuration settings + """ super().setUp() # No special setup for common test cases other than making sure we are working on a clean directory run_ns3("clean") def test_01_Debug(self): - return_code, stdout, stderr = run_ns3("configure -d debug") + return_code, stdout, stderr = run_ns3("configure -d debug --enable-verbose") self.assertEqual(return_code, 0) self.assertIn("Build profile : debug", stdout) self.assertIn("Build files have been written to", stdout) @@ -206,7 +257,7 @@ class NS3ConfigureBuildProfileTestCase(unittest.TestCase): self.assertIn("Build files have been written to", stdout) def test_03_Optimized(self): - return_code, stdout, stderr = run_ns3("configure -d optimized") + return_code, stdout, stderr = run_ns3("configure -d optimized --enable-verbose") self.assertEqual(return_code, 0) self.assertIn("Build profile : optimized", stdout) self.assertIn("Build files have been written to", stdout) @@ -236,11 +287,21 @@ class NS3BaseTestCase(unittest.TestCase): cleaned_once = False def config_ok(self, return_code, stdout): + """ + Check if configuration for release mode worked normally + :param return_code: return code from CMake + :param stdout: output from CMake + """ self.assertEqual(return_code, 0) self.assertIn("Build profile : release", stdout) self.assertIn("Build files have been written to", stdout) def setUp(self): + """ + Clean configuration/build artifacts before testing configuration and build settings + After configuring the build as release, + check if configuration worked and check expected output files + """ super().setUp() if os.path.exists(ns3rc_script): @@ -250,7 +311,7 @@ class NS3BaseTestCase(unittest.TestCase): if not NS3BaseTestCase.cleaned_once: NS3BaseTestCase.cleaned_once = True run_ns3("clean") - return_code, stdout, stderr = run_ns3("configure -d release") + return_code, stdout, stderr = run_ns3("configure -d release --enable-verbose") self.config_ok(return_code, stdout) # Check if build-status.py exists, then read to get list of executables @@ -267,6 +328,9 @@ class NS3ConfigureTestCase(NS3BaseTestCase): cleaned_once = False def setUp(self): + """ + Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned + """ if not NS3ConfigureTestCase.cleaned_once: NS3ConfigureTestCase.cleaned_once = True NS3BaseTestCase.cleaned_once = False @@ -455,7 +519,7 @@ class NS3ConfigureTestCase(NS3BaseTestCase): self.assertEqual(stderr, stderr1) # Build target before using below - run_ns3("configure -d release") + run_ns3("configure -d release --enable-verbose") run_ns3("build scratch-simulator") # Run all cases and then check outputs @@ -495,6 +559,9 @@ class NS3BuildBaseTestCase(NS3BaseTestCase): cleaned_once = False def setUp(self): + """ + Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned + """ if not NS3BuildBaseTestCase.cleaned_once: NS3BuildBaseTestCase.cleaned_once = True NS3BaseTestCase.cleaned_once = False @@ -525,7 +592,24 @@ class NS3BuildBaseTestCase(NS3BaseTestCase): self.assertEqual(return_code, 0) self.assertIn(cmake_build_project_command, stdout) - def test_05_TestVersionFile(self): + def test_05_BreakBuild(self): + # change an essential file to break the build + attribute_cc_path = os.sep.join([ns3_path, "src", "core", "model", "attribute.cc"]) + attribute_cc_bak_path = attribute_cc_path + ".bak" + shutil.move(attribute_cc_path, attribute_cc_bak_path) + + # build should break + return_code, stdout, stderr = run_ns3("build") + self.assertNotEqual(return_code, 0) + + # move file back + shutil.move(attribute_cc_bak_path, attribute_cc_path) + + # build should work again + return_code, stdout, stderr = run_ns3("build") + self.assertEqual(return_code, 0) + + def test_06_TestVersionFile(self): version_file = os.sep.join([ns3_path, "VERSION"]) with open(version_file, "w") as f: f.write("3-00\n") @@ -565,7 +649,7 @@ class NS3BuildBaseTestCase(NS3BaseTestCase): # Reset flag to let it clean the build NS3BuildBaseTestCase.cleaned_once = False - def test_06_OutputDirectory(self): + def test_07_OutputDirectory(self): # Re-build to return to the original state return_code, stdout, stderr = run_ns3("build") self.ns3_libraries = get_libraries_list() @@ -624,7 +708,7 @@ class NS3BuildBaseTestCase(NS3BaseTestCase): for library in libraries: self.assertTrue(os.path.exists(library)) - def test_07_InstallationAndUninstallation(self): + def test_08_InstallationAndUninstallation(self): # Remove existing libraries from the previous step libraries = get_libraries_list() for library in libraries: @@ -728,6 +812,10 @@ class NS3ExpectedUseTestCase(NS3BaseTestCase): cleaned_once = False def setUp(self): + """ + Reuse cleaning/release configuration from NS3BaseTestCase if flag is cleaned + Here examples, tests and documentation are also enabled + """ if not NS3ExpectedUseTestCase.cleaned_once: NS3ExpectedUseTestCase.cleaned_once = True NS3BaseTestCase.cleaned_once = False