Skip to content

Commit c06d249

Browse files
authored
Merge pull request #94 from Point72/tkp/integ
Fix and normalize loader args
2 parents 83524cc + 1b1c06c commit c06d249

File tree

3 files changed

+97
-1
lines changed

3 files changed

+97
-1
lines changed

hatch_cpp/tests/test_platform_specific.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ def test_link_flags_include_platform_specific_link_args(self):
345345

346346
flags = platform.get_link_flags(library)
347347
assert "-shared" in flags
348-
assert "-Wl,-rpath,$ORIGIN/lib" in flags
348+
assert r"-Wl,-rpath,\$ORIGIN/lib" in flags
349349

350350
def test_darwin_platform_uses_darwin_specific_fields(self):
351351
"""Test that darwin platform uses darwin-specific fields."""

hatch_cpp/tests/test_structs.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from toml import loads
99

1010
from hatch_cpp import HatchCppBuildConfig, HatchCppBuildPlan, HatchCppLibrary, HatchCppPlatform
11+
from hatch_cpp.toolchains.common import _normalize_rpath
1112

1213

1314
class TestStructs:
@@ -168,3 +169,72 @@ def test_hatch_cpp_vcpkg_env_force_on(self):
168169
with patch.dict(environ, {"HATCH_CPP_VCPKG": "1"}):
169170
hatch_build_plan.generate()
170171
assert "vcpkg" in hatch_build_plan._active_toolchains
172+
173+
174+
class TestNormalizeRpath:
175+
def test_origin_to_loader_path_on_darwin(self):
176+
"""$ORIGIN should be translated to @loader_path on macOS."""
177+
assert _normalize_rpath("-Wl,-rpath,$ORIGIN", "darwin") == "-Wl,-rpath,@loader_path"
178+
179+
def test_loader_path_to_origin_on_linux(self):
180+
"""@loader_path should be translated to (escaped) $ORIGIN on Linux."""
181+
result = _normalize_rpath("-Wl,-rpath,@loader_path", "linux")
182+
assert result == r"-Wl,-rpath,\$ORIGIN"
183+
184+
def test_origin_escaped_on_linux(self):
185+
"""$ORIGIN should be escaped as \\$ORIGIN on Linux for shell safety."""
186+
result = _normalize_rpath("-Wl,-rpath,$ORIGIN", "linux")
187+
assert result == r"-Wl,-rpath,\$ORIGIN"
188+
189+
def test_already_escaped_origin_on_darwin(self):
190+
"""Already-escaped \\$ORIGIN should still translate to @loader_path on macOS."""
191+
assert _normalize_rpath(r"-Wl,-rpath,\$ORIGIN", "darwin") == "-Wl,-rpath,@loader_path"
192+
193+
def test_no_rpath_unchanged(self):
194+
"""Args without rpath values should pass through unchanged."""
195+
assert _normalize_rpath("-lfoo", "linux") == "-lfoo"
196+
assert _normalize_rpath("-lfoo", "darwin") == "-lfoo"
197+
198+
def test_win32_no_transform(self):
199+
"""Windows should not transform rpath values."""
200+
assert _normalize_rpath("$ORIGIN", "win32") == "$ORIGIN"
201+
assert _normalize_rpath("@loader_path", "win32") == "@loader_path"
202+
203+
def test_link_flags_rpath_translation_darwin(self):
204+
"""Full integration: extra_link_args with $ORIGIN produce @loader_path on macOS."""
205+
library = HatchCppLibrary(
206+
name="test",
207+
sources=["test.cpp"],
208+
binding="generic",
209+
extra_link_args=["-Wl,-rpath,$ORIGIN"],
210+
)
211+
platform = HatchCppPlatform(
212+
cc="clang",
213+
cxx="clang++",
214+
ld="ld",
215+
platform="darwin",
216+
toolchain="clang",
217+
disable_ccache=True,
218+
)
219+
flags = platform.get_link_flags(library)
220+
assert "@loader_path" in flags
221+
assert "$ORIGIN" not in flags
222+
223+
def test_link_flags_rpath_escaped_linux(self):
224+
"""Full integration: extra_link_args with $ORIGIN are shell-escaped on Linux."""
225+
library = HatchCppLibrary(
226+
name="test",
227+
sources=["test.cpp"],
228+
binding="generic",
229+
extra_link_args=["-Wl,-rpath,$ORIGIN"],
230+
)
231+
platform = HatchCppPlatform(
232+
cc="gcc",
233+
cxx="g++",
234+
ld="ld",
235+
platform="linux",
236+
toolchain="gcc",
237+
disable_ccache=True,
238+
)
239+
flags = platform.get_link_flags(library)
240+
assert r"\$ORIGIN" in flags

hatch_cpp/toolchains/common.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"PlatformDefaults",
2121
"HatchCppLibrary",
2222
"HatchCppPlatform",
23+
"_normalize_rpath",
2324
)
2425

2526

@@ -207,6 +208,28 @@ def get_effective_undef_macros(self, platform: Platform) -> List[str]:
207208
return macros
208209

209210

211+
def _normalize_rpath(value: str, platform: Platform) -> str:
212+
r"""Translate and escape rpath values for the target platform.
213+
214+
- On macOS (darwin): ``$ORIGIN`` is replaced with ``@loader_path``.
215+
- On Linux: ``@loader_path`` is replaced with ``$ORIGIN``, and
216+
``$ORIGIN`` is escaped as ``\$ORIGIN`` so that ``os.system()``
217+
(which invokes a shell) passes it through literally.
218+
- On Windows: no transformation is applied (Windows does not use
219+
rpath).
220+
"""
221+
if platform == "darwin":
222+
# Handle already-escaped \$ORIGIN first, then plain $ORIGIN
223+
value = value.replace(r"\$ORIGIN", "@loader_path")
224+
value = value.replace("$ORIGIN", "@loader_path")
225+
elif platform == "linux":
226+
# Translate macOS rpath to Linux equivalent
227+
value = value.replace("@loader_path", "$ORIGIN")
228+
# Escape $ORIGIN for shell safety (os.system runs through bash)
229+
value = value.replace("$ORIGIN", r"\$ORIGIN")
230+
return value
231+
232+
210233
class HatchCppPlatform(BaseModel):
211234
cc: str
212235
cxx: str
@@ -338,6 +361,9 @@ def get_link_flags(self, library: HatchCppLibrary, build_type: BuildType = "rele
338361
effective_libraries = library.get_effective_libraries(self.platform)
339362
effective_library_dirs = library.get_effective_library_dirs(self.platform)
340363

364+
# Normalize rpath values ($ORIGIN <-> @loader_path) and escape for shell
365+
effective_link_args = [_normalize_rpath(arg, self.platform) for arg in effective_link_args]
366+
341367
if self.toolchain == "gcc":
342368
flags += " -shared"
343369
flags += " " + " ".join(effective_link_args)

0 commit comments

Comments
 (0)