@@ -47,6 +47,90 @@ function _translate_local_path_in_deps(cargotoml, rootdir)
4747 io .writefile (cargotoml , content )
4848end
4949
50+ -- is it a cargo workspace manifest? (it contains a [workspace] or [workspace.xxx] table)
51+ function _is_workspace_manifest (content )
52+ for _ , line in ipairs (content :split (" \n " , {plain = true , strict = true })) do
53+ if line :match (" ^%s*%[workspace[%].]" ) then
54+ return true
55+ end
56+ end
57+ return false
58+ end
59+
60+ -- get the workspace inheritance tables from the workspace root manifest
61+ --
62+ -- if the given Cargo.toml is a member of a cargo workspace and uses workspace inheritance,
63+ -- e.g. `anyhow.workspace = true`, `version.workspace = true`, `[lints] workspace = true`,
64+ -- we need to inject the `[workspace.package]`/`[workspace.dependencies]`/`[workspace.lints]`
65+ -- tables from the workspace root manifest, otherwise cargo will fail to resolve them, e.g.
66+ --
67+ -- error inheriting `anyhow` from workspace root manifest's `workspace.dependencies.anyhow`
68+ -- `workspace.dependencies` was not defined
69+ --
70+ -- @see https://github.com/xmake-io/xmake/issues/7619
71+ -- https://doc.rust-lang.org/cargo/reference/workspaces.html#the-workspacedependencies-table
72+ function _get_workspace_inherited_tables (cargo_toml )
73+
74+ -- find the workspace root manifest by walking up
75+ local rootmanifest
76+ local dir = path .directory (path .absolute (cargo_toml ))
77+ while dir and # dir > 0 do
78+ local manifest = path .join (dir , " Cargo.toml" )
79+ if os .isfile (manifest ) then
80+ local content = io .readfile (manifest )
81+ if content and _is_workspace_manifest (content ) then
82+ rootmanifest = manifest
83+ break
84+ end
85+ end
86+ local parentdir = path .directory (dir )
87+ if parentdir == dir then
88+ break
89+ end
90+ dir = parentdir
91+ end
92+
93+ -- not a workspace member, or the manifest itself is the workspace root (self-contained)
94+ if not rootmanifest or path .absolute (rootmanifest ) == path .absolute (cargo_toml ) then
95+ return
96+ end
97+
98+ -- extract all the [workspace.xxx] sub-tables, e.g. [workspace.package]/[workspace.dependencies]/[workspace.lints]
99+ -- we do not copy the bare [workspace] table (members/exclude), otherwise cargo will search for the member crates.
100+ local content = io .readfile (rootmanifest )
101+ if not content then
102+ return
103+ end
104+ local result = {}
105+ local in_subtable = false
106+ for _ , line in ipairs (content :split (" \n " , {plain = true , strict = true })) do
107+ local header = line :match (" ^%s*%[(.-)%]%s*$" )
108+ if header then
109+ in_subtable = header :trim ():startswith (" workspace." )
110+ if in_subtable then
111+ table.insert (result , line )
112+ end
113+ elseif in_subtable then
114+ table.insert (result , line )
115+ end
116+ end
117+ if # result == 0 then
118+ return
119+ end
120+
121+ -- translate the local paths in the workspace dependencies, they are relative to the workspace root directory
122+ local rootdir = path .directory (rootmanifest )
123+ local workspace_toml = table.concat (result , " \n " )
124+ workspace_toml = workspace_toml :gsub (" path%s+=%s+\" (.-)\" " , function (localpath )
125+ if not path .is_absolute (localpath ) then
126+ localpath = path .absolute (localpath , rootdir )
127+ end
128+ localpath = localpath :gsub (" \\ " , " /" )
129+ return " path = \" " .. localpath .. " \" "
130+ end )
131+ return workspace_toml
132+ end
133+
50134-- install package
51135--
52136-- e.g.
@@ -101,6 +185,13 @@ function main(name, opt)
101185 tomlfile :print (" " )
102186 tomlfile :print (" [workspace]" )
103187 tomlfile :print (" " )
188+ -- inject the workspace inheritance tables if the given Cargo.toml is a workspace member,
189+ -- e.g. `anyhow.workspace = true`. @see https://github.com/xmake-io/xmake/issues/7619
190+ local workspace_toml = _get_workspace_inherited_tables (configs .cargo_toml )
191+ if workspace_toml then
192+ tomlfile :write (workspace_toml )
193+ tomlfile :print (" " )
194+ end
104195 tomlfile :close ()
105196 else
106197 local tomlfile = io.open (cargotoml , " w" )
0 commit comments