Source code for pyccel.codegen.compiler
# coding: utf-8
import os
import subprocess
# ...
[docs]def clean(filename):
"""
removes the generated files: .pyccel and .f90
filename: str
name of the file to parse.
"""
name = filename.split('.py')[0]
for ext in ["f90", "pyccel"]:
f_name = name + "." + ext
cmd = "rm -f " + f_name
os.system(cmd)
# ...
# ...
[docs]def make_tmp_file(filename, output_dir=None):
"""
returns a temporary file of extension .pyccel that will be decorated with
indent/dedent so that textX can find the blocks easily.
filename: str
name of the file to parse.
output_dir: str
directory to store pyccel file
"""
name = filename.split('.py')[0]
if output_dir:
name = os.path.basename(name)
name = os.path.join(output_dir, name)
return name + ".pyccel"
# ...
# ...
[docs]def preprocess_as_str(lines):
"""
The input python file will be decorated with
indent/dedent so that textX can find the blocks easily.
This function will write the output code in filename_out.
lines: str or list
python code as a string
"""
if type(lines) == str:
ls = lines.split("\n")
lines = []
for l in ls:
lines.append(l + "\n")
# # to be sure that we dedent at the end
# lines += "\n"
lines_new = ""
def delta(line):
l = line.lstrip(' ')
n = len(line) - len(l)
return n
tab = 4
depth = 0
old_line = ""
annotated = ""
for i,line in enumerate(lines):
n = delta(line)
is_empty = (len(line.lstrip()) == 0)
is_comment = False
if not is_empty:
is_comment = (line.lstrip()[0] == '#')
skipped = is_empty or is_comment
is_annotated = (line.lstrip()[0:2] == '#$')
if n == depth * tab + tab:
depth += 1
lines_new += "indent" + "\n"
lines_new += line
elif not skipped:
d = n // tab
if (d > 0) or (n==0):
old = delta(old_line)
m = (old - n) // tab
depth -= m
for j in range(0, m):
lines_new += "dedent" + "\n"
lines_new += annotated
annotated = ""
lines_new += line
elif is_annotated:
annotated += line
else:
lines_new += line
if not skipped:
old_line = line
for i in range(0, depth):
lines_new += "dedent" + "\n"
lines_new += annotated
return lines_new
# ...
# ...
[docs]def preprocess(filename, filename_out):
"""
The input python file will be decorated with
indent/dedent so that textX can find the blocks easily.
This function will write the output code in filename_out.
filename: str
name of the file to parse.
filename_out: str
name of the temporary file that will be parsed by textX.
"""
f = open(filename)
lines = f.readlines()
f.close()
lines_new = preprocess_as_str(lines)
f = open(filename_out, "w")
for line in lines_new:
f.write(line)
f.close()
# ...
# ...
# TODO improve, when using mpi
[docs]def execute_file(binary):
"""
Execute a binary file.
binary: str
the name of the binary file
"""
cmd = binary
if not ('/' in binary):
cmd = "./" + binary
os.system(cmd)
# ...
# ...
[docs]def get_extension(language):
"""
returns the extension of a given language.
language: str
low-level target language used in the conversion
"""
if language == "fortran":
return "f90"
else:
raise ValueError("Only fortran is available")
# ...
# ...
[docs]def separator(n=40):
"""
Creates a separator string.
This is used to improve the readability of the generated code.
n: int
length of the separator
"""
txt = "."*n
comment = '!'
return '{0} {1}\n'.format(comment, txt)
# ...
[docs]class Compiler(object):
"""Base class for Code compiler for the Pyccel Grammar"""
def __init__(self, codegen, compiler,
flags=None,
accelerator=None,
binary=None,
debug=False,
inline=False,
include=[],
libdir=[],
libs=[],
ignored_modules=[]):
"""
Constructor of the code compiler.
codegen: pyccel.codegen.Codegen
a generation code object.
compiler: str
used compiler for the target language.
flags: str
compiler flags
accelerator: str
name of the selected accelerator.
One among ('openmp', 'openacc')
binary: str
name of the binary file to generate.
debug: bool
add some useful prints that may help for debugging.
inline: bool
set to True, if the file is being load inside a python session.
include: list
list of include directories paths
libdir: list
list of lib directories paths
libs: list
list of libraries to link with
ignored_modules: list
list of modules to ignore.
"""
self._codegen = codegen
self._compiler = compiler
self._binary = binary
self._debug = debug
self._inline = inline
self._accelerator = accelerator
self._include = include
self._libdir = libdir
self._libs = libs
self._ignored_modules = ignored_modules
if flags:
self._flags = flags
else:
self._flags = self.construct_flags()
@property
def codegen(self):
"""Returns the used codegen"""
return self._codegen
@property
def compiler(self):
"""Returns the used compiler"""
return self._compiler
@property
def flags(self):
"""Returns the used flags"""
return self._flags
@property
def binary(self):
"""Returns the used binary"""
return self._binary
@property
def debug(self):
"""Returns True if in debug mode"""
return self._debug
@property
def inline(self):
"""Returns True if in inline mode"""
return self._inline
@property
def accelerator(self):
"""Returns the used accelerator"""
return self._accelerator
@property
def include(self):
"""Returns include paths"""
return self._include
@property
def libdir(self):
"""Returns lib paths"""
return self._libdir
@property
def libs(self):
"""Returns libraries to link with"""
return self._libs
@property
def ignored_modules(self):
"""Returns ignored modules"""
return self._ignored_modules
[docs] def construct_flags(self):
"""
Constructs compiling flags
"""
# TODO use constructor and a dict to map flags w.r.t the compiler
_avail_compilers = ['gfortran', 'mpif90', 'pgfortran']
compiler = self.compiler
debug = self.debug
inline = self.inline
accelerator = self.accelerator
include = self.include
libdir = self.libdir
if not(compiler in _avail_compilers):
raise ValueError("Only {0} are available.".format(_avail_compilers))
flags = " -O3 "
if compiler == "gfortran":
if debug:
flags += " -fbounds-check "
if compiler == "mpif90":
if debug:
flags += " -fbounds-check "
if not (accelerator is None):
if accelerator == "openmp":
flags += " -fopenmp "
elif accelerator == "openacc":
flags += " -ta=multicore -Minfo=accel "
else:
raise ValueError("Only openmp and openacc are available")
if isinstance(include, str):
include = [include]
if len(include) > 0:
flags += ' '.join(' -I{0}'.format(i) for i in include)
if isinstance(libdir, str):
libdir = [libdir]
if len(libdir) > 0:
flags += ' '.join(' -L{0}'.format(i) for i in libdir)
return flags
[docs] def compile(self, verbose=False):
"""
Compiles the generated file.
verbose: bool
talk more
"""
compiler = self.compiler
flags = self.flags
inline = self.inline
libs = self.libs
filename = self.codegen.filename_out
is_module = self.codegen.is_module
modules = self.codegen.modules
# print('> ignored = ', self.ignored_modules)
# print('> modules = ', modules)
modules = [m for m in modules if not(m in self.ignored_modules)]
binary = ""
if self.binary is None:
if not is_module:
binary = filename.split('.')[0]
else:
binary = self.binary
o_code = ''
if not inline:
if not is_module:
o_code = "-o"
else:
flags += ' -c '
else:
o_code = '-m'
flags = '--quiet -c'
binary = self.codegen.name
compiler = 'f2py'
m_code = ' '.join('{}.o '.format(m) for m in modules)
if isinstance(libs, str):
libs = libs.split(',')
if len(libs) == 1:
libs = libs[0].split(' ')
if len(libs) > 0:
libs = ' '.join(' -l{0}'.format(i) for i in libs)
else:
libs = ''
cmd = '{0} {1} {2} {3} {4} {5} {6}'.format( \
compiler, flags, m_code, filename, o_code, binary, libs)
if verbose:
print(cmd)
output = subprocess.check_output(cmd, shell=True)
if verbose:
print(output)
# write and save a log file in .pyccel/'filename'.log
# ...
def mkdir_p(dir):
# type: (unicode) -> None
if os.path.isdir(dir):
return
os.makedirs(dir)
if inline:
tmp_dir = '.pyccel'
mkdir_p(tmp_dir)
logfile = '{0}.log'.format(binary)
logfile = os.path.join(tmp_dir, logfile)
f = open(logfile, 'w')
f.write(output)
f.close()
# ...
self._binary = binary
# ...