wscript
author Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
Thu, 08 Nov 2007 12:39:21 +0100
changeset 1819 c21093326f8d
parent 1764 04f2a1dd7e45
child 1855 ee15fef0cb62
child 2052 b03cc9fe34c6
permissions -rw-r--r--
dox typo
     1 ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
     2 import sys
     3 import shlex
     4 import shutil
     5 import types
     6 import optparse
     7 import os.path
     8 
     9 import Params
    10 import Object
    11 import pproc as subprocess
    12 
    13 Params.g_autoconfig = 1
    14 
    15 # the following two variables are used by the target "waf dist"
    16 VERSION = file("VERSION").read().strip()
    17 APPNAME = 'ns'
    18 
    19 # these variables are mandatory ('/' are converted automatically)
    20 srcdir = '.'
    21 blddir = 'build'
    22 
    23 def dist_hook():
    24     shutil.rmtree("doc/html", True)
    25     shutil.rmtree("doc/latex", True)
    26 
    27 def set_options(opt):
    28 
    29     def debug_option_callback(option, opt, value, parser):
    30         if value == 'debug':
    31             setattr(parser.values, option.dest, 'ultradebug')
    32         elif value == 'optimized':
    33             setattr(parser.values, option.dest, 'optimized')
    34         else:
    35             raise optparse.OptionValueError("allowed --debug-level values"
    36                                             " are debug, optimized.")
    37 
    38     opt.add_option('-d', '--debug-level',
    39                    action='callback',
    40                    type="string", dest='debug_level', default='ultradebug',
    41                    help=('Specify the debug level, does nothing if CFLAGS is set'
    42                          ' in the environment. [Allowed Values: debug, optimized].'
    43                          ' WARNING: this option only has effect '
    44                          'with the configure command.'),
    45                    callback=debug_option_callback)
    46     
    47     # options provided by the modules
    48     opt.tool_options('compiler_cxx')
    49 
    50     opt.add_option('--enable-gcov',
    51                    help=('Enable code coverage analysis.'
    52                          ' WARNING: this option only has effect '
    53                          'with the configure command.'),
    54                    action="store_true", default=False,
    55                    dest='enable_gcov')
    56 
    57     opt.add_option('--lcov-report',
    58                    help=('Generate a code coverage report '
    59                          '(use this option at build time, not in configure)'),
    60                    action="store_true", default=False,
    61                    dest='lcov_report')
    62 
    63     opt.add_option('--doxygen',
    64                    help=('Run doxygen to generate html documentation from source comments'),
    65                    action="store_true", default=False,
    66                    dest='doxygen')
    67 
    68     opt.add_option('--run',
    69                    help=('Run a locally built program; argument can be a program name,'
    70                          ' or a command starting with the program name.'),
    71                    type="string", default='', dest='run')
    72     opt.add_option('--command-template',
    73                    help=('Template of the command used to run the program given by --run;'
    74                          ' It should be a shell command string containing %s inside,'
    75                          ' which will be replaced by the actual program.'),
    76                    type="string", default=None, dest='command_template')
    77 
    78     opt.add_option('--shell',
    79                    help=('Run a shell with an environment suitably modified to run locally built programs'),
    80                    action="store_true", default=False,
    81                    dest='shell')
    82 
    83     # options provided in a script in a subdirectory named "src"
    84     opt.sub_options('src')
    85 
    86 
    87 def configure(conf):
    88     conf.check_tool('compiler_cxx')
    89 
    90     # create the second environment, set the variant and set its name
    91     variant_env = conf.env.copy()
    92     debug_level = Params.g_options.debug_level.lower()
    93     if debug_level == 'ultradebug':
    94         variant_name = 'debug'
    95     else:
    96         variant_name = debug_level
    97 
    98     variant_env['INCLUDEDIR'] = os.path.join(variant_env['PREFIX'], 'include')
    99 
   100     if Params.g_options.enable_gcov:
   101         variant_name += '-gcov'
   102         variant_env.append_value('CCFLAGS', '-fprofile-arcs')
   103         variant_env.append_value('CCFLAGS', '-ftest-coverage')
   104         variant_env.append_value('CXXFLAGS', '-fprofile-arcs')
   105         variant_env.append_value('CXXFLAGS', '-ftest-coverage')
   106         variant_env.append_value('LINKFLAGS', '-fprofile-arcs')
   107     
   108     conf.env['NS3_ACTIVE_VARIANT'] = variant_name
   109     variant_env['NS3_ACTIVE_VARIANT'] = variant_name
   110     variant_env.set_variant(variant_name)
   111     conf.set_env_name(variant_name, variant_env)
   112     conf.setenv(variant_name)
   113 
   114     variant_env.append_value('CXXDEFINES', 'RUN_SELF_TESTS')
   115     
   116     if (os.path.basename(conf.env['CXX']).startswith("g++")
   117         and 'CXXFLAGS' not in os.environ):
   118         variant_env.append_value('CXXFLAGS', ['-Werror'])
   119 
   120     if 'debug' in Params.g_options.debug_level.lower():
   121         variant_env.append_value('CXXDEFINES', 'NS3_ASSERT_ENABLE')
   122         variant_env.append_value('CXXDEFINES', 'NS3_LOG_ENABLE')
   123 
   124     ## In optimized builds we still want debugging symbols, e.g. for
   125     ## profiling, and at least partially usable stack traces.
   126     if ('optimized' in Params.g_options.debug_level.lower() 
   127         and 'CXXFLAGS' not in os.environ):
   128         for flag in variant_env['CXXFLAGS_DEBUG']:
   129             ## this probably doesn't work for MSVC
   130             if flag.startswith('-g'):
   131                 variant_env.append_value('CXXFLAGS', flag)
   132 
   133     ## in optimized builds, replace -O2 with -O3
   134     if 'optimized' in Params.g_options.debug_level.lower():
   135         lst = variant_env['CXXFLAGS']
   136         for i, flag in enumerate(lst):
   137             if flag == '-O2':
   138                 lst[i] = '-O3'
   139 
   140     if sys.platform == 'win32':
   141         if os.path.basename(conf.env['CXX']).startswith("g++"):
   142             variant_env.append_value("LINKFLAGS", "-Wl,--enable-runtime-pseudo-reloc")
   143 
   144     conf.sub_config('src')
   145     conf.sub_config('utils')
   146 
   147 
   148 def create_ns3_program(bld, name, dependencies=('simulator',)):
   149     program = bld.create_obj('cpp', 'program')
   150     program.name = name
   151     program.target = program.name
   152     program.uselib_local = 'ns3'
   153     return program
   154 
   155 
   156 def build(bld):
   157     print "Entering directory `%s/build'" % Params.g_build.m_curdirnode.abspath()
   158     Params.g_cwd_launch = Params.g_build.m_curdirnode.abspath()
   159 
   160     bld.create_ns3_program = types.MethodType(create_ns3_program, bld)
   161 
   162     variant_name = bld.env_of_name('default')['NS3_ACTIVE_VARIANT']
   163     variant_env = bld.env_of_name(variant_name)
   164     bld.m_allenvs['default'] = variant_env # switch to the active variant
   165 
   166     if Params.g_options.shell:
   167         run_shell()
   168         raise SystemExit(0)
   169 
   170     if Params.g_options.doxygen:
   171         doxygen()
   172         raise SystemExit(0)
   173 
   174     check_shell()
   175 
   176     if Params.g_options.doxygen:
   177         doxygen()
   178         raise SystemExit(0)
   179 
   180     # process subfolders from here
   181     bld.add_subdirs('src')
   182     bld.add_subdirs('samples utils examples tutorial')
   183 
   184     ## Create a single ns3 library containing all modules
   185     lib = bld.create_obj('cpp', 'shlib')
   186     lib.name = 'ns3'
   187     lib.target = 'ns3'
   188     lib.add_objects = list(bld.env_of_name('default')['NS3_MODULES'])
   189 
   190 
   191 def shutdown():
   192     #import UnitTest
   193     #ut = UnitTest.unit_test()
   194     #ut.change_to_testfile_dir = True
   195     #ut.want_to_see_test_output = True
   196     #ut.want_to_see_test_error = True
   197     #ut.run()
   198     #ut.print_results()
   199 
   200     if Params.g_commands['check']:
   201         _run_waf_check()
   202 
   203     if Params.g_options.lcov_report:
   204         lcov_report()
   205 
   206     if Params.g_options.run:
   207         run_program(Params.g_options.run, Params.g_options.command_template)
   208         raise SystemExit(0)
   209 
   210     if Params.g_options.command_template:
   211         Params.fatal("Option --command-template requires the option --run to be given")
   212 
   213 def _run_waf_check():
   214     ## generate the trace sources list docs
   215     env = Params.g_build.env_of_name('default')
   216     proc_env = _get_proc_env()
   217     prog = _find_program('print-trace-sources', env).m_linktask.m_outputs[0].abspath(env)
   218     out = open('doc/trace-source-list.h', 'w')
   219     if subprocess.Popen([prog], stdout=out, env=proc_env).wait():
   220         raise SystemExit(1)
   221     out.close()
   222 
   223     run_program('run-tests')
   224 
   225 
   226 def _find_program(program_name, env):
   227     launch_dir = os.path.abspath(Params.g_cwd_launch)
   228     found_programs = []
   229     for obj in Object.g_allobjs:
   230         if obj.m_type != 'program' or not obj.target:
   231             continue
   232 
   233         ## filter out programs not in the subtree starting at the launch dir
   234         if not (obj.path.abspath().startswith(launch_dir)
   235                 or obj.path.abspath(env).startswith(launch_dir)):
   236             continue
   237         
   238         found_programs.append(obj.target)
   239         if obj.target == program_name:
   240             return obj
   241     raise ValueError("program '%s' not found; available programs are: %r"
   242                      % (program_name, found_programs))
   243 
   244 def _get_proc_env(os_env=None):
   245     env = Params.g_build.env_of_name('default')
   246     if sys.platform == 'linux2':
   247         pathvar = 'LD_LIBRARY_PATH'
   248     elif sys.platform == 'darwin':
   249         pathvar = 'DYLD_LIBRARY_PATH'
   250     elif sys.platform == 'win32':
   251         pathvar = 'PATH'
   252     elif sys.platform == 'cygwin':
   253         pathvar = 'PATH'
   254     else:
   255         Params.warning(("Don't know how to configure "
   256                         "dynamic library path for the platform '%s'") % (sys.platform,))
   257         pathvar = None
   258 
   259     proc_env = dict(os.environ)
   260     if os_env is not None:
   261         proc_env.update(os_env)
   262 
   263     if pathvar is not None:
   264         if pathvar in proc_env:
   265             proc_env[pathvar] = os.pathsep.join(list(env['NS3_MODULE_PATH']) + [proc_env[pathvar]])
   266         else:
   267             proc_env[pathvar] = os.pathsep.join(list(env['NS3_MODULE_PATH']))
   268     return proc_env
   269 
   270 def _run_argv(argv, os_env=None):
   271     proc_env = _get_proc_env(os_env)
   272     retval = subprocess.Popen(argv, env=proc_env).wait()
   273     if retval:
   274         Params.fatal("Command %s exited with code %i" % (argv, retval))
   275 
   276 
   277 def run_program(program_string, command_template=None):
   278     """
   279     if command_template is not None, then program_string == program
   280     name and argv is given by command_template with %s replaced by the
   281     full path to the program.  Else, program_string is interpreted as
   282     a shell command with first name being the program name.
   283     """
   284     env = Params.g_build.env_of_name('default')
   285 
   286     if command_template is None:
   287         argv = shlex.split(program_string)
   288         program_name = argv[0]
   289 
   290         try:
   291             program_obj = _find_program(program_name, env)
   292         except ValueError, ex:
   293             Params.fatal(str(ex))
   294 
   295         try:
   296             program_node, = program_obj.m_linktask.m_outputs
   297         except AttributeError:
   298             Params.fatal("%s does not appear to be a program" % (program_name,))
   299 
   300         execvec = [program_node.abspath(env)] + argv[1:]
   301 
   302     else:
   303 
   304         program_name = program_string
   305         try:
   306             program_obj = _find_program(program_name, env)
   307         except ValueError, ex:
   308             Params.fatal(str(ex))
   309         try:
   310             program_node, = program_obj.m_linktask.m_outputs
   311         except AttributeError:
   312             Params.fatal("%s does not appear to be a program" % (program_name,))
   313 
   314         execvec = shlex.split(command_template % (program_node.abspath(env),))
   315 
   316 
   317     former_cwd = os.getcwd()
   318     os.chdir(Params.g_cwd_launch)
   319     try:
   320         retval = _run_argv(execvec)
   321     finally:
   322         os.chdir(former_cwd)
   323 
   324     return retval
   325 
   326 def check_shell():
   327     if 'NS3_MODULE_PATH' not in os.environ:
   328         return
   329     env = Params.g_build.env_of_name('default')
   330     correct_modpath = os.pathsep.join(env['NS3_MODULE_PATH'])
   331     found_modpath = os.environ['NS3_MODULE_PATH']
   332     if found_modpath != correct_modpath:
   333         msg = ("Detected shell (waf --shell) with incorrect configuration\n"
   334                "=========================================================\n"
   335                "Possible reasons for this problem:\n"
   336                "  1. You switched to another ns-3 tree from inside this shell\n"
   337                "  2. You switched ns-3 debug level (waf configure --debug)\n"
   338                "  3. You modified the list of built ns-3 modules\n"
   339                "You should correct this situation before running any program.  Possible solutions:\n"
   340                "  1. Exit this shell, and start a new one\n"
   341                "  2. Run a new nested shell")
   342         Params.fatal(msg)
   343 
   344 
   345 def run_shell():
   346     if sys.platform == 'win32':
   347         shell = os.environ.get("COMSPEC", "cmd.exe")
   348     else:
   349         shell = os.environ.get("SHELL", "/bin/sh")
   350 
   351     env = Params.g_build.env_of_name('default')
   352     _run_argv([shell], {'NS3_MODULE_PATH': os.pathsep.join(env['NS3_MODULE_PATH'])})
   353 
   354 
   355 def doxygen():
   356     if not os.path.exists('doc/trace-source-list.h'):
   357         Params.warning("doc/trace-source-list.h does not exist; run waf check to generate it.")
   358 
   359     ## run doxygen
   360     doxygen_config = os.path.join('doc', 'doxygen.conf')
   361     if subprocess.Popen(['doxygen', doxygen_config]).wait():
   362         raise SystemExit(1)
   363 
   364 def lcov_report():
   365     env = Params.g_build.env_of_name('default')
   366     variant_name = env['NS3_ACTIVE_VARIANT']
   367 
   368     if 'gcov' not in variant_name:
   369         Params.fatal("project not configured for code coverage;"
   370                      " reconfigure with --enable-gcov")
   371 
   372     os.chdir(blddir)
   373     try:
   374         lcov_report_dir = os.path.join(variant_name, 'lcov-report')
   375         create_dir_command = "rm -rf " + lcov_report_dir
   376         create_dir_command += " && mkdir " + lcov_report_dir + ";"
   377 
   378         if subprocess.Popen(create_dir_command, shell=True).wait():
   379             raise SystemExit(1)
   380 
   381         info_file = os.path.join(lcov_report_dir, variant_name + '.info')
   382         lcov_command = "../utils/lcov/lcov -c -d . -o " + info_file
   383         lcov_command += " --source-dirs=" + os.getcwd()
   384         lcov_command += ":" + os.path.join(
   385             os.getcwd(), variant_name, 'include')
   386         if subprocess.Popen(lcov_command, shell=True).wait():
   387             raise SystemExit(1)
   388 
   389         genhtml_command = "../utils/lcov/genhtml -o " + lcov_report_dir
   390         genhtml_command += " " + info_file
   391         if subprocess.Popen(genhtml_command, shell=True).wait():
   392             raise SystemExit(1)
   393     finally:
   394         os.chdir("..")
   395