Skip to content

xpack 在 linux 上打包 qt.widgetapp 时把 /usr/local/bin 拷进了安装包 #7771

Description

@space-axel

Xmake 版本

v3.1.1+HEAD.b27302bb0

操作系统版本和架构

ubuntu-24.04.5 LTS(GitHub Actions runner),x64 与 arm64 都能复现,gcc 编译;Qt 5.15.2(来自 qt 包)

描述问题

在 linux 上 xmake pack 直接失败:

error: cannot copy file /usr/local/bin/now to build/.xpack/<pkg>/zip/installed/zip/bin/now, No such file or directory

报这个错之前,xmake 已经把机器上整个 /usr/local/bin 拷进了安装包的 bin/ 目录。

原因在 xmake/rules/qt/installcmd.lua 第 83 行起的 linux 分支:

    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})
            ...

target:bindir() 返回的是安装目录,不是构建输出目录。它内部调用 target:installdir("bin"),而 installdir 会回落到 platform.get("installdir")——xmake/platforms/linux/xmake.lua:31 里设的是 /usr/local,所以 xmake pack 期间它恒等于 /usr/local/bin。正常机器上 os.isdir() 一定为真,于是系统目录被当成构建产物打进了包里。

windows 分支以前有同样的问题,已在 3f1fd97fix installdir for pack qt)里改用 package:installdir("bin") 修掉,linux 分支漏了。

另外 xmake/rules/qt/uninstallcmd.lua:77 也用了 target:bindir()(仅作判断用),而它下面会把整个包内 bin/ 目录清空,也就是连带删掉包里其它目标安装的文件。


On linux, xmake pack fails directly:

error: cannot copy file /usr/local/bin/now to build/.xpack/<pkg>/zip/installed/zip/bin/now, No such file or directory

Before hitting that error, xmake has already copied the machine's whole /usr/local/bin into the package's bin/ directory.

The cause is the linux branch of xmake/rules/qt/installcmd.lua (line 83):

    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})
            ...

target:bindir() is the install directory, not the build output directory. It calls target:installdir("bin"), and installdir falls back to platform.get("installdir")xmake/platforms/linux/xmake.lua:31 sets /usr/local, so during xmake pack it always resolves to /usr/local/bin. os.isdir() is true on every normal machine, so the system directory gets packaged as if it were the build output.

The windows branch had exactly the same problem and was fixed in 3f1fd97 (fix installdir for pack qt) by switching to package:installdir("bin"); the linux branch was left with target:bindir().

Also, xmake/rules/qt/uninstallcmd.lua:77 has the same target:bindir() check (used only as a guard), and its body clears the whole package bin/ directory, i.e. it also removes the files installed by the other targets of the package.

期待的结果

包里 bin/ 应该是应用可执行文件、lib/ 是它依赖的共享库,与 windows/mingw、macosx 的行为一致。

我想到的改法是:

     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})
-            
-            -- install shared libraries
-            local package_libdir = package:installdir("lib")
-            pack_batchcmds.install_target_shared_libraries(target, batchcmds, {bindir = package_libdir, package = package})
-        end
+        -- Linux: install the target binary
+        local package_bindir = package:installdir("bin")
+        batchcmds:mkdir(package_bindir)
+        batchcmds:cp(target:targetfile(), path.join(package_bindir, target:filename()))
+
+        -- install shared libraries
+        local package_libdir = package:installdir("lib")
+        pack_batchcmds.install_target_shared_libraries(target, batchcmds, {bindir = package_libdir, package = package})
     end

不过这样会改变已有的行为:原来是把这个目录下的其它文件(插件、翻译等)也一并装进包里的,改后就不再安装了。所以想先问下你的意见——linux 下你更倾向哪种?

  1. 像 windows 分支那样,只装可执行文件 + 共享库(上面的 diff);
  2. 保留"复制构建输出目录"的原意,改用 target:targetdir()(那样会把该构建目录下其它所有目标的产物也一起拖进包里)。

你定,我按你的意见提 PR。


The package should contain the application executable in bin/ and its shared libraries in lib/, as it does on windows/mingw and macosx.

The change I have in mind:

     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})
-            
-            -- install shared libraries
-            local package_libdir = package:installdir("lib")
-            pack_batchcmds.install_target_shared_libraries(target, batchcmds, {bindir = package_libdir, package = package})
-        end
+        -- Linux: install the target binary
+        local package_bindir = package:installdir("bin")
+        batchcmds:mkdir(package_bindir)
+        batchcmds:cp(target:targetfile(), path.join(package_bindir, target:filename()))
+
+        -- install shared libraries
+        local package_libdir = package:installdir("lib")
+        pack_batchcmds.install_target_shared_libraries(target, batchcmds, {bindir = package_libdir, package = package})
     end

But this changes the existing behaviour: the other files in that directory (plugins, translations, etc.) would no longer be installed into the package. So I'd like to know which behaviour you prefer for linux:

  1. install only the executable and the shared libraries, like the windows branch does (the diff above);
  2. keep the "copy the build output directory" intent by using target:targetdir() (which also pulls every other target's artifacts of that build directory into the package).

Happy to send a PR with whichever you think is right.

工程配置

本仓库里的测试工程就是同样的结构,直接用它就能复现(需要本机有 Qt SDK):

$ cd tests/plugins/pack/qtapp
$ xmake pack -f targz

等价的 xmake.lua:

includes("@builtin/xpack")

target("qtapp")
    add_rules("qt.widgetapp")
    add_files("src/*.cpp")

xpack("qtapp")
    set_formats("zip", "targz")
    add_targets("qtapp")

The test project in this repo has the same structure and should reproduce it directly (a Qt SDK is required):

$ cd tests/plugins/pack/qtapp
$ xmake pack -f targz

The equivalent xmake.lua:

includes("@builtin/xpack")

target("qtapp")
    add_rules("qt.widgetapp")
    add_files("src/*.cpp")

xpack("qtapp")
    set_formats("zip", "targz")
    add_targets("qtapp")

附加信息和错误日志

我们项目(space-ast/ast,一个含多个 qt.widgetapp 目标的工程)的 GitHub Actions 上,ubuntu-24.04 x64 与 arm64 都稳定复现。CI 中的命令是:

$ xmake f -cy --with_test=y -m release
$ xmake pack -f zip -o artifacts/ --basename=SpaceAST

失败片段:

[100%]: build ok, spent 344.438s
...
packing artifacts/SpaceAST.zip ..
error: cannot copy file /usr/local/bin/now to build/.xpack/ast/zip/installed/zip/bin/now, No such file or directory

/usr/local/bin/now 是 runner 镜像里的一个悬空软链,所以拷到它时报错;报错前系统目录里其它文件已经被拷进包里了。)

完整日志:https://github.com/space-ast/ast/actions/runs/35091960580

Windows/mingw 不会复现,因为走的是 windeployqt 那条分支;macosx 也不受影响。


On GitHub Actions for our project (space-ast/ast, which contains several qt.widgetapp targets), it reproduces reliably on both ubuntu-24.04 x64 and arm64. The commands are:

$ xmake f -cy --with_test=y -m release
$ xmake pack -f zip -o artifacts/ --basename=SpaceAST

The failing part:

[100%]: build ok, spent 344.438s
...
packing artifacts/SpaceAST.zip ..
error: cannot copy file /usr/local/bin/now to build/.xpack/ast/zip/installed/zip/bin/now, No such file or directory

(/usr/local/bin/now is a dangling symlink in the runner image, that's why copying it fails; the other files of the system directory have already been copied into the package before the error.)

Full log: https://github.com/space-ast/ast/actions/runs/35091960580

Windows/mingw is not affected because it goes through the windeployqt branch; macosx is not affected either.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions