summaryrefslogtreecommitdiff
path: root/xmake/rules/c++/modules/modules_support
diff options
context:
space:
mode:
authorArthur LAURENT <[email protected]>2024-01-23 18:23:27 +0100
committerArthur LAURENT <[email protected]>2024-01-26 18:20:57 +0100
commitd52291f3389b2bf3f4b90e24c4670f41605c3cc8 (patch)
treeb2a2d5eefca2ce9da55bba6dedbf2413c4f610f6 /xmake/rules/c++/modules/modules_support
parente20c6cafdb241ad6f858556c33463da1a092abdc (diff)
refactor module common infrastructure
Diffstat (limited to 'xmake/rules/c++/modules/modules_support')
-rw-r--r--xmake/rules/c++/modules/modules_support/builder.lua400
-rw-r--r--xmake/rules/c++/modules/modules_support/compiler_support.lua281
-rw-r--r--xmake/rules/c++/modules/modules_support/dependency_scanner.lua (renamed from xmake/rules/c++/modules/modules_support/common.lua)543
-rw-r--r--xmake/rules/c++/modules/modules_support/stl_headers.lua2
4 files changed, 860 insertions, 366 deletions
diff --git a/xmake/rules/c++/modules/modules_support/builder.lua b/xmake/rules/c++/modules/modules_support/builder.lua
new file mode 100644
index 000000000..481415661
--- /dev/null
+++ b/xmake/rules/c++/modules/modules_support/builder.lua
@@ -0,0 +1,400 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, TBOOX Open Source Group.
+--
+-- @author ruki, Arthapz
+-- @file common.lua
+--
+
+-- imports
+import("core.base.json")
+import("core.base.option")
+import("private.async.buildjobs")
+import("core.tool.compiler")
+import("core.project.config")
+import("core.project.depend")
+import("utils.progress")
+import("compiler_support")
+import("dependency_scanner")
+
+-- build target modules
+function _build_modules(target, sourcebatch, modules, opt)
+ local objectfiles = dependency_scanner.sort_modules_by_dependencies(sourcebatch.objectfiles, modules)
+
+ -- build modules
+ for _, objectfile in ipairs(objectfiles) do
+ local module = modules[objectfile]
+ if not module then
+ goto CONTINUE
+ end
+
+ local name, provide, cppfile = compiler_support.get_provided_module(module)
+ cppfile = cppfile or module.cppfile
+
+ local fileconfig = target:fileconfig(cppfile)
+ local bmifile = provide and compiler_support.get_bmi_path(provide.bmi)
+ local build = _should_build(target, cppfile, bmifile, objectfile, module.requires)
+
+ -- add objectfile if module is not from external dep
+ if not (fileconfig and fileconfig.external) then
+ target:add("objectfiles", objectfile)
+ end
+
+ -- needed to detect rebuild of dependencies
+ if provide then
+ compiler_support.memcache():set2(target:name(), name, build)
+ end
+
+ local deps = {}
+ for _, dep in ipairs(table.keys(module.requires or {})) do
+ table.insert(deps, opt.batchjobs and target:name() .. dep or dep)
+ end
+
+ opt.build_module(deps, build, module, name, provide, objectfile, cppfile, fileconfig)
+
+ ::CONTINUE::
+ end
+end
+
+-- build target headerunits
+function _build_headerunits(target, headerunits, opt)
+
+ local outputdir = compiler_support.headerunits_cachedir(target, {mkdir = true})
+ if opt.stl_headerunit then
+ outputdir = path.join(outputdir, "stl")
+ end
+
+ for _, headerunit in ipairs(headerunits) do
+ local outputdir = outputdir
+ if opt.stl_headerunit and headerunit.name:startswith("experimental/") then
+ outputdir = path.join(outputdir, "experimental")
+ end
+ local bmifile = path.join(outputdir, path.filename(headerunit.name) .. compiler_support.get_bmi_extension(target))
+ local key = path.normalize(headerunit.path)
+ local build = _should_build(target, headerunit.path, bmifile, nil, nil, {key = key, headerunit = true})
+
+ compiler_support.memcache():set2(target:name(), key, build)
+
+ opt.build_headerunit(headerunit, key, bmifile, outputdir, build)
+ end
+end
+
+-- should we build this module or headerunit ?
+function _should_build(target, sourcefile, bmifile, objectfile, requires, opt)
+
+ -- force rebuild a module if any of its module dependency is rebuilt
+ if requires then
+ for required, _ in pairs(requires) do
+ local m = get_from_target_mapper(target, required)
+ if m then
+ local rebuild = compiler_support.memcache():get2(target:name(), m.key)
+ if rebuild then
+ return true
+ end
+ end
+ end
+ end
+
+ -- or rebuild it if the file changed for headerunit and namedmodules
+ if compiler_support.has_module_extension(sourcefile) or (opt and opt.headerunit) then
+ local dryrun = option.get("dry-run")
+ local compinst = compiler.load("cxx", {target = target})
+ local compflags = compinst:compflags({sourcefile = sourcefile, target = target})
+
+ local dependfile = target:dependfile(bmifile or objectfile)
+ local dependinfo = target:is_rebuilt() and {} or (depend.load(dependfile) or {})
+
+ -- need build this object?
+ local depvalues = {compinst:program(), compflags}
+ local lastmtime = os.isfile(bmifile or objectfile) and os.mtime(dependfile) or 0
+
+ if dryrun or depend.is_changed(dependinfo, {lastmtime = lastmtime, values = depvalues}) then
+ return true
+ end
+ end
+
+ return false
+end
+
+-- generate meta module informations for package / other buildsystems import
+--
+-- e.g
+-- {
+-- "defines": ["FOO=BAR"]
+-- "imports": ["std", "bar"]
+-- "name": "foo"
+-- "file": "foo.cppm"
+-- }
+function _generate_meta_module_info(target, name, sourcefile, requires)
+
+ local modulehash = compiler_support.get_modulehash(target, sourcefile)
+ local module_metadata = {name = name, file = path.join(modulehash, path.filename(sourcefile))}
+
+ -- add definitions
+ module_metadata.defines = _builder(target).get_module_required_defines(target, sourcefile)
+
+ -- add imports
+ if requires then
+ for _name, _ in pairs(requires) do
+ module_metadata.imports = module_metadata.imports or {}
+ table.append(module_metadata.imports, _name)
+ end
+ end
+
+ return module_metadata
+end
+
+function _target_module_map_cachekey(target)
+ local mode = config.mode()
+ return target:name() .. "module_mapper" .. (mode or "")
+end
+
+function _is_duplicated_headerunit(target, headerunit)
+ local mapper = get_target_module_mapper(target)
+ local key = hash.md5(path.normalize(headerunit.path))
+
+ -- for _, mapped in pairs(mapper) do
+ -- print("CHECK", mapped.key, key)
+ -- if mapped.key == key then
+ -- return true
+ -- end
+ -- end
+
+ return false
+end
+
+-- add populate job
+function _init_build_for(target, batch, modules, opt)
+
+ if opt.batchjobs then
+ local job_name = get_modulemap_populate_jobname(target)
+ return { modulemap_populatejob_name = {
+ name = job_name,
+ job = batch:addjob(job_name, function(index, total)
+ progress.show((index * 100) / total, "${color.build.target}<%s> populating.%s.map", target:name(), opt.type)
+ _builder(target).populate_module_map(target, modules)
+ end)}}
+ else
+ batch:show_progress(opt.progress, "${color.build.target}<%s> populating.%s.map", target:name(), opt.type)
+ _builder(target).populate_module_map(target, modules)
+ end
+end
+
+function _builder(target)
+
+ local cachekey = tostring(target)
+ local builder = compiler_support.memcache():get2("builder", cachekey)
+ if builder == nil then
+ if target:has_tool("cxx", "clang", "clangxx") then
+ builder = import("clang.builder", {anonymous = true})
+ elseif target:has_tool("cxx", "gcc", "gxx") then
+ builder = import("gcc.builder", {anonymous = true})
+ elseif target:has_tool("cxx", "cl") then
+ builder = import("msvc.builder", {anonymous = true})
+ else
+ local _, toolname = target:tool("cxx")
+ raise("compiler(%s): does not support c++ module!", toolname)
+ end
+ compiler_support.memcache():set2("builder", cachekey, builder)
+ end
+ return builder
+end
+
+function get_modulemap_populate_jobname(target)
+ return target:name() .. "_module_map_populate"
+end
+
+-- build batchjobs for modules
+function build_batchjobs_for_modules(modules, batchjobs, rootjob)
+ return buildjobs(modules, batchjobs, rootjob)
+end
+
+-- build modules for batchjobs
+function build_modules_for_batchjobs(target, batchjobs, sourcebatch, modules, opt)
+
+ opt.rootjob = batchjobs:group_leave() or opt.rootjob
+ batchjobs:group_enter(target:name() .. "/build_modules", {rootjob = opt.rootjob})
+
+ local modulesjobs = {}
+
+ _build_modules(target, sourcebatch, modules, table.join(opt, {
+ build_module = function(deps, build, module, name, provide, objectfile, cppfile, fileconfig)
+ local job_name = name and target:name() .. name or cppfile
+
+ modulesjobs[job_name] = _builder(target).make_module_build_job(target, batchjobs, job_name, deps, {build = build, module = module, objectfile = objectfile, cppfile = cppfile})
+
+ if provide and fileconfig and fileconfig.public then
+ batchjobs:addjob(name .. "_metafile", function(index, total)
+ local metafilepath = compiler_support.get_metafile(target, cppfile)
+ depend.on_changed(function()
+ progress.show((index * 100) / total, "${color.build.target}<%s> generating.module.metadata %s", target:name(), name)
+ local metadata = _generate_meta_module_info(target, name, cppfile, module.requires)
+ json.savefile(metafilepath, metadata)
+ end, {dependfile = target:dependfile(metafilepath), files = {cppfile}, changed = target:is_rebuilt()})
+ end, {rootjob = opt.rootjob})
+ end
+ end
+ }))
+
+ local tailjob = _init_build_for(target, batchjobs, modules, table.join({type = "module"}, opt))
+ table.join2(modulesjobs, tailjob)
+
+ -- build batchjobs for modules
+ build_batchjobs_for_modules(modulesjobs, batchjobs, opt.rootjob)
+end
+
+-- build modules for batchcmds
+function build_modules_for_batchcmds(target, batchcmds, sourcebatch, modules, opt)
+
+ local depmtime = 0
+ opt.progress = opt.progress or 0
+ _init_build_for(target, batchcmds, modules, table.join({type = "module"}, opt))
+
+ -- build modules
+ _build_modules(target, sourcebatch, modules, table.join(opt, {
+ build_module = function(_, build, module, name, provide, objectfile, cppfile, fileconfig)
+ depmtime = math.max(depmtime, _builder(target).make_module_build_cmds(target, batchcmds, {build = build, module = module, cppfile = cppfile, objectfile = objectfile, progress = opt.progress}))
+
+ if provide and fileconfig and fileconfig.public then
+ local metafilepath = compiler_support.get_metafile(target, cppfile)
+ depend.on_changed(function()
+ progress.show(opt.progress, "${color.build.target}<%s> generating.module.metadata %s", target:name(), name)
+ local metadata = _generate_meta_module_info(target, name, cppfile, module.requires)
+ json.savefile(metafilepath, metadata)
+ end, {dependfile = target:dependfile(metafilepath), files = {cppfile}, changed = target:is_rebuilt()})
+ end
+ end
+ }))
+
+ batchcmds:set_depmtime(depmtime)
+end
+
+-- generate headerunits for batchjobs
+function build_headerunits_for_batchjobs(target, batchjobs, sourcebatch, modules, opt)
+
+ local user_headerunits, stl_headerunits = dependency_scanner.get_headerunits(target, sourcebatch, modules)
+ if not user_headerunits and not stl_headerunits then
+ return
+ end
+ -- we need new group(headerunits)
+ -- e.g. group(build_modules) -> group(headerunits)
+ opt.rootjob = batchjobs:group_leave() or opt.rootjob
+ batchjobs:group_enter(target:name() .. "/build_headerunits", {rootjob = opt.rootjob})
+
+ local build_headerunits = function(headerunits)
+ local modulesjobs = {}
+ _build_headerunits(target, headerunits, table.join(opt, {
+ build_headerunit = function(headerunit, key, bmifile, outputdir, build)
+ local job_name = target:name() .. key
+ local job = _builder(target).make_headerunit_build_job(target, job_name, batchjobs, headerunit, bmifile, outputdir, table.join(opt, {build = build}))
+ if job then
+ modulesjobs[job_name] = job
+ end
+ end
+ }))
+ build_batchjobs_for_modules(modulesjobs, batchjobs, opt.rootjob)
+ end
+
+ -- build stl header units first as other headerunits may need them
+ if stl_headerunits then
+ opt.stl_headerunit = true
+ build_headerunits(stl_headerunits)
+ end
+ if user_headerunits then
+ opt.stl_headerunit = false
+ build_headerunits(user_headerunits)
+ end
+end
+
+-- generate headerunits for batchcmds
+function build_headerunits_for_batchcmds(target, batchcmds, sourcebatch, modules, opt)
+
+ local user_headerunits, stl_headerunits = dependency_scanner.get_headerunits(target, sourcebatch, modules)
+ if not user_headerunits and not stl_headerunits then
+ return
+ end
+
+ local build_headerunits = function(headerunits)
+ local depmtime = 0
+ _build_headerunits(target, headerunits, table.join(opt, {
+ build_headerunit = function(headerunit, _, bmifile, outputdir, build)
+ depmtime = math.max(depmtime, _builder(target).make_headerunit_build_cmds(target, batchcmds, headerunit, bmifile, outputdir, table.join({build = build}, opt)))
+ end
+ }))
+ batchcmds:set_depmtime(depmtime)
+ end
+
+ -- build stl header units first as other headerunits may need them
+ if stl_headerunits then
+ opt.stl_headerunit = true
+ build_headerunits(stl_headerunits)
+ end
+ if user_headerunits then
+ opt.stl_headerunit = false
+ build_headerunits(user_headerunits)
+ end
+end
+
+-- append headerunits objectfiles to link
+function append_dependency_objectfiles(target)
+
+ local cachekey = target:name() .. "dependency_objectfiles"
+ local cache = compiler_support.localcache():get(cachekey)
+ if cache then
+ if target:is_binary() then
+ target:add("ldflags", cache, {force = true, expand = false})
+ elseif target:is_static() then
+ target:add("arflags", cache, {force = true, expand = false})
+ elseif target:is_shared() then
+ target:add("shflags", cache, {force = true, expand = false})
+ end
+ end
+end
+
+-- get or create a target module mapper
+function get_target_module_mapper(target)
+
+ opt = opt or {}
+ local memcache = compiler_support.memcache()
+ local mapper = memcache:get2(target:name(), "module_mapper")
+ if not mapper then
+ mapper = {}
+ memcache:set2(target:name(), "module_mapper", mapper)
+ end
+
+ return mapper
+end
+
+-- get a module or headerunit from target mapper
+function get_from_target_mapper(target, name)
+ local mapper = get_target_module_mapper(target)
+ if mapper[name] then
+ return mapper[name]
+ end
+end
+
+-- add a module to target mapper
+function add_module_to_target_mapper(target, name, sourcefile, bmifile, opt)
+ local mapper = get_target_module_mapper(target)
+ mapper[name] = {name = name, key = name, bmi = bmifile, sourcefile = sourcefile, opt = opt}
+end
+
+-- add a headerunit to target mapper
+function add_headerunit_to_target_mapper(target, headerunit, bmifile)
+ local mapper = get_target_module_mapper(target)
+ mapper[headerunit.name] = {name = headerunit.name, key = path.normalize(headerunit.path), headerunit = headerunit, bmi = bmifile}
+ return _is_duplicated_headerunit(target, headerunit)
+end
+
diff --git a/xmake/rules/c++/modules/modules_support/compiler_support.lua b/xmake/rules/c++/modules/modules_support/compiler_support.lua
new file mode 100644
index 000000000..5f1bc4617
--- /dev/null
+++ b/xmake/rules/c++/modules/modules_support/compiler_support.lua
@@ -0,0 +1,281 @@
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, TBOOX Open Source Group.
+--
+-- @author ruki, Arthapz
+-- @file compiler_support.lua
+--
+
+-- imports
+import("core.base.json")
+import("core.base.hashset")
+import("core.cache.memcache", {alias = "_memcache"})
+import("core.cache.localcache", {alias = "_localcache"})
+import("lib.detect.find_file")
+import("core.project.project")
+import("core.project.config")
+
+function _compiler_support(target)
+ local cachekey = tostring(target)
+ local compiler_support = memcache():get2("compiler_support", cachekey)
+ if compiler_support == nil then
+ if target:has_tool("cxx", "clang", "clangxx") then
+ compiler_support = import("clang.compiler_support", {anonymous = true})
+ elseif target:has_tool("cxx", "gcc", "gxx") then
+ compiler_support = import("gcc.compiler_support", {anonymous = true})
+ elseif target:has_tool("cxx", "cl") then
+ compiler_support = import("msvc.compiler_support", {anonymous = true})
+ else
+ local _, toolname = target:tool("cxx")
+ raise("compiler(%s): does not support c++ module!", toolname)
+ end
+ memcache():set2("compiler_support", cachekey, compiler_support)
+ end
+ return compiler_support
+end
+
+-- load module support for the current target
+function load(target)
+ _compiler_support(target).load(target)
+end
+
+-- patch sourcebatch
+function patch_sourcebatch(target, sourcebatch)
+ sourcebatch.sourcekind = "cxx"
+ sourcebatch.objectfiles = {}
+ sourcebatch.dependfiles = {}
+ for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
+ local objectfile = target:objectfile(sourcefile)
+ table.insert(sourcebatch.objectfiles, objectfile)
+
+ local dependfile = target:dependfile(sourcefile or objectfile)
+ table.insert(sourcebatch.dependfiles, dependfile)
+ end
+end
+
+-- cull sourcebatch objectfiles
+function cull_objectfiles(target, sourcebatch)
+
+ sourcebatch.sourcekind = "cxx"
+ sourcebatch.objectfiles = {}
+ for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
+ local fileconfig = target:fileconfig(sourcefile)
+ if not (fileconfig and fileconfig.external) then
+ local objectfile = target:objectfile(sourcefile)
+ table.insert(sourcebatch.objectfiles, objectfile)
+ end
+ end
+end
+
+-- get bmi extension
+function get_bmi_extension(target)
+ return _compiler_support(target).get_bmi_extension()
+end
+
+-- get bmi path
+-- @see https://github.com/xmake-io/xmake/issues/4063
+function get_bmi_path(bmifile)
+ bmifile = bmifile:gsub(":", "_PARTITION_")
+ return path.normalize(bmifile)
+end
+
+-- has module extension? e.g. *.mpp, ...
+function has_module_extension(sourcefile)
+ local modulexts = _g.modulexts
+ if modulexts == nil then
+ modulexts = hashset.of(".mpp", ".mxx", ".cppm", ".ixx")
+ _g.modulexts = modulexts
+ end
+ local extension = path.extension(sourcefile)
+ return modulexts:has(extension:lower())
+end
+
+-- this target contains module files?
+function contains_modules(target)
+ -- we can not use `"c++.build.builder"`, because it contains sourcekind/cxx.
+ local target_with_modules = target:sourcebatches()["c++.build.modules"] and true or false
+ if not target_with_modules then
+ target_with_modules = target:policy("build.c++.modules")
+ end
+ if not target_with_modules then
+ for _, dep in ipairs(target:orderdeps()) do
+ local sourcebatches = dep:sourcebatches()
+ if sourcebatches["c++.build.modules"] then
+ target_with_modules = true
+ break
+ end
+ end
+ end
+ return target_with_modules
+end
+
+-- load module infos
+function load_moduleinfos(target, sourcebatch)
+ local moduleinfos
+ for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
+ local dependfile = target:dependfile(sourcefile)
+ if os.isfile(dependfile) then
+ local data = io.load(dependfile)
+ if data then
+ moduleinfos = moduleinfos or {}
+ local moduleinfo = json.decode(data.moduleinfo)
+ moduleinfo.sourcefile = sourcefile
+ if moduleinfo then
+ table.insert(moduleinfos, moduleinfo)
+ end
+ end
+ end
+ end
+ return moduleinfos
+end
+
+function find_quote_header_file(target, sourcefile, file)
+ local p = path.join(path.directory(path.absolute(sourcefile, project.directory())), file)
+ assert(os.isfile(p))
+ return p
+end
+
+function find_angle_header_file(target, file)
+ local headerpaths = _compiler_support(target).toolchain_includedirs(target)
+ for _, dep in ipairs(target:orderdeps()) do
+ local includedirs = table.join(dep:get("sysincludedirs") or {}, dep:get("includedirs") or {})
+ table.join2(headerpaths, includedirs)
+ end
+ for _, pkg in pairs(target:pkgs()) do
+ local includedirs = table.join(pkg:get("sysincludedirs") or {}, pkg:get("includedirs") or {})
+ table.join2(headerpaths, includedirs)
+ end
+ table.join2(headerpaths, target:get("includedirs"))
+ local p = find_file(file, headerpaths)
+ assert(p, "find <%s> not found!", file)
+ return p
+end
+
+-- get stdmodules
+function get_stdmodules(target)
+ return _compiler_support(target).get_stdmodules(target)
+end
+
+-- get memcache
+function memcache()
+ return _memcache.cache("cxxmodules")
+end
+
+-- get localcache
+function localcache()
+ return _localcache.cache("cxxmodules")
+end
+
+
+-- get stl headerunits cache directory
+function stlheaderunits_cachedir(target, opt)
+ opt = opt or {}
+ local stlcachedir = path.join(target:autogendir(), "rules", "bmi", "cache", "stl-headerunits")
+ if opt.mkdir and not os.isdir(stlcachedir) then
+ os.mkdir(stlcachedir)
+ os.mkdir(path.join(stlcachedir, "experimental"))
+ end
+ return stlcachedir
+end
+-- get stl modules cache directory
+function stlmodules_cachedir(target, opt)
+ opt = opt or {}
+ local stlcachedir = path.join(target:autogendir(), "rules", "bmi", "cache", "stl-modules")
+ if opt.mkdir and not os.isdir(stlcachedir) then
+ os.mkdir(stlcachedir)
+ end
+ return stlcachedir
+end
+
+-- get headerunits cache directory
+function headerunits_cachedir(target, opt)
+ opt = opt or {}
+ local cachedir = path.join(target:autogendir(), "rules", "bmi", "cache", "headerunits")
+ if opt.mkdir and not os.isdir(cachedir) then
+ os.mkdir(cachedir)
+ end
+ return cachedir
+end
+
+-- get modules cache directory
+function modules_cachedir(target, opt)
+ opt = opt or {}
+ local cachedir = path.join(target:autogendir(), "rules", "bmi", "cache", "modules")
+ if opt.mkdir and not os.isdir(cachedir) then
+ os.mkdir(cachedir)
+ end
+ return cachedir
+end
+
+function get_modulehash(target, modulepath)
+ local key = path.directory(modulepath) .. target:name()
+ return hash.uuid(key):split("-", {plain = true})[1]:lower()
+end
+
+function get_metafile(target, modulefile)
+ local outputdir = get_outputdir(target, modulefile)
+ return path.join(outputdir, path.filename(modulefile) .. ".meta-info")
+end
+
+function get_outputdir(target, module)
+ local cachedir = module and modules_cachedir(target) or headerunits_cachedir(target)
+ local modulehash = get_modulehash(target, module.path or module)
+ local outputdir = path.join(cachedir, modulehash)
+ if not os.exists(outputdir) then
+ os.mkdir(outputdir)
+ end
+ return outputdir
+end
+
+-- get name provide info and cpp sourcefile of a module
+function get_provided_module(module)
+
+ local name, provide, cppfile
+ if module.provides then
+ -- assume there that provides is only one, until we encounter the cases
+ -- "Some compiler may choose to implement the :private module partition as a separate module for lookup purposes, and if so, it should be indicated as a separate provides entry."
+ local length = 0
+ for k, v in pairs(module.provides) do
+ length = length + 1
+ name = k
+ provide = v
+ cppfile = provide.sourcefile
+ if length > 1 then
+ raise("multiple provides are not supported now!")
+ end
+ break
+ end
+ end
+
+ return name, provide, cppfile
+end
+
+function install_module_target(target)
+ local sourcebatch = target:sourcebatches()["c++.build.modules.install"]
+ if sourcebatch and sourcebatch.sourcefiles then
+ for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
+ local fileconfig = target:fileconfig(sourcefile)
+ local install = fileconfig and fileconfig.public or false
+ if install then
+ local modulehash = get_modulehash(target, sourcefile)
+ local prefixdir = path.join("modules", modulehash)
+ target:add("installfiles", sourcefile, {prefixdir = prefixdir})
+ local metafile = get_metafile(target, sourcefile)
+ if os.exists(metafile) then
+ target:add("installfiles", metafile, {prefixdir = prefixdir})
+ end
+ end
+ end
+ end
+end
+
diff --git a/xmake/rules/c++/modules/modules_support/common.lua b/xmake/rules/c++/modules/modules_support/dependency_scanner.lua
index d42d0f958..830263d07 100644
--- a/xmake/rules/c++/modules/modules_support/common.lua
+++ b/xmake/rules/c++/modules/modules_support/dependency_scanner.lua
@@ -14,99 +14,39 @@
--
-- Copyright (C) 2015-present, TBOOX Open Source Group.
--
--- @author Arthapz, ruki
--- @file common.lua
+-- @author ruki, Arthapz
+-- @file dependency_scanner.lua
--
-- imports
import("core.base.json")
-import("core.base.bytes")
import("core.base.hashset")
-import("core.project.config")
-import("core.tool.compiler")
-import("core.cache.memcache", {alias = "_memcache"})
-import("core.cache.localcache", {alias = "_localcache"})
-import("core.project.project")
-import("lib.detect.find_file")
-import("private.async.buildjobs")
+import("compiler_support")
import("stl_headers")
--- get memcache
-function memcache()
- return _memcache.cache("cxxmodules")
-end
-
--- get localcache
-function localcache()
- return _localcache.cache("cxxmodules")
-end
-
--- get stl modules cache directory
-function stlmodules_cachedir(target, opt)
- opt = opt or {}
- local stlcachedir = path.join(config.buildir(), "stlmodules", "cache", target:arch(), config.mode() or "release")
- if opt.mkdir and not os.isdir(stlcachedir) then
- os.mkdir(stlcachedir)
- os.mkdir(path.join(stlcachedir, "experimental"))
- end
- return stlcachedir
-end
-
--- get modules cache directory
-function modules_cachedir(target, opt)
- opt = opt or {}
- local cachedir = path.join(target:autogendir(), "rules", "modules", "cache")
- if opt.mkdir and not os.isdir(cachedir) then
- os.mkdir(cachedir)
- end
- return cachedir
-end
-
--- get headerunits info
-function get_headerunits(target, sourcebatch, modules)
- local headerunits
- local stl_headerunits
- for _, objectfile in ipairs(sourcebatch.objectfiles) do
- local m = modules[objectfile]
- if m then
- for name, r in pairs(m.requires) do
- if r.method ~= "by-name" then
- local unittype = r.method == "include-angle" and ":angle" or ":quote"
- if stl_headers.is_stl_header(name) then
- stl_headerunits = stl_headerunits or {}
- if not table.find_if(stl_headerunits, function(i, v) return v.name == name end) then
- table.insert(stl_headerunits, {name = name, path = r.path, type = unittype, unique = r.unique})
- end
- else
- headerunits = headerunits or {}
- if not table.find_if(headerunits, function(i, v) return v.name == name end) then
- table.insert(headerunits, {name = name, path = r.path, type = unittype, unique = r.unique})
- end
- end
- end
- end
+function _dependency_scanner(target)
+ local cachekey = tostring(target)
+ local dependency_scanner = compiler_support.memcache():get2("dependency_scanner", cachekey)
+ if dependency_scanner == nil then
+ if target:has_tool("cxx", "clang", "clangxx") then
+ dependency_scanner = import("clang.dependency_scanner", {anonymous = true})
+ elseif target:has_tool("cxx", "gcc", "gxx") then
+ dependency_scanner = import("gcc.dependency_scanner", {anonymous = true})
+ elseif target:has_tool("cxx", "cl") then
+ dependency_scanner = import("msvc.dependency_scanner", {anonymous = true})
+ else
+ local _, toolname = target:tool("cxx")
+ raise("compiler(%s): does not support c++ module!", toolname)
end
+ compiler_support.memcache():set2("dependency_scanner", cachekey, dependency_scanner)
end
- return headerunits, stl_headerunits
+ return dependency_scanner
end
--- patch sourcebatch
-function patch_sourcebatch(target, sourcebatch)
- sourcebatch.sourcekind = "cxx"
- sourcebatch.objectfiles = {}
- sourcebatch.dependfiles = {}
- for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
- local objectfile = target:objectfile(sourcefile)
- local dependfile = target:dependfile(objectfile)
- table.insert(sourcebatch.objectfiles, objectfile)
- table.insert(sourcebatch.dependfiles, dependfile)
- end
-end
-
-function parse_meta_info(target, metafile)
+function _parse_meta_info(target, metafile)
local metadata = json.loadfile(metafile)
- if metadata._VENDOR_extension.xmake then
- return metadata._VENDOR_extension.xmake.file, metadata._VENDOR_extension.xmake.name, metadata
+ if metadata.file and metadata.name then
+ return metadata.file, metadata.name, metadata
end
local filename = path.basename(metafile)
@@ -133,130 +73,6 @@ function parse_meta_info(target, metafile)
return filename, name, metadata
end
--- extract packages modules dependencies
-function get_all_package_modules(target, modules, opt)
- local package_modules
-
- -- parse all meta-info and append their informations to the package store
- for _, package in pairs(target:pkgs()) do
- package_modules = package_modules or {}
- local modulesdir = path.join(package:installdir(), "modules")
- local metafiles = os.files(path.join(modulesdir, "*", "*.meta-info"))
- for _, metafile in ipairs(metafiles) do
- local modulefile, name, metadata = parse_meta_info(target, metafile)
- package_modules[name] = {
- file = path.join(modulesdir, modulefile),
- metadata = metadata
- }
- end
- end
- return package_modules
-end
-
--- cull unused modules
-function cull_unused_modules(target, modules, package_modules_data)
- local needed_modules = {}
- -- append all target dependencies
- for _, module in pairs(modules) do
- if module.requires then
- for required, _ in pairs(module.requires) do
- table.insert(needed_modules, required)
- end
- end
- end
-
- -- append all package dependencies
- local culled
- local module_names = table.keys(package_modules_data)
- for _, name in ipairs(module_names) do
- culled = culled or {}
- if table.find(needed_modules, name) and package_modules_data[name] and not culled[name] then
- culled[name] = package_modules_data[name]
-
- if culled[name].metadata.imports then
- table.join2(needed_modules, culled[name].metadata.imports)
- table.join2(module_names, culled[name].metadata.imports)
- end
- end
- end
- return culled
-end
-
--- get modules support
-function modules_support(target)
- local cachekey = tostring(target)
- local module_builder = memcache():get2("modules_support", cachekey)
- if module_builder == nil then
- if target:has_tool("cxx", "clang", "clangxx") then
- module_builder = import("clang", {anonymous = true})
- elseif target:has_tool("cxx", "gcc", "gxx") then
- module_builder = import("gcc", {anonymous = true})
- elseif target:has_tool("cxx", "cl") then
- module_builder = import("msvc", {anonymous = true})
- else
- local _, toolname = target:tool("cxx")
- raise("compiler(%s): does not support c++ module!", toolname)
- end
- memcache():set2("modules_support", cachekey, module_builder)
- end
- return module_builder
-end
-
--- get bmi extension
-function bmi_extension(target)
- return modules_support(target).get_bmi_extension()
-end
-
--- has module extension? e.g. *.mpp, ...
-function has_module_extension(sourcefile)
- local modulexts = _g.modulexts
- if modulexts == nil then
- modulexts = hashset.of(".mpp", ".mxx", ".cppm", ".ixx")
- _g.modulexts = modulexts
- end
- local extension = path.extension(sourcefile)
- return modulexts:has(extension:lower())
-end
-
--- this target contains module files?
-function contains_modules(target)
- -- we can not use `"c++.build.modules.builder"`, because it contains sourcekind/cxx.
- local target_with_modules = target:sourcebatches()["c++.build.modules"] and true or false
- if not target_with_modules then
- target_with_modules = target:policy("build.c++.modules")
- end
- if not target_with_modules then
- for _, dep in ipairs(target:orderdeps()) do
- local sourcebatches = dep:sourcebatches()
- if sourcebatches["c++.build.modules"] then
- target_with_modules = true
- break
- end
- end
- end
- return target_with_modules
-end
-
--- load module infos
-function load_moduleinfos(target, sourcebatch)
- local moduleinfos
- for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
- local dependfile = target:dependfile(sourcefile)
- if os.isfile(dependfile) then
- local data = io.load(dependfile)
- if data then
- moduleinfos = moduleinfos or {}
- local moduleinfo = json.decode(data.moduleinfo)
- moduleinfo.sourcefile = sourcefile
- if moduleinfo then
- table.insert(moduleinfos, moduleinfo)
- end
- end
- end
- end
- return moduleinfos
-end
-
-- parse module dependency data
--[[
{
@@ -300,7 +116,7 @@ function _parse_dependencies_data(target, moduleinfos)
-- try to find the compiled module path in outputs filed (MSVC doesn't generate compiled-module-path)
if not bmifile then
for _, output in ipairs(rule.outputs) do
- if output:endswith(".ifc") or output:endswith(".pcm") or output:endswith(".bmi") then
+ if output:endswith(compiler_support.get_bmi_extension(target)) then
bmifile = output
break
end
@@ -308,11 +124,11 @@ function _parse_dependencies_data(target, moduleinfos)
-- we didn't found the compiled module path, so we assume it
if not bmifile then
- local name = provide["logical-name"] .. bmi_extension(target)
+ local name = provide["logical-name"] .. compiler_support.get_bmi_extension(target)
-- partition ":" character is invalid path character on windows
-- @see https://github.com/xmake-io/xmake/issues/2954
name = name:replace(":", "-")
- bmifile = path.join(get_outputdir(target, name), name)
+ bmifile = path.join(compiler_support.get_outputdir(target, name), name)
end
end
m.provides[provide["logical-name"]] = {
@@ -354,6 +170,7 @@ function _parse_dependencies_data(target, moduleinfos)
return modules
end
+
-- check circular dependencies for the given module
function _check_circular_dependencies_of_module(name, moduledeps, modulesources, depspath)
for _, dep in ipairs(moduledeps[name]) do
@@ -418,7 +235,7 @@ function _topological_sort_visit(node, nodes, modules, output)
if not n.tempmarked then
local m2 = modules[n.objectfile]
if m2 then
- for name, provide in pairs(m1.provides) do
+ for name, _ in pairs(m1.provides) do
if m2.requires and m2.requires[name] then
_topological_sort_visit(n, nodes, modules, output)
end
@@ -448,43 +265,80 @@ function _topological_sort_get_first_unmarked_node(nodes)
end
end
--- topological sort
-function sort_modules_by_dependencies(objectfiles, modules)
- local output = {}
- local nodes = {}
- for _, objectfile in ipairs(objectfiles) do
- local m = modules[objectfile]
- if m then
- table.insert(nodes, {marked = false, tempmarked = false, objectfile = objectfile})
- end
- end
- while _topological_sort_has_node_without_mark(nodes) do
- local node = _topological_sort_get_first_unmarked_node(nodes)
- _topological_sort_visit(node, nodes, modules, output)
+function _fill_needed_module(target, modules, module)
+ local needed_modules = {}
+
+ for required_name, required_module in pairs(module.requires) do
+ table.insert(needed_modules, required_name)
+ table.join2(needed_modules, _fill_needed_module(target, modules, required_module))
end
- return output
+
+ return needed_modules
end
-function find_quote_header_file(target, sourcefile, file)
- local p = path.join(path.directory(path.absolute(sourcefile, project.directory())), file)
- assert(os.isfile(p))
- return p
+function _get_package_modules(target, package, opt)
+ local package_modules
+
+ local modulesdir = path.join(package:installdir(), "modules")
+ local metafiles = os.files(path.join(modulesdir, "*", "*.meta-info"))
+ for _, metafile in ipairs(metafiles) do
+ package_modules = package_modules or {}
+ local modulefile, name, metadata = _parse_meta_info(target, metafile)
+ package_modules[name] = {file = path.join(modulesdir, modulefile), metadata = metadata}
+ end
+
+ return package_modules
end
-function find_angle_header_file(target, file)
- local headerpaths = modules_support(target).toolchain_includedirs(target)
- for _, dep in ipairs(target:orderdeps()) do
- local includedirs = table.join(dep:get("sysincludedirs") or {}, dep:get("includedirs") or {})
- table.join2(headerpaths, includedirs)
+-- get module dependencies
+function get_module_dependencies(target, sourcebatch, opt)
+ local cachekey = target:name() .. "/" .. sourcebatch.rulename
+ local modules = compiler_support.memcache():get2("modules", cachekey)
+ if modules == nil or opt.regenerate then
+ modules = compiler_support.localcache():get2("modules", cachekey)
+ opt.progress = opt.progress or 0
+ local changed = _dependency_scanner(target).generate_dependencies(target, sourcebatch, opt)
+ if changed or modules == nil then
+ local moduleinfos = compiler_support.load_moduleinfos(target, sourcebatch)
+ modules = _parse_dependencies_data(target, moduleinfos)
+ if modules then
+ _check_circular_dependencies(modules)
+ end
+ modules = cull_unused_modules(target, modules)
+ compiler_support.localcache():set2("modules", cachekey, modules)
+ compiler_support.localcache():save()
+ end
+ compiler_support.memcache():set2("modules", cachekey, modules)
end
- for _, pkg in pairs(target:pkgs()) do
- local includedirs = table.join(pkg:get("sysincludedirs") or {}, pkg:get("includedirs") or {})
- table.join2(headerpaths, includedirs)
+ return modules
+end
+
+-- get headerunits info
+function get_headerunits(target, sourcebatch, modules)
+ local headerunits
+ local stl_headerunits
+ for _, objectfile in ipairs(sourcebatch.objectfiles) do
+ local m = modules[objectfile]
+ if m then
+ for name, r in pairs(m.requires) do
+ if r.method ~= "by-name" then
+ local unittype = r.method == "include-angle" and ":angle" or ":quote"
+ if stl_headers.is_stl_header(name) then
+ stl_headerunits = stl_headerunits or {}
+ if not table.find_if(stl_headerunits, function(i, v) return v.name == name end) then
+ table.insert(stl_headerunits, {name = name, path = r.path, type = unittype, unique = r.unique})
+ end
+ else
+ headerunits = headerunits or {}
+ if not table.find_if(headerunits, function(i, v) return v.name == name end) then
+ table.insert(headerunits, {name = name, path = r.path, type = unittype, unique = r.unique})
+ end
+ end
+ end
+ end
+ end
end
- table.join2(headerpaths, target:get("includedirs"))
- local p = find_file(file, headerpaths)
- assert(p, "find <%s> not found!", file)
- return p
+ return headerunits, stl_headerunits
end
-- https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html
@@ -545,7 +399,7 @@ function fallback_generate_dependencies(target, jsonfile, sourcefile, preprocess
local module_depname = line:match("import%s+(.+)%s*;")
-- we need to parse module interface dep in cxx/impl_unit.cpp, e.g. hello.mpp and hello_impl.cpp
-- @see https://github.com/xmake-io/xmake/pull/2664#issuecomment-1213167314
- if not module_depname and not has_module_extension(sourcefile) then
+ if not module_depname and not compiler_support.has_module_extension(sourcefile) then
module_depname = module_name_private
end
if module_depname and not module_deps_set:has(module_depname) then
@@ -560,12 +414,12 @@ function fallback_generate_dependencies(target, jsonfile, sourcefile, preprocess
module_depname = module_depname:sub(2, -2)
module_dep["lookup-method"] = "include-quote"
module_dep["unique-on-source-path"] = true
- module_dep["source-path"] = find_quote_header_file(target, sourcefile, module_depname)
+ module_dep["source-path"] = compiler_support.find_quote_header_file(target, sourcefile, module_depname)
elseif module_depname:startswith("<") then
module_depname = module_depname:sub(2, -2)
module_dep["lookup-method"] = "include-angle"
module_dep["unique-on-source-path"] = true
- module_dep["source-path"] = find_angle_header_file(target, module_depname)
+ module_dep["source-path"] = compiler_support.find_angle_header_file(target, module_depname)
end
module_dep["logical-name"] = module_depname
table.insert(module_deps, module_dep)
@@ -575,13 +429,13 @@ function fallback_generate_dependencies(target, jsonfile, sourcefile, preprocess
end
if module_name_export or internal then
- local outputdir = get_outputdir(target, sourcefile)
+ local outputdir = compiler_support.get_outputdir(target, sourcefile)
local provide = {}
provide["logical-name"] = module_name_export or module_name_private
provide["source-path"] = sourcefile
provide["is-interface"] = not internal
- provide["compiled-module-path"] = path.join(outputdir, (module_name_export or module_name_private) .. bmi_extension(target))
+ provide["compiled-module-path"] = path.join(outputdir, (module_name_export or module_name_private) .. compiler_support.get_bmi_extension(target))
rule.provides = {}
table.insert(rule.provides, provide)
@@ -593,145 +447,104 @@ function fallback_generate_dependencies(target, jsonfile, sourcefile, preprocess
io.writefile(jsonfile, jsondata)
end
--- get module dependencies
-function get_module_dependencies(target, sourcebatch, opt)
- local cachekey = target:name() .. "/" .. sourcebatch.rulename
- local modules = memcache():get2("modules", cachekey)
- if modules == nil or opt.regenerate then
- modules = localcache():get2("modules", cachekey)
- opt.progress = opt.progress or 0
- local changed = modules_support(target).generate_dependencies(target, sourcebatch, opt)
- if changed or modules == nil then
- local moduleinfos = load_moduleinfos(target, sourcebatch)
- modules = _parse_dependencies_data(target, moduleinfos)
- modules = table.join(modules or {}, modules_support(target).get_stdmodules(target))
- if modules then
- _check_circular_dependencies(modules)
- end
- localcache():set2("modules", cachekey, modules)
- localcache():save()
- end
- memcache():set2("modules", cachekey, modules)
- end
- return modules
-end
-
--- generate headerunits for batchcmds
-function generate_headerunits_for_batchcmds(target, batchcmds, sourcebatch, modules, opt)
- local user_headerunits, stl_headerunits = get_headerunits(target, sourcebatch, modules)
- -- build stl header units as other headerunits may need them
- if stl_headerunits then
- modules_support(target).generate_stl_headerunits_for_batchcmds(target, batchcmds, stl_headerunits, opt)
- end
- if user_headerunits then
- modules_support(target).generate_user_headerunits_for_batchcmds(target, batchcmds, user_headerunits, opt)
- end
-end
-
--- build batchjobs for modules
-function build_batchjobs_for_modules(modules, batchjobs, rootjob)
- return buildjobs(modules, batchjobs, rootjob)
-end
-
--- build modules for batchjobs
-function build_modules_for_batchjobs(target, batchjobs, sourcebatch, modules, opt)
- local objectfiles = sort_modules_by_dependencies(sourcebatch.objectfiles, modules)
- modules_support(target).build_modules_for_batchjobs(target, batchjobs, objectfiles, modules, opt)
-end
+-- extract packages modules dependencies
+function get_all_packages_modules(target, opt)
+ local packages_modules
--- build modules for batchcmds
-function build_modules_for_batchcmds(target, batchcmds, sourcebatch, modules, opt)
- local objectfiles = sort_modules_by_dependencies(sourcebatch.objectfiles, modules)
- modules_support(target).build_modules_for_batchcmds(target, batchcmds, objectfiles, modules, opt)
-end
+ -- parse all meta-info and append their informations to the package store
+ local packages = target:pkgs() or {}
--- append headerunits objectfiles to link
-function append_dependency_objectfiles(target)
- local cachekey = target:name() .. "dependency_objectfiles"
- local cache = localcache():get(cachekey)
- if cache then
- if target:is_binary() then
- target:add("ldflags", cache, {force = true, expand = false})
- elseif target:is_static() then
- target:add("arflags", cache, {force = true, expand = false})
- elseif target:is_shared() then
- target:add("shflags", cache, {force = true, expand = false})
- end
+ for _, deps in pairs(target:orderdeps()) do
+ table.join2(packages, deps:pkgs())
end
-end
-
--- generate meta module informations for package / other buildsystems import
--- based on https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2473r1.pdf
---
--- e.g
--- {
--- "include_paths": ["foo/", "bar/"]
--- "definitions": ["FOO=BAR"]
--- "imports": ["std", "bar"]
--- "_VENDOR_extension": {}
--- }
-function generate_meta_module_info(target, name, sourcefile, requires)
- local module_metadata = {}
- -- add include paths
- module_metadata.include_paths = table.wrap(target:get("includedirs")) or {}
- for _, deps in ipairs(target:orderdeps()) do
- table.join2(module_metadata.include_paths, deps:get("includedirs") or {})
+ for _, package in pairs(packages) do
+ local package_modules = _get_package_modules(target, package, opt)
+ if package_modules then
+ packages_modules = packages_modules or {}
+ table.join2(packages_modules, package_modules)
+ end
end
- -- add definitions
- module_metadata.definitions = table.wrap(target:get("defines")) or {}
- for _, deps in ipairs(target:orderdeps()) do
- table.join2(module_metadata.definitions, deps:get("defines") or {})
- end
+ return packages_modules
+end
- -- add imports
- if requires then
- for name, _ in pairs(requires) do
- module_metadata.imports = module_metadata.imports or {}
- table.append(module_metadata.imports, name)
+-- topological sort
+function sort_modules_by_dependencies(objectfiles, modules)
+ local output = {}
+ local nodes = {}
+ for _, objectfile in ipairs(objectfiles) do
+ local m = modules[objectfile]
+ if m then
+ table.insert(nodes, {marked = false, tempmarked = false, objectfile = objectfile})
end
end
-
- local modulehash = get_modulehash(target, sourcefile)
- module_metadata._VENDOR_extension = { xmake = { name = name, file = path.join(modulehash, path.filename(sourcefile)) }}
- return module_metadata
+ while _topological_sort_has_node_without_mark(nodes) do
+ local node = _topological_sort_get_first_unmarked_node(nodes)
+ _topological_sort_visit(node, nodes, modules, output)
+ end
+ return output
end
-function install_module_target(target)
- local sourcebatch = target:sourcebatches()["c++.build.modules.install"]
- if sourcebatch and sourcebatch.sourcefiles then
- for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
- local install = (fileconfig and not fileconfig.install) and false or true
- if install then
- local modulehash = get_modulehash(target, sourcefile)
- prefixdir = path.join("modules", modulehash)
- target:add("installfiles", sourcefile, {prefixdir = prefixdir})
- local metafile = get_metafile(target, sourcefile)
- if os.exists(metafile) then
- target:add("installfiles", metafile, {prefixdir = prefixdir})
+-- get source modulefile for external target deps
+function get_targetdeps_modules(target)
+ local sourcefiles
+ 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
+ local fileconfig = dep:fileconfig(sourcefile)
+ local public = (fileconfig and fileconfig.public and not fileconfig.external) or false
+ if public then
+ sourcefiles = sourcefiles or {}
+ table.insert(sourcefiles, sourcefile)
+ target:fileconfig_add(sourcefile, {external = true})
end
end
end
end
+ return sourcefiles
end
-function get_modulehash(target, modulepath)
- local key = path.directory(modulepath) .. target:name()
- return hash.uuid(key):split("-", {plain = true})[1]:lower()
-end
+-- cull unused packages modules
+-- removed named module not used in the translation units
+-- when building a library we only cull external modules because we need module objectfiles to be linked inside the library
+-- on an executable we cull explicitly referenced module
+function cull_unused_modules(target, modules)
-function get_metafile(target, modulefile)
- local outputdir = get_outputdir(target, modulefile)
- return path.join(outputdir, path.filename(modulefile) .. ".meta-info")
-end
+ local cull_all_modules = target:kind() == "executable"
+
+ local needed_modules = {}
+ for _, module in pairs(modules) do
+ local fileconfig = target:fileconfig(module.sourcefile)
+ local external = fileconfig and fileconfig.external
+ if not (cull_all_modules and external) then
+ goto CONTINUE
+ end
-function get_outputdir(target, module)
- local cachedir = modules_cachedir(target)
- local modulehash = get_modulehash(target, module.path or module)
- local outputdir = path.join(cachedir, modulehash)
- if not os.exists(outputdir) then
- os.mkdir(outputdir)
+ if module.provides and module.requires then
+ table.join2(needed_modules, _fill_needed_module(target, modules, module))
+ end
+
+ ::CONTINUE::
+ end
+
+ local culled = {}
+ for objectfile, module in pairs(modules) do
+ -- if cull_all_modules and modules.provides then
+ -- local name,_,_ = compiler_support.get_provided_module(module)
+ -- if module.requires then
+ -- for required, _ in pairs(module.requires) do
+ -- table.insert(needed_modules, required)
+ -- end
+ -- end
+ -- if table.find(needed_modules, name) then
+ -- culled[objectfile] = module
+ -- end
+ -- else
+ culled[objectfile] = module
+ -- end
end
- return outputdir
+
+ return culled
end
diff --git a/xmake/rules/c++/modules/modules_support/stl_headers.lua b/xmake/rules/c++/modules/modules_support/stl_headers.lua
index 2bc108765..4b2d6ed23 100644
--- a/xmake/rules/c++/modules/modules_support/stl_headers.lua
+++ b/xmake/rules/c++/modules/modules_support/stl_headers.lua
@@ -14,7 +14,7 @@
--
-- Copyright (C) 2015-present, TBOOX Open Source Group.
--
--- @author Arthapz, ruki
+-- @author ruki, Arthapz
-- @file stl_headers.lua
--