Skip to content

Improve toolchain configs, add_toolchains("name[configs]")#6924

Merged
waruqi merged 7 commits into
devfrom
toolchain
Oct 17, 2025
Merged

Improve toolchain configs, add_toolchains("name[configs]")#6924
waruqi merged 7 commits into
devfrom
toolchain

Conversation

@waruqi
Copy link
Copy Markdown
Member

@waruqi waruqi commented Oct 15, 2025

  • Fastly switch toolchain configs
    • set_toolchains("mingw[clang]"), it is equivalent to set_toolchains("mingw", {clang = true})
    • xmake f --toolchain=mingw[clang]
  • Add add_configs in toolchains to define and check toolchain configs
  • Add clang support for llvm-mingw toolchain
    • xmake f --toolchain=mingw[clang] --sdk=/xxx/llvm-mingw
    • set_toolchains("mingw[clang]@llvm-mingw")
  • Add gcc support for old version ndk toolchain
    • xmake f -p android --toolchain=ndk[gcc] --ndk=/xxxx

Simplify toolchain configs

1. only toolchain name
e.g. clang, gcc

2. toolchain@package
e.g. "clang@llvm-10", "@muslcc", zig

3. toolchain[configs]@package
e.g. "mingw[clang]@llvm-mingw", "msvc[vs=2025,..]"

@waruqi waruqi added this to the v3.0.5 milestone Oct 15, 2025
@waruqi waruqi changed the title Improve toolchain configs Improve toolchain configs, add_toolchains("name[configs]") Oct 15, 2025
@waruqi waruqi marked this pull request as ready for review October 16, 2025 03:59
@waruqi
Copy link
Copy Markdown
Member Author

waruqi commented Oct 16, 2025

/gemini review

Copy link
Copy Markdown
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 a significant improvement to toolchain configuration by allowing a more concise syntax, such as toolchain[config]. The changes are well-integrated into the core logic, affecting toolchain parsing, project loading, and platform handling. Support for clang in the llvm-mingw toolchain and gcc in the ndk toolchain are also valuable additions.

My review has identified a couple of bugs in the new configuration parsing logic within toolchain.parsename that could lead to incorrect behavior with complex config strings. I've provided a detailed comment with a suggested fix. Additionally, I've pointed out an opportunity to refactor duplicated code in toolchain.load and toolchain.load_withinfo to improve maintainability. With these adjustments, this will be a solid and robust enhancement.

Comment on lines +630 to +652
if toolchain_name_raw and configs_str then
configs_str = configs_str:gsub("%[(.*)%]", function (w)
return w:replace(",", ":")
end)
requirestr = configs_str
toolchain_name = toolchain_name_raw
local splitinfo = configs_str:split(",", {plain = true})
for _, v in ipairs(splitinfo) do
local parts = v:split("=", {plain = true})
local k = parts[1]
v = parts[2]
requireconfs = requireconfs or {}
if v then
if v:find(":", 1 ,true) then
requireconfs[k] = v:split(":", {plain = true})
else
requireconfs[k] = option.boolean(v)
end
else
requireconfs[k] = true
end
end
end
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

There are a couple of issues in the parsing logic for toolchain configs:

  1. The gsub pattern on line 631 is greedy. "%[(.*)%]" will cause incorrect parsing if there are multiple [...] sections in the config string. For example, with toolchain[config1=[a,b],config2=[c,d]], the pattern will match a,b],config2=[c,d. You should use a non-greedy pattern like "%[([^]]*)%]" to correctly handle this.

  2. When parsing a list-like value (e.g., sdk=[1,2]), the code on line 644 splits a string like "[1:2]" by the colon, which results in {"[1", "2]"}. The surrounding brackets should be removed before splitting the string. You could use v:sub(2, -2) to strip them.

Here's a suggested implementation that addresses both issues:

        if toolchain_name_raw and configs_str then
            configs_str = configs_str:gsub("%%[([^\]]*)%%]", function (w)
                return w:replace(",", ":")
            end)
            requirestr = configs_str
            toolchain_name = toolchain_name_raw
            local splitinfo = configs_str:split(",", {plain = true})
            for _, v in ipairs(splitinfo) do
                local parts = v:split("=", {plain = true})
                local k = parts[1]
                v = parts[2]
                requireconfs = requireconfs or {}
                if v then
                    if v:find(":", 1 ,true) then
                        if v:startswith("[") and v:endswith("]") then
                            v = v:sub(2, -2)
                        end
                        requireconfs[k] = v:split(":", {plain = true})
                    else
                        requireconfs[k] = option.boolean(v)
                    end
                else
                    requireconfs[k] = true
                end
            end
        end

Comment on lines +712 to +721
-- parse toolchain name
local parseinfo = toolchain.parsename(name)
name = parseinfo.name

-- init configs
local configs = parseinfo.requireconfs or {}
table.join2(configs, opt)
configs.packages = opt.packages or parseinfo.packages
configs.plat = opt.plat or config.get("plat") or os.host()
configs.arch = opt.arch or config.get("arch") or os.arch()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The logic for parsing the toolchain name and initializing the configs table is duplicated here and in toolchain.load_withinfo (lines 777-786). To improve maintainability and reduce redundancy, you could extract this common logic into a private helper function.

For example, you could create a function toolchain._prepare_load_params(name, opt):

function toolchain._prepare_load_params(name, opt)
    opt = opt or {}
    local parseinfo = toolchain.parsename(name)
    local name = parseinfo.name
    local configs = parseinfo.requireconfs or {}
    table.join2(configs, opt)
    configs.packages = opt.packages or parseinfo.packages
    configs.plat = opt.plat or config.get("plat") or os.host()
    configs.arch = opt.arch or config.get("arch") or os.arch()
    return name, configs, parseinfo
end

Then both toolchain.load and toolchain.load_withinfo can be simplified by calling this helper.

@waruqi waruqi merged commit 3dabade into dev Oct 17, 2025
44 of 45 checks passed
@waruqi waruqi deleted the toolchain branch October 17, 2025 01:42
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.

1 participant