diff options
| author | Arthur LAURENT <[email protected]> | 2025-09-10 22:22:04 +0200 |
|---|---|---|
| committer | Arthur LAURENT <[email protected]> | 2025-09-10 22:22:04 +0200 |
| commit | c71d77375c51440a4a5a743709e4945c00859990 (patch) | |
| tree | bc3e3a00b17f350fdc9bf074c6d20937343111d9 | |
| parent | 37b7101e21ce6a6dbfe29339db736bfe150a4069 (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.lua | 2 | ||||
| -rw-r--r-- | xmake/rules/c++/modules/builder.lua | 19 | ||||
| -rw-r--r-- | xmake/rules/c++/modules/clang/builder.lua | 32 |
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 |
