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