def _compile( self, name, tpl, tpl_vars, libs, libd=None, incd=None, flags=None): """ Compiles a source template into a module and returns it. The module's name is specified by ``name``. The template to compile is given by ``tpl``, while any variables required to process the template should be given as the dict ``tpl_vars``. Any C libraries needed for compilation should be given in the sequence type ``libs``. Library dirs and include dirs can be passed in using ``libd`` and ``incd``. Extra compiler arguments can be given in the list ``flags``. """ src_file = self._source_file() d_cache = tempfile.mkdtemp('myokit') try: # Create output directories d_build = os.path.join(d_cache, 'build') d_modul = os.path.join(d_cache, 'module') os.makedirs(d_build) os.makedirs(d_modul) # Export c file src_file = os.path.join(d_cache, src_file) self._export(tpl, tpl_vars, src_file) # Add runtime_library_dirs (to prevent LD_LIBRARY_PATH) errors on # unconventional linux sundials installations, but not on windows # as this can lead to a weird error in setuptools runtime = None if platform.system() == 'Windows' else libd # Create extension ext = Extension( name, sources=[src_file], libraries=libs, library_dirs=libd, runtime_library_dirs=runtime, include_dirs=incd, extra_compile_args=flags, ) # Compile, catch output with myokit.SubCapture() as s: try: setup( name=name, description='Temporary module', ext_modules=[ext], script_args=[ 'build', '--build-base=' + d_build, 'install', '--install-lib=' + d_modul, ]) except (Exception, SystemExit) as e: s.disable() t = [ 'Unable to compile.', 'Error message:', ' ' + e.message, 'Error traceback', traceback.format_exc(), 'Compiler output:', ] captured = s.text().strip() t.extend([' ' + x for x in captured.splitlines()]) raise myokit.CompilationError('\n'.join(t)) # Include module (and refresh in case 2nd model is loaded) (f, pathname, description) = imp.find_module(name, [d_modul]) return imp.load_dynamic(name, pathname) finally: try: shutil.rmtree(d_cache) except Exception: pass
def _compile(self, name, tpl, tpl_vars, libs, libd=None, incd=None, carg=None, larg=None): """ Compiles a source template into a module and returns it. The module's name is specified by ``name``. The template to compile is given by ``tpl``, while any variables required to process the template should be given as the dict ``tpl_vars``. Any C libraries needed for compilation should be given in the sequence type ``libs``. Library dirs and include dirs can be passed in using ``libd`` and ``incd``. Extra compiler arguments can be given in the list ``carg``, and linker args in ``larg``. """ src_file = self._source_file() working_dir = os.getcwd() d_cache = tempfile.mkdtemp('myokit') try: # Create output directories d_build = os.path.join(d_cache, 'build') os.makedirs(d_build) # Export c file src_file = os.path.join(d_cache, src_file) self._export(tpl, tpl_vars, src_file) # Ensure headers can be read from myokit/_sim if incd is None: incd = [] incd.append(myokit.DIR_CFUNC) # Inputs must all be strings name = str(name) src_file = str(src_file) incd = [str(x) for x in incd] libd = None if libd is None else [str(x) for x in libd] libs = None if libs is None else [str(x) for x in libs] carg = None if carg is None else [str(x) for x in carg] larg = None if larg is None else [str(x) for x in larg] # Uncomment to debug C89 issues ''' if carg is None: carg = [] carg.extend([ #'-Wall', #'-Wextra', #'-Werror=strict-prototypes', #'-Werror=old-style-definition', #'-Werror=missing-prototypes', #'-Werror=missing-declarations', '-Werror=declaration-after-statement', ]) #''' # Add runtime_library_dirs (to prevent LD_LIBRARY_PATH) errors on # unconventional linux sundials installations, but not on windows # as this can lead to a weird error in setuptools runtime = libd if platform.system() == 'Windows': # pragma: no linux cover if libd is not None: runtime = None # Determine strategy try: os.add_dll_directory use_add_dll_directory = True except AttributeError: use_add_dll_directory = False # Make windows search the libd directories if use_add_dll_directory: # Python 3.8 and up for path in libd: if os.path.isdir(path): os.add_dll_directory(path) else: # Older versions: add libd to path path = os.environ.get('path', '') if path is None: path = '' to_add = [x for x in libd if x not in path] os.environ['path'] = os.pathsep.join([path] + to_add) # Create extension ext = Extension( name, sources=[src_file], libraries=libs, library_dirs=libd, runtime_library_dirs=runtime, include_dirs=incd, extra_compile_args=carg, extra_link_args=larg, ) # Compile in build directory, catch output with myokit.SubCapture() as s: try: os.chdir(d_build) setup(name=name, description='Temporary module', ext_modules=[ext], script_args=[ str('build_ext'), str('--inplace'), ]) except (Exception, SystemExit) as e: # pragma: no cover s.disable() t = ['Unable to compile.', 'Error message:'] t.append(str(e)) t.append('Error traceback') t.append(traceback.format_exc()) t.append('Compiler output:') captured = s.text().strip() t.extend([' ' + x for x in captured.splitlines()]) raise myokit.CompilationError('\n'.join(t)) # Include module (and refresh in case 2nd model is loaded) return load_module(name, d_build) finally: # Revert changes to working directory os.chdir(working_dir) # Delete cached module try: myokit._rmtree(d_cache) except Exception: # pragma: no cover pass
def _compile(self, name, tpl, tpl_vars, libs, libd=None, incd=None, flags=None): """ Compiles a source template into a module and returns it. The module's name is specified by ``name``. The template to compile is given by ``tpl``, while any variables required to process the template should be given as the dict ``tpl_vars``. Any C libraries needed for compilation should be given in the sequence type ``libs``. Library dirs and include dirs can be passed in using ``libd`` and ``incd``. Extra compiler arguments can be given in the list ``flags``. """ src_file = self._source_file() d_cache = tempfile.mkdtemp('myokit') try: # Create output directories d_build = os.path.join(d_cache, 'build') d_modul = os.path.join(d_cache, 'module') os.makedirs(d_build) os.makedirs(d_modul) # Export c file src_file = os.path.join(d_cache, src_file) self._export(tpl, tpl_vars, src_file) # Inputs must all be strings name = str(name) src_file = str(src_file) flags = None if flags is None else [str(x) for x in flags] libd = None if libd is None else [str(x) for x in libd] incd = None if incd is None else [str(x) for x in incd] libs = None if libs is None else [str(x) for x in libs] # Add runtime_library_dirs (to prevent LD_LIBRARY_PATH) errors on # unconventional linux sundials installations, but not on windows # as this can lead to a weird error in setuptools runtime = libd if (platform.system() == 'Windows' and libd is not None): # pragma: no linux cover runtime = None # Instead, add libd to path on windows try: path = os.environ['path'] except KeyError: path = '' to_add = [x for x in libd if x not in path] if to_add: os.environ['path'] = os.pathsep.join([path] + to_add) # Create extension ext = Extension( name, sources=[src_file], libraries=libs, library_dirs=libd, runtime_library_dirs=runtime, include_dirs=incd, extra_compile_args=flags, ) # Compile, catch output with myokit.SubCapture() as s: try: setup(name=name, description='Temporary module', ext_modules=[ext], script_args=[ str('build'), str('--build-base=' + d_build), str('install'), str('--install-lib=' + d_modul), str('--old-and-unmanageable'), ]) except (Exception, SystemExit) as e: s.disable() t = ['Unable to compile.', 'Error message:'] t.append(str(e)) t.append('Error traceback') t.append(traceback.format_exc()) t.append('Compiler output:') captured = s.text().strip() t.extend([' ' + x for x in captured.splitlines()]) raise myokit.CompilationError('\n'.join(t)) finally: egg = name + '.egg-info' if os.path.exists(egg): shutil.rmtree(egg) # Include module (and refresh in case 2nd model is loaded) return load_module(name, d_modul) finally: try: shutil.rmtree(d_cache) except Exception: # pragma: no cover pass
def _compile(self, name, template, variables, libs, libd=None, incd=None, carg=None, larg=None, continue_in_debug_mode=False): """ Compiles a source ``template`` with the given ``variables`` into a module called ``name``, then imports it and returns a reference to the imported module. Any C libraries needed for compilation should be given in the sequence type ``libs``. Library dirs and include dirs can be passed in using ``libd`` and ``incd``. Extra compiler arguments can be given in the list ``carg``, and linker args in ``larg``. If ``myokit.DEBUG_SG`` or ``myokit.DEBUG_WG`` are set, the method will print the generated code to screen and/or write it to disk. Following this, it will terminate with exit code 1 unless ``continue_in_debug_mode`` is changed to ``True``. """ # Show and/or write code in debug mode if myokit.DEBUG_SG or myokit.DEBUG_WG: # pragma: no cover if myokit.DEBUG_SG: self._debug_show(template, variables) else: self._debug_write(template, variables) if not continue_in_debug_mode: sys.exit(1) # Write to temp dir and compile src_file = self._source_file() working_dir = os.getcwd() d_cache = tempfile.mkdtemp('myokit') try: # Create output directories d_build = os.path.join(d_cache, 'build') os.makedirs(d_build) # Export c file src_file = os.path.join(d_cache, src_file) self._export_inner(template, variables, src_file) # Ensure headers can be read from myokit/_sim if incd is None: incd = [] incd.append(myokit.DIR_CFUNC) # Inputs must all be strings name = str(name) src_file = str(src_file) incd = [str(x) for x in incd] libd = None if libd is None else [str(x) for x in libd] libs = None if libs is None else [str(x) for x in libs] carg = None if carg is None else [str(x) for x in carg] larg = None if larg is None else [str(x) for x in larg] # Show warnings if myokit.DEBUG_SC: if carg is None: carg = [] carg.append('-Wall') if platform.system() == 'Linux': carg.extend([ '-Wextra', '-Wstrict-prototypes', '-Wold-style-definition', '-Wmissing-prototypes', '-Wmissing-declarations', '-Wdeclaration-after-statement', ]) # Add runtime_library_dirs to prevent LD_LIBRARY_PATH errors on # unconventional linux sundials installations, but not on windows # as this can lead to a weird error in setuptools runtime = libd if platform.system() == 'Windows': # pragma: no linux cover if libd is not None: runtime = None # Make windows search the libd directories path = os.environ.get('path', '') if path is None: path = '' to_add = [x for x in libd if x not in path] os.environ['path'] = os.pathsep.join([path] + to_add) # In Python 3.8+, they need to be registered with # add_dll_directory too. This does not seem to be 100% # consistent. AppVeyor tests pass when using # add_dll_directory *without* adding the directories to the # path, while installations via miniconda seem to need the # path method too. try: # Fail if add_dll_directory not present os.add_dll_directory # Add DLL paths for path in libd: if os.path.isdir(path): os.add_dll_directory(path) except AttributeError: pass # Create extension ext = Extension( name, sources=[src_file], libraries=libs, library_dirs=libd, runtime_library_dirs=runtime, include_dirs=incd, extra_compile_args=carg, extra_link_args=larg, ) # Compile in build directory, catch output capture = not myokit.DEBUG_SC error, trace = None, None with myokit.tools.capture(fd=True, enabled=capture) as s: try: os.chdir(d_build) setup( name=name, description='Temporary module', ext_modules=[ext], script_args=[ str('build_ext'), str('--inplace'), ]) except (Exception, SystemExit) as e: # pragma: no cover error = e trace = traceback.format_exc() if error is not None: # pragma: no cover t = ['Unable to compile.', 'Error message:'] t.append(str(error)) t.append(trace) t.append('Compiler output:') captured = s.text().strip() t.extend([' ' + x for x in captured.splitlines()]) raise myokit.CompilationError('\n'.join(t)) # Include module (and refresh in case 2nd model is loaded) return load_module(name, d_build) finally: # Revert changes to working directory os.chdir(working_dir) # Delete cached module try: myokit.tools.rmtree(d_cache) except Exception: # pragma: no cover pass