From afa8d329edd2dda64ed15f29ce1498f7943b32db Mon Sep 17 00:00:00 2001 From: Arthur LAURENT Date: Fri, 16 May 2025 15:06:02 +0200 Subject: (C++ modules support) fix unitybuild, pch and c++ modules objectfiles conflicts --- xmake/core/project/target.lua | 3 +- xmake/rules/c++/modules/builder.lua | 29 ++++---- xmake/rules/c++/modules/clang/builder.lua | 9 +-- xmake/rules/c++/modules/config.lua | 28 +++++-- xmake/rules/c++/modules/gcc/builder.lua | 6 +- xmake/rules/c++/modules/msvc/builder.lua | 6 +- xmake/rules/c++/modules/scanner.lua | 120 +++++++++++++++++------------- xmake/rules/c++/modules/xmake.lua | 1 - 8 files changed, 113 insertions(+), 89 deletions(-) diff --git a/xmake/core/project/target.lua b/xmake/core/project/target.lua index 475c06d19..6f4ad4d89 100644 --- a/xmake/core/project/target.lua +++ b/xmake/core/project/target.lua @@ -2099,7 +2099,6 @@ function _instance:objectfiles() -- get object files from source batches local objectfiles = {} local batchcount = 0 - local sourcebatches = self:sourcebatches() local orderkeys = table.keys(sourcebatches) table.sort(orderkeys) -- @note we need to guarantee the order of objectfiles for depend.is_changed() and etc. for _, k in ipairs(orderkeys) do @@ -2381,7 +2380,7 @@ function _instance:sourcebatches() if filerule:extraconf("sourcekinds", sourcekind, "objectfiles") ~= false then sourcebatch.objectfiles = sourcebatch.objectfiles or {} sourcebatch.dependfiles = sourcebatch.dependfiles or {} - local objectfile = self:objectfile(sourcefile, sourcekind) + local objectfile = self:objectfile(sourcefile) table.insert(sourcebatch.objectfiles, objectfile) table.insert(sourcebatch.dependfiles, self:dependfile(objectfile)) end diff --git a/xmake/rules/c++/modules/builder.lua b/xmake/rules/c++/modules/builder.lua index 44da4d622..151c3ed9e 100644 --- a/xmake/rules/c++/modules/builder.lua +++ b/xmake/rules/c++/modules/builder.lua @@ -191,11 +191,11 @@ function build_modules_for_jobgraph(target, jobgraph, built_modules) local jobdeps = {} local buildfilejobs = {} - -- if two phase supported only build interface and implementation named modules bmi + -- get named modules local _built_modules = {} for _, sourcefile in ipairs(built_modules) do local module = mapper.get(target, sourcefile) - if module.interface or module.implementation then + if module.name then table.insert(_built_modules, sourcefile) else if not support.is_bmionly(target, sourcefile) then @@ -209,7 +209,6 @@ function build_modules_for_jobgraph(target, jobgraph, built_modules) end end end - -- add module jobs local moduletype = has_two_phase_compilation_support and "bmi" or "onephase" local buildgroup = _get_module_buildgroup_for(target, moduletype) @@ -221,7 +220,7 @@ function build_modules_for_jobgraph(target, jobgraph, built_modules) table.insert(buildfilejobs, buildfilejob) jobgraph:add(buildfilejob, function(_, _, jobopt) -- build bmi if named job - jobopt.bmi = module.interface or module.implementation + jobopt.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) @@ -261,7 +260,7 @@ function build_objectfiles_for_jobgraph(target, jobgraph, built_modules) else for _, sourcefile in ipairs(built_modules) do local module = mapper.get(target, sourcefile) - if not module.interface and not module.implementation then + if not module.name then table.insert(_built_modules, sourcefile) end end @@ -295,11 +294,11 @@ function build_modules_for_batchjobs(target, batchjobs, built_modules, opt) local builder = _builder(target) local has_two_phase_compilation_support = support.has_two_phase_compilation_support(target) - -- if two phase supported only build interface and implementation named modules bmi + -- if two phase supported only build named modules bmi local _built_modules = {} for _, sourcefile in ipairs(built_modules) do local module = mapper.get(target, sourcefile) - if module.interface or module.implementation then + if module.name then table.insert(_built_modules, sourcefile) end end @@ -339,7 +338,7 @@ function build_modules_for_batchjobs(target, batchjobs, built_modules, opt) sourcefile = module.sourcefile, job = batchjobs:newjob(buildfilejob, function(_, _, jobopt) -- build bmi if named job - jobopt.bmi = module.interface or module.implementation + jobopt.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) @@ -362,7 +361,7 @@ function build_objectfiles_for_batchjobs(target, batchjobs, built_modules, opt) else for _, sourcefile in ipairs(built_modules) do local module = mapper.get(target, sourcefile) - if not module.interface and not module.implementation then + if not module.name then table.insert(_built_modules, sourcefile) end end @@ -399,7 +398,7 @@ function build_modules_for_batchcmds(target, batchcmds, built_modules, opt) local _built_modules = {} for _, sourcefile in ipairs(built_modules) do local module = mapper.get(target, sourcefile) - if module.interface or module.implementation then + if module.name then table.insert(_built_modules, sourcefile) end end @@ -408,7 +407,7 @@ function build_modules_for_batchcmds(target, batchcmds, built_modules, opt) local bmionly = support.is_bmionly(target, sourcefile) local module = mapper.get(target, sourcefile) local jobopt = {} - jobopt.bmi = module.interface or module.implementation + jobopt.bmi = module.name jobopt.objectfile = not has_two_phase_compilation_support and not bmionly jobopt.progress = opt.progress depmtime = math.max(depmtime, builder.make_module_buildcmds(target, batchcmds, module, jobopt)) @@ -429,8 +428,8 @@ function build_objectfiles_for_batchcmds(target, batchcmds, built_modules, opt) else for _, sourcefile in ipairs(built_modules) do local module = mapper.get(target, sourcefile) - if not module.interface and not module.implementation then - table.insert(_built_modules, sourcefile) + if not module.name then + table.insert(_built_modules, sourcefile) end end end @@ -641,7 +640,7 @@ end function build_bmis(target, jobgraph, _, opt) opt = opt or {} if target:data("cxx.has_modules") then - if target:is_moduleonly() and not target:data("cxx.modules.reused") then + if target:is_moduleonly() and not target:data("cxx.modules.reused") or target:is_phony() then return end local modules = scanner.get_modules(target) @@ -686,7 +685,7 @@ end function build_objectfiles(target, jobgraph, _, opt) if target:data("cxx.has_modules") then - if target:is_moduleonly() and not target:data("cxx.modules.reused") then + if target:is_moduleonly() and not target:data("cxx.modules.reused") or target:is_phony() then return end local modules = scanner.get_modules(target) diff --git a/xmake/rules/c++/modules/clang/builder.lua b/xmake/rules/c++/modules/clang/builder.lua index 474793472..797167283 100644 --- a/xmake/rules/c++/modules/clang/builder.lua +++ b/xmake/rules/c++/modules/clang/builder.lua @@ -218,11 +218,6 @@ function make_module_job(target, module, opt) local dryrun = option.get("dry-run") - -- append requires flags - -- if module.deps then - -- _append_requires_flags(target, module) - -- end - local build = should_build(target, module) local bmi = opt and opt.bmi local objectfile = opt and opt.objectfile @@ -246,7 +241,7 @@ function make_module_job(target, module, opt) elseif bmi then _compile_bmi_step(target, module, opt) else - if support.has_module_extension(module.sourcefile) or module.interface or module.implementation then + if support.has_module_extension(module.sourcefile) or module.name then _compile_objectfile_step(target, module, opt) else os.tryrm(module.objectfile) -- force rebuild for .cpp files @@ -277,7 +272,7 @@ function make_module_buildcmds(target, batchcmds, module, opt) elseif bmi then _compile_bmi_step(target, module, table.join(opt, {batchcmds = batchcmds})) else - if support.has_module_extension(module.sourcefile) or module.interface or module.implementation then + if support.has_module_extension(module.sourcefile) or module.name then _compile_objectfile_step(target, module, table.join(opt, {batchcmds = batchcmds})) else batchcmds:rm(module.objectfile) -- force rebuild for .cpp files diff --git a/xmake/rules/c++/modules/config.lua b/xmake/rules/c++/modules/config.lua index 9daa1bfd0..589413532 100644 --- a/xmake/rules/c++/modules/config.lua +++ b/xmake/rules/c++/modules/config.lua @@ -67,10 +67,20 @@ function main(target) wprint("build.c++.modules.tryreuse.discriminate_on_defines is deprecated, please use build.c++.modules.reuse.strict") end + -- if containes modules, enable objectfiles output of c++.build.modules.builder + local rule = target:rule("c++.build.modules.builder") + rule = rule:clone() + if rule then + rule:extraconf_set("sourcekinds", "cxx", "objectfiles", true) + target:rule_add(rule) + target:add("files") + end + -- moduleonly modules are implicitly public if target:is_moduleonly() then - local sourcebatch = target:sourcebatches()["c++.build.modules.builder"] - if sourcebatch then + local sourcebatches = target:sourcebatches() + if sourcebatches and sourcebatches["c++.build.modules.scanner"] then + local sourcebatch = sourcebatches["c++.build.modules.scanner"] for _, sourcefile in ipairs(sourcebatch.sourcefiles) do target:fileconfig_add(sourcefile, {public = true}) end @@ -81,11 +91,15 @@ end function insert_stdmodules(target) if target:data("cxx.has_modules") then - -- add std modules to sourcebatch - local stdmodules = support.get_stdmodules(target) - for _, sourcefile in ipairs(stdmodules) do - table.insert(target:sourcebatches()["c++.build.modules.scanner"].sourcefiles, sourcefile) - table.insert(target:sourcebatches()["c++.build.modules.builder"].sourcefiles, sourcefile) + local sourcebatches = target:sourcebatches() + if sourcebatches and sourcebatches["c++.build.modules.scanner"] then + local sourcebatch = sourcebatches["c++.build.modules.scanner"] + sourcebatch.sourcefiles = sourcebatch.sourcefiles or {} + -- add std modules to sourcebatch + local stdmodules = support.get_stdmodules(target) + for _, sourcefile in ipairs(stdmodules) do + table.insert(sourcebatch.sourcefiles, sourcefile) + end end end end diff --git a/xmake/rules/c++/modules/gcc/builder.lua b/xmake/rules/c++/modules/gcc/builder.lua index 7e207f396..54b47d41c 100644 --- a/xmake/rules/c++/modules/gcc/builder.lua +++ b/xmake/rules/c++/modules/gcc/builder.lua @@ -129,7 +129,7 @@ end function _get_maplines(target, module) local maplines = {} - if module.interface or module.implementation then + if module.name then table.insert(maplines, module.name .. " " .. path.absolute(module.bmifile)) end for dep_name, dep_module in table.orderpairs(module.deps) do @@ -223,7 +223,7 @@ function make_module_job(target, module, opt) end local flags = {"-x", "c++"} - if support.has_module_extension(module.sourcefile) or module.interface or module.implementation then + if support.has_module_extension(module.sourcefile) or module.name then if bmi then if objectfile then table.insert(flags, module_flag) @@ -270,7 +270,7 @@ function make_module_buildcmds(target, batchcmds, module, opt) end local flags = {"-x", "c++"} - if support.has_module_extension(module.sourcefile) or module.interface or module.implementation then + if support.has_module_extension(module.sourcefile) or module.name then if bmi then if objectfile then table.insert(flags, module_flag) diff --git a/xmake/rules/c++/modules/msvc/builder.lua b/xmake/rules/c++/modules/msvc/builder.lua index fa9c7021c..0fb17244b 100644 --- a/xmake/rules/c++/modules/msvc/builder.lua +++ b/xmake/rules/c++/modules/msvc/builder.lua @@ -43,7 +43,7 @@ function _make_modulebuildflags(target, module, opt) local bmionly = opt.bmi and not opt.objectfile local flags - if module.interface or module.implementation then -- named module + if module.name then -- named module flags = table.join("-TP", module.interface and interfaceflag or internalpartitionflag, bmionly and ifconlyflag or {}, ifcoutputflag, path(module.bmifile)) else flags = {"-TP"} @@ -258,7 +258,7 @@ function make_module_job(target, module, opt) elseif bmi then _compile_bmi_step(target, module, opt) else - if support.has_module_extension(module.sourcefile) or module.interface or module.implementation then + if support.has_module_extension(module.sourcefile) or module.name then _compile_objectfile_step(target, module, opt) else os.tryrm(module.objectfile) -- force rebuild for .cpp files @@ -287,7 +287,7 @@ function make_module_buildcmds(target, batchcmds, module, opt) elseif bmi then _compile_bmi_step(target, module, table.join(opt, {batchcmds = batchcmds})) else - if support.has_module_extension(module.sourcefile) or module.interface or module.implementation then + if support.has_module_extension(module.sourcefile) or module.name then _compile_objectfile_step(target, module, table.join(opt, {batchcmds = batchcmds})) else batchcmds:rm(module.objectfile) -- force rebuild for .cpp files diff --git a/xmake/rules/c++/modules/scanner.lua b/xmake/rules/c++/modules/scanner.lua index fe9b9c63d..9c71fa035 100644 --- a/xmake/rules/c++/modules/scanner.lua +++ b/xmake/rules/c++/modules/scanner.lua @@ -145,8 +145,9 @@ function _parse_dependencies_data(target, moduleinfos) modules_names:insert(module.name) module.headerunit = provide["is-headerunit"] module.interface = (not module.headerunit and provide["is-interface"] == nil) and true or provide["is-interface"] - module.implementation = not module.interface and not module.headerunit - module.method = provide["lookup-method"] or "by-name" + if module.name then + module.method = provide["lookup-method"] or "by-name" + end if module.headerunit then local key = support.get_headerunit_key(target, module.sourcefile) @@ -199,7 +200,7 @@ function _get_edges(target, nodes, modules) local deps_names = hashset.new() for _, node in ipairs(table.unique(nodes)) do local module = modules[node] - if module.interface or module.implementation then + if module.name then if deps_names:has(module.name) then raise("duplicate module name detected for \"" .. module.name .. "\"\n <" .. target:fullname() .. "> -> " .. module.sourcefile .. "\n <" .. target:fullname() .. "> -> " .. name_filemap[module.name]) end @@ -212,7 +213,7 @@ function _get_edges(target, nodes, modules) for dep_name, _ in table.orderpairs(module.deps) do for _, dep_node in ipairs(nodes) do local dep_module = modules[dep_node] - if (dep_module.interface or dep_module.implementation or dep_module.headerunit) and dep_name == dep_module.name then + if dep_module.name and dep_name == dep_module.name then table.insert(edges, {dep_node, node}) break end @@ -280,31 +281,34 @@ function _get_targetdeps_modules(target) local _, stdmodules_set = support.get_stdmodules(target) local modules for _, dep in ipairs(target:orderdeps()) do - local sourcebatch = dep:sourcebatches()["c++.build.modules.builder"] - if sourcebatch and sourcebatch.sourcefiles then - for _, sourcefile in ipairs(sourcebatch.sourcefiles) do - modules = modules or {} - if support.is_public(dep, sourcefile) or stdmodules_set:has(sourcefile) then - local _fileconfig = dep:fileconfig(sourcefile) - local fileconfig = {} - if _fileconfig then - fileconfig.defines = _fileconfig.defines - fileconfig.undefines = _fileconfig.undefines - fileconfig.includedirs = _fileconfig.includedirs - end - fileconfig.defines = table.join(fileconfig.defines or {}, dep:get("defines") or {}) - fileconfig.undefines = table.join(fileconfig.undefines or {}, dep:get("undefines") or {}) - fileconfig.includedirs = table.join(fileconfig.includedirs or {}, dep:get("includedirs") or {}) - if not dep:is_phony() then - if target:namespace() == dep:namespace() then - fileconfig.external = dep:name() - else - fileconfig.external = dep:fullname() + local sourcebatches = dep:sourcebatches() + if sourcebatches and sourcebatches["c++.build.modules.scanner"] then + local sourcebatch = sourcebatches["c++.build.modules.scanner"] + if sourcebatch.sourcefiles then + for _, sourcefile in ipairs(sourcebatch.sourcefiles) do + modules = modules or {} + if support.is_public(dep, sourcefile) or stdmodules_set:has(sourcefile) then + local _fileconfig = dep:fileconfig(sourcefile) + local fileconfig = {} + if _fileconfig then + fileconfig.defines = _fileconfig.defines + fileconfig.undefines = _fileconfig.undefines + fileconfig.includedirs = _fileconfig.includedirs + end + fileconfig.defines = table.join(fileconfig.defines or {}, dep:get("defines") or {}) + fileconfig.undefines = table.join(fileconfig.undefines or {}, dep:get("undefines") or {}) + fileconfig.includedirs = table.join(fileconfig.includedirs or {}, dep:get("includedirs") or {}) + if not dep:is_phony() then + if target:namespace() == dep:namespace() then + fileconfig.external = dep:name() + else + fileconfig.external = dep:fullname() + end + fileconfig.bmionly = not dep:is_moduleonly() + end + if not modules[sourcefile] then + modules[sourcefile] = fileconfig end - fileconfig.bmionly = not dep:is_moduleonly() - end - if not modules[sourcefile] then - modules[sourcefile] = fileconfig end end end @@ -377,13 +381,12 @@ function _patch_sourcebatch(target, sourcebatch) end sourcebatch.sourcekind = "cxx" - sourcebatch.objectfiles = {} sourcebatch.dependfiles = {} for _, sourcefile in ipairs(sourcebatch.sourcefiles) do local reused, from = support.is_reused(target, sourcefile) local _target = reused and from or target local objectfile = _target:objectfile(sourcefile) - local dependfile = _target:dependfile(sourcefile or objectfile) + local dependfile = _target:dependfile(objectfile) table.insert(sourcebatch.dependfiles, dependfile) end end @@ -420,16 +423,24 @@ function _do_parse(target, sourcebatch) end -- steal from c++.build sourcebatch named modules with cpp extensions - local cxx_sourcebatch = target:sourcebatches()["c++.build"] - if cxx_sourcebatch then + local sourcebatches = target:sourcebatches() + if sourcebatches and sourcebatches["c++.build"] then + local cxx_sourcebatch = sourcebatches["c++.build"] cxx_sourcebatch.sourcefiles = {} cxx_sourcebatch.dependfiles = {} + cxx_sourcebatch.objectfiles = {} for _, sourcefile in ipairs(sourcebatch.sourcefiles) do local module = mapper.get(target, sourcefile) - if module and not module.interface and not module.implementation then + local insert = true + if module then + insert = not module.name + end + + if insert then table.insert(cxx_sourcebatch.sourcefiles, sourcefile) local objectfile = target:objectfile(sourcefile) table.insert(cxx_sourcebatch.dependfiles, target:dependfile(objectfile)) + table.insert(cxx_sourcebatch.objectfiles, objectfile) end end end @@ -688,16 +699,14 @@ function sort_modules_by_dependencies(target, modules) local culleds for _, sourcefile in ipairs(sourcefiles_sorted) do local module = mapper.get(target, sourcefile) - local is_named = module.interface or module.implementation or module.headerunit - local sort = (is_named and module.sourcealias) or (module.headerunit and not module.alias) or (not is_named) + local name = module.name + local is_named = name ~= nil + local sort = (is_named and (module.sourcealias or not module.alias)) or not is_named if sort then local insert = false - local name local reused, from = support.is_reused(target, sourcefile) - if module.name and not module.headerunit then -- named modules - name = module.name - + if is_named and not module.headerunit then -- named modules insert = not support.can_be_culled(target, sourcefile) -- if module is cullable (culling policy enabled and not a public module), try to cull @@ -726,9 +735,9 @@ function sort_modules_by_dependencies(target, modules) table.insert(built_headerunits, sourcefile) else table.insert(built_modules, sourcefile) - -- insert objectfile if module is not imported from a static / shared library and if has a custom extension (not .cpp) + -- insert objectfile if module named and is not imported from a static / shared library or if from a C++ file with a c++ module extension -- if not so objectfile will be handled by c++.build rule - if not support.is_bmionly(target, sourcefile) then + if not support.is_bmionly(target, sourcefile) and (support.has_module_extension(sourcefile) or is_named) then local objectfile = target:objectfile(sourcefile) table.insert(objectfiles, tostring(objectfile)) end @@ -775,19 +784,28 @@ function get_modules(target) end function after_scan(target) - local compile_commands = os.getenv("XMAKE_IN_PROJECT_GENERATOR") and os.getenv("XMAKE_IN_COMPILE_COMMANDS_PROJECT_GENERATOR") - if not os.getenv("XMAKE_IN_PROJECT_GENERATOR") or compile_commands then - local sourcebatch_scanner = target:sourcebatches()["c++.build.modules.scanner"] - local sourcebatch_builder = target:sourcebatches()["c++.build.modules.builder"] - if target:data("cxx.has_modules") and not target:is_moduleonly() then + local sourcebatches = target:sourcebatches() + local sourcebatch_builder = sourcebatches and sourcebatches["c++.build.modules.builder"] + local sourcebatch_scanner = sourcebatches and sourcebatches["c++.build.modules.scanner"] + if sourcebatch_scanner then + sourcebatch_scanner.sourcefiles = {} + end + if sourcebatch_builder then + sourcebatch_builder.sourcefiles = {} + sourcebatch_builder.dependfiles = {} + sourcebatch_builder.objectfiles = {} + end + if target:data("cxx.has_modules") then + if target:is_moduleonly() or target:is_phony() then + return + end + local compile_commands = os.getenv("XMAKE_IN_PROJECT_GENERATOR") and os.getenv("XMAKE_IN_COMPILE_COMMANDS_PROJECT_GENERATOR") + local need_objectfiles = not os.getenv("XMAKE_IN_PROJECT_GENERATOR") or compile_commands + if need_objectfiles then local modules = get_modules(target) local _, _, objectfiles = sort_modules_by_dependencies(target, modules, {jobgraph = target:policy("build.jobgraph")}) - sourcebatch_scanner.objectfiles = objectfiles + assert(sourcebatch_builder) sourcebatch_builder.objectfiles = objectfiles - elseif sourcebatch_scanner then - -- avoid duplicate linking of object files of non-module programs - sourcebatch_scanner.objectfiles = {} - sourcebatch_builder.objectfiles = {} end end end diff --git a/xmake/rules/c++/modules/xmake.lua b/xmake/rules/c++/modules/xmake.lua index 9afd4f6cc..8633c544c 100644 --- a/xmake/rules/c++/modules/xmake.lua +++ b/xmake/rules/c++/modules/xmake.lua @@ -58,6 +58,5 @@ rule("c++.build.modules.builder") -- install modules rule("c++.build.modules.install") set_extensions(".cppm", ".ccm", ".cxxm", ".c++m", ".mpp", ".mxx", ".ixx") - before_install("install.install") before_uninstall("install.uninstall") -- cgit v1.3.1