From 96e1ffb8e2991322c58e2c7ff5864367d9bea529 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 27 Nov 2025 23:38:25 +0800 Subject: add qtapp --- tests/plugins/pack/qtapp/LICENSE.md | 203 ++++++++++++++++++++++++++ tests/plugins/pack/qtapp/src/assets/xmake.ico | Bin 0 -> 119943 bytes tests/plugins/pack/qtapp/src/assets/xmake.png | Bin 0 -> 24378 bytes tests/plugins/pack/qtapp/src/main.cpp | 11 ++ tests/plugins/pack/qtapp/src/mainwindow.cpp | 16 ++ tests/plugins/pack/qtapp/src/mainwindow.h | 23 +++ tests/plugins/pack/qtapp/src/mainwindow.ui | 46 ++++++ tests/plugins/pack/qtapp/xmake.lua | 33 +++++ 8 files changed, 332 insertions(+) create mode 100644 tests/plugins/pack/qtapp/LICENSE.md create mode 100644 tests/plugins/pack/qtapp/src/assets/xmake.ico create mode 100644 tests/plugins/pack/qtapp/src/assets/xmake.png create mode 100644 tests/plugins/pack/qtapp/src/main.cpp create mode 100644 tests/plugins/pack/qtapp/src/mainwindow.cpp create mode 100644 tests/plugins/pack/qtapp/src/mainwindow.h create mode 100644 tests/plugins/pack/qtapp/src/mainwindow.ui create mode 100644 tests/plugins/pack/qtapp/xmake.lua diff --git a/tests/plugins/pack/qtapp/LICENSE.md b/tests/plugins/pack/qtapp/LICENSE.md new file mode 100644 index 000000000..fe2ff7e48 --- /dev/null +++ b/tests/plugins/pack/qtapp/LICENSE.md @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015-present Xmake Open Source Community + + 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. + diff --git a/tests/plugins/pack/qtapp/src/assets/xmake.ico b/tests/plugins/pack/qtapp/src/assets/xmake.ico new file mode 100644 index 000000000..52c8ef389 Binary files /dev/null and b/tests/plugins/pack/qtapp/src/assets/xmake.ico differ diff --git a/tests/plugins/pack/qtapp/src/assets/xmake.png b/tests/plugins/pack/qtapp/src/assets/xmake.png new file mode 100644 index 000000000..be7f6c4f2 Binary files /dev/null and b/tests/plugins/pack/qtapp/src/assets/xmake.png differ diff --git a/tests/plugins/pack/qtapp/src/main.cpp b/tests/plugins/pack/qtapp/src/main.cpp new file mode 100644 index 000000000..aee3b0862 --- /dev/null +++ b/tests/plugins/pack/qtapp/src/main.cpp @@ -0,0 +1,11 @@ +#include "mainwindow.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} + diff --git a/tests/plugins/pack/qtapp/src/mainwindow.cpp b/tests/plugins/pack/qtapp/src/mainwindow.cpp new file mode 100644 index 000000000..7002a54ea --- /dev/null +++ b/tests/plugins/pack/qtapp/src/mainwindow.cpp @@ -0,0 +1,16 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + setWindowTitle("Qt Widget App"); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + diff --git a/tests/plugins/pack/qtapp/src/mainwindow.h b/tests/plugins/pack/qtapp/src/mainwindow.h new file mode 100644 index 000000000..1f2d99bae --- /dev/null +++ b/tests/plugins/pack/qtapp/src/mainwindow.h @@ -0,0 +1,23 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow *ui; +}; + +#endif // MAINWINDOW_H + diff --git a/tests/plugins/pack/qtapp/src/mainwindow.ui b/tests/plugins/pack/qtapp/src/mainwindow.ui new file mode 100644 index 000000000..6772bd4b0 --- /dev/null +++ b/tests/plugins/pack/qtapp/src/mainwindow.ui @@ -0,0 +1,46 @@ + + + MainWindow + + + + 0 + 0 + 640 + 480 + + + + Qt Widget App + + + + + + + Welcome to Qt Widget App! + + + Qt::AlignCenter + + + + + + + + + 0 + 0 + 640 + 22 + + + + + + + + + + diff --git a/tests/plugins/pack/qtapp/xmake.lua b/tests/plugins/pack/qtapp/xmake.lua new file mode 100644 index 000000000..fb10ae741 --- /dev/null +++ b/tests/plugins/pack/qtapp/xmake.lua @@ -0,0 +1,33 @@ +set_version("1.0.0") +add_rules("mode.debug", "mode.release") + +includes("@builtin/xpack") + +target("qtapp") + add_rules("qt.widgetapp") + add_headerfiles("src/*.h") + add_files("src/*.cpp") + add_files("src/mainwindow.ui") + add_files("src/mainwindow.h") + +xpack("qtapp") + set_formats("nsis", "dmg", "appimage", "zip", "targz") + set_title("Qt Widget App") + set_author("ruki ") + set_description("A Qt Widget Application example for xpack.") + set_homepage("https://xmake.io") + set_license("Apache-2.0") + set_licensefile("LICENSE.md") + add_targets("qtapp") + + on_load(function (package) + package:set("basename", "qtapp-$(plat)-$(arch)-v$(version)") + -- set icon file based on format (use PNG for appimage, ICO for other formats) + local scriptdir = os.scriptdir() + if package:format() == "appimage" then + package:set("iconfile", path.join(scriptdir, "src/assets/xmake.png")) + else + package:set("iconfile", path.join(scriptdir, "src/assets/xmake.ico")) + end + end) + -- cgit v1.3.1 From 82d489354c52ffa5ae47803a79dbe8766cd49a08 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 27 Nov 2025 23:39:59 +0800 Subject: pack qt app --- xmake/rules/qt/xmake.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/xmake/rules/qt/xmake.lua b/xmake/rules/qt/xmake.lua index 69c8ff03a..45e47213c 100644 --- a/xmake/rules/qt/xmake.lua +++ b/xmake/rules/qt/xmake.lua @@ -116,6 +116,9 @@ rule("qt.widgetapp") after_install("windows", "install.windows") after_install("mingw", "install.mingw") + -- install application for xpack + on_installcmd("installcmd") + -- define rule: qt static widgetapp rule("qt.widgetapp_static") add_deps("qt.ui", "qt.moc", "qt._wasm_app", "qt.qrc", "qt.ts") @@ -144,6 +147,9 @@ rule("qt.widgetapp_static") after_install("windows", "install.windows") after_install("mingw", "install.mingw") + -- install application for xpack + on_installcmd("installcmd") + -- define rule: qt quickapp rule("qt.quickapp") add_deps("qt.qrc", "qt.moc", "qt._wasm_app", "qt.ts") @@ -166,6 +172,9 @@ rule("qt.quickapp") after_install("windows", "install.windows") after_install("mingw", "install.mingw") + -- install application for xpack + on_installcmd("installcmd") + -- define rule: qt static quickapp rule("qt.quickapp_static") add_deps("qt.qrc", "qt.moc", "qt._wasm_app", "qt.ts") @@ -190,6 +199,9 @@ rule("qt.quickapp_static") after_install("windows", "install.windows") after_install("mingw", "install.mingw") + -- install application for xpack + on_installcmd("installcmd") + -- define rule: qt qmlplugin rule("qt.qmlplugin") add_deps("qt.shared", "qt.qmltyperegistrar", "qt.ts") -- cgit v1.3.1 From b27bb56da9348cb78da9537bbe3d831585ce8be4 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 27 Nov 2025 23:43:14 +0800 Subject: add missing files --- xmake/rules/qt/installcmd.lua | 56 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 xmake/rules/qt/installcmd.lua diff --git a/xmake/rules/qt/installcmd.lua b/xmake/rules/qt/installcmd.lua new file mode 100644 index 000000000..b39f0558e --- /dev/null +++ b/xmake/rules/qt/installcmd.lua @@ -0,0 +1,56 @@ +--!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 installcmd.lua +-- + +-- install application for xpack +function main(target, batchcmds, opt) + local package = opt.package + + -- only for xpack (package exists) + if not package then + return + end + + -- get install directory + local installdir = package:installdir() + if not installdir then + return + end + + -- macOS: install .app bundle + if target:is_plat("macosx") then + local target_app = path.join(target:targetdir(), target:basename() .. ".app") + if os.isdir(target_app) then + -- copy .app to install directory + local appname = path.filename(target_app) + local dstappdir = path.join(installdir, appname) + batchcmds:cp(target_app, dstappdir, {symlink = true}) + 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 + 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.) + batchcmds:cp(path.join(bindir, "*"), package_bindir, {rootdir = bindir}) + end + end +end + -- cgit v1.3.1 From 214ad0573c9a3466ce9c7a2abaa3a8cde2c4e342 Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 28 Nov 2025 00:49:49 +0800 Subject: improve windeployqt for xpack --- xmake/rules/qt/install/mingw.lua | 86 +++----------------- xmake/rules/qt/install/windeployqt.lua | 140 +++++++++++++++++++++++++++++++++ xmake/rules/qt/install/windows.lua | 102 +++--------------------- xmake/rules/qt/installcmd.lua | 33 +++++++- 4 files changed, 188 insertions(+), 173 deletions(-) create mode 100644 xmake/rules/qt/install/windeployqt.lua 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 -- cgit v1.3.1 From 90a7323d1b7d910c8e1f7817932ec20a38839888 Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 28 Nov 2025 00:57:14 +0800 Subject: fix windeployqt --- xmake/rules/qt/install/mingw.lua | 8 ++------ xmake/rules/qt/install/windeployqt.lua | 11 ++++++----- xmake/rules/qt/install/windows.lua | 8 ++------ xmake/rules/qt/installcmd.lua | 27 +++++++++++++++------------ 4 files changed, 25 insertions(+), 29 deletions(-) diff --git a/xmake/rules/qt/install/mingw.lua b/xmake/rules/qt/install/mingw.lua index f9ac70488..1dc244c58 100644 --- a/xmake/rules/qt/install/mingw.lua +++ b/xmake/rules/qt/install/mingw.lua @@ -41,10 +41,6 @@ function main(target, opt) end end - -- prepare windeployqt arguments - local program, argv, envs = windeployqt.prepare(target, bindir, installfiles) - assert(program, "windeployqt.exe not found!") - - -- deploy Qt dependencies - os.vrunv(program, argv, {envs = envs}) + -- run windeployqt to deploy Qt dependencies + windeployqt.run_deploy(target, bindir, installfiles) end diff --git a/xmake/rules/qt/install/windeployqt.lua b/xmake/rules/qt/install/windeployqt.lua index b1340fc6c..d0404b8f6 100644 --- a/xmake/rules/qt/install/windeployqt.lua +++ b/xmake/rules/qt/install/windeployqt.lua @@ -26,12 +26,12 @@ 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) +-- run windeployqt to deploy Qt dependencies +function run_deploy(target, bindir, installfiles) -- get qt sdk local qt = find_qt() if not qt then - return nil, nil, nil + return end -- get windeployqt @@ -40,7 +40,7 @@ function prepare(target, bindir, installfiles) 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 + return end -- find qml directory @@ -135,6 +135,7 @@ function prepare(target, bindir, installfiles) -- windeployqt for both target and its deps table.join2(argv, installfiles) - return program, argv, envs + -- run windeployqt + os.vrunv(program, argv, {envs = envs}) end diff --git a/xmake/rules/qt/install/windows.lua b/xmake/rules/qt/install/windows.lua index 4b1c87b44..3ae05be35 100644 --- a/xmake/rules/qt/install/windows.lua +++ b/xmake/rules/qt/install/windows.lua @@ -41,10 +41,6 @@ function main(target, opt) end end - -- prepare windeployqt arguments - local program, argv, envs = windeployqt.prepare(target, bindir, installfiles) - assert(program, "windeployqt.exe not found!") - - -- deploy Qt dependencies - os.vrunv(program, argv, {envs = envs}) + -- run windeployqt to deploy Qt dependencies + windeployqt.run_deploy(target, bindir, installfiles) end diff --git a/xmake/rules/qt/installcmd.lua b/xmake/rules/qt/installcmd.lua index cf70d813c..5fc0a3cf4 100644 --- a/xmake/rules/qt/installcmd.lua +++ b/xmake/rules/qt/installcmd.lua @@ -47,29 +47,32 @@ function main(target, batchcmds, opt) 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) + -- First, deploy to target bindir, then copy all files to package bindir + + -- get target bindir for deployment + local bindir = assert(target:bindir(), "please use `xmake install -o installdir` or `set_installdir` to set install directory on windows.") -- copy target binary to bindir first - local targetfile = path.join(package_bindir, target:filename()) - batchcmds:cp(target:targetfile(), targetfile) + local targetfile = path.join(bindir, path.filename(target:targetfile())) + os.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) + local depfile = path.join(bindir, path.filename(dep:targetfile())) + os.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 + -- run windeployqt to deploy Qt dependencies to bindir + windeployqt.run_deploy(target, bindir, installfiles) + + -- copy all deployed files from bindir to package bindir + local package_bindir = package:installdir("bin") + batchcmds:mkdir(package_bindir) + batchcmds:cp(path.join(bindir, "*"), package_bindir, {rootdir = bindir}) else -- Linux: copy all files from bindir (plugins, translations, etc. should be handled separately) local bindir = target:bindir() -- cgit v1.3.1 From 3f1fd97cb59e469d9374d3d035810ea1d79296a2 Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 28 Nov 2025 21:00:05 +0800 Subject: fix installdir for pack qt --- xmake/rules/qt/installcmd.lua | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/xmake/rules/qt/installcmd.lua b/xmake/rules/qt/installcmd.lua index 5fc0a3cf4..73efac91d 100644 --- a/xmake/rules/qt/installcmd.lua +++ b/xmake/rules/qt/installcmd.lua @@ -47,32 +47,28 @@ function main(target, batchcmds, opt) end elseif target:is_plat("windows", "mingw") then -- Windows/Mingw: need to run windeployqt to deploy Qt dependencies - -- First, deploy to target bindir, then copy all files to package bindir + -- First, copy binaries to package bindir, then deploy Qt dependencies - -- get target bindir for deployment - local bindir = assert(target:bindir(), "please use `xmake install -o installdir` or `set_installdir` to set install directory on windows.") + -- get package bindir for deployment + local package_bindir = package:installdir("bin") + os.mkdir(package_bindir) - -- copy target binary to bindir first - local targetfile = path.join(bindir, path.filename(target:targetfile())) + -- copy target binary to package bindir first + local targetfile = path.join(package_bindir, target:filename()) os.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(bindir, path.filename(dep:targetfile())) + local depfile = path.join(package_bindir, path.filename(dep:targetfile())) os.cp(dep:targetfile(), depfile) table.insert(installfiles, depfile) end end - -- run windeployqt to deploy Qt dependencies to bindir - windeployqt.run_deploy(target, bindir, installfiles) - - -- copy all deployed files from bindir to package bindir - local package_bindir = package:installdir("bin") - batchcmds:mkdir(package_bindir) - batchcmds:cp(path.join(bindir, "*"), package_bindir, {rootdir = bindir}) + -- run windeployqt to deploy Qt dependencies to package bindir + windeployqt.run_deploy(target, package_bindir, installfiles) else -- Linux: copy all files from bindir (plugins, translations, etc. should be handled separately) local bindir = target:bindir() -- cgit v1.3.1 From e918f54bb7e5f722ef3ef375809ebcfdaf546ddb Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 28 Nov 2025 22:29:01 +0800 Subject: install deployed qt files --- xmake/rules/qt/installcmd.lua | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/xmake/rules/qt/installcmd.lua b/xmake/rules/qt/installcmd.lua index 73efac91d..f9aa87058 100644 --- a/xmake/rules/qt/installcmd.lua +++ b/xmake/rules/qt/installcmd.lua @@ -47,28 +47,33 @@ function main(target, batchcmds, opt) end elseif target:is_plat("windows", "mingw") then -- Windows/Mingw: need to run windeployqt to deploy Qt dependencies - -- First, copy binaries to package bindir, then deploy Qt dependencies + -- First, prepare files in a temporary directory, then copy to package bindir via batchcmds - -- get package bindir for deployment - local package_bindir = package:installdir("bin") - os.mkdir(package_bindir) + -- prepare deployment in a temporary directory + local deploydir = path.join(target:autogendir(), "qt", "deploy", target:name()) + os.mkdir(deploydir) - -- copy target binary to package bindir first - local targetfile = path.join(package_bindir, target:filename()) + -- copy target binary to deploydir first + local targetfile = path.join(deploydir, target:filename()) os.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())) + local depfile = path.join(deploydir, path.filename(dep:targetfile())) os.cp(dep:targetfile(), depfile) table.insert(installfiles, depfile) end end - -- run windeployqt to deploy Qt dependencies to package bindir - windeployqt.run_deploy(target, package_bindir, installfiles) + -- run windeployqt to deploy Qt dependencies to deploydir + windeployqt.run_deploy(target, deploydir, installfiles) + + -- copy all deployed files from deploydir to package bindir via batchcmds + local package_bindir = package:installdir("bin") + batchcmds:mkdir(package_bindir) + batchcmds:cp(path.join(deploydir, "*"), package_bindir, {rootdir = deploydir}) else -- Linux: copy all files from bindir (plugins, translations, etc. should be handled separately) local bindir = target:bindir() -- cgit v1.3.1 From b186dc3e4f917b820df6729d2054d4bc16f40654 Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 28 Nov 2025 22:31:14 +0800 Subject: fix cp dir --- xmake/plugins/pack/nsis/main.lua | 41 ++++++++++++++++++++++++++-------------- xmake/rules/qt/installcmd.lua | 17 ++++++++--------- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/xmake/plugins/pack/nsis/main.lua b/xmake/plugins/pack/nsis/main.lua index 793bed1cf..74a083a1d 100644 --- a/xmake/plugins/pack/nsis/main.lua +++ b/xmake/plugins/pack/nsis/main.lua @@ -93,22 +93,35 @@ function _get_command_strings(package, cmd, opt) local kind = cmd.kind if kind == "cp" then -- https://nsis.sourceforge.io/Reference/File - local srcfiles = os.files(cmd.srcpath) - for _, srcfile in ipairs(srcfiles) do - -- the destination is directory? append the filename - local dstfile = _translate_filepath(package, cmd.dstpath) - if #srcfiles > 1 or path.islastsep(dstfile) then - if opt.rootdir then - dstfile = path.join(dstfile, path.relative(srcfile, opt.rootdir)) - else - dstfile = path.join(dstfile, path.filename(srcfile)) + local srcpath = cmd.srcpath + local dstpath = _translate_filepath(package, cmd.dstpath) + + -- check if source is a directory + if os.isdir(srcpath) then + -- use File /r to recursively copy directory + srcpath = path.normalize(srcpath) + local dstdir = path.normalize(dstpath) + table.insert(result, string.format("SetOutPath \"%s\"", dstdir)) + table.insert(result, string.format("File /r \"%s\\*\"", srcpath)) + else + -- copy files + local srcfiles = os.files(srcpath) + for _, srcfile in ipairs(srcfiles) do + -- the destination is directory? append the filename + local dstfile = dstpath + if #srcfiles > 1 or path.islastsep(dstfile) then + if opt.rootdir then + dstfile = path.join(dstfile, path.relative(srcfile, opt.rootdir)) + else + dstfile = path.join(dstfile, path.filename(srcfile)) + end end + srcfile = path.normalize(srcfile) + local dstname = path.filename(dstfile) + local dstdir = path.normalize(path.directory(dstfile)) + table.insert(result, string.format("SetOutPath \"%s\"", dstdir)) + table.insert(result, string.format("File \"/oname=%s\" \"%s\"", dstname, srcfile)) end - srcfile = path.normalize(srcfile) - local dstname = path.filename(dstfile) - local dstdir = path.normalize(path.directory(dstfile)) - table.insert(result, string.format("SetOutPath \"%s\"", dstdir)) - table.insert(result, string.format("File \"/oname=%s\" \"%s\"", dstname, srcfile)) end elseif kind == "rm" then local filepath = _translate_filepath(package, cmd.filepath) diff --git a/xmake/rules/qt/installcmd.lua b/xmake/rules/qt/installcmd.lua index f9aa87058..705ca9843 100644 --- a/xmake/rules/qt/installcmd.lua +++ b/xmake/rules/qt/installcmd.lua @@ -24,7 +24,7 @@ import("rules.qt.install.windeployqt", {rootdir = os.programdir()}) -- install application for xpack function main(target, batchcmds, opt) local package = opt.package - + -- only for xpack (package exists) if not package then return @@ -48,15 +48,15 @@ function main(target, batchcmds, opt) elseif target:is_plat("windows", "mingw") then -- Windows/Mingw: need to run windeployqt to deploy Qt dependencies -- First, prepare files in a temporary directory, then copy to package bindir via batchcmds - + -- prepare deployment in a temporary directory local deploydir = path.join(target:autogendir(), "qt", "deploy", target:name()) os.mkdir(deploydir) - + -- copy target binary to deploydir first local targetfile = path.join(deploydir, target:filename()) os.cp(target:targetfile(), targetfile) - + -- copy qt.shared deps local installfiles = {targetfile} for _, dep in ipairs(target:orderdeps()) do @@ -69,11 +69,10 @@ function main(target, batchcmds, opt) -- run windeployqt to deploy Qt dependencies to deploydir windeployqt.run_deploy(target, deploydir, installfiles) - - -- copy all deployed files from deploydir to package bindir via batchcmds - local package_bindir = package:installdir("bin") - batchcmds:mkdir(package_bindir) - batchcmds:cp(path.join(deploydir, "*"), package_bindir, {rootdir = deploydir}) + + -- copy all deployed files and directories from deploydir to root install directory via batchcmds + local installdir = package:installdir() + batchcmds:cp(path.join(deploydir, "*"), installdir, {rootdir = deploydir}) else -- Linux: copy all files from bindir (plugins, translations, etc. should be handled separately) local bindir = target:bindir() -- cgit v1.3.1 From 9d6daa73dbbc1c46552d5377291339514a1b6153 Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 28 Nov 2025 22:34:57 +0800 Subject: improve nsis --- xmake/plugins/pack/nsis/main.lua | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/xmake/plugins/pack/nsis/main.lua b/xmake/plugins/pack/nsis/main.lua index 74a083a1d..3dc579608 100644 --- a/xmake/plugins/pack/nsis/main.lua +++ b/xmake/plugins/pack/nsis/main.lua @@ -96,31 +96,36 @@ function _get_command_strings(package, cmd, opt) local srcpath = cmd.srcpath local dstpath = _translate_filepath(package, cmd.dstpath) - -- check if source is a directory - if os.isdir(srcpath) then - -- use File /r to recursively copy directory - srcpath = path.normalize(srcpath) - local dstdir = path.normalize(dstpath) - table.insert(result, string.format("SetOutPath \"%s\"", dstdir)) - table.insert(result, string.format("File /r \"%s\\*\"", srcpath)) - else - -- copy files - local srcfiles = os.files(srcpath) - for _, srcfile in ipairs(srcfiles) do - -- the destination is directory? append the filename + -- match files and directories + local srcitems = os.filedirs(srcpath) + for _, srcitem in ipairs(srcitems) do + if os.isdir(srcitem) then + -- copy directory recursively + srcitem = path.normalize(srcitem) + local dstdir = dstpath + if opt.rootdir then + dstdir = path.join(dstdir, path.relative(srcitem, opt.rootdir)) + else + dstdir = path.join(dstdir, path.filename(srcitem)) + end + dstdir = path.normalize(dstdir) + table.insert(result, string.format("SetOutPath \"%s\"", dstdir)) + table.insert(result, string.format("File /r \"%s\\*\"", srcitem)) + else + -- copy file local dstfile = dstpath - if #srcfiles > 1 or path.islastsep(dstfile) then + if #srcitems > 1 or path.islastsep(dstfile) then if opt.rootdir then - dstfile = path.join(dstfile, path.relative(srcfile, opt.rootdir)) + dstfile = path.join(dstfile, path.relative(srcitem, opt.rootdir)) else - dstfile = path.join(dstfile, path.filename(srcfile)) + dstfile = path.join(dstfile, path.filename(srcitem)) end end - srcfile = path.normalize(srcfile) + srcitem = path.normalize(srcitem) local dstname = path.filename(dstfile) local dstdir = path.normalize(path.directory(dstfile)) table.insert(result, string.format("SetOutPath \"%s\"", dstdir)) - table.insert(result, string.format("File \"/oname=%s\" \"%s\"", dstname, srcfile)) + table.insert(result, string.format("File \"/oname=%s\" \"%s\"", dstname, srcitem)) end end elseif kind == "rm" then -- cgit v1.3.1 From 241d9b9ed100c437f5e659dfb626f4c037b92057 Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 28 Nov 2025 22:37:08 +0800 Subject: add uninstallcmd --- xmake/plugins/pack/wix/main.lua | 62 ++++++++++++++++++++++++---------- xmake/rules/qt/uninstallcmd.lua | 75 +++++++++++++++++++++++++++++++++++++++++ xmake/rules/qt/xmake.lua | 4 +++ 3 files changed, 123 insertions(+), 18 deletions(-) create mode 100644 xmake/rules/qt/uninstallcmd.lua diff --git a/xmake/plugins/pack/wix/main.lua b/xmake/plugins/pack/wix/main.lua index 080d4e66d..896ea2339 100644 --- a/xmake/plugins/pack/wix/main.lua +++ b/xmake/plugins/pack/wix/main.lua @@ -79,26 +79,52 @@ function _get_cp_kind_table(package, cmds, opt) end local option = table.join(cmd.opt or {}, opt) - local srcfiles = os.files(cmd.srcpath) - for _, srcfile in ipairs(srcfiles) do - -- the destination is directory? append the filename - local dstfile = cmd.dstpath - if #srcfiles > 1 or path.islastsep(dstfile) then - if option.rootdir then - dstfile = path.join(dstfile, path.relative(srcfile, option.rootdir)) - else - dstfile = path.join(dstfile, path.filename(srcfile)) + -- match files and directories + local srcitems = os.filedirs(cmd.srcpath) + for _, srcitem in ipairs(srcitems) do + if os.isdir(srcitem) then + -- for directory, recursively collect all files in it + local rootdir = option.rootdir or srcitem + local files = os.files(path.join(srcitem, "**")) + for _, srcfile in ipairs(files) do + -- the destination is directory? append the relative path + local dstfile = cmd.dstpath + if option.rootdir then + dstfile = path.join(dstfile, path.relative(srcfile, option.rootdir)) + else + dstfile = path.join(dstfile, path.relative(srcfile, rootdir)) + end + srcfile = path.normalize(srcfile) + local dstname = path.filename(dstfile) + local dstdir = path.normalize(path.directory(dstfile)) + dstdir = _translate_filepath(package, dstdir) + + if result[dstdir] then + table.insert(result[dstdir], {srcfile, dstname}) + else + result[dstdir] = {{srcfile, dstname}} + end end - end - srcfile = path.normalize(srcfile) - local dstname = path.filename(dstfile) - local dstdir = path.normalize(path.directory(dstfile)) - dstdir = _translate_filepath(package, dstdir) - - if result[dstdir] then - table.insert(result[dstdir], {srcfile, dstname}) else - result[dstdir] = {{srcfile, dstname}} + -- for file, use the original logic + local dstfile = cmd.dstpath + if #srcitems > 1 or path.islastsep(dstfile) then + if option.rootdir then + dstfile = path.join(dstfile, path.relative(srcitem, option.rootdir)) + else + dstfile = path.join(dstfile, path.filename(srcitem)) + end + end + srcitem = path.normalize(srcitem) + local dstname = path.filename(dstfile) + local dstdir = path.normalize(path.directory(dstfile)) + dstdir = _translate_filepath(package, dstdir) + + if result[dstdir] then + table.insert(result[dstdir], {srcitem, dstname}) + else + result[dstdir] = {{srcitem, dstname}} + end end end ::continue:: diff --git a/xmake/rules/qt/uninstallcmd.lua b/xmake/rules/qt/uninstallcmd.lua new file mode 100644 index 000000000..12ef3a345 --- /dev/null +++ b/xmake/rules/qt/uninstallcmd.lua @@ -0,0 +1,75 @@ +--!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 uninstallcmd.lua +-- + +-- uninstall application for xpack +function main(target, batchcmds, opt) + local package = opt.package + + -- only for xpack (package exists) + if not package then + return + end + + -- get install directory + local installdir = package:installdir() + if not installdir then + return + end + + -- macOS: uninstall .app bundle + if target:is_plat("macosx") then + local target_app = path.join(target:targetdir(), target:basename() .. ".app") + if os.isdir(target_app) then + -- remove .app from install directory + local appname = path.filename(target_app) + local dstappdir = path.join(installdir, appname) + batchcmds:rmdir(dstappdir, {emptydirs = true}) + end + elseif target:is_plat("windows", "mingw") then + -- Windows/Mingw: remove all deployed files and directories from install directory + local installdir = package:installdir() + + -- remove all files and directories deployed by windeployqt + -- since we copied everything from deploydir to installdir, we need to remove them + -- use os.filedirs to match all files and directories, then remove them + for _, item in ipairs(os.filedirs(path.join(installdir, "*"))) do + if os.isfile(item) then + batchcmds:rm(item, {emptydirs = true}) + elseif os.isdir(item) then + batchcmds:rmdir(item, {emptydirs = true}) + end + end + else + -- Linux: remove all files from bin directory + local bindir = target:bindir() + if bindir and os.isdir(bindir) then + local package_bindir = package:installdir("bin") + -- remove all files and directories from package bindir + for _, item in ipairs(os.filedirs(path.join(package_bindir, "*"))) do + if os.isfile(item) then + batchcmds:rm(item, {emptydirs = true}) + elseif os.isdir(item) then + batchcmds:rmdir(item, {emptydirs = true}) + end + end + end + end +end + diff --git a/xmake/rules/qt/xmake.lua b/xmake/rules/qt/xmake.lua index 45e47213c..db13aa913 100644 --- a/xmake/rules/qt/xmake.lua +++ b/xmake/rules/qt/xmake.lua @@ -118,6 +118,7 @@ rule("qt.widgetapp") -- install application for xpack on_installcmd("installcmd") + on_uninstallcmd("uninstallcmd") -- define rule: qt static widgetapp rule("qt.widgetapp_static") @@ -149,6 +150,7 @@ rule("qt.widgetapp_static") -- install application for xpack on_installcmd("installcmd") + on_uninstallcmd("uninstallcmd") -- define rule: qt quickapp rule("qt.quickapp") @@ -174,6 +176,7 @@ rule("qt.quickapp") -- install application for xpack on_installcmd("installcmd") + on_uninstallcmd("uninstallcmd") -- define rule: qt static quickapp rule("qt.quickapp_static") @@ -201,6 +204,7 @@ rule("qt.quickapp_static") -- install application for xpack on_installcmd("installcmd") + on_uninstallcmd("uninstallcmd") -- define rule: qt qmlplugin rule("qt.qmlplugin") -- cgit v1.3.1 From 574ee096281c94938eb396045f7e96a25df5387b Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 28 Nov 2025 22:38:15 +0800 Subject: fix uninstall files --- xmake/rules/qt/uninstallcmd.lua | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/xmake/rules/qt/uninstallcmd.lua b/xmake/rules/qt/uninstallcmd.lua index 12ef3a345..cc989ce12 100644 --- a/xmake/rules/qt/uninstallcmd.lua +++ b/xmake/rules/qt/uninstallcmd.lua @@ -46,15 +46,33 @@ function main(target, batchcmds, opt) -- Windows/Mingw: remove all deployed files and directories from install directory local installdir = package:installdir() - -- remove all files and directories deployed by windeployqt - -- since we copied everything from deploydir to installdir, we need to remove them - -- use os.filedirs to match all files and directories, then remove them - for _, item in ipairs(os.filedirs(path.join(installdir, "*"))) do - if os.isfile(item) then - batchcmds:rm(item, {emptydirs = true}) - elseif os.isdir(item) then - batchcmds:rmdir(item, {emptydirs = true}) + -- use the same deploydir as installcmd to determine what files were installed + -- deploydir exists at build/pack time when uninstallcmd is called + local deploydir = path.join(target:autogendir(), "qt", "deploy", target:name()) + + -- if deploydir exists, use it to get the list of files/dirs to remove + if os.isdir(deploydir) then + -- match all files and directories in deploydir (same as installcmd) + for _, item in ipairs(os.filedirs(path.join(deploydir, "*"))) do + local relpath = path.relative(item, deploydir) + local dstpath = path.join(installdir, relpath) + if os.isfile(item) then + batchcmds:rm(dstpath, {emptydirs = true}) + elseif os.isdir(item) then + batchcmds:rmdir(dstpath, {emptydirs = true}) + end + end + else + -- fallback: if deploydir doesn't exist, remove common Qt deployment items + -- this should rarely happen, but provides a safety net + batchcmds:rm(path.join(installdir, target:filename()), {emptydirs = true}) + for _, dep in ipairs(target:orderdeps()) do + if dep:rule("qt.shared") then + batchcmds:rm(path.join(installdir, path.filename(dep:targetfile())), {emptydirs = true}) + end end + -- note: we can't easily remove all deployed files without deploydir, + -- but at least we remove the main binaries end else -- Linux: remove all files from bin directory -- cgit v1.3.1 From 2766d4d1065f8daee0b7b5c95f9fd85b03144eb5 Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 28 Nov 2025 22:39:14 +0800 Subject: format code --- tests/plugins/pack/qtapp/LICENSE.md | 203 ---------------------------- tests/plugins/pack/qtapp/src/main.cpp | 3 +- tests/plugins/pack/qtapp/src/mainwindow.cpp | 6 +- 3 files changed, 3 insertions(+), 209 deletions(-) delete mode 100644 tests/plugins/pack/qtapp/LICENSE.md diff --git a/tests/plugins/pack/qtapp/LICENSE.md b/tests/plugins/pack/qtapp/LICENSE.md deleted file mode 100644 index fe2ff7e48..000000000 --- a/tests/plugins/pack/qtapp/LICENSE.md +++ /dev/null @@ -1,203 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2015-present Xmake Open Source Community - - 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. - diff --git a/tests/plugins/pack/qtapp/src/main.cpp b/tests/plugins/pack/qtapp/src/main.cpp index aee3b0862..a1cfd8dea 100644 --- a/tests/plugins/pack/qtapp/src/main.cpp +++ b/tests/plugins/pack/qtapp/src/main.cpp @@ -1,8 +1,7 @@ #include "mainwindow.h" #include -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); diff --git a/tests/plugins/pack/qtapp/src/mainwindow.cpp b/tests/plugins/pack/qtapp/src/mainwindow.cpp index 7002a54ea..a20d45d1a 100644 --- a/tests/plugins/pack/qtapp/src/mainwindow.cpp +++ b/tests/plugins/pack/qtapp/src/mainwindow.cpp @@ -3,14 +3,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) - , ui(new Ui::MainWindow) -{ + , ui(new Ui::MainWindow) { ui->setupUi(this); setWindowTitle("Qt Widget App"); } -MainWindow::~MainWindow() -{ +MainWindow::~MainWindow() { delete ui; } -- cgit v1.3.1