Ejemplo n.º 1
0
def main(namespace, *headers, outfile=None):
    generator_path, generator_name = utils.find_xml_generator()
    xml_generator_config = parser.xml_generator_configuration_t(
        xml_generator_path=generator_path,
        xml_generator=generator_name,
        cflags='-std=c++14')

    decls = parser.parse(headers, xml_generator_config)
    global_namespace = declarations.get_global_namespace(decls[0])

    ns = global_namespace.namespace(namespace)

    if outfile is None:
        outfile = sys.stdout
    else:
        outfile = open(outfile, 'w')

    def out(s):
        if not s: return
        outfile.write(s)
        outfile.write('\n')

    out("#include <pybind11/pybind11.h>")
    out("#include <pybind11/stl.h>")
    for header in headers:
        out(f'#include "{header}"')
    out("namespace py = pybind11;")
    out(f'PYBIND11_MODULE({namespace}, m){{')
    out("using namespace pybind11::literals;")
    for decl in ns.declarations:
        out(dump_decl(decl))
    out('}')
Ejemplo n.º 2
0
def pygccxml_parser(filename):
    """
    Implementation of pygccxml parser
    """
    generator_path, generator_name = utils.find_xml_generator()

    # Configure the xml generator
    xml_generator_config = parser.xml_generator_configuration_t(
        xml_generator_path=generator_path, xml_generator=generator_name)
    filename = filename
    decls = parser.parse([filename], xml_generator_config)

    global_namespace = declarations.get_global_namespace(decls)
    ns = global_namespace.namespace("sample_namespace")
    return declarations.print_declarations(ns)
Ejemplo n.º 3
0
    def test_config(self):
        """Test config setup with wrong xml generator setups."""

        # Some code to parse for the example
        code = "int a;"

        # Find the location of the xml generator (castxml or gccxml)
        generator_path, name = utils.find_xml_generator()

        # No xml generator path
        config = parser.xml_generator_configuration_t(xml_generator=name)
        self.assertRaises(
            RuntimeError, lambda: parser.parse_string(code, config))

        # Invalid path
        config = parser.xml_generator_configuration_t(
            xml_generator_path="wrong/path",
            xml_generator=name)
        self.assertRaises(
            RuntimeError, lambda: parser.parse_string(code, config))

        # None path
        config = parser.xml_generator_configuration_t(
            xml_generator_path=None,
            xml_generator=name)
        self.assertRaises(
            RuntimeError, lambda: parser.parse_string(code, config))

        # No name
        config = parser.xml_generator_configuration_t(
            xml_generator_path=generator_path)
        self.assertRaises(
            RuntimeError, lambda: parser.parse_string(code, config))

        # Random name
        config = parser.xml_generator_configuration_t(
            xml_generator_path=generator_path,
            xml_generator="not_a_generator")
        self.assertRaises(
            RuntimeError, lambda: parser.parse_string(code, config))

        # None name
        config = parser.xml_generator_configuration_t(
            xml_generator_path=generator_path,
            xml_generator=None)
        self.assertRaises(
            RuntimeError, lambda: parser.parse_string(code, config))
Ejemplo n.º 4
0
    def test_config(self):
        """
            Test that a missing include directory is printing a warning,
            not raising an error
        """

        # Some code to parse for the example
        code = "int a;"

        # Find the location of the xml generator (castxml or gccxml)
        generator_path, name = utils.find_xml_generator()

        # Path given as include director doesn't exist
        config = parser.xml_generator_configuration_t(
            xml_generator_path=generator_path,
            xml_generator=name,
            include_paths=["doesnt/exist", os.getcwd()])
        self.assertWarns(RuntimeWarning, parser.parse_string, code, config)
Ejemplo n.º 5
0
    def test_config(self):
        """Test config setup with wrong xml generator setups."""

        # Some code to parse for the example
        code = "int a;"

        # Find the location of the xml generator (castxml or gccxml)
        generator_path, name = utils.find_xml_generator()

        # No xml generator path
        config = parser.xml_generator_configuration_t(xml_generator=name)
        self.assertRaises(RuntimeError,
                          lambda: parser.parse_string(code, config))

        # Invalid path
        config = parser.xml_generator_configuration_t(
            xml_generator_path="wrong/path", xml_generator=name)
        self.assertRaises(RuntimeError,
                          lambda: parser.parse_string(code, config))

        # None path
        config = parser.xml_generator_configuration_t(xml_generator_path=None,
                                                      xml_generator=name)
        self.assertRaises(RuntimeError,
                          lambda: parser.parse_string(code, config))

        # No name
        config = parser.xml_generator_configuration_t(
            xml_generator_path=generator_path)
        self.assertRaises(RuntimeError,
                          lambda: parser.parse_string(code, config))

        # Random name
        config = parser.xml_generator_configuration_t(
            xml_generator_path=generator_path, xml_generator="not_a_generator")
        self.assertRaises(RuntimeError,
                          lambda: parser.parse_string(code, config))

        # None name
        config = parser.xml_generator_configuration_t(
            xml_generator_path=generator_path, xml_generator=None)
        self.assertRaises(RuntimeError,
                          lambda: parser.parse_string(code, config))
Ejemplo n.º 6
0
def main():
    """
    Main function to generate Python-C++ binding code
    """

    # Find out the xml generator (gccxml or castxml)
    generator_path, generator_name = utils.find_xml_generator()
    compiler = "g++"
    compiler_path = "/usr/bin/g++"

    # Create configuration for CastXML
    xml_generator_config = parser.xml_generator_configuration_t(
        xml_generator_path=generator_path,
        xml_generator=generator_name,
        compiler=compiler,
        compiler_path=compiler_path)

    # Set include dirs and cflags to avoid warnings and errors
    xml_generator_config.append_cflags("-std=c++11")
    xml_generator_config.include_paths.append("../include/generated/")

    # Find all relevant headers
    header_list = get_list_of_files("../include/generated/")
    header_list = header_list + get_list_of_files("../include/ad_rss/core/")
    header_list = header_list + get_list_of_files("../include/ad_rss/physics/")

    # Parses the source files and creates a module_builder object
    builder = module_builder.module_builder_t(
        header_list,
        xml_generator_path=generator_path,
        xml_generator_config=xml_generator_config)

    # Automatically detect properties and associated getters/setters
    builder.classes().add_properties(exclude_accessors=True)

    # Define a name for the module
    builder.build_code_creator(module_name="libad_rss_python")

    # Writes the C++ interface file
    builder.write_module('PythonWrapper.cpp')
Ejemplo n.º 7
0
def try_pygccxml():
    from pygccxml import declarations
    from pygccxml import utils
    from pygccxml import parser

    # Find out the c++ parser. This should resolve to the castxml
    # version installed in Docker.
    generator_path, generator_name = utils.find_xml_generator()

    # Configure the xml generator
    config = parser.xml_generator_configuration_t(
        xml_generator_path=generator_path,
        xml_generator=generator_name,
        include_paths=["/usr/include/eigen3"],
        # TODO(eric.cousineau): Why is `compiler_path` necessary?
        compiler_path=generator_path,
        start_with_declarations=["ns"],
    )

    t_start = time.time()
    (global_ns, ) = parser.parse_string(code, config)
    dt = time.time() - t_start
    return dt
Ejemplo n.º 8
0
def main():
    defines = []
    includes = []
    cxxflags = [
        '-Wno-unknown-attributes',
        '-Wno-unused-value',
        '-Wno-macro-redefined',
    ]
    compiler = 'g++'

    args = sys.argv[1:]
    while args and args[0].startswith('-'):
        arg = args.pop(0)
        if arg.startswith('-I'):
            include = arg[2:]
            includes.append(include)
        elif arg.startswith('-D'):
            define = arg[2:]
            defines.append(define)
        else:
            sys.stderr.write('error: unknown option %r\n' % arg)
            sys.exit(1)

    winsdk = True
    if winsdk:
        # Set up Clang compiler flags to use MinGW runtime
        # http://stackoverflow.com/a/19839946
        p = subprocess.Popen(
            ["x86_64-w64-mingw32-g++", "-x", "c++", "-E", "-Wp,-v", '-', '-fsyntax-only'],
            stdin=open(os.devnull, 'rt'),
            stdout=open(os.devnull, 'wt'),
            stderr=subprocess.PIPE)
        includes.append('/usr/share/castxml/clang/include')
        for line in p.stderr:
            if line.startswith(' '):
                include = line.strip()
                if os.path.isdir(include):
                    if os.path.exists(os.path.join(include, 'ia32intrin.h')):
                        # XXX: We must use Clang's intrinsic headers
                        continue
                    includes.append(os.path.normpath(include))

        winver = 0x0602

        defines += [
            # emulate MinGW
            '__MINGW32__',
            '_WIN32',
            '_WIN64',
            '__declspec(x)=',
            # Avoid namespace pollution when including windows.h
            # http://support.microsoft.com/kb/166474
            'WIN32_LEAN_AND_MEAN',
            # Set Windows version to 8.1
            '_WIN32_WINNT=0x%04X' % winver,
            'WINVER=0x%04X' % winver,
            'NTDDI_VERSION=0x%04X0000' % winver,
            # Prevent headers from requiring a rpcndr.h version beyond MinGW's
            '__REQUIRED_RPCNDR_H_VERSION__=475',
            # Avoid C++ helper classes
            'D3D10_NO_HELPERS',
            'D3D11_NO_HELPERS',
            'D3D11_VIDEO_NO_HELPERS',
        ]

        # XXX: Change compiler?
        #compiler = 'cl'

        # XXX: This doesn't seem to work well
        cxxflags += [
            #'-m32',
            #'-target', 'x86_64-pc-mingw32',
        ]

    sys.stderr.write('Include path:\n')
    for include in includes:
        sys.stderr.write('  %s\n' % include)
    sys.stderr.write('Definitions:\n')
    for define in defines:
        sys.stderr.write('  %s\n' % define)

    import logging
    utils.loggers.set_level(logging.DEBUG)

    # Find the location of the xml generator (castxml or gccxml)
    generator_path, generator_name = utils.find_xml_generator("castxml")

    # Configure the xml generator
    config = parser.xml_generator_configuration_t(
        xml_generator_path=generator_path,
        xml_generator=generator_name,
        define_symbols = defines,
        include_paths = includes,
        cflags = ' '.join(cxxflags),
        compiler = compiler,
        #keep_xml = True,
    )

    script_dir = os.path.dirname(__file__)
    headers = [
        os.path.join(script_dir, '..', '..', 'compat', 'winsdk_compat.h'),
        os.path.join(script_dir, 'cxx2api.h'),
    ]
    main_header = args[0]
    headers.append(main_header)

    decls = parser.parse(headers, config, parser.COMPILATION_MODE.ALL_AT_ONCE)
    global_ns = declarations.get_global_namespace(decls)

    def decl_filter(decl):
        location = decl.location
        if location is None:
            return False
        return os.path.basename(location.file_name) in map(os.path.basename, args)

    module, _ = os.path.splitext(main_header)
    visitor = decl2_dumper_t(module)
    visitor.start()
    for decl in global_ns.declarations:
        if not decl_filter(decl):
            continue

        if sys.stdout.isatty():
            print('# ' + str(decl))

        visitor.decl = decl
        algorithm.apply_visitor(visitor, decl)
    visitor.finish()
Ejemplo n.º 9
0
    def get_header_info(self):
        """
        PyGCCXML header code parser
        magic happens here!
        : returns the parsed header data in python dict
        : return dict keys: namespace, class, io_signature, make,
                       properties, methods
        : Can be used as an CLI command or an external API
        """
        gr = self.modname.split('-')[0]
        module = self.modname.split('-')[-1]
        self.parsed_data['module_name'] = module
        generator_path, generator_name = utils.find_xml_generator()
        xml_generator_config = parser.xml_generator_configuration_t(
            xml_generator_path=generator_path,
            xml_generator=generator_name,
            include_paths=self.include_paths,
            compiler='gcc',
            define_symbols=['BOOST_ATOMIC_DETAIL_EXTRA_BACKEND_GENERIC'],
            cflags='-std=c++11')
        decls = parser.parse([self.target_file], xml_generator_config)
        global_namespace = declarations.get_global_namespace(decls)

        # namespace
        try:
            self.parsed_data['namespace'] = []
            ns = global_namespace.namespace(gr)
            if ns is None:
                raise BlockToolException
            main_namespace = ns.namespace(module)
            if main_namespace is None:
                raise BlockToolException('namespace cannot be none')
            self.parsed_data['namespace'] = [gr, module]
            if main_namespace.declarations:
                for _namespace in main_namespace.declarations:
                    if isinstance(_namespace, declarations.namespace_t):
                        if Constants.KERNEL not in str(_namespace):
                            main_namespace = _namespace
                            self.parsed_data['namespace'].append(
                                str(_namespace).split('::')[-1].split(' ')[0])
        except RuntimeError:
            raise BlockToolException(
                'Invalid namespace format in the block header file')

        # class
        try:
            self.parsed_data['class'] = ''
            for _class in main_namespace.declarations:
                if isinstance(_class, declarations.class_t):
                    expected_class_name = self.filename.split('.')[0]
                    if expected_class_name in str(_class):
                        main_class = _class
                        self.parsed_data['class'] = str(_class).split(
                            '::')[2].split(' ')[0]
                        # in more complicated blocks, there are many classes included in this declaration
                        # Break after the first class - safe to assume this is the "main class"?
                        if len(main_class.bases) > 0:
                            self.parsed_data['block_type'] = main_class.bases[
                                0].declaration_path[-1]
                            break
        except RuntimeError:
            raise BlockToolException(
                'Block header namespace {} must consist of a valid class instance'
                .format(module))

        # io_signature, message_ports
        self.parsed_data['io_signature'] = {}
        self.parsed_data['message_port'] = {}
        if os.path.isfile(self.impl_file) and exist_comments(self):
            self.parsed_data['io_signature'] = io_signature(self.impl_file)
            self.parsed_data['message_port'] = message_port(self.impl_file)
            read_comments(self)
        elif os.path.isfile(self.impl_file) and not exist_comments(self):
            self.parsed_data['io_signature'] = io_signature(self.impl_file)
            self.parsed_data['message_port'] = message_port(self.impl_file)
            if self.addcomments:
                add_comments(self)
        elif not os.path.isfile(self.impl_file) and exist_comments(self):
            read_comments(self)
        else:
            self.parsed_data['io_signature'] = {"input": [], "output": []}
            self.parsed_data['message_port'] = self.parsed_data['io_signature']

        # make
        try:
            self.parsed_data['make'] = {}
            self.parsed_data['make']['arguments'] = []
            query_m = declarations.custom_matcher_t(
                lambda mem_fun: mem_fun.name.startswith('make'))
            query_make = query_m & declarations.access_type_matcher_t('public')
            make_func = main_class.member_functions(
                function=query_make,
                allow_empty=True,
                header_file=self.target_file)
            criteria = declarations.calldef_matcher(name='make')
            _make_fun = declarations.matcher.get_single(criteria, main_class)
            _make_fun = str(_make_fun).split('make')[-1].split(')')[0].split(
                '(')[1].lstrip().rstrip().split(',')
            if make_func:
                for arg in make_func[0].arguments:
                    make_arguments = None
                    '''
                    for _arg in _make_fun:
                        if str(arg.name) in _arg:
                            make_arguments = {
                                "name": str(arg.name),
                                "dtype": str(arg.decl_type),
                                "default": ""
                            }
                            if re.findall(r'[-+]?\d*\.\d+|\d+', _arg):
                                make_arguments['default'] = re.findall(
                                    r'[-+]?\d*\.\d+|\d+', _arg)[0]
                            elif re.findall(r'\"(.+?)\"', _arg):
                                make_arguments['default'] = re.findall(
                                    r'\"(.+?)\"', _arg)[0]
                            elif "true" in _arg:
                                make_arguments['default'] = "True"
                            elif "false" in _arg:
                                make_arguments['default'] = "False"
                    '''
                    # In case the search did not find an argument in the inner loop
                    # This happens while parsing digital/symbol_sync_cc.h
                    if make_arguments:
                        self.parsed_data['make']['arguments'].append(
                            make_arguments.copy())
                    else:
                        self.parsed_data['make']['arguments'].append({
                            "name":
                            str(arg.name),
                            "dtype":
                            str(arg.decl_type),
                            "default":
                            arg.
                            default_value  # can we get default argument directly from arg
                        })
        except RuntimeError:
            self.parsed_data['make'] = {}
            self.parsed_data['make']['arguments'] = []

        # setters
        try:
            self.parsed_data['methods'] = []
            query_methods = declarations.access_type_matcher_t('public')
            setters = main_class.member_functions(function=query_methods,
                                                  allow_empty=True,
                                                  header_file=self.target_file)
            getter_arguments = []
            if setters:
                for setter in setters:
                    if str(setter.name).startswith(
                            'set_') and setter.arguments:
                        setter_args = {
                            "name": str(setter.name),
                            "arguments_type": []
                        }
                        for argument in setter.arguments:
                            args = {
                                "name": str(argument.name),
                                "dtype": str(argument.decl_type)
                            }
                            getter_arguments.append(args['name'])
                            setter_args['arguments_type'].append(args.copy())
                        self.parsed_data['methods'].append(setter_args.copy())
        except RuntimeError:
            self.parsed_data['methods'] = []

        # getters
        try:
            self.parsed_data['properties'] = []
            query_properties = declarations.access_type_matcher_t('public')
            getters = main_class.member_functions(function=query_properties,
                                                  allow_empty=True,
                                                  header_file=self.target_file)
            if getters:
                for getter in getters:
                    if not getter.arguments or getter.has_const:
                        getter_args = {
                            "name": str(getter.name),
                            "dtype": str(getter.return_type),
                            "read_only": True
                        }
                        if getter_args['name'] in getter_arguments:
                            getter_args["read_only"] = False
                        self.parsed_data['properties'].append(
                            getter_args.copy())
        except RuntimeError:
            self.parsed_data['properties'] = []

        # all member functions
        # setters and getters do not return all member functions for a block
        try:
            self.parsed_data['member_functions'] = []
            query_methods = declarations.access_type_matcher_t('public')
            functions = main_class.member_functions(
                function=query_methods,
                allow_empty=True,
                header_file=self.target_file)
            if functions:
                for fcn in functions:
                    if str(fcn.name) not in [
                            main_class.name, '~' + main_class.name, 'make'
                    ]:
                        fcn_args = {"name": str(fcn.name), "arguments": []}
                        for argument in fcn.arguments:
                            args = {
                                "name": str(argument.name),
                                "dtype": str(argument.decl_type),
                                "default": argument.default_value
                            }
                            fcn_args['arguments'].append(args.copy())
                        self.parsed_data['member_functions'].append(
                            fcn_args.copy())
        except RuntimeError:
            self.parsed_data['member_functions'] = []

        # documentation
        try:
            _index = None
            header_file = codecs.open(self.target_file, 'r', 'cp932')
            self.parsed_data['docstring'] = re.compile(
                r'//.*?$|/\*.*?\*/',
                re.DOTALL | re.MULTILINE).findall(header_file.read())[2:]
            header_file.close()
            for doc in self.parsed_data['docstring']:
                if Constants.BLOCKTOOL in doc:
                    _index = self.parsed_data['docstring'].index(doc)
            if _index is not None:
                self.parsed_data['docstring'] = self.parsed_data[
                    'docstring'][:_index]
        except:
            self.parsed_data['docstring'] = []

        return self.parsed_data
Ejemplo n.º 10
0
def main():
    defines = []
    includes = []
    cxxflags = [
        '-Wno-unknown-attributes',
        '-Wno-unused-value',
        '-Wno-macro-redefined',
    ]
    compiler = 'g++'

    args = sys.argv[1:]
    while args and args[0].startswith('-'):
        arg = args.pop(0)
        if arg.startswith('-I'):
            include = arg[2:]
            includes.append(include)
        elif arg.startswith('-D'):
            define = arg[2:]
            defines.append(define)
        else:
            sys.stderr.write('error: unknown option %r\n' % arg)
            sys.exit(1)

    winsdk = True
    if winsdk:
        # Set up Clang compiler flags to use MinGW runtime
        if 0:
            # XXX: This doesn't work
            # http://stackoverflow.com/a/19839946
            p = subprocess.Popen([
                "x86_64-w64-mingw32-g++", "-x", "c++", "-E", "-Wp,-v", '-',
                '-fsyntax-only'
            ],
                                 stdin=open(os.devnull, 'rt'),
                                 stdout=open(os.devnull, 'wt'),
                                 stderr=subprocess.PIPE)
            includes.append('/usr/share/castxml/clang/include')
            for line in p.stderr:
                if line.startswith(' '):
                    include = line.strip()
                    if os.path.isdir(include):
                        includes.append(os.path.normpath(include))
        elif 0:
            # XXX: This matches what wclang does, but doensn't work neither
            cxxflags += [
                "-target",
                "x86_64-w64-mingw32",
                "-nostdinc",
                "-isystem",
                "/usr/lib/clang/3.6.0/include",
                "-isystem",
                "/usr/x86_64-w64-mingw32/include",
                "-isystem",
                "/usr/lib/gcc/x86_64-w64-mingw32/4.9-win32/include/c++",
                "-isystem",
                "/usr/lib/gcc/x86_64-w64-mingw32/4.9-win32/include/c++/x86_64-w64-mingw32",
            ]
        else:
            # This works somehow, but seems brittle
            includes += [
                '/usr/x86_64-w64-mingw32/include',
                '/usr/lib/gcc/x86_64-w64-mingw32/4.9-win32/include/c++',
                '/usr/lib/gcc/x86_64-w64-mingw32/4.9-win32/include/c++/x86_64-w64-mingw32',
            ]

        winver = 0x0602

        defines += [
            # emulate MinGW
            '__MINGW32__',
            '_WIN32',
            '_WIN64',
            '__declspec(x)=',
            # Avoid namespace pollution when including windows.h
            # http://support.microsoft.com/kb/166474
            'WIN32_LEAN_AND_MEAN',
            # Set Windows version to 8.1
            '_WIN32_WINNT=0x%04X' % winver,
            'WINVER=0x%04X' % winver,
            'NTDDI_VERSION=0x%04X0000' % winver,
            # Avoid C++ helper classes
            'D3D10_NO_HELPERS',
            'D3D11_NO_HELPERS',
            'D3D11_VIDEO_NO_HELPERS',
        ]

        # XXX: Change compiler?
        #compiler = 'cl'

        # XXX: This doesn't seem to work well
        cxxflags += [
            #'-m32',
            #'-target', 'x86_64-pc-mingw32',
        ]

    sys.stderr.write('Include path:\n')
    for include in includes:
        sys.stderr.write('  %s\n' % include)
    sys.stderr.write('Definitions:\n')
    for define in defines:
        sys.stderr.write('  %s\n' % define)

    import logging
    utils.loggers.set_level(logging.DEBUG)

    # Find the location of the xml generator (castxml or gccxml)
    generator_path, generator_name = utils.find_xml_generator("castxml")

    # Configure the xml generator
    config = parser.xml_generator_configuration_t(
        xml_generator_path=generator_path,
        xml_generator=generator_name,
        define_symbols=defines,
        include_paths=includes,
        cflags=' '.join(cxxflags),
        compiler=compiler,
        #keep_xml = True,
    )

    script_dir = os.path.dirname(__file__)
    headers = [
        os.path.join(script_dir, '..', '..', 'compat', 'winsdk_compat.h'),
        os.path.join(script_dir, 'cxx2api.h'),
    ]
    main_header = args[0]
    headers.append(main_header)

    decls = parser.parse(headers, config, parser.COMPILATION_MODE.ALL_AT_ONCE)
    global_ns = declarations.get_global_namespace(decls)

    def decl_filter(decl):
        location = decl.location
        if location is None:
            return False
        return os.path.basename(location.file_name) in args

    module, _ = os.path.splitext(main_header)
    visitor = decl2_dumper_t(module)
    visitor.start()
    for decl in global_ns.declarations:
        if not decl_filter(decl):
            continue
        visitor.decl = decl
        algorithm.apply_visitor(visitor, decl)
    visitor.finish()
Ejemplo n.º 11
0
def parse_file(filename, project_source_directory, include_directories,
               declaration_names, stream):
    """
    Entry point for parsing a file
    """
    # Find out the xml generator (gccxml or castxml)
    generator_path, generator_name = utils.find_xml_generator()

    # Configure the xml generator
    config = parser.xml_generator_configuration_t(
        start_with_declarations=declaration_names.split(" "),
        include_paths=include_directories.split(" "),
        xml_generator_path=generator_path,
        xml_generator=generator_name,
        cflags='-std=c++11 -Wc++11-extensions')

    # Parse source file
    decls = parser.parse([os.path.abspath(filename)],
                         config,
                         compilation_mode=parser.COMPILATION_MODE.ALL_AT_ONCE)

    # grab global namespace
    try:
        global_ns = declarations.get_global_namespace(decls)
    except RuntimeError:
        return []

    # output file preamble
    fileguard = "pybind_" + \
        filename.replace('.', '_').replace('/', '_').replace('-', '_')

    stream(
        """//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================
""")
    stream("#ifndef %s" % fileguard)
    stream("#define %s" % fileguard)
    stream("")
    stream("#include <pybind11/pybind11.h>")
    stream("")
    stream("#include \"%s\"" % os.path.relpath(
        os.path.abspath(filename),
        os.path.commonprefix([
            os.path.abspath(project_source_directory),
            os.path.abspath(filename)
        ])))
    stream("")
    includes = get_includes(global_ns, filename, project_source_directory)
    for include in includes:
        stream("#include \"%s\"" % include)
    if includes:
        stream("")
    stream("namespace py = pybind11;")
    stream("")

    wrapped_objects = {}

    for enum_ in global_ns.enumerations(allow_empty=True):
        if enum_.location.file_name != os.path.abspath(filename):
            continue
        if enum_.parent and type(enum_.parent).__name__.find('class_t') != -1:
            continue
        wrapped_objects[enum_] = parse_free_enumeration(enum_, stream)
        stream("")

    for class_ in global_ns.classes(allow_empty=True):
        if class_.location.file_name != os.path.abspath(filename) or\
                class_ in parsed_classes:
            continue
        if class_.parent and type(
                class_.parent).__name__.find('class_t') != -1:
            continue
        wrapped_objects[class_] = parse_class(class_, stream)
        stream("")

    all_functions = set()
    overloaded_functions = set()
    for fn_ in global_ns.free_functions(allow_empty=True):
        if fn_.location.file_name != os.path.abspath(filename):
            continue
        if fn_.name in all_functions:
            overloaded_functions.add(fn_.name)
        else:
            all_functions.add(fn_.name)

    for fn_ in global_ns.free_functions(allow_empty=True):
        if fn_.location.file_name != os.path.abspath(filename):
            continue
        wrapped_objects[fn_] = parse_free_function(
            fn_, fn_.name in overloaded_functions, stream)
        stream("")

    stream("#endif")
    parsed_classes.clear()

    return wrapped_objects
Ejemplo n.º 12
0
def parse_file(filename):
    # Find the location of the xml generator (castxml or gccxml)
    generator_path, generator_name = utils.find_xml_generator(name='castxml')

    # Configure the xml generator
    xml_generator_config = parser.xml_generator_configuration_t(
        # cflags="-std=gnu++11",
        cflags="-Wno-c++11-extensions",
        include_paths=["/Users/markoates/Repos/allegro_flare/include"],
        xml_generator_path=generator_path,
        compiler="g++",
        xml_generator=generator_name)

    # Parse the c++ file
    decls = parser.parse([filename], xml_generator_config)


    # output some GOLD!

    global_namespace = declarations.get_global_namespace(decls)

    # Search for functions which return a double. Two functions will be found
    criteria = declarations.calldef_matcher(
         #return_type="float",
         #header_file="/Users/markoates/Repos/allegro_flare/include/allegro_flare/" + filename)
         header_file=os.path.abspath(filename))
    # criteria = declarations.calldef_matcher(return_type=double_type)

    found_items = declarations.matcher.find(criteria, global_namespace)



    # populate the table with unique names

    count = 0
    for item in found_items:
        count = count + 1 
        cleaned_filename = re.match(r'/Users/markoates/Repos/allegro_flare/(.*)', item.location.file_name).group(1)

        # create `declaration_type`
        declaration_type = item.__class__.__name__
        if declaration_type[-2:] == "_t":
            declaration_type = declaration_type[:-2]
        declaration_type = declaration_type.replace('_', ' ')
        declaration_type = "%s" % declaration_type

        # create `declaration`
        declaration = str(item)
        declaration = re.match(r'(.*) \[.*\]$', declaration).group(1)

        # try to extract any preceeding '//' comments above the declaration
        in_source_documentation = extract_in_source_documentation(item.location.file_name, item.location.line)

        # get grandparent's name
        grandparent = item.parent.parent
        grandparent_name = grandparent.name if grandparent else ''

        # insert the content into the table
        parse_cache_make_table_connection.execute("INSERT INTO parsed_declarations VALUES (NULL,?,?,?,?,?,?,?,?,?,?,?,?);",
            (str(item), item.decl_string, declaration, item.name, declaration_type, str(item.parent.name), str(grandparent_name), item.attributes, cleaned_filename, str(item.location.line), in_source_documentation, "")
            )
        parse_cache_connection.commit()

    # create a list of unique items

    unique_item_names = set();
    for item in found_items:
        unique_item_names.update({item.name})


    # cross-correlate declarations in the database

    docs_connection = sqlite3.connect('doc_entries.db')
    docs_connection.row_factory = sqlite3.Row
    docs_c = docs_connection.cursor()


    found_items = 0
    unfound_items = 0

    for item in unique_item_names:
        docs_c.execute('SELECT * FROM entries WHERE decl=?', (item, ))
        entries = docs_c.fetchall()
        if len(entries) == 0:
            print item
            unfound_items += 1
        else:
            print_green(item + " - FOUND")
            found_items += 1

    if unfound_items == 0:
        print_func = print_green
    elif found_items == 0:
        print_func = print_red
    else:
        print_func = print_yellow

    print_func("==============================")
    print_func(str(found_items) + " items found.")
    print_func(str(unfound_items) + " matches missing.")
    print_func("==============================")

    return
Ejemplo n.º 13
0
def generate_python_wrapper(header_directories,
                            include_paths,
                            library_name,
                            cpp_filename,
                            declarations,
                            ignore_declarations={},
                            ignore_files={}):
    """
    Function to generate Python-C++ binding code by calling pygccxml and py++

    :param header_directories: directories with headers to create python binding for
    :type header_directories: list<string>
    :param include_paths: directories required as includes for the header files
    :type include_paths: list<string>
    :param library_name: the output name of the library to be created
    :type library_name: string
    :param cpp_filename: the output name of the C++ file to be created
    :type cpp_filename: string
    :param declarations: a list of declarations to be used for starting the AST syntax tree. See also
      pygccxml start_with_declarations or either -fxml-start(gccxml) or -castxml-start(castxml)
    :type declarations: list<string>
    :param ignore_declarations: a list of declarations to be ignored when generating C++ code
    :type ignore_declarations: list<string>
    :param ignore_files: a list of files to be ignored
    :type ignore_files: list<string>
    :return:
    """

    # Find out the xml generator (gccxml or castxml)
    generator_path, generator_name = utils.find_xml_generator()
    compiler = "g++"
    compiler_path = "/usr/bin/g++"

    # Create configuration for CastXML
    xml_generator_config = parser.xml_generator_configuration_t(
        xml_generator_path=generator_path,
        xml_generator=generator_name,
        compiler=compiler,
        compiler_path=compiler_path,
        start_with_declarations=declarations)

    # Set include dirs and cflags to avoid warnings and errors
    xml_generator_config.append_cflags("-std=c++11")

    for inc_dir in include_paths:
        xml_generator_config.include_paths.append(inc_dir)

    for header_dir in header_directories:
        xml_generator_config.include_paths.append(header_dir)

    # Find all relevant headers
    header_list = list()
    for header_dir in header_directories:
        header_list = header_list + get_list_of_files(
            header_dir, ignore_files=ignore_files)

    # Parses the source files and creates a module_builder object
    builder = module_builder.module_builder_t(
        header_list,
        xml_generator_path=generator_path,
        compilation_mode=parser.COMPILATION_MODE.ALL_AT_ONCE,
        xml_generator_config=xml_generator_config,
        indexing_suite_version=2)

    for ignore_declaration in ignore_declarations:
        builder.decls(lambda decl: ignore_declaration in decl.name).exclude()

    # for some reason there is a problem with variables named 'length'
    # the filename is empty and the line no is set to -1
    # therefore the declaration is not processed in the code later on
    # this is to fix length struct members
    for decl in builder.decls("length"):
        if isinstance(decl, decl_wrappers.variable_wrapper.variable_t):
            if isinstance(decl.parent, decl_wrappers.class_wrapper.class_t):
                decl.location.file_name = decl.parent.location.file_name
                decl.location.line = decl.parent.location.line + 1
                decl.ignore = False

    # Automatically detect properties and associated getters/setters
    builder.classes().add_properties(exclude_accessors=True)

    # Define a name for the module
    builder.build_code_creator(module_name=library_name)

    # Writes the C++ interface file
    builder.write_module(cpp_filename)
Ejemplo n.º 14
0
def main():
    defines = []
    includes = []
    cxxflags = ["-Wno-unknown-attributes", "-Wno-unused-value", "-Wno-macro-redefined"]
    compiler = "g++"

    args = sys.argv[1:]
    while args and args[0].startswith("-"):
        arg = args.pop(0)
        if arg.startswith("-I"):
            include = arg[2:]
            includes.append(include)
        elif arg.startswith("-D"):
            define = arg[2:]
            defines.append(define)
        else:
            sys.stderr.write("error: unknown option %r\n" % arg)
            sys.exit(1)

    winsdk = True
    if winsdk:
        # Set up Clang compiler flags to use MinGW runtime
        if 0:
            # XXX: This doesn't work
            # http://stackoverflow.com/a/19839946
            p = subprocess.Popen(
                ["x86_64-w64-mingw32-g++", "-x", "c++", "-E", "-Wp,-v", "-", "-fsyntax-only"],
                stdin=open(os.devnull, "rt"),
                stdout=open(os.devnull, "wt"),
                stderr=subprocess.PIPE,
            )
            includes.append("/usr/share/castxml/clang/include")
            for line in p.stderr:
                if line.startswith(" "):
                    include = line.strip()
                    if os.path.isdir(include):
                        includes.append(os.path.normpath(include))
        elif 0:
            # XXX: This matches what wclang does, but doensn't work neither
            cxxflags += [
                "-target",
                "x86_64-w64-mingw32",
                "-nostdinc",
                "-isystem",
                "/usr/lib/clang/3.6.0/include",
                "-isystem",
                "/usr/x86_64-w64-mingw32/include",
                "-isystem",
                "/usr/lib/gcc/x86_64-w64-mingw32/4.9-win32/include/c++",
                "-isystem",
                "/usr/lib/gcc/x86_64-w64-mingw32/4.9-win32/include/c++/x86_64-w64-mingw32",
            ]
        else:
            # This works somehow, but seems brittle
            includes += [
                "/usr/x86_64-w64-mingw32/include",
                "/usr/lib/gcc/x86_64-w64-mingw32/4.9-win32/include/c++",
                "/usr/lib/gcc/x86_64-w64-mingw32/4.9-win32/include/c++/x86_64-w64-mingw32",
            ]

        winver = 0x0602

        defines += [
            # emulate MinGW
            "__MINGW32__",
            "_WIN32",
            "_WIN64",
            "__declspec(x)=",
            # Avoid namespace pollution when including windows.h
            # http://support.microsoft.com/kb/166474
            "WIN32_LEAN_AND_MEAN",
            # Set Windows version to 8.1
            "_WIN32_WINNT=0x%04X" % winver,
            "WINVER=0x%04X" % winver,
            "NTDDI_VERSION=0x%04X0000" % winver,
            # Avoid C++ helper classes
            "D3D10_NO_HELPERS",
            "D3D11_NO_HELPERS",
            "D3D11_VIDEO_NO_HELPERS",
        ]

        # XXX: Change compiler?
        # compiler = 'cl'

        # XXX: This doesn't seem to work well
        cxxflags += [
            #'-m32',
            #'-target', 'x86_64-pc-mingw32',
        ]

    sys.stderr.write("Include path:\n")
    for include in includes:
        sys.stderr.write("  %s\n" % include)
    sys.stderr.write("Definitions:\n")
    for define in defines:
        sys.stderr.write("  %s\n" % define)

    import logging

    utils.loggers.set_level(logging.DEBUG)

    # Find the location of the xml generator (castxml or gccxml)
    generator_path, generator_name = utils.find_xml_generator("castxml")

    # Configure the xml generator
    config = parser.xml_generator_configuration_t(
        xml_generator_path=generator_path,
        xml_generator=generator_name,
        define_symbols=defines,
        include_paths=includes,
        cflags=" ".join(cxxflags),
        compiler=compiler,
        # keep_xml = True,
    )

    script_dir = os.path.dirname(__file__)
    headers = [os.path.join(script_dir, "..", "..", "compat", "winsdk_compat.h"), os.path.join(script_dir, "cxx2api.h")]
    main_header = args[0]
    headers.append(main_header)

    decls = parser.parse(headers, config, parser.COMPILATION_MODE.ALL_AT_ONCE)
    global_ns = declarations.get_global_namespace(decls)

    def decl_filter(decl):
        location = decl.location
        if location is None:
            return False
        return os.path.basename(location.file_name) in args

    module, _ = os.path.splitext(main_header)
    visitor = decl2_dumper_t(module)
    visitor.start()
    for decl in global_ns.declarations:
        if not decl_filter(decl):
            continue
        visitor.decl = decl
        algorithm.apply_visitor(visitor, decl)
    visitor.finish()
Ejemplo n.º 15
0
def parse_file(filename, project_source_directory, include_directories,
               declaration_names, stream):
    """
    Entry point for parsing a file
    """
    # Find out the xml generator (gccxml or castxml)
    generator_path, generator_name = utils.find_xml_generator()

    # Configure the xml generator
    config = parser.xml_generator_configuration_t(
        start_with_declarations=declaration_names.split(" "),
        include_paths=include_directories.split(" "),
        xml_generator_path=generator_path,
        xml_generator=generator_name,
        cflags='-std=c++11 -Wc++11-extensions')

    # Parse source file
    decls = parser.parse([os.path.abspath(filename)], config)

    # grab global namespace
    try:
        global_ns = declarations.get_global_namespace(decls)
    except RuntimeError:
        return []

    # output file preamble
    fileguard = "pybind_" + filename.replace('.', '_').replace('/', '_')

    stream("""//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================
""")
    stream("#ifndef %s" % fileguard)
    stream("#define %s" % fileguard)
    stream("")
    stream("#include <pybind11/pybind11.h>")
    stream("")
    stream("#include \"%s\"" % os.path.relpath(os.path.abspath(filename),
                                               os.path.commonprefix(
        [os.path.abspath(project_source_directory),
         os.path.abspath(filename)])))
    stream("")
    includes = get_includes(global_ns, filename, project_source_directory)
    for include in includes:
        stream("#include \"%s\"" % include)
    if includes:
        stream("")
    stream("namespace py = pybind11;")
    stream("")

    wrapped_objects = {}

    for enum_ in global_ns.enumerations(allow_empty=True):
        if enum_.location.file_name != os.path.abspath(filename):
            continue
        if enum_.parent and type(enum_.parent).__name__.find('class_t') != -1:
            continue
        wrapped_objects[enum_] = parse_free_enumeration(enum_, stream)
        stream("")

    for class_ in global_ns.classes(allow_empty=True):
        if class_.location.file_name != os.path.abspath(filename) or\
                class_ in parsed_classes:
            continue
        if class_.parent and type(class_.parent).__name__.find('class_t') != -1:
            continue
        wrapped_objects[class_] = parse_class(class_, stream)
        stream("")

    all_functions = set()
    overloaded_functions = set()
    for fn_ in global_ns.free_functions(allow_empty=True):
        if fn_.location.file_name != os.path.abspath(filename):
            continue
        if fn_.name in all_functions:
            overloaded_functions.add(fn_.name)
        else:
            all_functions.add(fn_.name)

    for fn_ in global_ns.free_functions(allow_empty=True):
        if fn_.location.file_name != os.path.abspath(filename):
            continue
        wrapped_objects[fn_] = parse_free_function(
            fn_, fn_.name in overloaded_functions, stream)
        stream("")

    stream("#endif")
    parsed_classes.clear()

    return wrapped_objects
Ejemplo n.º 16
0
    def get_header_info(self, namespace_to_parse):
        """
        PyGCCXML header code parser
        magic happens here!
        : returns the parsed header data in python dict
        : return dict keys: namespace, class, io_signature, make,
                       properties, methods
        : Can be used as an CLI command or an external API
        """
        module = self.modname.split('-')[-1]
        self.parsed_data['module_name'] = module
        self.parsed_data['filename'] = self.filename

        import hashlib
        hasher = hashlib.md5()
        with open(self.target_file, 'rb') as file_in:
            buf = file_in.read()
            hasher.update(buf)
        self.parsed_data['md5hash'] = hasher.hexdigest()

        # Right now if pygccxml is not installed, it will only handle the make function
        # TODO: extend this to other publicly declared functions in the h file
        if not PYGCCXML_AVAILABLE:
            self.parsed_data['parser'] = 'simple'
            (params, iosig, blockname) = self._parse_cc_h(self.target_file)
            self.parsed_data['target_namespace'] = namespace_to_parse

            namespace_dict = {}
            namespace_dict['name'] = "::".join(namespace_to_parse)
            class_dict = {}
            class_dict['name'] = blockname

            mf_dict = {
                "name": "make",
                "return_type":
                "::".join(namespace_to_parse + [blockname, "sptr"]),
                "has_static": "1"
            }

            args = []

            for p in params:
                arg_dict = {
                    "name": p['key'],
                    "dtype": p['type'],
                    "default": p['default']
                }
                args.append(arg_dict)

            mf_dict["arguments"] = args

            class_dict["member_functions"] = [mf_dict]
            namespace_dict["classes"] = [class_dict]
            self.parsed_data["namespace"] = namespace_dict

            return self.parsed_data
        else:
            self.parsed_data['parser'] = 'pygccxml'
            generator_path, generator_name = utils.find_xml_generator()
            xml_generator_config = parser.xml_generator_configuration_t(
                xml_generator_path=generator_path,
                xml_generator=generator_name,
                include_paths=self.include_paths,
                compiler='gcc',
                undefine_symbols=['__PIE__'],
                define_symbols=self.define_symbols,
                cflags='-std=c++17 -fPIC')
            decls = parser.parse([self.target_file], xml_generator_config)

            global_namespace = declarations.get_global_namespace(decls)

            # namespace
            # try:
            main_namespace = global_namespace
            for ns in namespace_to_parse:
                main_namespace = main_namespace.namespace(ns)
            if main_namespace is None:
                raise BlockToolException('namespace cannot be none')
            self.parsed_data['target_namespace'] = namespace_to_parse

            self.parsed_data['namespace'] = self.parse_namespace(
                main_namespace)

            # except RuntimeError:
            #     raise BlockToolException(
            #         'Invalid namespace format in the block header file')

            # namespace

            return self.parsed_data
Ejemplo n.º 17
0
def parse_file(filename):
    # Find the location of the xml generator (castxml or gccxml)
    generator_path, generator_name = utils.find_xml_generator(name='castxml')

    # Configure the xml generator
    xml_generator_config = parser.xml_generator_configuration_t(
        # cflags="-std=gnu++11",
        cflags="-Wno-c++11-extensions",
        include_paths=["/Users/markoates/Repos/allegro_flare/include"],
        xml_generator_path=generator_path,
        compiler="g++",
        xml_generator=generator_name)

    # Parse the c++ file
    decls = parser.parse([filename], xml_generator_config)

    # output some GOLD!

    global_namespace = declarations.get_global_namespace(decls)

    # Search for functions which return a double. Two functions will be found
    criteria = declarations.calldef_matcher(
        #return_type="float",
        #header_file="/Users/markoates/Repos/allegro_flare/include/allegro_flare/" + filename)
        header_file=os.path.abspath(filename))
    # criteria = declarations.calldef_matcher(return_type=double_type)

    found_items = declarations.matcher.find(criteria, global_namespace)

    # populate the table with unique names

    count = 0
    for item in found_items:
        count = count + 1
        #print item.location.file_name + " : " + str(item.location.line)
        cleaned_filename = re.match(
            r'/Users/markoates/Repos/allegro_flare/(.*)',
            item.location.file_name).group(1)

        # create `declaration_type`
        declaration_type = item.__class__.__name__
        if declaration_type[-2:] == "_t":
            declaration_type = declaration_type[:-2]
        declaration_type = declaration_type.replace('_', ' ')
        declaration_type = "%s" % declaration_type

        # create `declaration`
        declaration = str(item)
        declaration = re.match(r'(.*) \[.*\]$', declaration).group(1)

        parse_cache_make_table_connection.execute(
            "INSERT INTO parsed_declarations VALUES (NULL,?,?,?,?,?,?,?,?,?,?,?,?);",
            (str(item), item.decl_string, declaration, item.name,
             declaration_type, str(item.parent.name), str(
                 item.top_parent.name), item.attributes, cleaned_filename,
             str(item.location.line), "", ""))
        parse_cache_connection.commit()

    # create a list of unique items

    unique_item_names = set()
    for item in found_items:
        unique_item_names.update({item.name})

    # cross-correlate declarations in the database

    docs_connection = sqlite3.connect('doc_entries.db')
    docs_connection.row_factory = sqlite3.Row
    docs_c = docs_connection.cursor()

    found_items = 0
    unfound_items = 0

    for item in unique_item_names:
        docs_c.execute('SELECT * FROM entries WHERE decl=?', (item, ))
        entries = docs_c.fetchall()
        if len(entries) == 0:
            print item
            unfound_items += 1
        else:
            print_green(item + " - FOUND")
            found_items += 1

    if unfound_items == 0:
        print_func = print_green
    elif found_items == 0:
        print_func = print_red
    else:
        print_func = print_yellow

    print_func("==============================")
    print_func(str(found_items) + " items found.")
    print_func(str(unfound_items) + " matches missing.")
    print_func("==============================")

    return
Ejemplo n.º 18
0
    def get_header_info(self):
        """
        PyGCCXML header code parser
        magic happens here!
        : returns the parsed header data in python dict
        : return dict keys: namespace, class, io_signature, make,
                       properties, methods
        : Can be used as an CLI command or an extenal API
        """
        gr = self.modname.split('-')[0]
        module = self.modname.split('-')[-1]
        generator_path, generator_name = utils.find_xml_generator()
        xml_generator_config = parser.xml_generator_configuration_t(
            xml_generator_path=generator_path,
            xml_generator=generator_name,
            compiler='gcc')
        decls = parser.parse(
            [self.target_file], xml_generator_config)
        global_namespace = declarations.get_global_namespace(decls)

        # namespace
        try:
            self.parsed_data['namespace'] = []
            ns = global_namespace.namespace(gr)
            if ns is None:
                raise BlockToolException
            main_namespace = ns.namespace(module)
            if main_namespace is None:
                raise BlockToolException('namespace cannot be none')
            self.parsed_data['namespace'] = [gr, module]
            if main_namespace.declarations:
                for _namespace in main_namespace.declarations:
                    if isinstance(_namespace, declarations.namespace_t):
                        if Constants.KERNEL not in str(_namespace):
                            main_namespace = _namespace
                            self.parsed_data['namespace'].append(
                                str(_namespace).split('::')[-1].split(' ')[0])
        except RuntimeError:
            raise BlockToolException(
                'Invalid namespace format in the block header file')

        # class
        try:
            self.parsed_data['class'] = ''
            for _class in main_namespace.declarations:
                if isinstance(_class, declarations.class_t):
                    main_class = _class
                    self.parsed_data['class'] = str(_class).split('::')[
                        2].split(' ')[0]
        except RuntimeError:
            raise BlockToolException(
                'Block header namespace {} must consist of a valid class instance'.format(module))

        # io_signature, message_ports
        self.parsed_data['io_signature'] = {}
        self.parsed_data['message_port'] = {}
        if os.path.isfile(self.impl_file) and exist_comments(self):
            self.parsed_data['io_signature'] = io_signature(
                self.impl_file)
            self.parsed_data['message_port'] = message_port(
                self.impl_file)
            read_comments(self)
        elif os.path.isfile(self.impl_file) and not exist_comments(self):
            self.parsed_data['io_signature'] = io_signature(
                self.impl_file)
            self.parsed_data['message_port'] = message_port(
                self.impl_file)
            if self.addcomments:
                add_comments(self)
        elif not os.path.isfile(self.impl_file) and exist_comments(self):
            read_comments(self)
        else:
            self.parsed_data['io_signature'] = {
                "input": [],
                "output": []
            }
            self.parsed_data['message_port'] = self.parsed_data['io_signature']

        # make
        try:
            self.parsed_data['make'] = {}
            self.parsed_data['make']['arguments'] = []
            query_m = declarations.custom_matcher_t(
                lambda mem_fun: mem_fun.name.startswith('make'))
            query_make = query_m & declarations.access_type_matcher_t('public')
            make_func = main_class.member_functions(function=query_make,
                                                    allow_empty=True,
                                                    header_file=self.target_file)
            criteria = declarations.calldef_matcher(name='make')
            _make_fun = declarations.matcher.get_single(criteria, main_class)
            _make_fun = str(_make_fun).split(
                'make')[-1].split(')')[0].split('(')[1].lstrip().rstrip().split(',')
            if make_func:
                for arg in make_func[0].arguments:
                    for _arg in _make_fun:
                        if str(arg.name) in _arg:
                            make_arguments = {
                                "name": str(arg.name),
                                "dtype": str(arg.decl_type),
                                "default": ""
                            }
                            if re.findall(r'[-+]?\d*\.\d+|\d+', _arg):
                                make_arguments['default'] = re.findall(
                                    r'[-+]?\d*\.\d+|\d+', _arg)[0]
                            elif re.findall(r'\"(.+?)\"', _arg):
                                make_arguments['default'] = re.findall(
                                    r'\"(.+?)\"', _arg)[0]
                            elif "true" in _arg:
                                make_arguments['default'] = "True"
                            elif "false" in _arg:
                                make_arguments['default'] = "False"
                    self.parsed_data['make']['arguments'].append(
                        make_arguments.copy())
        except RuntimeError:
            self.parsed_data['make'] = {}
            self.parsed_data['make']['arguments'] = []

        # setters
        try:
            self.parsed_data['methods'] = []
            query_methods = declarations.access_type_matcher_t('public')
            setters = main_class.member_functions(function=query_methods,
                                                  allow_empty=True,
                                                  header_file=self.target_file)
            getter_arguments = []
            if setters:
                for setter in setters:
                    if str(setter.name).startswith('set_') and setter.arguments:
                        setter_args = {
                            "name": str(setter.name),
                            "arguments_type": []
                        }
                        for argument in setter.arguments:
                            args = {
                                "name": str(argument.name),
                                "dtype": str(argument.decl_type)
                            }
                            getter_arguments.append(args['name'])
                            setter_args['arguments_type'].append(args.copy())
                        self.parsed_data['methods'].append(setter_args.copy())
        except RuntimeError:
            self.parsed_data['methods'] = []

        # getters
        try:
            self.parsed_data['properties'] = []
            query_properties = declarations.access_type_matcher_t('public')
            getters = main_class.member_functions(function=query_properties,
                                                  allow_empty=True,
                                                  header_file=self.target_file)
            if getters:
                for getter in getters:
                    if not getter.arguments or getter.has_const:
                        getter_args = {
                            "name": str(getter.name),
                            "dtype": str(getter.return_type),
                            "read_only": True
                        }
                        if getter_args['name'] in getter_arguments:
                            getter_args["read_only"] = False
                        self.parsed_data['properties'].append(
                            getter_args.copy())
        except RuntimeError:
            self.parsed_data['properties'] = []

        # documentation
        try:
            _index = None
            header_file = codecs.open(self.target_file, 'r', 'cp932')
            self.parsed_data['docstring'] = re.compile(
                r'//.*?$|/\*.*?\*/', re.DOTALL | re.MULTILINE).findall(
                    header_file.read())[2:]
            header_file.close()
            for doc in self.parsed_data['docstring']:
                if Constants.BLOCKTOOL in doc:
                    _index = self.parsed_data['docstring'].index(doc)
            if _index is not None:
                self.parsed_data['docstring'] = self.parsed_data['docstring'][: _index]
        except:
            self.parsed_data['docstring'] = []

        return self.parsed_data
Ejemplo n.º 19
0
def generate_python_wrapper(header_directories,
                            include_paths,
                            library_name,
                            cpp_filename,
                            declarations,
                            main_namespace="",
                            ignore_declarations={},
                            ignore_files={}):
    """
    Function to generate Python-C++ binding code by calling pygccxml and py++

    :param header_directories: directories with headers to create python binding for
    :type header_directories: list<string>
    :param include_paths: directories required as includes for the header files
    :type include_paths: list<string>
    :param library_name: the output name of the library to be created
    :type library_name: string
    :param cpp_filename: the output name of the C++ file to be created
    :type cpp_filename: string
    :param declarations: a list of declarations to be used for starting the AST syntax tree. See also
      pygccxml start_with_declarations or either -fxml-start(gccxml) or -castxml-start(castxml)
    :type declarations: list<string>
    :param ignore_declarations: a list of declarations to be ignored when generating C++ code
    :type ignore_declarations: list<string>
    :param ignore_files: a list of files to be ignored
    :type ignore_files: list<string>
    :return:
    """

    warnings.filterwarnings(action="once", category=DeprecationWarning)

    # Find out the xml generator (gccxml or castxml)
    generator_path, generator_name = utils.find_xml_generator()
    compiler = "g++"
    compiler_path = "/usr/bin/g++"

    # Create configuration for CastXML
    xml_generator_config = parser.xml_generator_configuration_t(
        xml_generator_path=generator_path,
        xml_generator=generator_name,
        compiler=compiler,
        compiler_path=compiler_path,
        start_with_declarations=declarations)

    # Set include dirs and cflags to avoid warnings and errors
    xml_generator_config.append_cflags("-std=c++11")

    for inc_dir in include_paths:
        xml_generator_config.include_paths.append(inc_dir)

    for header_dir in header_directories:
        xml_generator_config.include_paths.append(header_dir)

    # Find all relevant headers
    header_list = list()
    for header_dir in header_directories:
        header_list = header_list + get_list_of_files(
            header_dir, ignore_files=ignore_files)

    # Parses the source files and creates a module_builder object
    builder = module_builder.module_builder_t(
        header_list,
        xml_generator_path=generator_path,
        compilation_mode=parser.COMPILATION_MODE.ALL_AT_ONCE,
        xml_generator_config=xml_generator_config,
        indexing_suite_version=2)

    # for some reason there is a problem with variables named 'length'
    # the filename is empty and the line no is set to -1
    # therefore the declaration is not processed in the code later on
    # this is to fix length struct members
    for decl in builder.decls("length"):
        if isinstance(decl, decl_wrappers.variable_wrapper.variable_t):
            if isinstance(decl.parent, decl_wrappers.class_wrapper.class_t):
                if decl.ignore:
                    decl.location.file_name = decl.parent.location.file_name
                    decl.location.line = decl.parent.location.line + 1
                    decl.ignore = False

    if main_namespace != "":
        if main_namespace.startswith("::"):
            main_namespace = main_namespace[2:]
        top_namespace_split = main_namespace.find(":")
        top_namespace = ""
        if top_namespace_split > 1:
            top_namespace = main_namespace[:top_namespace_split + 2]
        print(
            "Main namespace defined, namespace filtering enabled: top-namespace '{}' main-namespace '{}'"
            .format(top_namespace, main_namespace))

    for decl in builder.decls():
        for ignore_declaration in ignore_declarations:
            if ignore_declaration in decl.name or ignore_declaration in decl.alias:
                decl.exclude()
                decl.ignore = True
                decl.already_exposed = True
                # print("declaration to ignore found: {} (alias {})".format(decl, decl.alias))
                break
        if main_namespace != "":
            if isinstance(
                    decl, decl_wrappers.class_wrapper.class_t) or isinstance(
                        decl, decl_wrappers.class_wrapper.
                        class_declaration_t) or isinstance(
                            decl, decl_wrappers.typedef_wrapper.typedef_t):
                decl_full_string = "{}".format(decl)
                if main_namespace in decl_full_string:
                    # namespace present, ok
                    # print("typedef/class main namespace found: {}".format(decl_full_string))
                    continue
                if decl_full_string in main_namespace:
                    # declaration is a parent of main namespace, ok
                    # print("typedef/class declaration of upper level namespace found: {}".format(decl_full_string))
                    continue
                if top_namespace != "" and not top_namespace in decl_full_string:
                    # global or std defaults, ok
                    # print("typedef/class outside top namespace found: {}".format(decl_full_string))
                    continue
                # print("typedef/class outside of main namespace found. Ignoring: {}".format(decl_full_string))
                decl.exclude()
                decl.ignore = True
                decl.already_exposed = True

    # debug declarations
    # builder.print_declarations()

    # Automatically detect properties and associated getters/setters
    builder.classes().add_properties(exclude_accessors=True)

    # Define a name for the module
    builder.build_code_creator(module_name=library_name)

    # Writes the C++ interface file
    if os.path.exists(cpp_filename):
        os.remove(cpp_filename)
    builder.write_module(cpp_filename)

    print("generate_python_wrapper(): {} written.".format(cpp_filename))
Ejemplo n.º 20
0
from pygccxml import utils
from pygccxml import declarations
from pygccxml import parser

import os
import sys
import warnings

warnings.simplefilter("error", Warning)
# Find out the file location within the sources tree
this_module_dir_path = os.path.abspath(
    os.path.dirname(sys.modules[__name__].__file__))

# Find the location of the xml generator (castxml or gccxml)
generator_path, generator_name = utils.find_xml_generator()

# Configure the xml generator
xml_generator_config = parser.xml_generator_configuration_t(
    xml_generator_path=generator_path, xml_generator=generator_name)

# The c++ file we want to parse
filename = "example.hpp"
filename = this_module_dir_path + "/" + filename

# Parse the c++ file
decls = parser.parse([filename], xml_generator_config)

# Get access to the global namespace
global_namespace = declarations.get_global_namespace(decls)
Ejemplo n.º 21
0
        if b'\x00' in dat:
            length = dat.index(b'\x00')
            return dat[:length]
        else:
            return dat[:16]

    def get_formatted(self, data):
        return repr(data)[1:]


with open(os.path.join(os.path.dirname(__file__), "../stm/src/common/slots.h"),
          "r") as f:
    full_text = f.read()

# parse the file
gp, gn = utils.find_xml_generator()
xml_generator_config = parser.xml_generator_configuration_t(
    xml_generator_path=gp, xml_generator=gn)

declarations_in_file = parser.parse_string(full_text, xml_generator_config)


def _convert_opaque_to_token(x):
    """
    convert either a string from re or a value from pygccxml to a struct token
    """

    if type(x) is str:
        return {
            "BOOL": "?",
            "UINT8_T": "B",
Ejemplo n.º 22
0
# See http://www.boost.org/LICENSE_1_0.txt

from pygccxml import utils
from pygccxml import declarations
from pygccxml import parser

import os
import sys
import warnings
warnings.simplefilter("error", Warning)
# Find out the file location within the sources tree
this_module_dir_path = os.path.abspath(
    os.path.dirname(sys.modules[__name__].__file__))

# Find the location of the xml generator (castxml or gccxml)
generator_path, generator_name = utils.find_xml_generator()

# Configure the xml generator
xml_generator_config = parser.xml_generator_configuration_t(
    xml_generator_path=generator_path,
    xml_generator=generator_name)

# The c++ file we want to parse
filename = "example.hpp"
filename = this_module_dir_path + "/" + filename

# Parse the c++ file
decls = parser.parse([filename], xml_generator_config)

# Get access to the global namespace
global_namespace = declarations.get_global_namespace(decls)
Ejemplo n.º 23
0
    def __init__(self, license='//greetings earthling', cpp_revision=201103):
        self.license = license

        logging.basicConfig(level=logging.INFO)

        parser = argparse.ArgumentParser(
            description='Generate Python Bindings')
        parser.add_argument('-I',
                            '--include',
                            dest='includes',
                            action='append')
        parser.add_argument('-D', '--define', dest='defines', action='append')
        parser.add_argument('-o',
                            '--output_dir',
                            dest='output_dir',
                            action='store')
        parser.add_argument('-M',
                            '--module_name',
                            dest='module_name',
                            action='store')
        parser.add_argument('--dep_module',
                            dest='dep_modules',
                            action='append',
                            default=[])
        parser.add_argument('--decl_db',
                            dest='decl_dbs',
                            action='append',
                            default=[])
        parser.add_argument('sources', nargs='+')
        self.args = parser.parse_args()

        # Find the location of the xml generator (castxml or gccxml)
        generator_path, generator_name = utils.find_xml_generator()

        # Configure the xml generator
        xml_generator_config = gccxmlparser.xml_generator_configuration_t(
            xml_generator_path=generator_path,
            xml_generator=generator_name,
            include_paths=self.args.includes,
            define_symbols=self.args.defines)

        self.mb = pyplusplus.module_builder.module_builder_t(
            self.args.sources,
            working_directory=os.path.abspath(os.path.curdir),
            indexing_suite_version=2,
            cplusplus_revision=cpp_revision,
            xml_generator_config=xml_generator_config)

        self.number_of_files = -1

        try:
            os.makedirs(self.args.output_dir)
        except OSError:
            pass

        with open(os.path.join(self.args.output_dir, 'generate.sh'), 'w') as f:
            f.writelines([
                '#!/bin/sh\n', 'export PYTHONPATH="{}"\n'.format(
                    os.pathsep.join(sys.path).replace('"', '\\"').strip(':')),
                'ipython $@ -- {}\n'.format(' \\\n'.join(sys.argv))
            ])

        for cls in self.mb.classes(allow_empty=True):
            cls.redefine_operators = False

        # Register dependency modules so that already exposed declarations are
        # known to us
        decl_db_ext = '.exposed_decl.pypp.txt'
        for decl_db in self.args.decl_dbs:
            if not os.path.exists(decl_db):
                raise ValueError('Couldn\'t find "%s"' % decl_db)
            if not decl_db.endswith(decl_db_ext):
                raise ValueError('Invalid file ending on "%s", expected "%s"' %
                                 (decl_db, decl_db_ext))
            module = os.path.basename(decl_db)[:-len(decl_db_ext)]
            self.mb.register_module_dependency(decl_db, module)

        # Store the aliases of already exposed declarations
        self.already_exposed_aliases = [(d, d.alias)
                                        for d in self.mb.global_ns.decls()
                                        if d.already_exposed]

        for module in self.args.dep_modules:
            self.mb.add_registration_code('bp::import("%s");' % module, False)