summaryrefslogtreecommitdiff
path: root/xmake/rules/c++/modules
diff options
context:
space:
mode:
Diffstat (limited to 'xmake/rules/c++/modules')
-rw-r--r--xmake/rules/c++/modules/builder.lua43
-rw-r--r--xmake/rules/c++/modules/clang/builder.lua8
-rw-r--r--xmake/rules/c++/modules/clang/scanner.lua5
-rw-r--r--xmake/rules/c++/modules/clang/support.lua79
-rw-r--r--xmake/rules/c++/modules/gcc/support.lua7
-rw-r--r--xmake/rules/c++/modules/msvc/support.lua7
-rw-r--r--xmake/rules/c++/modules/support.lua19
7 files changed, 140 insertions, 28 deletions
diff --git a/xmake/rules/c++/modules/builder.lua b/xmake/rules/c++/modules/builder.lua
index d0863ad98..b988ff152 100644
--- a/xmake/rules/c++/modules/builder.lua
+++ b/xmake/rules/c++/modules/builder.lua
@@ -194,6 +194,10 @@ function should_build(target, module)
local dependinfo = {}
dependinfo.files = {module.sourcefile}
dependinfo.values = {compinst:program(), compflags}
+ if module.bmifile and not module.headerunit and support.has_two_phase_compilation_support(target) then
+ local bmi_mode = support.has_precompile_reduced_bmi_support(target) and support.get_modulesprecompilereducedbmiflag(target) or "--precompile"
+ table.insert(dependinfo.values, bmi_mode)
+ end
local objectfile_exists = (module.headerunit or support.is_bmionly(target, module.sourcefile)) and true or os.isfile(module.objectfile)
dependinfo.lastmtime = (os.isfile(module.bmifile or module.objectfile) and objectfile_exists) and os.mtime(dependfile) or 0
@@ -241,6 +245,7 @@ function build_modules_for_jobgraph(target, jobgraph, built_modules)
profiler.enter(target:fullname(), "c++ modules", "builder", "schedule module bmi build jobs")
local builder = _builder(target)
local has_two_phase_compilation_support = support.has_two_phase_compilation_support(target)
+ local has_precompile_reduced_bmi = support.has_precompile_reduced_bmi_support(target)
local jobdeps = {}
local buildfilejobs = {}
@@ -272,19 +277,24 @@ function build_modules_for_jobgraph(target, jobgraph, built_modules)
local buildfilejob = _get_module_buildfilejob_for(target, sourcefile, moduletype)
table.insert(buildfilejobs, buildfilejob)
jobgraph:add(buildfilejob, function(_, _, jobopt)
- progress.set_target(jobopt.progress, target)
+ local moduleopt = table.clone(jobopt)
+ progress.set_target(moduleopt.progress, target)
-- build bmi if named job
- jobopt.bmi = module.name
+ moduleopt.bmi = module.name
-- build objectfile here if two phase compilation is not supported
- jobopt.objectfile = not has_two_phase_compilation_support and not bmionly
- builder.make_module_job(target, module, jobopt)
+ moduleopt.objectfile = not has_two_phase_compilation_support and not bmionly
+ builder.make_module_job(target, module, moduleopt)
end)
_merge_jobdeps(jobdeps, _get_jobdeps(target, module, jobgraph, buildfilejob))
-- if two phase compilation supported set jobdeps for objectfile job
if has_two_phase_compilation_support and not bmionly then
local objbuildfilejob = _get_module_buildfilejob_for(target, sourcefile, "objectfile")
- _merge_jobdeps(jobdeps, {[objbuildfilejob] = {buildfilejob}})
+ if has_precompile_reduced_bmi then
+ _merge_jobdeps(jobdeps, _get_jobdeps(target, module, jobgraph, objbuildfilejob))
+ else
+ _merge_jobdeps(jobdeps, {[objbuildfilejob] = {buildfilejob}})
+ end
end
end
end)
@@ -327,10 +337,11 @@ function build_objectfiles_for_jobgraph(target, jobgraph, built_modules)
local module = mapper.get(target, sourcefile)
local buildfilejob = _get_module_buildfilejob_for(target, sourcefile, "objectfile")
jobgraph:add(buildfilejob, function(_, _, jobopt)
- progress.set_target(jobopt.progress, target)
- jobopt.bmi = false
- jobopt.objectfile = true
- builder.make_module_job(target, module, jobopt)
+ local moduleopt = table.clone(jobopt)
+ progress.set_target(moduleopt.progress, target)
+ moduleopt.bmi = false
+ moduleopt.objectfile = true
+ builder.make_module_job(target, module, moduleopt)
end)
end
end
@@ -719,6 +730,16 @@ function build_bmis(target, jobgraph, _, opt)
local built_modules, built_headerunits, _ = scanner.sort_modules_by_dependencies(target, modules, {jobgraph = target:policy("build.jobgraph")})
local headerunits, stlheaderunits = scanner.sort_headerunits(target, built_headerunits)
if jobgraph.add_orders then -- jobgraph
+ if support.has_precompile_reduced_bmi_support(target) then
+ -- schedule object jobs in the BMI stage so both jobs can run concurrently
+ -- after their imported BMIs are ready
+ local append_requires_flags = _builder(target).append_requires_flags
+ if append_requires_flags then
+ append_requires_flags(target, built_modules)
+ end
+ build_objectfiles_for_jobgraph(target, jobgraph, built_modules)
+ end
+
-- build headerunits
if stlheaderunits or headerunits then
build_headerunits_for_jobgraph(target, jobgraph, stlheaderunits, headerunits)
@@ -760,6 +781,10 @@ function build_objectfiles(target, jobgraph, _, opt)
if target:is_moduleonly() and not target:data("cxx.modules.reused") or target:is_phony() then
return
end
+ if jobgraph.add_orders and support.has_precompile_reduced_bmi_support(target) then
+ -- object jobs have already been scheduled by build_bmis()
+ return
+ end
profiler.enter(target:fullname(), "c++ modules", "builder", "objectfiles")
local modules = scanner.get_modules(target)
-- avoid building non referenced modules
diff --git a/xmake/rules/c++/modules/clang/builder.lua b/xmake/rules/c++/modules/clang/builder.lua
index 4839733e0..4ecafb17e 100644
--- a/xmake/rules/c++/modules/clang/builder.lua
+++ b/xmake/rules/c++/modules/clang/builder.lua
@@ -65,6 +65,8 @@ function _make_modulebuildflags(target, module, opt)
local modules_reduced_bmi_flag = support.get_modulesreducedbmiflag(target)
local has_two_phases = target:policy("build.c++.modules.two_phases")
+ local has_precompile_reduced_bmi = support.has_precompile_reduced_bmi_support(target)
+ local modules_precompile_reduced_bmi_flag = has_precompile_reduced_bmi and support.get_modulesprecompilereducedbmiflag(target)
local flags
if opt.bmi then
local module_outputflag = support.get_moduleoutputflag(target)
@@ -72,7 +74,7 @@ function _make_modulebuildflags(target, module, opt)
flags = {"-x", "c++-module"}
if not opt.objectfile then
- table.insert(flags, "--precompile")
+ table.insert(flags, modules_precompile_reduced_bmi_flag or "--precompile")
if target:has_tool("cxx", "clang_cl") then
table.join2(flags, "/clang:-o", "/clang:" .. module.bmifile)
end
@@ -92,7 +94,7 @@ function _make_modulebuildflags(target, module, opt)
end
else
flags = {}
- if not has_two_phases or not module.bmifile then
+ if (has_precompile_reduced_bmi and support.has_module_extension(module.sourcefile)) or not has_two_phases or not module.bmifile then
flags = {"-x", "c++"}
end
local std = (module.name == "std" or module.name == "std.compat")
@@ -171,7 +173,7 @@ function _compile(target, flags, module, opt)
opt = opt or {}
local sourcefile = module.sourcefile
- if not opt.bmi and opt.objectfile and module.bmifile then
+ if not support.has_precompile_reduced_bmi_support(target) and not opt.bmi and opt.objectfile and module.bmifile then
sourcefile = module.bmifile
end
local outputfile = ((opt.bmi and not opt.objectfile) or opt.headerunit) and module.bmifile or module.objectfile
diff --git a/xmake/rules/c++/modules/clang/scanner.lua b/xmake/rules/c++/modules/clang/scanner.lua
index d73c40621..f0023cb7c 100644
--- a/xmake/rules/c++/modules/clang/scanner.lua
+++ b/xmake/rules/c++/modules/clang/scanner.lua
@@ -59,7 +59,8 @@ function scan_dependency_for(target, sourcefile, rescan, opt)
if option.get("verbose") then
print(os.args(table.join(clangscandeps, dependency_flags)))
end
- local outdata, errdata = os.iorunv(clangscandeps, dependency_flags)
+ local outdata, errdata = os.iorunv(clangscandeps, dependency_flags,
+ {envs = compinst:runenvs()})
assert(outdata, errdata)
io.writefile(jsonfile, outdata)
@@ -77,7 +78,7 @@ function scan_dependency_for(target, sourcefile, rescan, opt)
end)
local ifile = path.translate(path.join(outputdir, path.filename(file) .. ".i"))
compflags = table.join(compflags or {}, keepsystemincludesflag or {}, {"-E", "-x", "c++", file, "-o", ifile})
- os.vrunv(compinst:program(), compflags)
+ os.vrunv(compinst:program(), compflags, {envs = compinst:runenvs()})
local content = io.readfile(ifile)
os.rm(ifile)
return content
diff --git a/xmake/rules/c++/modules/clang/support.lua b/xmake/rules/c++/modules/clang/support.lua
index b1699d21a..a010fdde1 100644
--- a/xmake/rules/c++/modules/clang/support.lua
+++ b/xmake/rules/c++/modules/clang/support.lua
@@ -46,7 +46,10 @@ function _get_toolchain_includedirs_for_stlheaders(target, includedirs, clang)
table.insert(argv, 1, "-stdlib=libstdc++")
end
end
- local result = try {function () return os.iorunv(clang, argv, {envs = compinst:runenvs()}) end}
+ local compinst = target:compiler("cxx")
+ local result = try {function ()
+ return os.iorunv(clang, argv, {envs = compinst and compinst:runenvs()})
+ end}
if result then
for _, line in ipairs(result:split("\n", {plain = true})) do
local line = line:trim()
@@ -64,17 +67,25 @@ end
function _get_std_module_manifest_path(target)
local print_module_manifest_flag = get_print_library_module_manifest_path_flag(target)
- local clang_path = path.directory(get_clang_path(target))
+ local clang_path = get_clang_path(target)
+ if not clang_path then
+ return
+ end
+ local clang_dir = path.directory(clang_path)
if print_module_manifest_flag then
local compinst = target:compiler("cxx")
- local outdata, _ = try { function() return os.iorunv(compinst:program(), {"-std=c++23", "-stdlib=libc++", "--sysroot=" .. path.join(clang_path, ".."), print_module_manifest_flag}, {envs = compinst:runenvs()}) end }
+ local sysroot = "--sysroot=" .. path.join(clang_dir, "..")
+ local flags = {"-std=c++23", "-stdlib=libc++", sysroot, print_module_manifest_flag}
+ local outdata, _ = try {function()
+ return os.iorunv(compinst:program(), flags, {envs = compinst:runenvs()})
+ end}
if outdata and not outdata:startswith("<NOT PRESENT>") then
return outdata:trim()
end
end
-- fallback on custom detection
-- manifest can be found in <llvm_path>/lib subdirectory (i.e on debian it should be <llvm_path>/lib/x86_64-unknown-linux-gnu/)
- local clang_lib_path = path.join(clang_path, "..", "lib")
+ local clang_lib_path = path.join(clang_dir, "..", "lib")
local modules_json_path = path.join(clang_lib_path, "libc++.modules.json")
if not os.isfile(modules_json_path) then
modules_json_path = find_file("*/libc++.modules.json", clang_lib_path)
@@ -119,6 +130,13 @@ function has_two_phase_compilation_support(target)
return target:policy("build.c++.modules.two_phases")
end
+function has_precompile_reduced_bmi_support(target)
+ if not target:policy("build.c++.modules.clang.precompile_reduced_bmi") or not has_two_phase_compilation_support(target) then
+ return false
+ end
+ return get_modulesprecompilereducedbmiflag(target) ~= nil
+end
+
-- flags that doesn't affect bmi generation
function strippeable_flags()
-- speculative list as there is no resource that list flags that prevent reusability, this list will likely be improve over time
@@ -161,7 +179,11 @@ function toolchain_includedirs(target)
runtime_flag = "-stdlib=libstdc++"
end
end
- local _, result = try {function () return os.iorunv(clang, table.join({"-E", "-Wp,-v", "-xc++", os.nuldev()}, runtime_flag or {})) end}
+ local compinst = target:compiler("cxx")
+ local flags = table.join({"-E", "-Wp,-v", "-xc++", os.nuldev()}, runtime_flag or {})
+ local _, result = try {function ()
+ return os.iorunv(clang, flags, {envs = compinst and compinst:runenvs()})
+ end}
if result then
for _, line in ipairs(result:split("\n", {plain = true})) do
local line = line:trim()
@@ -183,8 +205,10 @@ function get_clang_path(target)
if not clang_path then
local program, toolname = target:tool("cxx")
if program and toolname:startswith("clang") then
+ local compinst = target:compiler("cxx")
+ local envs = compinst and compinst:runenvs() or os.getenvs()
local clang = find_tool(toolname, {program = program,
- envs = os.getenvs(), cachekey = "modules_support_clang_" .. toolname})
+ envs = envs, cachekey = "modules_support_clang_" .. toolname})
if clang then
clang_path = clang.program
end
@@ -201,8 +225,10 @@ function get_clang_version(target)
if not clang_version then
local program, toolname = target:tool("cxx")
if program and toolname:startswith("clang") then
+ local compinst = target:compiler("cxx")
+ local envs = compinst and compinst:runenvs() or os.getenvs()
local clang = find_tool(toolname, {program = program, version = true,
- envs = os.getenvs(), cachekey = "modules_support_clang_" .. toolname})
+ envs = envs, cachekey = "modules_support_clang_" .. toolname})
if clang then
clang_version = clang.version
end
@@ -229,10 +255,13 @@ function get_clang_scan_deps(target)
if dir and dir ~= "." and os.isdir(dir) then
program = path.join(dir, program)
end
- local result = find_tool("clang-scan-deps", {program = program, version = true})
+ local compinst = target:compiler("cxx")
+ local envs = compinst and compinst:runenvs() or os.getenvs()
+ local result = find_tool("clang-scan-deps",
+ {program = program, version = true, envs = envs})
if not result then
-- find a system wide alternative
- result = find_tool("clang-scan-deps", {version = true})
+ result = find_tool("clang-scan-deps", {version = true, envs = envs})
end
if result then
clang_scan_deps = result.program
@@ -295,9 +324,16 @@ function get_stdmodules(target)
return {path.normalize(path.join(try_std_module_directory, "std.cppm")), path.normalize(path.join(try_std_module_directory, "std.compat.cppm"))}
end
-- then try the directory relative to clang bin directory
- try_std_module_directory = path.join(path.directory(get_original_file(get_clang_path(target))), std_module_directory)
- if os.isdir(try_std_module_directory) then
- return {path.normalize(path.join(try_std_module_directory, "std.cppm")), path.normalize(path.join(try_std_module_directory, "std.compat.cppm"))}
+ local clang_path = get_clang_path(target)
+ if clang_path then
+ local clang_dir = path.directory(get_original_file(clang_path))
+ try_std_module_directory = path.join(clang_dir, std_module_directory)
+ if os.isdir(try_std_module_directory) then
+ return {
+ path.normalize(path.join(try_std_module_directory, "std.cppm")),
+ path.normalize(path.join(try_std_module_directory, "std.compat.cppm"))
+ }
+ end
end
elseif cpplib == "stdc++" then
-- dont be greedy and don't enable stdc++ std module support for llvm < 19
@@ -382,6 +418,25 @@ function get_modulesreducedbmiflag(target)
return modulesreducedbmiflag or nil
end
+function get_modulesprecompilereducedbmiflag(target)
+ local modulesprecompilereducedbmiflag = _g.modulesprecompilereducedbmiflag
+ if modulesprecompilereducedbmiflag == nil then
+ local compinst = target:compiler("cxx")
+ local checkflags = "--precompile-reduced-bmi"
+ if target:has_tool("cxx", "clang_cl") then
+ -- clang_cl.has_flags() appends -c and treats unused-argument warnings as errors,
+ -- but --precompile-reduced-bmi does not use -c. Ignore that specific warning and
+ -- treat unknown-argument warnings as errors to reject unsupported flags.
+ checkflags = {checkflags, "-Wno-unused-command-line-argument", "-Werror=unknown-argument"}
+ end
+ if compinst:has_flags(checkflags, "cxxflags", {flagskey = "clang_modules_precompile_reduced_bmi", tryrun = true}) then
+ modulesprecompilereducedbmiflag = "--precompile-reduced-bmi"
+ end
+ _g.modulesprecompilereducedbmiflag = modulesprecompilereducedbmiflag or false
+ end
+ return modulesprecompilereducedbmiflag or nil
+end
+
function has_clangscandepssupport(target)
local support_clangscandeps = _g.support_clangscandeps
if support_clangscandeps == nil then
diff --git a/xmake/rules/c++/modules/gcc/support.lua b/xmake/rules/c++/modules/gcc/support.lua
index 1204d207d..e57bd5a63 100644
--- a/xmake/rules/c++/modules/gcc/support.lua
+++ b/xmake/rules/c++/modules/gcc/support.lua
@@ -79,6 +79,13 @@ function has_two_phase_compilation_support(_)
return false
end
+function has_precompile_reduced_bmi_support(_)
+ return false
+end
+
+function get_modulesprecompilereducedbmiflag(_)
+end
+
-- flags that doesn't affect bmi generation
function strippeable_flags()
-- speculative list as there is no resource that list flags that prevent reusability, this list will likely be improve over time
diff --git a/xmake/rules/c++/modules/msvc/support.lua b/xmake/rules/c++/modules/msvc/support.lua
index 588921d0e..caaeb9cc6 100644
--- a/xmake/rules/c++/modules/msvc/support.lua
+++ b/xmake/rules/c++/modules/msvc/support.lua
@@ -120,6 +120,13 @@ function has_two_phase_compilation_support(_)
return false
end
+function has_precompile_reduced_bmi_support(_)
+ return false
+end
+
+function get_modulesprecompilereducedbmiflag(_)
+end
+
-- build c++23 standard modules if needed
function get_stdmodules(target, opt)
opt = opt or {}
diff --git a/xmake/rules/c++/modules/support.lua b/xmake/rules/c++/modules/support.lua
index 126e25c61..78d74ae6c 100644
--- a/xmake/rules/c++/modules/support.lua
+++ b/xmake/rules/c++/modules/support.lua
@@ -92,7 +92,15 @@ function get_cpplibrary_name(target)
end
elseif target:is_plat("macosx", "iphoneos", "watchos", "appletvos", "applexros", "bsd", "harmony") then
return "c++"
- elseif target:is_plat("linux", "mingw", "cygwin", "msys", "haiku") then
+ elseif target:is_plat("linux", "cygwin", "msys", "haiku") then
+ return "stdc++"
+ elseif target:is_plat("mingw") then
+ local toolchain_inst = target:toolchain("mingw")
+ local is_clang = (toolchain_inst and toolchain_inst:config("clang")) or
+ target:has_tool("cxx", "clang", "clangxx", "clang_cl")
+ if is_clang then
+ return "c++"
+ end
return "stdc++"
elseif target:is_plat("windows") then
return "msstl"
@@ -103,6 +111,14 @@ function has_two_phase_compilation_support(target)
return _support(target).has_two_phase_compilation_support(target)
end
+function has_precompile_reduced_bmi_support(target)
+ return _support(target).has_precompile_reduced_bmi_support(target)
+end
+
+function get_modulesprecompilereducedbmiflag(target)
+ return _support(target).get_modulesprecompilereducedbmiflag(target)
+end
+
-- strip flags not relevent for module reuse
function strip_flags(target, flags, opt)
@@ -429,4 +445,3 @@ function add_installfiles_for_modules(target, modules)
end
end
end
-