Skip to content

Conversation

RubMaker
Copy link

  • Before adding new features and new modules, please go to issues to submit the relevant feature description first.
  • Write good commit messages and use the same coding conventions as the rest of the project.
  • Please commit code to dev branch and we will merge into master branch in feature
  • Ensure your edited codes with four spaces instead of TAB.

  • 增加新特性和新模块之前,请先到issues提交相关特性说明,经过讨论评估确认后,再进行相应的代码提交,避免做无用工作。
  • 编写友好可读的提交信息,并使用与工程代码相同的代码规范,代码请用4个空格字符代替tab缩进。
  • 请提交代码到dev分支,如果通过,我们会在特定时间合并到master分支上。
  • 为了规范化提交日志的格式,commit消息,不要用中文,请用英文描述。

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @RubMaker, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the packaging capabilities by introducing support for creating macOS DMG packages. It includes robust logic to identify Qt projects and automatically bundle their dependencies, ensuring that the generated DMGs are self-contained and ready for distribution. Additionally, it provides extensive configuration options for customizing the visual layout and properties of the DMG, making the packaging process more flexible and user-friendly.

Highlights

  • DMG Packaging Support: Introduced comprehensive support for creating macOS DMG (Disk Image) packages for applications.
  • Qt Project Integration: Added intelligent detection for Qt projects and automatic bundling of Qt dependencies using macdeployqt to ensure self-contained application bundles.
  • Customizable DMG Properties: Enabled extensive customization of DMG appearance and behavior, including window position and size, icon size, application and icon positions, and background images.
  • New xpack APIs: Expanded the xpack API with new functions to configure DMG-specific settings (set_dmg_window_pos, set_dmg_window_size, set_dmg_icon_size, set_dmg_applications_pos, set_dmg_icon_position) and AppImage settings (set_iconname, set_appimage_tool).
  • Packaging Type Updates: Updated xpack's internal mappings to correctly classify appimage and dmg as 'binary' input/output kinds and assign their respective file extensions (.AppImage, .dmg).
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for creating DMG packages on macOS, including specific handling for Qt applications. The implementation is quite comprehensive, with logic to find necessary tools, detect Qt projects, and bundle dependencies using macdeployqt. My review focuses on improving the robustness of the Qt dependency handling and fixing a minor issue with variable scope. Overall, this is a great addition.

if is_qt then
local macdeployqt = _get_macdeployqt()
if macdeployqt then
local qt_success = _deploy_qt_dependencies(package, app_source, macdeployqt)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The return value qt_success from _deploy_qt_dependencies is ignored. If deploying Qt dependencies fails, the packaging process continues, which will likely result in a non-functional application in the final DMG. The result of this operation should be checked to ensure the integrity of the package. You might consider failing the packaging process if this step is unsuccessful.

            local qt_success = _deploy_qt_dependencies(package, app_source, macdeployqt)
            if not qt_success then
                print("Warning: Failed to deploy Qt dependencies, the application may not run correctly.")
            end

Comment on lines 314 to 326
if success then
local otool_output = os.iorunv("otool", {"-L", main_executable})
if otool_output then
local external_qt_refs = {}
for line in otool_output:gmatch("[^\r\n]+") do
-- Look for Qt framework references that are not in the bundle
local qt_ref = line:match("(%S*Qt%w+%.framework[^%s]*)")
if qt_ref and not qt_ref:find("@executable_path") and not qt_ref:find("@rpath") then
table.insert(external_qt_refs, qt_ref)
end
end
end
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This final verification step using otool appears to be incomplete. The external_qt_refs table is populated with any found external Qt framework references, but it's never used. This could lead to silently producing a broken package if macdeployqt fails to correctly bundle all dependencies. You should use this table to validate the bundling process, for instance by checking if it's non-empty and printing a warning.

Additionally, there are several unused local variables in this function (qt_version on line 210, all_frameworks on line 294, plugin_files on line 304) that could be removed for better code clarity.


for _, bg_path in ipairs(bg_paths) do
if os.isfile(bg_path) then
ab_bg_path = path.absolute(bg_path)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The variable ab_bg_path is assigned without being declared with local. This makes it an implicit global variable, which can lead to subtle bugs and pollutes the global namespace. It should be declared as a local variable.

            local ab_bg_path = path.absolute(bg_path)

end

-- Method 2: Check executable for Qt dependencies using otool
local app_source, _ = _find_app_bundle(package)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

, _ 删了

local executables = os.files(path.join(macos_dir, "*"))
for _, executable in ipairs(executables) do
if os.isfile(executable) then
local otool_output = os.iorunv("otool", {"-L", executable})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

用 utils.binary.deplibs 模块就能获取到加载依赖。不要直接使用 otool

https://github.com/xmake-io/xmake/blob/dev/xmake/modules/utils/binary/deplibs.lua

end

-- Method 3: Check source files for Qt headers/includes
local srcfiles, _ = package:sourcefiles()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

, _

local executables = os.files(path.join(macos_dir, "*"))
for _, executable in ipairs(executables) do
if os.isfile(executable) then
local otool_output = os.iorunv("otool", {"-L", executable})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

还有这

local executables = os.files(path.join(macos_dir, "*"))
for _, executable in ipairs(executables) do
if os.isfile(executable) then
local otool_output = os.iorunv("otool", {"-L", executable})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

用 utils.binary.deplibs 模块就能获取到加载依赖。不要直接使用 otool

https://github.com/xmake-io/xmake/blob/dev/xmake/modules/utils/binary/deplibs.lua

-- set appimage icon name
"xpack.set_iconname",
-- set appimage tool
"xpack.set_appimage_tool",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

appimage 放到 appimage 的 pr 里面去,这里 dmg 不需要,删了

local executables = os.files(path.join(macos_dir, "*"))
for _, executable in ipairs(executables) do
if os.isfile(executable) then
local otool_output = os.iorunv("otool", {"-L", executable})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

还有这

end

-- Method 3: Check for .qml files in project
local qml_files = os.files("**.qml")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为啥要执行这个,递归这么搞,层级多的时候,会相当慢,甚至卡住。

如果要检测目录有没有 qml 文件,用 lib.detect.find_file 接口,只要找到一个文件,就会返回,而不是遍历所有

end

-- Method 3: Check source files for Qt headers/includes
local srcfiles, _ = package:sourcefiles()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

, _

end

-- detect if this is a Qt project
function _is_qt_project(package)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为什么要通过这种方式探测?这会非常慢,而且也不准。。

如果是 qt 工程,肯定会 apply qt 相关的 rules. 所以只需要 检测 target:data("qt") 就行了,或者检测 target:rule 是否能获取到 qt env 的 rule

target:data_set("qt", qt)

end

function _check_qml_usage(package, app_source)
-- Method 1: Check for QML-related libraries in links
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里也是,怪怪的,不应该是直接检测 qt qml 相关的 rule 是否存在不就好了么。

rule("qt.qmlplugin")


-- create dmg
local success = _create_dmg_with_create_dmg(create_dmg, package, staging_dir, dmg_file, appbundle_name, bg_image)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

空行去了

srpm = "source",
rpm = "source"
rpm = "source",
appimage = "binary",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

appimage 相关删了

srpm = "source",
rpm = "binary"
rpm = "binary",
appimage = "binary",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@waruqi
Copy link
Member

waruqi commented Sep 19, 2025

同样的,先把 qt 支持删,做最基础的 dmg 支持,然后先 merge 进来再说。

qt 的支持最后弄,参考 #6826 (comment)

opt = opt or {}

-- find program from system PATH
local program = find_program(opt.program or "macdeployqt")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个 qt 的也先删了

if os.isfile(info_plist) and os.isdir(macos_dir) then
return abs_location, appbundle_name
else
print(" Invalid .app structure")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

所有的错误日志,都用 raise

local staging_dir = path.join(os.tmpdir(), package:name() .. "_dmg_staging")
-- clean and create staging directory
if os.isdir(staging_dir) then
os.vrunv("rm", {"-rf", staging_dir})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

删除目录,为啥还要执行命令,os.rm 不就可以删么

local bg_dest = path.join(staging_dir, path.filename(bg_image))
os.vcp(bg_image, bg_dest)
if not os.isfile(bg_dest) then
print("Warning: Failed to copy background image")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

用 wprint

table.insert(args, staging_dir)
-- ensure output directory
os.vrunv("mkdir", {"-p", path.directory(dmg_file)})
os.vrunv("rm", {"-f", dmg_file})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为啥不用 os.mkdir 和 os.rm

return true
else
if errors then
print("Error output:", errors)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

os.iorunv 运行失败,原本就会抛异常,这里全删了

local app_source, appbundle_name = _find_app_bundle(package)
if not app_source then
return false
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

适当换行下,不要全挤在一起

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants