Skip to content

fix: statically link intel runtime for executables - #357

Open
wpbonelli wants to merge 6 commits into
modflowpy:developfrom
wpbonelli:static-intel
Open

wpbonelli wants to merge 6 commits into
modflowpy:developfrom
wpbonelli:static-intel

Conversation

@wpbonelli

@wpbonelli wpbonelli commented Sep 11, 2026

Copy link
Copy Markdown
Member

Switching to meson (#309, #322) caused some programs to be linked to the intel runtime dynamically instead of statically on linux. Meson appends -lifcore -limf whenever link_language is set, and pymake set link_language for every target.

  • Only set link_language for targets that mix languages
  • For the one case that still needs link_language (a mixed-language executable), force -lifcore/-limf static via -Wl,-Bstatic/-Bdynamic bracketing so it wins over meson's redundant dynamic copies

Separately, -static-intel was only ever requested for shared objects, never executables, and pymake stripped it out of an exe's flags. I think we got away with this because ifort links statically by default. Request static linking explicitly instead.

wpbonelli and others added 2 commits September 11, 2026 16:07
…intel actually take effect under meson

Pre-meson pymake invoked ifort directly, so a gap in the flags it
computed didn't matter: -static-intel was only ever requested for
shared objects, but classic ifort's own default is to link its
runtime statically anyway, so built executables ended up static by
accident of that default.

Building through a generated meson.build instead (modflowpy#309, modflowpy#322) broke
that accident: meson's IntelFortranCompiler unconditionally appends
its own dynamic -lifcore/-limf whenever link_language is set on a
target, and pymake's generator set link_language for every target
regardless of whether it was needed. That's what silently flipped
built executables (e.g. mp7) from static to dynamic-linked against
the intel runtime between releases.

Fix has two parts:
- link_language is now only set for targets that actually mix
  fortran and c sources, so single-language targets no longer
  trigger meson's dynamic injection at all.
- -static-intel is now requested for executables too, not just
  shared objects, and on linux a mixed-language executable
  additionally forces -lifcore/-limf static via
  -Wl,-Bstatic/-Bdynamic bracketing, since it can't avoid
  link_language and needs to win against meson's redundant dynamic
  copies.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xmzz7sESmbJJfG5mHxYaaJ
@wpbonelli wpbonelli added the bug label Sep 11, 2026
@codecov

codecov Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.14286% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.244%. Comparing base (a10ae3f) to head (0af326a).

Files with missing lines Patch % Lines
pymake/utils/_meson_build.py 50.000% 5 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##           develop      #357       +/-   ##
=============================================
+ Coverage   80.879%   81.244%   +0.364%     
=============================================
  Files           20        20               
  Lines         3274      3295       +21     
=============================================
+ Hits          2648      2677       +29     
+ Misses         626       618        -8     
Files with missing lines Coverage Δ
pymake/pymake.py 75.146% <ø> (ø)
pymake/utils/_compiler_switches.py 76.458% <100.000%> (+3.111%) ⬆️
pymake/utils/_meson_build.py 89.898% <50.000%> (-0.413%) ⬇️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

test_link_language_omitted_for_single_language_target and
test_link_language_kept_for_mixed_language_target passed a bare
"hello" as target, so _create_main_meson_build's
os.path.relpath(Path(target).parent, mesondir) resolved the target's
parent against the process cwd instead of tmp_path. That's harmless
where cwd and tmp_path share a drive, but on the windows-2022
intel-classic runner the checkout is on D: and pytest's tmp_path is
on C:, and os.path.relpath can't span drives on Windows, so both
tests failed with ValueError: path is on mount 'D:', start on mount
'C:'.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012xxs2DavENhMEb2mW56hzJ
@wpbonelli
wpbonelli marked this pull request as ready for review September 12, 2026 00:24
wpbonelli and others added 3 commits September 14, 2026 08:55
_get_linker_flags is shared by the meson and classic Makefile
generators, but the Bstatic/Bdynamic workaround it grew for mixed
Fortran+C ifort/icc targets exists only to counteract meson's
link_language override -- it was leaking into generated classic
Makefiles, which have no such override to counteract. Gate it behind
an explicit meson=True passed only from _meson_build.py.

Also document (and test) the workaround's known remaining gaps: it
can't be reused for a mixed-language shared object (libifcore.a isn't
-fPIC) or on darwin (-Bstatic/-Bdynamic is GNU-ld syntax), so those
cases still link the intel runtime dynamically under meson despite
-static-intel. Emit a verbose warning in those cases instead of
failing silently.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013YTnxXqYPj3TytxiDb1U56

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant