-
-
Notifications
You must be signed in to change notification settings - Fork 923
fix(xcode.application): bundle external dylibs in macOS apps #7453
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| int extfoo_value(void) { | ||
| return 7; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
| <plist version="1.0"> | ||
| <dict> | ||
| <key>CFBundleExecutable</key> | ||
| <string>demo</string> | ||
| <key>CFBundleIdentifier</key> | ||
| <string>io.xmake.demo.external</string> | ||
| <key>CFBundleName</key> | ||
| <string>demo</string> | ||
| <key>CFBundlePackageType</key> | ||
| <string>APPL</string> | ||
| <key>CFBundleVersion</key> | ||
| <string>1</string> | ||
| <key>CFBundleShortVersionString</key> | ||
| <string>1.0</string> | ||
| </dict> | ||
| </plist> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| #import <AppKit/AppKit.h> | ||
|
|
||
| int extfoo_value(void); | ||
|
|
||
| int main(void) { | ||
| return extfoo_value() == 7 ? 0 : 1; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| function _build_external_dylib() | ||
| local arch = os.arch() | ||
| local sdkdir = os.iorunv("xcrun", {"--sdk", "macosx", "--show-sdk-path"}):trim() | ||
| os.execv("xcrun", { | ||
| "--sdk", "macosx", "clang", | ||
| "-dynamiclib", | ||
| "-target", arch .. "-apple-macos", | ||
| "-isysroot", sdkdir, | ||
| "-install_name", "@rpath/libextfoo.dylib", | ||
| "-o", "ext/libextfoo.dylib", | ||
| "ext/extfoo.c" | ||
| }) | ||
| end | ||
|
|
||
| function main(t) | ||
| if not is_host("macosx") then | ||
| return t:skip("wrong host platform") | ||
| end | ||
|
|
||
| local homedir = path.absolute("home") | ||
| os.setenv("HOME", homedir) | ||
| os.mkdir(homedir) | ||
| os.mkdir(path.join(homedir, ".xmake")) | ||
|
|
||
| _build_external_dylib() | ||
| local arch = os.arch() | ||
|
|
||
| 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) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here |
||
|
|
||
| os.execv(xmake, {"f", "-p", "macosx", "-a", arch, "-c"}) | ||
| os.execv(xmake, {"-vD"}) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here |
||
|
|
||
| local appdir = path.join("build", "macosx", arch, "release", "demo.app", "Contents", "Frameworks") | ||
| local dylibfile = path.join(appdir, "libextfoo.dylib") | ||
| if not os.isfile(dylibfile) then | ||
| raise("missing external dylib in macOS app bundle: %s", dylibfile) | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| add_rules("mode.release", "mode.debug") | ||
|
|
||
| set_languages("c11", "objc") | ||
|
|
||
| target("demo") | ||
| add_rules("xcode.application") | ||
| add_linkdirs("ext") | ||
| add_links("extfoo") | ||
| add_files("src/main.m") | ||
| add_files("src/Info.plist") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,8 +22,42 @@ | |
| import("core.base.option") | ||
| import("core.theme.theme") | ||
| import("core.project.depend") | ||
| import("lib.detect.find_library") | ||
| import("private.tools.codesign") | ||
| import("private.utils.target", {alias = "target_utils"}) | ||
| import("utils.binary.deplibs", {alias = "get_depend_libraries"}) | ||
| import("utils.progress") | ||
|
|
||
| function _is_non_system_dylib(libfile) | ||
| return libfile and libfile:endswith(".dylib") | ||
| and not libfile:startswith("/usr/lib/") | ||
| and not libfile:startswith("/System/Library/") | ||
| end | ||
|
|
||
| local function _get_target_linkdirs(target) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here |
||
| local linkdirs = {} | ||
| for _, values in ipairs(table.wrap(target:get_from("linkdirs", "*"))) do | ||
| for _, linkdir in ipairs(table.wrap(values)) do | ||
| table.insert(linkdirs, path.absolute(linkdir)) | ||
| end | ||
| end | ||
| return table.unique(linkdirs) | ||
| end | ||
|
|
||
| local function _get_target_linklibfiles(target) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here |
||
| local linkdirs = _get_target_linkdirs(target) | ||
| local libfiles = {} | ||
| for _, values in ipairs(table.wrap(target:get_from("links", "*"))) do | ||
| for _, link in ipairs(table.wrap(values)) do | ||
| local libinfo = find_library(link, linkdirs, {plat = target:plat(), kind = "shared"}) | ||
| if libinfo then | ||
| table.insert(libfiles, path.join(libinfo.linkdir, libinfo.filename)) | ||
| end | ||
| end | ||
| end | ||
| return table.unique(libfiles) | ||
| end | ||
|
|
||
| function main (target, opt) | ||
|
|
||
| -- get app and resources directory | ||
|
|
@@ -51,18 +85,46 @@ function main (target, opt) | |
| try { function () os.vrunv("install_name_tool", {"-delete_rpath", "@loader_path", targetfile}) end } | ||
| os.vrunv("install_name_tool", {"-add_rpath", "@executable_path/../Frameworks", targetfile}) | ||
|
|
||
| -- copy dependent dynamic libraries and frameworks | ||
| -- copy dependent frameworks and dynamic libraries | ||
| local frameworks_to_copy = {} | ||
| local framework_targetfiles = {} | ||
| for _, dep in ipairs(target:orderdeps()) do | ||
| if dep:kind() == "shared" then | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
| if not os.isdir(frameworksdir) then | ||
| os.mkdir(frameworksdir) | ||
| end | ||
| local frameworkdir = dep:data("xcode.bundle.rootdir") | ||
| if dep:rule("xcode.framework") and frameworkdir then | ||
| os.cp(frameworkdir, frameworksdir, {symlink = true}) | ||
| else | ||
| os.vcp(dep:targetfile(), frameworksdir) | ||
| end | ||
| local frameworkdir = dep:data("xcode.bundle.rootdir") | ||
| if dep:rule("xcode.framework") and frameworkdir then | ||
| table.insert(frameworks_to_copy, frameworkdir) | ||
| framework_targetfiles[path.absolute(dep:targetfile())] = true | ||
| end | ||
| end | ||
| 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(), { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please refer to the implementation details in the installation guide. The |
||
| plat = target:plat(), | ||
| arch = target:arch(), | ||
| recursive = true, | ||
| resolve_path = true, | ||
| resolve_hint_paths = libfiles | ||
| }) | ||
| for _, dependfile in ipairs(table.wrap(dependfiles)) do | ||
| if _is_non_system_dylib(dependfile) then | ||
| table.insert(libfiles, dependfile) | ||
| end | ||
| end | ||
| local dylibs_to_copy = {} | ||
| for _, libfile in ipairs(table.unique(libfiles)) do | ||
| if not framework_targetfiles[path.absolute(libfile)] then | ||
| table.insert(dylibs_to_copy, libfile) | ||
| end | ||
| end | ||
| if #frameworks_to_copy > 0 or #dylibs_to_copy > 0 then | ||
| if not os.isdir(frameworksdir) then | ||
| os.mkdir(frameworksdir) | ||
| end | ||
| for _, frameworkdir in ipairs(frameworks_to_copy) do | ||
| os.cp(frameworkdir, frameworksdir, {symlink = true}) | ||
| end | ||
| for _, libfile in ipairs(dylibs_to_copy) do | ||
| os.vcp(libfile, frameworksdir) | ||
| end | ||
| end | ||
Akaps316 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
@@ -109,4 +171,3 @@ function main (target, opt) | |
|
|
||
| end, {dependfile = target:dependfile(bundledir), files = {bundledir, target:targetfile()}, changed = target:is_rebuilt()}) | ||
| end | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.