diff --git a/pybindgen/castxmlparser.py b/pybindgen/castxmlparser.py index 9377f9c..9815df7 100644 --- a/pybindgen/castxmlparser.py +++ b/pybindgen/castxmlparser.py @@ -653,7 +653,9 @@ class ModuleParser(object): self._post_scan_hooks.append(hook) def __location_match(self, decl): - if decl.location.file_name in self.header_files: + # Make sure absolute paths are compared. + abspath = os.path.abspath(decl.location.file_name) + if abspath in self.header_files: return True for incdir in self.whitelist_paths: if os.path.abspath(decl.location.file_name).startswith(incdir): diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index 230b629..da37154 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -35,6 +35,12 @@ from . import function import collections +# Prepare for python 3.9 +try: + collectionsCallable = collections.Callable +except AttributeError: + collectionsCallable = collections + try: set except NameError: @@ -1036,7 +1042,7 @@ class CppClass(object): all subclasses. The hook function is called like this:: hook_function(helper_class) """ - if not isinstance(hook, collections.Callable): + if not isinstance(hook, collectionsCallable): raise TypeError("hook function must be callable") self.helper_class_hooks.append(hook) diff --git a/tests/foomodulegen-auto.py b/tests/foomodulegen-auto.py index 69c4f8d..a15d53f 100644 --- a/tests/foomodulegen-auto.py +++ b/tests/foomodulegen-auto.py @@ -50,7 +50,6 @@ def my_module_gen(): module.generate(out) - def main(): logging.basicConfig(level=logging.DEBUG) if sys.argv[1] == '-d': diff --git a/tests/foomodulegen4.py b/tests/foomodulegen4.py index 3930cde..17465f8 100644 --- a/tests/foomodulegen4.py +++ b/tests/foomodulegen4.py @@ -9,16 +9,18 @@ from pybindgen.module import MultiSectionFactory from pybindgen import (FileCodeSink) import foomodulegen_split +import foomodulegen_module1, foomodulegen_module2 import foomodulegen_common +# TODO(cnicolaou): get the multi section tests working class MyMultiSectionFactory(MultiSectionFactory): - def __init__(self, main_file_name): - self.main_file_name = main_file_name - self.main_sink = FileCodeSink(open(main_file_name, "wt")) + def __init__(self, dir): + self.main_file_name = os.path.join(dir, "foomodule4.cc") + self.main_sink = FileCodeSink(open(self.main_file_name, "wt")) self.header_name = "foomodule4.h" - header_file_name = os.path.join(os.path.dirname(self.main_file_name), self.header_name) + header_file_name = os.path.join(dir, self.header_name) self.header_sink = FileCodeSink(open(header_file_name, "wt")) self.section_sinks = {} @@ -49,7 +51,7 @@ class MyMultiSectionFactory(MultiSectionFactory): sink.file.close() def my_module_gen(): - out = MyMultiSectionFactory(sys.argv[1]) + out = MyMultiSectionFactory(os.path.dirname(sys.argv[1])) root_module = foomodulegen_split.module_init() root_module.add_exception('exception', foreign_cpp_namespace='std', message_rvalue='%(EXC)s.what()') foomodulegen_common.customize_module_pre(root_module) diff --git a/tests/foomodulegen_common.py b/tests/foomodulegen_common.py index 4722376..9807c40 100644 --- a/tests/foomodulegen_common.py +++ b/tests/foomodulegen_common.py @@ -84,6 +84,12 @@ _wrap_foofunction_that_takes_foo_from_string(PyObject * PYBINDGEN_UNUSED(dummy), return py_retval; } ''' + + if len(module.classes) == 0: + # Some tests, eg. foomodulegen4.py, don't have Bar and Foo classes + # defined. + return + module.add_custom_function_wrapper('function_that_takes_foo', '_wrap_foofunction_that_takes_foo_from_string', wrapper_body, diff --git a/tests/footest.py b/tests/footest.py index c231a82..8b74cd0 100644 --- a/tests/footest.py +++ b/tests/footest.py @@ -21,6 +21,10 @@ elif which == 4: else: raise AssertionError("bad command line arguments") +cc_name = sys.argv.pop(1) +cc_version = sys.argv.pop(1) +print("compiler command and version:", cc_name, cc_version) + gccxml_mode = sys.argv.pop(1) print("gccxml_mode:", gccxml_mode) @@ -170,6 +174,10 @@ class TestFoo(unittest.TestCase): def test_type_narrowing_hidden_subclass(self): """This test only works with GCC >= 3.0 (and not with other compilers)""" + if "gcc" not in cc_name: + return + if int(cc_version) < 3: + return obj = foo.get_hidden_subclass_pointer() self.assertEqual(type(obj), foo.Bar) diff --git a/waf b/waf index 4e68fed..7ceee16 100755 Binary files a/waf and b/waf differ diff --git a/waf-tools/cflags.py b/waf-tools/cflags.py index 4814a50..2b25d41 100644 --- a/waf-tools/cflags.py +++ b/waf-tools/cflags.py @@ -49,7 +49,7 @@ class GccTraits(CompilerTraits): return (['-g'], []) elif level >= 2: return (['-ggdb', '-g3'], ['_DEBUG']) - + class IccTraits(CompilerTraits): def __init__(self): @@ -121,7 +121,6 @@ class MsvcTraits(CompilerTraits): return (['/ZI', '/RTC1'], ['_DEBUG']) - gcc = GccTraits() icc = IccTraits() msvc = MsvcTraits() diff --git a/waf-tools/command.py b/waf-tools/command.py index 0b13a3d..33fdea3 100644 --- a/waf-tools/command.py +++ b/waf-tools/command.py @@ -16,16 +16,18 @@ import re arg_rx = re.compile(r"(?P\$\$)|(?P\$\{(?P\w+)(?P.*?)\})", re.M) -class command_task(Task.Task): +def nice_path(input): + return input.path_from(input.ctx.launch_node()) + +class command(Task.Task): color = "BLUE" def __init__(self, env, generator): Task.Task.__init__(self, env=env, normal=1, generator=generator) 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]) + src_str = ' '.join([nice_path(a) for a in self.inputs]) + tgt_str = ' '.join([nice_path(a) for a in self.outputs]) if self.outputs: sep = ' -> ' else: @@ -151,5 +153,3 @@ def process_rule(self): cls.scan = scan setattr(tsk, "dep_vars", getattr(self, "dep_vars", None)) - - diff --git a/waf-tools/python_patched.py b/waf-tools/python_patched.py index ab0e97a..ffa9532 100644 --- a/waf-tools/python_patched.py +++ b/waf-tools/python_patched.py @@ -22,6 +22,7 @@ import os, sys from waflib import Utils, Options, Errors, Logs from waflib.TaskGen import extension, before_method, after_method, feature from waflib.Configure import conf +import waflib FRAG = ''' #include @@ -241,7 +242,12 @@ def check_python_headers(conf): if not pybin: conf.fatal('Could not find the python executable') - v = 'prefix SO LDFLAGS LIBDIR LIBPL INCLUDEPY Py_ENABLE_SHARED MACOSX_DEPLOYMENT_TARGET LDSHARED CFLAGS LDVERSION'.split() + extsuffix = "SO" + if '.'.join(env.PYTHON_VERSION.split('.')[:1]) == "3": + extsuffix = "EXT_SUFFIX" + + v = 'prefix LDFLAGS LIBDIR LIBPL INCLUDEPY Py_ENABLE_SHARED MACOSX_DEPLOYMENT_TARGET LDSHARED CFLAGS LDVERSION'.split() + v.append(extsuffix) try: lst = conf.get_python_variables(["get_config_var('%s') or ''" % x for x in v]) except RuntimeError: @@ -255,7 +261,7 @@ def check_python_headers(conf): if dct[x]: conf.env[x] = conf.environ[x] = dct[x] - env['pyext_PATTERN'] = '%s' + dct['SO'] # not a mistake + env['pyext_PATTERN'] = '%s' + dct[extsuffix] # not a mistake # Check for python libraries for embedding @@ -316,9 +322,12 @@ def check_python_headers(conf): num = '.'.join(env['PYTHON_VERSION'].split('.')[:2]) conf.find_program([''.join(pybin) + '-config', 'python%s-config' % num, 'python-config-%s' % num, 'python%sm-config' % num], var='PYTHON_CONFIG', mandatory=False) + if len(conf.env.PYTHON_CONFIG) != 1: + raise conf.errors.ConfigurationError("failed to find exactly one python config script") + includes = [] if conf.env.PYTHON_CONFIG: - for incstr in conf.cmd_and_log([ conf.env.PYTHON_CONFIG, '--includes']).strip().split(): + for incstr in conf.cmd_and_log([conf.env.PYTHON_CONFIG[0], '--includes']).strip().split(): # strip the -I or /I if (incstr.startswith('-I') or incstr.startswith('/I')): incstr = incstr[2:] diff --git a/wscript b/wscript index 88f3f85..a2a96b1 100644 --- a/wscript +++ b/wscript @@ -13,6 +13,8 @@ from waflib import Configure from waflib import Logs +from waflib.Tools import c_config + import os import subprocess import shutil @@ -58,7 +60,7 @@ def zipper(dir, zip_file, archive_main_folder=None): def options(opt): opt.load('python_patched', tooldir="waf-tools") - opt.load('compiler_cc') + opt.load('compiler_c') opt.load('compiler_cxx') opt.load('cflags', tooldir="waf-tools") opt.recurse('examples') @@ -120,8 +122,9 @@ def _check_compilation_flag(conf, flag, mode='cxx', linkflags=None): env.append_value("LINKFLAGS", linkflags) try: - retval = conf.run_c_code(code='#include \nint main() { return 0; }\n', + retval = conf.run_build(code='#include \nint main() { return 0; }\n', env=env, compile_filename=fname, + build_fun=c_config.build_fun, features=[mode, mode+'program'], execute=False) except conf.errors.ConfigurationError: ok = False @@ -151,7 +154,7 @@ def configure(conf): conf.check_python_version((2,3)) try: - conf.load('compiler_cc') + conf.load('compiler_c') conf.load('compiler_cxx') except conf.errors.ConfigurationError: Logs.warn("C/C++ compiler not detected. Unit tests and examples will not be compiled.") @@ -162,8 +165,7 @@ def configure(conf): if not Options.options.disable_pygccxml: castxml = conf.find_program('castxml', mandatory=False) - gccxml = conf.find_program('gccxml', mandatory=False) - if not (gccxml or castxml): + if not castxml: conf.env['ENABLE_PYGCCXML'] = False else: try: @@ -174,8 +176,6 @@ def configure(conf): conf.env['ENABLE_PYGCCXML'] = True if castxml: conf.env['PYGCCXML_MODE'] = 'castxml' - else: - conf.env['PYGCCXML_MODE'] = 'gccxml' # -fvisibility=hidden optimization if (conf.env['CXX_NAME'] == 'gcc' and [int(x) for x in conf.env['CC_VERSION']] >= [4,0,0] @@ -242,7 +242,6 @@ class CheckContext(Context.Context): bld.cmd = "check" bld.execute() - if Options.options.verbose: verbosity = ['-v'] else: @@ -253,6 +252,11 @@ class CheckContext(Context.Context): else: valgrind = [] + # Pass compiler name and version to some of the tests so that they + # test compiler specific features. + cc_name = bld.env['CC_NAME'] + cc_version = bld.env['CC_VERSION'][0] + print("Running pure python unit tests...") python = bld.env['PYTHON'][0] retval1 = subprocess.Popen([python, 'tests/test.py'] + verbosity).wait() @@ -263,19 +267,19 @@ class CheckContext(Context.Context): if env['CXX']: print("Running manual module generation unit tests (module foo)...") - retvals.append(subprocess.Popen(valgrind + [python, 'tests/footest.py', '1', 'none'] + verbosity).wait()) + retvals.append(subprocess.Popen(valgrind + [python, 'tests/footest.py', '1', cc_name, cc_version, 'none'] + verbosity).wait()) else: print("Skipping manual module generation unit tests (no C/C++ compiler)...") if env['ENABLE_PYGCCXML']: print("Running automatically scanned module generation unit tests (module foo2)...") - retvals.append(subprocess.Popen(valgrind + [python, 'tests/footest.py', '2', env['PYGCCXML_MODE']] + verbosity).wait()) + retvals.append(subprocess.Popen(valgrind + [python, 'tests/footest.py', '2', cc_name, cc_version, env['PYGCCXML_MODE']] + verbosity).wait()) print("Running module generated by automatically generated python script unit tests (module foo3)...") - retvals.append(subprocess.Popen(valgrind + [python, 'tests/footest.py', '3', env['PYGCCXML_MODE']] + verbosity).wait()) + retvals.append(subprocess.Popen(valgrind + [python, 'tests/footest.py', '3', cc_name, cc_version, env['PYGCCXML_MODE']] + verbosity).wait()) print("Running module generated by generated and split python script unit tests (module foo4)...") - retvals.append(subprocess.Popen(valgrind + [python, 'tests/footest.py', '4', env['PYGCCXML_MODE']] + verbosity).wait()) + retvals.append(subprocess.Popen(valgrind + [python, 'tests/footest.py', '4', cc_name, cc_version, env['PYGCCXML_MODE']] + verbosity).wait()) print("Running semi-automatically scanned c-hello module ('hello')...") retvals.append(subprocess.Popen(valgrind + [python, 'tests/c-hello/hellotest.py'] + verbosity).wait())