## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- import os, os.path import sys import shutil import types import warnings import TaskGen import Task import Options import Build import Utils #import Constants #import ccroot #ccroot.USE_TOP_LEVEL = True import wutils try: set except NameError: from sets import Set as set # Python 2.3 fallback all_modules = [ 'core', 'network', 'config-store', 'internet', 'propagation', 'point-to-point', 'csma', 'emu', 'bridge', 'tap-bridge', 'virtual-net-device', 'applications', 'nix-vector-routing', 'olsr', 'aodv', 'dsdv', 'click', 'openflow', 'mobility', 'wifi', 'netanim', 'stats', 'uan', 'spectrum', 'mesh', 'test', 'test/ns3tcp', 'test/ns3wifi', 'flow-monitor', 'wimax', 'lte', 'mpi', 'topology-read', 'energy', 'tools', 'visualizer', 'point-to-point-layout', 'csma-layout', 'template', ] def set_options(opt): opt.sub_options('core') opt.sub_options('click') opt.sub_options('openflow') opt.add_option('--enable-rpath', help=("Link programs with rpath" " (normally not needed, see " " --run and --shell; moreover, only works in some" " specific platforms, such as Linux and Solaris)"), action="store_true", dest='enable_rpath', default=False) opt.add_option('--enable-modules', help=("Build only these modules (and dependencies)"), dest='enable_modules') def configure(conf): conf.sub_config('core') conf.sub_config('emu') conf.sub_config('tap-bridge') conf.sub_config('config-store') conf.sub_config('internet') conf.sub_config('netanim') conf.sub_config('test') conf.sub_config('click') conf.sub_config('openflow') conf.sub_config('stats') conf.sub_config('visualizer') blddir = os.path.abspath(os.path.join(conf.bldnode.abspath(), conf.variant)) conf.env.append_value('NS3_MODULE_PATH', blddir) if Options.options.enable_rpath: conf.env.append_value('RPATH', '-Wl,-rpath=%s' % (os.path.join(blddir),)) ## Used to link the 'test-runner' program with all of ns-3 code conf.env['NS3_MODULES'] = ['ns3-' + module.split('/')[-1] for module in all_modules] # class ns3module_taskgen(TaskGen.task_gen): # def __init__(self, *args, **kwargs): # super(ns3module_taskgen, self).__init__(*args, **kwargs) # self.libs = [] # def apply(self): # print "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # static_enabled = False # shared_enabled = True # bld = self.bld # if bld.env['ENABLE_STATIC_NS3']: # static_enabled = True # shared_enabled = False # if bld.env['ENABLE_SHARED_AND_STATIC_NS3']: # static_enabled = True # shared_enabled = True # assert self.name.startswith("ns3-") # name = self.name.split("ns3-")[1] # if static_enabled: # static = self._create_ns3_module(self.bld, name, self.dependencies, True) # self.libs.append(static) # else: # static = None # if shared_enabled: # shared = self._create_ns3_module(self.bld, name, self.dependencies, False) # self.libs.append(shared) # else: # shared = None # if static is not None and shared is None: # static.name = self.name + "--lib" # static.uselib_local = ['ns3-%s--lib' % (dep,) for dep in self.dependencies] # elif shared is not None and static is None: # shared.name = self.name + "--lib" # shared.uselib_local = ['ns3-%s--lib' % (dep,) for dep in self.dependencies] # else: # shared.name = self.name + "--lib" # shared.uselib_local = ['ns3-%s--lib' % (dep,) for dep in self.dependencies] # static.name = self.name + "--static" # static.uselib_local = ['ns3-%s--static' % (dep,) for dep in self.dependencies] # if not self.test: # pcfile = bld.new_task_gen('ns3pcfile') # pcfile.module = self # def _create_ns3_module(self, bld, name, dependencies, static): # # FIXME: env modifications are overwritten by parent caller def create_ns3_module(bld, name, dependencies=(), test=False): static = bool(bld.env.ENABLE_STATIC_NS3) # Create a separate library for this module. if static: module = bld.new_task_gen(features=['cxx', 'cxxstlib']) else: module = bld.new_task_gen(features=['cxx', 'cxxshlib']) linkflags = [] cxxflags = [] ccflags = [] if not static: cxxflags = module.env['shlib_CXXFLAGS'] ccflags = module.env['shlib_CXXFLAGS'] # Turn on the link flags for shared libraries if we have the # proper compiler and platform. if module.env['CXX_NAME'] in ['gcc', 'icc'] and module.env['WL_SONAME_SUPPORTED']: # Get the module library name without any relative paths # at its beginning because all of the libraries will end # up in the same directory. if 0: # TODO FIXME module_library_name = os.path.basename(ccroot.get_target_name(module)) linkflags = '-Wl,--soname=%s' % module_library_name elif module.env['CXX_NAME'] in ['gcc', 'icc'] and \ os.uname()[4] == 'x86_64' and \ sys.platform != 'darwin' and \ module.env['ENABLE_PYTHON_BINDINGS']: # enable that flag for static builds only on x86-64 platforms # when gcc is present and only when we want python bindings # (it's more efficient to not use this option if we can avoid it) cxxflags = ['-mcmodel=large'] ccflags = ['-mcmodel=large'] cxxdefines = ["NS3_MODULE_COMPILATION"] ccdefines = ["NS3_MODULE_COMPILATION"] module.env.append_value('CXXFLAGS', cxxflags) module.env.append_value('CCFLAGS', ccflags) module.env.append_value('LINKFLAGS', linkflags) module.env.append_value('CXXDEFINES', cxxdefines) module.env.append_value('CCDEFINES', ccdefines) module.is_static = static module.vnum = wutils.VNUM # Add the proper path to the module's name. module.target = '%s/ns3-%s' % (bld.srcnode.relpath_gen(module.path), name) # Set the libraries this module depends on. module.module_deps = list(dependencies) module.install_path = "${LIBDIR}" module.bld = bld module.name = "ns3-" + name module.dependencies = dependencies # Initially create an empty value for this because the pcfile # writing task assumes every module has a uselib attribute. module.uselib = '' module.use = ['ns3-' + dep for dep in dependencies] module.test = test module.is_ns3_module = True module.ns3_dir_location = bld.path.relpath_gen(bld.srcnode) module.env.append_value("INCLUDES", '#') if len(module.source) > 0 and hasattr(self, 'ns3_dir_location'): uselib_cpppath = [] for lib in module.uselib.split(): if 'CPPPATH_%s' % lib in module.env: uselib_cpppath.extend(module.env['CPPPATH_%s' % lib]) objects = [] for src in module.source[0:-1]: full_src = os.path.join(self.ns3_dir_location, src) path = os.path.dirname(full_src) target = '%s_object' % src # XXX: calculate the features correctly here. obj = bld (source=[full_src], target=target, features='cxx cc', defines=['NS_TEST_SOURCEDIR="%s"' % path], includes=' '.join(uselib_cpppath), env = module.env) objects.append(target) last = module.source[-1] full_src = os.path.join(self.ns3_dir_location, last) path = os.path.dirname(full_src) module.defines.append('NS_TEST_SOURCEDIR="%s"' % path) module.source = [last] module.add_objects.extend(objects) return module def create_ns3_module_test_library(bld, name): # Create an ns3 module for the test library that depends only on # the module being tested. library_name = name + "-test" library = bld.create_ns3_module(library_name, [name], test = True) # Modify attributes for the test library that are different from a # normal module. del library.is_ns3_module library.is_ns3_module_test_library = True library.module_name = 'ns3-' + name # Add this module and test library to the list. bld.env.append_value('NS3_MODULES_WITH_TEST_LIBRARIES', (library.module_name, library.name)) # Set the include path from the build directory to modules. relative_path_from_build_to_here = bld.path.relpath_gen(bld.bldnode) include_flag = '-I' + relative_path_from_build_to_here library.env.append_value('CXXFLAGS', include_flag) library.env.append_value('CCFLAGS', include_flag) return library def create_obj(bld, *args): warnings.warn("(in %s) Use bld.new_task_gen(...) now, instead of bld.create_obj(...)" % str(bld.path), DeprecationWarning, stacklevel=2) return bld.new_task_gen(*args) def ns3_python_bindings(bld): # this method is called from a module wscript, so remember bld.path is not bindings/python! module_abs_src_path = bld.path.abspath() module = os.path.basename(module_abs_src_path) env = bld.env env.append_value("MODULAR_BINDINGS_MODULES", "ns3-"+module) if not env['ENABLE_PYTHON_BINDINGS']: return bindings_dir = bld.path.find_dir("bindings") if bindings_dir is None or not os.path.exists(bindings_dir.abspath()): warnings.warn("(in %s) Requested to build modular python bindings, but apidefs dir not found " "=> skipped the bindings." % str(bld.path), Warning, stacklevel=2) return if ("ns3-%s" % (module,)) not in env.NS3_ENABLED_MODULES: #print "bindings for module %s which is not enabled, skip" % module return env.append_value('PYTHON_MODULES_BUILT', module) apidefs = env['PYTHON_BINDINGS_APIDEFS'].replace("-", "_") #debug = ('PYBINDGEN_DEBUG' in os.environ) debug = True # XXX source = [bld.srcnode.find_resource('bindings/python/ns3modulegen-modular.py').relpath_gen(bld.path), "bindings/modulegen__%s.py" % apidefs] if bindings_dir.find_resource("modulegen_customizations.py") is not None: source.append("bindings/modulegen_customizations.py") # the local customization file may or not exist if bld.path.find_resource("bindings/modulegen_local.py"): source.append("bindings/modulegen_local.py") module_py_name = module.replace('-', '_') module_target_dir = bld.srcnode.find_dir("bindings/python/ns").relpath_gen(bld.path) # if bindings/.py exists, it becomes the module frontend, and the C extension befomes _ if bld.path.find_resource("bindings/%s.py" % (module_py_name,)) is not None: bld.new_task_gen( features='copy', source=("bindings/%s.py" % (module_py_name,)), target=('%s/%s.py' % (module_target_dir, module_py_name))) extension_name = '_%s' % (module_py_name,) bld.install_files('${PYTHONDIR}/ns', ["bindings/%s.py" % (module_py_name,)]) else: extension_name = module_py_name target = ['bindings/ns3module.cc', 'bindings/ns3module.h', 'bindings/ns3modulegen.log'] #if not debug: # target.append('ns3modulegen.log') argv = ['NS3_ENABLED_FEATURES=${FEATURES}', 'GCC_RTTI_ABI_COMPLETE=${GCC_RTTI_ABI_COMPLETE}', '${PYTHON}'] #if debug: # argv.extend(["-m", "pdb"]) argv.extend(['${SRC[0]}', module_abs_src_path, apidefs, extension_name, '${TGT[0]}']) argv.extend(['2>', '${TGT[2]}']) # 2> ns3modulegen.log features = [] for (name, caption, was_enabled, reason_not_enabled) in env['NS3_OPTIONAL_FEATURES']: if was_enabled: features.append(name) bindgen = bld.new_task_gen(features=['command'], source=source, target=target, command=argv) bindgen.env['FEATURES'] = ','.join(features) bindgen.dep_vars = ['FEATURES', "GCC_RTTI_ABI_COMPLETE"] bindgen.before = 'cxx' bindgen.after = 'gen_ns3_module_header' bindgen.name = "pybindgen(ns3 module %s)" % module # generate the extension module pymod = bld.new_task_gen(features='cxx cxxshlib pyext') pymod.source = ['bindings/ns3module.cc'] pymod.target = '%s/%s' % (module_target_dir, extension_name) pymod.name = 'ns3module_%s' % module pymod.use = ["%s" % mod for mod in pymod.env['NS3_ENABLED_MODULES']] # Should be '"ns3-"+module', but see bug 1117 if pymod.env['ENABLE_STATIC_NS3']: if sys.platform == 'darwin': pymod.env.append_value('LINKFLAGS', '-Wl,-all_load') for mod in pymod.usel: #mod = mod.split("--lib")[0] pymod.env.append_value('LINKFLAGS', '-l' + mod) else: pymod.env.append_value('LINKFLAGS', '-Wl,--whole-archive,-Bstatic') for mod in pymod.use: #mod = mod.split("--lib")[0] pymod.env.append_value('LINKFLAGS', '-l' + mod) pymod.env.append_value('LINKFLAGS', '-Wl,-Bdynamic,--no-whole-archive') defines = list(pymod.env['CXXDEFINES']) defines.extend(['NS_DEPRECATED=', 'NS3_DEPRECATED_H']) if Options.platform == 'win32': try: defines.remove('_DEBUG') # causes undefined symbols on win32 except ValueError: pass pymod.env['CXXDEFINES'] = defines pymod.includes = '# bindings' pymod.install_path = '${PYTHONDIR}/ns' return pymod def build(bld): bld.create_ns3_module = types.MethodType(create_ns3_module, bld) bld.create_ns3_module_test_library = types.MethodType(create_ns3_module_test_library, bld) bld.create_obj = types.MethodType(create_obj, bld) bld.ns3_python_bindings = types.MethodType(ns3_python_bindings, bld) # Remove these modules from the list of all modules. for not_built in bld.env['MODULES_NOT_BUILT']: # XXX Becaue these modules are located in subdirectories of # test, their names in the all_modules list include the extra # relative path "test/". If these modules are moved into the # src directory, then this if block should be removed. if not_built == 'ns3tcp' or not_built == 'ns3wifi': not_built = 'test/' + not_built if not_built in all_modules: all_modules.remove(not_built) bld.add_subdirs(list(all_modules)) for module in all_modules: modheader = bld.new_task_gen(features=['ns3moduleheader']) modheader.module = module.split('/')[-1] class ns3pcfile_task(Task.Task): after = 'cc cxx' def __str__(self): "string to display to the user" tgt_str = ' '.join([a.nice_path(self.env) for a in self.outputs]) return 'pcfile: %s\n' % (tgt_str) def runnable_status(self): return super(ns3pcfile_task, self).runnable_status() def _self_libs(self, env, name, libdir): if env['ENABLE_STATIC_NS3']: path_st = 'STATICLIBPATH_ST' lib_st = 'STATICLIB_ST' lib_marker = 'STATICLIB_MARKER' else: path_st = 'LIBPATH_ST' lib_st = 'LIB_ST' lib_marker = 'SHLIB_MARKER' retval = [env[path_st] % libdir, env[lib_marker], env[lib_st] % name] return retval def _lib(self, env, dep): libpath = env['LIBPATH_%s' % dep] linkflags = env['LINKFLAGS_%s' % dep] libs = env['LIB_%s' % dep] retval = [] for path in libpath: retval.append(env['LIBPATH_ST'] % path) retval = retval + linkflags for lib in libs: retval.append(env['LIB_ST'] % lib) return retval def _listify(self, v): if isinstance(v, list): return v else: return [v] def _cflags(self, dep): flags = self.env['CFLAGS_%s' % dep] return self._listify(flags) def _cxxflags(self, dep): return self._listify(self.env['CXXFLAGS_%s' % dep]) def _defines(self, dep): defines = self.env['CCDEFINES_%s' % dep] + self.env['CXXDEFINES_%s' % dep] return [self.env['CCDEFINES_ST'] % define for define in self.env['CCDEFINES_%s' % dep]] + \ [self.env['CXXDEFINES_ST'] % define for define in self.env['CXXDEFINES_%s' % dep]] def _includes(self, dep): includes = self.env['CPPPATH_%s' % dep] return [self.env['CPPPATH_ST'] % include for include in includes] def _generate_pcfile(self, name, uselib, use, env, outfilename): outfile = open(outfilename, 'w') prefix = env.PREFIX includedir = env.INCLUDEDIR libdir = env.LIBDIR libs = self._self_libs(self.env, name, '${libdir}') for dep in use: libs = libs + self._lib(self.env, dep) for dep in uselib: libs = libs + [self.env['LIB_ST'] % dep] cflags = [self.env['CPPPATH_ST'] % '${includedir}'] for dep in use: cflags = cflags + self._cflags(dep) + self._cxxflags(dep) + \ self._defines(dep) + self._includes(dep) print >> outfile, """ prefix=%s libdir=%s includedir=%s Name: lib%s Description: ns-3 module %s Version: devel Libs: %s Cflags: %s """ % (prefix, libdir, includedir, name, name, ' '.join(libs), ' '.join(cflags)) outfile.close() def run(self): output_filename = self.outputs[0].bldpath(self.env) self._generate_pcfile(self.module.name, self.module.uselib, self.module.use, self.env, output_filename) class ns3pcfile_taskgen(TaskGen.task_gen): def __init__(self, *args, **kwargs): super(ns3pcfile_taskgen, self).__init__(*args, **kwargs) def apply(self): output_filename = 'lib%s.pc' % self.module.name output_node = self.path.find_or_declare(output_filename) assert output_node is not None, str(self) task = self.create_task('ns3pcfile', env=self.env) self.bld.install_files('${LIBDIR}/pkgconfig', output_node) task.set_outputs([output_node]) task.module = self.module # @TaskGen.extension('.h') # class ns3header_taskgen(TaskGen.task_gen): # """A set of NS-3 header files""" # COLOR = 'BLUE' # def __init__(self, *args, **kwargs): # super(ns3header_taskgen, self).__init__(*args, **kwargs) # self.install_path = None # self.sub_dir = None # if not None, header files will be published as ns3/sub_dir/file.h # self.module = None # module name # self.mode = 'install' # self.features.append(features=['ns3header']) # print "YYYYYYYYYYYYYYYYYYYYYYYYYYY", self @TaskGen.feature('ns3header') @TaskGen.after_method('process_rule') def apply_ns3header(self): #for filename in set(self.to_list(self.source)): # src_node = self.path.find_resource(filename) if self.module is None: raise Utils.WafError("'module' missing on ns3headers object %s" % self) ns3_dir_node = self.bld.path.find_dir("ns3") #if self.sub_dir is not None: # ns3_dir_node = ns3_dir_node.find_dir(self.sub_dir) for filename in set(self.to_list(self.source)): src_node = self.path.find_resource(filename) if src_node is None: raise Utils.WafError("source ns3 header file %s not found" % (filename,)) dst_node = ns3_dir_node.find_or_declare(src_node.name) assert dst_node is not None task = self.create_task('ns3header') task.mode = getattr(self, 'mode', 'install') if task.mode == 'install': self.bld.install_files('${PREFIX}/include/ns3', [src_node]) task.set_inputs([src_node]) task.set_outputs([dst_node]) else: task.header_to_remove = dst_node self.headers = set(self.to_list(self.source)) self.source = '' # tell WAF not to process these files further class ns3header_task(Task.Task): before = 'cc cxx gen_ns3_module_header' color = 'BLUE' def __str__(self): "string to display to the user" env = self.env src_str = ' '.join([a.nice_path(env) for a in self.inputs]) tgt_str = ' '.join([a.nice_path(env) for a in self.outputs]) if self.outputs: sep = ' -> ' else: sep = '' if self.mode == 'remove': return 'rm-ns3-header %s\n' % (self.header_to_remove.bldpath(self.env),) return 'install-ns3-header: %s%s%s\n' % (src_str, sep, tgt_str) def runnable_status(self): if self.mode == 'remove': if os.path.exists(self.header_to_remove.bldpath(self.env)): return Task.RUN_ME else: return Task.SKIP_ME else: return super(ns3header_task, self).runnable_status() def run(self): if self.mode == 'install': assert len(self.inputs) == len(self.outputs) inputs = [node.abspath() for node in self.inputs] outputs = [node.abspath() for node in self.outputs] for src, dst in zip(inputs, outputs): try: os.chmod(dst, 0600) except OSError: pass shutil.copy2(src, dst) ## make the headers in builddir read-only, to prevent ## accidental modification os.chmod(dst, 0400) return 0 else: assert len(self.inputs) == 0 assert len(self.outputs) == 0 out_file_name = self.header_to_remove.bldpath(self.env) try: os.unlink(out_file_name) except OSError, ex: if ex.errno != 2: raise return 0 class gen_ns3_module_header_task(Task.Task): before = 'cc cxx' after = 'ns3header' color = 'BLUE' def runnable_status(self): if self.mode == 'remove': if os.path.exists(self.header_to_remove.bldpath(self.env)): return Task.RUN_ME else: return Task.SKIP_ME else: return super(gen_ns3_module_header_task, self).runnable_status() def __str__(self): "string to display to the user" env = self.env src_str = ' '.join([a.nice_path(env) for a in self.inputs]) tgt_str = ' '.join([a.nice_path(env) for a in self.outputs]) if self.outputs: sep = ' -> ' else: sep = '' if self.mode == 'remove': return 'rm-module-header %s\n' % (self.header_to_remove.bldpath(self.env),) return 'gen-module-header: %s%s%s\n' % (src_str, sep, tgt_str) def run(self): if self.mode == 'remove': assert len(self.inputs) == 0 assert len(self.outputs) == 0 out_file_name = self.header_to_remove.bldpath(self.env) try: os.unlink(out_file_name) except OSError, ex: if ex.errno != 2: raise return 0 assert len(self.outputs) == 1 out_file_name = self.outputs[0].get_bld().abspath()#self.env) header_files = [os.path.basename(node.abspath()) for node in self.inputs] outfile = file(out_file_name, "w") header_files.sort() print >> outfile, """ #ifdef NS3_MODULE_COMPILATION # error "Do not include ns3 module aggregator headers from other modules; these are meant only for end user scripts." #endif #ifndef NS3_MODULE_%s """ % (self.module.upper().replace('-', '_'),) # if self.module_deps: # print >> outfile, "// Module dependencies:" # for dep in self.module_deps: # print >> outfile, "#include \"%s-module.h\"" % dep print >> outfile print >> outfile, "// Module headers:" for header in header_files: print >> outfile, "#include \"%s\"" % (header,) print >> outfile, "#endif" outfile.close() return 0 def sig_explicit_deps(self): self.m.update('\n'.join([node.abspath() for node in self.inputs])) return self.m.digest() def unique_id(self): try: return self.uid except AttributeError: "this is not a real hot zone, but we want to avoid surprizes here" m = Utils.md5() m.update("ns-3-module-header-%s" % self.module) self.uid = m.digest() return self.uid # Generates a 'ns3/foo-module.h' header file that includes all public # ns3 headers of a certain module. @TaskGen.feature('ns3moduleheader') @TaskGen.after_method('process_rule') def apply_ns3moduleheader(self): ## get all of the ns3 headers ns3_dir_node = self.bld.path.find_dir("ns3") all_headers_inputs = [] found_the_module = False for ns3headers in self.bld.all_task_gen: if 'ns3header' in getattr(ns3headers, "features", []): if ns3headers.module != self.module: continue found_the_module = True for source in ns3headers.headers: source = os.path.basename(source) node = ns3_dir_node.find_or_declare(os.path.basename(source)) if node is None: fatal("missing header file %s" % (source,)) all_headers_inputs.append(node) if not found_the_module: raise Utils.WscriptError("error finding headers for module %s" % self.module) if not all_headers_inputs: return try: module_obj = self.bld.get_taskgen("ns3-" + self.module) except KeyError: # maybe the module was disabled, and therefore removed return all_headers_outputs = [ns3_dir_node.find_or_declare("%s-module.h" % self.module)] task = self.create_task('gen_ns3_module_header') task.module = self.module task.mode = getattr(self, "mode", "install") if task.mode == 'install': assert module_obj is not None, self.module self.bld.install_files('${PREFIX}/include/ns3', ns3_dir_node.find_or_declare("%s-module.h" % self.module)) task.set_inputs(all_headers_inputs) task.set_outputs(all_headers_outputs) task.module_deps = module_obj.module_deps else: task.header_to_remove = all_headers_outputs[0]