def test_reallocation_heap(language):

    def f():
        import numpy as np
        x = np.zeros((3, 7), dtype=int)
        x = np.ones ((4, 5), dtype=int)
        return x.sum()

    # Initialize singleton that stores Pyccel errors
    errors = Errors()

    # TODO: check if we get the correct Pyccel warning
    g = epyccel(f, language=language)

    # Check result of pyccelized function
    assert f() == g()

    # Check that we got exactly 1 Pyccel warning
    assert errors.has_warnings()
    assert errors.num_messages() == 1

    # Check that we the warning is correct
    warning_info = [*errors.error_info_map.values()][0][0]
    assert warning_info.symbol  == 'x'
    assert warning_info.message == ARRAY_REALLOCATION
def test_creation_in_loop_heap(language):

    def f():
        import numpy as np
        for i in range(3):
            x = np.ones(i, dtype=int)
        return x.sum()

    # Initialize singleton that stores Pyccel errors
    errors = Errors()

    # TODO: check if we get the correct Pyccel warning
    g = epyccel(f, language=language)

    # Check result of pyccelized function
    assert f() == g()

    # Check that we got exactly 1 Pyccel warning
    assert errors.has_warnings()
    assert errors.num_messages() == 1

    # Check that we the warning is correct
    warning_info = [*errors.error_info_map.values()][0][0]
    assert warning_info.symbol  == 'x'
    assert warning_info.message == ARRAY_DEFINITION_IN_LOOP
Exemplo n.º 3
0
def builtin_import(expr):
    """Returns a builtin pyccel-extension function/object from an import."""

    if not isinstance(expr, Import):
        raise TypeError('Expecting an Import expression')

    if isinstance(expr.source, AsName):
        source = str(expr.source.name)
    else:
        source = str(expr.source)

    if source == 'pyccel.decorators':
        funcs = [
            f[0]
            for f in inspect.getmembers(pyccel_decorators, inspect.isfunction)
        ]
        for target in expr.target:
            search_target = target.name if isinstance(target,
                                                      AsName) else str(target)
            if search_target not in funcs:
                errors = Errors()
                errors.report(
                    "{} does not exist in pyccel.decorators".format(target),
                    symbol=expr,
                    severity='error')

    elif source in builtin_import_registery:
        return collect_relevant_imports(builtin_import_registery[source],
                                        expr.target)

    return []
Exemplo n.º 4
0
def test_syntax(f):

    pyccel = Parser(f)
    pyccel.parse()

    # reset Errors singleton
    errors = Errors()
    errors.reset()
Exemplo n.º 5
0
def test_preprocess(f):
    pyccel = Parser(f)
    pyccel.parse()
    print(pyccel.fst)

    # reset Errors singleton
    errors = Errors()
    errors.reset()
Exemplo n.º 6
0
def test_syntax_errors(f):
    # reset Errors singleton
    errors = Errors()
    errors.reset()

    pyccel = Parser(f)

    ast = pyccel.parse()

    assert (errors.has_errors())
Exemplo n.º 7
0
def test_syntax_blockers(f):
    # reset Errors singleton
    errors = Errors()
    errors.reset()

    pyccel = Parser(f)

    with pytest.raises(PyccelSyntaxError):
        ast = pyccel.parse()

    assert (errors.has_blockers())
Exemplo n.º 8
0
def test_semantic(f):

    pyccel = Parser(f)
    pyccel.parse()

    settings = {}
    pyccel.annotate(**settings)

    # reset Errors singleton
    errors = Errors()
    errors.reset()
Exemplo n.º 9
0
def test_semantic_warnings(f):

    pyccel = Parser(f, show_traceback=False)
    pyccel.parse()

    settings = {}
    pyccel.annotate(**settings)

    # reset Errors singleton
    errors = Errors()
    assert(errors.num_messages()!=0)
    errors.reset()
Exemplo n.º 10
0
def test_semantic_non_blocking_errors(f):
    print('> testing {0}'.format(str(f)))

    # reset Errors singleton
    errors = Errors()
    errors.reset()

    pyccel = Parser(f, show_traceback=False)
    ast = pyccel.parse()

    settings = {}
    ast = pyccel.annotate(**settings)

    assert (errors.has_errors())
Exemplo n.º 11
0
def extend_tree(code):
    comment_lines_no, comments, else_no = get_comments(code)
    try:
        tree = parse(code)
    except SyntaxError as e:
        errors = Errors()
        errors.report(INVALID_PYTHON_SYNTAX, symbol='\n' + str(e),
                      severity='fatal')
    if len(tree.body) == 0:
        if len(comments) > 0:
            tree.body  = list(comments)
        return tree

    insert_comments(tree, comment_lines_no, comments, else_no)
    return tree
Exemplo n.º 12
0
    def set_printer(self, **settings):
        """ Set the current codeprinter instance"""
        # Get language used (default language used is fortran)
        language = settings.pop('language', 'fortran')

        # Set language
        if not language in ['fortran', 'c', 'python']:
            raise ValueError('{} language is not available'.format(language))
        self._language = language

        # instantiate codePrinter
        code_printer = printer_registry[language]
        errors = Errors()
        errors.set_parser_stage('codegen')
        # set the code printer
        self._printer = code_printer(self.parser, settings)
Exemplo n.º 13
0
def test_symbolic(f):

    pyccel = Parser(f)
    pyccel.parse()

    settings = {}
    ast = pyccel.annotate(**settings)

    name = os.path.basename(f)
    name = os.path.splitext(name)[0]

    codegen = Codegen(ast, name)
    codegen.doprint()

    # reset Errors singleton
    errors = Errors()
    errors.reset()
Exemplo n.º 14
0
def test_codegen(f):

    # reset Errors singleton
    errors = Errors()
    errors.reset()

    pyccel = Parser(f)
    ast = pyccel.parse()

    # Assert syntactic success
    assert (not errors.has_errors())

    settings = {}
    ast = pyccel.annotate(**settings)

    # Assert semantic success
    assert (not errors.has_errors())

    name = os.path.basename(f)
    name = os.path.splitext(name)[0]

    codegen = Codegen(ast, name)
    codegen.doprint(language='c')

    # Assert codegen success
    assert (not errors.has_errors())
Exemplo n.º 15
0
def test_codegen_errors(f):
    # reset Errors singleton
    errors = Errors()
    errors.reset()

    pyccel = Parser(f)
    ast = pyccel.parse()

    settings = {}
    ast = pyccel.annotate(**settings)

    name = os.path.basename(f)
    name = os.path.splitext(name)[0]

    codegen = Codegen(ast, name)
    with pytest.raises(PyccelCodegenError):
        codegen.doprint()

    assert (errors.has_errors())
def test_reallocation_stack(language):
    @stack_array('x')
    def f():
        import numpy as np
        x = np.zeros((3, 7), dtype=int)
        x = np.ones((4, 5), dtype=int)
        return x.sum()

    # Initialize singleton that stores Pyccel errors
    errors = Errors()

    # epyccel should raise an Exception
    with pytest.raises(PyccelSemanticError):
        epyccel(f, language=language)

    # Check that we got exactly 1 Pyccel error
    assert errors.has_errors()
    assert errors.num_messages() == 1

    # Check that the error is correct
    error_info = [*errors.error_info_map.values()][0][0]
    assert error_info.symbol == 'x'
    assert error_info.message == INCOMPATIBLE_REDEFINITION_STACK_ARRAY
def test_creation_in_loop_stack(language):
    @stack_array('x')
    def f():
        import numpy as np
        for i in range(3):
            x = np.ones(i, dtype=int)
        return x.sum()

    # Initialize singleton that stores Pyccel errors
    errors = Errors()

    # epyccel should raise an Exception
    with pytest.raises(PyccelSemanticError):
        epyccel(f, language=language)

    # Check that we got exactly 1 Pyccel error
    assert errors.has_errors()
    assert errors.num_messages() == 1

    # Check that the error is correct
    error_info = [*errors.error_info_map.values()][0][0]
    assert error_info.symbol == 'x'
    assert error_info.message == STACK_ARRAY_DEFINITION_IN_LOOP
def test_Reassign_to_Target():
    def f():
        import numpy as np
        x = np.zeros((3, 7), dtype=int)
        c = x
        x = np.ones((4, 5), dtype=int)
        return c.sum()

    # Initialize singleton that stores Pyccel errors
    errors = Errors()

    # epyccel should raise an Exception
    with pytest.raises(PyccelSemanticError):
        epyccel(f)

    # Check that we got exactly 1 Pyccel error
    assert errors.has_errors() == 1
    assert errors.num_messages() == 1

    # Check that the error is correct
    error_info = [*errors.error_info_map.values()][0][0]
    assert error_info.symbol == 'x'
    assert error_info.message == ARRAY_ALREADY_IN_USE
def test_Assign_Between_Allocatables():
    def f():
        import numpy as np
        x = np.zeros((3, 7), dtype=int)
        y = np.ones((4, 5), dtype=int)
        x = y
        x[0][0] = 1
        return y.sum()

    # Initialize singleton that stores Pyccel errors
    errors = Errors()

    # epyccel should raise an Exception
    with pytest.raises(PyccelSemanticError):
        epyccel(f)

    # Check that we got exactly 1 Pyccel error
    assert errors.has_errors() == 1
    assert errors.num_messages() == 1

    # Check that the error is correct
    error_info = [*errors.error_info_map.values()][0][0]
    assert error_info.symbol == 'x'
    assert error_info.message == ASSIGN_ARRAYS_ONE_ANOTHER
Exemplo n.º 20
0
                  'static',
                  'struct',
                  'super',
                  'trait',
                  'true',
                  'type',
                  'typeof',
                  'unsafe',
                  'unsized',
                  'use',
                  'virtual',
                  'where',
                  'while',
                  'yield']

errors = Errors()

class LuaCodePrinter(CodePrinter):
    """A printer to convert python expressions to strings of Lua code"""
    printmethod = "_lua_code"
    language = "Lua"

    _default_settings = {
        'order': None,
        'full_prec': 'auto',
        'precision': 15,
        'user_functions': {},
        'local_vars': {},
        'human': True,
        'contract': True,
        'dereference': set(),
Exemplo n.º 21
0
def execute_pyccel(fname,
                   *,
                   syntax_only=False,
                   semantic_only=False,
                   convert_only=False,
                   verbose=False,
                   folder=None,
                   language=None,
                   compiler=None,
                   mpi_compiler=None,
                   fflags=None,
                   includes=(),
                   libdirs=(),
                   modules=(),
                   libs=(),
                   debug=False,
                   accelerator=None,
                   output_name=None):
    """
    Carries out the main steps required to execute pyccel
    - Parses the python file (syntactic stage)
    - Annotates the abstract syntax tree (semantic stage)
    - Generates the translated file(s) (codegen stage)
    - Compiles the files to generate an executable and/or a shared library

    Parameters
    ----------
    fname         : str
                    Name of python file to be translated

    syntax_only   : bool
                    Boolean indicating whether the pipeline should stop
                    after the syntax stage
                    Default : False

    semantic_only : bool
                    Boolean indicating whether the pipeline should stop
                    after the semantic stage
                    Default : False

    convert_only  : bool
                    Boolean indicating whether the pipeline should stop
                    after the codegen stage
                    Default : False

    verbose       : bool
                    Boolean indicating whether debugging messages should be printed
                    Default : False

    folder        : str
                    Path to the working directory
                    Default : folder containing the file to be translated

    language      : str
                    The language which pyccel is translating to
                    Default : fortran

    compiler      : str
                    The compiler used to compile the generated files
                    Default : GNU

    mpi_compiler  : str
                    The compiler used to compile the generated files when mpi is needed.
                    This value must be provided to compile with mpi
                    Default : None (compile with 'compiler')

    fflags        : str
                    The flags passed to the compiler
                    Default : provided by codegen.utilities.construct_flags

    includes      : list
                    list of include directories paths

    libdirs       : list
                    list of paths to directories containing the required libraries

    modules       : list
                    list of files which must also be compiled in order to compile this module

    libs          : list
                    list of required libraries

    debug         : bool
                    Boolean indicating whether the file should be compiled in debug mode
                    (currently this only implies that the flag -fcheck=bounds is added)
                    Default : False

    accelerator   : str
                    Tool used to accelerate the code (e.g. openmp openacc)

    output_name   : str
                    Name of the generated module
                    Default : Same name as the file which was translated
    """

    # Reset Errors singleton before parsing a new file
    errors = Errors()
    errors.reset()

    # TODO [YG, 03.02.2020]: test validity of function arguments

    # Copy list arguments to local lists to avoid unexpected behavior
    includes = [*includes]
    libdirs = [*libdirs]
    modules = [*modules]
    libs = [*libs]

    # Store current directory and add it to sys.path
    # variable to imitate Python's import behavior
    base_dirpath = os.getcwd()
    sys.path.insert(0, base_dirpath)

    # Unified way to handle errors: print formatted error message, then move
    # to original working directory. Caller should then raise exception.
    def handle_error(stage):
        print('\nERROR at {} stage'.format(stage))
        errors.check()
        os.chdir(base_dirpath)

    # Identify absolute path, directory, and filename
    pymod_filepath = os.path.abspath(fname)
    pymod_dirpath, pymod_filename = os.path.split(pymod_filepath)

    # Extract module name
    module_name = os.path.splitext(pymod_filename)[0]

    # Define working directory 'folder'
    if folder is None or folder == "":
        folder = pymod_dirpath
    else:
        folder = os.path.abspath(folder)

    # Define directory name and path for pyccel & cpython build
    pyccel_dirname = '__pyccel__'
    pyccel_dirpath = os.path.join(folder, pyccel_dirname)

    # Create new directories if not existing
    os.makedirs(folder, exist_ok=True)
    os.makedirs(pyccel_dirpath, exist_ok=True)

    # Change working directory to 'folder'
    os.chdir(folder)

    if language is None:
        language = 'fortran'

    # Choose Fortran compiler
    if compiler is None:
        if language == 'fortran':
            compiler = 'gfortran'
        elif language == 'c':
            compiler = 'gcc'

    f90exec = mpi_compiler if mpi_compiler else compiler

    if (language == "c"):
        libs = libs + ['m']
    if accelerator == 'openmp':
        if compiler in ["gcc", "gfortran"]:
            if sys.platform == "darwin" and compiler == "gcc":
                libs = libs + ['omp']
            else:
                libs = libs + ['gomp']

        elif compiler == 'ifort':
            libs.append('iomp5')

    # ...
    # Construct flags for the Fortran compiler
    if fflags is None:
        fflags = construct_flags(f90exec,
                                 fflags=None,
                                 debug=debug,
                                 accelerator=accelerator,
                                 includes=())

    # Build position-independent code, suited for use in shared library
    fflags = ' {} -fPIC '.format(fflags)
    # ...

    # Parse Python file
    try:
        parser = Parser(pymod_filepath, show_traceback=verbose)
        parser.parse(verbose=verbose)
    except NotImplementedError as error:
        msg = str(error)
        errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO, severity='error')
    except PyccelError:
        handle_error('parsing (syntax)')
        raise
    if errors.has_errors():
        handle_error('parsing (syntax)')
        raise PyccelSyntaxError('Syntax step failed')

    if syntax_only:
        return

    # Annotate abstract syntax Tree
    try:
        settings = {'verbose': verbose}
        parser.annotate(**settings)
    except NotImplementedError as error:
        msg = str(error)
        errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO, severity='error')
    except PyccelError:
        handle_error('annotation (semantic)')
        # Raise a new error to avoid a large traceback
        raise PyccelSemanticError('Semantic step failed') from None

    if errors.has_errors():
        handle_error('annotation (semantic)')
        raise PyccelSemanticError('Semantic step failed')

    if semantic_only:
        return

    if parser.module_parser:
        parsers = [parser.module_parser, parser]
        program_name = os.path.basename(os.path.splitext(parser.filename)[0])
        module_names = [module_name, program_name]
    else:
        parsers = [parser]
        module_names = [module_name]

    # -------------------------------------------------------------------------
    # get path to pyccel/stdlib/lib_name
    stdlib_path = os.path.dirname(stdlib_folder.__file__)

    internal_libs_name = set()
    internal_libs_path = []
    internal_libs_files = []
    for parser, module_name in zip(parsers, module_names):
        semantic_parser = parser.semantic_parser
        # Generate .f90 file
        try:
            codegen = Codegen(semantic_parser, module_name)
            fname = os.path.join(pyccel_dirpath, module_name)
            fname = codegen.export(fname, language=language)
        except NotImplementedError as error:
            msg = str(error)
            errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO,
                          severity='error')
        except PyccelError:
            handle_error('code generation')
            # Raise a new error to avoid a large traceback
            raise PyccelCodegenError('Code generation failed') from None

        if errors.has_errors():
            handle_error('code generation')
            raise PyccelCodegenError('Code generation failed')

        #------------------------------------------------------
        # TODO: collect dependencies and proceed recursively
        # if recursive:
        #     for dep in parser.sons:
        #         # Call same function on 'dep'
        #         pass
        #------------------------------------------------------

        # Iterate over the internal_libs list and determine if the printer
        # requires an internal lib to be included.
        for lib in internal_libs:
            if lib in codegen.get_printer_imports():
                # get the include folder path and library files
                if lib not in internal_libs_name:
                    # get the library folder name
                    lib_name = internal_libs[lib]
                    # get lib path (stdlib_path/lib_name)
                    lib_path = os.path.join(stdlib_path, lib_name)
                    # remove library folder to avoid missing files and copy
                    # new one from pyccel stdlib
                    lib_dest_path = os.path.join(pyccel_dirpath, lib_name)
                    if os.path.exists(lib_dest_path):
                        shutil.rmtree(lib_dest_path)
                    shutil.copytree(lib_path, lib_dest_path)

                    # stop after copying lib to __pyccel__ directory for
                    # convert only
                    if convert_only:
                        continue

                    # get library source files
                    source_files = []
                    for e in os.listdir(lib_dest_path):
                        if e.endswith(lang_ext_dict[language]):
                            source_files.append(os.path.join(lib_dest_path, e))

                    # compile library source files
                    flags = construct_flags(f90exec,
                                            fflags=fflags,
                                            debug=debug,
                                            includes=[lib_dest_path])
                    try:
                        for f in source_files:
                            compile_files(f,
                                          f90exec,
                                          flags,
                                          binary=None,
                                          verbose=verbose,
                                          is_module=True,
                                          output=lib_dest_path,
                                          language=language)
                    except Exception:
                        handle_error('C {} library compilation'.format(lib))
                        raise
                    # Add internal lib to internal_libs_name set
                    internal_libs_name.add(lib)
                    # add source file without extension to internal_libs_files
                    internal_libs_files.extend(
                        os.path.splitext(f)[0] for f in source_files)
                    # add library path to internal_libs_path
                    internal_libs_path.append(lib_dest_path)

        if convert_only:
            continue

        # ...
        # Determine all .o files and all folders needed by executable
        def get_module_dependencies(parser, mods=(), folders=()):
            mod_folder = os.path.join(os.path.dirname(parser.filename),
                                      "__pyccel__")
            mod_base = os.path.splitext(os.path.basename(parser.filename))[0]

            # Stop conditions
            if parser.metavars.get('ignore_at_import', False) or \
               parser.metavars.get('module_name', None) == 'omp_lib':
                return mods, folders

            # Update lists
            mods = [*mods, os.path.join(mod_folder, mod_base)]
            folders = [*folders, mod_folder]

            # Proceed recursively
            for son in parser.sons:
                mods, folders = get_module_dependencies(son, mods, folders)

            return mods, folders

        dep_mods, inc_dirs = get_module_dependencies(parser)

        # Add internal dependencies
        dep_mods = [*dep_mods, *internal_libs_files]
        inc_dirs = [*inc_dirs, *internal_libs_path]

        # Remove duplicates without changing order
        dep_mods = tuple(OrderedDict.fromkeys(dep_mods))
        inc_dirs = tuple(OrderedDict.fromkeys(inc_dirs))
        # ...

        includes += inc_dirs

        if codegen.is_program:
            modules += [os.path.join(pyccel_dirpath, m) for m in dep_mods[1:]]

        # Construct compiler flags
        flags = construct_flags(f90exec,
                                fflags=fflags,
                                debug=debug,
                                accelerator=accelerator,
                                includes=includes)

        # Compile Fortran code
        #
        # TODO: stop at object files, do not compile executable
        #       This allows for properly linking program to modules
        #
        try:
            compile_files(fname,
                          f90exec,
                          flags,
                          binary=None,
                          verbose=verbose,
                          modules=modules,
                          is_module=codegen.is_module,
                          output=pyccel_dirpath,
                          libs=libs,
                          libdirs=libdirs,
                          language=language)
        except Exception:
            handle_error('Fortran compilation')
            raise

        # For a program stop here
        if codegen.is_program:
            if verbose:
                exec_filepath = os.path.join(folder, module_name)
                print(
                    '> Executable has been created: {}'.format(exec_filepath))
            os.chdir(base_dirpath)
            continue

        # Create shared library
        try:
            sharedlib_filepath = create_shared_library(
                codegen, language, pyccel_dirpath, compiler, mpi_compiler,
                accelerator, dep_mods, libs, libdirs, includes, flags,
                output_name, verbose)
        except NotImplementedError as error:
            msg = str(error)
            errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO,
                          severity='error')
            handle_error('code generation (wrapping)')
            raise PyccelCodegenError(msg) from None
        except PyccelError:
            handle_error('code generation (wrapping)')
            raise
        except Exception:
            handle_error('shared library generation')
            raise

        if errors.has_errors():
            handle_error('code generation (wrapping)')
            raise PyccelCodegenError('Code generation failed')

        # Move shared library to folder directory
        # (First construct absolute path of target location)
        sharedlib_filename = os.path.basename(sharedlib_filepath)
        target = os.path.join(folder, sharedlib_filename)
        shutil.move(sharedlib_filepath, target)
        sharedlib_filepath = target

        if verbose:
            print('> Shared library has been created: {}'.format(
                sharedlib_filepath))

    # Print all warnings now
    if errors.has_warnings():
        errors.check()

    # Change working directory back to starting point
    os.chdir(base_dirpath)
Exemplo n.º 22
0
def get_filename_from_import(module, input_folder=''):
    """Returns a valid filename with absolute path, that corresponds to the
    definition of module.
    The priority order is:
        - header files (extension == pyh)
        - python files (extension == py)
    """

    if (isinstance(module, AsName)):
        module = str(module.name)

    # Remove first '.' as it doesn't represent a folder change
    if module[0] == '.':
        module = module[1:]
    filename = module.replace('.', '/')

    # relative imports
    folder_above = '../'
    while filename.startswith('/'):
        filename = folder_above + filename[1:]

    filename_pyh = '{}.pyh'.format(filename)
    filename_py = '{}.py'.format(filename)

    if is_valid_filename_pyh(filename_pyh):
        return os.path.abspath(filename_pyh)
    if is_valid_filename_py(filename_py):
        return os.path.abspath(filename_py)
    folders = input_folder.split(""".""")
    for i in range(len(folders)):
        poss_dirname = os.path.join(*folders[:i + 1])
        poss_filename_pyh = os.path.join(poss_dirname, filename_pyh)
        poss_filename_py = os.path.join(poss_dirname, filename_py)
        if is_valid_filename_pyh(poss_filename_pyh):
            return os.path.abspath(poss_filename_pyh)
        if is_valid_filename_py(poss_filename_py):
            return os.path.abspath(poss_filename_py)

    source = module
    if len(module.split(""".""")) > 1:

        # we remove the last entry, since it can be a pyh file

        source = """.""".join(i for i in module.split(""".""")[:-1])
        _module = module.split(""".""")[-1]
        filename_pyh = '{}.pyh'.format(_module)
        filename_py = '{}.py'.format(_module)

    try:
        package = importlib.import_module(source)
        package_dir = str(package.__path__[0])
    except ImportError:
        errors = Errors()
        errors.report(PYCCEL_UNFOUND_IMPORTED_MODULE,
                      symbol=source,
                      severity='fatal')

    filename_pyh = os.path.join(package_dir, filename_pyh)
    filename_py = os.path.join(package_dir, filename_py)
    if os.path.isfile(filename_pyh):
        return filename_pyh
    elif os.path.isfile(filename_py):
        return filename_py

    errors = Errors()
    errors.report(PYCCEL_UNFOUND_IMPORTED_MODULE,
                  symbol=module,
                  severity='fatal')
Exemplo n.º 23
0
def execute_pyccel(fname,
                   *,
                   syntax_only=False,
                   semantic_only=False,
                   convert_only=False,
                   recursive=False,
                   verbose=False,
                   folder=None,
                   language=None,
                   compiler=None,
                   mpi_compiler=None,
                   fflags=None,
                   includes=(),
                   libdirs=(),
                   modules=(),
                   libs=(),
                   debug=False,
                   extra_args='',
                   accelerator=None,
                   output_name=None):

    # Reset Errors singleton before parsing a new file
    errors = Errors()
    errors.reset()

    # TODO [YG, 03.02.2020]: test validity of function arguments

    # Copy list arguments to local lists to avoid unexpected behavior
    includes = [*includes]
    libdirs = [*libdirs]
    modules = [*modules]
    libs = [*libs]

    # Store current directory and add it to sys.path
    # variable to imitate Python's import behavior
    base_dirpath = os.getcwd()
    sys.path.insert(0, base_dirpath)

    # Unified way to handle errors: print formatted error message, then move
    # to original working directory. Caller should then raise exception.
    def handle_error(stage):
        print('\nERROR at {} stage'.format(stage))
        errors.check()
        os.chdir(base_dirpath)

    # Identify absolute path, directory, and filename
    pymod_filepath = os.path.abspath(fname)
    pymod_dirpath, pymod_filename = os.path.split(pymod_filepath)

    # Extract module name
    module_name = os.path.splitext(pymod_filename)[0]

    # Define working directory 'folder'
    if folder is None or folder == "":
        folder = pymod_dirpath
    else:
        folder = os.path.abspath(folder)

    # Define directory name and path for pyccel & cpython build
    pyccel_dirname = '__pyccel__'
    pyccel_dirpath = os.path.join(folder, pyccel_dirname)

    # Create new directories if not existing
    os.makedirs(folder, exist_ok=True)
    os.makedirs(pyccel_dirpath, exist_ok=True)

    # Change working directory to 'folder'
    os.chdir(folder)

    if language is None:
        language = 'fortran'

    # Choose Fortran compiler
    if compiler is None:
        if language == 'fortran':
            compiler = 'gfortran'
        elif language == 'c':
            compiler = 'gcc'

    f90exec = mpi_compiler if mpi_compiler else compiler

    if (language == "c"):
        libs = libs + ['m']
    if accelerator == 'openmp':
        if compiler in ["gcc", "gfortran"]:
            if sys.platform == "darwin" and compiler == "gcc":
                libs = libs + ['omp']
            else:
                libs = libs + ['gomp']

        elif compiler == 'ifort':
            libs.append('iomp5')

    # ...
    # Construct flags for the Fortran compiler
    if fflags is None:
        fflags = construct_flags(f90exec,
                                 fflags=None,
                                 debug=debug,
                                 accelerator=accelerator,
                                 includes=())

    # Build position-independent code, suited for use in shared library
    fflags = ' {} -fPIC '.format(fflags)
    # ...

    # Parse Python file
    try:
        parser = Parser(pymod_filepath, show_traceback=verbose)
        parser.parse(verbose=verbose)
    except NotImplementedError as error:
        msg = str(error)
        errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO, severity='error')
    except PyccelError:
        handle_error('parsing (syntax)')
        raise
    if errors.has_errors():
        handle_error('parsing (syntax)')
        raise PyccelSyntaxError('Syntax step failed')

    if syntax_only:
        return

    # Annotate abstract syntax Tree
    try:
        settings = {'verbose': verbose}
        parser.annotate(**settings)
    except NotImplementedError as error:
        msg = str(error)
        errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO, severity='error')
    except PyccelError:
        handle_error('annotation (semantic)')
        # Raise a new error to avoid a large traceback
        raise PyccelSemanticError('Semantic step failed') from None

    if errors.has_errors():
        handle_error('annotation (semantic)')
        raise PyccelSemanticError('Semantic step failed')

    if semantic_only:
        return

    if parser.module_parser:
        parsers = [parser.module_parser, parser]
        program_name = os.path.basename(os.path.splitext(parser.filename)[0])
        module_names = [module_name, program_name]
    else:
        parsers = [parser]
        module_names = [module_name]

    for parser, module_name in zip(parsers, module_names):
        semantic_parser = parser.semantic_parser
        # Generate .f90 file
        try:
            codegen = Codegen(semantic_parser, module_name)
            fname = os.path.join(pyccel_dirpath, module_name)
            fname = codegen.export(fname, language=language)
        except NotImplementedError as error:
            msg = str(error)
            errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO,
                          severity='error')
        except PyccelError:
            handle_error('code generation')
            # Raise a new error to avoid a large traceback
            raise PyccelCodegenError('Code generation failed') from None

        if errors.has_errors():
            handle_error('code generation')
            raise PyccelCodegenError('Code generation failed')

        #------------------------------------------------------
        # TODO: collect dependencies and proceed recursively
    #    if recursive:
    #        for dep in parser.sons:
    #            # Call same function on 'dep'
    #            pass
    #------------------------------------------------------

        if convert_only:
            continue

        # ...
        # Determine all .o files and all folders needed by executable
        def get_module_dependencies(parser, mods=(), folders=()):
            mod_folder = os.path.join(os.path.dirname(parser.filename),
                                      "__pyccel__")
            mod_base = os.path.splitext(os.path.basename(parser.filename))[0]

            # Stop conditions
            if parser.metavars.get('ignore_at_import', False) or \
               parser.metavars.get('module_name', None) == 'omp_lib':
                return mods, folders

            # Update lists
            mods = [*mods, os.path.join(mod_folder, mod_base)]
            folders = [*folders, mod_folder]

            # Proceed recursively
            for son in parser.sons:
                mods, folders = get_module_dependencies(son, mods, folders)

            return mods, folders

        dep_mods, inc_dirs = get_module_dependencies(parser)

        # Remove duplicates without changing order
        dep_mods = tuple(OrderedDict.fromkeys(dep_mods))
        inc_dirs = tuple(OrderedDict.fromkeys(inc_dirs))
        # ...

        includes += inc_dirs

        if codegen.is_program:
            modules += [os.path.join(pyccel_dirpath, m) for m in dep_mods[1:]]

        # Construct compiler flags
        flags = construct_flags(f90exec,
                                fflags=fflags,
                                debug=debug,
                                accelerator=accelerator,
                                includes=includes)

        # Compile Fortran code
        #
        # TODO: stop at object files, do not compile executable
        #       This allows for properly linking program to modules
        #
        try:
            compile_files(fname,
                          f90exec,
                          flags,
                          binary=None,
                          verbose=verbose,
                          modules=modules,
                          is_module=codegen.is_module,
                          output=pyccel_dirpath,
                          libs=libs,
                          libdirs=libdirs,
                          language=language)
        except Exception:
            handle_error('Fortran compilation')
            raise

        # For a program stop here
        if codegen.is_program:
            if verbose:
                exec_filepath = os.path.join(folder, module_name)
                print(
                    '> Executable has been created: {}'.format(exec_filepath))
            os.chdir(base_dirpath)
            continue

        # Create shared library
        try:
            sharedlib_filepath = create_shared_library(
                codegen, language, pyccel_dirpath, compiler, mpi_compiler,
                accelerator, dep_mods, libs, libdirs, includes, flags,
                extra_args, output_name, verbose)
        except NotImplementedError as error:
            msg = str(error)
            errors.report(msg + '\n' + PYCCEL_RESTRICTION_TODO,
                          severity='error')
            handle_error('code generation (wrapping)')
            raise PyccelCodegenError(msg) from None
        except PyccelError:
            handle_error('code generation (wrapping)')
            raise
        except Exception:
            handle_error('shared library generation')
            raise

        if errors.has_errors():
            handle_error('code generation (wrapping)')
            raise PyccelCodegenError('Code generation failed')

        # Move shared library to folder directory
        # (First construct absolute path of target location)
        sharedlib_filename = os.path.basename(sharedlib_filepath)
        target = os.path.join(folder, sharedlib_filename)
        shutil.move(sharedlib_filepath, target)
        sharedlib_filepath = target

        if verbose:
            print('> Shared library has been created: {}'.format(
                sharedlib_filepath))

    # Print all warnings now
    if errors.has_warnings():
        errors.check()

    # Change working directory back to starting point
    os.chdir(base_dirpath)
Exemplo n.º 24
0
def pyccel(files=None, openmp=None, openacc=None, output_dir=None, compiler=None):
    """
    pyccel console command.
    """
    parser = MyParser(description='pyccel command line')

    parser.add_argument('files', metavar='N', type=str, nargs='+',
                        help='a Pyccel file')

    #... Version
    import pyccel
    version = pyccel.__version__
    libpath = pyccel.__path__[0]
    python  = 'python {}.{}'.format(*sys.version_info)
    message = "pyccel {} from {} ({})".format(version, libpath, python)
    parser.add_argument('-V', '--version', action='version', version=message)
    # ...

    # ... compiler syntax, semantic and codegen
    group = parser.add_argument_group('Pyccel compiling stages')
    group.add_argument('-x', '--syntax-only', action='store_true',
                       help='Using pyccel for Syntax Checking')
    group.add_argument('-e', '--semantic-only', action='store_true',
                       help='Using pyccel for Semantic Checking')
    group.add_argument('-t', '--convert-only', action='store_true',
                       help='Converts pyccel files only without build')
#    group.add_argument('-f', '--f2py-compatible', action='store_true',
#                        help='Converts pyccel files to be compiled by f2py')

    # ...

    # ... backend compiler options
    group = parser.add_argument_group('Backend compiler options')

    group.add_argument('--language', choices=('fortran', 'c', 'python'), help='Generated language')

    group.add_argument('--compiler', choices=('gfortran', 'ifort', 'pgfortran', \
            'gcc', 'icc'), help='Compiler name')

    group.add_argument('--mpi-compiler', help='MPI compiler wrapper')

    group.add_argument('--flags', type=str, \
                       help='Compiler flags.')
    group.add_argument('--debug', action='store_true', \
                       help='compiles the code in a debug mode.')

    group.add_argument('--include',
                        type=str,
                        nargs='*',
                        dest='includes',
                        default=(),
                        help='list of include directories.')

    group.add_argument('--libdir',
                        type=str,
                        nargs='*',
                        dest='libdirs',
                        default=(),
                        help='list of library directories.')

    group.add_argument('--libs',
                        type=str,
                        nargs='*',
                        dest='libs',
                        default=(),
                        help='list of libraries to link with.')

    group.add_argument('--output', type=str, default = '',\
                       help='folder in which the output is stored.')

    # ...

    # ... Accelerators
    group = parser.add_argument_group('Accelerators options')
    group.add_argument('--openmp', action='store_true', \
                       help='uses openmp')
    group.add_argument('--openacc', action='store_true', \
                       help='uses openacc')
    # ...

    # ... Other options
    group = parser.add_argument_group('Other options')
    group.add_argument('--verbose', action='store_true', \
                        help='enables verbose mode.')
    group.add_argument('--developer-mode', action='store_true', \
                        help='shows internal messages')
    # ...

    # TODO move to another cmd line
    parser.add_argument('--analysis', action='store_true', \
                        help='enables code analysis mode.')
    # ...

    # ...
    args = parser.parse_args()
    # ...

    # Imports
    from pyccel.errors.errors     import Errors, PyccelError
    from pyccel.errors.errors     import ErrorsMode
    from pyccel.errors.messages   import INVALID_FILE_DIRECTORY, INVALID_FILE_EXTENSION
    from pyccel.codegen.pipeline  import execute_pyccel

    # ...
    if not files:
        files = args.files

    if args.compiler:
        compiler = args.compiler

    if not openmp:
        openmp = args.openmp

    if not openacc:
        openacc = args.openacc

    if args.convert_only or args.syntax_only or args.semantic_only:
        compiler = None
    # ...

    # ...

    if len(files) > 1:
        errors = Errors()
        # severity is error to avoid needing to catch exception
        errors.report('Pyccel can currently only handle 1 file at a time',
                      severity='error')
        errors.check()
        sys.exit(1)
    # ...

    filename = files[0]

    # ... report error
    if os.path.isfile(filename):
        # we don't use is_valid_filename_py since it uses absolute path
        # file extension
        ext = filename.split('.')[-1]
        if not(ext in ['py', 'pyh']):
            errors = Errors()
            # severity is error to avoid needing to catch exception
            errors.report(INVALID_FILE_EXTENSION,
                          symbol=ext,
                          severity='error')
            errors.check()
            sys.exit(1)
    else:
        # we use Pyccel error manager, although we can do it in other ways
        errors = Errors()
        # severity is error to avoid needing to catch exception
        errors.report(INVALID_FILE_DIRECTORY,
                      symbol=filename,
                      severity='error')
        errors.check()
        sys.exit(1)
    # ...

    if compiler:
        if _which(compiler) is None:
            errors = Errors()
            # severity is error to avoid needing to catch exception
            errors.report('Could not find compiler',
                          symbol=compiler,
                          severity='error')
            errors.check()
            sys.exit(1)

    accelerator = None
    if openmp:
        accelerator = "openmp"
    if openacc:
        accelerator = "openacc"

    # ...

    # ...
    if args.developer_mode:
        # this will initialize the singelton ErrorsMode
        # making this settings available everywhere
        err_mode = ErrorsMode()
        err_mode.set_mode('developer')
    # ...

    base_dirpath = os.getcwd()

    try:
        # TODO: prune options
        execute_pyccel(filename,
                       syntax_only   = args.syntax_only,
                       semantic_only = args.semantic_only,
                       convert_only  = args.convert_only,
                       verbose       = args.verbose,
                       language      = args.language,
                       compiler      = compiler,
                       mpi_compiler  = args.mpi_compiler,
                       fflags        = args.flags,
                       includes      = args.includes,
                       libdirs       = args.libdirs,
                       modules       = (),
                       libs          = args.libs,
                       debug         = args.debug,
                       extra_args    = '',
                       accelerator   = accelerator,
                       folder        = args.output)
    except PyccelError:
        sys.exit(1)
    finally:
        os.chdir(base_dirpath)

    return