Skip to content

fix(xcode.application): bundle external dylibs in macOS apps#7453

Closed
Akaps316 wants to merge 2 commits intoxmake-io:devfrom
Akaps316:codex/fix-macapp-external-dylibs
Closed

fix(xcode.application): bundle external dylibs in macOS apps#7453
Akaps316 wants to merge 2 commits intoxmake-io:devfrom
Akaps316:codex/fix-macapp-external-dylibs

Conversation

@Akaps316
Copy link
Copy Markdown
Contributor

@Akaps316 Akaps316 commented Apr 3, 2026

Summary

Bundle non-target macOS dylibs into xcode.application app bundles in
addition to shared target and framework dependencies.

This fixes app bundles that link external or package-provided .dylib
files and otherwise end up with a binary that depends on @rpath/...
entries that are never copied into Contents/Frameworks.

Fixes #7452.

Changes

  • keep explicit framework-target bundle copying
  • reuse existing target/package shared library collection via get_target_libfiles()
  • resolve declared shared links against merged linkdirs to find non-target dylibs
  • walk the app binary dylib dependencies and copy non-system dylibs into Contents/Frameworks
  • add a regression test that builds an external dylib and verifies it is bundled into a macOS app

Why

xcode.application already performs partial app-bundle assembly by rewriting
rpath and copying shared target/framework dependencies.

However, it did not bundle non-target dylibs linked through raw
add_linkdirs() / add_links() usage, leaving the generated .app
incomplete relative to the app binary's load commands.

This patch makes that behavior consistent for macOS app bundles without
changing the separate duplicate-LC_RPATH issue, which is intentionally
left out of this change.

Testing

  • ran build/xmake l tests/run.lua macapp_external_dylib
  • result: 1 test(s) succeed
  • smoke-built existing fixtures:
    • tests/projects/objc/macapp_with_shared
    • tests/projects/objc/macapp_with_framework

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the Xcode application rule to improve the handling of dependent dynamic libraries and frameworks, ensuring they are correctly copied into the application bundle. The review feedback highlights that the accompanying test case hardcodes architecture and platform-specific paths, which limits portability. Additionally, the refactoring of the copy logic in the build rule was suggested to improve code clarity and maintainability by separating collection logic from file system operations.

Collect non-target shared library dependencies for macOS app bundles in addition to shared target and framework dependencies.

Resolve declared shared links against merged linkdirs, reuse existing target/package library collection, and then walk the app binary's dylib dependencies to copy non-system dylibs into Contents/Frameworks.

Add a regression test that builds an external dylib and verifies xcode.application bundles it into a macOS app.
@Akaps316 Akaps316 force-pushed the codex/fix-macapp-external-dylibs branch from 96b07e4 to 5714bab Compare April 3, 2026 15:34
@waruqi
Copy link
Copy Markdown
Member

waruqi commented Apr 6, 2026

This method is somewhat of a hack and doesn't guarantee that all dylibs will be reliably bundled into the bundle.

The fact that macOS also includes system dylibs under /Library/ and the Xcode built-in dylib (/Applications/Xcode.app/) system libraries is completely unreliable.

Additionally, the extra find_library will impact build time. If users want to package and generate an app, they shouldn't use add_linkdirs to manually link third-party dynamic libraries. Use add_deps and add_packages whenever possible.

At least for now, I cannot accept the current implementation.

local xmake = path.absolute(path.join(os.projectdir(), "build", "xmake"))
local xmake_program_dir = path.absolute(path.join(os.projectdir(), "xmake"))
os.setenv("XMAKE_PROGRAM_FILE", xmake)
os.setenv("XMAKE_PROGRAM_DIR", xmake_program_dir)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here

os.setenv("XMAKE_PROGRAM_DIR", xmake_program_dir)

os.execv(xmake, {"f", "-p", "macosx", "-a", arch, "-c"})
os.execv(xmake, {"-vD"})
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here

and not libfile:startswith("/System/Library/")
end

local function _get_target_linkdirs(target)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here

return table.unique(linkdirs)
end

local function _get_target_linklibfiles(target)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here

local libfiles = {}
target_utils.get_target_libfiles(target, libfiles, target:targetfile(), {})
table.join2(libfiles, _get_target_linklibfiles(target))
local dependfiles = get_depend_libraries(target:targetfile(), {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refer to the implementation details in the installation guide.

The get_target_libfiles function has already called get_depend_libraries internally. Then, _get_target_linklibfiles performs a very time-consuming find_library function, which then calls get_depend_libraries again.

local frameworks_to_copy = {}
local framework_targetfiles = {}
for _, dep in ipairs(target:orderdeps()) do
if dep:kind() == "shared" then
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The handling of dependencies on non-framework dynamic libraries is also missing here.

@Akaps316
Copy link
Copy Markdown
Contributor Author

Understood.

The current approach is too heuristic and duplicates dependency scanning,
and I agree that automatically bundling dylibs from raw add_linkdirs /
add_links is not the right direction here.

I also see that this refactor is missing the clearer handling for direct
non-framework shared target dependencies.

I’m fine dropping this version rather than pushing the implementation
further in the wrong direction.

@waruqi waruqi closed this Apr 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

xcode.application bundles shared target deps but omits external/package dylibs from macOS app bundles

2 participants