Esempio n. 1
0
    def __init__(self,
                 toolchain=None,
                 cc="gcc",
                 cflags="-std=c99 -O3 -fPIC".split(),
                 ldflags="-shared".split(),
                 libraries=[],
                 include_dirs=[],
                 library_dirs=[],
                 defines=[],
                 source_suffix="c"):
        # try to get a default toolchain
        # or subclass supplied version if available
        self.toolchain = toolchain
        if toolchain is None:
            try:
                self.toolchain = guess_toolchain()
            except (ToolchainGuessError, ExecError):
                # missing compiler python was built with (likely, Conda)
                # use a default GCCToolchain
                logger = logging.getLogger(__name__)
                logger.warn("Default toolchain guessed from python config "
                            "not found, replacing with default GCCToolchain.")
                # this is ugly, but I'm not sure there's a clean way to copy the
                # default args
                self.toolchain = GCCToolchain(
                    cc="gcc",
                    cflags="-std=c99 -O3 -fPIC".split(),
                    ldflags="-shared".split(),
                    libraries=[],
                    library_dirs=[],
                    defines=[],
                    undefines=[],
                    source_suffix="c",
                    so_ext=".so",
                    o_ext=".o",
                    include_dirs=[])

        if toolchain is None:
            # copy in all differing values
            diff = {
                "cc": cc,
                "cflags": cflags,
                "ldflags": ldflags,
                "libraries": libraries,
                "include_dirs": include_dirs,
                "library_dirs": library_dirs,
                "defines": defines
            }
            # filter empty and those equal to toolchain defaults
            diff = {
                k: v
                for k, v in diff.items()
                if v and (not hasattr(self.toolchain, k)
                          or getattr(self.toolchain, k) != v)
            }
            self.toolchain = self.toolchain.copy(**diff)
        self.tempdir = tempfile.mkdtemp(prefix="tmp_loopy")
        self.source_suffix = source_suffix
Esempio n. 2
0
    def __init__(self,
                 toolchain=None,
                 cc='gcc',
                 cflags='-std=c99 -O3 -fPIC'.split(),
                 ldflags='-shared'.split(),
                 libraries=[],
                 include_dirs=[],
                 library_dirs=[],
                 defines=[],
                 source_suffix='c'):
        # try to get a default toolchain
        # or subclass supplied version if available
        self.toolchain = toolchain
        if toolchain is None:
            try:
                self.toolchain = guess_toolchain()
            except (ToolchainGuessError, ExecError):
                # missing compiler python was built with (likely, Conda)
                # use a default GCCToolchain
                logger = logging.getLogger(__name__)
                logger.warn('Default toolchain guessed from python config '
                            'not found, replacing with default GCCToolchain.')
                # this is ugly, but I'm not sure there's a clean way to copy the
                # default args
                self.toolchain = GCCToolchain(
                    cc='gcc',
                    cflags='-std=c99 -O3 -fPIC'.split(),
                    ldflags='-shared'.split(),
                    libraries=[],
                    library_dirs=[],
                    defines=[],
                    undefines=[],
                    source_suffix='c',
                    so_ext='.so',
                    o_ext='.o',
                    include_dirs=[])

        if toolchain is None:
            # copy in all differing values
            diff = {
                'cc': cc,
                'cflags': cflags,
                'ldflags': ldflags,
                'libraries': libraries,
                'include_dirs': include_dirs,
                'library_dirs': library_dirs,
                'defines': defines
            }
            # filter empty and those equal to toolchain defaults
            diff = dict((k, v) for k, v in six.iteritems(diff)
                        if v and (not hasattr(self.toolchain, k)
                                  or getattr(self.toolchain, k) != v))
            self.toolchain = self.toolchain.copy(**diff)
        self.tempdir = tempfile.mkdtemp(prefix="tmp_loopy")
        self.source_suffix = source_suffix
Esempio n. 3
0
    def __init__(self, toolchain=None,
                 cc='gcc', cflags='-std=c99 -O3 -fPIC'.split(),
                 ldflags='-shared'.split(), libraries=[],
                 include_dirs=[], library_dirs=[], defines=[],
                 source_suffix='c'):
        # try to get a default toolchain
        # or subclass supplied version if available
        self.toolchain = toolchain
        if toolchain is None:
            try:
                self.toolchain = guess_toolchain()
            except (ToolchainGuessError, ExecError):
                # missing compiler python was built with (likely, Conda)
                # use a default GCCToolchain
                logger = logging.getLogger(__name__)
                logger.warn('Default toolchain guessed from python config '
                            'not found, replacing with default GCCToolchain.')
                # this is ugly, but I'm not sure there's a clean way to copy the
                # default args
                self.toolchain = GCCToolchain(
                    cc='gcc',
                    cflags='-std=c99 -O3 -fPIC'.split(),
                    ldflags='-shared'.split(),
                    libraries=[],
                    library_dirs=[],
                    defines=[],
                    undefines=[],
                    source_suffix='c',
                    so_ext='.so',
                    o_ext='.o',
                    include_dirs=[])

        if toolchain is None:
            # copy in all differing values
            diff = {'cc': cc,
                    'cflags': cflags,
                    'ldflags': ldflags,
                    'libraries': libraries,
                    'include_dirs': include_dirs,
                    'library_dirs': library_dirs,
                    'defines': defines}
            # filter empty and those equal to toolchain defaults
            diff = dict((k, v) for k, v in six.iteritems(diff)
                    if v and (not hasattr(self.toolchain, k) or
                              getattr(self.toolchain, k) != v))
            self.toolchain = self.toolchain.copy(**diff)
        self.tempdir = tempfile.mkdtemp(prefix="tmp_loopy")
        self.source_suffix = source_suffix
Esempio n. 4
0
def guess_toolchain():
    # copied from loopy/target/c/c_execution.py
    try:
        toolchain = guess_toolchain_base()
    except (ToolchainGuessError, ExecError):
        # missing compiler python was built with (likely, Conda)
        # use a default GCCToolchain
        # this is ugly, but I'm not sure there's a clean way to copy the
        # default args
        toolchain = GCCToolchain(
            cc="gcc",
            cflags="-std=c99 -O3 -fPIC".split(),
            ldflags=["-shared"],
            libraries=[],
            library_dirs=[],
            defines=[],
            undefines=[],
            source_suffix="c",
            so_ext=".so",
            o_ext=".o",
            include_dirs=[])

    return toolchain
Esempio n. 5
0
class CCompiler:
    """
    The compiler module handles invocation of compilers to generate a shared lib
    using codepy, which can subsequently be loaded via ctypes.

    The general strategy here is as follows:

    1.  A :class:`codepy.Toolchain` is guessed from distutils.
        The user may override any flags obtained therein by passing in arguements
        to cc, cflags, etc.

    2.  The kernel source is built into and object first, then made into a shared
        library using :meth:`codepy.jit.compile_from_string`, which additionally
        handles caching

    3.  The resulting shared library is turned into a :class:`ctypes.CDLL`
        to enable calling by the invoker generated by, e.g.,
        :class:`CExecutionWrapperGenerator`
    """

    def __init__(self, toolchain=None,
                 cc="gcc", cflags="-std=c99 -O3 -fPIC".split(),
                 ldflags="-shared".split(), libraries=[],
                 include_dirs=[], library_dirs=[], defines=[],
                 source_suffix="c"):
        # try to get a default toolchain
        # or subclass supplied version if available
        self.toolchain = toolchain
        if toolchain is None:
            try:
                self.toolchain = guess_toolchain()
            except (ToolchainGuessError, ExecError):
                # missing compiler python was built with (likely, Conda)
                # use a default GCCToolchain
                logger = logging.getLogger(__name__)
                logger.warn("Default toolchain guessed from python config "
                            "not found, replacing with default GCCToolchain.")
                # this is ugly, but I'm not sure there's a clean way to copy the
                # default args
                self.toolchain = GCCToolchain(
                    cc="gcc",
                    cflags="-std=c99 -O3 -fPIC".split(),
                    ldflags="-shared".split(),
                    libraries=[],
                    library_dirs=[],
                    defines=[],
                    undefines=[],
                    source_suffix="c",
                    so_ext=".so",
                    o_ext=".o",
                    include_dirs=[])

        if toolchain is None:
            # copy in all differing values
            diff = {"cc": cc,
                    "cflags": cflags,
                    "ldflags": ldflags,
                    "libraries": libraries,
                    "include_dirs": include_dirs,
                    "library_dirs": library_dirs,
                    "defines": defines}
            # filter empty and those equal to toolchain defaults
            diff = {k: v for k, v in diff.items()
                    if v and (not hasattr(self.toolchain, k) or
                              getattr(self.toolchain, k) != v)}
            self.toolchain = self.toolchain.copy(**diff)
        self.tempdir = tempfile.mkdtemp(prefix="tmp_loopy")
        self.source_suffix = source_suffix

    def _tempname(self, name):
        """Build temporary filename path in tempdir."""
        return os.path.join(self.tempdir, name)

    def build(self, name, code, debug=False, wait_on_error=None,
                     debug_recompile=True):
        """Compile code, build and load shared library."""
        logger.debug(code)
        c_fname = self._tempname("code." + self.source_suffix)

        # build object
        _, mod_name, ext_file, recompiled = \
            compile_from_string(self.toolchain, name, code, c_fname,
                                self.tempdir, debug, wait_on_error,
                                debug_recompile, False)

        if recompiled:
            logger.debug(f"Kernel {name} compiled from source")
        else:
            logger.debug(f"Kernel {name} retrieved from cache")

        # and return compiled
        return ctypes.CDLL(ext_file)
Esempio n. 6
0
class CCompiler(object):
    """
    The compiler module handles invocation of compilers to generate a shared lib
    using codepy, which can subsequently be loaded via ctypes.

    The general strategy here is as follows:

    1.  A :class:`codepy.Toolchain` is guessed from distutils.
        The user may override any flags obtained therein by passing in arguements
        to cc, cflags, etc.

    2.  The kernel source is built into and object first, then made into a shared
        library using :meth:`codepy.jit.compile_from_string`, which additionally
        handles caching

    3.  The resulting shared library is turned into a :class:`ctypes.CDLL`
        to enable calling by the invoker generated by, e.g.,
        :class:`CExecutionWrapperGenerator`
    """

    def __init__(self, toolchain=None,
                 cc='gcc', cflags='-std=c99 -O3 -fPIC'.split(),
                 ldflags='-shared'.split(), libraries=[],
                 include_dirs=[], library_dirs=[], defines=[],
                 source_suffix='c'):
        # try to get a default toolchain
        # or subclass supplied version if available
        self.toolchain = toolchain
        if toolchain is None:
            try:
                self.toolchain = guess_toolchain()
            except (ToolchainGuessError, ExecError):
                # missing compiler python was built with (likely, Conda)
                # use a default GCCToolchain
                logger = logging.getLogger(__name__)
                logger.warn('Default toolchain guessed from python config '
                            'not found, replacing with default GCCToolchain.')
                # this is ugly, but I'm not sure there's a clean way to copy the
                # default args
                self.toolchain = GCCToolchain(
                    cc='gcc',
                    cflags='-std=c99 -O3 -fPIC'.split(),
                    ldflags='-shared'.split(),
                    libraries=[],
                    library_dirs=[],
                    defines=[],
                    undefines=[],
                    source_suffix='c',
                    so_ext='.so',
                    o_ext='.o',
                    include_dirs=[])

        if toolchain is None:
            # copy in all differing values
            diff = {'cc': cc,
                    'cflags': cflags,
                    'ldflags': ldflags,
                    'libraries': libraries,
                    'include_dirs': include_dirs,
                    'library_dirs': library_dirs,
                    'defines': defines}
            # filter empty and those equal to toolchain defaults
            diff = dict((k, v) for k, v in six.iteritems(diff)
                    if v and (not hasattr(self.toolchain, k) or
                              getattr(self.toolchain, k) != v))
            self.toolchain = self.toolchain.copy(**diff)
        self.tempdir = tempfile.mkdtemp(prefix="tmp_loopy")
        self.source_suffix = source_suffix

    def _tempname(self, name):
        """Build temporary filename path in tempdir."""
        return os.path.join(self.tempdir, name)

    def build(self, name, code, debug=False, wait_on_error=None,
                     debug_recompile=True):
        """Compile code, build and load shared library."""
        logger.debug(code)
        c_fname = self._tempname('code.' + self.source_suffix)

        # build object
        _, mod_name, ext_file, recompiled = \
            compile_from_string(self.toolchain, name, code, c_fname,
                                self.tempdir, debug, wait_on_error,
                                debug_recompile, False)

        if recompiled:
            logger.debug('Kernel {0} compiled from source'.format(name))
        else:
            logger.debug('Kernel {0} retrieved from cache'.format(name))

        # and return compiled
        return ctypes.CDLL(ext_file)
Esempio n. 7
0
def get_toolchain(lang, shared=True, executable=True, **kwargs):
    """
    Return a codepy :class:`Toolchain` to build / link pyJac files.

    Parameters
    ----------
    lang: str
        The language to build
    shared: bool [True]
        If true, build a shared library
    executable: bool [True]
        If true, build a _executable_ shared library; note: requires
        :param:`shared`=True
    **kwargs:
    """

    # compilation flags
    compile_flags = opt_flags
    from pyjac.utils import get_env_val
    # read debug flag from ENV or config
    if get_env_val('debug'):
        compile_flags = debug_flags

    # link flags
    linkflags = ldflags[lang]
    if shared and not executable:
        linkflags += shared_flags[lang]
        compile_flags += shared_flags[lang]
    elif executable:
        if not shared:
            logger = logging.getLogger(__name__)
            logger.error('Cannot create an executable non-shared library!')
            raise LibraryGenerationError()

        compile_flags += shared_flags[lang]
        linkflags += shared_exec_flags[lang]
    if run_dirs[lang]:
        for rdir in utils.listify(run_dirs[lang]):
            linkflags += ['-Wl,-rpath,{}'.format(rdir)]
    so_ext = lib_ext(shared)

    toolchain_args = {'cc': cmd_compile[lang][:],
                      'cflags': (flags[lang] + compile_flags)[:],
                      'ldflags': linkflags[:],
                      'include_dirs': includes[lang][:],
                      'library_dirs': lib_dirs[lang][:],
                      'libraries': libs[lang][:],
                      'so_ext': so_ext,
                      'o_ext': '.o',
                      'defines': [],
                      'undefines': []}

    # merge in user kwargs
    for k, v in six.iteritems(kwargs):
        if k not in toolchain_args or not toolchain_args[k]:
            # empty or user supplied only
            toolchain_args[k] = v
        elif isinstance(toolchain_args[k], list):
            # may simply append to the list
            v = utils.listify(v)
            toolchain_args[k] += v[:]
        else:
            # else, replace
            toolchain_args[k] = v

    return GCCToolchain(**toolchain_args)