summaryrefslogtreecommitdiff
path: root/xmake/rules/c++/modules/support.lua
diff options
context:
space:
mode:
authorArthur LAURENT <[email protected]>2025-04-20 18:23:54 +0200
committerArthur LAURENT <[email protected]>2025-04-20 18:23:54 +0200
commita1fefbea336ad2294b853059d32850877f54bb90 (patch)
tree532fb41683d4e8024897e5d246fa951a3ebaab1f /xmake/rules/c++/modules/support.lua
parentedf1f0b82e739e1024b34f112fba51bc61444c65 (diff)
(C++ modules) simplify names of module support
Diffstat (limited to 'xmake/rules/c++/modules/support.lua')
-rw-r--r--xmake/rules/c++/modules/support.lua274
1 files changed, 274 insertions, 0 deletions
diff --git a/xmake/rules/c++/modules/support.lua b/xmake/rules/c++/modules/support.lua
new file mode 100644
index 000000000..e1e244c08
--- /dev/null
+++ b/xmake/rules/c++/modules/support.lua
@@ -0,0 +1,274 @@
+-- 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 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 _support(target)
+ local cachekey = tostring(target)
+ local support = memcache():get2("support", cachekey)
+ if support == nil then
+ if target:has_tool("cxx", "clang", "clangxx", "clang_cl") then
+ support = import("clang.support", {anonymous = true})
+ elseif target:has_tool("cxx", "gcc", "gxx") then
+ support = import("gcc.support", {anonymous = true})
+ elseif target:has_tool("cxx", "cl") then
+ support = import("msvc.support", {anonymous = true})
+ else
+ local _, toolname = target:tool("cxx")
+ raise("compiler(%s): does not support c++ module!", toolname)
+ end
+ memcache():set2("support", cachekey, support)
+ end
+ return support
+end
+
+-- load module support for the current target
+function load(target)
+
+ -- At least std c++20 is required, and we should call `set_languages("c++20")` to set it
+ local languages = target:get("languages")
+ local cxxlang = false
+ for _, lang in ipairs(languages) do
+ if lang:find("cxx", 1, true) or lang:find("c++", 1, true) or lang:find("gnuxx", 1, true) or lang:find("gnu++", 1, true) then
+ cxxlang = true
+ break
+ end
+ end
+ if not cxxlang then
+ target:add("languages", "c++20")
+ end
+
+ -- load module support for the specific compiler
+ _support(target).load(target)
+end
+
+-- strip flags not relevent for module reuse
+function strip_flags(target, flags)
+ return _support(target).strip_flags(target, flags)
+end
+
+-- get bmi extension
+function get_bmi_extension(target)
+ return _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, opt)
+ opt = opt or {}
+ local modulexts = _g.modulexts
+ if modulexts == nil then
+ modulexts = hashset.of(".mpp", ".mxx", ".cppm", ".ixx")
+ _g.modulexts = modulexts
+ end
+ local extension = opt.extension or 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), "\"%s\" not found", p)
+ return p
+end
+
+function find_angle_header_file(target, file)
+ local headerpaths = _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 ipairs(target:orderpkgs()) 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, "<%s> not found!", file)
+ return p
+end
+
+-- get stdmodules
+function get_stdmodules(target)
+ return _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:fullname()
+ 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 add_installfiles_for_modules(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
+