1## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- 2 3# python lib modules 4from __future__ import print_function 5import sys 6import shutil 7import types 8import optparse 9import os.path 10import re 11import shlex 12import subprocess 13import textwrap 14import fileinput 15import glob 16 17from utils import read_config_file 18 19 20# WAF modules 21from waflib import Utils, Scripting, Configure, Build, Options, TaskGen, Context, Task, Logs, Errors 22from waflib.Errors import WafError 23 24 25# local modules 26import wutils 27 28 29# By default, all modules will be enabled, examples will be disabled, 30# and tests will be disabled. 31modules_enabled = ['all_modules'] 32examples_enabled = False 33tests_enabled = False 34 35# GCC minimum version requirements for C++17 support 36gcc_min_version = (7, 0, 0) 37 38# Bug 2181: clang warnings about unused local typedefs and potentially 39# evaluated expressions affecting darwin clang/LLVM version 7.0.0 (Xcode 7) 40# or clang/LLVM version 3.6 or greater. We must make this platform-specific. 41darwin_clang_version_warn_unused_local_typedefs = (7, 0, 0) 42darwin_clang_version_warn_potentially_evaluated = (7, 0, 0) 43clang_version_warn_unused_local_typedefs = (3, 6, 0) 44clang_version_warn_potentially_evaluated = (3, 6, 0) 45 46# Get the information out of the NS-3 configuration file. 47config_file_exists = False 48(config_file_exists, modules_enabled, examples_enabled, tests_enabled) = read_config_file() 49 50sys.path.insert(0, os.path.abspath('waf-tools')) 51try: 52 import cflags # override the build profiles from waf 53finally: 54 sys.path.pop(0) 55 56cflags.profiles = { 57 # profile name: [optimization_level, warnings_level, debug_level] 58 'debug': [0, 2, 3], 59 'optimized': [3, 2, 1], 60 'release': [3, 2, 0], 61 } 62cflags.default_profile = 'debug' 63 64Configure.autoconfig = 0 65 66# the following two variables are used by the target "waf dist" 67with open("VERSION", "rt") as f: 68 VERSION = f.read().strip() 69APPNAME = 'ns' 70 71wutils.VERSION = VERSION 72wutils.APPNAME = APPNAME 73 74# we don't use VNUM anymore (see bug #1327 for details) 75wutils.VNUM = None 76 77# these variables are mandatory ('/' are converted automatically) 78top = '.' 79out = 'build' 80 81def load_env(): 82 bld_cls = getattr(Utils.g_module, 'build_context', Utils.Context) 83 bld_ctx = bld_cls() 84 bld_ctx.load_dirs(os.path.abspath(os.path.join (srcdir,'..')), 85 os.path.abspath(os.path.join (srcdir,'..', blddir))) 86 bld_ctx.load_envs() 87 env = bld_ctx.get_env() 88 return env 89 90def get_files(base_dir): 91 retval = [] 92 reference=os.path.dirname(base_dir) 93 for root, dirs, files in os.walk(base_dir): 94 if root.find('.hg') != -1: 95 continue 96 for file in files: 97 if file.find('.hg') != -1: 98 continue 99 fullname = os.path.join(root,file) 100 # we can't use os.path.relpath because it's new in python 2.6 101 relname = fullname.replace(reference + '/','') 102 retval.append([fullname,relname]) 103 return retval 104 105 106def dist_hook(): 107 import tarfile 108 shutil.rmtree("doc/html", True) 109 shutil.rmtree("doc/latex", True) 110 shutil.rmtree("nsc", True) 111 112# Print the sorted list of module names in columns. 113def print_module_names(names): 114 """Print the list of module names in 3 columns.""" 115 for i, name in enumerate(sorted(names)): 116 if i % 3 == 2 or i == len(names) - 1: 117 print(name) 118 else: 119 print(name.ljust(25), end=' ') 120 121# return types of some APIs differ in Python 2/3 (type string vs class bytes) 122# This method will decode('utf-8') a byte object in Python 3, 123# and do nothing in Python 2 124def maybe_decode(input): 125 if sys.version_info < (3,): 126 return input 127 else: 128 try: 129 return input.decode('utf-8') 130 except: 131 sys.exc_clear() 132 return input 133 134def options(opt): 135 # options provided by the modules 136 opt.load('md5_tstamp') 137 opt.load('compiler_c') 138 opt.load('compiler_cxx') 139 opt.load('cflags') 140 opt.load('gnu_dirs') 141 opt.load('boost', tooldir=['waf-tools']) 142 143 opt.add_option('--check-config', 144 help=('Print the current configuration.'), 145 action="store_true", default=False, 146 dest="check_config") 147 148 opt.add_option('--cwd', 149 help=('Set the working directory for a program.'), 150 action="store", type="string", default=None, 151 dest='cwd_launch') 152 153 opt.add_option('--enable-gcov', 154 help=('Enable code coverage analysis.' 155 ' WARNING: this option only has effect ' 156 'with the configure command.'), 157 action="store_true", default=False, 158 dest='enable_gcov') 159 160 opt.add_option('--no-task-lines', 161 help=("Don't print task lines, i.e. messages saying which tasks are being executed by WAF." 162 " Coupled with a single -v will cause WAF to output only the executed commands," 163 " just like 'make' does by default."), 164 action="store_true", default=False, 165 dest='no_task_lines') 166 167 opt.add_option('--lcov-report', 168 help=('Generate a code coverage report ' 169 '(use this option after configuring with --enable-gcov and running a program)'), 170 action="store_true", default=False, 171 dest='lcov_report') 172 173 opt.add_option('--lcov-zerocounters', 174 help=('Zero the lcov counters' 175 '(use this option before rerunning a program, when generating repeated lcov reports)'), 176 action="store_true", default=False, 177 dest='lcov_zerocounters') 178 179 opt.add_option('--run', 180 help=('Run a locally built program; argument can be a program name,' 181 ' or a command starting with the program name.'), 182 type="string", default='', dest='run') 183 opt.add_option('--run-no-build', 184 help=('Run a locally built program without rebuilding the project; argument can be a program name,' 185 ' or a command starting with the program name.'), 186 type="string", default='', dest='run_no_build') 187 opt.add_option('--visualize', 188 help=('Modify --run arguments to enable the visualizer'), 189 action="store_true", default=False, dest='visualize') 190 opt.add_option('--command-template', 191 help=('Template of the command used to run the program given by --run;' 192 ' It should be a shell command string containing %s inside,' 193 ' which will be replaced by the actual program.'), 194 type="string", default=None, dest='command_template') 195 opt.add_option('--pyrun', 196 help=('Run a python program using locally built ns3 python module;' 197 ' argument is the path to the python program, optionally followed' 198 ' by command-line options that are passed to the program.'), 199 type="string", default='', dest='pyrun') 200 opt.add_option('--pyrun-no-build', 201 help=('Run a python program using locally built ns3 python module without rebuilding the project;' 202 ' argument is the path to the python program, optionally followed' 203 ' by command-line options that are passed to the program.'), 204 type="string", default='', dest='pyrun_no_build') 205 opt.add_option('--gdb', 206 help=('Change the default command template to run programs and unit tests with gdb'), 207 action="store_true", default=False, 208 dest='gdb') 209 opt.add_option('--valgrind', 210 help=('Change the default command template to run programs and unit tests with valgrind'), 211 action="store_true", default=False, 212 dest='valgrind') 213 opt.add_option('--shell', 214 help=('DEPRECATED (run ./waf shell)'), 215 action="store_true", default=False, 216 dest='shell') 217 opt.add_option('--enable-sudo', 218 help=('Use sudo to setup suid bits on ns3 executables.'), 219 dest='enable_sudo', action='store_true', 220 default=False) 221 opt.add_option('--enable-tests', 222 help=('Build the ns-3 tests.'), 223 dest='enable_tests', action='store_true', 224 default=False) 225 opt.add_option('--disable-tests', 226 help=('Do not build the ns-3 tests.'), 227 dest='disable_tests', action='store_true', 228 default=False) 229 opt.add_option('--enable-examples', 230 help=('Build the ns-3 examples.'), 231 dest='enable_examples', action='store_true', 232 default=False) 233 opt.add_option('--disable-examples', 234 help=('Do not build the ns-3 examples.'), 235 dest='disable_examples', action='store_true', 236 default=False) 237 opt.add_option('--check', 238 help=('DEPRECATED (run ./test.py)'), 239 default=False, dest='check', action="store_true") 240 opt.add_option('--enable-static', 241 help=('Compile NS-3 statically: works only on linux, without python'), 242 dest='enable_static', action='store_true', 243 default=False) 244 opt.add_option('--enable-mpi', 245 help=('Compile NS-3 with MPI and distributed simulation support'), 246 dest='enable_mpi', action='store_true', 247 default=False) 248 opt.add_option('--doxygen-no-build', 249 help=('Run doxygen to generate html documentation from source comments, ' 250 'but do not wait for ns-3 to finish the full build.'), 251 action="store_true", default=False, 252 dest='doxygen_no_build') 253 opt.add_option('--docset', 254 help=('Create Docset, without building. This requires the docsetutil tool from Xcode 9.2 or earlier. See Bugzilla 2196 for more details.'), 255 action="store_true", default=False, 256 dest="docset_build") 257 opt.add_option('--enable-des-metrics', 258 help=('Log all events in a json file with the name of the executable (which must call CommandLine::Parse(argc, argv)'), 259 action="store_true", default=False, 260 dest='enable_desmetrics') 261 opt.add_option('--cxx-standard', 262 help=('Compile NS-3 with the given C++ standard'), 263 type='string', dest='cxx_standard') 264 opt.add_option('--enable-asserts', 265 help=('Enable the asserts regardless of the compile mode'), 266 action="store_true", default=False, 267 dest='enable_asserts') 268 opt.add_option('--enable-logs', 269 help=('Enable the logs regardless of the compile mode'), 270 action="store_true", default=False, 271 dest='enable_logs') 272 273 # options provided in subdirectories 274 opt.recurse('src') 275 opt.recurse('bindings/python') 276 opt.recurse('src/internet') 277 opt.recurse('contrib') 278 279def _check_compilation_flag(conf, flag, mode='cxx', linkflags=None): 280 """ 281 Checks if the C++ compiler accepts a certain compilation flag or flags 282 flag: can be a string or a list of strings 283 """ 284 l = [] 285 if flag: 286 l.append(flag) 287 if isinstance(linkflags, list): 288 l.extend(linkflags) 289 else: 290 if linkflags: 291 l.append(linkflags) 292 if len(l) > 1: 293 flag_str = 'flags ' + ' '.join(l) 294 else: 295 flag_str = 'flag ' + ' '.join(l) 296 if len(flag_str) > 28: 297 flag_str = flag_str[:28] + "..." 298 299 conf.start_msg('Checking for compilation %s support' % (flag_str,)) 300 env = conf.env.derive() 301 302 retval = False 303 if mode == 'cc': 304 mode = 'c' 305 306 if mode == 'cxx': 307 env.append_value('CXXFLAGS', flag) 308 else: 309 env.append_value('CFLAGS', flag) 310 311 if linkflags is not None: 312 env.append_value("LINKFLAGS", linkflags) 313 314 try: 315 retval = conf.check(compiler=mode, fragment='int main() { return 0; }', features='c', env=env) 316 except Errors.ConfigurationError: 317 ok = False 318 else: 319 ok = (retval == True) 320 conf.end_msg(ok) 321 return ok 322 323 324def report_optional_feature(conf, name, caption, was_enabled, reason_not_enabled): 325 conf.env.append_value('NS3_OPTIONAL_FEATURES', [(name, caption, was_enabled, reason_not_enabled)]) 326 327 328def check_optional_feature(conf, name): 329 for (name1, caption, was_enabled, reason_not_enabled) in conf.env.NS3_OPTIONAL_FEATURES: 330 if name1 == name: 331 return was_enabled 332 raise KeyError("Feature %r not declared yet" % (name,)) 333 334 335# starting with waf 1.6, conf.check() becomes fatal by default if the 336# test fails, this alternative method makes the test non-fatal, as it 337# was in waf <= 1.5 338def _check_nonfatal(conf, *args, **kwargs): 339 try: 340 return conf.check(*args, **kwargs) 341 except conf.errors.ConfigurationError: 342 return None 343 344# Write a summary of optional features status 345def print_config(env, phase='configure'): 346 if phase == 'configure': 347 profile = get_build_profile(env) 348 else: 349 profile = get_build_profile() 350 351 print("---- Summary of optional NS-3 features:") 352 print("%-30s: %s%s%s" % ("Build profile", Logs.colors('GREEN'), 353 profile, Logs.colors('NORMAL'))) 354 bld = wutils.bld 355 print("%-30s: %s%s%s" % ("Build directory", Logs.colors('GREEN'), 356 Options.options.out, Logs.colors('NORMAL'))) 357 358 359 for (name, caption, was_enabled, reason_not_enabled) in sorted(env['NS3_OPTIONAL_FEATURES'], key=lambda s : s[1]): 360 if was_enabled: 361 status = 'enabled' 362 color = 'GREEN' 363 else: 364 status = 'not enabled (%s)' % reason_not_enabled 365 color = 'RED' 366 print("%-30s: %s%s%s" % (caption, Logs.colors(color), status, Logs.colors('NORMAL'))) 367 368# Checking for boost headers and libraries 369# 370# There are four cases: 371# A. Only need headers, and they are required 372# B. Also need compiled libraries, and they are required 373# C. Only use headers, but they are not required 374# D. Use compiled libraries, but they are not required 375# 376# A. If you only need includes there are just two steps: 377# 378# A1. Add this in your module wscript configure function: 379# 380# if not conf.require_boost_incs('my-module', 'my module caption'): 381# return 382# 383# A2. Do step FINAL below. 384# 385# B. If you also need some compiled boost libraries there are 386# three steps (instead of the above): 387# 388# B1. Declare the libraries you need by adding to your module wscript: 389# 390# REQUIRED_BOOST_LIBS = ['lib1', 'lib2'...] 391# 392# def required_boost_libs(conf): 393# conf.env['REQUIRED_BOOST_LIBS'] += REQUIRED_BOOST_LIBS 394# 395# B2. Check that the libs are present in your module wscript configure function: 396# 397# if conf.missing_boost_libs('my-module', 'my module caption', REQUIRED_BOOST_LIBS): 398# return 399# 400# B3. Do step FINAL below. 401# 402# Finally, 403# 404# FINAL. Add boost to your module wscript build function: 405# 406# # Assuming you have 407# # module = bld.create_ns3_module('my-module') 408# module.use.append('BOOST') 409# 410# If your use of boost is optional it's even simpler. 411# 412# C. For optional headers only, the two steps above are modified a little: 413# 414# C1. Add this to your module wscript configure function: 415# 416# conf.require_boost_incs('my-module', 'my module caption', required=False) 417# # Continue with config, adjusting for missing boost 418# 419# C2. Modify step FINAL as follows 420# 421# if bld.env['INCLUDES_BOOST']: 422# module.use.append('BOOST') 423# 424# D. For compiled boost libraries 425# 426# D1. Do B1 above to declare the libraries you would like to use 427# 428# D2. If you need to take action at configure time, 429# add to your module wscript configure function: 430# 431# missing_boost_libs = conf.missing_boost_libs('my-module', 'my module caption', REQUIRED_BOOST_LIBS, required=False) 432# # Continue with config, adjusting for missing boost libs 433# 434# At this point you can inspect missing_boost_libs to see 435# what libs were found and do the right thing. 436# See below for preprocessor symbols which will be available 437# in your source files. 438# 439# D3. If any of your libraries are present add to your 440# module wscript build function: 441# 442# missing_boost_libs = bld.missing_boost_libs('lib-opt', REQUIRED_BOOST_LIBS) 443# # Continue with build, adjusting for missing boost libs 444# 445# At this point you can inspect missing_boost_libs to see 446# what libs were found and do the right thing. 447# 448# In all cases you can test for boost in your code with 449# 450# #ifdef HAVE_BOOST 451# 452# Each boost compiled library will be indicated with a 'HAVE_BOOST_<lib>' 453# preprocessor symbol, which you can test. For example, for the boost 454# Signals2 library: 455# 456# #ifdef HAVE_BOOST_SIGNALS2 457# 458 459def require_boost_incs(conf, module, caption, required=True): 460 461 conf.to_log('boost: %s wants incs, required: %s' % (module, required)) 462 if conf.env['INCLUDES_BOOST']: 463 conf.to_log('boost: %s: have boost' % module) 464 return True 465 elif not required: 466 conf.to_log('boost: %s: no boost, but not required' % module) 467 return False 468 else: 469 conf.to_log('boost: %s: no boost, but required' % module) 470 conf.report_optional_feature(module, caption, False, 471 "boost headers required but not found") 472 473 # Add this module to the list of modules that won't be built 474 # if they are enabled. 475 conf.env['MODULES_NOT_BUILT'].append(module) 476 return False 477 478# Report any required boost libs which are missing 479# Return values of truthy are bad; falsey is good: 480# If all are present return False (non missing) 481# If boost not present, or no libs return True 482# If some libs present, return the list of missing libs 483def conf_missing_boost_libs(conf, module, caption, libs, required= True): 484 conf.to_log('boost: %s wants %s, required: %s' % (module, libs, required)) 485 486 if not conf.require_boost_incs(module, caption, required): 487 # No headers found, so the libs aren't there either 488 return libs 489 490 missing_boost_libs = [lib for lib in libs if lib not in conf.boost_libs] 491 492 if required and missing_boost_libs: 493 if not conf.env['LIB_BOOST']: 494 conf.to_log('boost: %s requires libs, but none found' % module) 495 conf.report_optional_feature(module, caption, False, 496 "No boost libraries were found") 497 else: 498 conf.to_log('boost: %s requires libs, but missing %s' % (module, missing_boost_libs)) 499 conf.report_optional_feature(module, caption, False, 500 "Required boost libraries not found, missing: %s" % missing_boost_libs) 501 502 # Add this module to the list of modules that won't be built 503 # if they are enabled. 504 conf.env['MODULES_NOT_BUILT'].append(module) 505 return missing_boost_libs 506 507 # Required libraries were found, or are not required 508 return missing_boost_libs 509 510def get_boost_libs(libs): 511 names = set() 512 for lib in libs: 513 if lib.startswith("boost_"): 514 lib = lib[6:] 515 if lib.endswith("-mt"): 516 lib = lib[:-3] 517 names.add(lib) 518 return names 519 520def configure_boost(conf): 521 conf.to_log('boost: loading conf') 522 conf.load('boost') 523 524 # Find Boost libraries by modules 525 conf.to_log('boost: scanning for required libs') 526 conf.env['REQUIRED_BOOST_LIBS'] = [] 527 for modules_dir in ['src', 'contrib']: 528 conf.recurse (modules_dir, name="get_required_boost_libs", mandatory=False) 529 # Check for any required boost libraries 530 if conf.env['REQUIRED_BOOST_LIBS'] is not []: 531 conf.env['REQUIRED_BOOST_LIBS'] = list(set(conf.env['REQUIRED_BOOST_LIBS'])) 532 conf.to_log("boost: libs required: %s" % conf.env['REQUIRED_BOOST_LIBS']) 533 conf.check_boost(lib=' '.join (conf.env['REQUIRED_BOOST_LIBS']), mandatory=False, required=False) 534 if not conf.env['LIB_BOOST']: 535 conf.env['LIB_BOOST'] = [] 536 else: 537 # Check with no libs, so we find the includes 538 conf.check_boost(mandatory=False, required=False) 539 540 conf.to_log('boost: checking if we should define HAVE_BOOST') 541 if conf.env['INCLUDES_BOOST']: 542 conf.to_log('boost: defining HAVE_BOOST') 543 conf.env.append_value ('CPPFLAGS', '-DHAVE_BOOST') 544 545 # Some boost libraries may have been found. 546 # Add preprocessor symbols for them 547 conf.to_log('boost: checking which libs are present') 548 if conf.env['LIB_BOOST']: 549 conf.boost_libs = get_boost_libs(conf.env['LIB_BOOST']) 550 for lib in conf.boost_libs: 551 msg='boost: lib present: ' + lib 552 if lib in conf.env['REQUIRED_BOOST_LIBS']: 553 have = '-DHAVE_BOOST_' + lib.upper() 554 conf.to_log('%s, requested, adding %s' % (msg, have)) 555 conf.env.append_value('CPPFLAGS', have) 556 else: 557 conf.to_log('%s, not required, ignoring' % msg) 558 559 boost_libs_missing = [lib for lib in conf.env['REQUIRED_BOOST_LIBS'] if lib not in conf.boost_libs] 560 for lib in boost_libs_missing: 561 conf.to_log('boost: lib missing: %s' % lib) 562 563def bld_missing_boost_libs (bld, module, libs): 564 missing_boost_libs = [lib for lib in libs if lib not in bld.boost_libs] 565 return missing_boost_libs 566 567def configure(conf): 568 # Waf does not work correctly if the absolute path contains whitespaces 569 if (re.search(r"\s", os.getcwd ())): 570 conf.fatal('Waf does not support whitespace in the path to current working directory: %s' % os.getcwd()) 571 572 conf.load('relocation', tooldir=['waf-tools']) 573 574 # attach some extra methods 575 conf.check_nonfatal = types.MethodType(_check_nonfatal, conf) 576 conf.check_compilation_flag = types.MethodType(_check_compilation_flag, conf) 577 conf.report_optional_feature = types.MethodType(report_optional_feature, conf) 578 conf.check_optional_feature = types.MethodType(check_optional_feature, conf) 579 conf.require_boost_incs = types.MethodType(require_boost_incs, conf) 580 conf.missing_boost_libs = types.MethodType(conf_missing_boost_libs, conf) 581 conf.boost_libs = set() 582 conf.env['NS3_OPTIONAL_FEATURES'] = [] 583 584 conf.load('compiler_c') 585 cc_string = '.'.join(conf.env['CC_VERSION']) 586 conf.msg('Checking for cc version',cc_string,'GREEN') 587 conf.load('compiler_cxx') 588 conf.load('cflags', tooldir=['waf-tools']) 589 conf.load('command', tooldir=['waf-tools']) 590 conf.load('gnu_dirs') 591 conf.load('clang_compilation_database', tooldir=['waf-tools']) 592 593 env = conf.env 594 595 if Options.options.enable_gcov: 596 env['GCOV_ENABLED'] = True 597 env.append_value('CCFLAGS', '-fprofile-arcs') 598 env.append_value('CCFLAGS', '-ftest-coverage') 599 env.append_value('CXXFLAGS', '-fprofile-arcs') 600 env.append_value('CXXFLAGS', '-ftest-coverage') 601 env.append_value('LINKFLAGS', '-lgcov') 602 env.append_value('LINKFLAGS', '-coverage') 603 604 if Options.options.build_profile == 'debug': 605 env.append_value('DEFINES', 'NS3_BUILD_PROFILE_DEBUG') 606 env.append_value('DEFINES', 'NS3_ASSERT_ENABLE') 607 env.append_value('DEFINES', 'NS3_LOG_ENABLE') 608 609 if Options.options.build_profile == 'release': 610 env.append_value('DEFINES', 'NS3_BUILD_PROFILE_RELEASE') 611 612 if Options.options.build_profile == 'optimized': 613 env.append_value('DEFINES', 'NS3_BUILD_PROFILE_OPTIMIZED') 614 615 if Options.options.enable_logs: 616 env.append_unique('DEFINES', 'NS3_LOG_ENABLE') 617 if Options.options.enable_asserts: 618 env.append_unique('DEFINES', 'NS3_ASSERT_ENABLE') 619 620 env['PLATFORM'] = sys.platform 621 env['BUILD_PROFILE'] = Options.options.build_profile 622 if Options.options.build_profile == "release": 623 env['BUILD_SUFFIX'] = '' 624 else: 625 env['BUILD_SUFFIX'] = '-'+Options.options.build_profile 626 627 env['APPNAME'] = wutils.APPNAME 628 env['VERSION'] = wutils.VERSION 629 630 if conf.env['CXX_NAME'] in ['gcc']: 631 if tuple(map(int, conf.env['CC_VERSION'])) < gcc_min_version: 632 conf.fatal('gcc version %s older than minimum supported version %s' % 633 ('.'.join(conf.env['CC_VERSION']), '.'.join(map(str, gcc_min_version)))) 634 635 if conf.env['CXX_NAME'] in ['gcc', 'icc']: 636 if Options.options.build_profile == 'release': 637 env.append_value('CXXFLAGS', '-fomit-frame-pointer') 638 if Options.options.build_profile == 'optimized': 639 if conf.check_compilation_flag('-march=native'): 640 env.append_value('CXXFLAGS', '-march=native') 641 env.append_value('CXXFLAGS', '-fstrict-overflow') 642 if conf.env['CXX_NAME'] in ['gcc']: 643 env.append_value('CXXFLAGS', '-Wstrict-overflow=2') 644 645 if sys.platform == 'win32': 646 env.append_value("LINKFLAGS", "-Wl,--enable-runtime-pseudo-reloc") 647 elif sys.platform == 'cygwin': 648 env.append_value("LINKFLAGS", "-Wl,--enable-auto-import") 649 650 cxx = env['CXX'] 651 cxx_check_libstdcxx = cxx + ['-print-file-name=libstdc++.so'] 652 p = subprocess.Popen(cxx_check_libstdcxx, stdout=subprocess.PIPE) 653 libstdcxx_output = maybe_decode(p.stdout.read().strip()) 654 libstdcxx_location = os.path.dirname(libstdcxx_output) 655 p.wait() 656 if libstdcxx_location: 657 conf.env.append_value('NS3_MODULE_PATH', libstdcxx_location) 658 659 if Utils.unversioned_sys_platform() in ['linux']: 660 if conf.check_compilation_flag('-Wl,--soname=foo'): 661 env['WL_SONAME_SUPPORTED'] = True 662 663 # bug 2181 on clang warning suppressions 664 if conf.env['CXX_NAME'] in ['clang']: 665 if Utils.unversioned_sys_platform() == 'darwin': 666 if tuple(map(int, conf.env['CC_VERSION'])) >= darwin_clang_version_warn_unused_local_typedefs: 667 env.append_value('CXXFLAGS', '-Wno-unused-local-typedefs') 668 669 if tuple(map(int, conf.env['CC_VERSION'])) >= darwin_clang_version_warn_potentially_evaluated: 670 671 env.append_value('CXXFLAGS', '-Wno-potentially-evaluated-expression') 672 673 else: 674 if tuple(map(int, conf.env['CC_VERSION'])) >= clang_version_warn_unused_local_typedefs: 675 676 env.append_value('CXXFLAGS', '-Wno-unused-local-typedefs') 677 678 if tuple(map(int, conf.env['CC_VERSION'])) >= clang_version_warn_potentially_evaluated: 679 env.append_value('CXXFLAGS', '-Wno-potentially-evaluated-expression') 680 681 env['ENABLE_STATIC_NS3'] = False 682 if Options.options.enable_static: 683 if Utils.unversioned_sys_platform() == 'darwin': 684 if conf.check_compilation_flag(flag=[], linkflags=['-Wl,-all_load']): 685 conf.report_optional_feature("static", "Static build", True, '') 686 env['ENABLE_STATIC_NS3'] = True 687 else: 688 conf.report_optional_feature("static", "Static build", False, 689 "Link flag -Wl,-all_load does not work") 690 else: 691 if conf.check_compilation_flag(flag=[], linkflags=['-Wl,--whole-archive,-Bstatic', '-Wl,-Bdynamic,--no-whole-archive']): 692 conf.report_optional_feature("static", "Static build", True, '') 693 env['ENABLE_STATIC_NS3'] = True 694 else: 695 conf.report_optional_feature("static", "Static build", False, 696 "Link flag -Wl,--whole-archive,-Bstatic does not work") 697 698 # Checks if environment variable specifies the C++ language standard and/or 699 # if the user has specified the standard via the -cxx-standard argument 700 # to 'waf configure'. The following precedence and behavior is implemented: 701 # 1) if user does not specify anything, Waf will use the default standard 702 # configured for ns-3, which is configured below 703 # 2) if user specifies the '-cxx-standard' option, it will be used instead 704 # of the default. 705 # Example: ./waf configure --cxx-standard=-std=c++14 706 # 3) if user specifies the C++ standard via the CXXFLAGS environment 707 # variable, it will be used instead of the default. 708 # Example: CXXFLAGS="-std=c++14" ./waf configure 709 # 4) if user specifies both the CXXFLAGS environment variable and the 710 # -cxx-standard argument, the latter will take precedence and a warning 711 # will be emitted in the configure output if there were conflicting 712 # standards between the two. 713 # Example: CXXFLAGS="-std=c++14" ./waf configure --cxx-standard=-std=c++17 714 # (in the above scenario, Waf will use c++17 but warn about it) 715 # Note: If the C++ standard is not recognized, configuration will error exit 716 cxx_standard = "" 717 cxx_standard_env = "" 718 for flag in env['CXXFLAGS']: 719 if flag[:5] == "-std=": 720 cxx_standard_env = flag 721 722 if not cxx_standard_env and Options.options.cxx_standard: 723 cxx_standard = Options.options.cxx_standard 724 env.append_value('CXXFLAGS', cxx_standard) 725 elif cxx_standard_env and not Options.options.cxx_standard: 726 cxx_standard = cxx_standard_env 727 # No need to change CXXFLAGS 728 elif cxx_standard_env and Options.options.cxx_standard and cxx_standard_env != Options.options.cxx_standard: 729 Logs.warn("user-specified --cxx-standard (" + 730 Options.options.cxx_standard + ") does not match the value in CXXFLAGS (" + cxx_standard_env + "); Waf will use the --cxx-standard value") 731 cxx_standard = Options.options.cxx_standard 732 env['CXXFLAGS'].remove(cxx_standard_env) 733 env.append_value('CXXFLAGS', cxx_standard) 734 elif cxx_standard_env and Options.options.cxx_standard and cxx_standard_env == Options.options.cxx_standard: 735 cxx_standard = Options.options.cxx_standard 736 # No need to change CXXFLAGS 737 elif not cxx_standard and not Options.options.cxx_standard: 738 cxx_standard = "-std=c++17" 739 env.append_value('CXXFLAGS', cxx_standard) 740 741 if not conf.check_compilation_flag(cxx_standard): 742 raise Errors.ConfigurationError("Exiting because C++ standard value " + cxx_standard + " is not recognized") 743 744 # Handle boost 745 configure_boost(conf) 746 747 # Set this so that the lists won't be printed at the end of this 748 # configure command. 749 conf.env['PRINT_BUILT_MODULES_AT_END'] = False 750 751 conf.env['MODULES_NOT_BUILT'] = [] 752 753 conf.recurse('bindings/python') 754 755 conf.recurse('src') 756 conf.recurse('contrib') 757 758 # Set the list of enabled modules. 759 if Options.options.enable_modules: 760 # Use the modules explicitly enabled. 761 _enabled_mods = [] 762 _enabled_contrib_mods = [] 763 for mod in Options.options.enable_modules.split(','): 764 if mod in conf.env['NS3_MODULES'] and mod.startswith('ns3-'): 765 _enabled_mods.append(mod) 766 elif 'ns3-' + mod in conf.env['NS3_MODULES']: 767 _enabled_mods.append('ns3-' + mod) 768 elif mod in conf.env['NS3_CONTRIBUTED_MODULES'] and mod.startswith('ns3-'): 769 _enabled_contrib_mods.append(mod) 770 elif 'ns3-' + mod in conf.env['NS3_CONTRIBUTED_MODULES']: 771 _enabled_contrib_mods.append('ns3-' + mod) 772 conf.env['NS3_ENABLED_MODULES'] = _enabled_mods 773 conf.env['NS3_ENABLED_CONTRIBUTED_MODULES'] = _enabled_contrib_mods 774 775 else: 776 # Use the enabled modules list from the ns3 configuration file. 777 if modules_enabled[0] == 'all_modules': 778 # Enable all modules if requested. 779 conf.env['NS3_ENABLED_MODULES'] = conf.env['NS3_MODULES'] 780 conf.env['NS3_ENABLED_CONTRIBUTED_MODULES'] = conf.env['NS3_CONTRIBUTED_MODULES'] 781 else: 782 # Enable the modules from the list. 783 _enabled_mods = [] 784 _enabled_contrib_mods = [] 785 for mod in modules_enabled: 786 if mod in conf.env['NS3_MODULES'] and mod.startswith('ns3-'): 787 _enabled_mods.append(mod) 788 elif 'ns3-' + mod in conf.env['NS3_MODULES']: 789 _enabled_mods.append('ns3-' + mod) 790 elif mod in conf.env['NS3_CONTRIBUTED_MODULES'] and mod.startswith('ns3-'): 791 _enabled_contrib_mods.append(mod) 792 elif 'ns3-' + mod in conf.env['NS3_CONTRIBUTED_MODULES']: 793 _enabled_contrib_mods.append('ns3-' + mod) 794 conf.env['NS3_ENABLED_MODULES'] = _enabled_mods 795 conf.env['NS3_ENABLED_CONTRIBUTED_MODULES'] = _enabled_contrib_mods 796 797 # Add the template module to the list of enabled modules that 798 # should not be built if this is a static build on Darwin. They 799 # don't work there for the template module, and this is probably 800 # because the template module has no source files. 801 if conf.env['ENABLE_STATIC_NS3'] and sys.platform == 'darwin': 802 conf.env['MODULES_NOT_BUILT'].append('template') 803 804 # Remove these modules from the list of enabled modules. 805 for not_built in conf.env['MODULES_NOT_BUILT']: 806 not_built_name = 'ns3-' + not_built 807 if not_built_name in conf.env['NS3_ENABLED_MODULES']: 808 conf.env['NS3_ENABLED_MODULES'].remove(not_built_name) 809 if not conf.env['NS3_ENABLED_MODULES']: 810 raise WafError('Exiting because the ' + not_built + ' module can not be built and it was the only one enabled.') 811 elif not_built_name in conf.env['NS3_ENABLED_CONTRIBUTED_MODULES']: 812 conf.env['NS3_ENABLED_CONTRIBUTED_MODULES'].remove(not_built_name) 813 814 # for suid bits 815 try: 816 conf.find_program('sudo', var='SUDO') 817 except WafError: 818 pass 819 820 why_not_sudo = "because we like it" 821 if Options.options.enable_sudo and conf.env['SUDO']: 822 env['ENABLE_SUDO'] = True 823 else: 824 env['ENABLE_SUDO'] = False 825 if Options.options.enable_sudo: 826 why_not_sudo = "program sudo not found" 827 else: 828 why_not_sudo = "option --enable-sudo not selected" 829 830 conf.report_optional_feature("ENABLE_SUDO", "Use sudo to set suid bit", env['ENABLE_SUDO'], why_not_sudo) 831 832 # Decide if tests will be built or not. 833 if Options.options.enable_tests: 834 # Tests were explicitly enabled. 835 env['ENABLE_TESTS'] = True 836 why_not_tests = "option --enable-tests selected" 837 elif Options.options.disable_tests: 838 # Tests were explicitly disabled. 839 env['ENABLE_TESTS'] = False 840 why_not_tests = "option --disable-tests selected" 841 else: 842 # Enable tests based on the ns3 configuration file. 843 env['ENABLE_TESTS'] = tests_enabled 844 if config_file_exists: 845 why_not_tests = "based on configuration file" 846 elif tests_enabled: 847 why_not_tests = "defaults to enabled" 848 else: 849 why_not_tests = "defaults to disabled" 850 851 conf.report_optional_feature("ENABLE_TESTS", "Tests", env['ENABLE_TESTS'], why_not_tests) 852 853 # Decide if examples will be built or not. 854 if Options.options.enable_examples: 855 # Examples were explicitly enabled. 856 env['ENABLE_EXAMPLES'] = True 857 why_not_examples = "option --enable-examples selected" 858 elif Options.options.disable_examples: 859 # Examples were explicitly disabled. 860 env['ENABLE_EXAMPLES'] = False 861 why_not_examples = "option --disable-examples selected" 862 else: 863 # Enable examples based on the ns3 configuration file. 864 env['ENABLE_EXAMPLES'] = examples_enabled 865 if config_file_exists: 866 why_not_examples = "based on configuration file" 867 elif examples_enabled: 868 why_not_examples = "defaults to enabled" 869 else: 870 why_not_examples = "defaults to disabled" 871 872 conf.report_optional_feature("ENABLE_EXAMPLES", "Examples", env['ENABLE_EXAMPLES'], 873 why_not_examples) 874 try: 875 for dir in os.listdir('examples'): 876 if dir.startswith('.') or dir == 'CVS': 877 continue 878 conf.env.append_value('EXAMPLE_DIRECTORIES', dir) 879 except OSError: 880 return 881 882 env['VALGRIND_FOUND'] = False 883 try: 884 conf.find_program('valgrind', var='VALGRIND') 885 env['VALGRIND_FOUND'] = True 886 except WafError: 887 pass 888 889 # These flags are used for the implicitly dependent modules. 890 if env['ENABLE_STATIC_NS3']: 891 if sys.platform == 'darwin': 892 env.STLIB_MARKER = '-Wl,-all_load' 893 else: 894 env.STLIB_MARKER = '-Wl,--whole-archive,-Bstatic' 895 env.SHLIB_MARKER = '-Wl,-Bdynamic,--no-whole-archive' 896 897 898 have_gsl = conf.check_cfg(package='gsl', args=['--cflags', '--libs'], 899 uselib_store='GSL', mandatory=False) 900 conf.env['ENABLE_GSL'] = have_gsl 901 conf.report_optional_feature("GSL", "GNU Scientific Library (GSL)", 902 conf.env['ENABLE_GSL'], 903 "GSL not found") 904 905 conf.find_program('libgcrypt-config', var='LIBGCRYPT_CONFIG', msg="libgcrypt-config", mandatory=False) 906 if env.LIBGCRYPT_CONFIG: 907 conf.check_cfg(path=env.LIBGCRYPT_CONFIG, msg="Checking for libgcrypt", args='--cflags --libs', package='', 908 define_name="HAVE_GCRYPT", global_define=True, uselib_store='GCRYPT', mandatory=False) 909 conf.report_optional_feature("libgcrypt", "Gcrypt library", 910 conf.env.HAVE_GCRYPT, "libgcrypt not found: you can use libgcrypt-config to find its location.") 911 912 why_not_desmetrics = "defaults to disabled" 913 if Options.options.enable_desmetrics: 914 conf.env['ENABLE_DES_METRICS'] = True 915 env.append_value('DEFINES', 'ENABLE_DES_METRICS') 916 why_not_desmetrics = "option --enable-des-metrics selected" 917 conf.report_optional_feature("DES Metrics", "DES Metrics event collection", conf.env['ENABLE_DES_METRICS'], why_not_desmetrics) 918 919 920 # for compiling C code, copy over the CXX* flags 921 conf.env.append_value('CCFLAGS', conf.env['CXXFLAGS']) 922 923 def add_gcc_flag(flag): 924 if env['COMPILER_CXX'] == 'g++' and 'CXXFLAGS' not in os.environ: 925 if conf.check_compilation_flag(flag, mode='cxx'): 926 env.append_value('CXXFLAGS', flag) 927 if env['COMPILER_CC'] == 'gcc' and 'CCFLAGS' not in os.environ: 928 if conf.check_compilation_flag(flag, mode='cc'): 929 env.append_value('CCFLAGS', flag) 930 931 add_gcc_flag('-fstrict-aliasing') 932 add_gcc_flag('-Wstrict-aliasing') 933 934 try: 935 conf.find_program('doxygen', var='DOXYGEN') 936 except WafError: 937 pass 938 939 # append user defined flags after all our ones 940 for (confvar, envvar) in [['CCFLAGS', 'CCFLAGS_EXTRA'], 941 ['CXXFLAGS', 'CXXFLAGS_EXTRA'], 942 ['LINKFLAGS', 'LINKFLAGS_EXTRA'], 943 ['LINKFLAGS', 'LDFLAGS_EXTRA']]: 944 if envvar in os.environ: 945 value = shlex.split(os.environ[envvar]) 946 conf.env.append_value(confvar, value) 947 948 print_config(env) 949 950 951class SuidBuild_task(Task.Task): 952 """task that makes a binary Suid 953 """ 954 after = ['cxxprogram', 'cxxshlib', 'cxxstlib'] 955 def __init__(self, *args, **kwargs): 956 super(SuidBuild_task, self).__init__(*args, **kwargs) 957 self.m_display = 'build-suid' 958 try: 959 program_obj = wutils.find_program(self.generator.name, self.generator.env) 960 except ValueError as ex: 961 raise WafError(str(ex)) 962 program_node = program_obj.path.find_or_declare(program_obj.target) 963 self.filename = program_node.get_bld().abspath() 964 965 966 def run(self): 967 print('setting suid bit on executable ' + self.filename, file=sys.stderr) 968 if subprocess.Popen(['sudo', 'chown', 'root', self.filename]).wait(): 969 return 1 970 if subprocess.Popen(['sudo', 'chmod', 'u+s', self.filename]).wait(): 971 return 1 972 return 0 973 974 def runnable_status(self): 975 "RUN_ME SKIP_ME or ASK_LATER" 976 try: 977 st = os.stat(self.filename) 978 except OSError: 979 return Task.ASK_LATER 980 if st.st_uid == 0: 981 return Task.SKIP_ME 982 else: 983 return Task.RUN_ME 984 985def create_suid_program(bld, name): 986 grp = bld.current_group 987 bld.add_group() # this to make sure no two sudo tasks run at the same time 988 program = bld(features='cxx cxxprogram') 989 program.is_ns3_program = True 990 program.module_deps = list() 991 program.name = name 992 program.target = "%s%s-%s%s" % (wutils.APPNAME, wutils.VERSION, name, bld.env.BUILD_SUFFIX) 993 994 if bld.env['ENABLE_SUDO']: 995 program.create_task("SuidBuild_task") 996 997 bld.set_group(grp) 998 999 return program 1000 1001def create_ns3_program(bld, name, dependencies=('core',)): 1002 program = bld(features='cxx cxxprogram') 1003 1004 program.is_ns3_program = True 1005 program.name = name 1006 program.target = "%s%s-%s%s" % (wutils.APPNAME, wutils.VERSION, name, bld.env.BUILD_SUFFIX) 1007 # Each of the modules this program depends on has its own library. 1008 program.ns3_module_dependencies = ['ns3-'+dep for dep in dependencies] 1009 program.includes = Context.out_dir 1010 #make a copy here to prevent additions to program.use from polluting program.ns3_module_dependencies 1011 program.use = program.ns3_module_dependencies.copy() 1012 if program.env['ENABLE_STATIC_NS3']: 1013 if sys.platform == 'darwin': 1014 program.env.STLIB_MARKER = '-Wl,-all_load' 1015 else: 1016 program.env.STLIB_MARKER = '-Wl,-Bstatic,--whole-archive' 1017 program.env.SHLIB_MARKER = '-Wl,-Bdynamic,--no-whole-archive' 1018 else: 1019 if program.env.DEST_BINFMT == 'elf': 1020 # All ELF platforms are impacted but only the gcc compiler has a flag to fix it. 1021 if 'gcc' in (program.env.CXX_NAME, program.env.CC_NAME): 1022 program.env.append_value ('SHLIB_MARKER', '-Wl,--no-as-needed') 1023 1024 return program 1025 1026def register_ns3_script(bld, name, dependencies=('core',)): 1027 ns3_module_dependencies = ['ns3-'+dep for dep in dependencies] 1028 bld.env.append_value('NS3_SCRIPT_DEPENDENCIES', [(name, ns3_module_dependencies)]) 1029 1030def add_examples_programs(bld): 1031 env = bld.env 1032 if env['ENABLE_EXAMPLES']: 1033 # Add a define, so this is testable from code 1034 env.append_value('DEFINES', 'NS3_ENABLE_EXAMPLES') 1035 1036 try: 1037 for dir in os.listdir('examples'): 1038 if dir.startswith('.') or dir == 'CVS': 1039 continue 1040 if os.path.isdir(os.path.join('examples', dir)): 1041 bld.recurse(os.path.join('examples', dir)) 1042 except OSError: 1043 return 1044 1045def add_scratch_programs(bld): 1046 all_modules = [mod[len("ns3-"):] for mod in bld.env['NS3_ENABLED_MODULES'] + bld.env['NS3_ENABLED_CONTRIBUTED_MODULES']] 1047 1048 try: 1049 for filename in os.listdir("scratch"): 1050 if filename.startswith('.') or filename == 'CVS': 1051 continue 1052 if os.path.isdir(os.path.join("scratch", filename)): 1053 obj = bld.create_ns3_program(filename, all_modules) 1054 obj.path = obj.path.find_dir('scratch').find_dir(filename) 1055 obj.source = obj.path.ant_glob('*.cc') 1056 obj.target = filename 1057 obj.name = obj.target 1058 obj.install_path = None 1059 elif filename.endswith(".cc"): 1060 name = filename[:-len(".cc")] 1061 obj = bld.create_ns3_program(name, all_modules) 1062 obj.path = obj.path.find_dir('scratch') 1063 obj.source = filename 1064 obj.target = name 1065 obj.name = obj.target 1066 obj.install_path = None 1067 except OSError: 1068 return 1069 1070def _get_all_task_gen(self): 1071 for group in self.groups: 1072 for taskgen in group: 1073 yield taskgen 1074 1075 1076# ok, so WAF does not provide an API to prevent an 1077# arbitrary taskgen from running; we have to muck around with 1078# WAF internal state, something that might stop working if 1079# WAF is upgraded... 1080def _exclude_taskgen(self, taskgen): 1081 for group in self.groups: 1082 for tg1 in group: 1083 if tg1 is taskgen: 1084 group.remove(tg1) 1085 break 1086 else: 1087 continue 1088 break 1089 1090 1091def _find_ns3_module(self, name): 1092 for obj in _get_all_task_gen(self): 1093 # disable the modules themselves 1094 if hasattr(obj, "is_ns3_module") and obj.name == name: 1095 return obj 1096 raise KeyError(name) 1097 1098# Parse the waf lockfile generated by latest 'configure' operation 1099def get_build_profile(env=None): 1100 if env: 1101 return Options.options.build_profile 1102 1103 lockfile = os.environ.get('WAFLOCK', '.lock-waf_%s_build' % sys.platform) 1104 with open(lockfile, "r") as f: 1105 for line in f: 1106 if line.startswith("options ="): 1107 _, val = line.split('=', 1) 1108 for x in val.split(','): 1109 optkey, optval = x.split(':') 1110 if (optkey.lstrip() == '\'build_profile\''): 1111 return str(optval.lstrip()).replace("'","") 1112 1113 return "not found" 1114 1115def build(bld): 1116 env = bld.env 1117 1118 if Options.options.check_config: 1119 print_config(env, 'build') 1120 else: 1121 if Options.options.check_profile: 1122 profile = get_build_profile() 1123 print("Build profile: %s" % profile) 1124 1125 if Options.options.check_profile or Options.options.check_config: 1126 raise SystemExit(0) 1127 return 1128 1129 # If --enabled-modules option was given, then print a warning 1130 # message and exit this function. 1131 if Options.options.enable_modules: 1132 Logs.warn("No modules were built. Use waf configure --enable-modules to enable modules.") 1133 return 1134 1135 bld.env['NS3_MODULES_WITH_TEST_LIBRARIES'] = [] 1136 bld.env['NS3_ENABLED_MODULE_TEST_LIBRARIES'] = [] 1137 bld.env['NS3_SCRIPT_DEPENDENCIES'] = [] 1138 bld.env['NS3_RUNNABLE_PROGRAMS'] = [] 1139 bld.env['NS3_RUNNABLE_SCRIPTS'] = [] 1140 1141 wutils.bld = bld 1142 if Options.options.no_task_lines: 1143 from waflib import Runner 1144 def null_printout(s): 1145 pass 1146 Runner.printout = null_printout 1147 1148 Options.cwd_launch = bld.path.abspath() 1149 bld.create_ns3_program = types.MethodType(create_ns3_program, bld) 1150 bld.register_ns3_script = types.MethodType(register_ns3_script, bld) 1151 bld.create_suid_program = types.MethodType(create_suid_program, bld) 1152 bld.__class__.all_task_gen = property(_get_all_task_gen) 1153 bld.exclude_taskgen = types.MethodType(_exclude_taskgen, bld) 1154 bld.find_ns3_module = types.MethodType(_find_ns3_module, bld) 1155 bld.missing_boost_libs = types.MethodType(bld_missing_boost_libs, bld) 1156 1157 # Clean documentation build directories; other cleaning happens later 1158 if bld.cmd == 'clean': 1159 _cleandocs() 1160 1161 # Cache available boost lib names 1162 bld.boost_libs = set() 1163 if bld.env['LIB_BOOST']: 1164 bld.boost_libs = get_boost_libs(bld.env['LIB_BOOST']) 1165 1166 1167 # process subfolders from here 1168 bld.recurse('src') 1169 bld.recurse('contrib') 1170 1171 # If modules have been enabled, then set lists of enabled modules 1172 # and enabled module test libraries. 1173 if env['NS3_ENABLED_MODULES'] or env['NS3_ENABLED_CONTRIBUTED_MODULES']: 1174 1175 modules = env['NS3_ENABLED_MODULES'] 1176 contribModules = env['NS3_ENABLED_CONTRIBUTED_MODULES'] 1177 1178 # Find out about additional modules that need to be enabled 1179 # due to dependency constraints. 1180 changed = True 1181 while changed: 1182 changed = False 1183 for module in modules + contribModules: 1184 module_obj = bld.get_tgen_by_name(module) 1185 if module_obj is None: 1186 raise ValueError("module %s not found" % module) 1187 # Each enabled module has its own library. 1188 for dep in module_obj.use: 1189 if not dep.startswith('ns3-'): 1190 continue 1191 if dep not in modules and dep not in contribModules: 1192 if dep in env['NS3_MODULES']: 1193 modules.append(dep) 1194 changed = True 1195 elif dep in env['NS3_CONTRIBUTED_MODULES']: 1196 contribModules.append(dep) 1197 changed = True 1198 else: 1199 Logs.error("Error: Cannot find dependency \'" + dep[4:] + "\' of module \'" 1200 + module[4:] + "\'; check the module wscript for errors.") 1201 raise SystemExit(1) 1202 1203 env['NS3_ENABLED_MODULES'] = modules 1204 1205 env['NS3_ENABLED_CONTRIBUTED_MODULES'] = contribModules 1206 1207 # If tests are being built, then set the list of the enabled 1208 # module test libraries. 1209 if env['ENABLE_TESTS']: 1210 for (mod, testlib) in bld.env['NS3_MODULES_WITH_TEST_LIBRARIES']: 1211 if mod in bld.env['NS3_ENABLED_MODULES'] or mod in bld.env['NS3_ENABLED_CONTRIBUTED_MODULES']: 1212 bld.env.append_value('NS3_ENABLED_MODULE_TEST_LIBRARIES', testlib) 1213 1214 add_examples_programs(bld) 1215 add_scratch_programs(bld) 1216 1217 if env['NS3_ENABLED_MODULES'] or env['NS3_ENABLED_CONTRIBUTED_MODULES']: 1218 modules = env['NS3_ENABLED_MODULES'] 1219 contribModules = env['NS3_ENABLED_CONTRIBUTED_MODULES'] 1220 1221 # Exclude the programs other misc task gens that depend on disabled modules 1222 for obj in list(bld.all_task_gen): 1223 1224 # check for ns3moduleheader_taskgen 1225 if 'ns3moduleheader' in getattr(obj, "features", []): 1226 if ("ns3-%s" % obj.module) not in modules and ("ns3-%s" % obj.module) not in contribModules: 1227 obj.mode = 'remove' # tell it to remove headers instead of installing 1228 1229 # check for programs 1230 if hasattr(obj, 'ns3_module_dependencies'): 1231 # this is an NS-3 program (bld.create_ns3_program) 1232 program_built = True 1233 for dep in obj.ns3_module_dependencies: 1234 if dep not in modules and dep not in contribModules: # prog. depends on a module that isn't enabled? 1235 bld.exclude_taskgen(obj) 1236 program_built = False 1237 break 1238 1239 # Add this program to the list if all of its 1240 # dependencies will be built. 1241 if program_built: 1242 object_name = "%s%s-%s%s" % (wutils.APPNAME, wutils.VERSION, 1243 obj.name, bld.env.BUILD_SUFFIX) 1244 1245 # Get the relative path to the program from the 1246 # launch directory. 1247 launch_dir = os.path.abspath(Context.launch_dir) 1248 object_relative_path = os.path.join( 1249 wutils.relpath(obj.path.get_bld().abspath(), launch_dir), 1250 object_name) 1251 1252 bld.env.append_value('NS3_RUNNABLE_PROGRAMS', object_relative_path) 1253 1254 # disable the modules themselves 1255 if hasattr(obj, "is_ns3_module") and obj.name not in modules and obj.name not in contribModules: 1256 bld.exclude_taskgen(obj) # kill the module 1257 1258 # disable the module test libraries 1259 if hasattr(obj, "is_ns3_module_test_library"): 1260 if not env['ENABLE_TESTS'] or ((obj.module_name not in modules) and (obj.module_name not in contribModules)) : 1261 bld.exclude_taskgen(obj) # kill the module test library 1262 1263 # disable the ns3header_taskgen 1264 if 'ns3header' in getattr(obj, "features", []): 1265 if ("ns3-%s" % obj.module) not in modules and ("ns3-%s" % obj.module) not in contribModules: 1266 obj.mode = 'remove' # tell it to remove headers instead of installing 1267 1268 # disable the ns3privateheader_taskgen 1269 if 'ns3privateheader' in getattr(obj, "features", []): 1270 if ("ns3-%s" % obj.module) not in modules and ("ns3-%s" % obj.module) not in contribModules: 1271 1272 obj.mode = 'remove' # tell it to remove headers instead of installing 1273 1274 # disable pcfile taskgens for disabled modules 1275 if 'ns3pcfile' in getattr(obj, "features", []): 1276 if obj.module not in bld.env.NS3_ENABLED_MODULES and obj.module not in bld.env.NS3_ENABLED_CONTRIBUTED_MODULES: 1277 bld.exclude_taskgen(obj) 1278 1279 # disable python bindings for disabled modules 1280 if 'pybindgen' in obj.name: 1281 if ("ns3-%s" % obj.module) not in modules and ("ns3-%s" % obj.module) not in contribModules: 1282 bld.exclude_taskgen(obj) 1283 if 'pyext' in getattr(obj, "features", []): 1284 if ("ns3-%s" % obj.module) not in modules and ("ns3-%s" % obj.module) not in contribModules: 1285 bld.exclude_taskgen(obj) 1286 1287 1288 if env['NS3_ENABLED_MODULES']: 1289 env['NS3_ENABLED_MODULES'] = list(modules) 1290 1291 if env['NS3_ENABLED_CONTRIBUTED_MODULES']: 1292 env['NS3_ENABLED_CONTRIBUTED_MODULES'] = list(contribModules) 1293 1294 # Determine which scripts will be runnable. 1295 for (script, dependencies) in bld.env['NS3_SCRIPT_DEPENDENCIES']: 1296 script_runnable = True 1297 for dep in dependencies: 1298 if dep not in modules and dep not in contribModules: 1299 script_runnable = False 1300 break 1301 1302 # Add this script to the list if all of its dependencies will 1303 # be built. 1304 if script_runnable: 1305 bld.env.append_value('NS3_RUNNABLE_SCRIPTS', script) 1306 1307 bld.recurse('bindings/python') 1308 1309 # Process this subfolder here after the lists of enabled modules 1310 # and module test libraries have been set. 1311 bld.recurse('utils') 1312 1313 # Set this so that the lists will be printed at the end of this 1314 # build command. 1315 bld.env['PRINT_BUILT_MODULES_AT_END'] = True 1316 1317 # Do not print the modules built if build command was "clean" 1318 if bld.cmd == 'clean': 1319 bld.env['PRINT_BUILT_MODULES_AT_END'] = False 1320 1321 if Options.options.run: 1322 # Check that the requested program name is valid 1323 program_name, dummy_program_argv = wutils.get_run_program(Options.options.run, wutils.get_command_template(env)) 1324 1325 # When --run'ing a program, tell WAF to only build that program, 1326 # nothing more; this greatly speeds up compilation when all you 1327 # want to do is run a test program. 1328 Options.options.targets += ',' + os.path.basename(program_name) 1329 if getattr(Options.options, "visualize", False): 1330 program_obj = wutils.find_program(program_name, bld.env) 1331 program_obj.use.append('ns3-visualizer') 1332 for gen in bld.all_task_gen: 1333 if type(gen).__name__ in ['ns3header_taskgen', 'ns3privateheader_taskgen', 'ns3moduleheader_taskgen']: 1334 gen.post() 1335 1336 if Options.options.run or Options.options.pyrun: 1337 bld.env['PRINT_BUILT_MODULES_AT_END'] = False 1338 1339 if Options.options.doxygen_no_build: 1340 _doxygen(bld, skip_pid=True) 1341 raise SystemExit(0) 1342 1343 if Options.options.run_no_build: 1344 # Check that the requested program name is valid 1345 program_name, dummy_program_argv = wutils.get_run_program(Options.options.run_no_build, wutils.get_command_template(bld.env)) 1346 # Run the program 1347 wutils.run_program(Options.options.run_no_build, bld.env, wutils.get_command_template(bld.env), visualize=Options.options.visualize) 1348 raise SystemExit(0) 1349 1350 if Options.options.pyrun_no_build: 1351 wutils.run_python_program(Options.options.pyrun_no_build, bld.env, 1352 visualize=Options.options.visualize) 1353 raise SystemExit(0) 1354 1355def _cleandir(name): 1356 try: 1357 shutil.rmtree(name) 1358 except: 1359 pass 1360 1361def _cleandocs(): 1362 _cleandir('doc/html') 1363 _cleandir('doc/html-warn') 1364 _cleandir('doc/manual/build') 1365 _cleandir('doc/manual/source-temp') 1366 _cleandir('doc/tutorial/build') 1367 _cleandir('doc/models/build') 1368 _cleandir('doc/models/source-temp') 1369 1370# 'distclean' typically only cleans out build/ directory 1371# Here we clean out any build or documentation artifacts not in build/ 1372def distclean(ctx): 1373 _cleandocs() 1374 # Now call waf's normal distclean 1375 Scripting.distclean(ctx) 1376 1377def shutdown(ctx): 1378 bld = wutils.bld 1379 if wutils.bld is None: 1380 return 1381 env = bld.env 1382 1383 # Only print the lists if a build was done. 1384 if (env['PRINT_BUILT_MODULES_AT_END']): 1385 1386 # Print the list of built modules. 1387 print() 1388 print('Modules built:') 1389 names_without_prefix = [] 1390 for name in env['NS3_ENABLED_MODULES'] + env['NS3_ENABLED_CONTRIBUTED_MODULES']: 1391 name1 = name[len('ns3-'):] 1392 if name not in env.MODULAR_BINDINGS_MODULES: 1393 name1 += " (no Python)" 1394 names_without_prefix.append(name1) 1395 print_module_names(names_without_prefix) 1396 print() 1397 1398 # Print the list of enabled modules that were not built. 1399 if env['MODULES_NOT_BUILT']: 1400 print('Modules not built (see ns-3 tutorial for explanation):') 1401 print_module_names(env['MODULES_NOT_BUILT']) 1402 print() 1403 1404 # Set this so that the lists won't be printed until the next 1405 # build is done. 1406 bld.env['PRINT_BUILT_MODULES_AT_END'] = False 1407 1408 # Write the build status file. 1409 build_status_file = os.path.join(bld.out_dir, 'build-status.py') 1410 with open(build_status_file, 'w') as out: 1411 out.write('#! /usr/bin/env python3\n') 1412 out.write('\n') 1413 out.write('# Programs that are runnable.\n') 1414 out.write('ns3_runnable_programs = ' + str(env['NS3_RUNNABLE_PROGRAMS']) + '\n') 1415 out.write('\n') 1416 out.write('# Scripts that are runnable.\n') 1417 out.write('ns3_runnable_scripts = ' + str(env['NS3_RUNNABLE_SCRIPTS']) + '\n') 1418 out.write('\n') 1419 1420 if Options.options.lcov_report: 1421 lcov_report(bld) 1422 1423 if Options.options.lcov_zerocounters: 1424 lcov_zerocounters(bld) 1425 1426 if Options.options.run: 1427 wutils.run_program(Options.options.run, env, wutils.get_command_template(env), 1428 visualize=Options.options.visualize) 1429 raise SystemExit(0) 1430 1431 if Options.options.pyrun: 1432 wutils.run_python_program(Options.options.pyrun, env, 1433 visualize=Options.options.visualize) 1434 raise SystemExit(0) 1435 1436 if Options.options.shell: 1437 raise WafError("Please run `./waf shell' now, instead of `./waf --shell'") 1438 1439 if Options.options.check: 1440 raise WafError("Please run `./test.py' now, instead of `./waf --check'") 1441 1442 check_shell(bld) 1443 1444 1445 1446class CheckContext(Context.Context): 1447 """run the equivalent of the old ns-3 unit tests using test.py""" 1448 cmd = 'check' 1449 def execute(self): 1450 # first we execute the build 1451 bld = Context.create_context("build") 1452 bld.options = Options.options # provided for convenience 1453 bld.cmd = "build" 1454 bld.execute() 1455 1456 wutils.bld = bld 1457 wutils.run_python_program("test.py -n -c core", bld.env) 1458 1459def check_shell(bld): 1460 if ('NS3_MODULE_PATH' not in os.environ) or ('NS3_EXECUTABLE_PATH' not in os.environ): 1461 return 1462 env = bld.env 1463 correct_modpath = os.pathsep.join(env['NS3_MODULE_PATH']) 1464 found_modpath = os.environ['NS3_MODULE_PATH'] 1465 correct_execpath = os.pathsep.join(env['NS3_EXECUTABLE_PATH']) 1466 found_execpath = os.environ['NS3_EXECUTABLE_PATH'] 1467 if (found_modpath != correct_modpath) or (correct_execpath != found_execpath): 1468 msg = ("Detected shell (./waf shell) with incorrect configuration\n" 1469 "=========================================================\n" 1470 "Possible reasons for this problem:\n" 1471 " 1. You switched to another ns-3 tree from inside this shell\n" 1472 " 2. You switched ns-3 debug level (waf configure --debug)\n" 1473 " 3. You modified the list of built ns-3 modules\n" 1474 "You should correct this situation before running any program. Possible solutions:\n" 1475 " 1. Exit this shell, and start a new one\n" 1476 " 2. Run a new nested shell") 1477 raise WafError(msg) 1478 1479 1480class Ns3ShellContext(Context.Context): 1481 """run a shell with an environment suitably modified to run locally built programs""" 1482 cmd = 'shell' 1483 1484 def execute(self): 1485 # first we execute the build 1486 bld = Context.create_context("build") 1487 bld.options = Options.options # provided for convenience 1488 bld.cmd = "build" 1489 bld.execute() 1490 1491 # Set this so that the lists won't be printed when the user 1492 # exits the shell. 1493 bld.env['PRINT_BUILT_MODULES_AT_END'] = False 1494 1495 if sys.platform == 'win32': 1496 shell = os.environ.get("COMSPEC", "cmd.exe") 1497 else: 1498 shell = os.environ.get("SHELL", "/bin/sh") 1499 1500 env = bld.env 1501 os_env = { 1502 'NS3_MODULE_PATH': os.pathsep.join(env['NS3_MODULE_PATH']), 1503 'NS3_EXECUTABLE_PATH': os.pathsep.join(env['NS3_EXECUTABLE_PATH']), 1504 } 1505 wutils.run_argv([shell], env, os_env) 1506 1507 1508def _print_introspected_doxygen(bld): 1509 env = wutils.bld.env 1510 proc_env = wutils.get_proc_env() 1511 try: 1512 program_obj = wutils.find_program('print-introspected-doxygen', env) 1513 except ValueError: 1514 Logs.warn("print-introspected-doxygen does not exist") 1515 raise SystemExit(1) 1516 return 1517 1518 prog = program_obj.path.find_or_declare(program_obj.target).get_bld().abspath() 1519 1520 if not os.path.exists(prog): 1521 Logs.error("print-introspected-doxygen has not been built yet." 1522 " You need to build ns-3 at least once before " 1523 "generating doxygen docs...") 1524 raise SystemExit(1) 1525 1526 Logs.info("Running print-introspected-doxygen") 1527 1528 # Create a header file with the introspected information. 1529 with open(os.path.join('doc', 'introspected-doxygen.h'), 'w') as doxygen_out: 1530 if subprocess.Popen([prog], stdout=doxygen_out, env=proc_env).wait(): 1531 raise SystemExit(1) 1532 1533 # Create a text file with the introspected information. 1534 with open(os.path.join('doc', 'ns3-object.txt'), 'w') as text_out: 1535 if subprocess.Popen([prog, '--output-text'], stdout=text_out, env=proc_env).wait(): 1536 raise SystemExit(1) 1537 1538 # Gather the CommandLine doxy 1539 # test.py appears not to create or keep the output directory 1540 # if no real tests are run, so we just stuff all the 1541 # .command-line output files into testpy-output/ 1542 # NS_COMMANDLINE_INTROSPECTION=".." test.py --nowaf --constrain=example 1543 Logs.info("Running CommandLine introspection") 1544 proc_env['NS_COMMANDLINE_INTROSPECTION'] = '..' 1545 subprocess.run(["./test.py", "--nowaf", "--constrain=example"], 1546 env=proc_env, stdout=subprocess.DEVNULL) 1547 1548 doxygen_out = os.path.join('doc', 'introspected-command-line.h') 1549 try: 1550 os.remove(doxygen_out) 1551 except OSError as e: 1552 pass 1553 1554 with open(doxygen_out, 'w') as out_file: 1555 lines=""" 1556/* This file is automatically generated by 1557CommandLine::PrintDoxygenUsage() from the CommandLine configuration 1558in various example programs. Do not edit this file! Edit the 1559CommandLine configuration in those files instead. 1560*/\n 1561""" 1562 out_file.write(lines) 1563 1564 with open(doxygen_out,'a') as outfile: 1565 for in_file in glob.glob('testpy-output/*.command-line'): 1566 with open(in_file,'r') as infile: 1567 outfile.write(infile.read()) 1568 1569def _doxygen(bld, skip_pid=False): 1570 env = wutils.bld.env 1571 proc_env = wutils.get_proc_env() 1572 1573 if not env['DOXYGEN']: 1574 Logs.error("waf configure did not detect doxygen in the system -> cannot build api docs.") 1575 raise SystemExit(1) 1576 return 1577 1578 if not skip_pid: 1579 _print_introspected_doxygen(bld) 1580 1581 _getVersion() 1582 doxygen_config = os.path.join('doc', 'doxygen.conf') 1583 if subprocess.Popen(env['DOXYGEN'] + [doxygen_config]).wait(): 1584 Logs.error("Doxygen build returned an error.") 1585 raise SystemExit(1) 1586 1587def _docset(bld): 1588 # Get the doxygen config 1589 doxyfile = os.path.join('doc', 'doxygen.conf') 1590 Logs.info("docset: reading " + doxyfile) 1591 with open(doxyfile, 'r') as doxygen_config: 1592 doxygen_config_contents = doxygen_config.read() 1593 1594 # Create the output directory 1595 docset_path = os.path.join('doc', 'docset') 1596 Logs.info("docset: checking for output directory " + docset_path) 1597 if not os.path.exists(docset_path): 1598 Logs.info("docset: creating output directory " + docset_path) 1599 os.mkdir(docset_path) 1600 1601 doxyfile = os.path.join('doc', 'doxygen.docset.conf') 1602 with open(doxyfile, 'w') as doxygen_config: 1603 Logs.info("docset: writing doxygen conf " + doxyfile) 1604 doxygen_config.write(doxygen_config_contents) 1605 doxygen_config.write( 1606 """ 1607 HAVE_DOT = NO 1608 GENERATE_DOCSET = YES 1609 DISABLE_INDEX = YES 1610 SEARCHENGINE = NO 1611 GENERATE_TREEVIEW = NO 1612 OUTPUT_DIRECTORY=""" + docset_path + "\n" 1613 ) 1614 1615 # Run Doxygen manually, so as to avoid build 1616 Logs.info("docset: running doxygen") 1617 env = wutils.bld.env 1618 _getVersion() 1619 if subprocess.Popen(env['DOXYGEN'] + [doxyfile]).wait(): 1620 Logs.error("Doxygen docset build returned an error.") 1621 raise SystemExit(1) 1622 1623 # Build docset 1624 docset_path = os.path.join(docset_path, 'html') 1625 Logs.info("docset: Running docset Make") 1626 if subprocess.Popen(["make"], cwd=docset_path).wait(): 1627 Logs.error("Docset make returned and error.") 1628 raise SystemExit(1) 1629 1630 # Additional steps from 1631 # https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/ns-3 1632 docset_out = os.path.join(docset_path, 'org.nsnam.ns3.docset') 1633 icons = os.path.join('doc', 'ns3_html_theme', 'static') 1634 shutil.copy(os.path.join(icons, 'ns-3-bars-16x16.png'), 1635 os.path.join(docset_out, 'icon.png')) 1636 shutil.copy(os.path.join(icons, 'ns-3-bars-32x32.png'), 1637 os.path.join(docset_out, 'icon@x2.png')) 1638 shutil.copy(os.path.join(docset_path, 'Info.plist'), 1639 os.path.join(docset_out, 'Contents')) 1640 shutil.move(docset_out, os.path.join('doc', 'ns-3.docset')) 1641 1642 print("Docset built successfully.") 1643 1644 1645def _getVersion(): 1646 """update the ns3_version.js file, when building documentation""" 1647 1648 prog = "doc/ns3_html_theme/get_version.sh" 1649 if subprocess.Popen([prog]).wait() : 1650 Logs.error(prog + " returned an error") 1651 raise SystemExit(1) 1652 1653class Ns3DoxygenContext(Context.Context): 1654 """do a full build, generate the introspected doxygen and then the doxygen""" 1655 cmd = 'doxygen' 1656 def execute(self): 1657 # first we execute the build 1658 bld = Context.create_context("build") 1659 bld.options = Options.options # provided for convenience 1660 bld.cmd = "build" 1661 bld.execute() 1662 _doxygen(bld) 1663 1664class Ns3SphinxContext(Context.Context): 1665 """build the Sphinx documentation: manual, tutorial, models""" 1666 1667 cmd = 'sphinx' 1668 1669 def sphinx_build(self, path): 1670 print() 1671 print("[waf] Building sphinx docs for " + path) 1672 if subprocess.Popen(["make", "SPHINXOPTS=-N", "-k", 1673 "html", "singlehtml", "latexpdf" ], 1674 cwd=path).wait() : 1675 Logs.error("Sphinx build of " + path + " returned an error.") 1676 raise SystemExit(1) 1677 1678 def execute(self): 1679 _getVersion() 1680 for sphinxdir in ["manual", "models", "tutorial"] : 1681 self.sphinx_build(os.path.join("doc", sphinxdir)) 1682 1683 1684class Ns3DocContext(Context.Context): 1685 """build all the documentation: doxygen, manual, tutorial, models""" 1686 1687 cmd = 'docs' 1688 1689 def execute(self): 1690 steps = ['doxygen', 'sphinx'] 1691 Options.commands = steps + Options.commands 1692 1693 1694def lcov_report(bld): 1695 env = bld.env 1696 1697 if not env['GCOV_ENABLED']: 1698 raise WafError("project not configured for code coverage;" 1699 " reconfigure with --enable-gcov") 1700 try: 1701 subprocess.call(["lcov", "--help"], stdout=subprocess.DEVNULL) 1702 except OSError as e: 1703 if e.errno == os.errno.ENOENT: 1704 raise WafError("Error: lcov program not found") 1705 else: 1706 raise 1707 try: 1708 subprocess.call(["genhtml", "--help"], stdout=subprocess.DEVNULL) 1709 except OSError as e: 1710 if e.errno == os.errno.ENOENT: 1711 raise WafError("Error: genhtml program not found") 1712 else: 1713 raise 1714 os.chdir(out) 1715 try: 1716 lcov_report_dir = 'lcov-report' 1717 create_dir_command = "rm -rf " + lcov_report_dir 1718 create_dir_command += " && mkdir " + lcov_report_dir + ";" 1719 1720 if subprocess.Popen(create_dir_command, shell=True).wait(): 1721 raise SystemExit(1) 1722 1723 info_file = os.path.join(lcov_report_dir, 'report.info') 1724 lcov_command = "lcov -c -d . -o " + info_file 1725 lcov_command += " -b " + os.getcwd() 1726 if subprocess.Popen(lcov_command, shell=True).wait(): 1727 raise SystemExit(1) 1728 1729 genhtml_command = "genhtml -o " + lcov_report_dir 1730 genhtml_command += " " + info_file 1731 if subprocess.Popen(genhtml_command, shell=True).wait(): 1732 raise SystemExit(1) 1733 finally: 1734 os.chdir("..") 1735 1736def lcov_zerocounters(bld): 1737 env = bld.env 1738 1739 if not env['GCOV_ENABLED']: 1740 raise WafError("project not configured for code coverage;" 1741 " reconfigure with --enable-gcov") 1742 try: 1743 subprocess.call(["lcov", "--help"], stdout=subprocess.DEVNULL) 1744 except OSError as e: 1745 if e.errno == os.errno.ENOENT: 1746 raise WafError("Error: lcov program not found") 1747 else: 1748 raise 1749 1750 os.chdir(out) 1751 lcov_clear_command = "lcov -d . --zerocounters" 1752 if subprocess.Popen(lcov_clear_command, shell=True).wait(): 1753 raise SystemExit(1) 1754 os.chdir("..") 1755