Source code for cis_interface.drivers.GCCModelDriver

import os
import logging
from cis_interface import platform, tools
from cis_interface.config import cis_cfg
from cis_interface.drivers.ModelDriver import ModelDriver


_top_dir = os.path.normpath(os.path.join(os.path.dirname(__file__), '../'))
_incl_interface = os.path.join(_top_dir, 'interface')
_incl_io = os.path.join(_top_dir, 'io')
_incl_seri = os.path.join(_top_dir, 'serialize')
_incl_comm = os.path.join(_top_dir, 'communication')
_regex_win32_lib = os.path.join(_top_dir, 'regex_win32.lib')


[docs]def get_zmq_flags(): r"""Get the necessary flags for compiling & linking with zmq libraries. Returns: tuple(list, list): compile and linker flags. """ _compile_flags = [] _linker_flags = [] if tools._zmq_installed_c: if platform._is_win: # pragma: windows for l in ["libzmq", "czmq"]: plib = cis_cfg.get('windows', '%s_static' % l, False) pinc = cis_cfg.get('windows', '%s_include' % l, False) if not (plib and pinc): # pragma: debug raise Exception("Could not locate %s .lib and .h files." % l) pinc_d = os.path.dirname(pinc) plib_d, plib_f = os.path.split(plib) _compile_flags.append("-I%s" % pinc_d) _linker_flags += [plib_f, '/LIBPATH:"%s"' % plib_d] else: _linker_flags += ["-lczmq", "-lzmq"] _compile_flags += ["-DZMQINSTALLED"] return _compile_flags, _linker_flags
[docs]def get_ipc_flags(): r"""Get the necessary flags for compiling & linking with ipc libraries. Returns: tuple(list, list): compile and linker flags. """ _compile_flags = [] _linker_flags = [] if tools._ipc_installed: _compile_flags += ["-DIPCINSTALLED"] return _compile_flags, _linker_flags
[docs]def get_flags(): r"""Get the necessary flags for compiling & linking with CiS libraries. Returns: tuple(list, list): compile and linker flags. """ _compile_flags = [] _linker_flags = [] if not tools._c_library_avail: # pragma: windows logging.warning("No library installed for models written in C") return _compile_flags, _linker_flags if platform._is_win: # pragma: windows _regex_win32 = os.path.split(_regex_win32_lib) _compile_flags += ["/nologo", "-D_CRT_SECURE_NO_WARNINGS", "-I" + _regex_win32[0]] _linker_flags += [_regex_win32[1], '/LIBPATH:"%s"' % _regex_win32[0]] if tools._zmq_installed_c: zmq_flags = get_zmq_flags() _compile_flags += zmq_flags[0] _linker_flags += zmq_flags[1] if tools._ipc_installed: ipc_flags = get_ipc_flags() _compile_flags += ipc_flags[0] _linker_flags += ipc_flags[1] for x in [_incl_interface, _incl_io, _incl_comm, _incl_seri]: _compile_flags += ["-I" + x] if tools.get_default_comm() == 'IPCComm': _compile_flags += ["-DIPCDEF"] return _compile_flags, _linker_flags
[docs]def build_regex_win32(): # pragma: windows r"""Build the regex_win32 library.""" _regex_win32_dir = os.path.dirname(_regex_win32_lib) _regex_win32_cpp = os.path.join(_regex_win32_dir, 'regex_win32.cpp') _regex_win32_obj = os.path.join(_regex_win32_dir, 'regex_win32.obj') # Compile object cmd = ['cl', '/c', '/Zi', '/EHsc', '/I', '%s' % _regex_win32_dir, _regex_win32_cpp] # '/out:%s' % _regex_win32_obj, comp_process = tools.popen_nobuffer(cmd, cwd=_regex_win32_dir) output, err = comp_process.communicate() exit_code = comp_process.returncode if exit_code != 0: # pragma: debug print(' '.join(cmd)) tools.print_encoded(output, end="") raise RuntimeError("Could not create regex_win32.obj") assert(os.path.isfile(_regex_win32_obj)) # Create library cmd = ['lib', '/out:%s' % _regex_win32_lib, _regex_win32_obj] comp_process = tools.popen_nobuffer(cmd, cwd=_regex_win32_dir) output, err = comp_process.communicate() exit_code = comp_process.returncode if exit_code != 0: # pragma: debug print(' '.join(cmd)) tools.print_encoded(output, end="") raise RuntimeError("Could not build regex_win32.lib") assert(os.path.isfile(_regex_win32_lib))
if platform._is_win and (not os.path.isfile(_regex_win32_lib)): # pragma: windows build_regex_win32()
[docs]def do_compile(src, out=None, cc=None, ccflags=None, ldflags=None, working_dir=None): r"""Compile a C/C++ program with necessary interface libraries. Args: src (list): List of source files. out (str, optional): Path where compile executable should be saved. Defaults to name of source file without extension on linux/osx and with .exe extension on windows. cc (str, optional): Compiler command. Defaults to gcc/g++ on linux/osx and cl on windows. ccflags (list, optional): Compiler flags. Defaults to []. ldflags (list, optional): Linker flags. Defaults to []. working_dir (str, optional): Working directory that input file paths are relative to. Defaults to current working directory. Returns: str: Full path to the compiled executable. """ if working_dir is None: # pragma: no cover working_dir = os.getcwd() if ccflags is None: # pragma: no cover ccflags = [] if ldflags is None: # pragma: no cover ldflags = [] _compile_flags, _linker_flags = get_flags() ldflags0 = _linker_flags if platform._is_win: # pragma: windows ccflags0 = ['/W4', '/Zi', "/EHsc"] else: ccflags0 = ['-g', '-Wall'] ccflags0 += _compile_flags # Change format for path (windows compat of examples) if platform._is_win: # pragma: windows for i in range(len(src)): src[i] = os.path.join(*(src[i].split('/'))) # Get primary file cfile = src[0] src_base, src_ext = os.path.splitext(cfile) # Select compiler if cc is None: if platform._is_win: # pragma: windows cc = 'cl' else: if src_ext in ['.c']: cc = 'gcc' else: cc = 'g++' # Create/fix executable if out is None: if platform._is_win: # pragma: windows osuffix = '_%s.exe' % src_ext[1:] else: osuffix = '_%s.out' % src_ext[1:] out = src_base + osuffix if not os.path.isabs(out): out = os.path.normpath(os.path.join(working_dir, out)) # Get flag specifying standard library if '++' in cc and (not platform._is_win): std_flag = None for a in ccflags: if a.startswith('-std='): std_flag = a break if std_flag is None: ccflags.append('-std=c++11') # Construct compile arguments compile_args = [cc] if not platform._is_win: compile_args += ["-o", out] compile_args += src + ccflags0 + ccflags if platform._is_win: # pragma: windows compile_args += ['/link', '/out:%s' % out] compile_args += ldflags0 + ldflags if os.path.isfile(out): os.remove(out) # Compile comp_process = tools.popen_nobuffer(compile_args) output, err = comp_process.communicate() exit_code = comp_process.returncode if exit_code != 0: # pragma: debug print(' '.join(compile_args)) tools.print_encoded(output, end="") raise RuntimeError("Compilation failed with code %d." % exit_code) assert(os.path.isfile(out)) return out
[docs]class GCCModelDriver(ModelDriver): r"""Class for running gcc compiled drivers. Args: name (str): Driver name. args (str or list): Argument(s) for running the model on the command line. If the first element ends with '.c', the driver attempts to compile the code with the necessary interface include directories. Additional arguments that start with '-I' are included in the compile command. Others are assumed to be runtime arguments. cc (str, optional): C/C++ Compiler that should be used. Defaults to gcc for '.c' files, and g++ for '.cpp' or '.cc' files on Linux or OSX. Defaults to cl on Windows. **kwargs: Additional keyword arguments are passed to parent class. Attributes (in additon to parent class's): compiled (bool): True if the compilation was succesful. False otherwise. cfile (str): Source file. cc (str): C/C++ Compiler that should be used. flags (list): List of compiler flags. efile (str): Compiled executable file. Raises: RuntimeError: If neither the IPC or ZMQ C libraries are available. RuntimeError: If the compilation fails. """ def __init__(self, name, args, cc=None, **kwargs): super(GCCModelDriver, self).__init__(name, args, **kwargs) if not tools._c_library_avail: # pragma: windows raise RuntimeError("No library available for models written in C/C++.") self.debug('') self.cc = cc # Prepare arguments to compile the file self.parse_arguments(self.args) self.debug("Compiling") self.efile = do_compile(self.src, out=self.efile, cc=self.cc, ccflags=self.ccflags, ldflags=self.ldflags, working_dir=self.workingDir) assert(os.path.isfile(self.efile)) self.debug("Compiled %s", self.efile) if platform._is_win: # pragma: windows self.args = [os.path.splitext(self.efile)[0]] else: self.args = [os.path.join(".", self.efile)] self.args += self.run_args self.debug('Compiled executable with %s', self.cc)
[docs] def parse_arguments(self, args): r"""Sort arguments based on their syntax. Arguments ending with '.c' or '.cpp' are considered source and the first one will be compiled to an executable. Arguments starting with '-L' or '-l' are treated as linker flags. Arguments starting with '-' are treated as compiler flags. Any arguments that do not fall into one of the categories will be treated as command line arguments for the compiled executable. Args: args (list): List of arguments provided. Raises: RuntimeError: If there is not a valid source file in the argument list. """ self.src = [] self.ldflags = [] self.ccflags = [] self.ccflags.append('-DCIS_DEBUG=%d' % self.logger.getEffectiveLevel()) self.run_args = [] self.efile = None is_object = False is_link = False for a in args: if a.endswith('.c') or a.endswith('.cpp') or a.endswith('.cc'): self.src.append(a) elif a.lower().startswith('-l') or is_link: if a.lower().startswith('/out:'): # pragma: windows self.efile = a[5:] elif a.lower().startswith('-l') and platform._is_win: # pragma: windows a1 = '/LIBPATH:"%s"' % a[2:] if a1 not in self.ldflags: self.ldflags.append(a1) elif a not in self.ldflags: self.ldflags.append(a) elif a == '-o': # Next argument should be the name of the executable is_object = True elif a.lower() == '/link': # pragma: windows # Following arguments should be linker options is_link = True elif a.startswith('-') or (platform._is_win and a.startswith('/')): if a not in self.ccflags: self.ccflags.append(a) else: if is_object: # Previous argument was -o flag self.efile = a is_object = False else: self.run_args.append(a) # Check source file if len(self.src) == 0: raise RuntimeError("Could not locate a source file in the " + "provided arguments.")
[docs] def remove_products(self): r"""Delete products produced during the compilation process.""" if self.efile is None: # pragma: debug return products = [self.efile] if platform._is_win: # pragma: windows base = os.path.splitext(self.efile)[0] products = [base + ext for ext in ['.ilk', '.pdb', '.obj']] for p in products: if os.path.isfile(p): os.remove(p)
[docs] def cleanup(self): r"""Remove compile executable.""" self.remove_products() super(GCCModelDriver, self).cleanup()