diff options
| author | ruki <[email protected]> | 2024-07-23 12:50:50 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-07-23 12:50:50 +0800 |
| commit | d4364efee8f1906288ac5c650ae7bd101066240a (patch) | |
| tree | 72baca766710b28ca154f4b0f0983d7e9d8abc00 /xmake/modules/utils/binary | |
| parent | 45852e6ffcd3f043d961574e72f40d287a2898ab (diff) | |
| parent | 56ca5adad669d45f400198b0c99a1d20f546970c (diff) | |
Merge pull request #5335 from xmake-io/install
Improve installation
Diffstat (limited to 'xmake/modules/utils/binary')
| -rw-r--r-- | xmake/modules/utils/binary/deplibs.lua | 226 | ||||
| -rw-r--r-- | xmake/modules/utils/binary/rpath.lua | 277 |
2 files changed, 503 insertions, 0 deletions
diff --git a/xmake/modules/utils/binary/deplibs.lua b/xmake/modules/utils/binary/deplibs.lua new file mode 100644 index 000000000..146e79504 --- /dev/null +++ b/xmake/modules/utils/binary/deplibs.lua @@ -0,0 +1,226 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015-present, TBOOX Open Source Group. +-- +-- @author ruki +-- @file deplibs.lua +-- + +-- imports +import("core.base.option") +import("core.tool.toolchain") +import("lib.detect.find_tool") + +function _get_all_depends_by_dumpbin(binaryfile, opt) + local depends + local plat = opt.plat or os.host() + local arch = opt.arch or os.arch() + local cachekey = "utils.binary.deplibs" + local msvc = toolchain.load("msvc", {plat = plat, arch = arch}) + if msvc:check() then + local dumpbin = find_tool("dumpbin", {cachekey = cachekey, envs = msvc:runenvs()}) + if dumpbin then + local binarydir = path.directory(binaryfile) + local result = try { function () return os.iorunv(dumpbin.program, {"/dependents", "/nologo", binaryfile}) end } + if result then + for _, line in ipairs(result:split("\n")) do + line = line:trim() + if line:endswith(".dll") then + depends = depends or {} + table.insert(depends, line) + end + end + end + end + end + return depends +end + +function _get_all_depends_by_objdump(binaryfile, opt) + local depends + local plat = opt.plat or os.host() + local arch = opt.arch or os.arch() + local cachekey = "utils.binary.deplibs" + local objdump = find_tool("llvm-objdump", {cachekey = cachekey}) or find_tool("objdump", {cachekey = cachekey}) + if objdump then + local binarydir = path.directory(binaryfile) + local argv = {"-p", binaryfile} + if plat == "macosx" or plat == "iphoneos" or plat == "appletvos" or plat == "watchos" then + argv = {"--macho", "--dylibs-used", binaryfile} + end + local result = try { function () return os.iorunv(objdump.program, argv) end } + if result then + for _, line in ipairs(result:split("\n")) do + line = line:trim() + if plat == "windows" or plat == "mingw" then + if line:startswith("DLL Name:") then + local filename = line:split(":")[2]:trim() + if filename:endswith(".dll") then + depends = depends or {} + table.insert(depends, filename) + end + end + elseif plat == "macosx" or plat == "iphoneos" or plat == "appletvos" or plat == "watchos" then + local filename = line:match(".-%.dylib") or line:match(".-%.framework") + if filename then + depends = depends or {} + table.insert(depends, filename) + end + else + if line:startswith("NEEDED") then + local filename = line:split("%s+")[2] + if filename and filename:endswith(".so") then + depends = depends or {} + table.insert(depends, filename) + end + end + end + end + end + end + return depends +end + +-- $ldd ./build/linux/x86_64/release/test +-- linux-vdso.so.1 (0x00007ffc51fdd000) +-- libfoo.so => /mnt/xmake/tests/projects/c/shared_library/./build/linux/x86_64/release/libfoo.so (0x00007fe241233000) +-- libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fe240fca000) +-- libm.so.6 => /lib64/libm.so.6 (0x00007fe240ee7000) +-- libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fe240eba000) +-- libc.so.6 => /lib64/libc.so.6 (0x00007fe240ccd000) +-- /lib64/ld-linux-x86-64.so.2 (0x00007fe24123a000) +-- +function _get_all_depends_by_ldd(binaryfile, opt) + local plat = opt.plat or os.host() + local arch = opt.arch or os.arch() + if plat ~= "linux" and plat ~= "bsd" then + return + end + local depends + local cachekey = "utils.binary.deplibs" + local ldd = find_tool("ldd", {cachekey = cachekey}) + if ldd then + local binarydir = path.directory(binaryfile) + local result = try { function () return os.iorunv(ldd.program, {binaryfile}) end } + if result then + for _, line in ipairs(result:split("\n")) do + local splitinfo = line:split("=>") + line = splitinfo[2] + if not line or line:find("not found", 1, true) then + line = splitinfo[1] + end + line = line:gsub("%(.+%)", ""):trim() + local filename = line:match(".-%.so$") or line:match(".-%.so%.%d+") + if filename then + depends = depends or {} + table.insert(depends, filename:trim()) + end + end + end + end + return depends +end + +-- $ readelf -d build/linux/x86_64/release/test +-- +-- Dynamic section at offset 0x2db8 contains 29 entries: +-- Tag Type Name/Value +-- 0x0000000000000001 (NEEDED) Shared library: [libfoo.so] +-- 0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6] +-- 0x0000000000000001 (NEEDED) Shared library: [libm.so.6] +-- 0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1] +-- 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] +-- 0x000000000000001d (RUNPATH) Library runpath: [$ORIGIN] +function _get_all_depends_by_readelf(binaryfile, opt) + local plat = opt.plat or os.host() + local arch = opt.arch or os.arch() + if plat ~= "linux" and plat ~= "bsd" and plat ~= "android" and plat ~= "cross" then + return + end + local depends + local cachekey = "utils.binary.deplibs" + local readelf = find_tool("readelf", {cachekey = cachekey}) + if readelf then + local binarydir = path.directory(binaryfile) + local result = try { function () return os.iorunv(readelf.program, {"-d", binaryfile}) end } + if result then + for _, line in ipairs(result:split("\n")) do + if line:find("NEEDED", 1, true) then + local filename = line:match("Shared library: %[(.-)%]") + if filename then + depends = depends or {} + table.insert(depends, filename:trim()) + end + end + end + end + end + return depends +end + +-- $ otool -L build/iphoneos/arm64/release/test +-- build/iphoneos/arm64/release/test: +-- @rpath/libfoo.dylib (compatibility version 0.0.0, current version 0.0.0) +-- /System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 2048.1.101) +-- /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0) +-- /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1600.151.0) +-- /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1336.0.0) +-- +function _get_all_depends_by_otool(binaryfile, opt) + local plat = opt.plat or os.host() + local arch = opt.arch or os.arch() + if plat ~= "macosx" and plat ~= "iphoneos" and plat ~= "appletvos" and plat ~= "watchos" then + return + end + local depends + local cachekey = "utils.binary.deplibs" + local otool = find_tool("otool", {cachekey = cachekey}) + if otool then + local binarydir = path.directory(binaryfile) + local result = try { function () return os.iorunv(otool.program, {"-L", binaryfile}) end } + if result then + for _, line in ipairs(result:split("\n")) do + local filename = line:match(".-%.dylib") or line:match(".-%.framework") + if filename then + depends = depends or {} + table.insert(depends, filename:trim()) + end + end + end + end + return depends +end + +function main(binaryfile, opt) + opt = opt or {} + local ops = { + _get_all_depends_by_objdump, + _get_all_depends_by_readelf + } + if is_host("windows") then + table.insert(ops, 2, _get_all_depends_by_dumpbin) + elseif is_host("linux", "bsd") then + table.insert(ops, 1, _get_all_depends_by_ldd) + elseif is_host("macosx") then + table.insert(ops, 1, _get_all_depends_by_otool) + end + for _, op in ipairs(ops) do + local depends = op(binaryfile, opt) + if depends then + return depends + end + end +end + diff --git a/xmake/modules/utils/binary/rpath.lua b/xmake/modules/utils/binary/rpath.lua new file mode 100644 index 000000000..674a9edf0 --- /dev/null +++ b/xmake/modules/utils/binary/rpath.lua @@ -0,0 +1,277 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015-present, TBOOX Open Source Group. +-- +-- @author ruki +-- @file rpath.lua +-- + +-- imports +import("core.base.option") +import("lib.detect.find_tool") + +function _replace_rpath_vars(rpath, opt) + local plat = opt.plat or os.host() + local arch = opt.arch or os.arch() + if plat == "macosx" or plat == "iphoneos" or plat == "appletvos" or plat == "watchos" then + rpath = rpath:gsub("%$ORIGIN", "@loader_path") + else + rpath = rpath:gsub("@loader_path", "$ORIGIN") + rpath = rpath:gsub("@executable_path", "$ORIGIN") + end + return rpath +end + +function _get_rpath_list_by_objdump(binaryfile, opt) + local list + local plat = opt.plat or os.host() + local arch = opt.arch or os.arch() + local cachekey = "utils.binary.rpath" + local objdump = find_tool("llvm-objdump", {cachekey = cachekey}) or find_tool("objdump", {cachekey = cachekey}) + if objdump then + local binarydir = path.directory(binaryfile) + local argv = {"-x", binaryfile} + if plat == "macosx" or plat == "iphoneos" or plat == "appletvos" or plat == "watchos" then + argv = {"--macho", "-x", binaryfile} + end + local result = try { function () return os.iorunv(objdump.program, argv) end } + if result then + local cmd = false + for _, line in ipairs(result:split("\n")) do + line = line:trim() + if plat == "macosx" or plat == "iphoneos" or plat == "appletvos" or plat == "watchos" then + if not cmd and line:find("cmd LC_RPATH", 1, true) then + cmd = true + elseif cmd and (line:find("cmd ", 1, true) or line:find("Load command", 1, true)) then + cmd = false + end + if cmd then + local p = line:match("path (.-) %(") + if p then + list = list or {} + table.insert(list, p:trim()) + end + end + else + if line:startswith("RUNPATH") or line:startswith("RPATH") then + local p = line:split("%s+")[2] + if p then + list = list or {} + table.insert(list, p:trim()) + end + end + end + end + end + end + return list +end + +-- $ readelf -d build/linux/x86_64/release/test +-- +-- Dynamic section at offset 0x2db8 contains 29 entries: +-- Tag Type Name/Value +-- 0x0000000000000001 (NEEDED) Shared library: [libfoo.so] +-- 0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6] +-- 0x0000000000000001 (NEEDED) Shared library: [libm.so.6] +-- 0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1] +-- 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] +-- 0x000000000000001d (RUNPATH) Library runpath: [$ORIGIN] +-- ... +-- 0x000000000000000f (RPATH) Library rpath: [$ORIGIN] +function _get_rpath_list_by_readelf(binaryfile, opt) + local plat = opt.plat or os.host() + local arch = opt.arch or os.arch() + if plat ~= "linux" and plat ~= "bsd" and plat ~= "android" and plat ~= "cross" then + return + end + local list + local cachekey = "utils.binary.rpath" + local readelf = find_tool("readelf", {cachekey = cachekey}) + if readelf then + local binarydir = path.directory(binaryfile) + local result = try { function () return os.iorunv(readelf.program, {"-d", binaryfile}) end } + if result then + for _, line in ipairs(result:split("\n")) do + if line:find("RUNPATH", 1, true) then + local p = line:match("Library runpath: %[(.-)%]") + if p then + list = list or {} + table.insert(list, p:trim()) + end + elseif line:find("RPATH", 1, true) then + local p = line:match("Library rpath: %[(.-)%]") + if p then + list = list or {} + table.insert(list, p:trim()) + end + end + end + end + end + return list +end + +-- $ otool -l build/iphoneos/arm64/release/test +-- build/iphoneos/arm64/release/test: +-- cmd LC_RPATH +-- cmdsize 32 +-- path @loader_path (offset 12) +-- +function _get_rpath_list_by_otool(binaryfile, opt) + local plat = opt.plat or os.host() + local arch = opt.arch or os.arch() + if plat ~= "macosx" and plat ~= "iphoneos" and plat ~= "appletvos" and plat ~= "watchos" then + return + end + local list + local cachekey = "utils.binary.rpath" + local otool = find_tool("otool", {cachekey = cachekey}) + if otool then + local binarydir = path.directory(binaryfile) + local result = try { function () return os.iorunv(otool.program, {"-l", binaryfile}) end } + if result then + local cmd = false + for _, line in ipairs(result:split("\n")) do + if not cmd and line:find("cmd LC_RPATH", 1, true) then + cmd = true + elseif cmd and (line:find("cmd ", 1, true) or line:find("Load command", 1, true)) then + cmd = false + end + if cmd then + local p = line:match("path (.-) %(") + if p then + list = list or {} + table.insert(list, p:trim()) + end + end + end + end + end + return list +end + +-- install_name_tool -add_rpath <rpath> binaryfile +function _insert_rpath_by_install_name_tool(binaryfile, rpath, opt) + local plat = opt.plat or os.host() + local arch = opt.arch or os.arch() + if plat ~= "macosx" and plat ~= "iphoneos" and plat ~= "appletvos" and plat ~= "watchos" then + return false + end + local ok = try { function () + os.vrunv("install_name_tool", {"-add_rpath", rpath, binaryfile}) + return true + end } + return ok +end + +-- install_name_tool -rpath <rpath_old> <rpath_new> binaryfile +function _change_rpath_by_install_name_tool(binaryfile, rpath_old, rpath_new, opt) + local plat = opt.plat or os.host() + local arch = opt.arch or os.arch() + if plat ~= "macosx" and plat ~= "iphoneos" and plat ~= "appletvos" and plat ~= "watchos" then + return false + end + local ok = try { function () + os.vrunv("install_name_tool", {"-rpath", rpath_old, rpath_new, binaryfile}) + return true + end } + return ok +end + +-- install_name_tool -delete_rpath <rpath> binaryfile +function _remove_rpath_by_install_name_tool(binaryfile, rpath, opt) + local plat = opt.plat or os.host() + local arch = opt.arch or os.arch() + if plat ~= "macosx" and plat ~= "iphoneos" and plat ~= "appletvos" and plat ~= "watchos" then + return false + end + local ok = try { function () + os.vrunv("install_name_tool", {"-delete_rpath", rpath, binaryfile}) + return true + end } + return ok +end + +-- get rpath list +function list(binaryfile, opt) + opt = opt or {} + local ops = { + _get_rpath_list_by_objdump, + _get_rpath_list_by_readelf + } + if is_host("macosx") then + table.insert(ops, 1, _get_rpath_list_by_otool) + end + for _, op in ipairs(ops) do + local result = op(binaryfile, opt) + if result then + return result + end + end +end + +-- insert rpath +function insert(binaryfile, rpath, opt) + opt = opt or {} + local ops = {} + if is_host("macosx") then + table.insert(ops, 1, _insert_rpath_by_install_name_tool) + end + rpath = _replace_rpath_vars(rpath, opt) + for _, op in ipairs(ops) do + if op(binaryfile, rpath, opt) then + break + end + end +end + +-- change rpath +function change(binaryfile, rpath_old, rpath_new, opt) + opt = opt or {} + local ops = {} + if is_host("macosx") then + table.insert(ops, 1, _change_rpath_by_install_name_tool) + end + rpath_old = _replace_rpath_vars(rpath_old, opt) + rpath_new = _replace_rpath_vars(rpath_new, opt) + for _, op in ipairs(ops) do + if op(binaryfile, rpath_old, rpath_new, opt) then + break + end + end +end + +-- remove rpath +function remove(binaryfile, rpath, opt) + opt = opt or {} + local ops = {} + if is_host("macosx") then + table.insert(ops, 1, _remove_rpath_by_install_name_tool) + end + rpath = _replace_rpath_vars(rpath, opt) + for _, op in ipairs(ops) do + if op(binaryfile, rpath, opt) then + break + end + end +end + +-- clean rpath +function clean(binaryfile, opt) + for _, rpath in ipairs(list(binaryfile, opt)) do + remove(binaryfile, rpath, opt) + end +end |
