Skip to content

Commit 715b176

Browse files
authored
Merge pull request #6421 from Arthapz/improve-libstdc++-module
(C++ modules support) improve std module support for gcc and clang
2 parents 9ecfe9b + 730ec08 commit 715b176

File tree

9 files changed

+114
-102
lines changed

9 files changed

+114
-102
lines changed

tests/projects/c++/modules/stdmodules_deps/src/bar.mpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ import std;
44

55
export auto my_sum2(std::size_t a, std::size_t b) -> std::size_t;
66

7+
#if defined(__GNUC__) && !defined(__clang__)
8+
inline
9+
#else
710
module :private;
8-
9-
auto my_sum2(std::size_t a, std::size_t b) -> std::size_t {
11+
#endif
12+
auto my_sum2(std::size_t a, std::size_t b) -> std::size_t {
1013
return a + a + b + b;
1114
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module foo;
2+
3+
auto my_sum(std::size_t a, std::size_t b) -> std::size_t { return a + b; }

tests/projects/c++/modules/stdmodules_deps/src/foo.mpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,3 @@ export module foo;
33
import std;
44

55
export auto my_sum(std::size_t a, std::size_t b) -> std::size_t;
6-
7-
module :private;
8-
9-
auto my_sum(std::size_t a, std::size_t b) -> std::size_t {
10-
return a + b;
11-
}

tests/projects/c++/modules/stdmodules_deps/xmake.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ set_languages("c++latest")
33

44
target("foo")
55
set_kind("static")
6+
add_files("src/foo.cpp")
67
add_files("src/foo.mpp", {public = true})
78

89
target("bar")

tests/projects/c++/modules/test_stdmodules.lua

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,25 @@ function main(t)
3838
end
3939
end
4040
elseif is_subhost("msys") then
41-
-- os.exec("xmake f -c -p mingw --yes")
42-
-- _build()
41+
local gcc = find_tool("gcc", {version = true})
42+
if is_host("linux") and gcc and gcc.version and semver.compare(gcc.version, "15.0") >= 0 then
43+
os.exec("xmake f -c -p mingw --yes")
44+
_build()
45+
end
4346
elseif is_host("linux") then -- or is_host("macosx") then
44-
-- gcc don't support std modules atm
45-
-- local gcc = find_tool("gcc", {version = true})
46-
-- if is_host("linux") and gcc and gcc.version and semver.compare(gcc.version, "11.0") >= 0 then
47-
-- os.exec("xmake f -c --yes")
48-
-- _build()
49-
-- end
47+
local gcc = find_tool("gcc", {version = true})
48+
if is_host("linux") and gcc and gcc.version and semver.compare(gcc.version, "15.0") >= 0 then
49+
os.exec("xmake f -c --yes")
50+
_build()
51+
end
5052
local clang = find_tool("clang", {version = true})
5153
if clang and clang.version and semver.compare(clang.version, "19.0") >= 0 then
52-
-- clang don't support libstdc++ std modules atm
53-
-- os.exec("xmake clean -a")
54-
-- os.exec("xmake f --toolchain=clang -c --yes")
55-
-- _build()
54+
local gcc = find_tool("clang", {version = true})
55+
if gcc and gcc.version and semver.compare(gcc.version, "15.0") >= 0 then
56+
os.exec("xmake clean -a")
57+
os.exec("xmake f --toolchain=clang -c --yes")
58+
_build()
59+
end
5660
os.exec("xmake clean -a")
5761
os.exec("xmake f --toolchain=clang --runtimes=c++_shared -c --yes")
5862
_build()

xmake/core/project/policy.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ function policy.policies()
101101
-- If in the future, gcc can support it well, we'll turn it on by default
102102
-- https://github.com/xmake-io/xmake/issues/3855
103103
["build.c++.modules.gcc.cxx11abi"] = {description = "Force to enable new cxx11 abi in C++ modules for gcc.", type = "boolean"},
104-
["build.c++.gcc.modules.cxx11abi"] = {description = "Force to enable new cxx11 abi in C++ modules for gcc. (deprecated)", type = "boolean", default = false},
104+
["build.c++.gcc.modules.cxx11abi"] = {description = "Force to enable new cxx11 abi in C++ modules for gcc. (deprecated)", type = "boolean"},
105105
-- Set the default vs runtime, e.g. MT, MD
106106
["build.c++.msvc.runtime"] = {description = "Set the default vs runtime.", type = "string", values = {"MT", "MD"}},
107107
-- Enable cuda device link

xmake/rules/c++/modules/clang/support.lua

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ function _get_cpplibrary_name(target)
7171
elseif target:has_runtime("MD", "MT", "MDd", "MTd") then
7272
return "msstl"
7373
end
74-
if target:is_plat("macosx") then
74+
if target:is_plat("macosx", "iphoneos", "appletvos") then
7575
return "c++"
7676
elseif target:is_plat("linux") then
7777
return "stdc++"
@@ -258,49 +258,42 @@ end
258258

259259
function get_stdmodules(target)
260260

261-
if target:policy("build.c++.modules.std") then
262-
local cpplib = _get_cpplibrary_name(target)
263-
if cpplib then
264-
if cpplib == "c++" then
265-
-- libc++ module is found by parsing libc++.modules.json
266-
local modules_json_path = _get_std_module_manifest_path(target)
267-
if modules_json_path then
268-
local modules_json = json.decode(io.readfile(modules_json_path))
269-
if modules_json and modules_json.modules and #modules_json.modules > 0 then
270-
local std_module_directory = path.directory(modules_json.modules[1]["source-path"])
271-
if not path.is_absolute(std_module_directory) then
272-
std_module_directory = path.join(path.directory(modules_json_path), std_module_directory)
273-
end
274-
if os.isdir(std_module_directory) then
275-
return {path.normalize(path.join(std_module_directory, "std.cppm")), path.normalize(path.join(std_module_directory, "std.compat.cppm"))}
276-
end
261+
if not target:policy("build.c++.modules.std") then
262+
return
263+
end
264+
local cpplib = _get_cpplibrary_name(target)
265+
if cpplib then
266+
if cpplib == "c++" then
267+
-- libc++ module is found by parsing libc++.modules.json
268+
local modules_json_path = _get_std_module_manifest_path(target)
269+
if modules_json_path then
270+
local modules_json = json.decode(io.readfile(modules_json_path))
271+
if modules_json and modules_json.modules and #modules_json.modules > 0 then
272+
local std_module_directory = path.directory(modules_json.modules[1]["source-path"])
273+
if not path.is_absolute(std_module_directory) then
274+
std_module_directory = path.join(path.directory(modules_json_path), std_module_directory)
277275
end
278-
end
279-
elseif cpplib == "stdc++" then
280-
-- libstdc++ doesn't have a std module file atm
281-
elseif cpplib == "msstl" then
282-
-- msstl std module file is not compatible with llvm < 19
283-
local clang_version = get_clang_version(target)
284-
if clang_version and semver.compare(clang_version, "19.0") >= 0 then
285-
local toolchain = target:toolchain("llvm") or target:toolchain("clang") or target:toolchain("clang-cl")
286-
local msvc = import("core.tool.toolchain", {anonymous = true}).load("msvc", {plat = toolchain:plat(), arch = toolchain:arch()})
287-
if msvc and msvc:check({ignore_sdk = true}) then
288-
local vcvars = msvc:config("vcvars")
289-
if vcvars.VCInstallDir and vcvars.VCToolsVersion then
290-
local stdmodulesdir = path.join(vcvars.VCInstallDir, "Tools", "MSVC", vcvars.VCToolsVersion, "modules")
291-
if os.isdir(stdmodulesdir) then
292-
return {path.normalize(path.join(stdmodulesdir, "std.ixx")), path.normalize(path.join(stdmodulesdir, "std.compat.ixx"))}
293-
end
294-
end
276+
if os.isdir(std_module_directory) then
277+
return {path.normalize(path.join(std_module_directory, "std.cppm")), path.normalize(path.join(std_module_directory, "std.compat.cppm"))}
295278
end
296-
else
297-
wprint("msstl std module file is not compatible with llvm < 19, please upgrade clang/clang-cl version!")
298-
return
299279
end
300280
end
281+
elseif cpplib == "stdc++" then
282+
-- dont be greedy and don't enable stdc++ std module support for llvm < 19
283+
local clang_version = get_clang_version(target)
284+
if clang_version and semver.compare(clang_version, "19.0") >= 0 then
285+
return import(".gcc.support").get_stdmodules(target)
286+
end
287+
elseif cpplib == "msstl" then
288+
-- msstl std module file is not compatible with llvm < 19
289+
local clang_version = get_clang_version(target)
290+
if clang_version and semver.compare(clang_version, "19.0") >= 0 then
291+
local toolchain = target:toolchain("llvm") or target:toolchain("clang") or target:toolchain("clang-cl")
292+
return import(".msvc.support").get_stdmodules(target, {toolchain = toolchain})
293+
end
301294
end
302-
wprint("std and std.compat modules not found! maybe try to add --sdk=<PATH/TO/LLVM> or install libc++")
303295
end
296+
wprint("std and std.compat modules not found! maybe try to add --sdk=<PATH/TO/LLVM> or install libc++")
304297
end
305298

306299
function get_bmi_extension()

xmake/rules/c++/modules/gcc/support.lua

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ function load(target)
6161
-- https://github.com/xmake-io/xmake/issues/3855
6262
local cxx11abi = target:policy("build.c++.modules.gcc.cxx11abi") or
6363
target:policy("build.c++.gcc.modules.cxx11abi")
64+
if cxx11abi == nil then
65+
-- enable cxx11abi on GCC >= 15 as it is required for C++23 module
66+
local gcc_version = get_gcc_version(target)
67+
if gcc_version and semver.compare(gcc_version, "15") > 0 then
68+
cxx11abi = true
69+
end
70+
end
6471
if cxx11abi then
6572
target:add("cxxflags", "-D_GLIBCXX_USE_CXX11_ABI=1")
6673
else
@@ -139,33 +146,47 @@ function _get_std_module_manifest_path(target)
139146
return modules_json_path
140147
end
141148
end
142-
-- fallback on custom detection
143-
-- manifest can be found alongside libstdc++.so
144-
145-
-- TODO
149+
local ext = ".so"
150+
if target:is_plat("windows", "mingw") then
151+
ext = ".dll"
152+
elseif target:is_plat("macosx", "iphoneos", "appletvos") then
153+
ext = ".dylib"
154+
end
155+
local stdcpp_libfile, _ = try {
156+
function()
157+
return os.iorunv(compinst:program(), {"-print-file-name=libstdc++" .. ext}, {envs = compinst:runenvs()})
158+
end
159+
}
160+
if stdcpp_libfile then
161+
modules_json_path = path.join(path.directory(stdcpp_libfile), "libstdc++.modules.json")
162+
modules_json_path = modules_json_path:trim()
163+
if os.isfile(modules_json_path) then
164+
return modules_json_path
165+
end
166+
end
146167
end
147168

148169
function get_stdmodules(target)
149170
if not target:policy("build.c++.modules.std") then
150171
return
151172
end
152173
local modules_json_path = _get_std_module_manifest_path(target)
153-
if not modules_json_path then
154-
return
155-
end
156-
local modules_json = json.loadfile(modules_json_path)
157-
if modules_json and modules_json.modules and #modules_json.modules > 0 then
158-
local std_module_files = {}
159-
local modules_json_dir = path.directory(modules_json_path)
160-
for _, module_file in ipairs(modules_json.modules) do
161-
local module_file_path = module_file["source-path"]
162-
if not path.is_absolute(module_file_path) then
163-
module_file_path = path.join(modules_json_dir, module_file_path)
174+
if modules_json_path then
175+
local modules_json = json.loadfile(modules_json_path)
176+
if modules_json and modules_json.modules and #modules_json.modules > 0 then
177+
local std_module_files = {}
178+
local modules_json_dir = path.directory(modules_json_path)
179+
for _, module_file in ipairs(modules_json.modules) do
180+
local module_file_path = module_file["source-path"]
181+
if not path.is_absolute(module_file_path) then
182+
module_file_path = path.join(modules_json_dir, module_file_path)
183+
end
184+
table.insert(std_module_files, path.normalize(module_file_path))
164185
end
165-
table.insert(std_module_files, module_file_path)
186+
return std_module_files
166187
end
167-
return std_module_files
168188
end
189+
wprint("std and std.compat modules not found! maybe try to add --sdk=<PATH/TO/LLVM> or install libc++")
169190
end
170191

171192
function get_bmi_extension()

xmake/rules/c++/modules/msvc/support.lua

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
-- imports
2222
import("core.base.semver")
23+
import("core.tool.toolchain")
2324
import("core.project.config")
2425
import("lib.detect.find_tool")
2526
import(".support", {inherit = true})
@@ -99,7 +100,6 @@ end
99100

100101
-- provide toolchain include dir for stl headerunit when p1689 is not supported
101102
function toolchain_includedirs(target)
102-
103103
for _, toolchain_inst in ipairs(target:toolchains()) do
104104
if toolchain_inst:name() == "msvc" then
105105
local vcvars = toolchain_inst:config("vcvars")
@@ -117,32 +117,34 @@ function has_two_phase_compilation_support(_)
117117
end
118118

119119
-- build c++23 standard modules if needed
120-
function get_stdmodules(target)
121-
122-
if target:policy("build.c++.modules.std") then
123-
local msvc = target:toolchain("msvc")
124-
if msvc then
125-
local vcvars = msvc:config("vcvars")
126-
if vcvars.VCInstallDir and vcvars.VCToolsVersion then
127-
modules = {}
128-
129-
local stdmodulesdir = path.join(vcvars.VCInstallDir, "Tools", "MSVC", vcvars.VCToolsVersion, "modules")
130-
131-
if os.isdir(stdmodulesdir) then
132-
return {path.normalize(path.join(stdmodulesdir, "std.ixx")), path.normalize(path.join(stdmodulesdir, "std.compat.ixx"))}
133-
end
120+
function get_stdmodules(target, opt)
121+
opt = opt or {}
122+
if not target:policy("build.c++.modules.std") then
123+
return
124+
end
125+
local msvc
126+
if opt.toolchain then
127+
msvc = toolchain.load("msvc", {plat = opt.toolchain:plat(), arch = opt.toolchain:arch()})
128+
else
129+
msvc = target:toolchain("msvc")
130+
end
131+
if msvc and msvc:check() then
132+
local vcvars = msvc:config("vcvars")
133+
if vcvars.VCInstallDir and vcvars.VCToolsVersion then
134+
local stdmodulesdir = path.join(vcvars.VCInstallDir, "Tools", "MSVC", vcvars.VCToolsVersion, "modules")
135+
if os.isdir(stdmodulesdir) then
136+
return {path.normalize(path.join(stdmodulesdir, "std.ixx")), path.normalize(path.join(stdmodulesdir, "std.compat.ixx"))}
134137
end
135138
end
136-
wprint("std and std.compat modules not found! disabling them for the build")
137139
end
140+
wprint("std and std.compat modules not found! disabling them for the build")
138141
end
139142

140143
function get_bmi_extension()
141144
return ".ifc"
142145
end
143146

144147
function get_ifcoutputflag(target)
145-
146148
local ifcoutputflag = _g.ifcoutputflag
147149
if ifcoutputflag == nil then
148150
local compinst = target:compiler("cxx")
@@ -156,7 +158,6 @@ function get_ifcoutputflag(target)
156158
end
157159

158160
function get_ifconlyflag(target)
159-
160161
local ifconlyflag = _g.ifconlyflag
161162
if ifconlyflag == nil then
162163
local compinst = target:compiler("cxx")
@@ -169,7 +170,6 @@ function get_ifconlyflag(target)
169170
end
170171

171172
function get_interfaceflag(target)
172-
173173
local interfaceflag = _g.interfaceflag
174174
if interfaceflag == nil then
175175
local compinst = target:compiler("cxx")
@@ -183,7 +183,6 @@ function get_interfaceflag(target)
183183
end
184184

185185
function get_referenceflag(target)
186-
187186
local referenceflag = _g.referenceflag
188187
if referenceflag == nil then
189188
local compinst = target:compiler("cxx")
@@ -197,7 +196,6 @@ function get_referenceflag(target)
197196
end
198197

199198
function get_headernameflag(target)
200-
201199
local headernameflag = _g.headernameflag
202200
if headernameflag == nil then
203201
local compinst = target:compiler("cxx")
@@ -211,7 +209,6 @@ function get_headernameflag(target)
211209
end
212210

213211
function get_headerunitflag(target)
214-
215212
local headerunitflag = _g.headerunitflag
216213
if headerunitflag == nil then
217214
local compinst = target:compiler("cxx")
@@ -226,7 +223,6 @@ function get_headerunitflag(target)
226223
end
227224

228225
function get_exportheaderflag(target)
229-
230226
local exportheaderflag = _g.exportheaderflag
231227
if exportheaderflag == nil then
232228
if get_headernameflag(target) then
@@ -238,7 +234,6 @@ function get_exportheaderflag(target)
238234
end
239235

240236
function get_scandependenciesflag(target)
241-
242237
local scandependenciesflag = _g.scandependenciesflag
243238
if scandependenciesflag == nil then
244239
local compinst = target:compiler("cxx")
@@ -261,7 +256,6 @@ function get_scandependenciesflag(target)
261256
end
262257

263258
function get_cppversionflag(target)
264-
265259
local cppversionflag = _g.cppversionflag
266260
if cppversionflag == nil then
267261
local compinst = target:compiler("cxx")
@@ -272,7 +266,6 @@ function get_cppversionflag(target)
272266
end
273267

274268
function get_internalpartitionflag(target)
275-
276269
local internalpartitionflag = _g.internalpartitionflag
277270
if internalpartitionflag == nil then
278271
local compinst = target:compiler("cxx")

0 commit comments

Comments
 (0)