View | Details | Raw Unified | Return to bug 2630
Collapse All | Expand All

(-)8f477a0e8cfe (+752 lines)
Added Link Here 
1
2
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
3
from __future__ import print_function
4
import os, os.path
5
import sys
6
import shutil
7
import types
8
import warnings
9
10
from waflib import TaskGen, Task, Options, Build, Utils
11
from waflib.Errors import WafError
12
import wutils
13
14
try:
15
    set
16
except NameError:
17
    from sets import Set as set # Python 2.3 fallback
18
19
20
21
all_modules = []
22
for dirname in os.listdir('contrib'):
23
    if dirname.startswith('.') or dirname == 'CVS':
24
        continue
25
    path = os.path.join('contrib', dirname)
26
    if not os.path.isdir(path):
27
        continue
28
    if os.path.exists(os.path.join(path, 'wscript')):
29
        all_modules.append(dirname)
30
all_modules.sort()
31
32
33
34
def options(opt):
35
    opt.add_option('--enable-rpath',
36
                   help=("Link programs with rpath"
37
                         " (normally not needed, see "
38
                         " --run and --shell; moreover, only works in some"
39
                         " specific platforms, such as Linux and Solaris)"),
40
                   action="store_true", dest='enable_rpath', default=False)
41
    
42
    opt.add_option('--enable-modules',
43
                   help=("Build only these modules (and dependencies)"),
44
                   dest='enable_modules')
45
46
    opt.load('boost', tooldir=['waf-tools'])
47
48
    for module in all_modules:
49
        opt.recurse(module, mandatory=False)
50
51
def configure(conf):
52
    conf.env['REQUIRED_BOOST_LIBS'] = []
53
    for module in all_modules:
54
        conf.recurse (module, name="required_boost_libs", mandatory=False)
55
56
    if conf.env['REQUIRED_BOOST_LIBS'] is not []:
57
        conf.load('boost')
58
        conf.check_boost(lib=' '.join (conf.env['REQUIRED_BOOST_LIBS']), mandatory=False)
59
        if not conf.env['LIB_BOOST']:
60
            conf.check_boost(lib=' '.join (conf.env['REQUIRED_BOOST_LIBS']), libpath="/usr/lib64", mandatory=False)
61
            if not conf.env['LIB_BOOST']:
62
                conf.env['LIB_BOOST'] = []
63
64
    # Append blddir to the module path before recursing into modules
65
    blddir = os.path.abspath(os.path.join(conf.bldnode.abspath(), conf.variant))
66
    conf.env.append_value('NS3_MODULE_PATH', blddir)
67
68
    for module in all_modules:
69
        conf.recurse(module, mandatory=False)
70
71
    # Remove duplicate path items
72
    conf.env['NS3_MODULE_PATH'] = wutils.uniquify_list(conf.env['NS3_MODULE_PATH'])
73
74
    if Options.options.enable_rpath:
75
        conf.env.append_value('RPATH', '-Wl,-rpath,%s' % (os.path.join(blddir),))
76
77
    ## Used to link the 'test-runner' program with all of ns-3 code
78
    conf.env['NS3_CONTRIBUTED_MODULES'] = ['ns3-' + module.split('/')[-1] for module in all_modules]
79
80
81
82
# we need the 'ns3module' waf "feature" to be created because code
83
# elsewhere looks for it to find the ns3 module objects.
84
@TaskGen.feature('ns3module')
85
def _add_test_code(module):
86
    pass
87
88
def create_ns3_module(bld, name, dependencies=(), test=False):
89
    static = bool(bld.env.ENABLE_STATIC_NS3)
90
    # Create a separate library for this module.
91
    if static:
92
        module = bld(features='cxx cxxstlib ns3module')
93
    else:
94
        module = bld(features='cxx cxxshlib ns3module')
95
    module.target = '%s/ns%s-%s%s' % (bld.srcnode.path_from(module.path), wutils.VERSION,
96
                                       name, bld.env.BUILD_SUFFIX)
97
    linkflags = []
98
    cxxflags = []
99
    ccflags = []
100
    if not static:
101
        cxxflags = module.env['shlib_CXXFLAGS']
102
        ccflags = module.env['shlib_CXXFLAGS']
103
        # Turn on the link flags for shared libraries if we have the
104
        # proper compiler and platform.
105
        if module.env['CXX_NAME'] in ['gcc', 'icc'] and module.env['WL_SONAME_SUPPORTED']:
106
            # Get the module library name without any relative paths
107
            # at its beginning because all of the libraries will end
108
            # up in the same directory.
109
            module_library_name = module.env.cshlib_PATTERN % (os.path.basename(module.target),)
110
            linkflags = '-Wl,--soname=' + module_library_name
111
    cxxdefines = ["NS3_MODULE_COMPILATION"]
112
    ccdefines = ["NS3_MODULE_COMPILATION"]
113
114
    module.env.append_value('CXXFLAGS', cxxflags)
115
    module.env.append_value('CCFLAGS', ccflags)
116
    module.env.append_value('LINKFLAGS', linkflags)
117
    module.env.append_value('CXXDEFINES', cxxdefines)
118
    module.env.append_value('CCDEFINES', ccdefines)
119
120
    module.is_static = static
121
    module.vnum = wutils.VNUM
122
    # Add the proper path to the module's name.
123
    # Set the libraries this module depends on.  
124
    module.module_deps = list(dependencies)
125
126
    module.install_path = "${LIBDIR}"
127
128
    module.name = "ns3-" + name
129
    module.dependencies = dependencies
130
    # Initially create an empty value for this because the pcfile
131
    # writing task assumes every module has a uselib attribute.
132
    module.uselib = ''
133
    module.use = ['ns3-' + dep for dep in dependencies]
134
    module.test = test
135
    module.is_ns3_module = True
136
    module.ns3_dir_location = bld.path.path_from(bld.srcnode)
137
138
    module.env.append_value("INCLUDES", '#')
139
140
    module.pcfilegen = bld(features='ns3pcfile')
141
    module.pcfilegen.module = module.name
142
    
143
    return module
144
145
@TaskGen.feature("ns3testlib")
146
@TaskGen.before_method("apply_incpaths")
147
def apply_incpaths_ns3testlib(self):
148
    if not self.source:
149
        return
150
    testdir = self.source[-1].parent.path_from(self.bld.srcnode)
151
    self.env.append_value("DEFINES", 'NS_TEST_SOURCEDIR="%s"' % (testdir,))
152
153
154
def create_ns3_module_test_library(bld, name):
155
    # Create an ns3 module for the test library that depends only on
156
    # the module being tested.
157
    library_name = name + "-test"
158
    library = bld.create_ns3_module(library_name, [name], test=True)
159
    library.features += " ns3testlib"
160
161
    # Modify attributes for the test library that are different from a
162
    # normal module.
163
    del library.is_ns3_module
164
    library.is_ns3_module_test_library = True
165
    library.module_name = 'ns3-' + name
166
167
    # Add this module and test library to the list.
168
    bld.env.append_value('NS3_MODULES_WITH_TEST_LIBRARIES', [(library.module_name, library.name)])
169
170
    # Set the include path from the build directory to modules. 
171
    relative_path_from_build_to_here = bld.path.path_from(bld.bldnode)
172
    include_flag = '-I' + relative_path_from_build_to_here
173
    library.env.append_value('CXXFLAGS', include_flag)
174
    library.env.append_value('CCFLAGS',  include_flag)
175
176
    return library
177
178
def create_obj(bld, *args):
179
    warnings.warn("(in %s) Use bld(...) call now, instead of bld.create_obj(...)" % str(bld.path),
180
                  DeprecationWarning, stacklevel=2)
181
    return bld(*args)
182
183
184
def ns3_python_bindings(bld):
185
186
    # this method is called from a module wscript, so remember bld.path is not bindings/python!
187
    module_abs_src_path = bld.path.abspath()
188
    module = os.path.basename(module_abs_src_path)
189
    env = bld.env
190
    env.append_value("MODULAR_BINDINGS_MODULES", "ns3-"+module)
191
192
    if Options.options.apiscan:
193
        return
194
195
    if not env['ENABLE_PYTHON_BINDINGS']:
196
        return
197
198
    bindings_dir = bld.path.find_dir("bindings")
199
    if bindings_dir is None or not os.path.exists(bindings_dir.abspath()):
200
        warnings.warn("(in %s) Requested to build modular python bindings, but apidefs dir not found "
201
                      "=> skipped the bindings." % str(bld.path),
202
                      Warning, stacklevel=2)
203
        return
204
205
    if ("ns3-%s" % (module,)) not in env.NS3_ENABLED_MODULES:
206
        #print "bindings for module %s which is not enabled, skip" % module
207
        return
208
209
    env.append_value('PYTHON_MODULES_BUILT', module)
210
    try:
211
        apidefs = env['PYTHON_BINDINGS_APIDEFS'].replace("-", "_")
212
    except AttributeError:
213
        # we likely got an empty list for env['PYTHON_BINDINGS_APIDEFS']
214
        return
215
216
    #debug = ('PYBINDGEN_DEBUG' in os.environ)
217
    debug = True # XXX
218
    source = [bld.srcnode.find_resource('bindings/python/ns3modulegen-modular.py'),
219
              bld.path.find_resource("bindings/modulegen__%s.py" % apidefs)]
220
221
    modulegen_customizations = bindings_dir.find_resource("modulegen_customizations.py")
222
    if modulegen_customizations is not None:
223
        source.append(modulegen_customizations)
224
225
    modulegen_local = bld.path.find_resource("bindings/modulegen_local.py")
226
    # the local customization file may or not exist
227
    if modulegen_local is not None:
228
        source.append("bindings/modulegen_local.py")
229
230
    module_py_name = module.replace('-', '_')
231
    module_target_dir = bld.srcnode.find_dir("bindings/python/ns").path_from(bld.path)
232
233
    # if bindings/<module>.py exists, it becomes the module frontend, and the C extension befomes _<module>
234
    if bld.path.find_resource("bindings/%s.py" % (module_py_name,)) is not None:
235
        bld(features='copy',
236
            source=("bindings/%s.py" % (module_py_name,)),
237
            target=('%s/%s.py' % (module_target_dir, module_py_name)))
238
        extension_name = '_%s' % (module_py_name,)
239
        bld.install_files('${PYTHONARCHDIR}/ns', ["bindings/%s.py" % (module_py_name,)])
240
    else:
241
        extension_name = module_py_name
242
243
    target = ['bindings/ns3module.cc', 'bindings/ns3module.h', 'bindings/ns3modulegen.log']
244
    #if not debug:
245
    #    target.append('ns3modulegen.log')
246
247
    argv = ['NS3_ENABLED_FEATURES=${FEATURES}',
248
            'GCC_RTTI_ABI_COMPLETE=${GCC_RTTI_ABI_COMPLETE}',
249
            '${PYTHON}']
250
    #if debug:
251
    #    argv.extend(["-m", "pdb"])
252
    
253
    argv.extend(['${SRC[0]}', module_abs_src_path, apidefs, extension_name, '${TGT[0]}'])
254
255
    argv.extend(['2>', '${TGT[2]}']) # 2> ns3modulegen.log
256
257
    features = []
258
    for (name, caption, was_enabled, reason_not_enabled) in env['NS3_OPTIONAL_FEATURES']:
259
        if was_enabled:
260
            features.append(name)
261
262
    bindgen = bld(features='command', source=source, target=target, command=argv)
263
    bindgen.env['FEATURES'] = ','.join(features)
264
    bindgen.dep_vars = ['FEATURES', "GCC_RTTI_ABI_COMPLETE"]
265
    bindgen.before = 'cxx'
266
    bindgen.after = 'gen_ns3_module_header'
267
    bindgen.name = "pybindgen(ns3 module %s)" % module
268
    bindgen.install_path = None
269
270
    # generate the extension module
271
    pymod = bld(features='cxx cxxshlib pyext')
272
    pymod.source = ['bindings/ns3module.cc']
273
    pymod.target = '%s/%s' % (module_target_dir, extension_name)
274
    pymod.name = 'ns3module_%s' % module
275
    pymod.use = ["%s" % mod for mod in pymod.env['NS3_ENABLED_MODULES']] #  Should be '"ns3-"+module', but see bug 1117
276
    if pymod.env['ENABLE_STATIC_NS3']:
277
        if sys.platform == 'darwin':
278
            pymod.env.append_value('LINKFLAGS', '-Wl,-all_load')
279
            for mod in pymod.usel:
280
                #mod = mod.split("--lib")[0]
281
                pymod.env.append_value('LINKFLAGS', '-l' + mod)
282
        else:
283
            pymod.env.append_value('LINKFLAGS', '-Wl,--whole-archive,-Bstatic')
284
            for mod in pymod.use:
285
                #mod = mod.split("--lib")[0]
286
                pymod.env.append_value('LINKFLAGS', '-l' + mod)
287
            pymod.env.append_value('LINKFLAGS', '-Wl,-Bdynamic,--no-whole-archive')
288
    defines = list(pymod.env['DEFINES'])
289
    defines.extend(['NS_DEPRECATED=', 'NS3_DEPRECATED_H'])
290
    if Options.platform == 'win32':
291
        try:
292
            defines.remove('_DEBUG') # causes undefined symbols on win32
293
        except ValueError:
294
            pass
295
    pymod.env['DEFINES'] = defines
296
    pymod.includes = '# bindings'
297
    pymod.install_path = '${PYTHONARCHDIR}/ns'
298
299
    # Workaround to a WAF bug, remove this when ns-3 upgrades to WAF > 1.6.10
300
    # https://www.nsnam.org/bugzilla/show_bug.cgi?id=1335
301
    # http://code.google.com/p/waf/issues/detail?id=1098
302
    if Utils.unversioned_sys_platform() == 'darwin':
303
        pymod.mac_bundle = True
304
305
    return pymod
306
307
308
def build(bld):
309
    bld.create_ns3_module = types.MethodType(create_ns3_module, bld)
310
    bld.create_ns3_module_test_library = types.MethodType(create_ns3_module_test_library, bld)
311
    bld.create_obj = types.MethodType(create_obj, bld)
312
    bld.ns3_python_bindings = types.MethodType(ns3_python_bindings, bld)
313
    
314
    # Remove these modules from the list of all modules.
315
    for not_built in bld.env['MODULES_NOT_BUILT']:
316
317
        # XXX Because these modules are located in subdirectories of
318
        # test, their names in the all_modules list include the extra
319
        # relative path "test/".  If these modules are moved into the
320
        # src directory, then this if block should be removed.
321
        if not_built == 'ns3tcp' or not_built == 'ns3wifi':
322
            not_built = 'test/' + not_built
323
324
        if not_built in all_modules:
325
            all_modules.remove(not_built)
326
327
    bld.recurse(list(all_modules))
328
329
    for module in all_modules:
330
        modheader = bld(features='ns3moduleheader')
331
        modheader.module = module.split('/')[-1]
332
333
class ns3pcfile_task(Task.Task):
334
    after = 'cxx'
335
336
    def __str__(self):
337
        "string to display to the user"
338
        tgt_str = ' '.join([a.bldpath() for a in self.outputs])
339
        return 'pcfile: %s\n' % (tgt_str)
340
341
    def runnable_status(self):
342
        return super(ns3pcfile_task, self).runnable_status()
343
344
    def _self_libs(self, env, name, libdir):
345
        if env['ENABLE_STATIC_NS3']:
346
            path_st = 'STLIBPATH_ST'
347
            lib_st = 'STLIB_ST'
348
            lib_marker = 'STLIB_MARKER'
349
        else:
350
            path_st = 'LIBPATH_ST'
351
            lib_st = 'LIB_ST'
352
            lib_marker = 'SHLIB_MARKER'
353
        retval = [env[path_st] % libdir]
354
        if env[lib_marker]:
355
            retval.append(env[lib_marker])
356
        retval.append(env[lib_st] % name)
357
        return retval
358
359
    def _lib(self, env, dep):
360
        libpath = env['LIBPATH_%s' % dep]
361
        linkflags = env['LINKFLAGS_%s' % dep]
362
        libs = env['LIB_%s' % dep]
363
        retval = []
364
        for path in libpath:
365
            retval.append(env['LIBPATH_ST'] % path)
366
            retval = retval + linkflags
367
        for lib in libs:
368
            retval.append(env['LIB_ST'] % lib)
369
        return retval
370
371
    def _listify(self, v):
372
        if isinstance(v, list):
373
            return v
374
        else:
375
            return [v]
376
377
    def _cflags(self, dep):
378
        flags = self.env['CFLAGS_%s' % dep]
379
        return self._listify(flags)
380
381
    def _cxxflags(self, dep):
382
        return self._listify(self.env['CXXFLAGS_%s' % dep])
383
384
    def _defines(self, dep):
385
        return [self.env['DEFINES_ST'] % define for define in self.env['DEFINES_%s' % dep]] 
386
387
    def _includes(self, dep):
388
        includes = self.env['INCLUDES_%s' % dep]
389
        return [self.env['CPPPATH_ST'] % include for include in includes]
390
391
    def _generate_pcfile(self, name, use, env, outfilename):
392
        outfile = open(outfilename, 'wt')
393
        prefix = env.PREFIX
394
        includedir = Utils.subst_vars('${INCLUDEDIR}/%s%s' % (wutils.APPNAME, wutils.VERSION), env)
395
        libdir = env.LIBDIR
396
        libs = self._self_libs(env, "%s%s-%s%s" % (wutils.APPNAME, wutils.VERSION, name[4:], env.BUILD_SUFFIX), '${libdir}')
397
        for dep in use:
398
            libs += self._lib(env, dep)
399
        for dep in env.LIBS:
400
            libs += self.env['LIB_ST'] % dep
401
        cflags = [self.env['CPPPATH_ST'] % '${includedir}']
402
        requires = []
403
        for dep in use:
404
            cflags = cflags + self._cflags(dep) + self._cxxflags(dep) + \
405
                self._defines(dep) + self._includes(dep)
406
            if dep.startswith('ns3-'):
407
                dep_name = dep[4:]
408
                requires.append("libns%s-%s%s" % (wutils.VERSION, dep_name, env.BUILD_SUFFIX))
409
        print("""\
410
prefix=%s
411
libdir=%s
412
includedir=%s
413
414
Name: lib%s
415
Description: ns-3 module %s
416
Version: %s
417
Libs: %s
418
Cflags: %s
419
Requires: %s\
420
""" % (prefix, libdir, includedir,
421
       name, name, wutils.VERSION, ' '.join(libs), ' '.join(cflags), ' '.join(requires)), file=outfile)
422
        outfile.close()
423
424
    def run(self):
425
        output_filename = self.outputs[0].abspath()
426
        self._generate_pcfile(self.module.name, 
427
                              self.module.to_list(self.module.use),
428
                              self.env, output_filename)
429
430
431
@TaskGen.feature('ns3pcfile')
432
@TaskGen.after_method('process_rule')
433
def apply(self):
434
    module = self.bld.find_ns3_module(self.module)
435
    output_filename = 'lib%s.pc' % os.path.basename(module.target)
436
    output_node = self.path.find_or_declare(output_filename)
437
    assert output_node is not None, str(self)
438
    task = self.create_task('ns3pcfile')
439
    self.bld.install_files('${LIBDIR}/pkgconfig', output_node)
440
    task.set_outputs([output_node])
441
    task.module = module
442
443
444
445
@TaskGen.feature('ns3header')
446
@TaskGen.after_method('process_rule')
447
def apply_ns3header(self):
448
    if self.module is None:
449
        raise WafError("'module' missing on ns3headers object %s" % self)
450
    ns3_dir_node = self.bld.path.find_or_declare("ns3")
451
    for filename in set(self.to_list(self.source)):
452
        src_node = self.path.find_resource(filename)
453
        if src_node is None:
454
            raise WafError("source ns3 header file %s not found" % (filename,))
455
        dst_node = ns3_dir_node.find_or_declare(src_node.name)
456
        assert dst_node is not None
457
        task = self.create_task('ns3header')
458
        task.mode = getattr(self, 'mode', 'install')
459
        if task.mode == 'install':
460
            self.bld.install_files('${INCLUDEDIR}/%s%s/ns3' % (wutils.APPNAME, wutils.VERSION), [src_node])
461
            task.set_inputs([src_node])
462
            task.set_outputs([dst_node])
463
        else:
464
            task.header_to_remove = dst_node
465
    self.headers = set(self.to_list(self.source))
466
    self.source = '' # tell WAF not to process these files further
467
468
469
class ns3header_task(Task.Task):
470
    before = 'cxx gen_ns3_module_header'
471
    color = 'BLUE'
472
473
    def __str__(self):
474
        "string to display to the user"
475
        env = self.env
476
        src_str = ' '.join([a.bldpath() for a in self.inputs])
477
        tgt_str = ' '.join([a.bldpath() for a in self.outputs])
478
        if self.outputs: sep = ' -> '
479
        else: sep = ''
480
        if self.mode == 'remove':
481
            return 'rm-ns3-header %s' % (self.header_to_remove.abspath(),)
482
        return 'install-ns3-header: %s' % (tgt_str)
483
484
    def __repr__(self):
485
        return str(self)
486
487
    def uid(self):
488
        try:
489
            return self.uid_
490
        except AttributeError:
491
            m = Utils.md5()
492
            up = m.update
493
            up(self.__class__.__name__.encode())
494
            for x in self.inputs + self.outputs:
495
                up(x.abspath().encode())
496
            up(self.mode.encode())
497
            if self.mode == 'remove':
498
                up(self.header_to_remove.abspath().encode())
499
            self.uid_ = m.digest()
500
            return self.uid_
501
502
    def runnable_status(self):
503
        if self.mode == 'remove':
504
            if os.path.exists(self.header_to_remove.abspath()):
505
                return Task.RUN_ME
506
            else:
507
                return Task.SKIP_ME
508
        else:
509
            return super(ns3header_task, self).runnable_status()
510
511
    def run(self):
512
        if self.mode == 'install':
513
            assert len(self.inputs) == len(self.outputs)
514
            inputs = [node.abspath() for node in self.inputs]
515
            outputs = [node.abspath() for node in self.outputs]
516
            for src, dst in zip(inputs, outputs):
517
                try:
518
                    os.chmod(dst, 0o600)
519
                except OSError:
520
                    pass
521
                shutil.copy2(src, dst)
522
                ## make the headers in builddir read-only, to prevent
523
                ## accidental modification
524
                os.chmod(dst, 0o400)
525
            return 0
526
        else:
527
            assert len(self.inputs) == 0
528
            assert len(self.outputs) == 0
529
            out_file_name = self.header_to_remove.abspath()
530
            try:
531
                os.unlink(out_file_name)
532
            except OSError as ex:
533
                if ex.errno != 2:
534
                    raise
535
            return 0
536
537
538
@TaskGen.feature('ns3privateheader')
539
@TaskGen.after_method('process_rule')
540
def apply_ns3privateheader(self):
541
    if self.module is None:
542
        raise WafError("'module' missing on ns3headers object %s" % self)
543
    ns3_dir_node = self.bld.path.find_or_declare("ns3/private")
544
    for filename in set(self.to_list(self.source)):
545
        src_node = self.path.find_resource(filename)
546
        if src_node is None:
547
            raise WafError("source ns3 header file %s not found" % (filename,))
548
        dst_node = ns3_dir_node.find_or_declare(src_node.name)
549
        assert dst_node is not None
550
        task = self.create_task('ns3privateheader')
551
        task.mode = getattr(self, 'mode', 'install')
552
        if task.mode == 'install':
553
            task.set_inputs([src_node])
554
            task.set_outputs([dst_node])
555
        else:
556
            task.header_to_remove = dst_node
557
    self.headers = set(self.to_list(self.source))
558
    self.source = '' # tell WAF not to process these files further
559
560
class ns3privateheader_task(Task.Task):
561
    before = 'cxx gen_ns3_module_header'
562
    after = 'ns3header'
563
    color = 'BLUE'
564
565
    def __str__(self):
566
        "string to display to the user"
567
        env = self.env
568
        src_str = ' '.join([a.bldpath() for a in self.inputs])
569
        tgt_str = ' '.join([a.bldpath() for a in self.outputs])
570
        if self.outputs: sep = ' -> '
571
        else: sep = ''
572
        if self.mode == 'remove':
573
            return 'rm-ns3-header %s' % (self.header_to_remove.abspath(),)
574
        return 'install-ns3-header: %s' % (tgt_str)
575
576
    def __repr__(self):
577
        return str(self)
578
579
    def uid(self):
580
        try:
581
            return self.uid_
582
        except AttributeError:
583
            m = Utils.md5()
584
            up = m.update
585
            up(self.__class__.__name__.encode())
586
            for x in self.inputs + self.outputs:
587
                up(x.abspath().encode())
588
            up(self.mode.encode())
589
            if self.mode == 'remove':
590
                up(self.header_to_remove.abspath().encode())
591
            self.uid_ = m.digest()
592
            return self.uid_
593
594
    def runnable_status(self):
595
        if self.mode == 'remove':
596
            if os.path.exists(self.header_to_remove.abspath()):
597
                return Task.RUN_ME
598
            else:
599
                return Task.SKIP_ME
600
        else:
601
            return super(ns3privateheader_task, self).runnable_status()
602
603
    def run(self):
604
        if self.mode == 'install':
605
            assert len(self.inputs) == len(self.outputs)
606
            inputs = [node.abspath() for node in self.inputs]
607
            outputs = [node.abspath() for node in self.outputs]
608
            for src, dst in zip(inputs, outputs):
609
                try:
610
                    os.chmod(dst, 0o600)
611
                except OSError:
612
                    pass
613
                shutil.copy2(src, dst)
614
                ## make the headers in builddir read-only, to prevent
615
                ## accidental modification
616
                os.chmod(dst, 0o400)
617
            return 0
618
        else:
619
            assert len(self.inputs) == 0
620
            assert len(self.outputs) == 0
621
            out_file_name = self.header_to_remove.abspath()
622
            try:
623
                os.unlink(out_file_name)
624
            except OSError as ex:
625
                if ex.errno != 2:
626
                    raise
627
            return 0
628
629
630
class gen_ns3_module_header_task(Task.Task):
631
    before = 'cxx'
632
    after = 'ns3header'
633
    color = 'BLUE'
634
635
    def runnable_status(self):
636
        if self.mode == 'remove':
637
            if os.path.exists(self.header_to_remove.abspath()):
638
                return Task.RUN_ME
639
            else:
640
                return Task.SKIP_ME
641
        else:
642
            return super(gen_ns3_module_header_task, self).runnable_status()
643
644
    def __str__(self):
645
        "string to display to the user"
646
        env = self.env
647
        src_str = ' '.join([a.bldpath() for a in self.inputs])
648
        tgt_str = ' '.join([a.bldpath() for a in self.outputs])
649
        if self.outputs: sep = ' -> '
650
        else: sep = ''
651
        if self.mode == 'remove':
652
            return 'rm-module-header %s' % (self.header_to_remove.abspath(),)
653
        return 'gen-module-header: %s' % (tgt_str)
654
655
    def run(self):
656
        if self.mode == 'remove':
657
            assert len(self.inputs) == 0
658
            assert len(self.outputs) == 0
659
            out_file_name = self.header_to_remove.abspath()
660
            try:
661
                os.unlink(out_file_name)
662
            except OSError as ex:
663
                if ex.errno != 2:
664
                    raise
665
            return 0
666
        assert len(self.outputs) == 1
667
        out_file_name = self.outputs[0].get_bld().abspath()#self.env)
668
        header_files = [os.path.basename(node.abspath()) for node in self.inputs]
669
        outfile = open(out_file_name, "w")
670
        header_files.sort()
671
672
        print("""
673
#ifdef NS3_MODULE_COMPILATION
674
# error "Do not include ns3 module aggregator headers from other modules; these are meant only for end user scripts."
675
#endif
676
677
#ifndef NS3_MODULE_%s
678
    """ % (self.module.upper().replace('-', '_'),), file=outfile)
679
680
    #     if self.module_deps:
681
    #         print >> outfile, "// Module dependencies:"
682
    #     for dep in self.module_deps:
683
    #         print >> outfile, "#include \"%s-module.h\"" % dep
684
685
        print(file=outfile)
686
        print("// Module headers:", file=outfile)
687
        for header in header_files:
688
            print("#include \"%s\"" % (header,), file=outfile)
689
690
        print("#endif", file=outfile)
691
692
        outfile.close()
693
        return 0
694
695
    def sig_explicit_deps(self):
696
        self.m.update('\n'.join(sorted([node.abspath() for node in self.inputs])).encode('utf-8'))
697
        return self.m.digest()
698
699
    def unique_id(self):
700
        try:
701
            return self.uid
702
        except AttributeError:
703
            "this is not a real hot zone, but we want to avoid surprizes here"
704
            m = Utils.md5()
705
            m.update("ns-3-module-header-%s" % self.module)
706
            self.uid = m.digest()
707
            return self.uid
708
709
710
# Generates a 'ns3/foo-module.h' header file that includes all public
711
# ns3 headers of a certain module.
712
@TaskGen.feature('ns3moduleheader')
713
@TaskGen.after_method('process_rule')
714
def apply_ns3moduleheader(self):
715
    ## get all of the ns3 headers
716
    ns3_dir_node = self.bld.path.find_or_declare("ns3")
717
    all_headers_inputs = []
718
    found_the_module = False
719
    for ns3headers in self.bld.all_task_gen:
720
        if 'ns3header' in getattr(ns3headers, "features", []):
721
            if ns3headers.module != self.module:
722
                continue
723
            found_the_module = True
724
            for source in sorted(ns3headers.headers):
725
                source = os.path.basename(source)
726
                node = ns3_dir_node.find_or_declare(os.path.basename(source))
727
                if node is None:
728
                    fatal("missing header file %s" % (source,))
729
                all_headers_inputs.append(node)
730
    if not found_the_module:
731
        raise WafError("error finding headers for module %s" % self.module)
732
    if not all_headers_inputs:
733
        return
734
735
    try:
736
        module_obj = self.bld.get_tgen_by_name("ns3-" + self.module)
737
    except WafError: # maybe the module was disabled, and therefore removed
738
        return
739
740
    all_headers_outputs = [ns3_dir_node.find_or_declare("%s-module.h" % self.module)]
741
    task = self.create_task('gen_ns3_module_header')
742
    task.module = self.module
743
    task.mode = getattr(self, "mode", "install")
744
    if task.mode == 'install':
745
        assert module_obj is not None, self.module
746
        self.bld.install_files('${INCLUDEDIR}/%s%s/ns3' % (wutils.APPNAME, wutils.VERSION),
747
                               ns3_dir_node.find_or_declare("%s-module.h" % self.module))
748
        task.set_inputs(all_headers_inputs)
749
        task.set_outputs(all_headers_outputs)
750
        task.module_deps = module_obj.module_deps
751
    else:
752
        task.header_to_remove = all_headers_outputs[0]
(-)a/doc/doxygen.conf (-1 / +2 lines)
 Lines 788-794    Link Here 
788
                         doc/introspected-doxygen.h \
788
                         doc/introspected-doxygen.h \
789
                         examples \
789
                         examples \
790
                         utils \
790
                         utils \
791
                         src
791
                         src \
792
                         contrib
792
793
793
# This tag can be used to specify the character encoding of the source files
794
# This tag can be used to specify the character encoding of the source files
794
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
795
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
(-)a/test.py (+21 lines)
 Lines 46-51    Link Here 
46
#
46
#
47
interesting_config_items = [
47
interesting_config_items = [
48
    "NS3_ENABLED_MODULES",
48
    "NS3_ENABLED_MODULES",
49
    "NS3_ENABLED_CONTRIBUTED_MODULES",
49
    "NS3_MODULE_PATH",
50
    "NS3_MODULE_PATH",
50
    "NSC_ENABLED",
51
    "NSC_ENABLED",
51
    "ENABLE_REAL_TIME",
52
    "ENABLE_REAL_TIME",
 Lines 1153-1158    Link Here 
1153
            example_tests,
1154
            example_tests,
1154
            example_names_original,
1155
            example_names_original,
1155
            python_tests)
1156
            python_tests)
1157
            
1158
    for module in NS3_ENABLED_CONTRIBUTED_MODULES:
1159
        # Remove the "ns3-" from the module name.
1160
        module = module[len("ns3-"):]
1161
1162
        # Set the directories and paths for this example. 
1163
        module_directory     = os.path.join("contrib", module)
1164
        example_directory    = os.path.join(module_directory, "examples")
1165
        examples_to_run_path = os.path.join(module_directory, "test", "examples-to-run.py")
1166
        cpp_executable_dir   = os.path.join(NS3_BUILDDIR, example_directory)
1167
        python_script_dir    = os.path.join(example_directory)
1168
1169
        # Parse this module's file.
1170
        parse_examples_to_run_file(
1171
            examples_to_run_path,
1172
            cpp_executable_dir,
1173
            python_script_dir,
1174
            example_tests,
1175
            example_names_original,
1176
            python_tests)
1156
1177
1157
    #
1178
    #
1158
    # If lots of logging is enabled, we can crash Python when it tries to 
1179
    # If lots of logging is enabled, we can crash Python when it tries to 
(-)a/wscript (-21 / +60 lines)
 Lines 246-252    Link Here 
246
    opt.recurse('src')
246
    opt.recurse('src')
247
    opt.recurse('bindings/python')
247
    opt.recurse('bindings/python')
248
    opt.recurse('src/internet')
248
    opt.recurse('src/internet')
249
249
    opt.recurse('contrib')
250
250
251
def _check_compilation_flag(conf, flag, mode='cxx', linkflags=None):
251
def _check_compilation_flag(conf, flag, mode='cxx', linkflags=None):
252
    """
252
    """
 Lines 457-477    Link Here 
457
    conf.recurse('bindings/python')
457
    conf.recurse('bindings/python')
458
458
459
    conf.recurse('src')
459
    conf.recurse('src')
460
    conf.recurse('contrib')
460
461
461
    # Set the list of enabled modules.
462
    # Set the list of enabled modules.
462
    if Options.options.enable_modules:
463
    if Options.options.enable_modules:
463
        # Use the modules explicitly enabled. 
464
        # Use the modules explicitly enabled. 
464
        conf.env['NS3_ENABLED_MODULES'] = ['ns3-'+mod for mod in
465
        _enabled_mods = []
465
                                           Options.options.enable_modules.split(',')]
466
        _enabled_contrib_mods = []
467
        for mod in Options.options.enable_modules.split(','): 
468
            if mod in conf.env['NS3_MODULES'] and mod.startswith('ns3-'): 
469
                _enabled_mods.append(mod)
470
            elif 'ns3-' + mod in conf.env['NS3_MODULES']: 
471
                _enabled_mods.append('ns3-' + mod)
472
            elif mod in conf.env['NS3_CONTRIBUTED_MODULES'] and mod.startswith('ns3-'): 
473
                _enabled_contrib_mods.append(mod)
474
            elif 'ns3-' + mod in conf.env['NS3_CONTRIBUTED_MODULES']: 
475
                _enabled_contrib_mods.append('ns3-' + mod)
476
        conf.env['NS3_ENABLED_MODULES'] = _enabled_mods
477
        conf.env['NS3_ENABLED_CONTRIBUTED_MODULES'] = _enabled_contrib_mods
478
        
466
    else:
479
    else:
467
        # Use the enabled modules list from the ns3 configuration file.
480
        # Use the enabled modules list from the ns3 configuration file.
468
        if modules_enabled[0] == 'all_modules':
481
        if modules_enabled[0] == 'all_modules':
469
            # Enable all modules if requested.
482
            # Enable all modules if requested.
470
            conf.env['NS3_ENABLED_MODULES'] = conf.env['NS3_MODULES']
483
            conf.env['NS3_ENABLED_MODULES'] = conf.env['NS3_MODULES']
484
            conf.env['NS3_ENABLED_CONTRIBUTED_MODULES'] = conf.env['NS3_CONTRIBUTED_MODULES']
471
        else:
485
        else:
472
            # Enable the modules from the list.
486
            # Enable the modules from the list.
473
            conf.env['NS3_ENABLED_MODULES'] = ['ns3-'+mod for mod in
487
            _enabled_mods = []
474
                                               modules_enabled]
488
            _enabled_contrib_mods = []
489
            for mod in modules_enabled: 
490
                if mod in conf.env['NS3_MODULES'] and mod.startswith('ns3-'): 
491
                    _enabled_mods.append(mod)
492
                elif 'ns3-' + mod in conf.env['NS3_MODULES']: 
493
                    _enabled_mods.append('ns3-' + mod)
494
                elif mod in conf.env['NS3_CONTRIBUTED_MODULES'] and mod.startswith('ns3-'): 
495
                    _enabled_contrib_mods.append(mod)
496
                elif 'ns3-' + mod in conf.env['NS3_CONTRIBUTED_MODULES']: 
497
                    _enabled_contrib_mods.append('ns3-' + mod)
498
            conf.env['NS3_ENABLED_MODULES'] = _enabled_mods
499
            conf.env['NS3_ENABLED_CONTRIBUTED_MODULES'] = _enabled_contrib_mods
475
500
476
    # Add the template module to the list of enabled modules that
501
    # Add the template module to the list of enabled modules that
477
    # should not be built if this is a static build on Darwin.  They
502
    # should not be built if this is a static build on Darwin.  They
 Lines 487-492    Link Here 
487
            conf.env['NS3_ENABLED_MODULES'].remove(not_built_name)
512
            conf.env['NS3_ENABLED_MODULES'].remove(not_built_name)
488
            if not conf.env['NS3_ENABLED_MODULES']:
513
            if not conf.env['NS3_ENABLED_MODULES']:
489
                raise WafError('Exiting because the ' + not_built + ' module can not be built and it was the only one enabled.')
514
                raise WafError('Exiting because the ' + not_built + ' module can not be built and it was the only one enabled.')
515
        elif not_built_name in conf.env['NS3_ENABLED_CONTRIBUTED_MODULES']:
516
            conf.env['NS3_ENABLED_CONTRIBUTED_MODULES'].remove(not_built_name)
490
517
491
    conf.recurse('src/mpi')
518
    conf.recurse('src/mpi')
492
519
 Lines 719-725    Link Here 
719
            return
746
            return
720
747
721
def add_scratch_programs(bld):
748
def add_scratch_programs(bld):
722
    all_modules = [mod[len("ns3-"):] for mod in bld.env['NS3_ENABLED_MODULES']]
749
    all_modules = [mod[len("ns3-"):] for mod in bld.env['NS3_ENABLED_MODULES'] + bld.env['NS3_ENABLED_CONTRIBUTED_MODULES']]
750
723
    try:
751
    try:
724
        for filename in os.listdir("scratch"):
752
        for filename in os.listdir("scratch"):
725
            if filename.startswith('.') or filename == 'CVS':
753
            if filename.startswith('.') or filename == 'CVS':
 Lines 835-852    Link Here 
835
863
836
    # process subfolders from here
864
    # process subfolders from here
837
    bld.recurse('src')
865
    bld.recurse('src')
866
    bld.recurse('contrib')
838
867
839
    # If modules have been enabled, then set lists of enabled modules
868
    # If modules have been enabled, then set lists of enabled modules
840
    # and enabled module test libraries.
869
    # and enabled module test libraries.
841
    if env['NS3_ENABLED_MODULES']:
870
    if env['NS3_ENABLED_MODULES'] or env['NS3_ENABLED_CONTRIBUTED_MODULES']:
871
842
        modules = env['NS3_ENABLED_MODULES']
872
        modules = env['NS3_ENABLED_MODULES']
873
        contribModules = env['NS3_ENABLED_CONTRIBUTED_MODULES']
843
874
844
        # Find out about additional modules that need to be enabled
875
        # Find out about additional modules that need to be enabled
845
        # due to dependency constraints.
876
        # due to dependency constraints.
846
        changed = True
877
        changed = True
847
        while changed:
878
        while changed:
848
            changed = False
879
            changed = False
849
            for module in modules:
880
            for module in modules + contribModules:
850
                module_obj = bld.get_tgen_by_name(module)
881
                module_obj = bld.get_tgen_by_name(module)
851
                if module_obj is None:
882
                if module_obj is None:
852
                    raise ValueError("module %s not found" % module)
883
                    raise ValueError("module %s not found" % module)
 Lines 854-884    Link Here 
854
                for dep in module_obj.use:
885
                for dep in module_obj.use:
855
                    if not dep.startswith('ns3-'):
886
                    if not dep.startswith('ns3-'):
856
                        continue
887
                        continue
857
                    if dep not in modules:
888
                    if dep not in modules and dep not in contribModules:
858
                        modules.append(dep)
889
                        if dep in env['NS3_MODULES']: modules.append(dep)
890
                        elif dep in env['NS3_CONTRIBUTED_MODULES']: contribModules.append(dep)
859
                        changed = True
891
                        changed = True
860
892
861
        env['NS3_ENABLED_MODULES'] = modules
893
        env['NS3_ENABLED_MODULES'] = modules
862
894
895
        env['NS3_ENABLED_CONTRIBUTED_MODULES'] = contribModules
896
863
        # If tests are being built, then set the list of the enabled
897
        # If tests are being built, then set the list of the enabled
864
        # module test libraries.
898
        # module test libraries.
865
        if env['ENABLE_TESTS']:
899
        if env['ENABLE_TESTS']:
866
            for (mod, testlib) in bld.env['NS3_MODULES_WITH_TEST_LIBRARIES']:
900
            for (mod, testlib) in bld.env['NS3_MODULES_WITH_TEST_LIBRARIES']:
867
                if mod in bld.env['NS3_ENABLED_MODULES']:
901
                if mod in bld.env['NS3_ENABLED_MODULES'] or mod in bld.env['NS3_ENABLED_CONTRIBUTED_MODULES']:
868
                    bld.env.append_value('NS3_ENABLED_MODULE_TEST_LIBRARIES', testlib)
902
                    bld.env.append_value('NS3_ENABLED_MODULE_TEST_LIBRARIES', testlib)
869
903
870
    add_examples_programs(bld)
904
    add_examples_programs(bld)
871
    add_scratch_programs(bld)
905
    add_scratch_programs(bld)
872
906
873
    if env['NS3_ENABLED_MODULES']:
907
    if env['NS3_ENABLED_MODULES'] or env['NS3_ENABLED_CONTRIBUTED_MODULES']:
874
        modules = env['NS3_ENABLED_MODULES']
908
        modules = env['NS3_ENABLED_MODULES']
909
        contribModules = env['NS3_ENABLED_CONTRIBUTED_MODULES']
875
910
876
        # Exclude the programs other misc task gens that depend on disabled modules
911
        # Exclude the programs other misc task gens that depend on disabled modules
877
        for obj in list(bld.all_task_gen):
912
        for obj in list(bld.all_task_gen):
878
913
879
            # check for ns3moduleheader_taskgen
914
            # check for ns3moduleheader_taskgen
880
            if 'ns3moduleheader' in getattr(obj, "features", []):
915
            if 'ns3moduleheader' in getattr(obj, "features", []):
881
                if ("ns3-%s" % obj.module) not in modules:
916
                if ("ns3-%s" % obj.module) not in modules and ("ns3-%s" % obj.module) not in contribModules:
882
                    obj.mode = 'remove' # tell it to remove headers instead of installing
917
                    obj.mode = 'remove' # tell it to remove headers instead of installing
883
918
884
            # check for programs
919
            # check for programs
 Lines 886-892    Link Here 
886
                # this is an NS-3 program (bld.create_ns3_program)
921
                # this is an NS-3 program (bld.create_ns3_program)
887
                program_built = True
922
                program_built = True
888
                for dep in obj.ns3_module_dependencies:
923
                for dep in obj.ns3_module_dependencies:
889
                    if dep not in modules: # prog. depends on a module that isn't enabled?
924
                    if dep not in modules and dep not in contribModules: # prog. depends on a module that isn't enabled?
890
                        bld.exclude_taskgen(obj)
925
                        bld.exclude_taskgen(obj)
891
                        program_built = False
926
                        program_built = False
892
                        break
927
                        break
 Lines 907-944    Link Here 
907
                    bld.env.append_value('NS3_RUNNABLE_PROGRAMS', object_relative_path)
942
                    bld.env.append_value('NS3_RUNNABLE_PROGRAMS', object_relative_path)
908
943
909
            # disable the modules themselves
944
            # disable the modules themselves
910
            if hasattr(obj, "is_ns3_module") and obj.name not in modules:
945
            if hasattr(obj, "is_ns3_module") and obj.name not in modules and obj.name not in contribModules:
911
                bld.exclude_taskgen(obj) # kill the module
946
                bld.exclude_taskgen(obj) # kill the module
912
947
913
            # disable the module test libraries
948
            # disable the module test libraries
914
            if hasattr(obj, "is_ns3_module_test_library"):
949
            if hasattr(obj, "is_ns3_module_test_library"):
915
                if not env['ENABLE_TESTS'] or (obj.module_name not in modules):
950
                if not env['ENABLE_TESTS'] or ((obj.module_name not in modules) and (obj.module_name not in contribModules)) :
916
                    bld.exclude_taskgen(obj) # kill the module test library
951
                    bld.exclude_taskgen(obj) # kill the module test library
917
952
918
            # disable the ns3header_taskgen
953
            # disable the ns3header_taskgen
919
            if 'ns3header' in getattr(obj, "features", []):
954
            if 'ns3header' in getattr(obj, "features", []):
920
                if ("ns3-%s" % obj.module) not in modules:
955
                if ("ns3-%s" % obj.module) not in modules and ("ns3-%s" % obj.module) not in contribModules:
921
                    obj.mode = 'remove' # tell it to remove headers instead of installing 
956
                    obj.mode = 'remove' # tell it to remove headers instead of installing 
922
957
923
            # disable the ns3privateheader_taskgen
958
            # disable the ns3privateheader_taskgen
924
            if 'ns3privateheader' in getattr(obj, "features", []):
959
            if 'ns3privateheader' in getattr(obj, "features", []):
925
                if ("ns3-%s" % obj.module) not in modules:
960
                if ("ns3-%s" % obj.module) not in modules and ("ns3-%s" % obj.module) not in contribModules:
961
926
                    obj.mode = 'remove' # tell it to remove headers instead of installing 
962
                    obj.mode = 'remove' # tell it to remove headers instead of installing 
927
963
928
            # disable pcfile taskgens for disabled modules
964
            # disable pcfile taskgens for disabled modules
929
            if 'ns3pcfile' in getattr(obj, "features", []):
965
            if 'ns3pcfile' in getattr(obj, "features", []):
930
                if obj.module not in bld.env.NS3_ENABLED_MODULES:
966
                if obj.module not in bld.env.NS3_ENABLED_MODULES and obj.module not in bld.env.NS3_ENABLED_CONTRIBUTED_MODULES:
931
                    bld.exclude_taskgen(obj)
967
                    bld.exclude_taskgen(obj)
932
968
933
969
934
    if env['NS3_ENABLED_MODULES']:
970
    if env['NS3_ENABLED_MODULES']:
935
        env['NS3_ENABLED_MODULES'] = list(modules)
971
        env['NS3_ENABLED_MODULES'] = list(modules)
936
972
973
    if env['NS3_ENABLED_CONTRIBUTED_MODULES']:
974
        env['NS3_ENABLED_CONTRIBUTED_MODULES'] = list(contribModules)
975
937
    # Determine which scripts will be runnable.
976
    # Determine which scripts will be runnable.
938
    for (script, dependencies) in bld.env['NS3_SCRIPT_DEPENDENCIES']:
977
    for (script, dependencies) in bld.env['NS3_SCRIPT_DEPENDENCIES']:
939
        script_runnable = True
978
        script_runnable = True
940
        for dep in dependencies:
979
        for dep in dependencies:
941
            if dep not in modules:
980
            if dep not in modules and dep not in contribModules:
942
                script_runnable = False
981
                script_runnable = False
943
                break
982
                break
944
983
 Lines 1016-1022    Link Here 
1016
        print()
1055
        print()
1017
        print('Modules built:')
1056
        print('Modules built:')
1018
        names_without_prefix = []
1057
        names_without_prefix = []
1019
        for name in env['NS3_ENABLED_MODULES']:
1058
        for name in env['NS3_ENABLED_MODULES'] + env['NS3_ENABLED_CONTRIBUTED_MODULES']:
1020
            name1 = name[len('ns3-'):]
1059
            name1 = name[len('ns3-'):]
1021
            if name not in env.MODULAR_BINDINGS_MODULES:
1060
            if name not in env.MODULAR_BINDINGS_MODULES:
1022
                name1 += " (no Python)"
1061
                name1 += " (no Python)"

Return to bug 2630