Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions xmake/modules/package/manager/cargo/install_package.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,90 @@ function _translate_local_path_in_deps(cargotoml, rootdir)
io.writefile(cargotoml, content)
end

-- is it a cargo workspace manifest? (it contains a [workspace] or [workspace.xxx] table)
function _is_workspace_manifest(content)
for _, line in ipairs(content:split("\n", {plain = true, strict = true})) do
if line:match("^%s*%[workspace[%].]") then
return true
end
end
return false
end
Comment on lines +51 to +58

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

Splitting the entire file content into lines just to check if it contains a workspace pattern is inefficient and can consume unnecessary memory for larger files. We can use Lua's pattern matching directly on the file content to check for the pattern at the start of the string or after a newline.

function _is_workspace_manifest(content)
    return content:match("^%s*%[workspace[%].]") ~= nil or content:match("\n%s*%[workspace[%].]") ~= nil
end


-- get the workspace inheritance tables from the workspace root manifest
--
-- if the given Cargo.toml is a member of a cargo workspace and uses workspace inheritance,
-- e.g. `anyhow.workspace = true`, `version.workspace = true`, `[lints] workspace = true`,
-- we need to inject the `[workspace.package]`/`[workspace.dependencies]`/`[workspace.lints]`
-- tables from the workspace root manifest, otherwise cargo will fail to resolve them, e.g.
--
-- error inheriting `anyhow` from workspace root manifest's `workspace.dependencies.anyhow`
-- `workspace.dependencies` was not defined
--
-- @see https://github.com/xmake-io/xmake/issues/7619
-- https://doc.rust-lang.org/cargo/reference/workspaces.html#the-workspacedependencies-table
function _get_workspace_inherited_tables(cargo_toml)

-- find the workspace root manifest by walking up
local rootmanifest
local dir = path.directory(path.absolute(cargo_toml))
while dir and #dir > 0 do
local manifest = path.join(dir, "Cargo.toml")
if os.isfile(manifest) then
local content = io.readfile(manifest)
if content and _is_workspace_manifest(content) then
rootmanifest = manifest
break
end
end
local parentdir = path.directory(dir)
if parentdir == dir then
break
end
dir = parentdir
end

-- not a workspace member, or the manifest itself is the workspace root (self-contained)
if not rootmanifest or path.absolute(rootmanifest) == path.absolute(cargo_toml) then
return
end

-- extract all the [workspace.xxx] sub-tables, e.g. [workspace.package]/[workspace.dependencies]/[workspace.lints]
-- we do not copy the bare [workspace] table (members/exclude), otherwise cargo will search for the member crates.
local content = io.readfile(rootmanifest)
if not content then
return
end
local result = {}
local in_subtable = false
for _, line in ipairs(content:split("\n", {plain = true, strict = true})) do
local header = line:match("^%s*%[(.-)%]%s*$")
if header then
in_subtable = header:trim():startswith("workspace.")
if in_subtable then
table.insert(result, line)
end
elseif in_subtable then
table.insert(result, line)
end
end
if #result == 0 then
return
end

-- translate the local paths in the workspace dependencies, they are relative to the workspace root directory
local rootdir = path.directory(rootmanifest)
local workspace_toml = table.concat(result, "\n")
workspace_toml = workspace_toml:gsub("path%s+=%s+\"(.-)\"", function (localpath)

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

Using %s+ requires at least one space before and after the = operator. In TOML, it is perfectly valid to have no spaces (e.g., path="...") or multiple spaces. Using %s* instead of %s+ makes the pattern matching much more robust.

    workspace_toml = workspace_toml:gsub("path%s*=%s*\"(.-)\"", function (localpath)

if not path.is_absolute(localpath) then
localpath = path.absolute(localpath, rootdir)
end
localpath = localpath:gsub("\\", "/")
return "path = \"" .. localpath .. "\""
end)
return workspace_toml
end

-- install package
--
-- e.g.
Expand Down Expand Up @@ -101,6 +185,13 @@ function main(name, opt)
tomlfile:print("")
tomlfile:print("[workspace]")
tomlfile:print("")
-- inject the workspace inheritance tables if the given Cargo.toml is a workspace member,
-- e.g. `anyhow.workspace = true`. @see https://github.com/xmake-io/xmake/issues/7619
local workspace_toml = _get_workspace_inherited_tables(configs.cargo_toml)
if workspace_toml then
tomlfile:write(workspace_toml)
tomlfile:print("")
end
Comment on lines +191 to +194

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

Instead of calling tomlfile:write followed by tomlfile:print(""), you can directly use tomlfile:print(workspace_toml). This is cleaner, more idiomatic, and automatically appends the trailing newline.

        if workspace_toml then
            tomlfile:print(workspace_toml)
        end

tomlfile:close()
else
local tomlfile = io.open(cargotoml, "w")
Expand Down
Loading