From e43e3e3ff780d130a1e1402ed9eabf0aa54c5c1d Mon Sep 17 00:00:00 2001
From: Akaps316 <48785708+Akaps316@users.noreply.github.com>
Date: Fri, 3 Apr 2026 19:54:54 +0530
Subject: fix(xcode.application): use the correct framework rpath for iOS apps
Use @executable_path/Frameworks for iphoneos application bundles when
embedding dependent frameworks.
The existing @executable_path/../Frameworks rpath is correct for macOS
app layout, but incorrect for iOS where the executable lives directly in
MyApp.app and embedded frameworks live in MyApp.app/Frameworks.
Add a regression test that builds an iOS simulator app with an embedded
framework and verifies the generated LC_RPATH.
---
.../objc/iosapp_framework_rpath/src/app/Info.plist | 20 +++++++++++++++
.../objc/iosapp_framework_rpath/src/app/main.m | 13 ++++++++++
.../src/framework/Info.plist | 18 +++++++++++++
.../iosapp_framework_rpath/src/framework/test.h | 3 +++
.../iosapp_framework_rpath/src/framework/test.m | 3 +++
.../projects/objc/iosapp_framework_rpath/test.lua | 30 ++++++++++++++++++++++
.../projects/objc/iosapp_framework_rpath/xmake.lua | 13 ++++++++++
xmake/rules/xcode/application/build.lua | 4 +--
8 files changed, 102 insertions(+), 2 deletions(-)
create mode 100644 tests/projects/objc/iosapp_framework_rpath/src/app/Info.plist
create mode 100644 tests/projects/objc/iosapp_framework_rpath/src/app/main.m
create mode 100644 tests/projects/objc/iosapp_framework_rpath/src/framework/Info.plist
create mode 100644 tests/projects/objc/iosapp_framework_rpath/src/framework/test.h
create mode 100644 tests/projects/objc/iosapp_framework_rpath/src/framework/test.m
create mode 100644 tests/projects/objc/iosapp_framework_rpath/test.lua
create mode 100644 tests/projects/objc/iosapp_framework_rpath/xmake.lua
diff --git a/tests/projects/objc/iosapp_framework_rpath/src/app/Info.plist b/tests/projects/objc/iosapp_framework_rpath/src/app/Info.plist
new file mode 100644
index 000000000..64e1f01a8
--- /dev/null
+++ b/tests/projects/objc/iosapp_framework_rpath/src/app/Info.plist
@@ -0,0 +1,20 @@
+
+
+
+
+ CFBundleExecutable
+ demo
+ CFBundleIdentifier
+ io.xmake.demo.ios
+ CFBundleName
+ demo
+ CFBundlePackageType
+ APPL
+ CFBundleVersion
+ 1
+ CFBundleShortVersionString
+ 1.0
+ UIApplicationSceneManifest
+
+
+
diff --git a/tests/projects/objc/iosapp_framework_rpath/src/app/main.m b/tests/projects/objc/iosapp_framework_rpath/src/app/main.m
new file mode 100644
index 000000000..44183e7aa
--- /dev/null
+++ b/tests/projects/objc/iosapp_framework_rpath/src/app/main.m
@@ -0,0 +1,13 @@
+#import
+#import
+
+@interface AppDelegate : UIResponder
+@property (strong, nonatomic) UIWindow *window;
+@end
+
+@implementation AppDelegate
+@end
+
+int main(int argc, char *argv[]) {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])) + test_value();
+}
diff --git a/tests/projects/objc/iosapp_framework_rpath/src/framework/Info.plist b/tests/projects/objc/iosapp_framework_rpath/src/framework/Info.plist
new file mode 100644
index 000000000..0f3689457
--- /dev/null
+++ b/tests/projects/objc/iosapp_framework_rpath/src/framework/Info.plist
@@ -0,0 +1,18 @@
+
+
+
+
+ CFBundleExecutable
+ test
+ CFBundleIdentifier
+ io.xmake.test.framework
+ CFBundleName
+ test
+ CFBundlePackageType
+ FMWK
+ CFBundleVersion
+ 1
+ CFBundleShortVersionString
+ 1.0
+
+
diff --git a/tests/projects/objc/iosapp_framework_rpath/src/framework/test.h b/tests/projects/objc/iosapp_framework_rpath/src/framework/test.h
new file mode 100644
index 000000000..91434eb54
--- /dev/null
+++ b/tests/projects/objc/iosapp_framework_rpath/src/framework/test.h
@@ -0,0 +1,3 @@
+#import
+
+FOUNDATION_EXPORT int test_value(void);
diff --git a/tests/projects/objc/iosapp_framework_rpath/src/framework/test.m b/tests/projects/objc/iosapp_framework_rpath/src/framework/test.m
new file mode 100644
index 000000000..7b8c414be
--- /dev/null
+++ b/tests/projects/objc/iosapp_framework_rpath/src/framework/test.m
@@ -0,0 +1,3 @@
+int test_value(void) {
+ return 7;
+}
diff --git a/tests/projects/objc/iosapp_framework_rpath/test.lua b/tests/projects/objc/iosapp_framework_rpath/test.lua
new file mode 100644
index 000000000..41e770e68
--- /dev/null
+++ b/tests/projects/objc/iosapp_framework_rpath/test.lua
@@ -0,0 +1,30 @@
+function main(t)
+ if not is_host("macosx") then
+ return t:skip("wrong host platform")
+ end
+
+ os.execv("xmake", {"f", "-p", "iphoneos", "-a", "arm64", "--appledev=simulator", "-c"})
+ os.execv("xmake", {"-vD"})
+
+ import("utils.binary.rpath")
+ local appfile = "build/iphoneos/arm64/release/demo.app/demo"
+ local rpaths = rpath.list(appfile)
+ local found_ios = false
+ local found_macos = false
+ if rpaths then
+ for _, p in ipairs(rpaths) do
+ if p == "@executable_path/Frameworks" then
+ found_ios = true
+ end
+ if p == "@executable_path/../Frameworks" then
+ found_macos = true
+ end
+ end
+ end
+ if not found_ios then
+ raise("missing iOS framework rpath @executable_path/Frameworks")
+ end
+ if found_macos then
+ raise("found macOS-style framework rpath in iOS app")
+ end
+end
diff --git a/tests/projects/objc/iosapp_framework_rpath/xmake.lua b/tests/projects/objc/iosapp_framework_rpath/xmake.lua
new file mode 100644
index 000000000..68d0d7db1
--- /dev/null
+++ b/tests/projects/objc/iosapp_framework_rpath/xmake.lua
@@ -0,0 +1,13 @@
+add_rules("mode.release", "mode.debug")
+
+target("test")
+ add_rules("xcode.framework")
+ add_files("src/framework/test.m")
+ add_files("src/framework/Info.plist")
+ add_headerfiles("src/framework/test.h")
+
+target("demo")
+ add_rules("xcode.application")
+ add_deps("test")
+ add_files("src/app/*.m")
+ add_files("src/app/Info.plist")
diff --git a/xmake/rules/xcode/application/build.lua b/xmake/rules/xcode/application/build.lua
index 8ea734d28..7d1c836fa 100644
--- a/xmake/rules/xcode/application/build.lua
+++ b/xmake/rules/xcode/application/build.lua
@@ -49,7 +49,8 @@ function main (target, opt)
-- @see https://github.com/xmake-io/xmake/issues/2679#issuecomment-1221839215
local targetfile = path.join(binarydir, path.filename(target:targetfile()))
try { function () os.vrunv("install_name_tool", {"-delete_rpath", "@loader_path", targetfile}) end }
- os.vrunv("install_name_tool", {"-add_rpath", "@executable_path/../Frameworks", targetfile})
+ local rpath = target:is_plat("macosx") and "@executable_path/../Frameworks" or "@executable_path/Frameworks"
+ os.vrunv("install_name_tool", {"-add_rpath", rpath, targetfile})
-- copy dependent dynamic libraries and frameworks
for _, dep in ipairs(target:orderdeps()) do
@@ -109,4 +110,3 @@ function main (target, opt)
end, {dependfile = target:dependfile(bundledir), files = {bundledir, target:targetfile()}, changed = target:is_rebuilt()})
end
-
--
cgit v1.3.1
From 84061455bdc813d3072afa8a998c8d049840b748 Mon Sep 17 00:00:00 2001
From: Akaps316 <48785708+Akaps316@users.noreply.github.com>
Date: Sun, 5 Apr 2026 10:27:42 +0530
Subject: fix(test): move import to top of test.lua per review comment
Co-authored-by: Qwen-Coder
---
tests/projects/objc/iosapp_framework_rpath/test.lua | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/projects/objc/iosapp_framework_rpath/test.lua b/tests/projects/objc/iosapp_framework_rpath/test.lua
index 41e770e68..efc69dbea 100644
--- a/tests/projects/objc/iosapp_framework_rpath/test.lua
+++ b/tests/projects/objc/iosapp_framework_rpath/test.lua
@@ -1,3 +1,5 @@
+import("utils.binary.rpath")
+
function main(t)
if not is_host("macosx") then
return t:skip("wrong host platform")
@@ -5,8 +7,6 @@ function main(t)
os.execv("xmake", {"f", "-p", "iphoneos", "-a", "arm64", "--appledev=simulator", "-c"})
os.execv("xmake", {"-vD"})
-
- import("utils.binary.rpath")
local appfile = "build/iphoneos/arm64/release/demo.app/demo"
local rpaths = rpath.list(appfile)
local found_ios = false
--
cgit v1.3.1
From d0f83e55778e37afc767cf8fa382e16d6e5dc001 Mon Sep 17 00:00:00 2001
From: Akaps316 <48785708+Akaps316@users.noreply.github.com>
Date: Sun, 5 Apr 2026 10:38:00 +0530
Subject: fix(xcode.application): resolve merge conflict with upstream dev -
add rpath duplicate guard
Co-authored-by: Qwen-Coder
---
xmake/rules/xcode/application/build.lua | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/xmake/rules/xcode/application/build.lua b/xmake/rules/xcode/application/build.lua
index 7d1c836fa..d55d71a9c 100644
--- a/xmake/rules/xcode/application/build.lua
+++ b/xmake/rules/xcode/application/build.lua
@@ -23,6 +23,7 @@ import("core.base.option")
import("core.theme.theme")
import("core.project.depend")
import("private.tools.codesign")
+import("utils.binary.rpath", {alias = "rpath_utils"})
import("utils.progress")
function main (target, opt)
@@ -49,8 +50,13 @@ function main (target, opt)
-- @see https://github.com/xmake-io/xmake/issues/2679#issuecomment-1221839215
local targetfile = path.join(binarydir, path.filename(target:targetfile()))
try { function () os.vrunv("install_name_tool", {"-delete_rpath", "@loader_path", targetfile}) end }
+ -- macOS uses @executable_path/../Frameworks due to Contents/MacOS/ layout
+ -- iOS/watchOS/tvOS/visionOS use flat bundle layout where frameworks are in the same directory as the executable
local rpath = target:is_plat("macosx") and "@executable_path/../Frameworks" or "@executable_path/Frameworks"
- os.vrunv("install_name_tool", {"-add_rpath", rpath, targetfile})
+ local rpathdirs = rpath_utils.list(targetfile, {plat = target:plat(), arch = target:arch()}) or {}
+ if not table.contains(rpathdirs, rpath) then
+ os.vrunv("install_name_tool", {"-add_rpath", rpath, targetfile})
+ end
-- copy dependent dynamic libraries and frameworks
for _, dep in ipairs(target:orderdeps()) do
--
cgit v1.3.1