wscript
author Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
Sat, 04 Jul 2009 08:15:48 +0200
changeset 4654 2eaebe77d66b
parent 4594 14ce84fd7ae3
permissions -rw-r--r--
Added tag ns-3.5 for changeset c975274c9707
     1 ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
     2 
     3 # python lib modules
     4 import sys
     5 import shutil
     6 import types
     7 import optparse
     8 import os.path
     9 import re
    10 
    11 # WAF modules
    12 import pproc as subprocess
    13 import Options
    14 
    15 import Logs
    16 import TaskGen
    17 import Constants
    18 
    19 import ccroot
    20 ccroot.USE_TOP_LEVEL = True
    21 
    22 import Task
    23 Task.algotype = Constants.JOBCONTROL # so that Task.maxjobs=1 takes effect
    24 
    25 import Utils
    26 import Build
    27 import Configure
    28 import Scripting
    29 
    30 import cflags # override the build profiles from waf
    31 cflags.profiles = {
    32 	# profile name: [optimization_level, warnings_level, debug_level]
    33 	'debug':     [0, 2, 3],
    34 	'optimized': [3, 2, 1],
    35 	}
    36 cflags.default_profile = 'debug'
    37 
    38 # local modules
    39 import wutils
    40 import regression
    41 
    42 Configure.autoconfig = 1
    43 
    44 # the following two variables are used by the target "waf dist"
    45 VERSION = file("VERSION", "rt").read().strip()
    46 APPNAME = 'ns'
    47 
    48 wutils.VERSION = VERSION
    49 wutils.APPNAME = APPNAME
    50 
    51 #
    52 # The last part of the path name to use to find the regression traces.  The
    53 # path will be APPNAME + '-' + VERSION + REGRESSION_SUFFIX, e.g.,
    54 # ns-3-dev-ref-traces
    55 #
    56 REGRESSION_SUFFIX = "-ref-traces"
    57 
    58 
    59 # these variables are mandatory ('/' are converted automatically)
    60 srcdir = '.'
    61 blddir = 'build'
    62 
    63 def load_env():
    64     bld_cls = getattr(Utils.g_module, 'build_context', Utils.Context)
    65     bld_ctx = bld_cls()
    66     bld_ctx.load_dirs(os.path.abspath(os.path.join (srcdir,'..')),
    67                       os.path.abspath(os.path.join (srcdir,'..', blddir)))
    68     bld_ctx.load_envs()
    69     env = bld_ctx.get_env()
    70     return env
    71 
    72 def get_files(base_dir):
    73     retval = []
    74     reference=os.path.dirname(base_dir)
    75     for root, dirs, files in os.walk(base_dir):
    76         if root.find('.hg') != -1:
    77             continue
    78         for file in files:
    79             if file.find('.hg') != -1:
    80                 continue
    81             fullname = os.path.join(root,file)
    82             # we can't use os.path.relpath because it's new in python 2.6
    83             relname = fullname.replace(reference + '/','')
    84             retval.append([fullname,relname])
    85     return retval
    86 
    87 
    88 def dist_hook():
    89     import tarfile
    90     shutil.rmtree("doc/html", True)
    91     shutil.rmtree("doc/latex", True)
    92     shutil.rmtree("nsc", True)
    93 
    94     ## build the name of the traces subdirectory.  Will be something like
    95     ## ns-3-dev-ref-traces
    96     traces_name = APPNAME + '-' + VERSION + REGRESSION_SUFFIX
    97     ## Create a tar.bz2 file with the traces
    98     env = load_env()
    99     regression_dir = env['REGRESSION_TRACES']
   100     if not os.path.isdir(regression_dir):
   101         Logs.warn("Not creating traces archive: the %s directory does not exist" % regression_dir)
   102     else:
   103         traceball = traces_name + wutils.TRACEBALL_SUFFIX
   104         tar = tarfile.open(os.path.join("..", traceball), 'w:bz2')
   105         files = get_files(regression_dir)
   106         for fullfilename,relfilename in files:
   107             tar.add(fullfilename,arcname=relfilename)
   108         tar.close()
   109 
   110 def set_options(opt):
   111     # options provided by the modules
   112     opt.tool_options('compiler_cxx')
   113     opt.tool_options('cflags')
   114 
   115     opt.add_option('--cwd',
   116                    help=('Set the working directory for a program.'),
   117                    action="store", type="string", default=None,
   118                    dest='cwd_launch')
   119 
   120     opt.add_option('--enable-gcov',
   121                    help=('Enable code coverage analysis.'
   122                          ' WARNING: this option only has effect '
   123                          'with the configure command.'),
   124                    action="store_true", default=False,
   125                    dest='enable_gcov')
   126 
   127     opt.add_option('--no-task-lines',
   128                    help=("Don't print task lines, i.e. messages saying which tasks are being executed by WAF."
   129                          "  Coupled with a single -v will cause WAF to output only the executed commands,"
   130                          " just like 'make' does by default."),
   131                    action="store_true", default=False,
   132                    dest='no_task_lines')
   133 
   134     opt.add_option('--lcov-report',
   135                    help=('Generate a code coverage report '
   136                          '(use this option at build time, not in configure)'),
   137                    action="store_true", default=False,
   138                    dest='lcov_report')
   139 
   140     opt.add_option('--doxygen',
   141                    help=('Run doxygen to generate html documentation from source comments'),
   142                    action="store_true", default=False,
   143                    dest='doxygen')
   144 
   145     opt.add_option('--run',
   146                    help=('Run a locally built program; argument can be a program name,'
   147                          ' or a command starting with the program name.'),
   148                    type="string", default='', dest='run')
   149     opt.add_option('--command-template',
   150                    help=('Template of the command used to run the program given by --run;'
   151                          ' It should be a shell command string containing %s inside,'
   152                          ' which will be replaced by the actual program.'),
   153                    type="string", default=None, dest='command_template')
   154     opt.add_option('--pyrun',
   155                    help=('Run a python program using locally built ns3 python module;'
   156                          ' argument is the path to the python program, optionally followed'
   157                          ' by command-line options that are passed to the program.'),
   158                    type="string", default='', dest='pyrun')
   159     opt.add_option('--valgrind',
   160                    help=('Change the default command template to run programs and unit tests with valgrind'),
   161                    action="store_true", default=False,
   162                    dest='valgrind')
   163     opt.add_option('--shell',
   164                    help=('DEPRECATED (run ./waf shell)'),
   165                    action="store_true", default=False,
   166                    dest='shell')
   167     opt.add_option('--enable-sudo',
   168                    help=('Use sudo to setup suid bits on ns3 executables.'),
   169                    dest='enable_sudo', action='store_true',
   170                    default=False)
   171     opt.add_option('--regression',
   172                    help=("Enable regression testing; only used for the 'check' target"),
   173                    default=False, dest='regression', action="store_true")
   174     opt.add_option('--check',
   175                    help=("Enable unit testing"),
   176                    default=False, dest='check', action="store_true")
   177     opt.add_option('--regression-generate',
   178                    help=("Generate new regression test traces."),
   179                    default=False, dest='regression_generate', action="store_true")
   180     opt.add_option('--regression-tests',
   181                    help=('For regression testing, only run/generate the indicated regression tests, '
   182                          'specified as a comma separated list of test names'),
   183                    dest='regression_tests', type="string")
   184     opt.add_option('--with-regression-traces',
   185                    help=('Path to the regression reference traces directory'),
   186                    default=None,
   187                    dest='regression_traces', type="string")
   188     opt.add_option('--enable-static',
   189                    help=('Compile NS-3 statically: works only on linux, without python'),
   190                    dest='enable_static', action='store_true',
   191                    default=False)
   192 
   193     # options provided in a script in a subdirectory named "src"
   194     opt.sub_options('src')
   195     opt.sub_options('bindings/python')
   196     opt.sub_options('src/internet-stack')
   197 
   198 
   199 def _check_compilation_flag(conf, flag):
   200     """
   201     Checks if the C++ compiler accepts a certain compilation flag or flags
   202     flag: can be a string or a list of strings
   203     """
   204 
   205     env = conf.env.copy()
   206     env.append_value('CXXFLAGS', flag)
   207     try:
   208         retval = conf.run_c_code(code='#include <stdio.h>\nint main() { return 0; }\n',
   209                                  env=env, compile_filename='test.cc',
   210                                  compile_mode='cxx',type='cprogram', execute=False)
   211     except Configure.ConfigurationError:
   212         ok = False
   213     else:
   214         ok = (retval == 0)
   215     conf.check_message_custom(flag, 'support', (ok and 'yes' or 'no'))
   216     return ok
   217 
   218     
   219 def report_optional_feature(conf, name, caption, was_enabled, reason_not_enabled):
   220     conf.env.append_value('NS3_OPTIONAL_FEATURES', (name, caption, was_enabled, reason_not_enabled))
   221 
   222 def configure(conf):
   223     # attach some extra methods
   224     conf.check_compilation_flag = types.MethodType(_check_compilation_flag, conf)
   225     conf.report_optional_feature = types.MethodType(report_optional_feature, conf)
   226     conf.env['NS3_OPTIONAL_FEATURES'] = []
   227 
   228     conf.env['NS3_BUILDDIR'] = conf.blddir
   229     conf.check_tool('compiler_cxx')
   230     conf.check_tool('cflags')
   231     try:
   232         conf.check_tool('pkgconfig')
   233     except Configure.ConfigurationError:
   234         pass
   235     conf.check_tool('command')
   236 
   237     # Check for the location of regression reference traces
   238     if Options.options.regression_traces is not None:
   239         if os.path.isdir(Options.options.regression_traces):
   240             conf.check_message("regression traces location", '', True, ("%s (given)" % Options.options.regression_traces))
   241             conf.env['REGRESSION_TRACES'] = os.path.abspath(Options.options.regression_traces)
   242     else:
   243         traces = os.path.join('..', "%s-%s%s" % (APPNAME, VERSION, REGRESSION_SUFFIX))
   244         if os.path.isdir(traces):
   245             conf.check_message("regression reference traces", '', True, ("%s (guessed)" % traces))
   246             conf.env['REGRESSION_TRACES'] = os.path.abspath(traces)
   247         del traces
   248     if not conf.env['REGRESSION_TRACES']:
   249         conf.check_message("regression reference traces", '', False)
   250 
   251     # create the second environment, set the variant and set its name
   252     variant_env = conf.env.copy()
   253     variant_name = Options.options.build_profile
   254 
   255     if Options.options.enable_gcov:
   256         variant_name += '-gcov'
   257         variant_env.append_value('CCFLAGS', '-fprofile-arcs')
   258         variant_env.append_value('CCFLAGS', '-ftest-coverage')
   259         variant_env.append_value('CXXFLAGS', '-fprofile-arcs')
   260         variant_env.append_value('CXXFLAGS', '-ftest-coverage')
   261         variant_env.append_value('LINKFLAGS', '-fprofile-arcs')
   262     
   263     conf.env['NS3_ACTIVE_VARIANT'] = variant_name
   264     variant_env['NS3_ACTIVE_VARIANT'] = variant_name
   265     variant_env.set_variant(variant_name)
   266     conf.set_env_name(variant_name, variant_env)
   267     conf.setenv(variant_name)
   268     env = variant_env
   269 
   270     env.append_value('CXXDEFINES', 'RUN_SELF_TESTS')
   271     
   272     if env['COMPILER_CXX'] == 'g++' and 'CXXFLAGS' not in os.environ:
   273         if conf.check_compilation_flag('-Wno-error=deprecated-declarations'):
   274             env.append_value('CXXFLAGS', '-Wno-error=deprecated-declarations')
   275         
   276     if Options.options.build_profile == 'debug':
   277         env.append_value('CXXDEFINES', 'NS3_ASSERT_ENABLE')
   278         env.append_value('CXXDEFINES', 'NS3_LOG_ENABLE')
   279 
   280     env['PLATFORM'] = sys.platform
   281 
   282     if conf.env['CXX_NAME'] == 'gcc':
   283         if sys.platform == 'win32':
   284             env.append_value("LINKFLAGS", "-Wl,--enable-runtime-pseudo-reloc")
   285         elif sys.platform == 'cygwin':
   286             env.append_value("LINKFLAGS", "-Wl,--enable-auto-import")
   287 
   288         cxx, = env['CXX']
   289 
   290         p = subprocess.Popen([cxx, '-print-file-name=libstdc++.so'], stdout=subprocess.PIPE)
   291         libstdcxx_location = os.path.dirname(p.stdout.read().strip())
   292         p.wait()
   293         if libstdcxx_location:
   294             conf.env.append_value('NS3_MODULE_PATH', libstdcxx_location)
   295 
   296         if Options.platform in ['linux']:
   297             if conf.check_compilation_flag('-Wl,--soname=foo'):
   298                 env['WL_SONAME_SUPPORTED'] = True
   299 
   300     conf.sub_config('src')
   301     conf.sub_config('bindings/python')
   302 
   303     if Options.options.enable_modules:
   304         conf.env['NS3_ENABLED_MODULES'] = ['ns3-'+mod for mod in
   305                                            Options.options.enable_modules.split(',')]
   306 
   307     # for suid bits
   308     conf.find_program('sudo', var='SUDO')
   309 
   310     why_not_sudo = "because we like it"
   311     if Options.options.enable_sudo and conf.env['SUDO']:
   312         env['ENABLE_SUDO'] = True
   313     else:
   314         env['ENABLE_SUDO'] = False
   315         if Options.options.enable_sudo:
   316             why_not_sudo = "program sudo not found"
   317         else:
   318             why_not_sudo = "option --enable-sudo not selected"
   319 
   320     conf.report_optional_feature("ENABLE_SUDO", "Use sudo to set suid bit", env['ENABLE_SUDO'], why_not_sudo)
   321 
   322     # we cannot pull regression traces without mercurial
   323     conf.find_program('hg', var='MERCURIAL')
   324 
   325     conf.find_program('valgrind', var='VALGRIND')
   326 
   327     env['ENABLE_STATIC_NS3'] = False
   328     if Options.options.enable_static:
   329         if env['PLATFORM'].startswith('linux') and \
   330                 env['CXX_NAME'] == 'gcc':
   331             if re.match('i[3-6]86', os.uname()[4]):
   332                 conf.report_optional_feature("static", "Static build", True, '')
   333                 env['ENABLE_STATIC_NS3'] = True
   334             elif os.uname()[4] == 'x86_64':
   335                 if env['ENABLE_PYTHON_BINDINGS'] and \
   336                         not conf.check_compilation_flag('-mcmodel=large'):
   337                     conf.report_optional_feature("static", "Static build", False,
   338                                                  "Can't enable static builds because " + \
   339                                                      "no -mcmodel=large compiler " \
   340                                                      "option. Try --disable-python or upgrade your " \
   341                                                      "compiler to at least gcc 4.3.x.")
   342                 else:
   343                     conf.report_optional_feature("static", "Static build", True, '')
   344                     env['ENABLE_STATIC_NS3'] = True                    
   345         elif env['CXX_NAME'] == 'gcc' and \
   346                 (env['PLATFORM'].startswith('darwin') or \
   347                      env['PLATFORM'].startswith('cygwin')):
   348                 conf.report_optional_feature("static", "Static build", True, '')
   349                 env['ENABLE_STATIC_NS3'] = True
   350         else:
   351             conf.report_optional_feature("static", "Static build", False,
   352                                          "Unsupported platform")
   353     else:
   354         conf.report_optional_feature("static", "Static build", False,
   355                                      "option --enable-static not selected")
   356 
   357 
   358 
   359     # Write a summary of optional features status
   360     print "---- Summary of optional NS-3 features:"
   361     for (name, caption, was_enabled, reason_not_enabled) in conf.env['NS3_OPTIONAL_FEATURES']:
   362         if was_enabled:
   363             status = 'enabled'
   364         else:
   365             status = 'not enabled (%s)' % reason_not_enabled
   366         print "%-30s: %s" % (caption, status)
   367 
   368 
   369 class SuidBuildTask(Task.TaskBase):
   370     """task that makes a binary Suid
   371     """
   372     after = 'cxx_link cc_link'
   373     maxjobs = 1
   374     def __init__(self, bld, program):
   375         self.bld = bld
   376         self.m_display = 'build-suid'
   377         self.__program = program
   378         self.__env = bld.env.copy ()
   379         super(SuidBuildTask, self).__init__(generator=self)
   380         try:
   381             program_obj = wutils.find_program(self.__program.target, self.__env)
   382         except ValueError, ex:
   383             raise Utils.WafError(str(ex))
   384         program_node = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj))
   385         self.filename = program_node.abspath(self.__env)
   386 
   387 
   388     def run(self):
   389         print >> sys.stderr, 'setting suid bit on executable ' + self.filename
   390         if subprocess.Popen(['sudo', 'chown', 'root', self.filename]).wait():
   391             return 1
   392         if subprocess.Popen(['sudo', 'chmod', 'u+s', self.filename]).wait():
   393             return 1
   394         return 0
   395 
   396     def runnable_status(self):
   397         "RUN_ME SKIP_ME or ASK_LATER"
   398         st = os.stat(self.filename)
   399         if st.st_uid == 0:
   400             return Constants.SKIP_ME
   401         else:
   402             return Constants.RUN_ME
   403 
   404 
   405 def create_suid_program(bld, name):
   406     program = bld.new_task_gen('cxx', 'program')
   407     program.is_ns3_program = True
   408     program.module_deps = list()
   409     program.name = name
   410     program.target = name
   411 
   412     if bld.env['ENABLE_SUDO']:
   413         SuidBuildTask(bld, program)
   414 
   415     return program
   416 
   417 def create_ns3_program(bld, name, dependencies=('simulator',)):
   418     program = bld.new_task_gen('cxx', 'program')
   419     program.is_ns3_program = True
   420     program.name = name
   421     program.target = program.name
   422     program.uselib_local = 'ns3'
   423     program.ns3_module_dependencies = ['ns3-'+dep for dep in dependencies]
   424     if program.env['ENABLE_STATIC_NS3']:
   425         if sys.platform == 'darwin':
   426             program.env.append_value('LINKFLAGS', '-Wl,-all_load')
   427             program.env.append_value('LINKFLAGS', '-lns3')
   428         else:
   429             program.env.append_value('LINKFLAGS', '-Wl,--whole-archive,-Bstatic')
   430             program.env.append_value('LINKFLAGS', '-lns3')
   431             program.env.append_value('LINKFLAGS', '-Wl,-Bdynamic,--no-whole-archive')
   432     return program
   433 
   434 def add_scratch_programs(bld):
   435     all_modules = [mod[len("ns3-"):] for mod in bld.env['NS3_MODULES']]
   436     for filename in os.listdir("scratch"):
   437         if filename.startswith('.') or filename == 'CVS':
   438 	    continue
   439         if os.path.isdir(os.path.join("scratch", filename)):
   440             obj = bld.create_ns3_program(filename, all_modules)
   441             obj.path = obj.path.find_dir('scratch').find_dir(filename)
   442             obj.find_sources_in_dirs('.')
   443             obj.target = filename
   444             obj.name = obj.target
   445         elif filename.endswith(".cc"):
   446             name = filename[:-len(".cc")]
   447             obj = bld.create_ns3_program(name, all_modules)
   448             obj.path = obj.path.find_dir('scratch')
   449             obj.source = filename
   450             obj.target = name
   451             obj.name = obj.target
   452 
   453 
   454 def build(bld):
   455     wutils.bld = bld
   456     if Options.options.no_task_lines:
   457         import Runner
   458         def null_printout(s):
   459             pass
   460         Runner.printout = null_printout
   461 
   462     Options.cwd_launch = bld.path.abspath()
   463     bld.create_ns3_program = types.MethodType(create_ns3_program, bld)
   464     bld.create_suid_program = types.MethodType(create_suid_program, bld)
   465 
   466     # switch default variant to the one matching our debug level
   467     variant_name = bld.env_of_name('default')['NS3_ACTIVE_VARIANT']
   468     variant_env = bld.env_of_name(variant_name)
   469     bld.all_envs['default'] = variant_env
   470 
   471     # process subfolders from here
   472     bld.add_subdirs('src')
   473     bld.add_subdirs('samples utils examples')
   474 
   475     add_scratch_programs(bld)
   476 
   477     ## if --enabled-modules option was given, we disable building the
   478     ## modules that were not enabled, and programs that depend on
   479     ## disabled modules.
   480     env = bld.env
   481 
   482     if Options.options.enable_modules:
   483         Logs.warn("the option --enable-modules is being applied to this build only;"
   484                        " to make it permanent it needs to be given to waf configure.")
   485         env['NS3_ENABLED_MODULES'] = ['ns3-'+mod for mod in
   486                                       Options.options.enable_modules.split(',')]
   487 
   488     if env['NS3_ENABLED_MODULES']:
   489         modules = env['NS3_ENABLED_MODULES']
   490         changed = True
   491         while changed:
   492             changed = False
   493             for module in modules:
   494                 module_obj = Object.name_to_obj(module)
   495                 if module_obj is None:
   496                     raise ValueError("module %s not found" % module)
   497                 for dep in module_obj.add_objects:
   498                     if not dep.startswith('ns3-'):
   499                         continue
   500                     if dep not in modules:
   501                         modules.append(dep)
   502                         changed = True
   503 
   504         ## remove objects that depend on modules not listed
   505         for obj in list(bld.all_task_gen):
   506             if hasattr(obj, 'ns3_module_dependencies'):
   507                 for dep in obj.ns3_module_dependencies:
   508                     if dep not in modules:
   509                         bld.all_task_gen.remove(obj)
   510                         break
   511             if obj.name in env['NS3_MODULES'] and obj.name not in modules:
   512                 bld.all_task_gen.remove(obj)
   513 
   514     ## Create a single ns3 library containing all enabled modules
   515     if env['ENABLE_STATIC_NS3']:
   516         lib = bld.new_task_gen('cxx', 'staticlib')
   517         lib.name = 'ns3'
   518         lib.target = 'ns3'
   519     else:
   520         lib = bld.new_task_gen('cxx', 'shlib')
   521         lib.name = 'ns3'
   522         lib.target = 'ns3'
   523         if lib.env['CXX_NAME'] == 'gcc' and env['WL_SONAME_SUPPORTED']:
   524             lib.env.append_value('LINKFLAGS', '-Wl,--soname=%s' % ccroot.get_target_name(lib))
   525         if sys.platform == 'cygwin':
   526             lib.features.append('implib') # workaround for WAF bug #472
   527 
   528     if env['NS3_ENABLED_MODULES']:
   529         lib.add_objects = list(modules)
   530         env['NS3_ENABLED_MODULES'] = list(modules)
   531         lib.uselib_local = list(modules)
   532     else:
   533         lib.add_objects = list(env['NS3_MODULES'])
   534         lib.uselib_local = list(env['NS3_MODULES'])
   535 
   536     bld.add_subdirs('bindings/python')
   537 
   538     if Options.options.run:
   539         # Check that the requested program name is valid
   540         program_name, dummy_program_argv = wutils.get_run_program(Options.options.run, wutils.get_command_template(env))
   541 
   542         # When --run'ing a program, tell WAF to only build that program,
   543         # nothing more; this greatly speeds up compilation when all you
   544         # want to do is run a test program.
   545         if not Options.options.compile_targets:
   546             Options.options.compile_targets = os.path.basename(program_name)
   547 
   548     if Options.options.regression or Options.options.regression_generate:
   549         regression_traces = env['REGRESSION_TRACES']
   550         if not regression_traces:
   551             raise Utils.WafError("Cannot run regression tests: reference traces directory not given"
   552                                  " (--with-regression-traces configure option)")
   553         regression.run_regression(bld, regression_traces)
   554 
   555     if Options.options.check:
   556         _run_check(bld)
   557 
   558 
   559 def shutdown(ctx):
   560     bld = wutils.bld
   561     if wutils.bld is None:
   562         return
   563     env = bld.env
   564 
   565     #if Options.commands['check']:
   566     #    _run_waf_check()
   567 
   568     if Options.options.lcov_report:
   569         lcov_report()
   570 
   571     if Options.options.run:
   572         wutils.run_program(Options.options.run, env, wutils.get_command_template(env))
   573         raise SystemExit(0)
   574 
   575     if Options.options.pyrun:
   576         wutils.run_python_program(Options.options.pyrun, env)
   577         raise SystemExit(0)
   578 
   579     if Options.options.shell:
   580         raise Utils.WafError("Run `./waf shell' now, instead of `./waf shell'")
   581 
   582     if Options.options.doxygen:
   583         doxygen()
   584         raise SystemExit(0)
   585 
   586     check_shell(bld)
   587 
   588     if Options.options.doxygen:
   589         doxygen()
   590         raise SystemExit(0)
   591 
   592 
   593 check_context = Build.BuildContext
   594 def check(bld):
   595     """run the NS-3 unit tests (deprecated in favour of --check option)"""
   596     raise Utils.WafError("Please run `./waf --check' instead.")
   597 
   598 
   599 class print_introspected_doxygen_task(Task.TaskBase):
   600     after = 'cc cxx cc_link cxx_link'
   601     color = 'BLUE'
   602 
   603     def __init__(self, bld):
   604         self.bld = bld
   605         super(print_introspected_doxygen_task, self).__init__(generator=self)
   606         
   607     def __str__(self):
   608         return 'print-introspected-doxygen\n'
   609 
   610     def runnable_status(self):
   611         return Task.RUN_ME
   612 
   613     def run(self):
   614         ## generate the trace sources list docs
   615         env = wutils.bld.env
   616         proc_env = wutils.get_proc_env()
   617         try:
   618             program_obj = wutils.find_program('print-introspected-doxygen', env)
   619         except ValueError: # could happen if print-introspected-doxygen is
   620                            # not built because of waf configure
   621                            # --enable-modules=xxx
   622             pass
   623         else:
   624             prog = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj)).abspath(env)
   625             out = open(os.path.join('..', 'doc', 'introspected-doxygen.h'), 'w')
   626             if subprocess.Popen([prog], stdout=out, env=proc_env).wait():
   627                 raise SystemExit(1)
   628             out.close()
   629 
   630 class run_python_unit_tests_task(Task.TaskBase):
   631     after = 'cc cxx cc_link cxx_link'
   632     color = 'BLUE'
   633 
   634     def __init__(self, bld):
   635         self.bld = bld
   636         super(run_python_unit_tests_task, self).__init__(generator=self)
   637         
   638     def __str__(self):
   639         return 'run-python-unit-tests\n'
   640 
   641     def runnable_status(self):
   642         return Task.RUN_ME
   643 
   644     def run(self):
   645         proc_env = wutils.get_proc_env()
   646         wutils.run_argv([self.bld.env['PYTHON'], os.path.join("..", "utils", "python-unit-tests.py")],
   647                         self.bld.env, proc_env, force_no_valgrind=True)
   648 
   649 
   650 class run_a_unit_test_task(Task.TaskBase):
   651     after = 'cc cxx cc_link cxx_link'
   652     color = 'BLUE'
   653 
   654     def __init__(self, bld, name_of_test):
   655         self.bld = bld
   656         super(run_a_unit_test_task, self).__init__(generator=self)
   657         self.name_of_test = name_of_test
   658         try:
   659             program_obj = wutils.find_program("run-tests", self.bld.env)
   660         except ValueError, ex:
   661             raise Utils.WafError(str(ex))
   662         program_node = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj))
   663         self.program_path = program_node.abspath(self.bld.env)
   664 
   665     def __str__(self):
   666         return 'run-unit-test(%s)\n' % self.name_of_test
   667 
   668     def runnable_status(self):
   669         return Task.RUN_ME
   670 
   671     def run(self):
   672         #print repr([self.program_path, self.name_of_test])
   673         try:
   674             self.retval = wutils.run_argv([self.program_path, self.name_of_test], self.bld.env)
   675         except Utils.WafError:
   676             self.retval = 1
   677         #print "running test %s: exit with %i" % (self.name_of_test, retval)
   678         return 0
   679 
   680 class get_list_of_unit_tests_task(Task.TaskBase):
   681     after = 'cc cxx cc_link cxx_link'
   682     color = 'BLUE'
   683 
   684     def __init__(self, bld):
   685         self.bld = bld
   686         super(get_list_of_unit_tests_task, self).__init__(generator=self)
   687         self.tests = []
   688 
   689     def __str__(self):
   690         return 'get-unit-tests-list\n'
   691 
   692     def runnable_status(self):
   693         return Task.RUN_ME
   694 
   695     def run(self):
   696         try:
   697             program_obj = wutils.find_program("run-tests", self.bld.env)
   698         except ValueError, ex:
   699             raise Utils.WafError(str(ex))
   700         program_node = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj))
   701         program_path = program_node.abspath(self.bld.env)
   702         proc = subprocess.Popen([program_path, "--ListTests"], stdout=subprocess.PIPE,
   703                                 env=wutils.get_proc_env())
   704         self.tests = [l.rstrip() for l in proc.stdout.readlines()]
   705         retval = proc.wait()
   706         if retval:
   707             return retval
   708         test_tasks = []
   709         for name_of_test in self.tests:
   710             test_tasks.append(run_a_unit_test_task(self.bld, name_of_test))
   711         collector = collect_unit_test_results_task(self.bld, list(test_tasks))
   712         collector.run_after = list(test_tasks)
   713         self.more_tasks = [collector] + test_tasks
   714         
   715 
   716 class collect_unit_test_results_task(Task.TaskBase):
   717     after = 'run_a_unit_test_task'
   718     color = 'BLUE'
   719 
   720     def __init__(self, bld, test_tasks):
   721         self.bld = bld
   722         super(collect_unit_test_results_task, self).__init__(generator=self)
   723         self.test_tasks = test_tasks
   724 
   725     def __str__(self):
   726         return 'collect-unit-tests-results\n'
   727 
   728     def runnable_status(self):
   729         for t in self.run_after:
   730             if not t.hasrun:
   731                 return Task.ASK_LATER
   732         return Task.RUN_ME
   733 
   734     def run(self):
   735         failed_tasks = []
   736         for task in self.test_tasks:
   737             if task.retval:
   738                 failed_tasks.append(task)
   739         if failed_tasks:
   740             print "C++ UNIT TESTS: %i tests passed, %i failed (%s)." % \
   741                 (len(self.test_tasks) - len(failed_tasks), len(failed_tasks),
   742                  ', '.join(t.name_of_test for t in failed_tasks))
   743             return 1
   744         else:
   745             print "C++ UNIT TESTS: all %i tests passed." % (len(self.test_tasks),)
   746             return 0
   747 
   748 
   749 def _run_check(bld):
   750     task = get_list_of_unit_tests_task(bld)
   751     print_introspected_doxygen_task(bld)
   752     if bld.env['ENABLE_PYTHON_BINDINGS']:
   753         run_python_unit_tests_task(bld)
   754 
   755 
   756 def check_shell(bld):
   757     if 'NS3_MODULE_PATH' not in os.environ:
   758         return
   759     env = bld.env
   760     correct_modpath = os.pathsep.join(env['NS3_MODULE_PATH'])
   761     found_modpath = os.environ['NS3_MODULE_PATH']
   762     if found_modpath != correct_modpath:
   763         msg = ("Detected shell (./waf shell) with incorrect configuration\n"
   764                "=========================================================\n"
   765                "Possible reasons for this problem:\n"
   766                "  1. You switched to another ns-3 tree from inside this shell\n"
   767                "  2. You switched ns-3 debug level (waf configure --debug)\n"
   768                "  3. You modified the list of built ns-3 modules\n"
   769                "You should correct this situation before running any program.  Possible solutions:\n"
   770                "  1. Exit this shell, and start a new one\n"
   771                "  2. Run a new nested shell")
   772         raise Utils.WafError(msg)
   773 
   774 
   775 shell_context = Build.BuildContext
   776 def shell(ctx):
   777     """run a shell with an environment suitably modified to run locally built programs"""
   778 
   779     #make sure we build first"
   780     Scripting.build(ctx)
   781 
   782     if sys.platform == 'win32':
   783         shell = os.environ.get("COMSPEC", "cmd.exe")
   784     else:
   785         shell = os.environ.get("SHELL", "/bin/sh")
   786 
   787     env = wutils.bld.env
   788     wutils.run_argv([shell], env, {'NS3_MODULE_PATH': os.pathsep.join(env['NS3_MODULE_PATH'])})
   789 
   790 def doxygen():
   791     if not os.path.exists('doc/introspected-doxygen.h'):
   792         Logs.warn("doc/introspected-doxygen.h does not exist; run waf check to generate it.")
   793 
   794     ## run doxygen
   795     doxygen_config = os.path.join('doc', 'doxygen.conf')
   796     if subprocess.Popen(['doxygen', doxygen_config]).wait():
   797         raise SystemExit(1)
   798 
   799 def lcov_report():
   800     env = Build.bld.env
   801     variant_name = env['NS3_ACTIVE_VARIANT']
   802 
   803     if 'gcov' not in variant_name:
   804         raise Utils.WafError("project not configured for code coverage;"
   805                      " reconfigure with --enable-gcov")
   806 
   807     os.chdir(blddir)
   808     try:
   809         lcov_report_dir = os.path.join(variant_name, 'lcov-report')
   810         create_dir_command = "rm -rf " + lcov_report_dir
   811         create_dir_command += " && mkdir " + lcov_report_dir + ";"
   812 
   813         if subprocess.Popen(create_dir_command, shell=True).wait():
   814             raise SystemExit(1)
   815 
   816         info_file = os.path.join(lcov_report_dir, variant_name + '.info')
   817         lcov_command = "../utils/lcov/lcov -c -d . -o " + info_file
   818         lcov_command += " --source-dirs=" + os.getcwd()
   819         lcov_command += ":" + os.path.join(
   820             os.getcwd(), variant_name, 'include')
   821         if subprocess.Popen(lcov_command, shell=True).wait():
   822             raise SystemExit(1)
   823 
   824         genhtml_command = "../utils/lcov/genhtml -o " + lcov_report_dir
   825         genhtml_command += " " + info_file
   826         if subprocess.Popen(genhtml_command, shell=True).wait():
   827             raise SystemExit(1)
   828     finally:
   829         os.chdir("..")
   830 
   831 
   832 
   833 
   834 ##
   835 ## The default WAF DistDir implementation is rather slow, because it
   836 ## first copies everything and only later removes unwanted files and
   837 ## directories; this means that it needless copies the full build dir
   838 ## and the .hg repository tree.  Here we provide a replacement DistDir
   839 ## implementation that is more efficient.
   840 ##
   841 import Scripting
   842 from Scripting import dist_exts, excludes, BLDDIR
   843 import Utils
   844 import os
   845 
   846 def _copytree(src, dst, symlinks=False, excludes=(), build_dir=None):
   847     """Recursively copy a directory tree using copy2().
   848 
   849     The destination directory must not already exist.
   850     If exception(s) occur, an Error is raised with a list of reasons.
   851 
   852     If the optional symlinks flag is true, symbolic links in the
   853     source tree result in symbolic links in the destination tree; if
   854     it is false, the contents of the files pointed to by symbolic
   855     links are copied.
   856 
   857     XXX Consider this example code rather than the ultimate tool.
   858 
   859     Note: this is a modified version of shutil.copytree from python
   860     2.5.2 library; modified for WAF purposes to exclude dot dirs and
   861     another list of files.
   862     """
   863     names = os.listdir(src)
   864     os.makedirs(dst)
   865     errors = []
   866     for name in names:
   867         srcname = os.path.join(src, name)
   868         dstname = os.path.join(dst, name)
   869         try:
   870             if symlinks and os.path.islink(srcname):
   871                 linkto = os.readlink(srcname)
   872                 os.symlink(linkto, dstname)
   873             elif os.path.isdir(srcname):
   874                 if name in excludes:
   875                     continue
   876                 elif name.startswith('.') or name.startswith(',,') or name.startswith('++') or name.startswith('CVS'):
   877                     continue
   878                 elif name == build_dir:
   879                     continue
   880                 else:
   881                     ## build_dir is not passed into the recursive
   882                     ## copytree, but that is intentional; it is a
   883                     ## directory name valid only at the top level.
   884                     copytree(srcname, dstname, symlinks, excludes)
   885             else:
   886                 ends = name.endswith
   887                 to_remove = False
   888                 if name.startswith('.') or name.startswith('++'):
   889                     to_remove = True
   890                 else:
   891                     for x in dist_exts:
   892                         if ends(x):
   893                             to_remove = True
   894                             break
   895                 if not to_remove:
   896                     shutil.copy2(srcname, dstname)
   897             # XXX What about devices, sockets etc.?
   898         except (IOError, os.error), why:
   899             errors.append((srcname, dstname, str(why)))
   900         # catch the Error from the recursive copytree so that we can
   901         # continue with other files
   902         except shutil.Error, err:
   903             errors.extend(err.args[0])
   904     try:
   905         shutil.copystat(src, dst)
   906     except WindowsError:
   907         # can't copy file access times on Windows
   908         pass
   909     except OSError, why:
   910         errors.extend((src, dst, str(why)))
   911     if errors:
   912         raise shutil.Error, errors
   913 
   914 
   915 def DistDir(appname, version):
   916     #"make a distribution directory with all the sources in it"
   917     import shutil
   918 
   919     # Our temporary folder where to put our files
   920     TMPFOLDER=appname+'-'+version
   921 
   922     # Remove an old package directory
   923     if os.path.exists(TMPFOLDER): shutil.rmtree(TMPFOLDER)
   924 
   925     global g_dist_exts, g_excludes
   926 
   927     # Remove the Build dir
   928     build_dir = getattr(Utils.g_module, BLDDIR, None)
   929 
   930     # Copy everything into the new folder
   931     _copytree('.', TMPFOLDER, excludes=excludes, build_dir=build_dir)
   932 
   933     # TODO undocumented hook
   934     dist_hook = getattr(Utils.g_module, 'dist_hook', None)
   935     if dist_hook:
   936         os.chdir(TMPFOLDER)
   937         try:
   938             dist_hook()
   939         finally:
   940             # go back to the root directory
   941             os.chdir('..')
   942     return TMPFOLDER
   943 
   944 Scripting.DistDir = DistDir
   945 
   946