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 __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 __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 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
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)
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)
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)