From 68086738b4a04012bdd7523cb89223cc634c6195 Mon Sep 17 00:00:00 2001 From: Joerg Henrichs Date: Thu, 21 Aug 2025 15:04:34 +1000 Subject: [PATCH 1/5] #482 Write pragma files into output dir, not source. --- source/fab/steps/c_pragma_injector.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/source/fab/steps/c_pragma_injector.py b/source/fab/steps/c_pragma_injector.py index 196d0867..7b6ea3a5 100644 --- a/source/fab/steps/c_pragma_injector.py +++ b/source/fab/steps/c_pragma_injector.py @@ -9,11 +9,13 @@ """ import re from pathlib import Path -from typing import Generator, Pattern, Optional, Match +from typing import Generator, Match, Optional, Pattern, Tuple from fab import FabException +from fab.build_config import BuildConfig from fab.artefacts import ArtefactSet, ArtefactsGetter, SuffixFilter from fab.steps import run_mp, step +from fab.util import input_to_output_fpath DEFAULT_SOURCE_GETTER = SuffixFilter(ArtefactSet.C_BUILD_FILES, '.c') @@ -21,7 +23,7 @@ # todo: test @step def c_pragma_injector(config, source: Optional[ArtefactsGetter] = None, - output_name=None): + output_name: Optional[ArtefactSet] = None) -> None: """ A build step to inject custom pragmas to mark blocks of user and system include statements. @@ -50,15 +52,25 @@ def c_pragma_injector(config, source: Optional[ArtefactsGetter] = None, output_name = output_name or ArtefactSet.PRAGMAD_C files = source_getter(config.artefact_store) - results = run_mp(config, items=files, func=_process_artefact) + args = [(config, file) for file in files] + results = run_mp(config, items=args, func=_process_artefact) config.artefact_store[output_name] = set(results) config.artefact_store.replace(ArtefactSet.C_BUILD_FILES, remove_files=files, add_files=results) -def _process_artefact(fpath: Path): - prag_output_fpath = fpath.with_suffix('.prag') +def _process_artefact(config_fpath: Tuple[BuildConfig, Path]) -> None: + ''' + Adds the pragmas to a given C file, and stores the modified file + with a ".prag" suffix in the output directory. + + :param config_fpath: a tuple of the config directory and the file + to process. + ''' + config, fpath = config_fpath + prag_output_fpath = input_to_output_fpath(config, + fpath.with_suffix('.prag')) prag_output_fpath.open('w').writelines(inject_pragmas(fpath)) return prag_output_fpath From 4aca5ddeb096ae60fce51420cab55574f84703f0 Mon Sep 17 00:00:00 2001 From: Joerg Henrichs Date: Wed, 1 Apr 2026 18:56:39 +1100 Subject: [PATCH 2/5] #482 Create directories in case they do not exist. --- source/fab/steps/c_pragma_injector.py | 1 + 1 file changed, 1 insertion(+) diff --git a/source/fab/steps/c_pragma_injector.py b/source/fab/steps/c_pragma_injector.py index 98760a2d..12b6d981 100644 --- a/source/fab/steps/c_pragma_injector.py +++ b/source/fab/steps/c_pragma_injector.py @@ -71,6 +71,7 @@ def _process_artefact(config_fpath: Tuple[BuildConfig, Path]) -> None: config, fpath = config_fpath prag_output_fpath = input_to_output_fpath(config, fpath.with_suffix('.prag')) + prag_output_fpath.parent.mkdir(parents=True, exist_ok=True) prag_output_fpath.open('w').writelines(inject_pragmas(fpath)) return prag_output_fpath From f81fe0f466675b3c8bb27915069e10d9affb9171 Mon Sep 17 00:00:00 2001 From: Joerg Henrichs Date: Wed, 1 Apr 2026 18:57:12 +1100 Subject: [PATCH 3/5] #482 Fixed coding style. --- source/fab/steps/c_pragma_injector.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/fab/steps/c_pragma_injector.py b/source/fab/steps/c_pragma_injector.py index 12b6d981..71192236 100644 --- a/source/fab/steps/c_pragma_injector.py +++ b/source/fab/steps/c_pragma_injector.py @@ -28,8 +28,8 @@ def c_pragma_injector(config, source: Optional[ArtefactsGetter] = None, A build step to inject custom pragmas to mark blocks of user and system include statements. - By default, reads .c files from the *INITIAL_SOURCE_FILES* artefact and creates - the *pragmad_c* artefact. + By default, reads .c files from the *INITIAL_SOURCE_FILES* artefact and + creates the *pragmad_c* artefact. This step does not write to the build output folder, it creates the pragmad c in the same folder as the c file. This is because a subsequent From d214a1622b61258a3d1f427fab31e15ee0035c3c Mon Sep 17 00:00:00 2001 From: Joerg Henrichs Date: Thu, 2 Apr 2026 00:21:44 +1100 Subject: [PATCH 4/5] #482 Fix failing test by using root_inc_file and adding .h as prefix. --- source/fab/cli.py | 3 ++- tests/system_tests/CFortranInterop/test_CFortranInterop.py | 2 ++ tests/system_tests/CUserHeader/test_CUserHeader.py | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/source/fab/cli.py b/source/fab/cli.py index dc78c235..e3d98fcb 100644 --- a/source/fab/cli.py +++ b/source/fab/cli.py @@ -50,7 +50,8 @@ def _generic_build_config(folder: Path, kwargs=None) -> BuildConfig: tool_box=tool_box, **kwargs) as config: grab_folder(config, folder) find_source_files(config) - root_inc_files(config) # JULES helper, get rid of this eventually + # JULES helper, get rid of this eventually + root_inc_files(config, suffix_list=[".inc", ".h"]) preprocess_fortran(config) c_pragma_injector(config) preprocess_c(config, source=CollectionGetter(ArtefactSet.C_COMPILER_FILES)) diff --git a/tests/system_tests/CFortranInterop/test_CFortranInterop.py b/tests/system_tests/CFortranInterop/test_CFortranInterop.py index 1231c374..5313462c 100644 --- a/tests/system_tests/CFortranInterop/test_CFortranInterop.py +++ b/tests/system_tests/CFortranInterop/test_CFortranInterop.py @@ -18,6 +18,7 @@ from fab.steps.grab.folder import grab_folder from fab.steps.link import link_exe from fab.steps.preprocess import preprocess_fortran, preprocess_c +from fab.steps.root_inc_files import root_inc_files from fab.tools.tool_box import ToolBox clang = importorskip('clang', reason="Clang bindings not found.") @@ -32,6 +33,7 @@ def test_CFortranInterop(tmp_path): tool_box=ToolBox(), multiprocessing=False) as config: grab_folder(config, src=PROJECT_SOURCE) find_source_files(config) + root_inc_files(config, suffix_list=[".h"]) c_pragma_injector(config) preprocess_c(config) preprocess_fortran(config) diff --git a/tests/system_tests/CUserHeader/test_CUserHeader.py b/tests/system_tests/CUserHeader/test_CUserHeader.py index 44a3d7de..03ad2baf 100644 --- a/tests/system_tests/CUserHeader/test_CUserHeader.py +++ b/tests/system_tests/CUserHeader/test_CUserHeader.py @@ -17,6 +17,7 @@ from fab.steps.grab.folder import grab_folder from fab.steps.link import link_exe from fab.steps.preprocess import preprocess_c +from fab.steps.root_inc_files import root_inc_files from fab.tools.tool_box import ToolBox clang = importorskip('clang', reason="Clang bindings not found.") @@ -32,6 +33,7 @@ def test_CUseHeader(tmp_path): grab_folder(config, PROJECT_SOURCE) find_source_files(config) + root_inc_files(config, suffix_list=[".h"]) c_pragma_injector(config) preprocess_c(config) analyse(config, root_symbol='main') From f197c6797459ce98cf8bb93e115d1e0a0accfa02 Mon Sep 17 00:00:00 2001 From: Joerg Henrichs Date: Thu, 2 Apr 2026 00:22:08 +1100 Subject: [PATCH 5/5] #482 Fixed pylint issues. --- source/fab/cli.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/source/fab/cli.py b/source/fab/cli.py index e3d98fcb..e4323a11 100644 --- a/source/fab/cli.py +++ b/source/fab/cli.py @@ -31,7 +31,8 @@ def _generic_build_config(folder: Path, kwargs=None) -> BuildConfig: project_label = 'zero_config_build' if kwargs: - project_label = kwargs.pop('project_label', 'zero_config_build') or project_label + project_label = kwargs.pop('project_label', + 'zero_config_build') or project_label # Set the default Fortran compiler as linker (otherwise e.g. the # C compiler might be used in linking, requiring additional flags) @@ -45,7 +46,8 @@ def _generic_build_config(folder: Path, kwargs=None) -> BuildConfig: tool_box.add_tool(fc) tool_box.add_tool(linker) # Within the fab workspace, we'll create a project workspace. - # Ideally we'd just use folder.name, but to avoid clashes, we'll use the full absolute path. + # Ideally we'd just use folder.name, but to avoid clashes, we'll use the + # full absolute path. with BuildConfig(project_label=project_label, mpi=False, openmp=False, tool_box=tool_box, **kwargs) as config: grab_folder(config, folder) @@ -54,7 +56,8 @@ def _generic_build_config(folder: Path, kwargs=None) -> BuildConfig: root_inc_files(config, suffix_list=[".inc", ".h"]) preprocess_fortran(config) c_pragma_injector(config) - preprocess_c(config, source=CollectionGetter(ArtefactSet.C_COMPILER_FILES)) + preprocess_c(config, + source=CollectionGetter(ArtefactSet.C_COMPILER_FILES)) analyse(config, find_programs=True) compile_fortran(config) compile_c(config) @@ -68,9 +71,9 @@ def _generic_build_config(folder: Path, kwargs=None) -> BuildConfig: def cli_fab(folder: Optional[Path] = None, kwargs: Optional[Dict] = None): """ - Running Fab from the command line will attempt to build the project in the current or - given folder. The following params are used for testing. When run normally any parameters - will be caught by a common_arg_parser. + Running Fab from the command line will attempt to build the project in + the current or given folder. The following params are used for testing. + When run normally any parameters will be caught by a common_arg_parser. :param folder: source folder (Testing Only) @@ -80,9 +83,9 @@ def cli_fab(folder: Optional[Path] = None, kwargs: Optional[Dict] = None): """ kwargs = kwargs or {} - # We check if 'fab' was called directly. As it can be called by other things like 'pytest', - # the cli arguments may not apply to 'fab' which will cause arg_parser to fail with an - # invalid argument message. + # We check if 'fab' was called directly. As it can be called by other + # things like 'pytest', the cli arguments may not apply to 'fab' which + # will cause arg_parser to fail with an invalid argument message. if Path(sys.argv[0]).parts[-1] == 'fab': arg_parser = common_arg_parser() kwargs = vars(arg_parser.parse_args())