Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 10 additions & 16 deletions Documentation/source/fab_base/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -123,26 +123,20 @@ for all compilers and linkers:

.. code-block:: python

from fab.api import ProfileFlags

def update_toolbox(self, build_config: BuildConfig) -> None:

for compiler in (tr[Category.C_COMPILER] +
tr[Category.FORTRAN_COMPILER] +
tr[Category.LINKER]):
compiler.define_profile("base", inherit_from="")
for profile in self.get_valid_profiles():
compiler.define_profile(profile, inherit_from="base")
ProfileFlags.define_profile("base")
for profile in self.get_valid_profiles():
ProfileFlags.define_profile(profile, inherit_from="base")

This sets up a hierarchy where each of the valid compilation profiles
inherits from a ``base`` profile. And they are defined for all
compilers, even if they might not be available. This will make sure
that using compilation modes work in a Fab compiler wrapper, since
it is possible that the wrapped compiler is not available, i.e.
not in ``$PATH``, but the wrapper is. Additionally, using
``get_valid_profiles`` also means that any additional profiles defined
from a derived class will automatically be created. If a different
hierarchy is requested (e.g. ``memory-profile`` might want to inherit
from ``full-debug``, this needs to be updated in the inheriting
class).
inherits from a ``base`` profile. Using ``get_valid_profiles`` also means
that any additional profiles defined from a derived class will automatically
be created. If a different hierarchy is requested (e.g. ``memory-profile``
might want to inherit from ``full-debug``, this needs to be updated in the
inheriting class).

After the profiling modes, a ``default`` class should setup
all compilers (including the various flags for the different
Expand Down
2 changes: 1 addition & 1 deletion Documentation/source/fab_base/usage_patterns.rst
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ how a site can then add its own compilation profile:
'''
Determines the list of all allowed compiler profiles. Here we
add one additional profile `memory-debug`. Note that the default
setup will automatically create that mode for any available compiler.
setup will automatically create that profile mode for all tools.

:returns List[str]: list of all supported compiler profiles.
'''
Expand Down
2 changes: 2 additions & 0 deletions source/fab/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from fab.tools.linker import Linker
from fab.tools.pkg_config import PkgConfig
from fab.tools.preprocessor import Cpp, Fpp
from fab.tools.profile_flags import ProfileFlags
from fab.tools.tool import Tool
from fab.tools.tool_box import ToolBox
from fab.tools.tool_repository import ToolRepository
Expand Down Expand Up @@ -87,6 +88,7 @@
"preprocess_c",
"preprocess_fortran",
"preprocess_x90",
"ProfileFlags",
"psyclone",
"root_inc_files",
"run_mp",
Expand Down
4 changes: 3 additions & 1 deletion source/fab/fab_base/fab_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,9 @@ def handle_command_line_options(self,
# profile in the site config file.
if (self.args.profile and self.args.profile
not in self._site_config.get_valid_profiles()):
raise RuntimeError(f"Invalid profile '{self.args.profile}")
valid = self._site_config.get_valid_profiles()
raise RuntimeError(f"Invalid profile '{self.args.profile}'. "
f"Valid profiles are: \"{valid}\".")

if self.args.suite:
tr.set_default_compiler_suite(self.args.suite)
Expand Down
193 changes: 0 additions & 193 deletions source/fab/tools/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@
FlagList:
Manages a list of flags, each of which is an instance of an AbstractFlag.

ProfileFlags:
Manages a set of flags for specific profiles, including inheritance.

Each tool uses a ProfileFlags instance. At runtime, the compilation steps
will use the selected profile to get the Flags instance to use.
The function `get_flags` will resolve the list of AbstractFlags by
Expand Down Expand Up @@ -392,193 +389,3 @@ def remove_flag(self, remove_flag: str, has_parameter: bool = False):

for flags in self:
flags.remove_flag(remove_flag, has_parameter)


class ProfileFlags:
'''A list of flags that support a 'profile' to be used. If no profile is
specified, it will use "" (empty string) as 'profile'. All functions take
an optional profile parameter, so this class can also be used for tools
that do not need a profile.

:param flags: optional flags to be added to this profile.
:param profile: optional profile to use if flags are specified,
defaults to "".
'''

def __init__(self: "ProfileFlags",
flags: Optional[Union[AbstractFlags, str, list[str]]] = None,
profile: str = "") -> None:
# Stores the flags for each profile mode. The key is the (lower case)
# name of the profile mode, and it contains a list of flags.
# Initialise the dict with the default (empty) profile
self._profiles: dict[str, FlagList] = {"": FlagList()}

# This dictionary stores an optional inheritance, where one mode
# 'inherits' the flags from a different mode (recursively)
self._inherit_from: dict[str, str] = {}

if flags:
if profile != "":
self.define_profile(profile)
self.add_flags(flags, profile)

def get_flags(self,
config: Optional["BuildConfig"] = None,
file_path: Optional[Path] = None) -> list[str]:
'''
This method returns the flags used for the specified file,
i.e. it will support path-specific flags. The BuildConfig
is added as parameter to get the profile, but also to
allow flags to use templated expressions `$relative` and
`$output` (the values are taken from the config object).

:param config: the build config object. It stores the selected
compilation profile, and paths that can be used in templated
expressions.
:param file_path: path to the source file to compile.
'''
if not file_path:
# If no path, provide a dummy path
file_path = Path()
if config:
profile = config.profile
else:
profile = ""

all_flags = self[profile]

resolved_flags = []
for flags in all_flags:
resolved_flags.extend(flags.get_flags(config, file_path))

return resolved_flags

def __getitem__(self,
profile: Optional[str] = None) -> list[AbstractFlags]:
'''Returns the flags for the requested profile. If profile is not
specified, the empty profile ("") will be used. It will also take
inheritance into account, so add flags (recursively) from inherited
profiles. But this function will not resolve the flags, i.e. replace
the AbstractFlags instances with a list of strings.

:param profile: the optional profile to use.

:raises KeyError: if a profile is specified it is not defined
'''
if profile is None:
profile = ""
else:
profile = profile.lower()

# First add any flags that we inherit. This will recursively call
# this __getitem__ to resolve inheritance chains.
if profile in self._inherit_from:
inherit_from = self._inherit_from[profile]
flags = self[inherit_from][:]
else:
flags = []
# Now add the flags from this ProfileFlags. Note if no profile
# is specified, "" will be used as key, and this is always
# defined in the constructor of this object, so it will never
# raise an exception in this case
try:
flags.extend(self._profiles[profile])
except KeyError as err:
raise KeyError(f"Profile '{profile}' is not defined.") from err

return flags

def define_profile(self,
name: str,
inherit_from: Optional[str] = None):
'''Defines a new profile name, and allows to specify if this new
profile inherit settings from an existing profile. If inherit_from
is specified, the newly defined profile will inherit from an existing
profile (including the default profile "").

:param name: Name of the profile to define.
:param inherit_from: Optional name of a profile to inherit
settings from.
'''
if name in self._profiles:
raise KeyError(f"Profile '{name}' is already defined.")
self._profiles[name.lower()] = FlagList()

if inherit_from is not None:
if inherit_from not in self._profiles:
raise KeyError(f"Inherited profile '{inherit_from}' is "
f"not defined.")
self._inherit_from[name.lower()] = inherit_from.lower()

def add_flags(self,
new_flags: Union[AbstractFlags, str, list[str]],
profile: Optional[str] = None) -> None:
'''Adds the specified flags to the list of flags.

:param new_flags: A single string or list of strings which are the
flags to be added.
'''
if profile is None:
profile = ""
else:
profile = profile.lower()

if profile not in self._profiles:
raise KeyError(f"add_flags: Profile '{profile}' is not defined.")

if isinstance(new_flags, str):
new_flags = [new_flags]

self._profiles[profile].add_flags(new_flags)

def remove_flag(self,
remove_flag: str,
profile: Optional[str] = None,
has_parameter: bool = False):
'''Removes all occurrences of `remove_flag` in flags.
If `has_parameter` is defined, the next entry in flags will also be
removed, and if this object contains this flag+parameter without space
(e.g. `-J/tmp`), it will be correctly removed. Note that only the
flag itself must be specified, you cannot remove a flag only if a
specific parameter is given (i.e. `remove_flag="-J/tmp"` will not
work if this object contains `[...,"-J", "/tmp"]`).

:param remove_flag: the flag to remove
:param has_parameter: if the flag to remove takes a parameter
'''

if not profile:
profile = ""
else:
profile = profile.lower()

if profile not in self._profiles:
raise KeyError(f"remove_flag: Profile '{profile}' is not defined.")

self._profiles[profile].remove_flag(remove_flag, has_parameter)

def checksum(self,
config: Optional["BuildConfig"] = None,
file_path: Optional[Path] = None) -> int:
"""
:param config: the config object (used for templating)
:param file_path: the file path of the source file, used for
path-specific flags.

:returns: a checksum of the flags.
"""

if not file_path:
# If no path, provide a dummy path
file_path = Path()
if config:
profile = config.profile
else:
profile = ""

if profile not in self._profiles:
raise KeyError(f"checksum: Profile '{profile}' is "
f"not defined.")

resolve_flags: list[str] = self.get_flags(config, file_path)
return string_checksum(str(resolve_flags))
2 changes: 1 addition & 1 deletion source/fab/tools/linker.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from fab.build_config import BuildConfig
from fab.tools.category import Category
from fab.tools.compiler import Compiler
from fab.tools.flags import ProfileFlags
from fab.tools.profile_flags import ProfileFlags
from fab.tools.compiler_suite_tool import CompilerSuiteTool


Expand Down
Loading