Skip to content

Commit

Permalink
Merge branch 'additional_compilers' into add_shell_tool
Browse files Browse the repository at this point in the history
  • Loading branch information
hiker committed Nov 19, 2024
2 parents 3826cf3 + 20fe928 commit 0370549
Show file tree
Hide file tree
Showing 73 changed files with 606 additions and 2,537 deletions.
142 changes: 131 additions & 11 deletions source/fab/tools/psyclone.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
"""

from pathlib import Path
import re
from typing import Callable, List, Optional, TYPE_CHECKING, Union
import warnings

from fab.tools.category import Category
from fab.tools.tool import Tool
Expand All @@ -24,45 +26,163 @@ class Psyclone(Tool):
'''This is the base class for `PSyclone`.
'''

def __init__(self, api: Optional[str] = None):
def __init__(self):
super().__init__("psyclone", "psyclone", Category.PSYCLONE)
self._api = api
self._version = None

def check_available(self) -> bool:
'''This function determines if PSyclone is available. Additionally,
it established the version, since command line option changes
significantly from python 2.5.0 to the next release.
'''

# First get the version (and confirm that PSyclone is installed):
try:
# Older versions of PSyclone (2.3.1 and earlier) expect a filename
# even when --version is used, and won't produce version info
# without this. So provide a dummy file (which does not need to
# exist), and check the error for details to see if PSyclone does
# not exist, or if the error is because of the non-existing file
version_output = self.run(["--version", "does_not_exist"],
capture_output=True)
except RuntimeError as err:
# If the command is not found, the error contains the following:
if "could not be executed" in str(err):
return False
# Otherwise, psyclone likely complained about the not existing
# file. Continue and try to find version information in the output:
version_output = str(err)

# Search for the version info:
exp = r"PSyclone version: (\d[\d.]+\d)"
matches = re.search(exp, version_output)
if not matches:
warnings.warn(f"Unexpected version information for PSyclone: "
f"'{version_output}'.")
# If we don't recognise the version number, something is wrong
return False

# Now convert the version info to integer. The regular expression
# match guarantees that we have integer numbers now:
version = tuple(int(x) for x in matches.groups()[0].split('.'))

if version == (2, 5, 0):
# The behaviour of PSyclone changes from 2.5.0 to the next
# release. But since head-of-trunk still reports 2.5.0, we
# need to run additional tests to see if we have the official
# 2.5.0 release, or current trunk (which already has the new
# command line options). PSyclone needs an existing file
# in order to work, so use __file__ to present this file.
# PSyclone will obviously abort since this is not a Fortran
# file, but we only need to check the error message to
# see if the domain name is incorrect (--> current trunk)
# or not (2.5.0 release)
try:
self.run(["-api", "nemo", __file__], capture_output=True)
except RuntimeError as err:
if "Unsupported PSyKAL DSL / API 'nemo' specified" in str(err):
# It is current development. Just give it a version number
# greater than 2.5.0 for now, till the official release
# is done.
version = (2, 5, 0, 1)

self._version = version
return True

def process(self,
config: "BuildConfig",
x90_file: Path,
psy_file: Path,
alg_file: Union[Path, str],
psy_file: Optional[Path] = None,
alg_file: Optional[Union[Path, str]] = None,
transformed_file: Optional[Path] = None,
transformation_script: Optional[Callable[[Path, "BuildConfig"],
Path]] = None,
additional_parameters: Optional[List[str]] = None,
kernel_roots: Optional[List[Union[str, Path]]] = None,
api: Optional[str] = None,
):
# pylint: disable=too-many-arguments
'''Run PSyclone with the specified parameters.
# pylint: disable=too-many-arguments, too-many-branches
'''Run PSyclone with the specified parameters. If PSyclone is used to
transform existing Fortran files, `api` must be None, and the output
file name is `transformed_file`. If PSyclone is using its DSL
features, api must be a valid PSyclone API, and the two output
filenames are `psy_file` and `alg_file`.
:param api: the PSyclone API.
:param x90_file: the input file for PSyclone
:param psy_file: the output PSy-layer file.
:param alg_file: the output modified algorithm file.
:param transformed_file: the output filename if PSyclone is called
as transformation tool.
:param transformation_script: an optional transformation script
:param additional_parameters: optional additional parameters
for PSyclone
:param kernel_roots: optional directories with kernels.
'''

if not self.is_available:
raise RuntimeError("PSyclone is not available.")

# Convert the old style API nemo to be empty
if api and api.lower() == "nemo":
api = ""

if api:
# API specified, we need both psy- and alg-file, but not
# transformed file.
if not psy_file:
raise RuntimeError(f"PSyclone called with api '{api}', but "
f"no psy_file is specified.")
if not alg_file:
raise RuntimeError(f"PSyclone called with api '{api}', but "
f"no alg_file is specified.")
if transformed_file:
raise RuntimeError(f"PSyclone called with api '{api}' and "
f"transformed_file.")
else:
if psy_file:
raise RuntimeError("PSyclone called without api, but "
"psy_file is specified.")
if alg_file:
raise RuntimeError("PSyclone called without api, but "
"alg_file is specified.")
if not transformed_file:
raise RuntimeError("PSyclone called without api, but "
"transformed_file is not specified.")

parameters: List[Union[str, Path]] = []
# If an api is defined in this call (or in the constructor) add it
# as parameter. No API is required if PSyclone works as
# transformation tool only, so calling PSyclone without api is
# actually valid.
if api:
parameters.extend(["-api", api])
elif self._api:
parameters.extend(["-api", self._api])

parameters.extend(["-l", "all", "-opsy", psy_file, "-oalg", alg_file])
if self._version > (2, 5, 0):
api_param = "--psykal-dsl"
# Mapping from old names to new names:
mapping = {"dynamo0.3": "lfric",
"gocean1.0": "gocean"}
else:
api_param = "-api"
# Mapping from new names to old names:
mapping = {"lfric": "dynamo0.3",
"gocean": "gocean1.0"}
# Make mypy happy - we tested above that these variables
# are defined
assert psy_file
assert alg_file
parameters.extend([api_param, mapping.get(api, api),
"-opsy", psy_file, "-oalg", alg_file])
else: # no api
# Make mypy happy - we tested above that transformed_file is
# specified when no api is specified.
assert transformed_file
if self._version > (2, 5, 0):
# New version: no API, parameter, but -o for output name:
parameters.extend(["-o", transformed_file])
else:
# 2.5.0 or earlier: needs api nemo, output name is -oalg
parameters.extend(["-api", "nemo", "-opsy", transformed_file])
parameters.extend(["-l", "all"])

if transformation_script:
transformation_script_return_path = \
Expand Down
62 changes: 35 additions & 27 deletions source/fab/tools/versioning.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,40 @@
# For further details please refer to the file COPYRIGHT
# which you should have received as part of this distribution
##############################################################################

"""This file contains the base class for versioning tools like git and
subversion. It also contains derived classes Git, Subversion, and Fcm.
"""

Versioning tools such as Subversion and Git.
"""
from abc import ABC
from pathlib import Path
from typing import Dict, List, Optional, Union

from fab.tools.category import Category
from fab.tools.tool import Tool


class Versioning(Tool):
'''This is the base class for versioning tools like git and svn.
:param name: the name of the tool.
:param exec_name: the name of the executable of this tool.
:param category: the category to which this tool belongs).
'''

class Versioning(Tool, ABC):
"""
Base class for versioning tools like Git and Subversion.
"""
def __init__(self, name: str,
exec_name: Union[str, Path],
category: Category):
"""
Constructor.
:param name: Display name of this tool.
:param exec_name: Executable for this tool.
:param category: Tool belongs to this category.
"""
super().__init__(name, exec_name, category,
availability_option="help")


# =============================================================================
class Git(Versioning):
'''This is the base class for git.
'''
"""
Interface to Git version control system.
"""

def __init__(self):
super().__init__("git", "git",
Expand Down Expand Up @@ -111,20 +114,23 @@ def merge(self, dst: Union[str, Path],

# =============================================================================
class Subversion(Versioning):
'''This is the base class for subversion. Note that this is also the
base class for FCM, so it allows overwriting name, exec_name and
category, but will default to use svn.
:param name: name of the tool, defaults to subversion.
:param exec_name: name of the executable, defaults to "svn".
:param category: the category, FCM or SUBVERSION (the latter is
the default)
'''

"""
Interface to the Subversion version control system.
"""
def __init__(self, name: Optional[str] = None,
exec_name: Optional[Union[str, Path]] = None,
category: Category = Category.SUBVERSION):
name = name or "subversion"
"""
Constructor.
This is class is extended by the FCM interface which is why name and
executable are mutable.
:param name: Tool name, defaults to "subversion."
:param exec_name: Tool executable, defaults to "svn."
:param category: Tool category, defaults to SUBVERSION.
"""
name = name or "Subversion"
exec_name = exec_name or "svn"
super().__init__(name, exec_name, category=category)

Expand Down Expand Up @@ -166,7 +172,9 @@ def export(self, src: Union[str, Path],
:param dst: destination path.
:param revision: revision to export.
'''
self.execute(['export', '--force'], revision, [str(src), str(dst)])
self.execute(['export', '--force'],
revision,
[str(src), str(dst)])

def checkout(self, src: Union[str, Path],
dst: Union[str, Path],
Expand Down Expand Up @@ -214,4 +222,4 @@ class Fcm(Subversion):
'''

def __init__(self):
super().__init__("fcm", "fcm", Category.FCM)
super().__init__("FCM", "fcm", Category.FCM)
33 changes: 0 additions & 33 deletions tests-old/TestCases/CompiletimeDependency/Makefile

This file was deleted.

6 changes: 0 additions & 6 deletions tests-old/TestCases/CompiletimeDependency/bisto.h

This file was deleted.

20 changes: 0 additions & 20 deletions tests-old/TestCases/CompiletimeDependency/hillfort.F90

This file was deleted.

17 changes: 0 additions & 17 deletions tests-old/TestCases/CompiletimeDependency/looper.c

This file was deleted.

6 changes: 0 additions & 6 deletions tests-old/TestCases/CompiletimeDependency/oxo.h

This file was deleted.

Loading

0 comments on commit 0370549

Please sign in to comment.