|
1 ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- |
|
2 |
|
3 import waflib |
|
4 |
|
5 def options(opt): |
|
6 opt.tool_options('compiler_cc') |
|
7 opt.tool_options('compiler_cxx') |
|
8 opt.add_option('--enable-static', |
|
9 help=('Compile module statically: works only on linux, without python'), |
|
10 dest='enable_static', action='store_true', |
|
11 default=False) |
|
12 opt.add_option('--disable-log', |
|
13 help=('Do not compile into the code the log output instructions.'), |
|
14 dest='enable_log', action='store_false', |
|
15 default=True) |
|
16 opt.add_option('--disable-assert', |
|
17 help='Do not compile into the code the assert checks.', |
|
18 dest='enable_assert', action='store_false', |
|
19 default=True) |
|
20 opt.add_option('--enable-gcov', |
|
21 help='Enable code coverage collection.', |
|
22 dest='enable_gcov', action='store_true', |
|
23 default=False) |
|
24 opt.add_option('--disable-examples', help='Disable compilation of examples', |
|
25 dest='enable_examples', action='store_false', |
|
26 default=True) |
|
27 opt.add_option('--disable-tests', help='Disable compilation of tests', |
|
28 dest='enable_tests', action='store_false', |
|
29 default=True) |
|
30 opt.add_option('--disable-debug', help='Disable generation of debug information', |
|
31 dest='enable_debug', action='store_false', |
|
32 default=True) |
|
33 |
|
34 def _report_optional_feature(conf, name, caption, was_enabled, reason_not_enabled): |
|
35 if not 'NS3_OPTIONAL_FEATURES' in conf.env: |
|
36 conf.env['NS3_OPTIONAL_FEATURES'] = [] |
|
37 conf.env['NS3_OPTIONAL_FEATURES'].append((name, caption, was_enabled, reason_not_enabled)) |
|
38 |
|
39 |
|
40 def _check_compilation_flag(conf, flag, mode='cxx'): |
|
41 """ |
|
42 Checks if the C++ compiler accepts a certain compilation flag or flags |
|
43 flag: can be a string or a list of strings |
|
44 """ |
|
45 try: |
|
46 if mode == 'cxx': |
|
47 conf.check_cc(fragment='#include <stdio.h>\nint main() { return 0; }\n', |
|
48 cflags=flag, |
|
49 execute = False, msg = "Checking for %s" % flag) |
|
50 else: |
|
51 conf.check_cxx(fragment='#include <stdio.h>\nint main() { return 0; }\n', |
|
52 cxxflags=flag, |
|
53 execute = False, msg = "Checking for %s" % flag) |
|
54 |
|
55 except conf.errors.ConfigurationError: |
|
56 ok = False |
|
57 else: |
|
58 ok = True |
|
59 return ok |
|
60 |
|
61 |
|
62 def _print_optional_features(conf): |
|
63 # Write a summary of optional features status |
|
64 print "---- Summary of optional NS-3 features:" |
|
65 for (name, caption, was_enabled, reason_not_enabled) in conf.env['NS3_OPTIONAL_FEATURES']: |
|
66 if was_enabled: |
|
67 status = 'enabled' |
|
68 else: |
|
69 status = 'not enabled (%s)' % reason_not_enabled |
|
70 print "%-30s: %s" % (caption, status) |
|
71 |
|
72 def _check_static(conf): |
|
73 import Options |
|
74 import sys |
|
75 import re |
|
76 import os |
|
77 env = conf.env |
|
78 env['NS3_ENABLE_STATIC'] = False |
|
79 if Options.options.enable_static: |
|
80 if sys.platform.startswith('linux') and \ |
|
81 env['CXX_NAME'] in ['gcc', 'icc']: |
|
82 if re.match('i[3-6]86', os.uname()[4]): |
|
83 _report_optional_feature(conf, "static", "Static build", True, '') |
|
84 env['NS3_ENABLE_STATIC'] = True |
|
85 elif os.uname()[4] == 'x86_64': |
|
86 if env['NS3_ENABLE_PYTHON_BINDINGS'] and \ |
|
87 not _check_compilation_flag(conf, '-mcmodel=large'): |
|
88 _report_optional_feature(conf, "static", "Static build", False, |
|
89 "Can't enable static builds because " + \ |
|
90 "no -mcmodel=large compiler " \ |
|
91 "option. Try --disable-python or upgrade your " \ |
|
92 "compiler to at least gcc 4.3.x.") |
|
93 else: |
|
94 _report_optional_feature(conf, "static", "Static build", True, '') |
|
95 env['NS3_ENABLE_STATIC'] = True |
|
96 elif env['CXX_NAME'] == 'gcc' and \ |
|
97 (sys.platform.startswith('darwin') or \ |
|
98 sys.platform.startswith('cygwin')): |
|
99 _report_optional_feature(conf, "static", "Static build", True, '') |
|
100 env['NS3_ENABLE_STATIC'] = True |
|
101 else: |
|
102 _report_optional_feature(conf, "static", "Static build", False, |
|
103 "Unsupported platform") |
|
104 else: |
|
105 _report_optional_feature(conf, "static", "Static build", False, |
|
106 "option --enable-static not selected") |
|
107 # These flags are used for the implicitly dependent modules. |
|
108 if env['NS3_ENABLE_STATIC']: |
|
109 if sys.platform == 'darwin': |
|
110 env['STLIB_MARKER'] = '-Wl,-all_load' |
|
111 else: |
|
112 env['STLIB_MARKER'] = '-Wl,--whole-archive,-Bstatic' |
|
113 env['SHLIB_MARKER'] = '-Wl,-Bdynamic,--no-whole-archive' |
|
114 |
|
115 def _check_win32(conf): |
|
116 import Options |
|
117 import sys |
|
118 import subprocess |
|
119 import os |
|
120 env = conf.env |
|
121 if conf.env['CXX_NAME'] in ['gcc', 'icc']: |
|
122 if sys.platform == 'win32': |
|
123 env.append_value("LINKFLAGS", "-Wl,--enable-runtime-pseudo-reloc") |
|
124 elif sys.platform == 'cygwin': |
|
125 env.append_value("LINKFLAGS", "-Wl,--enable-auto-import") |
|
126 |
|
127 cxx, = env['CXX'] |
|
128 |
|
129 p = subprocess.Popen([cxx, '-print-file-name=libstdc++.so'], stdout=subprocess.PIPE) |
|
130 libstdcxx_location = os.path.dirname(p.stdout.read().strip()) |
|
131 p.wait() |
|
132 if libstdcxx_location: |
|
133 conf.env.append_value('NS3_MODULE_PATH', libstdcxx_location) |
|
134 |
|
135 if Options.platform in ['linux']: |
|
136 if _check_compilation_flag(conf, '-Wl,--soname=foo'): |
|
137 env['WL_SONAME_SUPPORTED'] = True |
|
138 |
|
139 |
|
140 def _check_dependencies(conf, required, mandatory): |
|
141 found = [] |
|
142 for module in required: |
|
143 retval = conf.check_cfg(package = 'libns3-%s' % module.lower(), |
|
144 args='--cflags --libs', mandatory=mandatory, |
|
145 msg="Checking for ns3-%s" % module.lower(), |
|
146 uselib_store='NS3_%s' % module.upper()) |
|
147 if not retval is None: |
|
148 found.append(module) |
|
149 import copy |
|
150 if not 'NS3_MODULES_FOUND' in conf.env: |
|
151 conf.env['NS3_MODULES_FOUND'] = [] |
|
152 conf.env['NS3_MODULES_FOUND'] = conf.env['NS3_MODULES_FOUND'] + copy.copy(found) |
|
153 |
|
154 def modules_uselib(bld, names): |
|
155 return ['NS3_%s' % name.upper() for name in names] + \ |
|
156 ['NS3_LIBRARY_%s' % name.upper() for name in names] + \ |
|
157 ['NS3_HEADERS_%s' % name.upper() for name in names] |
|
158 |
|
159 def modules_found(bld, needed): |
|
160 for module in needed: |
|
161 if not module in bld.env['NS3_MODULES_FOUND']: |
|
162 return False |
|
163 return True |
|
164 |
|
165 def _c_libname(bld, name): |
|
166 libname = 'ns3-' + name |
|
167 if bld.env['NS3_ENABLE_STATIC']: |
|
168 return bld.env['cstlib_PATTERN'] % libname |
|
169 else: |
|
170 return bld.env['cshlib_PATTERN'] % libname |
|
171 |
|
172 def check_modules(conf, modules, mandatory = True): |
|
173 import Options |
|
174 import os |
|
175 |
|
176 if not 'NS3_CHECK_MODULE_ONCE' in conf.env: |
|
177 conf.env['NS3_CHECK_MODULE_ONCE'] = '' |
|
178 conf.check_tool('compiler_cc') |
|
179 conf.check_tool('compiler_cxx') |
|
180 conf.check_cfg(atleast_pkgconfig_version='0.0.0') |
|
181 _check_win32(conf) |
|
182 _check_static(conf) |
|
183 if Options.options.enable_log: |
|
184 _report_optional_feature(conf, "log", "Logging", True, '') |
|
185 conf.env.append_value('DEFINES', 'NS3_LOG_ENABLE') |
|
186 else: |
|
187 _report_optional_feature(conf, "log", "Logging", False, |
|
188 'option --disable-log selected') |
|
189 if Options.options.enable_assert: |
|
190 _report_optional_feature(conf, "assert", "Assert checks", True, '') |
|
191 conf.env.append_value('DEFINES', 'NS3_ASSERT_ENABLE') |
|
192 else: |
|
193 _report_optional_feature(conf, "assert", "Assert checks", False, |
|
194 'option --disable-assert selected') |
|
195 if Options.options.enable_gcov: |
|
196 _report_optional_feature(conf, "coverage", "Code coverage", True, '') |
|
197 conf.env.append_value('CFLAGS', '-fprofile-arcs') |
|
198 conf.env.append_value('CFLAGS', '-ftest-coverage') |
|
199 conf.env.append_value('CXXFLAGS', '-fprofile-arcs') |
|
200 conf.env.append_value('CXXFLAGS', '-ftest-coverage') |
|
201 conf.env.append_value('LINKFLAGS', '-fprofile-arcs') |
|
202 else: |
|
203 _report_optional_feature(conf, "coverage", "Code coverage", False, |
|
204 'option --enable-gcov not selected') |
|
205 if Options.options.enable_examples: |
|
206 _report_optional_feature(conf, "examples", "Example programs", True, '') |
|
207 conf.env['NS3_ENABLE_EXAMPLES'] = True |
|
208 else: |
|
209 _report_optional_feature(conf, "examples", "Example programs", False, |
|
210 'option --disable-examples selected') |
|
211 conf.env['NS3_ENABLE_EXAMPLES'] = False |
|
212 |
|
213 if Options.options.enable_tests: |
|
214 _report_optional_feature(conf, "tests", "Test programs", True, '') |
|
215 conf.env['NS3_ENABLE_TESTS'] = True |
|
216 else: |
|
217 _report_optional_feature(conf, "tests", "Test programs", False, |
|
218 'option --disable-tests selected') |
|
219 conf.env['NS3_ENABLE_TESTS'] = False |
|
220 |
|
221 if Options.options.enable_debug: |
|
222 if 'CXXFLAGS' in conf.env: |
|
223 tmp = conf.env['CXXFLAGS'] |
|
224 else: |
|
225 tmp = [] |
|
226 conf.env['CXXFLAGS'] = tmp + ['-g'] |
|
227 if 'CFLAGS' in conf.env: |
|
228 tmp = conf.env['CFLAGS'] |
|
229 else: |
|
230 tmp = [] |
|
231 conf.env['CFLAGS'] = tmp + ['-g'] |
|
232 _report_optional_feature(conf, "debug", "Debug Symbols", True, '') |
|
233 else: |
|
234 _report_optional_feature(conf, "debug", "Debug Symbols", False, |
|
235 'option --disable-debug selected') |
|
236 |
|
237 _check_dependencies(conf, modules, mandatory) |
|
238 |
|
239 def print_feature_summary(conf): |
|
240 _print_optional_features(conf) |
|
241 |
|
242 def _dirs(source): |
|
243 import os |
|
244 dirs = [os.path.dirname(s) for s in source] |
|
245 def uniq(l): |
|
246 d = dict() |
|
247 for i in l: |
|
248 d[i] = True |
|
249 return d.keys() |
|
250 return uniq(dirs) |
|
251 |
|
252 def _build_library(bld, name, *k, **kw): |
|
253 import os |
|
254 source = kw.get('source') |
|
255 if source is None: |
|
256 return |
|
257 cxxflags = [] |
|
258 cflags = [] |
|
259 linkflags = [] |
|
260 ccdefines = ['NS3_MODULE_COMPILATION'] |
|
261 cxxdefines = ['NS3_MODULE_COMPILATION'] |
|
262 includes = _dirs(source) |
|
263 target = os.path.join('lib', 'ns3-%s' % name) |
|
264 if not bld.env['NS3_ENABLE_STATIC']: |
|
265 if bld.env['CXX_NAME'] in ['gcc', 'icc'] and bld.env['WL_SONAME_SUPPORTED']: |
|
266 linkflags.append('-Wl,--soname=%s' % _c_libname(bld, name)) |
|
267 pass |
|
268 elif bld.env['CXX_NAME'] in ['gcc', 'icc'] and \ |
|
269 os.uname()[4] == 'x86_64' and \ |
|
270 bld.env['NS3_ENABLE_PYTHON_BINDINGS']: |
|
271 # enable that flag for static builds only on x86-64 platforms |
|
272 # when gcc is present and only when we want python bindings |
|
273 # (it's more efficient to not use this option if we can avoid it) |
|
274 cxxflags.append('-mcmodel=large') |
|
275 cflags.append('-mcmodel=large') |
|
276 if bld.env['NS3_ENABLE_STATIC']: |
|
277 lib_type = 'stlib' |
|
278 else: |
|
279 lib_type = 'shlib' |
|
280 features = waflib.Tools.c_aliases.sniff_features(source=source, _type=lib_type) |
|
281 kw['features'] = features |
|
282 kw['target'] = target |
|
283 kw['cxxflags'] = kw.get('cxxflags', []) + cxxflags |
|
284 kw['cflags'] = kw.get('cflags', []) + cflags |
|
285 kw['linkflags'] = kw.get('linkflags', []) + linkflags |
|
286 kw['ccdefines'] = kw.get('ccdefines', []) + ccdefines |
|
287 kw['cxxdefines'] = kw.get('cxxdefines', []) + cxxdefines |
|
288 kw['includes'] = kw.get('includes', []) + includes |
|
289 bld(*k, **kw) |
|
290 bld(name='NS3_LIBRARY_%s' % name.upper(), use=[target]) |
|
291 |
|
292 |
|
293 def _build_headers(bld, name, headers): |
|
294 if headers is None: |
|
295 return |
|
296 import os |
|
297 import shutil |
|
298 def run(task): |
|
299 out_dir = os.path.dirname(task.outputs[0].abspath()) |
|
300 for header in task.inputs: |
|
301 dst = os.path.join(out_dir, os.path.basename(header.abspath())) |
|
302 src = header.abspath() |
|
303 shutil.copyfile(src, dst) |
|
304 |
|
305 outfile = file(task.outputs[0].abspath(), "w") |
|
306 |
|
307 print >> outfile, """ |
|
308 #ifdef NS3_MODULE_COMPILATION |
|
309 # error "Do not include ns3 module aggregator headers from other modules; these are meant only for end user scripts." |
|
310 #endif |
|
311 |
|
312 #ifndef NS3_MODULE_%s |
|
313 """ % (name.upper().replace('-', '_'),) |
|
314 |
|
315 print >> outfile |
|
316 print >> outfile, "// Module headers:" |
|
317 for header in [src.abspath() for src in task.inputs]: |
|
318 print >> outfile, "#include \"%s\"" % (os.path.basename(header),) |
|
319 |
|
320 print >> outfile, "#endif" |
|
321 |
|
322 outfile.close() |
|
323 target = os.path.join('include', 'ns3', '%s-module.h' % name) |
|
324 bld(rule=run, source=headers, target=target) |
|
325 bld(use=[target], target='NS3_HEADERS_%s' % name.upper(), |
|
326 export_includes=['include']) |
|
327 bld.install_files(os.path.join('${PREFIX}', 'include', 'ns3'), headers + [target]) |
|
328 |
|
329 |
|
330 |
|
331 def _lib(bld, dep): |
|
332 libpath = bld.env['LIBPATH_%s' % dep.upper()] |
|
333 linkflags = bld.env['LINKFLAGS_%s' % dep.upper()] |
|
334 libs = bld.env['LIB_%s' % dep.upper()] |
|
335 retval = [] |
|
336 for path in libpath: |
|
337 retval.append(bld.env['LIBPATH_ST'] % path) |
|
338 retval = retval + linkflags |
|
339 for lib in libs: |
|
340 retval.append(bld.env['LIB_ST'] % lib) |
|
341 return retval |
|
342 |
|
343 def _cflags(bld, dep): |
|
344 return bld.env['CFLAGS_%s' % dep] |
|
345 def _cxxflags(bld, dep): |
|
346 return bld.env['CXXFLAGS_%s' % dep] |
|
347 def _defines(bld, dep): |
|
348 return [bld.env['DEFINES_ST'] % define for define in bld.env['DEFINES_%s' % dep]] |
|
349 def _includes(bld, dep): |
|
350 return [bld.env['CPPPATH_ST'] % include for include in bld.env['INCLUDES_%s' % dep]] |
|
351 |
|
352 def _self_lib(bld, name, libdir): |
|
353 if bld.env['NS3_ENABLE_STATIC']: |
|
354 path_st = 'STLIBPATH_ST' |
|
355 lib_st = 'STLIB_ST' |
|
356 lib_marker = 'STLIB_MARKER' |
|
357 else: |
|
358 path_st = 'LIBPATH_ST' |
|
359 lib_st = 'LIB_ST' |
|
360 lib_marker = 'SHLIB_MARKER' |
|
361 libname = 'ns3-' + name |
|
362 return [bld.env[path_st] % libdir, |
|
363 bld.env[lib_marker], |
|
364 bld.env[lib_st] % libname] |
|
365 |
|
366 def _generate_pcfile(bld, name, use, prefix, outfilename): |
|
367 import os |
|
368 outfile = open(outfilename, 'w') |
|
369 includedir = os.path.join(prefix, 'include') |
|
370 libdir = os.path.join(prefix, 'lib') |
|
371 libs = _self_lib(bld, name, '${libdir}') |
|
372 for dep in use: |
|
373 libs = libs + _lib(bld,dep) |
|
374 cflags = [bld.env['CPPPATH_ST'] % '${includedir}'] |
|
375 for dep in use: |
|
376 cflags = cflags + _cflags(bld, dep) + _cxxflags(bld, dep) + \ |
|
377 _defines(bld, dep) + _includes(bld, dep) |
|
378 print >> outfile, """ |
|
379 prefix=%s |
|
380 libdir=%s |
|
381 includedir=%s |
|
382 |
|
383 Name: libns3-%s |
|
384 Description: ns-3 module %s |
|
385 Version: devel |
|
386 Libs: %s |
|
387 Cflags: %s |
|
388 """ % (prefix, libdir, includedir, |
|
389 name, name, ' '.join(libs), ' '.join(cflags)) |
|
390 outfile.close() |
|
391 |
|
392 def _build_pkgconfig(bld, name, use): |
|
393 import os |
|
394 def run(task): |
|
395 _generate_pcfile(bld, name, use, bld.env['PREFIX'], task.outputs[0].abspath()) |
|
396 return 0 |
|
397 target = os.path.join('lib', 'pkgconfig', 'libns3-%s.pc' % name) |
|
398 bld(rule=run, target=target, always=True) |
|
399 bld.install_files(os.path.join('${PREFIX}', 'lib', 'pkgconfig'), [target]) |
|
400 |
|
401 class Module: |
|
402 def __init__(self, dirs, bld, name): |
|
403 self._source_dirs = dirs |
|
404 self._bld = bld |
|
405 self._name = name |
|
406 def add_tests(self, name = None, **kw): |
|
407 import copy |
|
408 import os |
|
409 import tempfile |
|
410 |
|
411 if not name is None: |
|
412 target='ns3test-%s-%s' % (self._name, name) |
|
413 else: |
|
414 target='ns3test-%s' % self._name |
|
415 target = os.path.join('bin', target) |
|
416 |
|
417 uselib = kw.get('use', []) |
|
418 if not modules_uselib(self._bld, [self._name]) in uselib: |
|
419 uselib = uselib + modules_uselib(self._bld, [self._name]) |
|
420 kw['use'] = uselib |
|
421 kw['includes'] = kw.get('includes', []) + self._source_dirs |
|
422 |
|
423 tmp = self._bld.path.relpath_gen(self._bld.srcnode) |
|
424 objects = [] |
|
425 for src in kw['source']: |
|
426 src_target = '%s_object' % src |
|
427 objects.append(src_target) |
|
428 kw_copy = copy.copy (kw) |
|
429 path = os.path.dirname(os.path.join(tmp, src)) |
|
430 kw_copy['source'] = [src] |
|
431 kw_copy['target'] = src_target |
|
432 kw_copy['defines'] = kw_copy.get('defines', []) + ['NS_TEST_SOURCEDIR=%s' % path] |
|
433 self._bld.objects(**kw_copy) |
|
434 handle, filename = tempfile.mkstemp(suffix='.cc') |
|
435 os.write (handle, """ |
|
436 #include "ns3/test.h" |
|
437 |
|
438 int main (int argc, char *argv[]) |
|
439 { |
|
440 return ns3::TestRunner::Run(argc, argv); |
|
441 } |
|
442 """) |
|
443 os.close(handle) |
|
444 kw['source'] = [os.path.relpath(filename, self._bld.bldnode.abspath())] |
|
445 kw['use'] = uselib + objects |
|
446 kw['target'] = target |
|
447 kw['install_path'] = None |
|
448 self._bld.program(**kw) |
|
449 |
|
450 |
|
451 def create_module(bld, name, *k, **kw): |
|
452 _build_library(bld, name, *k, **kw) |
|
453 _build_headers(bld, name, kw.get('headers')) |
|
454 _build_pkgconfig(bld, name, kw.get('use')) |
|
455 return Module(_dirs(kw.get('source')), bld, name) |
|
456 |
|
457 def build_program(bld, target=None, source = None, use = None): |
|
458 bld.program(source=source, target=target, use=use) |
|
459 |
|
460 def build_example(bld, target=None, source = None, use = None): |
|
461 if bld.env['NS3_ENABLE_EXAMPLES']: |
|
462 bld.program(source=source, target=target, use=use) |
|
463 |
|
464 |
|
465 |