Skip to content

Commit fae3738

Browse files
authored
Merge pull request numpy#26703 from HaoZeke/fixF2PYflags
BUG: Handle `--f77flags` and `--f90flags` for `meson`
2 parents 21a02cc + d5ce926 commit fae3738

File tree

6 files changed

+63
-6
lines changed

6 files changed

+63
-6
lines changed

numpy/f2py/_backends/_meson.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def __init__(
2828
include_dirs: list[Path],
2929
object_files: list[Path],
3030
linker_args: list[str],
31-
c_args: list[str],
31+
fortran_args: list[str],
3232
build_type: str,
3333
python_exe: str,
3434
):
@@ -46,12 +46,18 @@ def __init__(
4646
self.include_dirs = []
4747
self.substitutions = {}
4848
self.objects = object_files
49+
# Convert args to '' wrapped variant for meson
50+
self.fortran_args = [
51+
f"'{x}'" if not (x.startswith("'") and x.endswith("'")) else x
52+
for x in fortran_args
53+
]
4954
self.pipeline = [
5055
self.initialize_template,
5156
self.sources_substitution,
5257
self.deps_substitution,
5358
self.include_substitution,
5459
self.libraries_substitution,
60+
self.fortran_args_substitution,
5561
]
5662
self.build_type = build_type
5763
self.python_exe = python_exe
@@ -109,6 +115,14 @@ def include_substitution(self) -> None:
109115
[f"{self.indent}'''{inc}'''," for inc in self.include_dirs]
110116
)
111117

118+
def fortran_args_substitution(self) -> None:
119+
if self.fortran_args:
120+
self.substitutions["fortran_args"] = (
121+
f"{self.indent}fortran_args: [{', '.join([arg for arg in self.fortran_args])}],"
122+
)
123+
else:
124+
self.substitutions["fortran_args"] = ""
125+
112126
def generate_meson_build(self):
113127
for node in self.pipeline:
114128
node()
@@ -126,6 +140,7 @@ def __init__(self, *args, **kwargs):
126140
self.build_type = (
127141
"debug" if any("debug" in flag for flag in self.fc_flags) else "release"
128142
)
143+
self.fc_flags = _get_flags(self.fc_flags)
129144

130145
def _move_exec_to_root(self, build_dir: Path):
131146
walk_dir = Path(build_dir) / self.meson_build_dir
@@ -203,3 +218,17 @@ def _prepare_sources(mname, sources, bdir):
203218
if not Path(source).suffix == ".pyf"
204219
]
205220
return extended_sources
221+
222+
223+
def _get_flags(fc_flags):
224+
flag_values = []
225+
flag_pattern = re.compile(r"--f(77|90)flags=(.*)")
226+
for flag in fc_flags:
227+
match_result = flag_pattern.match(flag)
228+
if match_result:
229+
values = match_result.group(2).strip().split()
230+
values = [val.strip("'\"") for val in values]
231+
flag_values.extend(values)
232+
# Hacky way to preserve order of flags
233+
unique_flags = list(dict.fromkeys(flag_values))
234+
return unique_flags

numpy/f2py/_backends/meson.build.template

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,5 @@ ${dep_list}
5151
${lib_list}
5252
${lib_dir_list}
5353
],
54+
${fortran_args}
5455
install : true)

numpy/f2py/f2py2e.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -635,10 +635,14 @@ def run_compile():
635635
r'--((f(90)?compiler(-exec|)|compiler)=|help-compiler)')
636636
flib_flags = [_m for _m in sys.argv[1:] if _reg3.match(_m)]
637637
sys.argv = [_m for _m in sys.argv if _m not in flib_flags]
638-
_reg4 = re.compile(
639-
r'--((f(77|90)(flags|exec)|opt|arch)=|(debug|noopt|noarch|help-fcompiler))')
640-
fc_flags = [_m for _m in sys.argv[1:] if _reg4.match(_m)]
641-
sys.argv = [_m for _m in sys.argv if _m not in fc_flags]
638+
# TODO: Once distutils is dropped completely, i.e. min_ver >= 3.12, unify into --fflags
639+
reg_f77_f90_flags = re.compile(r'--f(77|90)flags=')
640+
reg_distutils_flags = re.compile(r'--((f(77|90)exec|opt|arch)=|(debug|noopt|noarch|help-fcompiler))')
641+
fc_flags = [_m for _m in sys.argv[1:] if reg_f77_f90_flags.match(_m)]
642+
distutils_flags = [_m for _m in sys.argv[1:] if reg_distutils_flags.match(_m)]
643+
if not (MESON_ONLY_VER or backend_key == 'meson'):
644+
fc_flags.extend(distutils_flags)
645+
sys.argv = [_m for _m in sys.argv if _m not in (fc_flags + distutils_flags)]
642646

643647
del_list = []
644648
for s in flib_flags:
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
C This is an invalid file, but it does compile with -ffixed-form
2+
subroutine mwe(
3+
& x)
4+
real x
5+
end subroutine mwe

numpy/f2py/tests/test_regression.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def test_gh26148b(self):
109109
assert(res[0] == 8)
110110
assert(res[1] == 15)
111111

112+
@pytest.mark.slow
112113
def test_gh26623():
113114
# Including libraries with . should not generate an incorrect meson.build
114115
try:
@@ -119,3 +120,20 @@ def test_gh26623():
119120
)
120121
except RuntimeError as rerr:
121122
assert "lparen got assign" not in str(rerr)
123+
124+
125+
@pytest.mark.slow
126+
def test_gh25784():
127+
# Compile dubious file using passed flags
128+
try:
129+
aa = util.build_module(
130+
[util.getpath("tests", "src", "regression", "f77fixedform.f95")],
131+
options=[
132+
# Meson will collect and dedup these to pass to fortran_args:
133+
"--f77flags='-ffixed-form -O2'",
134+
"--f90flags=\"-ffixed-form -Og\"",
135+
],
136+
module_name="Blah",
137+
)
138+
except ImportError as rerr:
139+
assert "unknown_subroutine_" in str(rerr)

numpy/f2py/tests/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def build_module(source_files, options=[], skip=[], only=[], module_name=None):
121121
dst_sources.append(dst)
122122

123123
base, ext = os.path.splitext(dst)
124-
if ext in (".f90", ".f", ".c", ".pyf"):
124+
if ext in (".f90", ".f95", ".f", ".c", ".pyf"):
125125
f2py_sources.append(dst)
126126

127127
assert f2py_sources

0 commit comments

Comments
 (0)