From 681bd950beecd815e7576765673e7c0407406a1a Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 3 Jul 2011 12:01:29 +0100 Subject: [PATCH] Bug 1174 - Ns-3 does not generate static libraries --- src/wscript | 160 ++++++++++++++++++++++++++++++++++++++-------------- wscript | 74 +++++++++++++++++++----- 2 files changed, 177 insertions(+), 57 deletions(-) diff --git a/src/wscript b/src/wscript index 5e646ab9e..220ebabca 100644 --- a/src/wscript +++ b/src/wscript @@ -103,52 +103,126 @@ def configure(conf): conf.env['NS3_MODULES'] = ['ns3-' + module.split('/')[-1] for module in all_modules] -def create_ns3_module(bld, name, dependencies=(), test=False): - # Create a separate library for this module. - if bld.env['ENABLE_STATIC_NS3']: - module = bld.new_task_gen('cxx', 'cstaticlib') - else: - module = bld.new_task_gen('cxx', 'cshlib') - if not test: - pcfile = bld.new_task_gen('ns3pcfile') - pcfile.module = module +class ns3module_taskgen(TaskGen.task_gen): + def __init__(self, *args, **kwargs): + super(ns3module_taskgen, self).__init__(*args, **kwargs) + self.libs = [] + def apply(self): + 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 + + # Create a separate library for this module. + if static: + module = bld.new_task_gen('cxx', 'cstaticlib') + else: + module = bld.new_task_gen('cxx', 'cshlib') + + module.source = self.source + module.env = self.env.copy() + features = list(self.features) + features.remove("ns3module") + module.features.extend(features) + module.path = self.path + module.uselib = self.uselib + if hasattr(self, 'includes'): + module.includes = self.includes + if hasattr(self, "is_ns3_module"): + module.is_ns3_module = self.is_ns3_module + + 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(self.path), name) + # Set the libraries this module depends on. + module.module_deps = list(dependencies) + if not static: + module.env.append_value('CXXFLAGS', module.env['shlib_CXXFLAGS']) + module.env.append_value('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. + module_library_name = os.path.basename(ccroot.get_target_name(module)) + module.env.append_value('LINKFLAGS', '-Wl,--soname=%s' % module_library_name) + elif module.env['CXX_NAME'] in ['gcc', 'icc'] and \ + os.uname()[4] == 'x86_64' 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) + module.env.append_value('CXXFLAGS', '-mcmodel=large') + module.env.append_value('CCFLAGS', '-mcmodel=large') + module.env.append_value('CXXDEFINES', "NS3_MODULE_COMPILATION") + module.env.append_value('CCDEFINES', "NS3_MODULE_COMPILATION") + + module.install_path = "${LIBDIR}" + + return module + + +def create_ns3_module(bld, name, dependencies=(), test=False): + module = bld.new_task_gen('ns3module') + 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.is_ns3_module = True - module.name = 'ns3-' + name - module.vnum = wutils.VNUM - # Add the proper path to the module's name. - module.target = '%s/ns3-%s' % (bld.srcnode.relpath_gen(bld.path), name) - # Set the libraries this module depends on. module.uselib_local = ['ns3-' + dep for dep in dependencies] module.module_deps = list(dependencies) - if not module.env['ENABLE_STATIC_NS3']: - module.env.append_value('CXXFLAGS', module.env['shlib_CXXFLAGS']) - module.env.append_value('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. - module_library_name = os.path.basename(ccroot.get_target_name(module)) - module.env.append_value('LINKFLAGS', '-Wl,--soname=%s' % module_library_name) - elif module.env['CXX_NAME'] in ['gcc', 'icc'] and \ - os.uname()[4] == 'x86_64' 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) - module.env.append_value('CXXFLAGS', '-mcmodel=large') - module.env.append_value('CCFLAGS', '-mcmodel=large') - - module.env.append_value('CXXDEFINES', "NS3_MODULE_COMPILATION") - module.env.append_value('CCDEFINES', "NS3_MODULE_COMPILATION") + module.test = test + module.is_ns3_module = True + 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. @@ -258,15 +332,17 @@ def ns3_python_bindings(bld): pymod.source = ['bindings/ns3module.cc'] pymod.target = '%s/%s' % (module_target_dir, extension_name) pymod.name = 'ns3module_%s' % module - pymod.uselib_local = pymod.env['NS3_ENABLED_MODULES'] # Should be '"ns3-"+module', but see bug 1117 + pymod.uselib_local = ["%s--lib" % 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.uselib_local: + 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.uselib_local: + 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']) @@ -416,7 +492,6 @@ class ns3header_taskgen(TaskGen.task_gen): def apply(self): for filename in set(self.to_list(self.source)): src_node = self.path.find_resource(filename) - self.bld.install_files('${PREFIX}/include/ns3', [src_node]) if self.module is None: raise Utils.WafError("'module' missing on ns3headers object %s" % self) ns3_dir_node = self.bld.path.find_dir("ns3") @@ -431,6 +506,7 @@ class ns3header_taskgen(TaskGen.task_gen): task = self.create_task('ns3header', env=self.env) task.mode = self.mode if self.mode == 'install': + self.bld.install_files('${PREFIX}/include/ns3', [src_node]) task.set_inputs([src_node]) task.set_outputs([dst_node]) else: @@ -598,13 +674,13 @@ class ns3moduleheader_taskgen(TaskGen.task_gen): raise Utils.WscriptError("error finding headers for module %s" % self.module) if not all_headers_inputs: return - self.bld.install_files('${PREFIX}/include/ns3', - ns3_dir_node.find_or_declare("%s-module.h" % self.module)) all_headers_outputs = [ns3_dir_node.find_or_declare("%s-module.h" % self.module)] task = self.create_task('gen_ns3_module_header', env=self.env) task.module = self.module task.mode = self.mode if self.mode == 'install': + 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) module_obj = self.bld.name_to_obj("ns3-" + self.module, self.env) diff --git a/wscript b/wscript index 5ddf22f5a..7665fea9a 100644 --- a/wscript +++ b/wscript @@ -207,6 +207,10 @@ def set_options(opt): help=('Compile NS-3 statically: works only on linux, without python'), dest='enable_static', action='store_true', default=False) + opt.add_option('--enable-shared-and-static', + help=('Compile NS-3 both shared and static libraries at the same time: static works only on linux'), + dest='enable_shared_and_static', action='store_true', + default=False) opt.add_option('--enable-mpi', help=('Compile NS-3 with MPI and distributed simulation support'), dest='enable_mpi', action='store_true', @@ -316,12 +320,15 @@ def configure(conf): env['WL_SONAME_SUPPORTED'] = True env['ENABLE_STATIC_NS3'] = False - if Options.options.enable_static: + if Options.options.enable_static or Options.options.enable_shared_and_static: if env['PLATFORM'].startswith('linux') and \ env['CXX_NAME'] in ['gcc', 'icc']: if re.match('i[3-6]86', os.uname()[4]): conf.report_optional_feature("static", "Static build", True, '') - env['ENABLE_STATIC_NS3'] = True + if Options.options.enable_static: + env['ENABLE_STATIC_NS3'] = True + if Options.options.enable_shared_and_static: + env['ENABLE_SHARED_AND_STATIC_NS3'] = True elif os.uname()[4] == 'x86_64': if env['ENABLE_PYTHON_BINDINGS'] and \ not conf.check_compilation_flag('-mcmodel=large'): @@ -332,12 +339,18 @@ def configure(conf): "compiler to at least gcc 4.3.x.") else: conf.report_optional_feature("static", "Static build", True, '') - env['ENABLE_STATIC_NS3'] = True + if Options.options.enable_static: + env['ENABLE_STATIC_NS3'] = True + if Options.options.enable_shared_and_static: + env['ENABLE_SHARED_AND_STATIC_NS3'] = True elif env['CXX_NAME'] == 'gcc' and \ (env['PLATFORM'].startswith('darwin') or \ env['PLATFORM'].startswith('cygwin')): conf.report_optional_feature("static", "Static build", True, '') - env['ENABLE_STATIC_NS3'] = True + if Options.options.enable_static: + env['ENABLE_STATIC_NS3'] = True + if Options.options.enable_shared_and_static: + env['ENABLE_SHARED_AND_STATIC_NS3'] = True else: conf.report_optional_feature("static", "Static build", False, "Unsupported platform") @@ -558,18 +571,7 @@ def create_ns3_program(bld, name, dependencies=('core',)): program.name = name program.target = program.name # Each of the modules this program depends on has its own library. - program.uselib_local = ['ns3-' + dep for dep in dependencies] program.ns3_module_dependencies = ['ns3-'+dep for dep in dependencies] - if program.env['ENABLE_STATIC_NS3']: - if sys.platform == 'darwin': - program.env.append_value('LINKFLAGS', '-Wl,-all_load') - for dep in dependencies: - program.env.append_value('LINKFLAGS', '-lns3-' + dep) - else: - program.env.append_value('LINKFLAGS', '-Wl,--whole-archive,-Bstatic') - for dep in dependencies: - program.env.append_value('LINKFLAGS', '-lns3-' + dep) - program.env.append_value('LINKFLAGS', '-Wl,-Bdynamic,--no-whole-archive') return program def register_ns3_script(bld, name, dependencies=('core',)): @@ -606,6 +608,22 @@ def add_scratch_programs(bld): obj.name = obj.target +def _add_ns3_program_missing_deps(bld, program): + deps_found = program.ns3_module_dependencies + program.uselib_local = [dep + "--lib" for dep in deps_found] + if program.env['ENABLE_STATIC_NS3'] and not program.env['ENABLE_SHARED_AND_STATIC_NS3']: + if sys.platform == 'darwin': + program.env.append_value('LINKFLAGS', '-Wl,-all_load') + for dep in deps_found: + program.env.append_value('LINKFLAGS', '-l' + dep) + else: + program.env.append_value('LINKFLAGS', '-Wl,--whole-archive,-Bstatic') + for dep in deps_found: + program.env.append_value('LINKFLAGS', '-l' + dep) + program.env.append_value('LINKFLAGS', '-Wl,-Bdynamic,--no-whole-archive') + + + def build(bld): # If --enabled-modules option was given, then print a warning # message and exit this function. @@ -751,6 +769,32 @@ def build(bld): bld.add_subdirs('bindings/python') + ## do a topological sort on the modules graph + dep_graph = [] + for gen in bld.all_task_gen: + if type(gen).__name__ in ['ns3module_taskgen']: + for dep in gen.dependencies: + dep_graph.append(("ns3-"+dep, gen.name)) + dep_graph.sort() + sys.path.insert(0, "bindings/python") + from topsort import topsort + sorted_ns3_modules = topsort(dep_graph) + #print sorted_ns3_modules + + # we need to post() the ns3 modules, so they create libraries underneath, and programs can list them in uselib_local + for module in sorted_ns3_modules: + gen = bld.name_to_obj(module, bld.env) + if type(gen).__name__ in ['ns3module_taskgen']: + gen.post() + for lib in gen.libs: + lib.post() + + for gen in bld.all_task_gen: + if not getattr(gen, "is_ns3_program", False) or not hasattr(gen, "ns3_module_dependencies"): + continue + _add_ns3_program_missing_deps(bld, gen) + + if Options.options.run: # Check that the requested program name is valid program_name, dummy_program_argv = wutils.get_run_program(Options.options.run, wutils.get_command_template(env))