diff options
| author | ruki <[email protected]> | 2025-11-28 12:05:12 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-11-28 12:05:12 +0800 |
| commit | ec0c0d458a14c6a9aff1a01d5e1cf264c8dcfeb2 (patch) | |
| tree | 648c6e438c1ee829c4f61f8f83c6fef20057ebbb | |
| parent | 4be95ee920757c2c813b84af5f07653c191c0215 (diff) | |
| parent | 2766d4d1065f8daee0b7b5c95f9fd85b03144eb5 (diff) | |
Merge pull request #7071 from xmake-io/qt
Add Qt pack support
| -rw-r--r-- | tests/plugins/pack/qtapp/src/assets/xmake.ico | bin | 0 -> 119943 bytes | |||
| -rw-r--r-- | tests/plugins/pack/qtapp/src/assets/xmake.png | bin | 0 -> 24378 bytes | |||
| -rw-r--r-- | tests/plugins/pack/qtapp/src/main.cpp | 10 | ||||
| -rw-r--r-- | tests/plugins/pack/qtapp/src/mainwindow.cpp | 14 | ||||
| -rw-r--r-- | tests/plugins/pack/qtapp/src/mainwindow.h | 23 | ||||
| -rw-r--r-- | tests/plugins/pack/qtapp/src/mainwindow.ui | 46 | ||||
| -rw-r--r-- | tests/plugins/pack/qtapp/xmake.lua | 33 | ||||
| -rw-r--r-- | xmake/plugins/pack/nsis/main.lua | 42 | ||||
| -rw-r--r-- | xmake/plugins/pack/wix/main.lua | 62 | ||||
| -rw-r--r-- | xmake/rules/qt/install/mingw.lua | 84 | ||||
| -rw-r--r-- | xmake/rules/qt/install/windeployqt.lua | 141 | ||||
| -rw-r--r-- | xmake/rules/qt/install/windows.lua | 100 | ||||
| -rw-r--r-- | xmake/rules/qt/installcmd.lua | 86 | ||||
| -rw-r--r-- | xmake/rules/qt/uninstallcmd.lua | 93 | ||||
| -rw-r--r-- | xmake/rules/qt/xmake.lua | 16 |
15 files changed, 548 insertions, 202 deletions
diff --git a/tests/plugins/pack/qtapp/src/assets/xmake.ico b/tests/plugins/pack/qtapp/src/assets/xmake.ico Binary files differnew file mode 100644 index 000000000..52c8ef389 --- /dev/null +++ b/tests/plugins/pack/qtapp/src/assets/xmake.ico diff --git a/tests/plugins/pack/qtapp/src/assets/xmake.png b/tests/plugins/pack/qtapp/src/assets/xmake.png Binary files differnew file mode 100644 index 000000000..be7f6c4f2 --- /dev/null +++ b/tests/plugins/pack/qtapp/src/assets/xmake.png diff --git a/tests/plugins/pack/qtapp/src/main.cpp b/tests/plugins/pack/qtapp/src/main.cpp new file mode 100644 index 000000000..a1cfd8dea --- /dev/null +++ b/tests/plugins/pack/qtapp/src/main.cpp @@ -0,0 +1,10 @@ +#include "mainwindow.h" +#include <QApplication> + +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..a20d45d1a --- /dev/null +++ b/tests/plugins/pack/qtapp/src/mainwindow.cpp @@ -0,0 +1,14 @@ +#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 <QMainWindow> + +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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>640</width> + <height>480</height> + </rect> + </property> + <property name="windowTitle"> + <string>Qt Widget App</string> + </property> + <widget class="QWidget" name="centralWidget"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Welcome to Qt Widget App!</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menuBar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>640</width> + <height>22</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusBar"/> + </widget> + <layoutdefault spacing="6" margin="11"/> + <resources/> + <connections/> +</ui> + 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 <[email protected]>") + 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) + diff --git a/xmake/plugins/pack/nsis/main.lua b/xmake/plugins/pack/nsis/main.lua index 793bed1cf..3dc579608 100644 --- a/xmake/plugins/pack/nsis/main.lua +++ b/xmake/plugins/pack/nsis/main.lua @@ -93,22 +93,40 @@ 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 + local srcpath = cmd.srcpath + local dstpath = _translate_filepath(package, cmd.dstpath) + + -- 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 - dstfile = path.join(dstfile, path.relative(srcfile, opt.rootdir)) + dstdir = path.join(dstdir, path.relative(srcitem, opt.rootdir)) else - dstfile = path.join(dstfile, path.filename(srcfile)) + 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 #srcitems > 1 or path.islastsep(dstfile) then + if opt.rootdir then + dstfile = path.join(dstfile, path.relative(srcitem, opt.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)) + table.insert(result, string.format("SetOutPath \"%s\"", dstdir)) + table.insert(result, string.format("File \"/oname=%s\" \"%s\"", dstname, srcitem)) 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/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)) - end - end - srcfile = path.normalize(srcfile) - local dstname = path.filename(dstfile) - local dstdir = path.normalize(path.directory(dstfile)) - dstdir = _translate_filepath(package, dstdir) + -- 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}) + if result[dstdir] then + table.insert(result[dstdir], {srcfile, dstname}) + else + result[dstdir] = {{srcfile, dstname}} + end + end 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/install/mingw.lua b/xmake/rules/qt/install/mingw.lua index fb54c8e2c..1dc244c58 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,13 @@ 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!") - - -- 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}) + -- 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 new file mode 100644 index 000000000..d0404b8f6 --- /dev/null +++ b/xmake/rules/qt/install/windeployqt.lua @@ -0,0 +1,141 @@ +--!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") + +-- run windeployqt to deploy Qt dependencies +function run_deploy(target, bindir, installfiles) + -- get qt sdk + local qt = find_qt() + if not qt then + return + 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 + 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) + + -- 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 5a60de706..3ae05be35 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,13 @@ 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!") - - -- 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}) + -- 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 new file mode 100644 index 000000000..705ca9843 --- /dev/null +++ b/xmake/rules/qt/installcmd.lua @@ -0,0 +1,86 @@ +--!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 +-- + +-- imports +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 + 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 + 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 + if dep:rule("qt.shared") then + 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 deploydir + windeployqt.run_deploy(target, deploydir, installfiles) + + -- 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() + if bindir and os.isdir(bindir) then + local package_bindir = package:installdir("bin") + -- copy all files and directories from bindir + batchcmds:cp(path.join(bindir, "*"), package_bindir, {rootdir = bindir}) + end + end +end + diff --git a/xmake/rules/qt/uninstallcmd.lua b/xmake/rules/qt/uninstallcmd.lua new file mode 100644 index 000000000..cc989ce12 --- /dev/null +++ b/xmake/rules/qt/uninstallcmd.lua @@ -0,0 +1,93 @@ +--!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() + + -- 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 + 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 69c8ff03a..db13aa913 100644 --- a/xmake/rules/qt/xmake.lua +++ b/xmake/rules/qt/xmake.lua @@ -116,6 +116,10 @@ rule("qt.widgetapp") after_install("windows", "install.windows") after_install("mingw", "install.mingw") + -- install application for xpack + on_installcmd("installcmd") + on_uninstallcmd("uninstallcmd") + -- define rule: qt static widgetapp rule("qt.widgetapp_static") add_deps("qt.ui", "qt.moc", "qt._wasm_app", "qt.qrc", "qt.ts") @@ -144,6 +148,10 @@ rule("qt.widgetapp_static") after_install("windows", "install.windows") after_install("mingw", "install.mingw") + -- install application for xpack + on_installcmd("installcmd") + on_uninstallcmd("uninstallcmd") + -- define rule: qt quickapp rule("qt.quickapp") add_deps("qt.qrc", "qt.moc", "qt._wasm_app", "qt.ts") @@ -166,6 +174,10 @@ rule("qt.quickapp") after_install("windows", "install.windows") after_install("mingw", "install.mingw") + -- install application for xpack + on_installcmd("installcmd") + on_uninstallcmd("uninstallcmd") + -- define rule: qt static quickapp rule("qt.quickapp_static") add_deps("qt.qrc", "qt.moc", "qt._wasm_app", "qt.ts") @@ -190,6 +202,10 @@ rule("qt.quickapp_static") after_install("windows", "install.windows") after_install("mingw", "install.mingw") + -- install application for xpack + on_installcmd("installcmd") + on_uninstallcmd("uninstallcmd") + -- define rule: qt qmlplugin rule("qt.qmlplugin") add_deps("qt.shared", "qt.qmltyperegistrar", "qt.ts") |
