2008-07-08 10:43:58 -07:00
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
2015-09-03 21:14:55 -07:00
from __future__ import print_function
2010-11-07 23:17:52 +00:00
import types
2008-07-08 10:43:58 -07:00
import re
import os
2011-09-08 16:13:40 +01:00
import subprocess
2008-07-08 10:43:58 -07:00
import shutil
2008-09-04 18:45:21 +01:00
import sys
2008-07-08 10:43:58 -07:00
2013-04-01 22:34:50 +02:00
from waflib import Task, Options, Configure, TaskGen, Logs, Build, Utils, Errors
2011-09-13 13:47:17 +01:00
from waflib.Errors import WafError
2013-04-26 11:13:12 -07:00
# feature = TaskGen.feature
# after = TaskGen.after
2013-04-01 22:34:50 +02:00
2015-07-26 14:01:43 +01:00
# https://github.com/gjcarneiro/pybindgen
2020-10-08 10:37:29 -07:00
# If specifying a released pybindgen version, specify the required PYBINDGEN
# version as, e.g. '0.21.0'
# If specifying a commit on the development tree, specify it like this based
# on 'git describe --tags' command. Example, if the latest release was 0.21.0,
2021-03-12 12:21:57 -08:00
# and 'git describe --tags' reports "0.21.0-6-g8e7c0a9", then write the
2020-10-08 10:37:29 -07:00
# PYBINDGEN version string below as '0.21.0.post6+ng8e7c0a9'
2021-03-12 12:21:57 -08:00
REQUIRED_PYBINDGEN_VERSION = '0.21.0.post20+ng71852b1'
2020-10-08 13:32:06 -07:00
REQUIRED_PYGCCXML_VERSION = (2, 0, 1)
2019-08-12 16:41:07 -07:00
REQUIRED_CASTXML_VERSION = '0.2'
2008-07-08 10:43:58 -07:00
2013-11-12 10:37:52 -08:00
RUN_ME=-3
2010-03-17 12:34:52 +00:00
2015-09-09 15:14:27 -07:00
# return types of some APIs differ in Python 2/3 (type string vs class bytes)
# This method will decode('utf-8') a byte object in Python 3,
# and do nothing in Python 2
def maybe_decode(input):
if sys.version_info < (3,):
return input
else:
try:
return input.decode('utf-8')
except:
sys.exc_clear()
return input
2008-11-19 17:32:37 +00:00
def add_to_python_path(path):
if os.environ.get('PYTHONPATH', ''):
os.environ['PYTHONPATH'] = path + os.pathsep + os.environ.get('PYTHONPATH')
else:
os.environ['PYTHONPATH'] = path
def set_pybindgen_pythonpath(env):
if env['WITH_PYBINDGEN']:
add_to_python_path(env['WITH_PYBINDGEN'])
2011-09-12 14:54:27 +01:00
def options(opt):
2013-04-01 22:34:50 +02:00
opt.load('python')
2008-09-06 14:46:01 +01:00
opt.add_option('--disable-python',
2008-07-08 10:43:58 -07:00
help=("Don't build Python bindings."),
action="store_true", default=False,
dest='python_disable')
2010-11-07 23:17:52 +00:00
opt.add_option('--apiscan',
2011-09-18 12:19:14 +01:00
help=("Rescan the API for the indicated module(s), for Python bindings. "
2017-09-18 13:46:16 -07:00
"Needs working CastXML / pygccxml environment. "
2011-03-20 15:41:43 +00:00
"The metamodule 'all' expands to all available ns-3 modules."),
2010-11-07 23:17:52 +00:00
default=None, dest='apiscan', metavar="MODULE[,MODULE...]")
2008-11-19 17:32:37 +00:00
opt.add_option('--with-pybindgen',
help=('Path to an existing pybindgen source tree to use.'),
default=None,
dest='with_pybindgen', type="string")
2011-09-20 18:37:33 +01:00
opt.add_option('--with-python',
help=('Path to the Python interpreter to use.'),
default=None, dest='with_python', type="string")
2008-07-08 10:43:58 -07:00
2016-11-30 20:13:49 -08:00
def split_version(version):
2017-05-25 08:02:44 -07:00
if (re.search ('post', version)):
# Version format such as '0.17.0.post58+ngcf00cc0'
ver = re.split('[.+]', version)[:4]
return (int(ver[0]), int(ver[1]), int(ver[2]), int(ver[3].split('post')[1]))
else:
# Version format such as '0.18.0'
ver = re.split('[.]', version)[:3]
return (int(ver[0]), int(ver[1]), int(ver[2]), 0)
2016-11-30 20:13:49 -08:00
2008-07-08 10:43:58 -07:00
def configure(conf):
conf.env['ENABLE_PYTHON_BINDINGS'] = False
2008-12-29 13:28:54 +00:00
if Options.options.python_disable:
2008-09-05 18:16:29 +01:00
conf.report_optional_feature("python", "Python Bindings", False,
"disabled by user request")
2008-07-08 10:43:58 -07:00
return
2011-08-31 06:23:28 -07:00
# Disable python in static builds (bug #1253)
if ((conf.env['ENABLE_STATIC_NS3']) or \
(conf.env['ENABLE_SHARED_AND_STATIC_NS3'])):
conf.report_optional_feature("python", "Python Bindings", False,
"bindings incompatible with static build")
return
2008-07-08 10:43:58 -07:00
2011-03-20 13:01:40 +00:00
enabled_modules = list(conf.env['NS3_ENABLED_MODULES'])
enabled_modules.sort()
available_modules = list(conf.env['NS3_MODULES'])
available_modules.sort()
all_modules_enabled = (enabled_modules == available_modules)
2013-04-01 22:34:50 +02:00
conf.load('misc', tooldir=['waf-tools'])
2008-07-08 10:43:58 -07:00
2009-09-25 12:15:27 +01:00
if sys.platform == 'cygwin':
conf.report_optional_feature("python", "Python Bindings", False,
"unsupported platform 'cygwin'")
Logs.warn("Python is not supported in CygWin environment. Try MingW instead.")
return
2008-09-04 18:45:21 +01:00
2011-09-20 18:37:33 +01:00
2008-07-08 10:43:58 -07:00
## Check for Python
2011-09-20 18:37:33 +01:00
if Options.options.with_python is not None:
conf.env.PYTHON = Options.options.with_python
2008-07-08 10:43:58 -07:00
try:
2013-04-01 22:34:50 +02:00
conf.load('python')
2015-09-03 21:14:55 -07:00
except Errors.ConfigurationError as ex:
2012-02-20 12:17:26 +00:00
conf.report_optional_feature("python", "Python Bindings", False,
"The python interpreter was not found")
return
try:
2008-07-22 16:54:24 +01:00
conf.check_python_version((2,3))
2015-09-03 21:14:55 -07:00
except Errors.ConfigurationError as ex:
2012-02-20 12:17:26 +00:00
conf.report_optional_feature("python", "Python Bindings", False,
"The python found version is too low (2.3 required)")
return
try:
2008-07-08 10:43:58 -07:00
conf.check_python_headers()
2015-09-03 21:14:55 -07:00
except Errors.ConfigurationError as ex:
2012-02-20 12:17:26 +00:00
conf.report_optional_feature("python", "Python Bindings", False,
"Python library or headers missing")
2008-07-08 10:43:58 -07:00
return
2011-09-13 13:47:17 +01:00
# stupid Mac OSX Python wants to build extensions as "universal
# binaries", i386, x86_64, and ppc, but this way the type
# __uint128_t is not available. We need to disable the multiarch
# crap by removing the -arch parameters.
for flags_var in ["CFLAGS_PYEXT", "CFLAGS_PYEMBED", "CXXFLAGS_PYEMBED",
"CXXFLAGS_PYEXT", "LINKFLAGS_PYEMBED", "LINKFLAGS_PYEXT"]:
flags = conf.env[flags_var]
i = 0
while i < len(flags):
if flags[i] == '-arch':
del flags[i]
del flags[i]
continue
i += 1
conf.env[flags_var] = flags
2011-07-11 14:24:59 +01:00
2009-03-03 18:51:16 +00:00
# -fvisibility=hidden optimization
2009-04-30 10:57:30 +01:00
if (conf.env['CXX_NAME'] == 'gcc' and [int(x) for x in conf.env['CC_VERSION']] >= [4,0,0]
and conf.check_compilation_flag('-fvisibility=hidden')):
2009-03-03 18:51:16 +00:00
conf.env.append_value('CXXFLAGS_PYEXT', '-fvisibility=hidden')
conf.env.append_value('CCFLAGS_PYEXT', '-fvisibility=hidden')
2012-02-24 11:39:28 +00:00
if conf.check_compilation_flag('-Wno-array-bounds'):
conf.env.append_value('CXXFLAGS_PYEXT', '-Wno-array-bounds')
2009-01-24 15:28:54 +00:00
# Check for the location of pybindgen
if Options.options.with_pybindgen is not None:
if os.path.isdir(Options.options.with_pybindgen):
2011-09-08 16:13:40 +01:00
conf.msg("Checking for pybindgen location", ("%s (given)" % Options.options.with_pybindgen))
2009-01-24 15:28:54 +00:00
conf.env['WITH_PYBINDGEN'] = os.path.abspath(Options.options.with_pybindgen)
else:
2010-08-20 12:17:19 -07:00
# ns-3-dev uses ../pybindgen, while ns-3 releases use ../REQUIRED_PYBINDGEN_VERSION
2009-01-24 15:28:54 +00:00
pybindgen_dir = os.path.join('..', "pybindgen")
2015-09-21 10:21:07 -07:00
pybindgen_release_str = "pybindgen-" + REQUIRED_PYBINDGEN_VERSION
2010-08-20 12:17:19 -07:00
pybindgen_release_dir = os.path.join('..', pybindgen_release_str)
2009-01-24 15:28:54 +00:00
if os.path.isdir(pybindgen_dir):
2011-09-08 16:13:40 +01:00
conf.msg("Checking for pybindgen location", ("%s (guessed)" % pybindgen_dir))
2009-01-24 15:28:54 +00:00
conf.env['WITH_PYBINDGEN'] = os.path.abspath(pybindgen_dir)
2010-08-20 12:17:19 -07:00
elif os.path.isdir(pybindgen_release_dir):
2011-09-08 16:13:40 +01:00
conf.msg("Checking for pybindgen location", ("%s (guessed)" % pybindgen_release_dir))
2010-08-20 12:17:19 -07:00
conf.env['WITH_PYBINDGEN'] = os.path.abspath(pybindgen_release_dir)
2009-01-24 15:28:54 +00:00
del pybindgen_dir
2010-08-20 12:17:19 -07:00
del pybindgen_release_dir
2009-01-24 15:28:54 +00:00
if not conf.env['WITH_PYBINDGEN']:
2011-09-19 19:56:58 +01:00
conf.msg("Checking for pybindgen location", False)
2008-11-19 17:32:37 +00:00
2009-01-24 15:28:54 +00:00
# Check for pybindgen
2008-11-19 17:32:37 +00:00
set_pybindgen_pythonpath(conf.env)
2008-07-08 10:43:58 -07:00
try:
conf.check_python_module('pybindgen')
2013-04-01 22:34:50 +02:00
except Errors.ConfigurationError:
2009-01-11 23:26:34 +00:00
Logs.warn("pybindgen missing => no python bindings")
conf.report_optional_feature("python", "Python Bindings", False,
"PyBindGen missing")
2009-01-12 18:45:03 +00:00
return
2008-07-08 10:43:58 -07:00
else:
2011-09-08 16:13:40 +01:00
out = subprocess.Popen([conf.env['PYTHON'][0], "-c",
2008-07-08 10:43:58 -07:00
"import pybindgen.version; "
2015-07-26 14:01:43 +01:00
"print(pybindgen.__version__)"],
2008-07-08 10:43:58 -07:00
stdout=subprocess.PIPE).communicate()[0]
2015-09-09 15:14:27 -07:00
pybindgen_version = maybe_decode(out.strip())
2015-07-26 14:01:43 +01:00
conf.msg('Checking for pybindgen version', pybindgen_version)
2018-04-11 13:46:07 -07:00
if not pybindgen_version:
Logs.warn("pybindgen_version is an empty string")
conf.report_optional_feature("python", "Python Bindings", False,
"PyBindGen version not found")
return
2016-11-30 20:13:49 -08:00
if not (split_version(pybindgen_version) >= split_version(REQUIRED_PYBINDGEN_VERSION)):
2015-07-26 14:01:43 +01:00
Logs.warn("pybindgen (found %r), (need %r)" %
(pybindgen_version, REQUIRED_PYBINDGEN_VERSION))
2009-01-24 15:28:54 +00:00
conf.report_optional_feature("python", "Python Bindings", False,
2020-10-08 11:58:48 -07:00
"PyBindGen found but version %s is not the required version %s" % (pybindgen_version, REQUIRED_PYBINDGEN_VERSION))
2009-01-24 15:28:54 +00:00
return
2008-07-08 10:43:58 -07:00
2009-11-22 18:27:14 +00:00
def test(t1, t2):
test_program = '''
#include <stdint.h>
#include <vector>
int main ()
{
std::vector< %(type1)s > t = std::vector< %(type2)s > ();
return 0;
}
''' % dict(type1=t1, type2=t2)
try:
2015-06-23 14:34:53 +02:00
ret = conf.check(compiler='cxx', fragment=test_program, features='cxx')
2013-04-01 22:34:50 +02:00
except Errors.ConfigurationError:
2015-06-23 14:34:53 +02:00
ret = False
2011-09-08 16:13:40 +01:00
conf.msg('Checking for types %s and %s equivalence' % (t1, t2), (ret and 'no' or 'yes'))
2015-06-23 14:34:53 +02:00
return ret
2009-11-22 18:27:14 +00:00
uint64_is_long = test("uint64_t", "unsigned long")
uint64_is_long_long = test("uint64_t", "unsigned long long")
if uint64_is_long:
conf.env['PYTHON_BINDINGS_APIDEFS'] = 'gcc-LP64'
elif uint64_is_long_long:
conf.env['PYTHON_BINDINGS_APIDEFS'] = 'gcc-ILP32'
else:
conf.env['PYTHON_BINDINGS_APIDEFS'] = None
if conf.env['PYTHON_BINDINGS_APIDEFS'] is None:
msg = 'none available'
else:
msg = conf.env['PYTHON_BINDINGS_APIDEFS']
2011-09-08 16:13:40 +01:00
conf.msg('Checking for the apidefs that can be used for Python bindings', msg)
2009-11-22 18:27:14 +00:00
if conf.env['PYTHON_BINDINGS_APIDEFS'] is None:
conf.report_optional_feature("python", "Python Bindings", False,
"No apidefs are available that can be used in this system")
return
2011-03-20 13:01:40 +00:00
2008-07-08 10:43:58 -07:00
## If all has gone well, we finally enable the Python bindings
conf.env['ENABLE_PYTHON_BINDINGS'] = True
2008-09-05 18:16:29 +01:00
conf.report_optional_feature("python", "Python Bindings", True, None)
2008-07-08 10:43:58 -07:00
2011-08-07 16:52:49 +01:00
# check cxxabi stuff (which Mac OS X Lion breaks)
fragment = r"""
# include <cxxabi.h>
int main ()
{
const abi::__si_class_type_info *_typeinfo __attribute__((unused)) = NULL;
return 0;
}
"""
2011-09-08 16:13:40 +01:00
gcc_rtti_abi = conf.check_nonfatal(fragment=fragment, msg="Checking for internal GCC cxxabi",
okmsg="complete", errmsg='incomplete',
mandatory=False)
2011-08-07 16:52:49 +01:00
conf.env["GCC_RTTI_ABI_COMPLETE"] = str(bool(gcc_rtti_abi))
2008-07-08 10:43:58 -07:00
## Check for pygccxml
try:
conf.check_python_module('pygccxml')
2013-04-01 22:34:50 +02:00
except Errors.ConfigurationError:
2017-07-06 11:49:29 -07:00
conf.report_optional_feature("castxml", "Python API Scanning Support", False,
2008-09-05 18:16:29 +01:00
"Missing 'pygccxml' Python module")
2008-07-08 10:43:58 -07:00
return
2018-03-05 21:10:36 -08:00
try:
import pygccxml as pygccxml_imported
pygccxml_version_str = pygccxml_imported.__version__
except (ImportError, AttributeError):
Logs.warn("pygccxml version cannot be determined")
conf.report_optional_feature("castxml", "Python API Scanning Support", False,
"pygccxml Python module version is unknown")
return
2014-11-26 14:59:32 -08:00
# Bug 2013: pygccxml versions > 1.0.0 prepend a 'v' to version number
pygccxml_version_str = pygccxml_version_str.lstrip('v')
2008-07-08 10:43:58 -07:00
pygccxml_version = tuple([int(x) for x in pygccxml_version_str.split('.')])
2011-09-08 16:13:40 +01:00
conf.msg('Checking for pygccxml version', pygccxml_version_str)
2008-07-08 10:43:58 -07:00
if not (pygccxml_version >= REQUIRED_PYGCCXML_VERSION):
2008-12-29 16:54:53 +00:00
Logs.warn("pygccxml (found %s) is too old (need %s) => "
2008-07-08 10:43:58 -07:00
"automatic scanning of API definitions will not be possible" %
(pygccxml_version_str,
'.'.join([str(x) for x in REQUIRED_PYGCCXML_VERSION])))
2017-07-06 11:49:29 -07:00
conf.report_optional_feature("castxml", "Python API Scanning Support", False,
"pygccxml Python module too old")
2008-07-08 10:43:58 -07:00
return
2014-09-28 12:39:26 +01:00
2008-07-08 10:43:58 -07:00
2017-07-06 11:49:29 -07:00
## Check castxml version
2011-09-13 13:47:17 +01:00
try:
2017-07-06 11:49:29 -07:00
castxml = conf.find_program('castxml', var='CASTXML')
2011-09-13 13:47:17 +01:00
except WafError:
2017-07-06 11:49:29 -07:00
castxml = None
if not castxml:
Logs.warn("castxml missing; automatic scanning of API definitions will not be possible")
conf.report_optional_feature("castxml", "Python API Scanning Support", False,
"castxml missing")
2008-07-08 10:43:58 -07:00
return
2017-07-06 11:49:29 -07:00
out = subprocess.Popen([castxml[0], '--version'],
stdout=subprocess.PIPE).communicate()[0]
castxml_version_line = maybe_decode(out).split('\n', 1)[0].strip()
## Expecting version string such as 'castxml version 0.1-gfab9c47'
m = re.match( "^castxml version (\d\.\d)(-)?(\w+)?", castxml_version_line)
2015-08-15 17:14:51 +02:00
try:
2017-07-06 11:49:29 -07:00
castxml_version = m.group(1)
castxml_version_ok = castxml_version >= REQUIRED_CASTXML_VERSION
2015-08-15 17:14:51 +02:00
except AttributeError:
2017-07-06 11:49:29 -07:00
castxml_version = castxml_version_line
castxml_version_ok = False
conf.msg('Checking for castxml version', castxml_version)
if not castxml_version_ok:
Logs.warn("castxml version unknown or too old, need version >= %s; automatic scanning of API definitions will not be possible" % REQUIRED_CASTXML_VERSION)
conf.report_optional_feature("castxml", "Python API Scanning Support", False,
"castxml too old")
2008-07-08 10:43:58 -07:00
return
2010-11-07 23:17:52 +00:00
2008-07-08 10:43:58 -07:00
## If we reached
conf.env['ENABLE_PYTHON_SCANNING'] = True
2017-07-06 11:49:29 -07:00
conf.report_optional_feature("castxml", "Python API Scanning Support", True, None)
2008-07-08 10:43:58 -07:00
2010-11-07 23:17:52 +00:00
# ---------------------
def get_headers_map(bld):
headers_map = {} # header => module
for ns3headers in bld.all_task_gen:
2011-09-12 18:54:57 +01:00
if 'ns3header' in getattr(ns3headers, "features", []):
2011-03-29 18:15:52 +01:00
if ns3headers.module.endswith('-test'):
continue
2011-09-12 18:54:57 +01:00
for h in ns3headers.to_list(ns3headers.headers):
2010-11-07 23:17:52 +00:00
headers_map[os.path.basename(h)] = ns3headers.module
return headers_map
def get_module_path(bld, module):
for ns3headers in bld.all_task_gen:
2011-09-12 18:54:57 +01:00
if 'ns3header' in getattr(ns3headers, "features", []):
2010-11-07 23:17:52 +00:00
if ns3headers.module == module:
break
else:
raise ValueError("Module %r not found" % module)
return ns3headers.path.abspath()
2013-10-13 17:54:19 +01:00
class apiscan_task(Task.Task):
2017-07-06 11:49:29 -07:00
"""Uses castxml to scan the file 'everything.h' and extract API definitions.
2010-11-07 23:17:52 +00:00
"""
2019-04-21 11:52:53 -07:00
before = ['cxxprogram', 'cxxshlib', 'cxxstlib', 'command']
after = ['gen_ns3_module_header', 'ns3header']
2010-11-07 23:17:52 +00:00
color = "BLUE"
def __init__(self, curdirnode, env, bld, target, cflags, module):
self.bld = bld
2013-10-13 17:54:19 +01:00
super(apiscan_task, self).__init__(generator=self, env=env)
2010-11-07 23:17:52 +00:00
self.curdirnode = curdirnode
self.env = env
self.target = target
self.cflags = cflags
self.module = module
def display(self):
return 'api-scan-%s\n' % (self.target,)
2013-04-26 11:13:12 -07:00
def uid(self):
try:
return self.uid_
except AttributeError:
m = Utils.md5()
up = m.update
up(self.__class__.__name__.encode())
up(self.curdirnode.abspath().encode())
2019-08-10 17:03:53 +03:00
up(self.target.encode())
2013-04-26 11:13:12 -07:00
self.uid_ = m.digest()
return self.uid_
2010-11-07 23:17:52 +00:00
def run(self):
2013-10-13 17:54:19 +01:00
self.inputs = [self.bld.bldnode.find_resource("ns3/{0}-module.h".format(self.module))]
self.outputs = [self.bld.srcnode.find_resource("src/{}/bindings/modulegen__{}.py".format(self.module, self.target))]
2011-09-12 18:54:57 +01:00
top_builddir = self.bld.bldnode.abspath()
2010-11-07 23:17:52 +00:00
module_path = get_module_path(self.bld, self.module)
headers_map = get_headers_map(self.bld)
2011-03-20 17:56:06 +00:00
scan_header = os.path.join(top_builddir, "ns3", "%s-module.h" % self.module)
if not os.path.exists(scan_header):
Logs.error("Cannot apiscan module %r: %s does not exist" % (self.module, scan_header))
return 0
2010-11-07 23:17:52 +00:00
argv = [
2011-09-12 18:54:57 +01:00
self.env['PYTHON'][0],
2010-11-07 23:17:52 +00:00
os.path.join(self.curdirnode.abspath(), 'ns3modulescan-modular.py'), # scanning script
top_builddir,
self.module,
repr(get_headers_map(self.bld)),
os.path.join(module_path, "bindings", 'modulegen__%s.py' % (self.target)), # output file
self.cflags,
]
scan = subprocess.Popen(argv, stdin=subprocess.PIPE)
retval = scan.wait()
2018-07-21 16:13:14 -07:00
if retval >= 0 and "LP64" in self.target:
self.lp64_to_ilp32(
os.path.join(module_path, "bindings", 'modulegen__%s.py' % (self.target)),
os.path.join(module_path, "bindings", 'modulegen__%s.py' % "gcc_ILP32")
)
2010-11-07 23:17:52 +00:00
return retval
2013-11-12 10:37:52 -08:00
def runnable_status(self):
2013-11-18 08:53:07 -08:00
# By default, Waf Task will skip running a task if the signature of
# the build has not changed. We want this task to always run if
# invoked by the user, particularly since --apiscan=all will require
# invoking this task many times, once per module.
2013-11-12 10:37:52 -08:00
return RUN_ME
2008-07-08 10:43:58 -07:00
2018-07-21 16:13:14 -07:00
def lp64_to_ilp32(self, lp64path, ilp32path):
lp64file = open(lp64path, "r")
lp64bindings = lp64file.read()
lp64file.close()
ilp32file = open(ilp32path, "w")
ilp32bindings = re.sub("unsigned long(?!( long))", "unsigned long long", lp64bindings)
ilp32file.write(ilp32bindings)
ilp32file.close()
2009-04-13 23:10:37 +01:00
def get_modules_and_headers(bld):
2008-07-08 10:43:58 -07:00
"""
Gets a dict of
module_name => ([module_dep1, module_dep2, ...], [module_header1, module_header2, ...])
tuples, one for each module.
"""
retval = {}
2009-04-13 23:10:37 +01:00
for module in bld.all_task_gen:
2008-07-08 10:43:58 -07:00
if not module.name.startswith('ns3-'):
continue
2011-03-29 18:15:52 +01:00
if module.name.endswith('-test'):
continue
2008-07-08 10:43:58 -07:00
module_name = module.name[4:] # strip the ns3- prefix
## find the headers object for this module
headers = []
2009-04-13 23:10:37 +01:00
for ns3headers in bld.all_task_gen:
2011-09-12 18:54:57 +01:00
if 'ns3header' not in getattr(ns3headers, "features", []):
2008-07-08 10:43:58 -07:00
continue
if ns3headers.module != module_name:
continue
2011-09-12 18:54:57 +01:00
for source in ns3headers.to_list(ns3headers.headers):
2011-01-31 19:03:19 +00:00
headers.append(os.path.basename(source))
2008-07-08 10:43:58 -07:00
retval[module_name] = (list(module.module_deps), headers)
return retval
2008-08-18 23:04:49 +01:00
2009-09-22 16:13:42 +01:00
2011-03-28 15:31:13 +01:00
class gen_ns3_compat_pymod_task(Task.Task):
2011-03-13 16:03:33 +00:00
"""Generates a 'ns3.py' compatibility module."""
2019-04-21 11:52:53 -07:00
before = ['cxxprogram', 'cxxshlib', 'cxxstlib']
2011-03-13 16:03:33 +00:00
color = 'BLUE'
2014-09-28 12:39:26 +01:00
2011-03-13 16:03:33 +00:00
def run(self):
2011-03-28 15:31:13 +01:00
assert len(self.outputs) == 1
2015-09-09 15:14:27 -07:00
outfile = open(self.outputs[0].abspath(), "w")
2015-09-03 21:14:55 -07:00
print("import warnings", file=outfile)
print('warnings.warn("the ns3 module is a compatibility layer '\
'and should not be used in newly written code", DeprecationWarning, stacklevel=2)', file=outfile)
print(file=outfile)
2011-03-13 16:03:33 +00:00
for module in self.bld.env['PYTHON_MODULES_BUILT']:
2015-09-03 21:14:55 -07:00
print("from ns.%s import *" % (module.replace('-', '_')), file=outfile)
2011-03-13 16:03:33 +00:00
outfile.close()
return 0
2008-07-08 10:43:58 -07:00
def build(bld):
2008-12-29 13:28:54 +00:00
if Options.options.python_disable:
2008-07-08 10:43:58 -07:00
return
2008-12-29 13:28:54 +00:00
env = bld.env
2008-11-19 17:32:37 +00:00
set_pybindgen_pythonpath(env)
2010-11-07 23:17:52 +00:00
if Options.options.apiscan:
if not env['ENABLE_PYTHON_SCANNING']:
2011-10-05 16:39:57 +01:00
raise WafError("Cannot re-scan python bindings: (py)gccxml not available")
2010-11-07 23:17:52 +00:00
scan_targets = []
if sys.platform == 'cygwin':
scan_targets.append(('gcc_cygwin', ''))
else:
import struct
if struct.calcsize('I') == 4 and struct.calcsize('L') == 8 and struct.calcsize('P') == 8:
2014-03-02 12:24:39 +00:00
scan_targets.append(('gcc_LP64', '-m64'))
2010-11-07 23:17:52 +00:00
elif struct.calcsize('I') == 4 and struct.calcsize('L') == 4 and struct.calcsize('P') == 4:
scan_targets.append(('gcc_ILP32', ''))
else:
2011-10-05 16:39:57 +01:00
raise WafError("Cannot scan python bindings for unsupported data model")
2011-03-21 18:16:41 +00:00
test_module_path = bld.path.find_dir("../../src/test")
2011-03-20 15:41:43 +00:00
if Options.options.apiscan == 'all':
2011-03-21 18:16:41 +00:00
scan_modules = []
for mod in bld.all_task_gen:
if not mod.name.startswith('ns3-'):
continue
if mod.path.is_child_of(test_module_path):
continue
2011-03-21 18:53:10 +00:00
if mod.name.endswith('-test'):
continue
2011-03-22 15:56:41 +00:00
bindings_enabled = (mod.name in env.MODULAR_BINDINGS_MODULES)
#print mod.name, bindings_enabled
if bindings_enabled:
scan_modules.append(mod.name.split('ns3-')[1])
2011-03-20 15:41:43 +00:00
else:
scan_modules = Options.options.apiscan.split(',')
2015-09-03 21:14:55 -07:00
print("Modules to scan: ", scan_modules)
2010-11-07 23:17:52 +00:00
for target, cflags in scan_targets:
2011-09-12 18:54:57 +01:00
group = bld.get_group(bld.current_group)
2011-03-20 15:41:43 +00:00
for module in scan_modules:
2011-09-12 18:54:57 +01:00
group.append(apiscan_task(bld.path, env, bld, target, cflags, module))
2010-11-07 23:17:52 +00:00
return
2011-09-01 17:28:10 +01:00
if env['ENABLE_PYTHON_BINDINGS']:
2011-09-12 18:54:57 +01:00
task = gen_ns3_compat_pymod_task(env=env.derive())
2011-03-28 15:31:13 +01:00
task.set_outputs(bld.path.find_or_declare("ns3.py"))
task.dep_vars = ['PYTHON_MODULES_BUILT']
2011-09-12 18:54:57 +01:00
task.bld = bld
grp = bld.get_group(bld.current_group)
grp.append(task)
2011-03-28 15:31:13 +01:00
2013-04-01 22:34:50 +02:00
bld(features='copy', source="ns__init__.py", target='ns/__init__.py')
2012-01-05 01:53:51 +00:00
bld.install_as('${PYTHONARCHDIR}/ns/__init__.py', 'ns__init__.py')
2011-09-23 12:19:53 +01:00
2011-08-18 16:53:15 +01:00
# note: the actual build commands for the python bindings are in
# src/wscript, not here.