19 REQUIRED_PYGCCXML_VERSION = (0, 9, 5) |
19 REQUIRED_PYGCCXML_VERSION = (0, 9, 5) |
20 |
20 |
21 |
21 |
22 |
22 |
23 from TaskGen import feature, after |
23 from TaskGen import feature, after |
24 import Task, ccroot |
24 import Task |
25 from python import _get_python_variables # this comes from wafadmin/Tools/python.py |
25 #from python import _get_python_variables # this comes from wafadmin/Tools/python.py |
26 |
26 |
27 |
27 if 0: |
28 # Patch a bug in waf-1.5.16's python detection, see |
28 # Patch a bug in waf-1.5.16's python detection, see |
29 # https://www.nsnam.org/bugzilla/show_bug.cgi?id=1250 |
29 # https://www.nsnam.org/bugzilla/show_bug.cgi?id=1250 |
30 import python |
30 import python |
31 python.FRAG_2 = """ |
31 python.FRAG_2 = """ |
32 #include <Python.h> |
32 #include <Python.h> |
33 """ + python.FRAG_2 |
33 """ + python.FRAG_2 |
34 del python |
34 del python |
35 |
35 |
36 |
36 |
37 |
37 |
38 def add_to_python_path(path): |
38 def add_to_python_path(path): |
39 if os.environ.get('PYTHONPATH', ''): |
39 if os.environ.get('PYTHONPATH', ''): |
84 enabled_modules.sort() |
84 enabled_modules.sort() |
85 available_modules = list(conf.env['NS3_MODULES']) |
85 available_modules = list(conf.env['NS3_MODULES']) |
86 available_modules.sort() |
86 available_modules.sort() |
87 all_modules_enabled = (enabled_modules == available_modules) |
87 all_modules_enabled = (enabled_modules == available_modules) |
88 |
88 |
89 conf.check_tool('misc') |
89 conf.check_tool('misc', tooldir=['waf-tools']) |
90 |
90 |
91 if sys.platform == 'cygwin': |
91 if sys.platform == 'cygwin': |
92 conf.report_optional_feature("python", "Python Bindings", False, |
92 conf.report_optional_feature("python", "Python Bindings", False, |
93 "unsupported platform 'cygwin'") |
93 "unsupported platform 'cygwin'") |
94 Logs.warn("Python is not supported in CygWin environment. Try MingW instead.") |
94 Logs.warn("Python is not supported in CygWin environment. Try MingW instead.") |
102 except Configure.ConfigurationError, ex: |
102 except Configure.ConfigurationError, ex: |
103 conf.report_optional_feature("python", "Python Bindings", False, str(ex)) |
103 conf.report_optional_feature("python", "Python Bindings", False, str(ex)) |
104 return |
104 return |
105 |
105 |
106 |
106 |
107 # alternative code to computing PYTHONDIR, that is more correct than the one in waf 1.5.16 |
107 if 0: |
108 if 'PYTHONDIR' in conf.environ: |
108 # alternative code to computing PYTHONDIR, that is more correct than the one in waf 1.5.16 |
109 pydir = conf.environ['PYTHONDIR'] |
109 if 'PYTHONDIR' in conf.environ: |
110 else: |
110 pydir = conf.environ['PYTHONDIR'] |
111 (pydir,) = _get_python_variables(conf.env['PYTHON'], |
111 else: |
112 ["get_python_lib(plat_specific=1, standard_lib=0, prefix=%r)" % conf.env['PREFIX']], |
112 (pydir,) = _get_python_variables(conf.env['PYTHON'], |
113 ['from distutils.sysconfig import get_python_lib']) |
113 ["get_python_lib(plat_specific=1, standard_lib=0, prefix=%r)" % conf.env['PREFIX']], |
114 if hasattr(conf, 'define'): # conf.define is added by the C tool, so may not exist |
114 ['from distutils.sysconfig import get_python_lib']) |
115 conf.define('PYTHONDIR', pydir) |
115 if hasattr(conf, 'define'): # conf.define is added by the C tool, so may not exist |
116 conf.env['PYTHONDIR'] = pydir |
116 conf.define('PYTHONDIR', pydir) |
|
117 conf.env['PYTHONDIR'] = pydir |
117 |
118 |
118 |
119 |
119 # -fvisibility=hidden optimization |
120 # -fvisibility=hidden optimization |
120 if (conf.env['CXX_NAME'] == 'gcc' and [int(x) for x in conf.env['CC_VERSION']] >= [4,0,0] |
121 if (conf.env['CXX_NAME'] == 'gcc' and [int(x) for x in conf.env['CC_VERSION']] >= [4,0,0] |
121 and conf.check_compilation_flag('-fvisibility=hidden')): |
122 and conf.check_compilation_flag('-fvisibility=hidden')): |
123 conf.env.append_value('CCFLAGS_PYEXT', '-fvisibility=hidden') |
124 conf.env.append_value('CCFLAGS_PYEXT', '-fvisibility=hidden') |
124 |
125 |
125 # Check for the location of pybindgen |
126 # Check for the location of pybindgen |
126 if Options.options.with_pybindgen is not None: |
127 if Options.options.with_pybindgen is not None: |
127 if os.path.isdir(Options.options.with_pybindgen): |
128 if os.path.isdir(Options.options.with_pybindgen): |
128 conf.check_message("pybindgen location", '', True, ("%s (given)" % Options.options.with_pybindgen)) |
129 conf.msg("Checking for pybindgen location", ("%s (given)" % Options.options.with_pybindgen)) |
129 conf.env['WITH_PYBINDGEN'] = os.path.abspath(Options.options.with_pybindgen) |
130 conf.env['WITH_PYBINDGEN'] = os.path.abspath(Options.options.with_pybindgen) |
130 else: |
131 else: |
131 # ns-3-dev uses ../pybindgen, while ns-3 releases use ../REQUIRED_PYBINDGEN_VERSION |
132 # ns-3-dev uses ../pybindgen, while ns-3 releases use ../REQUIRED_PYBINDGEN_VERSION |
132 pybindgen_dir = os.path.join('..', "pybindgen") |
133 pybindgen_dir = os.path.join('..', "pybindgen") |
133 pybindgen_release_str = "pybindgen-" + '.'.join([str(x) for x in REQUIRED_PYBINDGEN_VERSION]) |
134 pybindgen_release_str = "pybindgen-" + '.'.join([str(x) for x in REQUIRED_PYBINDGEN_VERSION]) |
134 pybindgen_release_dir = os.path.join('..', pybindgen_release_str) |
135 pybindgen_release_dir = os.path.join('..', pybindgen_release_str) |
135 if os.path.isdir(pybindgen_dir): |
136 if os.path.isdir(pybindgen_dir): |
136 conf.check_message("pybindgen location", '', True, ("%s (guessed)" % pybindgen_dir)) |
137 conf.msg("Checking for pybindgen location", ("%s (guessed)" % pybindgen_dir)) |
137 conf.env['WITH_PYBINDGEN'] = os.path.abspath(pybindgen_dir) |
138 conf.env['WITH_PYBINDGEN'] = os.path.abspath(pybindgen_dir) |
138 elif os.path.isdir(pybindgen_release_dir): |
139 elif os.path.isdir(pybindgen_release_dir): |
139 conf.check_message("pybindgen location", '', True, ("%s (guessed)" % pybindgen_release_dir)) |
140 conf.msg("Checking for pybindgen location", ("%s (guessed)" % pybindgen_release_dir)) |
140 conf.env['WITH_PYBINDGEN'] = os.path.abspath(pybindgen_release_dir) |
141 conf.env['WITH_PYBINDGEN'] = os.path.abspath(pybindgen_release_dir) |
141 del pybindgen_dir |
142 del pybindgen_dir |
142 del pybindgen_release_dir |
143 del pybindgen_release_dir |
143 if not conf.env['WITH_PYBINDGEN']: |
144 if not conf.env['WITH_PYBINDGEN']: |
144 conf.check_message("pybindgen location", '', False) |
145 conf.msg("pybindgen location", False) |
145 |
146 |
146 # Check for pybindgen |
147 # Check for pybindgen |
147 |
148 |
148 set_pybindgen_pythonpath(conf.env) |
149 set_pybindgen_pythonpath(conf.env) |
149 |
150 |
153 Logs.warn("pybindgen missing => no python bindings") |
154 Logs.warn("pybindgen missing => no python bindings") |
154 conf.report_optional_feature("python", "Python Bindings", False, |
155 conf.report_optional_feature("python", "Python Bindings", False, |
155 "PyBindGen missing") |
156 "PyBindGen missing") |
156 return |
157 return |
157 else: |
158 else: |
158 out = subprocess.Popen([conf.env['PYTHON'], "-c", |
159 out = subprocess.Popen([conf.env['PYTHON'][0], "-c", |
159 "import pybindgen.version; " |
160 "import pybindgen.version; " |
160 "print '.'.join([str(x) for x in pybindgen.version.__version__])"], |
161 "print '.'.join([str(x) for x in pybindgen.version.__version__])"], |
161 stdout=subprocess.PIPE).communicate()[0] |
162 stdout=subprocess.PIPE).communicate()[0] |
162 pybindgen_version_str = out.strip() |
163 pybindgen_version_str = out.strip() |
163 pybindgen_version = tuple([int(x) for x in pybindgen_version_str.split('.')]) |
164 pybindgen_version = tuple([int(x) for x in pybindgen_version_str.split('.')]) |
164 conf.check_message('pybindgen', 'version', |
165 conf.msg('Checking for pybindgen version', pybindgen_version_str) |
165 (pybindgen_version == REQUIRED_PYBINDGEN_VERSION), |
|
166 pybindgen_version_str) |
|
167 if not (pybindgen_version == REQUIRED_PYBINDGEN_VERSION): |
166 if not (pybindgen_version == REQUIRED_PYBINDGEN_VERSION): |
168 Logs.warn("pybindgen (found %s), (need %s)" % |
167 Logs.warn("pybindgen (found %s), (need %s)" % |
169 (pybindgen_version_str, |
168 (pybindgen_version_str, |
170 '.'.join([str(x) for x in REQUIRED_PYBINDGEN_VERSION]))) |
169 '.'.join([str(x) for x in REQUIRED_PYBINDGEN_VERSION]))) |
171 conf.report_optional_feature("python", "Python Bindings", False, |
170 conf.report_optional_feature("python", "Python Bindings", False, |
186 ''' % dict(type1=t1, type2=t2) |
185 ''' % dict(type1=t1, type2=t2) |
187 |
186 |
188 try: |
187 try: |
189 ret = conf.run_c_code(code=test_program, |
188 ret = conf.run_c_code(code=test_program, |
190 env=conf.env.copy(), compile_filename='test.cc', |
189 env=conf.env.copy(), compile_filename='test.cc', |
191 compile_mode='cxx',type='cprogram', execute=False) |
190 features='cxx cprogram', execute=False) |
192 except Configure.ConfigurationError: |
191 except Configure.ConfigurationError: |
193 ret = 1 |
192 ret = 1 |
194 conf.check_message_custom('types %s and %s' % (t1, t2), 'equivalency', (ret and 'no' or 'yes')) |
193 conf.msg('Checking for types %s and %s equivalence' % (t1, t2), (ret and 'no' or 'yes')) |
195 return not ret |
194 return not ret |
196 |
195 |
197 uint64_is_long = test("uint64_t", "unsigned long") |
196 uint64_is_long = test("uint64_t", "unsigned long") |
198 uint64_is_long_long = test("uint64_t", "unsigned long long") |
197 uint64_is_long_long = test("uint64_t", "unsigned long long") |
199 |
198 |
206 if conf.env['PYTHON_BINDINGS_APIDEFS'] is None: |
205 if conf.env['PYTHON_BINDINGS_APIDEFS'] is None: |
207 msg = 'none available' |
206 msg = 'none available' |
208 else: |
207 else: |
209 msg = conf.env['PYTHON_BINDINGS_APIDEFS'] |
208 msg = conf.env['PYTHON_BINDINGS_APIDEFS'] |
210 |
209 |
211 conf.check_message_custom('the apidefs that can be used for Python bindings', '', msg) |
210 conf.msg('Checking for the apidefs that can be used for Python bindings', msg) |
212 |
211 |
213 if conf.env['PYTHON_BINDINGS_APIDEFS'] is None: |
212 if conf.env['PYTHON_BINDINGS_APIDEFS'] is None: |
214 conf.report_optional_feature("python", "Python Bindings", False, |
213 conf.report_optional_feature("python", "Python Bindings", False, |
215 "No apidefs are available that can be used in this system") |
214 "No apidefs are available that can be used in this system") |
216 return |
215 return |
228 { |
227 { |
229 const abi::__si_class_type_info *_typeinfo __attribute__((unused)) = NULL; |
228 const abi::__si_class_type_info *_typeinfo __attribute__((unused)) = NULL; |
230 return 0; |
229 return 0; |
231 } |
230 } |
232 """ |
231 """ |
233 gcc_rtti_abi = conf.check(fragment=fragment, msg="Checking for internal GCC cxxabi", |
232 gcc_rtti_abi = conf.check_nonfatal(fragment=fragment, msg="Checking for internal GCC cxxabi", |
234 okmsg="complete", errmsg='incomplete', |
233 okmsg="complete", errmsg='incomplete', |
235 mandatory=False) |
234 mandatory=False) |
236 conf.env["GCC_RTTI_ABI_COMPLETE"] = str(bool(gcc_rtti_abi)) |
235 conf.env["GCC_RTTI_ABI_COMPLETE"] = str(bool(gcc_rtti_abi)) |
237 |
236 |
238 |
237 |
239 |
238 |
240 ## Check for pygccxml |
239 ## Check for pygccxml |
243 except Configure.ConfigurationError: |
242 except Configure.ConfigurationError: |
244 conf.report_optional_feature("pygccxml", "Python API Scanning Support", False, |
243 conf.report_optional_feature("pygccxml", "Python API Scanning Support", False, |
245 "Missing 'pygccxml' Python module") |
244 "Missing 'pygccxml' Python module") |
246 return |
245 return |
247 |
246 |
248 out = subprocess.Popen([conf.env['PYTHON'], "-c", |
247 out = subprocess.Popen([conf.env['PYTHON'][0], "-c", |
249 "import pygccxml; print pygccxml.__version__"], |
248 "import pygccxml; print pygccxml.__version__"], |
250 stdout=subprocess.PIPE).communicate()[0] |
249 stdout=subprocess.PIPE).communicate()[0] |
251 pygccxml_version_str = out.strip() |
250 pygccxml_version_str = out.strip() |
252 pygccxml_version = tuple([int(x) for x in pygccxml_version_str.split('.')]) |
251 pygccxml_version = tuple([int(x) for x in pygccxml_version_str.split('.')]) |
253 conf.check_message('pygccxml', 'version', |
252 conf.msg('Checking for pygccxml version', pygccxml_version_str) |
254 (pygccxml_version >= REQUIRED_PYGCCXML_VERSION), |
|
255 pygccxml_version_str) |
|
256 if not (pygccxml_version >= REQUIRED_PYGCCXML_VERSION): |
253 if not (pygccxml_version >= REQUIRED_PYGCCXML_VERSION): |
257 Logs.warn("pygccxml (found %s) is too old (need %s) => " |
254 Logs.warn("pygccxml (found %s) is too old (need %s) => " |
258 "automatic scanning of API definitions will not be possible" % |
255 "automatic scanning of API definitions will not be possible" % |
259 (pygccxml_version_str, |
256 (pygccxml_version_str, |
260 '.'.join([str(x) for x in REQUIRED_PYGCCXML_VERSION]))) |
257 '.'.join([str(x) for x in REQUIRED_PYGCCXML_VERSION]))) |
273 |
270 |
274 gccxml_version_line = os.popen(gccxml + " --version").readline().strip() |
271 gccxml_version_line = os.popen(gccxml + " --version").readline().strip() |
275 m = re.match( "^GCC-XML version (\d\.\d(\.\d)?)$", gccxml_version_line) |
272 m = re.match( "^GCC-XML version (\d\.\d(\.\d)?)$", gccxml_version_line) |
276 gccxml_version = m.group(1) |
273 gccxml_version = m.group(1) |
277 gccxml_version_ok = ([int(s) for s in gccxml_version.split('.')] >= [0, 9]) |
274 gccxml_version_ok = ([int(s) for s in gccxml_version.split('.')] >= [0, 9]) |
278 conf.check_message('gccxml', 'version', True, gccxml_version) |
275 conf.msg('Checking for gccxml version', gccxml_version) |
279 if not gccxml_version_ok: |
276 if not gccxml_version_ok: |
280 Logs.warn("gccxml too old, need version >= 0.9; automatic scanning of API definitions will not be possible") |
277 Logs.warn("gccxml too old, need version >= 0.9; automatic scanning of API definitions will not be possible") |
281 conf.report_optional_feature("pygccxml", "Python API Scanning Support", False, |
278 conf.report_optional_feature("pygccxml", "Python API Scanning Support", False, |
282 "gccxml too old") |
279 "gccxml too old") |
283 return |
280 return |
309 return ns3headers.path.abspath() |
306 return ns3headers.path.abspath() |
310 |
307 |
311 class apiscan_task(Task.TaskBase): |
308 class apiscan_task(Task.TaskBase): |
312 """Uses gccxml to scan the file 'everything.h' and extract API definitions. |
309 """Uses gccxml to scan the file 'everything.h' and extract API definitions. |
313 """ |
310 """ |
314 after = 'gen_ns3_module_header_task ns3header_task' |
311 after = 'gen_ns3_module_header ns3header' |
315 before = 'cc cxx gchx' |
312 before = 'cc cxx gchx' |
316 color = "BLUE" |
313 color = "BLUE" |
317 def __init__(self, curdirnode, env, bld, target, cflags, module): |
314 def __init__(self, curdirnode, env, bld, target, cflags, module): |
318 self.bld = bld |
315 self.bld = bld |
319 super(apiscan_task, self).__init__(generator=self) |
316 super(apiscan_task, self).__init__(generator=self) |
376 retval[module_name] = (list(module.module_deps), headers) |
373 retval[module_name] = (list(module.module_deps), headers) |
377 return retval |
374 return retval |
378 |
375 |
379 |
376 |
380 |
377 |
381 class python_scan_task(Task.TaskBase): |
|
382 """Uses gccxml to scan the file 'everything.h' and extract API definitions. |
|
383 """ |
|
384 after = 'gen_everything_h_task' |
|
385 before = 'cc cxx gchx' |
|
386 color = "BLUE" |
|
387 def __init__(self, curdirnode, env, bld, target, cflags): |
|
388 self.bld = bld |
|
389 super(python_scan_task, self).__init__(generator=self) |
|
390 self.curdirnode = curdirnode |
|
391 self.env = env |
|
392 self.target = target |
|
393 self.cflags = cflags |
|
394 |
|
395 def display(self): |
|
396 return 'python-scan-%s\n' % (self.target,) |
|
397 |
|
398 def run(self): |
|
399 defsdir = os.path.join(self.curdirnode.abspath(), 'apidefs', self.target) |
|
400 try: |
|
401 os.mkdir(defsdir) |
|
402 except OSError: |
|
403 pass |
|
404 argv = [ |
|
405 self.env['PYTHON'], |
|
406 os.path.join(self.curdirnode.abspath(), 'ns3modulescan.py'), # scanning script |
|
407 self.curdirnode.find_dir('../..').abspath(self.env), # include path (where the ns3 include dir is) |
|
408 self.curdirnode.find_or_declare('everything.h').abspath(self.env), |
|
409 os.path.join(defsdir, 'ns3modulegen_generated.py'), # output file |
|
410 self.cflags, |
|
411 ] |
|
412 scan = subprocess.Popen(argv, stdin=subprocess.PIPE) |
|
413 print >> scan.stdin, repr(get_modules_and_headers(self.bld)) |
|
414 scan.stdin.close() |
|
415 retval = scan.wait() |
|
416 return retval |
|
417 |
378 |
418 class python_scan_task_collector(Task.TaskBase): |
379 class python_scan_task_collector(Task.TaskBase): |
419 """Tasks that waits for the python-scan-* tasks to complete and then signals WAF to exit |
380 """Tasks that waits for the python-scan-* tasks to complete and then signals WAF to exit |
420 """ |
381 """ |
421 after = 'python_scan_task apiscan_task' |
382 after = 'apiscan' |
422 before = 'cc cxx' |
383 before = 'cc cxx' |
423 color = "BLUE" |
384 color = "BLUE" |
424 def __init__(self, curdirnode, env, bld): |
385 def __init__(self, curdirnode, env, bld): |
425 self.bld = bld |
386 self.bld = bld |
426 super(python_scan_task_collector, self).__init__(generator=self) |
387 super(python_scan_task_collector, self).__init__(generator=self) |
510 python_scan_task_collector(bld.path, env, bld) |
471 python_scan_task_collector(bld.path, env, bld) |
511 return |
472 return |
512 |
473 |
513 |
474 |
514 if env['ENABLE_PYTHON_BINDINGS']: |
475 if env['ENABLE_PYTHON_BINDINGS']: |
515 task = gen_ns3_compat_pymod_task(env) |
476 task = gen_ns3_compat_pymod_task(env=env) |
516 task.set_outputs(bld.path.find_or_declare("ns3.py")) |
477 task.set_outputs(bld.path.find_or_declare("ns3.py")) |
517 task.dep_vars = ['PYTHON_MODULES_BUILT'] |
478 task.dep_vars = ['PYTHON_MODULES_BUILT'] |
518 |
479 |
519 # note: the actual build commands for the python bindings are in |
480 # note: the actual build commands for the python bindings are in |
520 # src/wscript, not here. |
481 # src/wscript, not here. |