diff options
| author | ruki <[email protected]> | 2025-11-28 00:49:49 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2025-11-28 00:49:49 +0800 |
| commit | 214ad0573c9a3466ce9c7a2abaa3a8cde2c4e342 (patch) | |
| tree | 6d6beba771dd47057f486d69ae6fb88feb2cdf30 | |
| parent | b27bb56da9348cb78da9537bbe3d831585ce8be4 (diff) | |
improve windeployqt for xpack
| -rw-r--r-- | xmake/rules/qt/install/mingw.lua | 86 | ||||
| -rw-r--r-- | xmake/rules/qt/install/windeployqt.lua | 140 | ||||
| -rw-r--r-- | xmake/rules/qt/install/windows.lua | 102 | ||||
| -rw-r--r-- | xmake/rules/qt/installcmd.lua | 33 |
4 files changed, 188 insertions, 173 deletions
diff --git a/xmake/rules/qt/install/mingw.lua b/xmake/rules/qt/install/mingw.lua index fb54c8e2c..f9ac70488 100644 --- a/xmake/rules/qt/install/mingw.lua +++ b/xmake/rules/qt/install/mingw.lua @@ -19,12 +19,7 @@ -- -- imports -import("core.base.option") -import("core.project.config") -import("core.tool.toolchain") -import("lib.detect.find_path") -import("lib.detect.find_file") -import("detect.sdks.find_qt") +import("rules.qt.install.windeployqt", {rootdir = os.programdir()}) -- get bin directory, if set_prefixdir changed bindir, we should use bindir function _get_bindir(target) @@ -39,80 +34,17 @@ function main(target, opt) local targetfile = path.join(bindir, path.filename(target:targetfile())) local installfiles = {} table.insert(installfiles, targetfile) - for _, t in ipairs(target:orderdeps()) do - if t:rules()["qt.shared"] then -- qt.shared deps - local installfile = path.join(bindir, path.filename(t:targetfile())) + for _, dep in ipairs(target:orderdeps()) do + if dep:rule("qt.shared") then -- qt.shared deps + local installfile = path.join(bindir, path.filename(dep:targetfile())) table.insert(installfiles, installfile) end end - -- get qt sdk - local qt = assert(find_qt(), "Qt SDK not found!") + -- prepare windeployqt arguments + local program, argv, envs = windeployqt.prepare(target, bindir, installfiles) + assert(program, "windeployqt.exe not found!") - -- get windeployqt - local search_dirs = {} - if qt.bindir_host then table.insert(search_dirs, qt.bindir_host) end - if qt.bindir then table.insert(search_dirs, qt.bindir) end - local windeployqt = find_file("windeployqt" .. (is_host("windows") and ".exe" or ""), search_dirs) - assert(os.isexec(windeployqt), "windeployqt.exe not found!") - - -- find qml directory - local qmldir = target:values("qt.deploy.qmldir") - if not qmldir then - for _, sourcebatch in pairs(target:sourcebatches()) do - if sourcebatch.rulename == "qt.qrc" then - for _, sourcefile in ipairs(sourcebatch.sourcefiles) do - qmldir = find_path("*.qml", path.directory(sourcefile)) - if qmldir then - break - end - end - end - end - else - qmldir = path.join(target:scriptdir(), qmldir) - end - - -- add mingw dlls to PATH - local envs = nil - local mingw = toolchain.load("mingw", {plat = target:plat(), arch = target:arch()}) - if mingw then - local bindir = mingw:bindir() - if bindir then - envs = {PATH = bindir} - end - end - - local argv = {"--force"} - if option.get("diagnosis") then - table.insert(argv, "--verbose=2") - elseif option.get("verbose") then - table.insert(argv, "--verbose=1") - else - table.insert(argv, "--verbose=0") - end - - -- make sure user flags have priority over default - local user_flags = table.wrap(target:values("qt.deploy.flags")) - if table.contains(user_flags, "--debug", "--release") then - if is_mode("debug") then - table.insert(argv, "--debug") - else - table.insert(argv, "--release") - end - end - - if qmldir then - table.insert(argv, "--qmldir=" .. qmldir) - end - - -- add user flags - if user_flags then - argv = table.join(argv, user_flags) - end - - -- windeployqt for both target and its deps - table.join2(argv, installfiles) - - os.vrunv(windeployqt, argv, {envs = envs}) + -- deploy Qt dependencies + os.vrunv(program, argv, {envs = envs}) end diff --git a/xmake/rules/qt/install/windeployqt.lua b/xmake/rules/qt/install/windeployqt.lua new file mode 100644 index 000000000..b1340fc6c --- /dev/null +++ b/xmake/rules/qt/install/windeployqt.lua @@ -0,0 +1,140 @@ +--!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, Xmake Open Source Community. +-- +-- @author ruki +-- @file windeployqt.lua +-- + +-- imports +import("core.base.option") +import("core.project.config") +import("core.tool.toolchain") +import("lib.detect.find_file") +import("lib.detect.find_path") +import("detect.sdks.find_qt") + +-- prepare windeployqt arguments and environment +function prepare(target, bindir, installfiles) + -- get qt sdk + local qt = find_qt() + if not qt then + return nil, nil, nil + end + + -- get windeployqt + local search_dirs = {} + if qt.bindir_host then table.insert(search_dirs, qt.bindir_host) end + if qt.bindir then table.insert(search_dirs, qt.bindir) end + local program = find_file("windeployqt" .. (is_host("windows") and ".exe" or ""), search_dirs) + if not program or not os.isexec(program) then + return nil, nil, nil + end + + -- find qml directory + local qmldir = target:values("qt.deploy.qmldir") + if not qmldir then + for _, sourcebatch in pairs(target:sourcebatches()) do + if sourcebatch.rulename == "qt.qrc" then + for _, sourcefile in ipairs(sourcebatch.sourcefiles) do + qmldir = find_path("*.qml", path.directory(sourcefile)) + if qmldir then + break + end + end + end + end + else + qmldir = path.join(target:scriptdir(), qmldir) + end + + -- prepare environment + local envs = nil + if target:is_plat("windows") then + -- find msvc to set VCINSTALLDIR env + local msvc = toolchain.load("msvc", {plat = target:plat(), arch = target:arch()}) + if msvc then + local vcvars = msvc:config("vcvars") + if vcvars and vcvars.VCInstallDir then + envs = {VCINSTALLDIR = vcvars.VCInstallDir} + end + end + elseif target:is_plat("mingw") then + -- add mingw dlls to PATH + local mingw = toolchain.load("mingw", {plat = target:plat(), arch = target:arch()}) + if mingw then + local mingw_bindir = mingw:bindir() + if mingw_bindir then + envs = {PATH = {}} + table.insert(envs.PATH, mingw_bindir) + end + end + end + + -- bind qt bin path + -- https://github.com/xmake-io/xmake/issues/4297 + if qt.bindir_host or qt.bindir then + envs = envs or {} + envs.PATH = envs.PATH or {} + if type(envs.PATH) == "string" then + envs.PATH = {envs.PATH} + end + if qt.bindir_host then + table.insert(envs.PATH, qt.bindir_host) + end + if qt.bindir then + table.insert(envs.PATH, qt.bindir) + end + local curpath = os.getenv("PATH") + if curpath then + table.join2(envs.PATH, path.splitenv(curpath)) + end + end + + -- prepare arguments + local argv = {"--force"} + if option.get("diagnosis") then + table.insert(argv, "--verbose=2") + elseif option.get("verbose") then + table.insert(argv, "--verbose=1") + else + table.insert(argv, "--verbose=0") + end + + -- make sure user flags have priority over default + local user_flags = table.wrap(target:values("qt.deploy.flags")) + if table.contains(user_flags, "--debug", "--release") then + if is_mode("debug") then + table.insert(argv, "--debug") + else + table.insert(argv, "--release") + end + end + + if qmldir then + table.insert(argv, "--qmldir=" .. qmldir) + end + + -- add user flags + if user_flags then + argv = table.join(argv, user_flags) + end + + -- windeployqt for both target and its deps + table.join2(argv, installfiles) + + return program, argv, envs +end + diff --git a/xmake/rules/qt/install/windows.lua b/xmake/rules/qt/install/windows.lua index 5a60de706..4b1c87b44 100644 --- a/xmake/rules/qt/install/windows.lua +++ b/xmake/rules/qt/install/windows.lua @@ -19,12 +19,7 @@ -- -- imports -import("core.base.option") -import("core.project.config") -import("core.tool.toolchain") -import("lib.detect.find_file") -import("lib.detect.find_path") -import("detect.sdks.find_qt") +import("rules.qt.install.windeployqt", {rootdir = os.programdir()}) -- get bin directory, if set_prefixdir changed bindir, we should use bindir function _get_bindir(target) @@ -39,96 +34,17 @@ function main(target, opt) local targetfile = path.join(bindir, path.filename(target:targetfile())) local installfiles = {} table.insert(installfiles, targetfile) - for _, t in ipairs(target:orderdeps()) do - if t:rules()["qt.shared"] then -- qt.shared deps - local installfile = path.join(bindir, path.filename(t:targetfile())) + for _, dep in ipairs(target:orderdeps()) do + if dep:rule("qt.shared") then -- qt.shared deps + local installfile = path.join(bindir, path.filename(dep:targetfile())) table.insert(installfiles, installfile) end end - -- get qt sdk - local qt = assert(find_qt(), "Qt SDK not found!") + -- prepare windeployqt arguments + local program, argv, envs = windeployqt.prepare(target, bindir, installfiles) + assert(program, "windeployqt.exe not found!") - -- get windeployqt - local search_dirs = {} - if qt.bindir_host then table.insert(search_dirs, qt.bindir_host) end - if qt.bindir then table.insert(search_dirs, qt.bindir) end - local windeployqt = find_file("windeployqt" .. (is_host("windows") and ".exe" or ""), search_dirs) - assert(os.isexec(windeployqt), "windeployqt.exe not found!") - - -- find qml directory - local qmldir = target:values("qt.deploy.qmldir") - if not qmldir then - for _, sourcebatch in pairs(target:sourcebatches()) do - if sourcebatch.rulename == "qt.qrc" then - for _, sourcefile in ipairs(sourcebatch.sourcefiles) do - qmldir = find_path("*.qml", path.directory(sourcefile)) - if qmldir then - break - end - end - end - end - else - qmldir = path.join(target:scriptdir(), qmldir) - end - - -- find msvc to set VCINSTALLDIR env - local envs = nil - local msvc = toolchain.load("msvc", {plat = target:plat(), arch = target:arch()}) - if msvc then - local vcvars = msvc:config("vcvars") - if vcvars and vcvars.VCInstallDir then - envs = {VCINSTALLDIR = vcvars.VCInstallDir} - end - end - -- bind qt bin path - -- https://github.com/xmake-io/xmake/issues/4297 - if qt.bindir_host or qt.bindir then - envs = envs or {} - envs.PATH = {} - if qt.bindir_host then - table.insert(envs.PATH, qt.bindir_host) - end - if qt.bindir then - table.insert(envs.PATH, qt.bindir) - end - local curpath = os.getenv("PATH") - if curpath then - table.join2(envs.PATH, path.splitenv(curpath)) - end - end - - local argv = {"--force"} - if option.get("diagnosis") then - table.insert(argv, "--verbose=2") - elseif option.get("verbose") then - table.insert(argv, "--verbose=1") - else - table.insert(argv, "--verbose=0") - end - - -- make sure user flags have priority over default - local user_flags = table.wrap(target:values("qt.deploy.flags")) - if table.contains(user_flags, "--debug", "--release") then - if is_mode("debug") then - table.insert(argv, "--debug") - else - table.insert(argv, "--release") - end - end - - if qmldir then - table.insert(argv, "--qmldir=" .. qmldir) - end - - -- add user flags - if user_flags then - argv = table.join(argv, user_flags) - end - - -- windeployqt for both target and its deps - table.join2(argv, installfiles) - - os.vrunv(windeployqt, argv, {envs = envs}) + -- deploy Qt dependencies + os.vrunv(program, argv, {envs = envs}) end diff --git a/xmake/rules/qt/installcmd.lua b/xmake/rules/qt/installcmd.lua index b39f0558e..cf70d813c 100644 --- a/xmake/rules/qt/installcmd.lua +++ b/xmake/rules/qt/installcmd.lua @@ -18,6 +18,9 @@ -- @file installcmd.lua -- +-- imports +import("rules.qt.install.windeployqt", {rootdir = os.programdir()}) + -- install application for xpack function main(target, batchcmds, opt) local package = opt.package @@ -42,13 +45,37 @@ function main(target, batchcmds, opt) local dstappdir = path.join(installdir, appname) batchcmds:cp(target_app, dstappdir, {symlink = true}) end + elseif target:is_plat("windows", "mingw") then + -- Windows/Mingw: need to run windeployqt to deploy Qt dependencies + -- First, copy binary to package bindir (windeployqt needs it there) + local package_bindir = package:installdir("bin") + batchcmds:mkdir(package_bindir) + + -- copy target binary to bindir first + local targetfile = path.join(package_bindir, target:filename()) + batchcmds:cp(target:targetfile(), targetfile) + + -- copy qt.shared deps + local installfiles = {targetfile} + for _, dep in ipairs(target:orderdeps()) do + if dep:rule("qt.shared") then + local depfile = path.join(package_bindir, path.filename(dep:targetfile())) + batchcmds:cp(dep:targetfile(), depfile) + table.insert(installfiles, depfile) + end + end + + -- reuse existing logic to prepare windeployqt arguments + local program, argv, envs = windeployqt.prepare(target, package_bindir, installfiles) + if program and argv and envs then + batchcmds:vrunv(program, argv, {envs = envs}) + end else - -- Windows/Linux: after_install has already run windeployqt which deploys Qt dependencies to bindir - -- We need to copy all files from bindir (including plugins, translations, etc.) to the package install directory + -- Linux: copy all files from bindir (plugins, translations, etc. should be handled separately) local bindir = target:bindir() if bindir and os.isdir(bindir) then local package_bindir = package:installdir("bin") - -- copy all files and directories from bindir (includes deployed Qt dependencies, plugins, translations, etc.) + -- copy all files and directories from bindir batchcmds:cp(path.join(bindir, "*"), package_bindir, {rootdir = bindir}) end end |
