summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArthur LAURENT <[email protected]>2025-09-10 22:22:04 +0200
committerArthur LAURENT <[email protected]>2025-09-10 22:22:04 +0200
commitc71d77375c51440a4a5a743709e4945c00859990 (patch)
treebc3e3a00b17f350fdc9bf074c6d20937343111d9
parent37b7101e21ce6a6dbfe29339db736bfe150a4069 (diff)
feat(C++ modules) add build.c++.modules.non_cascading_changes policy to enable bmi hash comparison as incremental build optimisation for clang
-rw-r--r--xmake/core/project/policy.lua2
-rw-r--r--xmake/rules/c++/modules/builder.lua19
-rw-r--r--xmake/rules/c++/modules/clang/builder.lua32
3 files changed, 43 insertions, 10 deletions
diff --git a/xmake/core/project/policy.lua b/xmake/core/project/policy.lua
index 6949a6b94..967f9062e 100644
--- a/xmake/core/project/policy.lua
+++ b/xmake/core/project/policy.lua
@@ -72,6 +72,8 @@ function policy.policies()
["build.rpath"] = {description = "Enable build rpath.", default = true, type = "boolean"},
-- Enable C++ modules for C++ building, even if no .mpp is involved in the compilation
["build.c++.modules"] = {description = "Enable C++ modules for C++ building.", type = "boolean"},
+ -- Enable non cascading changes (experimental)
+ ["build.c++.modules.non_cascading_changes"] = {description = "Enable non cascading changes when supported (experimental).", default = false, type = "boolean"},
-- Hide C++ required files to reduce noise (may reduce build performance)
["build.c++.modules.hide_dependencies"] = {description = "Hide dependencies from the commandline when build C++ modules.", default = false, type = "boolean"},
-- Enable two phase compilation for C++ modules if supported by the compiler
diff --git a/xmake/rules/c++/modules/builder.lua b/xmake/rules/c++/modules/builder.lua
index b84d5d23e..a1c93a35f 100644
--- a/xmake/rules/c++/modules/builder.lua
+++ b/xmake/rules/c++/modules/builder.lua
@@ -180,6 +180,15 @@ function should_build(target, module)
local old_dependinfo = target:is_rebuilt() and {} or (depend.load(dependfile) or {})
old_dependinfo.files = {module.sourcefile}
+ -- need build this object?
+ local dryrun = option.get("dry-run")
+ if dryrun or depend.is_changed(old_dependinfo, dependinfo) then
+ depend.save(dependinfo, dependfile)
+ memcache:set2(target:fullname(), "should_build_" .. module.sourcefile, true)
+ profiler.leave(target:fullname(), "c++ modules", "builder", "check if " .. (module.name or module.sourcefile) .. " should be rebuilt")
+ return true
+ end
+
-- force rebuild a module if any of its module dependency is rebuilt
for dep_name, dep_module in table.orderpairs(module.deps) do
local mapped_dep = mapper.get(target, dep_module.headerunit and dep_name .. dep_module.key or dep_name)
@@ -188,18 +197,10 @@ function should_build(target, module)
depend.save(dependinfo, dependfile)
memcache:set2(target:fullname(), "should_build_" .. module.sourcefile, true)
profiler.leave(target:fullname(), "c++ modules", "builder", "check if " .. (module.name or module.sourcefile) .. " should be rebuilt")
- return true
+ return true, true
end
end
- -- need build this object?
- local dryrun = option.get("dry-run")
- if dryrun or depend.is_changed(old_dependinfo, dependinfo) then
- depend.save(dependinfo, dependfile)
- memcache:set2(target:fullname(), "should_build_" .. module.sourcefile, true)
- profiler.leave(target:fullname(), "c++ modules", "builder", "check if " .. (module.name or module.sourcefile) .. " should be rebuilt")
- return true
- end
memcache:set2(target:fullname(), "should_build_" .. module.sourcefile, false)
profiler.leave(target:fullname(), "c++ modules", "builder", "check if " .. (module.name or module.sourcefile) .. " should be rebuilt")
return false
diff --git a/xmake/rules/c++/modules/clang/builder.lua b/xmake/rules/c++/modules/clang/builder.lua
index 684a6a030..60f2186e5 100644
--- a/xmake/rules/c++/modules/clang/builder.lua
+++ b/xmake/rules/c++/modules/clang/builder.lua
@@ -20,6 +20,7 @@
-- imports
import("core.base.json")
+import("core.base.bytes")
import("core.base.option")
import("core.base.semver")
import("utils.progress")
@@ -46,6 +47,19 @@ function _get_bmifile(target, module)
return bmifile, add_reduced_flag
end
+function _update_bmihash(target, module)
+ local localcache = support.localcache()
+
+ local bmifile = _get_bmifile(target, module)
+ local bmihash = hash.xxhash128(bytes(io.readfile(bmifile)))
+ local old_bmihash = localcache:get2(bmifile, "hash")
+
+ if not old_bmihash or bmihash ~= old_bmihash then
+ localcache:set2(bmifile, "hash", bmihash)
+ support.memcache():set2(bmifile, "updated", true)
+ end
+end
+
function _make_modulebuildflags(target, module, opt)
assert(not module.headerunit)
@@ -285,11 +299,23 @@ end
function make_module_job(target, module, opt)
local dryrun = option.get("dry-run")
+ local enable_hash_comparison = target:policy("build.c++.modules.non_cascading_changes")
- local build = should_build(target, module)
+ local build, because_of_dependencies = should_build(target, module)
local bmi = opt and opt.bmi
local objectfile = opt and opt.objectfile
+ if build and enable_hash_comparison and because_of_dependencies then
+ build = false
+ for dep_name, dep_module in table.orderpairs(module.deps) do
+ local mapped_dep = mapper.get(target, dep_module.headerunit and dep_name .. dep_module.key or dep_name)
+ if support.memcache():get2(_get_bmifile(target, dep_module), "updated") then
+ build = true
+ break
+ end
+ end
+ end
+
if build then
if not dryrun then
local objectdir = path.directory(module.objectfile)
@@ -315,6 +341,10 @@ function make_module_job(target, module, opt)
os.tryrm(module.objectfile) -- force rebuild for .cpp files
end
end
+
+ if enable_hash_comparison and bmi then
+ _update_bmihash(target, module)
+ end
end
end