Initial import: grid-bot — grid trading bot for BTC-USDT on Cifra Markets
This commit is contained in:
@@ -0,0 +1,657 @@
|
||||
import glob
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
from sysconfig import get_config_var, get_config_vars, get_path
|
||||
|
||||
from .runners import (
|
||||
CCompilerRunner,
|
||||
CppCompilerRunner,
|
||||
FortranCompilerRunner
|
||||
)
|
||||
from .util import (
|
||||
get_abspath, make_dirs, copy, Glob, ArbitraryDepthGlob,
|
||||
glob_at_depth, import_module_from_file, pyx_is_cplus,
|
||||
sha256_of_string, sha256_of_file, CompileError
|
||||
)
|
||||
|
||||
if os.name == 'posix':
|
||||
objext = '.o'
|
||||
elif os.name == 'nt':
|
||||
objext = '.obj'
|
||||
else:
|
||||
warnings.warn("Unknown os.name: {}".format(os.name))
|
||||
objext = '.o'
|
||||
|
||||
|
||||
def compile_sources(files, Runner=None, destdir=None, cwd=None, keep_dir_struct=False,
|
||||
per_file_kwargs=None, **kwargs):
|
||||
""" Compile source code files to object files.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
files : iterable of str
|
||||
Paths to source files, if ``cwd`` is given, the paths are taken as relative.
|
||||
Runner: CompilerRunner subclass (optional)
|
||||
Could be e.g. ``FortranCompilerRunner``. Will be inferred from filename
|
||||
extensions if missing.
|
||||
destdir: str
|
||||
Output directory, if cwd is given, the path is taken as relative.
|
||||
cwd: str
|
||||
Working directory. Specify to have compiler run in other directory.
|
||||
also used as root of relative paths.
|
||||
keep_dir_struct: bool
|
||||
Reproduce directory structure in `destdir`. default: ``False``
|
||||
per_file_kwargs: dict
|
||||
Dict mapping instances in ``files`` to keyword arguments.
|
||||
\\*\\*kwargs: dict
|
||||
Default keyword arguments to pass to ``Runner``.
|
||||
|
||||
Returns
|
||||
=======
|
||||
List of strings (paths of object files).
|
||||
"""
|
||||
_per_file_kwargs = {}
|
||||
|
||||
if per_file_kwargs is not None:
|
||||
for k, v in per_file_kwargs.items():
|
||||
if isinstance(k, Glob):
|
||||
for path in glob.glob(k.pathname):
|
||||
_per_file_kwargs[path] = v
|
||||
elif isinstance(k, ArbitraryDepthGlob):
|
||||
for path in glob_at_depth(k.filename, cwd):
|
||||
_per_file_kwargs[path] = v
|
||||
else:
|
||||
_per_file_kwargs[k] = v
|
||||
|
||||
# Set up destination directory
|
||||
destdir = destdir or '.'
|
||||
if not os.path.isdir(destdir):
|
||||
if os.path.exists(destdir):
|
||||
raise OSError("{} is not a directory".format(destdir))
|
||||
else:
|
||||
make_dirs(destdir)
|
||||
if cwd is None:
|
||||
cwd = '.'
|
||||
for f in files:
|
||||
copy(f, destdir, only_update=True, dest_is_dir=True)
|
||||
|
||||
# Compile files and return list of paths to the objects
|
||||
dstpaths = []
|
||||
for f in files:
|
||||
if keep_dir_struct:
|
||||
name, ext = os.path.splitext(f)
|
||||
else:
|
||||
name, ext = os.path.splitext(os.path.basename(f))
|
||||
file_kwargs = kwargs.copy()
|
||||
file_kwargs.update(_per_file_kwargs.get(f, {}))
|
||||
dstpaths.append(src2obj(f, Runner, cwd=cwd, **file_kwargs))
|
||||
return dstpaths
|
||||
|
||||
|
||||
def get_mixed_fort_c_linker(vendor=None, cplus=False, cwd=None):
|
||||
vendor = vendor or os.environ.get('SYMPY_COMPILER_VENDOR', 'gnu')
|
||||
|
||||
if vendor.lower() == 'intel':
|
||||
if cplus:
|
||||
return (FortranCompilerRunner,
|
||||
{'flags': ['-nofor_main', '-cxxlib']}, vendor)
|
||||
else:
|
||||
return (FortranCompilerRunner,
|
||||
{'flags': ['-nofor_main']}, vendor)
|
||||
elif vendor.lower() == 'gnu' or 'llvm':
|
||||
if cplus:
|
||||
return (CppCompilerRunner,
|
||||
{'lib_options': ['fortran']}, vendor)
|
||||
else:
|
||||
return (FortranCompilerRunner,
|
||||
{}, vendor)
|
||||
else:
|
||||
raise ValueError("No vendor found.")
|
||||
|
||||
|
||||
def link(obj_files, out_file=None, shared=False, Runner=None,
|
||||
cwd=None, cplus=False, fort=False, extra_objs=None, **kwargs):
|
||||
""" Link object files.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
obj_files: iterable of str
|
||||
Paths to object files.
|
||||
out_file: str (optional)
|
||||
Path to executable/shared library, if ``None`` it will be
|
||||
deduced from the last item in obj_files.
|
||||
shared: bool
|
||||
Generate a shared library?
|
||||
Runner: CompilerRunner subclass (optional)
|
||||
If not given the ``cplus`` and ``fort`` flags will be inspected
|
||||
(fallback is the C compiler).
|
||||
cwd: str
|
||||
Path to the root of relative paths and working directory for compiler.
|
||||
cplus: bool
|
||||
C++ objects? default: ``False``.
|
||||
fort: bool
|
||||
Fortran objects? default: ``False``.
|
||||
extra_objs: list
|
||||
List of paths to extra object files / static libraries.
|
||||
\\*\\*kwargs: dict
|
||||
Keyword arguments passed to ``Runner``.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
The absolute path to the generated shared object / executable.
|
||||
|
||||
"""
|
||||
if out_file is None:
|
||||
out_file, ext = os.path.splitext(os.path.basename(obj_files[-1]))
|
||||
if shared:
|
||||
out_file += get_config_var('EXT_SUFFIX')
|
||||
|
||||
if not Runner:
|
||||
if fort:
|
||||
Runner, extra_kwargs, vendor = \
|
||||
get_mixed_fort_c_linker(
|
||||
vendor=kwargs.get('vendor', None),
|
||||
cplus=cplus,
|
||||
cwd=cwd,
|
||||
)
|
||||
for k, v in extra_kwargs.items():
|
||||
if k in kwargs:
|
||||
kwargs[k].expand(v)
|
||||
else:
|
||||
kwargs[k] = v
|
||||
else:
|
||||
if cplus:
|
||||
Runner = CppCompilerRunner
|
||||
else:
|
||||
Runner = CCompilerRunner
|
||||
|
||||
flags = kwargs.pop('flags', [])
|
||||
if shared:
|
||||
if '-shared' not in flags:
|
||||
flags.append('-shared')
|
||||
run_linker = kwargs.pop('run_linker', True)
|
||||
if not run_linker:
|
||||
raise ValueError("run_linker was set to False (nonsensical).")
|
||||
|
||||
out_file = get_abspath(out_file, cwd=cwd)
|
||||
runner = Runner(obj_files+(extra_objs or []), out_file, flags, cwd=cwd, **kwargs)
|
||||
runner.run()
|
||||
return out_file
|
||||
|
||||
|
||||
def link_py_so(obj_files, so_file=None, cwd=None, libraries=None,
|
||||
cplus=False, fort=False, extra_objs=None, **kwargs):
|
||||
""" Link Python extension module (shared object) for importing
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
obj_files: iterable of str
|
||||
Paths to object files to be linked.
|
||||
so_file: str
|
||||
Name (path) of shared object file to create. If not specified it will
|
||||
have the basname of the last object file in `obj_files` but with the
|
||||
extension '.so' (Unix).
|
||||
cwd: path string
|
||||
Root of relative paths and working directory of linker.
|
||||
libraries: iterable of strings
|
||||
Libraries to link against, e.g. ['m'].
|
||||
cplus: bool
|
||||
Any C++ objects? default: ``False``.
|
||||
fort: bool
|
||||
Any Fortran objects? default: ``False``.
|
||||
extra_objs: list
|
||||
List of paths of extra object files / static libraries to link against.
|
||||
kwargs**: dict
|
||||
Keyword arguments passed to ``link(...)``.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
Absolute path to the generate shared object.
|
||||
"""
|
||||
libraries = libraries or []
|
||||
|
||||
include_dirs = kwargs.pop('include_dirs', [])
|
||||
library_dirs = kwargs.pop('library_dirs', [])
|
||||
|
||||
# Add Python include and library directories
|
||||
# PY_LDFLAGS does not available on all python implementations
|
||||
# e.g. when with pypy, so it's LDFLAGS we need to use
|
||||
if sys.platform == "win32":
|
||||
warnings.warn("Windows not yet supported.")
|
||||
elif sys.platform == 'darwin':
|
||||
cfgDict = get_config_vars()
|
||||
kwargs['linkline'] = kwargs.get('linkline', []) + [cfgDict['LDFLAGS']]
|
||||
library_dirs += [cfgDict['LIBDIR']]
|
||||
|
||||
# In macOS, linker needs to compile frameworks
|
||||
# e.g. "-framework CoreFoundation"
|
||||
is_framework = False
|
||||
for opt in cfgDict['LIBS'].split():
|
||||
if is_framework:
|
||||
kwargs['linkline'] = kwargs.get('linkline', []) + ['-framework', opt]
|
||||
is_framework = False
|
||||
elif opt.startswith('-l'):
|
||||
libraries.append(opt[2:])
|
||||
elif opt.startswith('-framework'):
|
||||
is_framework = True
|
||||
# The python library is not included in LIBS
|
||||
libfile = cfgDict['LIBRARY']
|
||||
libname = ".".join(libfile.split('.')[:-1])[3:]
|
||||
libraries.append(libname)
|
||||
|
||||
elif sys.platform[:3] == 'aix':
|
||||
# Don't use the default code below
|
||||
pass
|
||||
else:
|
||||
if get_config_var('Py_ENABLE_SHARED'):
|
||||
cfgDict = get_config_vars()
|
||||
kwargs['linkline'] = kwargs.get('linkline', []) + [cfgDict['LDFLAGS']]
|
||||
library_dirs += [cfgDict['LIBDIR']]
|
||||
for opt in cfgDict['BLDLIBRARY'].split():
|
||||
if opt.startswith('-l'):
|
||||
libraries += [opt[2:]]
|
||||
else:
|
||||
pass
|
||||
|
||||
flags = kwargs.pop('flags', [])
|
||||
needed_flags = ('-pthread',)
|
||||
for flag in needed_flags:
|
||||
if flag not in flags:
|
||||
flags.append(flag)
|
||||
|
||||
return link(obj_files, shared=True, flags=flags, cwd=cwd, cplus=cplus, fort=fort,
|
||||
include_dirs=include_dirs, libraries=libraries,
|
||||
library_dirs=library_dirs, extra_objs=extra_objs, **kwargs)
|
||||
|
||||
|
||||
def simple_cythonize(src, destdir=None, cwd=None, **cy_kwargs):
|
||||
""" Generates a C file from a Cython source file.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
src: str
|
||||
Path to Cython source.
|
||||
destdir: str (optional)
|
||||
Path to output directory (default: '.').
|
||||
cwd: path string (optional)
|
||||
Root of relative paths (default: '.').
|
||||
**cy_kwargs:
|
||||
Second argument passed to cy_compile. Generates a .cpp file if ``cplus=True`` in ``cy_kwargs``,
|
||||
else a .c file.
|
||||
"""
|
||||
from Cython.Compiler.Main import (
|
||||
default_options, CompilationOptions
|
||||
)
|
||||
from Cython.Compiler.Main import compile as cy_compile
|
||||
|
||||
assert src.lower().endswith('.pyx') or src.lower().endswith('.py')
|
||||
cwd = cwd or '.'
|
||||
destdir = destdir or '.'
|
||||
|
||||
ext = '.cpp' if cy_kwargs.get('cplus', False) else '.c'
|
||||
c_name = os.path.splitext(os.path.basename(src))[0] + ext
|
||||
|
||||
dstfile = os.path.join(destdir, c_name)
|
||||
|
||||
if cwd:
|
||||
ori_dir = os.getcwd()
|
||||
else:
|
||||
ori_dir = '.'
|
||||
os.chdir(cwd)
|
||||
try:
|
||||
cy_options = CompilationOptions(default_options)
|
||||
cy_options.__dict__.update(cy_kwargs)
|
||||
# Set language_level if not set by cy_kwargs
|
||||
# as not setting it is deprecated
|
||||
if 'language_level' not in cy_kwargs:
|
||||
cy_options.__dict__['language_level'] = 3
|
||||
cy_result = cy_compile([src], cy_options)
|
||||
if cy_result.num_errors > 0:
|
||||
raise ValueError("Cython compilation failed.")
|
||||
|
||||
# Move generated C file to destination
|
||||
# In macOS, the generated C file is in the same directory as the source
|
||||
# but the /var is a symlink to /private/var, so we need to use realpath
|
||||
if os.path.realpath(os.path.dirname(src)) != os.path.realpath(destdir):
|
||||
if os.path.exists(dstfile):
|
||||
os.unlink(dstfile)
|
||||
shutil.move(os.path.join(os.path.dirname(src), c_name), destdir)
|
||||
finally:
|
||||
os.chdir(ori_dir)
|
||||
return dstfile
|
||||
|
||||
|
||||
extension_mapping = {
|
||||
'.c': (CCompilerRunner, None),
|
||||
'.cpp': (CppCompilerRunner, None),
|
||||
'.cxx': (CppCompilerRunner, None),
|
||||
'.f': (FortranCompilerRunner, None),
|
||||
'.for': (FortranCompilerRunner, None),
|
||||
'.ftn': (FortranCompilerRunner, None),
|
||||
'.f90': (FortranCompilerRunner, None), # ifort only knows about .f90
|
||||
'.f95': (FortranCompilerRunner, 'f95'),
|
||||
'.f03': (FortranCompilerRunner, 'f2003'),
|
||||
'.f08': (FortranCompilerRunner, 'f2008'),
|
||||
}
|
||||
|
||||
|
||||
def src2obj(srcpath, Runner=None, objpath=None, cwd=None, inc_py=False, **kwargs):
|
||||
""" Compiles a source code file to an object file.
|
||||
|
||||
Files ending with '.pyx' assumed to be cython files and
|
||||
are dispatched to pyx2obj.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
srcpath: str
|
||||
Path to source file.
|
||||
Runner: CompilerRunner subclass (optional)
|
||||
If ``None``: deduced from extension of srcpath.
|
||||
objpath : str (optional)
|
||||
Path to generated object. If ``None``: deduced from ``srcpath``.
|
||||
cwd: str (optional)
|
||||
Working directory and root of relative paths. If ``None``: current dir.
|
||||
inc_py: bool
|
||||
Add Python include path to kwarg "include_dirs". Default: False
|
||||
\\*\\*kwargs: dict
|
||||
keyword arguments passed to Runner or pyx2obj
|
||||
|
||||
"""
|
||||
name, ext = os.path.splitext(os.path.basename(srcpath))
|
||||
if objpath is None:
|
||||
if os.path.isabs(srcpath):
|
||||
objpath = '.'
|
||||
else:
|
||||
objpath = os.path.dirname(srcpath)
|
||||
objpath = objpath or '.' # avoid objpath == ''
|
||||
|
||||
if os.path.isdir(objpath):
|
||||
objpath = os.path.join(objpath, name + objext)
|
||||
|
||||
include_dirs = kwargs.pop('include_dirs', [])
|
||||
if inc_py:
|
||||
py_inc_dir = get_path('include')
|
||||
if py_inc_dir not in include_dirs:
|
||||
include_dirs.append(py_inc_dir)
|
||||
|
||||
if ext.lower() == '.pyx':
|
||||
return pyx2obj(srcpath, objpath=objpath, include_dirs=include_dirs, cwd=cwd,
|
||||
**kwargs)
|
||||
|
||||
if Runner is None:
|
||||
Runner, std = extension_mapping[ext.lower()]
|
||||
if 'std' not in kwargs:
|
||||
kwargs['std'] = std
|
||||
|
||||
flags = kwargs.pop('flags', [])
|
||||
needed_flags = ('-fPIC',)
|
||||
for flag in needed_flags:
|
||||
if flag not in flags:
|
||||
flags.append(flag)
|
||||
|
||||
# src2obj implies not running the linker...
|
||||
run_linker = kwargs.pop('run_linker', False)
|
||||
if run_linker:
|
||||
raise CompileError("src2obj called with run_linker=True")
|
||||
|
||||
runner = Runner([srcpath], objpath, include_dirs=include_dirs,
|
||||
run_linker=run_linker, cwd=cwd, flags=flags, **kwargs)
|
||||
runner.run()
|
||||
return objpath
|
||||
|
||||
|
||||
def pyx2obj(pyxpath, objpath=None, destdir=None, cwd=None,
|
||||
include_dirs=None, cy_kwargs=None, cplus=None, **kwargs):
|
||||
"""
|
||||
Convenience function
|
||||
|
||||
If cwd is specified, pyxpath and dst are taken to be relative
|
||||
If only_update is set to `True` the modification time is checked
|
||||
and compilation is only run if the source is newer than the
|
||||
destination
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
pyxpath: str
|
||||
Path to Cython source file.
|
||||
objpath: str (optional)
|
||||
Path to object file to generate.
|
||||
destdir: str (optional)
|
||||
Directory to put generated C file. When ``None``: directory of ``objpath``.
|
||||
cwd: str (optional)
|
||||
Working directory and root of relative paths.
|
||||
include_dirs: iterable of path strings (optional)
|
||||
Passed onto src2obj and via cy_kwargs['include_path']
|
||||
to simple_cythonize.
|
||||
cy_kwargs: dict (optional)
|
||||
Keyword arguments passed onto `simple_cythonize`
|
||||
cplus: bool (optional)
|
||||
Indicate whether C++ is used. default: auto-detect using ``.util.pyx_is_cplus``.
|
||||
compile_kwargs: dict
|
||||
keyword arguments passed onto src2obj
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
Absolute path of generated object file.
|
||||
|
||||
"""
|
||||
assert pyxpath.endswith('.pyx')
|
||||
cwd = cwd or '.'
|
||||
objpath = objpath or '.'
|
||||
destdir = destdir or os.path.dirname(objpath)
|
||||
|
||||
abs_objpath = get_abspath(objpath, cwd=cwd)
|
||||
|
||||
if os.path.isdir(abs_objpath):
|
||||
pyx_fname = os.path.basename(pyxpath)
|
||||
name, ext = os.path.splitext(pyx_fname)
|
||||
objpath = os.path.join(objpath, name + objext)
|
||||
|
||||
cy_kwargs = cy_kwargs or {}
|
||||
cy_kwargs['output_dir'] = cwd
|
||||
if cplus is None:
|
||||
cplus = pyx_is_cplus(pyxpath)
|
||||
cy_kwargs['cplus'] = cplus
|
||||
|
||||
interm_c_file = simple_cythonize(pyxpath, destdir=destdir, cwd=cwd, **cy_kwargs)
|
||||
|
||||
include_dirs = include_dirs or []
|
||||
flags = kwargs.pop('flags', [])
|
||||
needed_flags = ('-fwrapv', '-pthread', '-fPIC')
|
||||
for flag in needed_flags:
|
||||
if flag not in flags:
|
||||
flags.append(flag)
|
||||
|
||||
options = kwargs.pop('options', [])
|
||||
|
||||
if kwargs.pop('strict_aliasing', False):
|
||||
raise CompileError("Cython requires strict aliasing to be disabled.")
|
||||
|
||||
# Let's be explicit about standard
|
||||
if cplus:
|
||||
std = kwargs.pop('std', 'c++98')
|
||||
else:
|
||||
std = kwargs.pop('std', 'c99')
|
||||
|
||||
return src2obj(interm_c_file, objpath=objpath, cwd=cwd,
|
||||
include_dirs=include_dirs, flags=flags, std=std,
|
||||
options=options, inc_py=True, strict_aliasing=False,
|
||||
**kwargs)
|
||||
|
||||
|
||||
def _any_X(srcs, cls):
|
||||
for src in srcs:
|
||||
name, ext = os.path.splitext(src)
|
||||
key = ext.lower()
|
||||
if key in extension_mapping:
|
||||
if extension_mapping[key][0] == cls:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def any_fortran_src(srcs):
|
||||
return _any_X(srcs, FortranCompilerRunner)
|
||||
|
||||
|
||||
def any_cplus_src(srcs):
|
||||
return _any_X(srcs, CppCompilerRunner)
|
||||
|
||||
|
||||
def compile_link_import_py_ext(sources, extname=None, build_dir='.', compile_kwargs=None,
|
||||
link_kwargs=None, extra_objs=None):
|
||||
""" Compiles sources to a shared object (Python extension) and imports it
|
||||
|
||||
Sources in ``sources`` which is imported. If shared object is newer than the sources, they
|
||||
are not recompiled but instead it is imported.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
sources : list of strings
|
||||
List of paths to sources.
|
||||
extname : string
|
||||
Name of extension (default: ``None``).
|
||||
If ``None``: taken from the last file in ``sources`` without extension.
|
||||
build_dir: str
|
||||
Path to directory in which objects files etc. are generated.
|
||||
compile_kwargs: dict
|
||||
keyword arguments passed to ``compile_sources``
|
||||
link_kwargs: dict
|
||||
keyword arguments passed to ``link_py_so``
|
||||
extra_objs: list
|
||||
List of paths to (prebuilt) object files / static libraries to link against.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
The imported module from of the Python extension.
|
||||
"""
|
||||
if extname is None:
|
||||
extname = os.path.splitext(os.path.basename(sources[-1]))[0]
|
||||
|
||||
compile_kwargs = compile_kwargs or {}
|
||||
link_kwargs = link_kwargs or {}
|
||||
|
||||
try:
|
||||
mod = import_module_from_file(os.path.join(build_dir, extname), sources)
|
||||
except ImportError:
|
||||
objs = compile_sources(list(map(get_abspath, sources)), destdir=build_dir,
|
||||
cwd=build_dir, **compile_kwargs)
|
||||
so = link_py_so(objs, cwd=build_dir, fort=any_fortran_src(sources),
|
||||
cplus=any_cplus_src(sources), extra_objs=extra_objs, **link_kwargs)
|
||||
mod = import_module_from_file(so)
|
||||
return mod
|
||||
|
||||
|
||||
def _write_sources_to_build_dir(sources, build_dir):
|
||||
build_dir = build_dir or tempfile.mkdtemp()
|
||||
if not os.path.isdir(build_dir):
|
||||
raise OSError("Non-existent directory: ", build_dir)
|
||||
|
||||
source_files = []
|
||||
for name, src in sources:
|
||||
dest = os.path.join(build_dir, name)
|
||||
differs = True
|
||||
sha256_in_mem = sha256_of_string(src.encode('utf-8')).hexdigest()
|
||||
if os.path.exists(dest):
|
||||
if os.path.exists(dest + '.sha256'):
|
||||
sha256_on_disk = Path(dest + '.sha256').read_text()
|
||||
else:
|
||||
sha256_on_disk = sha256_of_file(dest).hexdigest()
|
||||
|
||||
differs = sha256_on_disk != sha256_in_mem
|
||||
if differs:
|
||||
with open(dest, 'wt') as fh:
|
||||
fh.write(src)
|
||||
with open(dest + '.sha256', 'wt') as fh:
|
||||
fh.write(sha256_in_mem)
|
||||
source_files.append(dest)
|
||||
return source_files, build_dir
|
||||
|
||||
|
||||
def compile_link_import_strings(sources, build_dir=None, **kwargs):
|
||||
""" Compiles, links and imports extension module from source.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
sources : iterable of name/source pair tuples
|
||||
build_dir : string (default: None)
|
||||
Path. ``None`` implies use a temporary directory.
|
||||
**kwargs:
|
||||
Keyword arguments passed onto `compile_link_import_py_ext`.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
mod : module
|
||||
The compiled and imported extension module.
|
||||
info : dict
|
||||
Containing ``build_dir`` as 'build_dir'.
|
||||
|
||||
"""
|
||||
source_files, build_dir = _write_sources_to_build_dir(sources, build_dir)
|
||||
mod = compile_link_import_py_ext(source_files, build_dir=build_dir, **kwargs)
|
||||
info = {"build_dir": build_dir}
|
||||
return mod, info
|
||||
|
||||
|
||||
def compile_run_strings(sources, build_dir=None, clean=False, compile_kwargs=None, link_kwargs=None):
|
||||
""" Compiles, links and runs a program built from sources.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
sources : iterable of name/source pair tuples
|
||||
build_dir : string (default: None)
|
||||
Path. ``None`` implies use a temporary directory.
|
||||
clean : bool
|
||||
Whether to remove build_dir after use. This will only have an
|
||||
effect if ``build_dir`` is ``None`` (which creates a temporary directory).
|
||||
Passing ``clean == True`` and ``build_dir != None`` raises a ``ValueError``.
|
||||
This will also set ``build_dir`` in returned info dictionary to ``None``.
|
||||
compile_kwargs: dict
|
||||
Keyword arguments passed onto ``compile_sources``
|
||||
link_kwargs: dict
|
||||
Keyword arguments passed onto ``link``
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
(stdout, stderr): pair of strings
|
||||
info: dict
|
||||
Containing exit status as 'exit_status' and ``build_dir`` as 'build_dir'
|
||||
|
||||
"""
|
||||
if clean and build_dir is not None:
|
||||
raise ValueError("Automatic removal of build_dir is only available for temporary directory.")
|
||||
try:
|
||||
source_files, build_dir = _write_sources_to_build_dir(sources, build_dir)
|
||||
objs = compile_sources(list(map(get_abspath, source_files)), destdir=build_dir,
|
||||
cwd=build_dir, **(compile_kwargs or {}))
|
||||
prog = link(objs, cwd=build_dir,
|
||||
fort=any_fortran_src(source_files),
|
||||
cplus=any_cplus_src(source_files), **(link_kwargs or {}))
|
||||
p = subprocess.Popen([prog], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
exit_status = p.wait()
|
||||
stdout, stderr = [txt.decode('utf-8') for txt in p.communicate()]
|
||||
finally:
|
||||
if clean and os.path.isdir(build_dir):
|
||||
shutil.rmtree(build_dir)
|
||||
build_dir = None
|
||||
info = {"exit_status": exit_status, "build_dir": build_dir}
|
||||
return (stdout, stderr), info
|
||||
Reference in New Issue
Block a user