bindings/python/wscript
author Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
Sat, 04 Jul 2009 08:15:48 +0200
changeset 4654 2eaebe77d66b
parent 4459 d65af64db144
permissions -rw-r--r--
Added tag ns-3.5 for changeset c975274c9707
gjc@3408
     1
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
gjc@3408
     2
gjc@3408
     3
import re
gjc@3408
     4
import os
gjc@3408
     5
import pproc as subprocess
gjc@3408
     6
import shutil
gjc@3619
     7
import sys
gjc@3408
     8
gjc@4064
     9
import Task
gjc@4064
    10
import Options
gjc@4064
    11
import Configure
gjc@4064
    12
import TaskGen
gjc@4064
    13
import Logs
gjc@4064
    14
import Build
gjc@4066
    15
import Utils
gjc@4064
    16
gjc@3408
    17
## https://launchpad.net/pybindgen/
gjc@4459
    18
REQUIRED_PYBINDGEN_VERSION = (0, 10, 0, 640)
gjc@3408
    19
REQUIRED_PYGCCXML_VERSION = (0, 9, 5)
gjc@3408
    20
gjc@3408
    21
gjc@3873
    22
def add_to_python_path(path):
gjc@3873
    23
    if os.environ.get('PYTHONPATH', ''):
gjc@3873
    24
        os.environ['PYTHONPATH'] = path + os.pathsep + os.environ.get('PYTHONPATH')
gjc@3873
    25
    else:
gjc@3873
    26
        os.environ['PYTHONPATH'] = path
gjc@3873
    27
gjc@3873
    28
def set_pybindgen_pythonpath(env):
gjc@3873
    29
    if env['WITH_PYBINDGEN']:
gjc@3873
    30
        add_to_python_path(env['WITH_PYBINDGEN'])
gjc@3873
    31
gjc@3873
    32
gjc@3408
    33
def set_options(opt):
gjc@3408
    34
    opt.tool_options('python')
gjc@3637
    35
    opt.add_option('--disable-python',
gjc@3408
    36
                   help=("Don't build Python bindings."),
gjc@3408
    37
                   action="store_true", default=False,
gjc@3408
    38
                   dest='python_disable')
gjc@3408
    39
    opt.add_option('--python-scan',
gjc@3408
    40
                   help=("Rescan Python bindings.  Needs working GCCXML / pygccxml environment."),
gjc@3408
    41
                   action="store_true", default=False,
gjc@3408
    42
                   dest='python_scan')
gjc@3873
    43
    opt.add_option('--with-pybindgen',
gjc@3873
    44
                   help=('Path to an existing pybindgen source tree to use.'),
gjc@3873
    45
                   default=None,
gjc@3873
    46
                   dest='with_pybindgen', type="string")
gjc@3408
    47
gjc@3408
    48
gjc@3408
    49
def configure(conf):
gjc@3408
    50
    conf.env['ENABLE_PYTHON_BINDINGS'] = False
gjc@4064
    51
    if Options.options.python_disable:
gjc@3625
    52
        conf.report_optional_feature("python", "Python Bindings", False,
gjc@3625
    53
                                     "disabled by user request")
gjc@3408
    54
        return
gjc@3408
    55
gjc@3408
    56
    conf.check_tool('misc')
gjc@3408
    57
gjc@3619
    58
    if sys.platform == 'cygwin':
gjc@3625
    59
        conf.report_optional_feature("python", "Python Bindings", False,
gjc@3625
    60
                                     "unsupported platform 'cygwin'")
gjc@4064
    61
        Logs.warn("Python is not supported in CygWin environment.  Try MingW instead.")
gjc@3619
    62
        return
gjc@3619
    63
gjc@3408
    64
    ## Check for Python
gjc@3408
    65
    try:
gjc@3408
    66
        conf.check_tool('python')
gjc@3473
    67
        conf.check_python_version((2,3))
gjc@3408
    68
        conf.check_python_headers()
gjc@3625
    69
    except Configure.ConfigurationError, ex:
gjc@3625
    70
        conf.report_optional_feature("python", "Python Bindings", False, str(ex))
gjc@3408
    71
        return
gjc@3408
    72
gjc@4253
    73
    # -fvisibility=hidden optimization
gjc@4430
    74
    if (conf.env['CXX_NAME'] == 'gcc' and [int(x) for x in conf.env['CC_VERSION']] >= [4,0,0]
gjc@4430
    75
        and conf.check_compilation_flag('-fvisibility=hidden')):
gjc@4253
    76
        conf.env.append_value('CXXFLAGS_PYEXT', '-fvisibility=hidden')
gjc@4253
    77
        conf.env.append_value('CCFLAGS_PYEXT', '-fvisibility=hidden')
gjc@4253
    78
gjc@4116
    79
    # Check for the location of pybindgen
gjc@4116
    80
    if Options.options.with_pybindgen is not None:
gjc@4116
    81
        if os.path.isdir(Options.options.with_pybindgen):
gjc@4116
    82
            conf.check_message("pybindgen location", '', True, ("%s (given)" % Options.options.with_pybindgen))
gjc@4116
    83
            conf.env['WITH_PYBINDGEN'] = os.path.abspath(Options.options.with_pybindgen)
gjc@4116
    84
    else:
gjc@4116
    85
        pybindgen_dir = os.path.join('..', "pybindgen")
gjc@4116
    86
        if os.path.isdir(pybindgen_dir):
gjc@4116
    87
            conf.check_message("pybindgen location", '', True, ("%s (guessed)" % pybindgen_dir))
gjc@4116
    88
            conf.env['WITH_PYBINDGEN'] = os.path.abspath(pybindgen_dir)
gjc@4116
    89
        del pybindgen_dir
gjc@4116
    90
    if not conf.env['WITH_PYBINDGEN']:
gjc@4116
    91
        conf.check_message("pybindgen location", '', False)
gjc@3873
    92
gjc@4116
    93
    # Check for pybindgen
gjc@3873
    94
gjc@3873
    95
    set_pybindgen_pythonpath(conf.env)
gjc@3873
    96
gjc@3408
    97
    try:
gjc@3408
    98
        conf.check_python_module('pybindgen')
gjc@3408
    99
    except Configure.ConfigurationError:
gjc@4077
   100
        Logs.warn("pybindgen missing => no python bindings")
gjc@4077
   101
        conf.report_optional_feature("python", "Python Bindings", False,
gjc@4077
   102
                                     "PyBindGen missing")
gjc@4082
   103
        return
gjc@3408
   104
    else:
gjc@3408
   105
        out = subprocess.Popen([conf.env['PYTHON'], "-c",
gjc@3408
   106
                                "import pybindgen.version; "
gjc@3408
   107
                                "print '.'.join([str(x) for x in pybindgen.version.__version__])"],
gjc@3408
   108
                                stdout=subprocess.PIPE).communicate()[0]
gjc@3408
   109
        pybindgen_version_str = out.strip()
gjc@3408
   110
        pybindgen_version = tuple([int(x) for x in pybindgen_version_str.split('.')])
gjc@3408
   111
        conf.check_message('pybindgen', 'version',
mathieu@4457
   112
                           (pybindgen_version == REQUIRED_PYBINDGEN_VERSION),
gjc@3408
   113
                           pybindgen_version_str)
mathieu@4457
   114
        if not (pybindgen_version == REQUIRED_PYBINDGEN_VERSION):
mathieu@4457
   115
            Logs.warn("pybindgen (found %s), (need %s)" %
gjc@3408
   116
                    (pybindgen_version_str,
gjc@3408
   117
                     '.'.join([str(x) for x in REQUIRED_PYBINDGEN_VERSION])))
gjc@4116
   118
            conf.report_optional_feature("python", "Python Bindings", False,
mathieu@4457
   119
                                         "PyBindGen version not correct and newer version could not be retrieved")
gjc@4116
   120
            return
gjc@3408
   121
gjc@3408
   122
    ## If all has gone well, we finally enable the Python bindings
gjc@3408
   123
    conf.env['ENABLE_PYTHON_BINDINGS'] = True
gjc@3625
   124
    conf.report_optional_feature("python", "Python Bindings", True, None)
gjc@3408
   125
gjc@3408
   126
    ## Check for pygccxml
gjc@3408
   127
    try:
gjc@3408
   128
        conf.check_python_module('pygccxml')
gjc@3408
   129
    except Configure.ConfigurationError:
gjc@3625
   130
        conf.report_optional_feature("pygccxml", "Python API Scanning Support", False,
gjc@3625
   131
                                     "Missing 'pygccxml' Python module")
gjc@3408
   132
        return
gjc@3408
   133
gjc@3408
   134
    out = subprocess.Popen([conf.env['PYTHON'], "-c",
gjc@3408
   135
                            "import pygccxml; print pygccxml.__version__"],
gjc@3408
   136
                            stdout=subprocess.PIPE).communicate()[0]
gjc@3408
   137
    pygccxml_version_str = out.strip()
gjc@3408
   138
    pygccxml_version = tuple([int(x) for x in pygccxml_version_str.split('.')])
gjc@3408
   139
    conf.check_message('pygccxml', 'version',
gjc@3408
   140
                       (pygccxml_version >= REQUIRED_PYGCCXML_VERSION),
gjc@3408
   141
                       pygccxml_version_str)
gjc@3408
   142
    if not (pygccxml_version >= REQUIRED_PYGCCXML_VERSION):
gjc@4067
   143
        Logs.warn("pygccxml (found %s) is too old (need %s) => "
gjc@3408
   144
                "automatic scanning of API definitions will not be possible" %
gjc@3408
   145
                (pygccxml_version_str,
gjc@3408
   146
                 '.'.join([str(x) for x in REQUIRED_PYGCCXML_VERSION])))
gjc@3625
   147
        conf.report_optional_feature("pygccxml", "Python API Scanning Support", False,
gjc@3625
   148
                                     "pygccxml too old")
gjc@3408
   149
        return
gjc@3408
   150
    
gjc@3408
   151
gjc@3408
   152
    ## Check gccxml version
gjc@3408
   153
    gccxml = conf.find_program('gccxml', var='GCCXML')
gjc@3408
   154
    if not gccxml:
gjc@4067
   155
        Logs.warn("gccxml missing; automatic scanning of API definitions will not be possible")
gjc@3625
   156
        conf.report_optional_feature("pygccxml", "Python API Scanning Support", False,
gjc@3625
   157
                                     "gccxml missing")
gjc@3408
   158
        return
gjc@3408
   159
gjc@3408
   160
    gccxml_version_line = os.popen(gccxml + " --version").readline().strip()
gjc@3408
   161
    m = re.match( "^GCC-XML version (\d\.\d(\.\d)?)$", gccxml_version_line)
gjc@3408
   162
    gccxml_version = m.group(1)
gjc@3408
   163
    gccxml_version_ok = ([int(s) for s in gccxml_version.split('.')] >= [0, 9])
gjc@3408
   164
    conf.check_message('gccxml', 'version', True, gccxml_version)
gjc@3408
   165
    if not gccxml_version_ok:
gjc@4067
   166
        Logs.warn("gccxml too old, need version >= 0.9; automatic scanning of API definitions will not be possible")
gjc@3625
   167
        conf.report_optional_feature("pygccxml", "Python API Scanning Support", False,
gjc@3625
   168
                                     "gccxml too old")
gjc@3408
   169
        return
gjc@3408
   170
    
gjc@3408
   171
    ## If we reached
gjc@3408
   172
    conf.env['ENABLE_PYTHON_SCANNING'] = True
gjc@3625
   173
    conf.report_optional_feature("pygccxml", "Python API Scanning Support", True, None)
gjc@3408
   174
gjc@3408
   175
gjc@3408
   176
prio_headers = {
gjc@3408
   177
    -2: (
gjc@3408
   178
        "string.h", # work around http://www.gccxml.org/Bug/view.php?id=6682
gjc@3408
   179
        ),
gjc@3408
   180
    -1: (
gjc@3408
   181
        "propagation-delay-model.h",
gjc@3408
   182
        "propagation-loss-model.h",
gjc@3408
   183
        "net-device.h",
gjc@3408
   184
        )
gjc@3408
   185
     }
gjc@3408
   186
gjc@3408
   187
def get_header_prio(header):
gjc@3408
   188
    for prio, headers in prio_headers.iteritems():
gjc@3408
   189
        if header in headers:
gjc@3408
   190
            return prio
gjc@3408
   191
    return 1
gjc@3408
   192
gjc@3485
   193
gjc@3485
   194
def calc_header_include(path):
gjc@3485
   195
    (head, tail) = os.path.split (path)
gjc@3485
   196
    if tail == 'ns3':
gjc@3485
   197
        return ''
gjc@3485
   198
    else:
gjc@3485
   199
        return os.path.join (calc_header_include (head), tail)
gjc@3485
   200
gjc@3485
   201
gjc@4064
   202
class gen_everything_h_task(Task.Task):
gjc@4064
   203
    before = 'cc cxx'
gjc@4068
   204
    after = 'ns3header_task'
gjc@4068
   205
    color = 'BLUE'
gjc@3485
   206
gjc@4064
   207
    def run(self):
gjc@4064
   208
        assert len(self.outputs) == 1
gjc@3408
   209
gjc@4064
   210
        header_files = [calc_header_include(node.abspath(self.env)) for node in self.inputs]
gjc@4064
   211
        outfile = file(self.outputs[0].bldpath(self.env), "w")
gjc@3408
   212
gjc@4064
   213
        def sort_func(h1, h2):
gjc@4064
   214
            return cmp((get_header_prio(h1), h1), (get_header_prio(h1), h2))
gjc@3408
   215
gjc@4064
   216
        header_files.sort(sort_func)
gjc@4064
   217
gjc@4064
   218
        print >> outfile, """
gjc@3917
   219
gjc@3917
   220
/* http://www.nsnam.org/bugzilla/show_bug.cgi?id=413 */
gjc@3917
   221
#ifdef ECHO
gjc@3917
   222
# undef ECHO
gjc@3917
   223
#endif
gjc@3917
   224
gjc@4064
   225
    """
gjc@4064
   226
        for header in header_files:
gjc@4064
   227
            print >> outfile, "#include \"ns3/%s\"" % (header,)
gjc@3408
   228
gjc@4064
   229
        print >> outfile, """
gjc@3408
   230
namespace ns3 {
gjc@3408
   231
static inline Ptr<Object>
gjc@3408
   232
__dummy_function_to_force_template_instantiation (Ptr<Object> obj, TypeId typeId)
gjc@3408
   233
{
gjc@3408
   234
   return obj->GetObject<Object> (typeId);
gjc@3408
   235
}
gjc@3408
   236
gjc@3731
   237
gjc@3731
   238
static inline void
gjc@3731
   239
__dummy_function_to_force_template_instantiation_v2 ()
gjc@3731
   240
{
gjc@3731
   241
   Time t1, t2, t3;
gjc@3731
   242
   t1 = t2 + t3;
gjc@3731
   243
   t1 = t2 - t3;
gjc@3731
   244
   TimeSquare tsq = t2*t3;
gjc@3731
   245
   Time tsqdiv = tsq/Seconds(1);
gjc@3731
   246
   Scalar scal = t2/t3;
gjc@3731
   247
   TimeInvert inv = scal/t3;
gjc@3731
   248
   t1 = scal*t1;
gjc@3731
   249
   t1 = t1/scal;
gjc@3731
   250
   t1 < t2;
gjc@3731
   251
   t1 <= t2;
gjc@3731
   252
   t1 == t2;
gjc@3731
   253
   t1 != t2;
gjc@3731
   254
   t1 >= t2;
gjc@3731
   255
   t1 > t2;
gjc@3731
   256
}
gjc@3731
   257
gjc@3929
   258
gjc@3408
   259
}
gjc@3408
   260
"""
gjc@4064
   261
        outfile.close()
gjc@4064
   262
        return 0
gjc@3408
   263
gjc@3408
   264
gjc@3541
   265
gjc@4064
   266
class all_ns3_headers_taskgen(TaskGen.task_gen):
gjc@3408
   267
    """Generates a 'everything.h' header file that includes some/all public ns3 headers.
gjc@3408
   268
    This single header file is to be parsed only once by gccxml, for greater efficiency.
gjc@3408
   269
    """
gjc@4064
   270
    def __init__(self, *args, **kwargs):
gjc@4064
   271
        super(all_ns3_headers_taskgen, self).__init__(*args, **kwargs)
gjc@4064
   272
        self.install_path = None
gjc@3409
   273
        #self.inst_dir = 'ns3'
gjc@3408
   274
gjc@3408
   275
    def apply(self):
gjc@3408
   276
        ## get all of the ns3 headers
gjc@4326
   277
        ns3_dir_node = self.bld.path.find_dir("ns3")
gjc@3408
   278
        all_headers_inputs = []
gjc@3408
   279
gjc@3408
   280
        for filename in self.to_list(self.source):
gjc@4064
   281
            src_node = ns3_dir_node.find_or_declare(filename)
gjc@3408
   282
            if src_node is None:
gjc@4064
   283
                raise Utils.WafError("source ns3 header file %s not found" % (filename,))
gjc@3408
   284
            all_headers_inputs.append(src_node)
gjc@3408
   285
gjc@3408
   286
        ## if self.source was empty, include all ns3 headers in enabled modules
gjc@3408
   287
        if not all_headers_inputs:
gjc@4326
   288
            for ns3headers in self.bld.all_task_gen:
gjc@3408
   289
                if type(ns3headers).__name__ == 'ns3header_taskgen': # XXX: find less hackish way to compare
gjc@3408
   290
                    ## skip headers not part of enabled modules
gjc@3408
   291
                    if self.env['NS3_ENABLED_MODULES']:
gjc@3408
   292
                        if ("ns3-%s" % ns3headers.module) not in self.env['NS3_ENABLED_MODULES']:
gjc@3408
   293
                            continue
gjc@3408
   294
gjc@3408
   295
                    for source in ns3headers.to_list(ns3headers.source):
gjc@3485
   296
                        #source = os.path.basename(source)
gjc@4064
   297
                        node = ns3_dir_node.find_or_declare(source)
gjc@3408
   298
                        if node is None:
gjc@4067
   299
                            raise Utils.WafError("missing header file %s" % (source,))
gjc@3408
   300
                        all_headers_inputs.append(node)
gjc@3408
   301
        assert all_headers_inputs
gjc@4064
   302
        all_headers_outputs = [self.path.find_or_declare("everything.h")]
gjc@4064
   303
        task = self.create_task('gen_everything_h', self.env)
gjc@3408
   304
        task.set_inputs(all_headers_inputs)
gjc@3408
   305
        task.set_outputs(all_headers_outputs)
gjc@3408
   306
gjc@3408
   307
    def install(self):
gjc@3408
   308
        pass
gjc@3408
   309
gjc@3408
   310
gjc@4326
   311
def get_modules_and_headers(bld):
gjc@3408
   312
    """
gjc@3408
   313
    Gets a dict of
gjc@3408
   314
       module_name => ([module_dep1, module_dep2, ...], [module_header1, module_header2, ...])
gjc@3408
   315
    tuples, one for each module.
gjc@3408
   316
    """
gjc@3408
   317
gjc@3408
   318
    retval = {}
gjc@4326
   319
    for module in bld.all_task_gen:
gjc@3408
   320
        if not module.name.startswith('ns3-'):
gjc@3408
   321
            continue
gjc@3408
   322
        module_name = module.name[4:] # strip the ns3- prefix
gjc@3408
   323
        ## find the headers object for this module
gjc@3408
   324
        headers = []
gjc@4326
   325
        for ns3headers in bld.all_task_gen:
gjc@3408
   326
            if type(ns3headers).__name__ != 'ns3header_taskgen': # XXX: find less hackish way to compare
gjc@3408
   327
                continue
gjc@3408
   328
            if ns3headers.module != module_name:
gjc@3408
   329
                continue
gjc@3408
   330
            for source in ns3headers.to_list(ns3headers.source):
gjc@3408
   331
                headers.append(source)
gjc@3408
   332
        retval[module_name] = (list(module.module_deps), headers)
gjc@3408
   333
    return retval
gjc@3408
   334
gjc@3408
   335
gjc@3541
   336
gjc@4064
   337
class python_scan_task(Task.TaskBase):
gjc@3541
   338
    """Uses gccxml to scan the file 'everything.h' and extract API definitions.
gjc@3541
   339
    """
gjc@4066
   340
    after = 'gen_everything_h_task'
gjc@4066
   341
    before = 'cc cxx'
gjc@4326
   342
    def __init__(self, curdirnode, env, bld):
gjc@4326
   343
        self.bld = bld
gjc@4326
   344
        super(python_scan_task, self).__init__(generator=self)
gjc@3541
   345
        self.curdirnode = curdirnode
gjc@3541
   346
        self.env = env
gjc@4066
   347
gjc@4066
   348
    def display(self):
gjc@4066
   349
        return 'python-scan\n'
gjc@3541
   350
gjc@3541
   351
    def run(self):
gjc@3541
   352
        #print "Rescanning the python bindings..."
gjc@3541
   353
        argv = [
gjc@3541
   354
            self.env['PYTHON'],
gjc@3541
   355
            os.path.join(self.curdirnode.abspath(), 'ns3modulescan.py'), # scanning script
gjc@3541
   356
            self.curdirnode.find_dir('../..').abspath(self.env), # include path (where the ns3 include dir is)
gjc@4064
   357
            self.curdirnode.find_or_declare('everything.h').abspath(self.env),
gjc@3541
   358
            os.path.join(self.curdirnode.abspath(), 'ns3modulegen_generated.py'), # output file
gjc@3541
   359
            ]
gjc@3541
   360
        scan = subprocess.Popen(argv, stdin=subprocess.PIPE)
gjc@4326
   361
        scan.stdin.write(repr(get_modules_and_headers(self.bld)))
gjc@3541
   362
        scan.stdin.close()
gjc@4066
   363
        retval = scan.wait()
gjc@4066
   364
        print "Scan finished with exit code", retval
gjc@4066
   365
        if retval:
gjc@4066
   366
            return retval
gjc@4066
   367
        # signal stop (we generated files into the source dir and WAF
gjc@4066
   368
        # can't cope with it, so we have to force the user to restart
gjc@4066
   369
        # WAF)
gjc@4326
   370
        self.bld.generator.stop = 1
gjc@4066
   371
        return 0
gjc@4066
   372
gjc@3541
   373
gjc@3408
   374
def build(bld):
gjc@4064
   375
    if Options.options.python_disable:
gjc@3408
   376
        return
gjc@3408
   377
gjc@4064
   378
    env = bld.env
gjc@4064
   379
    curdir = bld.path.abspath()
gjc@3591
   380
gjc@3873
   381
    set_pybindgen_pythonpath(env)
gjc@3873
   382
gjc@3486
   383
    if env['ENABLE_PYTHON_BINDINGS']:
gjc@4064
   384
        obj = bld.new_task_gen('all_ns3_headers')
gjc@3408
   385
gjc@4064
   386
    if Options.options.python_scan:
gjc@3408
   387
        if not env['ENABLE_PYTHON_SCANNING']:
gjc@4064
   388
            raise Utils.WafError("Cannot re-scan python bindings: (py)gccxml not available")
gjc@4326
   389
        python_scan_task(bld.path, env, bld)
gjc@4066
   390
        return
gjc@3408
   391
gjc@3424
   392
    ## Get a list of scanned modules; the set of scanned modules
gjc@3424
   393
    ## may be smaller than the set of all modules, in case a new
gjc@3424
   394
    ## ns3 module is being developed which wasn't scanned yet.
gjc@3424
   395
    scanned_modules = []
gjc@3591
   396
    for filename in os.listdir(curdir):
gjc@3424
   397
        m = re.match(r"^ns3_module_(.+)\.py$", filename)
gjc@3424
   398
        if m is None:
gjc@3424
   399
            continue
gjc@3591
   400
        name = m.group(1)
gjc@3591
   401
        if name.endswith("__local"):
gjc@3591
   402
            continue
gjc@3591
   403
        scanned_modules.append(name)
gjc@3424
   404
gjc@3408
   405
    if env['ENABLE_PYTHON_BINDINGS']:
gjc@4064
   406
        source = [
gjc@4064
   407
            'ns3modulegen.py',
gjc@4064
   408
            'ns3modulegen_generated.py',
gjc@4064
   409
            'ns3modulegen_core_customizations.py',
gjc@4064
   410
            ]
gjc@4064
   411
        target = [
gjc@4064
   412
            'ns3module.cc',
gjc@4064
   413
            'ns3module.h',
gjc@4064
   414
            'ns3modulegen.log',
gjc@4064
   415
            ]
gjc@4064
   416
        argv = ['NS3_ENABLED_FEATURES=${FEATURES}', '${PYTHON}', '${SRC[0]}', '${TGT[0]}']
gjc@4326
   417
        argv.extend(get_modules_and_headers(bld).iterkeys())
gjc@3423
   418
        for module in scanned_modules:
gjc@4064
   419
            source.append("ns3_module_%s.py" % module)
gjc@3591
   420
            local = "ns3_module_%s__local.py" % module
gjc@3591
   421
            if os.path.exists(os.path.join(curdir, local)):
gjc@4064
   422
                source.append(local)
gjc@3423
   423
gjc@4064
   424
        argv.extend(['2>', '${TGT[2]}']) # 2> ns3modulegen.log
gjc@4064
   425
gjc@3423
   426
        for module in scanned_modules:
gjc@4064
   427
            target.append("ns3_module_%s.cc" % module)
gjc@3423
   428
gjc@3639
   429
        features = []
gjc@3639
   430
        for (name, caption, was_enabled, reason_not_enabled) in env['NS3_OPTIONAL_FEATURES']:
gjc@3639
   431
            if was_enabled:
gjc@3639
   432
                features.append(name)
gjc@4064
   433
gjc@4312
   434
        bindgen = bld.new_task_gen('command', source=source, target=target, command=argv)
gjc@4312
   435
        bindgen.env['FEATURES'] = ','.join(features)
gjc@4312
   436
        bindgen.dep_vars = ['FEATURES']
gjc@4088
   437
        bindgen.before = 'cxx'
gjc@4088
   438
        bindgen.after = 'gen_everything_h_task'
gjc@4326
   439
        bindgen.name = "pybindgen-command"
gjc@3408
   440
gjc@4064
   441
        pymod = bld.new_task_gen('cxx', 'shlib', 'pyext')
gjc@4535
   442
        if sys.platform == 'cygwin':
gjc@4535
   443
            pymod.features.append('implib') # workaround for WAF bug #472
gjc@3408
   444
        pymod.source = ['ns3module.cc', 'ns3module_helpers.cc']
gjc@3408
   445
        pymod.includes = '.'
gjc@3424
   446
        for module in scanned_modules:
gjc@3424
   447
            pymod.source.append("ns3_module_%s.cc" % module)
gjc@3408
   448
        pymod.target = 'ns3/_ns3'
gjc@3408
   449
        pymod.name = 'ns3module'
gjc@3408
   450
        pymod.uselib_local = "ns3"
mathieu@4395
   451
        if pymod.env['ENABLE_STATIC_NS3']:
mathieu@4440
   452
            if sys.platform == 'darwin':
mathieu@4440
   453
                pymod.env.append_value('LINKFLAGS', '-Wl,-all_load')
mathieu@4440
   454
                pymod.env.append_value('LINKFLAGS', '-lns3')
mathieu@4440
   455
            else:
mathieu@4440
   456
                pymod.env.append_value('LINKFLAGS', '-Wl,--whole-archive,-Bstatic')
mathieu@4440
   457
                pymod.env.append_value('LINKFLAGS', '-lns3')
mathieu@4440
   458
                pymod.env.append_value('LINKFLAGS', '-Wl,-Bdynamic,--no-whole-archive')
gjc@4104
   459
gjc@4104
   460
        defines = list(pymod.env['CXXDEFINES'])
gjc@4104
   461
        defines.extend(['NS_DEPRECATED=', 'NS3_DEPRECATED_H'])
gjc@4104
   462
        if Options.platform == 'win32':
gjc@4104
   463
            try:
gjc@4104
   464
                defines.remove('_DEBUG') # causes undefined symbols on win32
gjc@4104
   465
            except ValueError:
gjc@4104
   466
                pass
gjc@4104
   467
        pymod.env['CXXDEFINES'] = defines
gjc@3408
   468
gjc@4064
   469
        # copy the __init__.py file to the build dir. waf can't handle
gjc@3408
   470
        # this, it's against waf's principles to have build dir files
gjc@3408
   471
        # with the same name as source dir files, apparently.
gjc@4064
   472
        dirnode = bld.path.find_dir('ns3')
gjc@3408
   473
        src = os.path.join(dirnode.abspath(), '__init__.py')
gjc@3408
   474
        dst = os.path.join(dirnode.abspath(env), '__init__.py')
gjc@3408
   475
        try:
gjc@3408
   476
            need_copy = os.stat(src).st_mtime > os.stat(dst).st_mtime
gjc@3408
   477
        except OSError:
gjc@3408
   478
            need_copy = True
gjc@3408
   479
        if need_copy:
gjc@3408
   480
            try:
gjc@3408
   481
                os.mkdir(os.path.dirname(dst))
gjc@3408
   482
            except OSError:
gjc@3408
   483
                pass
gjc@3408
   484
            print "%r -> %r" % (src, dst)
gjc@3408
   485
            shutil.copy2(src, dst)