summaryrefslogtreecommitdiff
path: root/xmake/rules/c++/modules/modules_support
diff options
context:
space:
mode:
authorArthur LAURENT <[email protected]>2025-04-18 14:09:48 +0200
committerArthur LAURENT <[email protected]>2025-04-20 16:54:51 +0200
commitedf1f0b82e739e1024b34f112fba51bc61444c65 (patch)
tree8538b3a5898f4fd1d17a8a0f6246761b253c58ca /xmake/rules/c++/modules/modules_support
parent08a71b4242c8f9f1c61bf585f6518b04e2b2de98 (diff)
(C++ modules) simplify module support directory tree
Diffstat (limited to 'xmake/rules/c++/modules/modules_support')
-rw-r--r--xmake/rules/c++/modules/modules_support/builder.lua618
-rw-r--r--xmake/rules/c++/modules/modules_support/clang/builder.lua478
-rw-r--r--xmake/rules/c++/modules/modules_support/clang/compiler_support.lua499
-rw-r--r--xmake/rules/c++/modules/modules_support/clang/dependency_scanner.lua89
-rw-r--r--xmake/rules/c++/modules/modules_support/compiler_support.lua274
-rw-r--r--xmake/rules/c++/modules/modules_support/dependency_scanner.lua516
-rw-r--r--xmake/rules/c++/modules/modules_support/gcc/builder.lua469
-rw-r--r--xmake/rules/c++/modules/modules_support/gcc/compiler_support.lua325
-rw-r--r--xmake/rules/c++/modules/modules_support/gcc/dependency_scanner.lua79
-rw-r--r--xmake/rules/c++/modules/modules_support/msvc/builder.lua512
-rw-r--r--xmake/rules/c++/modules/modules_support/msvc/compiler_support.lua300
-rw-r--r--xmake/rules/c++/modules/modules_support/msvc/dependency_scanner.lua68
-rw-r--r--xmake/rules/c++/modules/modules_support/stl_headers.lua155
13 files changed, 0 insertions, 4382 deletions
diff --git a/xmake/rules/c++/modules/modules_support/builder.lua b/xmake/rules/c++/modules/modules_support/builder.lua
deleted file mode 100644
index a02e1e7d3..000000000
--- a/xmake/rules/c++/modules/modules_support/builder.lua
+++ /dev/null
@@ -1,618 +0,0 @@
---!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("core.base.hashset")
-import("async.runjobs")
-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 = sourcebatch.objectfiles
- for _, objectfile in ipairs(objectfiles) do
- local module = modules[objectfile]
- if not module then
- goto continue
- end
-
- local name, _, cppfile = compiler_support.get_provided_module(module)
- cppfile = cppfile or module.cppfile
-
- local deps = {}
- for name, req in pairs(module.requires or {}) do
- -- we need to use the full path as dep name if requre item is headerunit
- local dep = name
- if req.method:startswith("include-") and req.path then
- dep = req.path
- end
- dep = path.normalize(dep)
- local depname = target:fullname() .. "/module/" .. dep
- table.insert(deps, depname)
- end
- opt.build_module(deps, module, name, objectfile, cppfile)
-
- ::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, {key = key, headerunit = true})
- if build then
- mark_build(target, key)
- end
- opt.build_headerunit(headerunit, key, bmifile, outputdir, build)
- end
-end
-
--- check if flags are compatible for module reuse
-function _are_flags_compatible(target, other, cppfile)
- local compinst1 = target:compiler("cxx")
- local flags1 = compinst1:compflags({sourcefile = cppfile, target = target})
-
- local compinst2 = other:compiler("cxx")
- local flags2 = compinst2:compflags({sourcefile = cppfile, target = other})
-
- -- strip unrelevent flags
- flags1 = compiler_support.strip_flags(target, flags1)
- flags2 = compiler_support.strip_flags(target, flags2)
-
- if #flags1 ~= #flags2 then
- return false
- end
-
- table.sort(flags1)
- table.sort(flags2)
-
- for i = 1, #flags1 do
- if flags1[i] ~= flags2[i] then
- return false
- end
- end
- return true
-end
-
--- try to reuse modules from other target
-function _try_reuse_modules(target, modules)
- for _, module in pairs(modules) do
- local name, provide, cppfile = compiler_support.get_provided_module(module)
- if not provide then
- goto continue
- end
-
- cppfile = cppfile or module.cppfile
-
- local fileconfig = target:fileconfig(cppfile)
- local public = fileconfig and (fileconfig.public or fileconfig.external)
- if not public then
- goto continue
- end
-
- for _, dep in ipairs(target:orderdeps()) do
- if not _are_flags_compatible(target, dep, cppfile) then
- goto nextdep
- end
- local mapped = get_from_target_mapper(dep, name)
- if mapped then
- compiler_support.memcache():set2(target:fullname() .. name, "reuse", true)
- add_module_to_target_mapper(target, mapped.name, mapped.sourcefile, mapped.bmi, table.join(mapped.opt or {}, {target = dep}))
- break
- end
- ::nextdep::
- end
-
- ::continue::
- end
- return modules
-end
-
--- should we build this module or headerunit ?
-function should_build(target, sourcefile, bmifile, opt)
- opt = opt or {}
- local objectfile = opt.objectfile
- 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 {})
- local depvalues = {compinst:program(), compflags}
-
- -- force rebuild a module if any of its module dependency is rebuilt
- local requires = opt.requires
- if requires then
- for required, _ in table.orderpairs(requires) do
- local m = get_from_target_mapper(target, required)
- if m then
- local rebuild = (m.opt and m.opt.target) and compiler_support.memcache():get2("should_build_in_" .. m.opt.target:fullname(), m.key)
- or compiler_support.memcache():get2("should_build_in_" .. target:fullname(), m.key)
- if rebuild then
- dependinfo.files = {}
- table.insert(dependinfo.files, sourcefile)
- dependinfo.values = depvalues
- return true, dependinfo
- end
- end
- end
- end
-
- -- reused
- if opt.name then
- local m = get_from_target_mapper(target, opt.name)
- if m and m.opt and m.opt.target then
- local rebuild = compiler_support.memcache():get2("should_build_in_" .. m.opt.target:fullname(), m.key)
- if rebuild then
- dependinfo.files = {}
- table.insert(dependinfo.files, sourcefile)
- dependinfo.values = depvalues
- end
- return rebuild, dependinfo
- end
- end
-
- -- need build this object?
- local dryrun = option.get("dry-run")
- 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
- dependinfo.files = {}
- table.insert(dependinfo.files, sourcefile)
- dependinfo.values = depvalues
- return true, dependinfo
- 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 table.orderpairs(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:fullname() .. "module_mapper" .. (mode or "")
-end
-
-function _is_duplicated_headerunit(target, key)
- local _, mapper_keys = get_target_module_mapper(target)
- return mapper_keys[key]
-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", "clang_cl") 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 mark_build(target, name)
- compiler_support.memcache():set2("should_build_in_" .. target:fullname(), name, true)
-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:fullname() .. "/module/build_modules", {rootjob = opt.rootjob})
-
- -- add populate module job
- local modulesjobs = {}
- local populate_jobname = target:fullname() .. "/module/populate_module_map"
- modulesjobs[populate_jobname] = {
- name = populate_jobname,
- job = batchjobs:newjob(populate_jobname, function(_, _)
- _try_reuse_modules(target, modules)
- _builder(target).populate_module_map(target, modules)
- end)
- }
-
- -- add module jobs
- _build_modules(target, sourcebatch, modules, table.join(opt, {
- build_module = function(deps, module, name, objectfile, cppfile)
- local job_name = target:fullname() .. "/module/" .. path.normalize(name or cppfile)
- modulesjobs[job_name] = _builder(target).make_module_buildjobs(target, batchjobs, job_name, deps,
- {module = module, objectfile = objectfile, cppfile = cppfile})
- end
- }))
-
- -- build batchjobs for modules
- build_batchjobs_for_modules(modulesjobs, batchjobs, opt.rootjob)
-end
-
--- build modules for jobgraph
-function build_modules_for_jobgraph(target, jobgraph, sourcebatch, modules, opt)
- local jobdeps = {}
- local jobsize = jobgraph:size()
- local build_modules_group = target:fullname() .. "/module/build_modules"
- jobgraph:group(build_modules_group, function ()
-
- -- add populate module job
- local populate_jobname = target:fullname() .. "/module/populate_module_map"
- jobgraph:add(populate_jobname, function(index, total, opt)
- _try_reuse_modules(target, modules)
- _builder(target).populate_module_map(target, modules)
- end)
-
- -- add module jobs
- _build_modules(target, sourcebatch, modules, table.join(opt, {
- build_module = function(deps, module, name, objectfile, cppfile)
- local jobname = target:fullname() .. "/module/" .. path.normalize(name or cppfile)
- _builder(target).make_module_jobgraph(target, jobgraph, {
- module = module, objectfile = objectfile, cppfile = cppfile
- })
- jobdeps[jobname] = table.join(populate_jobname, deps)
- end})
- )
- end)
- if jobgraph:size() > jobsize then
- return build_modules_group, jobdeps
- end
-end
-
--- build modules for batchcmds
-function build_modules_for_batchcmds(target, batchcmds, sourcebatch, modules, opt)
- local depmtime = 0
- opt.progress = opt.progress or 0
-
- _try_reuse_modules(target, modules)
- _builder(target).populate_module_map(target, modules)
-
- -- build modules
- _build_modules(target, sourcebatch, modules, table.join(opt, {
- build_module = function(_, module, _, objectfile, cppfile)
- depmtime = math.max(depmtime, _builder(target).make_module_buildcmds(target, batchcmds, {
- module = module, cppfile = cppfile, objectfile = objectfile, progress = opt.progress}))
- end
- }))
-
- batchcmds:set_depmtime(depmtime)
-end
-
--- build 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:fullname() .. "/module/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:fullname() .. "/module/" .. path.normalize(key)
- local job = _builder(target).make_headerunit_buildjobs(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
-
--- build headerunits for jobgraph
-function build_headerunits_for_jobgraph(target, jobgraph, 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)
- local jobsize = jobgraph:size()
- local build_headerunits_group = target:fullname() .. "/module/build_headerunits"
- jobgraph:group(build_headerunits_group, function ()
- 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:fullname() .. "/module/" .. path.normalize(key)
- _builder(target).make_headerunit_jobgraph(target,
- job_name, jobgraph, headerunit, bmifile, outputdir, table.join(opt, {build = build}))
- end
- }))
- 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)
- if jobgraph:size() > jobsize then
- return build_headerunits_group
- end
-end
-
--- build 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_buildcmds(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
-
--- build modules and headerunits, and we need to build headerunits first
-function build_modules_and_headerunits(target, jobgraph, sourcebatch, modules, opt)
- if jobgraph.add_orders then
- local build_modules_group, jobdeps = build_modules_for_jobgraph(target, jobgraph, sourcebatch, modules, opt)
- local build_headerunits_group = build_headerunits_for_jobgraph(target, jobgraph, sourcebatch, modules, opt)
- if build_modules_group then
- for jobname, deps in pairs(jobdeps) do
- for _, depname in ipairs(deps) do
- jobgraph:add_orders(depname, jobname)
- end
- end
- if build_headerunits_group then
- jobgraph:add_orders(build_headerunits_group, build_modules_group)
- end
- end
- elseif jobgraph.runcmds then
- build_headerunits_for_batchcmds(target, jobgraph, sourcebatch, modules, opt)
- build_modules_for_batchcmds(target, jobgraph, sourcebatch, modules, opt)
- elseif jobgraph.newjob then -- deprecated
- build_modules_for_batchjobs(target, jobgraph, sourcebatch, modules, opt)
- build_headerunits_for_batchjobs(target, jobgraph, sourcebatch, modules, opt)
- end
-end
-
--- generate metadata
-function generate_metadata(target, modules)
- local public_modules
- for _, module in table.orderpairs(modules) do
- local _, _, cppfile = compiler_support.get_provided_module(module)
- local fileconfig = target:fileconfig(cppfile)
- local public = fileconfig and fileconfig.public
- if public then
- public_modules = public_modules or {}
- table.insert(public_modules, module)
- end
- end
-
- if not public_modules then
- return
- end
-
- local jobs = option.get("jobs") or os.default_njob()
- runjobs(target:fullname() .. "/module/install_modules", function(index, total, jobopt)
- local module = public_modules[index]
- local name, _, cppfile = compiler_support.get_provided_module(module)
- local metafilepath = compiler_support.get_metafile(target, cppfile)
- progress.show(jobopt.progress, "${color.build.target}<%s> generating.module.metadata %s", target:fullname(), name)
- local metadata = _generate_meta_module_info(target, name, cppfile, module.requires)
- json.savefile(metafilepath, metadata)
- end, {comax = jobs, total = #public_modules})
-end
-
--- flush target module mapper keys
-function flush_target_module_mapper_keys(target)
- local memcache = compiler_support.memcache()
- memcache:set2(target:fullname(), "module_mapper_keys", nil)
-end
-
--- get or create a target module mapper
-function get_target_module_mapper(target)
- local memcache = compiler_support.memcache()
- local mapper = memcache:get2(target:fullname(), "module_mapper")
- if not mapper then
- mapper = {}
- memcache:set2(target:fullname(), "module_mapper", mapper)
- end
-
- -- we generate the keys map to optimise the efficiency of _is_duplicated_headerunit
- local mapper_keys = memcache:get2(target:fullname(), "module_mapper_keys")
- if not mapper_keys then
- mapper_keys = {}
- for _, item in pairs(mapper) do
- if item.key then
- mapper_keys[item.key] = item
- end
- end
- memcache:set2(target:fullname(), "module_mapper_keys", mapper_keys)
- end
- return mapper, mapper_keys
-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)
- if not mapper[name] then
- mapper[name] = {name = name, key = name, bmi = bmifile, sourcefile = sourcefile, opt = opt}
- end
- flush_target_module_mapper_keys(target)
-end
-
--- add a headerunit to target mapper
-function add_headerunit_to_target_mapper(target, headerunit, bmifile)
- local mapper = get_target_module_mapper(target)
- local key = hash.uuid(path.normalize(headerunit.path))
- local deduplicated = _is_duplicated_headerunit(target, key)
- if deduplicated then
- mapper[headerunit.name] = {name = headerunit.name, key = key, aliasof = deduplicated.name, headerunit = headerunit}
- else
- mapper[headerunit.name] = {name = headerunit.name, key = key, headerunit = headerunit, bmi = bmifile}
- end
- flush_target_module_mapper_keys(target)
- return deduplicated and true or false
-end
-
--- check if dependencies changed
-function is_dependencies_changed(target, module)
- local cachekey = target:fullname() .. module.name
- local requires = hashset.from(table.keys(module.requires or {}))
- local oldrequires = compiler_support.memcache():get2(cachekey, "oldrequires")
- local changed = false
- if oldrequires then
- if oldrequires ~= requires then
- requires_changed = true
- else
- for required in requires:items() do
- if not oldrequires:has(required) then
- requires_changed = true
- break
- end
- end
- end
- end
- return requires, changed
-end
-
--- patch sourcebatch
-function patch_sourcebatch(target, sourcebatch, opt)
-
- -- add target deps modules
- if target:orderdeps() then
- local deps_sourcefiles = dependency_scanner.get_targetdeps_modules(target)
- if deps_sourcefiles then
- table.join2(sourcebatch.sourcefiles, deps_sourcefiles)
- end
- end
-
- -- append std module
- local std_modules = compiler_support.get_stdmodules(target)
- if std_modules then
- table.join2(sourcebatch.sourcefiles, std_modules)
- end
-
- -- extract packages modules dependencies
- local package_modules_data = dependency_scanner.get_all_packages_modules(target, opt)
- if package_modules_data then
- -- append to sourcebatch
- for _, package_module_data in table.orderpairs(package_modules_data) do
- table.insert(sourcebatch.sourcefiles, package_module_data.file)
- target:fileconfig_set(package_module_data.file, {external = package_module_data.external, defines = package_module_data.metadata.defines})
- end
- end
-
- -- patch objectfiles and dependencies
- 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
-
diff --git a/xmake/rules/c++/modules/modules_support/clang/builder.lua b/xmake/rules/c++/modules/modules_support/clang/builder.lua
deleted file mode 100644
index 420d1f0ee..000000000
--- a/xmake/rules/c++/modules/modules_support/clang/builder.lua
+++ /dev/null
@@ -1,478 +0,0 @@
---!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 clang/builder.lua
---
-
--- imports
-import("core.base.json")
-import("core.base.option")
-import("core.base.semver")
-import("utils.progress")
-import("private.action.build.object", {alias = "objectbuilder"})
-import("core.tool.compiler")
-import("core.project.config")
-import("core.project.depend")
-import("compiler_support")
-import(".builder", {inherit = true})
-
-function _compile_one_step(target, bmifile, sourcefile, objectfile, opt)
- local module_outputflag = compiler_support.get_moduleoutputflag(target)
- if opt.is_mapped_bmi then
- -- get flags
- if module_outputflag then
- local flags = table.join(opt.std and {"-Wno-include-angled-in-module-purview", "-Wno-reserved-module-identifier"} or {})
- if opt and opt.batchcmds then
- _batchcmds_compile(opt.batchcmds, target, flags, bmifile, objectfile)
- else
- _compile(target, flags, bmifile, objectfile)
- end
- else
- _compile_objectfile_step(target, bmifile, sourcefile, objectfile, opt)
- end
- else
- -- get flags
- if module_outputflag then
- local flags = table.join({"-x", "c++-module", module_outputflag .. bmifile}, opt.std and {"-Wno-include-angled-in-module-purview", "-Wno-reserved-module-identifier"} or {})
- if opt and opt.batchcmds then
- _batchcmds_compile(opt.batchcmds, target, flags, sourcefile, objectfile)
- else
- _compile(target, flags, sourcefile, objectfile)
- end
- else
- _compile_bmi_step(target, bmifile, sourcefile, opt)
- _compile_objectfile_step(target, bmifile, sourcefile, objectfile, opt)
- end
- end
-end
-
-function _compile_bmi_step(target, bmifile, sourcefile, opt)
- local flags = table.join({"-x", "c++-module", "--precompile"}, opt.std and {"-Wno-include-angled-in-module-purview", "-Wno-reserved-module-identifier"} or {})
- if opt and opt.batchcmds then
- _batchcmds_compile(opt.batchcmds, target, flags, sourcefile, bmifile)
- else
- _compile(target, flags, sourcefile, bmifile)
- end
-end
-
-function _compile_objectfile_step(target, bmifile, sourcefile, objectfile, opt)
- _compile(target, {}, sourcefile, objectfile, {bmifile = bmifile})
- if opt and opt.batchcmds then
- _batchcmds_compile(opt.batchcmds, target, {}, sourcefile, objectfile, {bmifile = bmifile})
- else
- _compile(target, {}, sourcefile, objectfile, {bmifile = bmifile})
- end
-end
-
--- get flags for building a headerunit
-function _make_headerunitflags(target, headerunit, bmifile)
-
- local module_headerflag = compiler_support.get_moduleheaderflag(target)
- assert(module_headerflag, "compiler(clang): does not support c++ header units!")
-
- local local_directory = (headerunit.type == ":quote") and {"-I" .. path.directory(headerunit.path)} or {}
- local headertype = (headerunit.type == ":angle") and "system" or "user"
- local flags = table.join(local_directory, {"-xc++-header", "-Wno-everything", module_headerflag .. headertype})
- return flags
-end
-
--- do compile
-function _compile(target, flags, sourcefile, outputfile, opt)
-
- opt = opt or {}
- local dryrun = option.get("dry-run")
- local compinst = target:compiler("cxx")
- local compflags = compinst:compflags({sourcefile = sourcefile, target = target})
- local flags = table.join(compflags or {}, flags)
-
- -- trace
- if option.get("verbose") then
- print(compinst:compcmd(opt.bmifile or sourcefile, outputfile, {target = target, compflags = flags, rawargs = true}))
- end
-
- -- do compile
- if not dryrun then
- assert(compinst:compile(opt.bmifile or sourcefile, outputfile, {target = target, compflags = flags}))
- end
-end
-
--- do compile for batchcmds
--- @note we need to use batchcmds:compilev to translate paths in compflags for generator, e.g. -Ixx
-function _batchcmds_compile(batchcmds, target, flags, sourcefile, outputfile, opt)
- opt = opt or {}
- local compinst = target:compiler("cxx")
- local compflags = compinst:compflags({sourcefile = sourcefile, target = target})
- flags = table.join("-c", compflags or {}, flags, {"-o", outputfile, opt.bmifile or sourcefile})
- batchcmds:compilev(flags, {compiler = compinst, sourcekind = "cxx"})
-end
-
--- get module requires flags
--- e.g
--- -fmodule-file=build/.gens/Foo/rules/modules/cache/foo.pcm
--- -fmodule-file=build/.gens/Foo/rules/modules/cache/iostream.pcm
--- -fmodule-file=build/.gens/Foo/rules/modules/cache/bar.hpp.pcm
--- on LLVM >= 16
--- -fmodule-file=foo=build/.gens/Foo/rules/modules/cache/foo.pcm
--- -fmodule-file=build/.gens/Foo/rules/modules/cache/iostream.pcm
--- -fmodule-file=build/.gens/Foo/rules/modules/cache/bar.hpp.pcm
---
-function _get_requiresflags(target, module, opt)
-
- local modulefileflag = compiler_support.get_modulefileflag(target)
- local name = module.name
- local cachekey = target:fullname() .. name
-
- local requires, requires_changed = is_dependencies_changed(target, module)
- local requiresflags = compiler_support.memcache():get2(cachekey, "requiresflags")
- if not requiresflags or requires_changed then
- requiresflags = {}
- for required in requires:orderitems() do
- local dep_module = get_from_target_mapper(target, required)
- assert(dep_module, "module dependency %s required for %s not found", required, name)
-
- -- aliased headerunit
- local bmifile = dep_module.bmi
- if dep_module.aliasof then
- local aliased = get_from_target_mapper(target, dep_module.aliasof)
- bmifile = aliased.bmi
- end
- local mapflag = (dep_module.opt and dep_module.opt.namedmodule) and format("%s%s=%s", modulefileflag, required, bmifile) or modulefileflag .. bmifile
- table.insert(requiresflags, mapflag)
-
- -- append deps
- if dep_module.opt and dep_module.opt.deps then
- local deps = _get_requiresflags(target, {name = dep_module.name or dep_module.sourcefile, bmi = bmifile, requires = dep_module.opt.deps})
- table.join2(requiresflags, deps)
- end
- end
- requiresflags = table.unique(requiresflags)
- compiler_support.memcache():set2(cachekey, "requiresflags", requiresflags)
- compiler_support.memcache():set2(cachekey, "oldrequires", requires)
- end
- return requiresflags
-end
-
-function _append_requires_flags(target, module, name, cppfile, bmifile, opt)
- local cxxflags = {}
- local requiresflags = _get_requiresflags(target, {name = (name or cppfile), bmi = bmifile, requires = module.requires})
- for _, flag in ipairs(requiresflags) do
- -- we need to wrap flag to support flag with space
- if type(flag) == "string" and flag:find(" ", 1, true) then
- table.insert(cxxflags, {flag})
- else
- table.insert(cxxflags, flag)
- end
- end
- target:fileconfig_add(cppfile, {force = {cxxflags = cxxflags}})
-end
-
--- populate module map
-function populate_module_map(target, modules)
- local clang_version = compiler_support.get_clang_version(target)
- local support_namedmodule = semver.compare(clang_version, "16.0") >= 0
- for _, module in pairs(modules) do
- local name, provide, cppfile = compiler_support.get_provided_module(module)
- if provide then
- local bmifile = compiler_support.get_bmi_path(provide.bmi)
- add_module_to_target_mapper(target, name, cppfile, bmifile, {deps = module.requires, namedmodule = support_namedmodule})
- end
- end
-end
-
--- get defines for a module
-function get_module_required_defines(target, sourcefile)
- local compinst = compiler.load("cxx", {target = target})
- local compflags = compinst:compflags({sourcefile = sourcefile, target = target})
- local defines
- for _, flag in ipairs(compflags) do
- if flag:startswith("-D") then
- defines = defines or {}
- table.insert(defines, flag:sub(3))
- end
- end
- return defines
-end
-
--- build module file for batchjobs
-function make_module_buildjobs(target, batchjobs, job_name, deps, opt)
-
- local name, provide, _ = compiler_support.get_provided_module(opt.module)
- local bmifile = provide and compiler_support.get_bmi_path(provide.bmi)
- local dryrun = option.get("dry-run")
-
- return {
- name = job_name,
- deps = table.join(target:fullname() .. "/module/populate_module_map", deps),
- sourcefile = opt.cppfile,
- job = batchjobs:newjob(target:fullname() .. "/module/" .. (name or opt.cppfile), function(index, total, jobopt)
- local mapped_bmi
- if provide and compiler_support.memcache():get2(target:fullname() .. name, "reuse") then
- mapped_bmi = get_from_target_mapper(target, name).bmi
- end
-
- local build, dependinfo
- local dependfile = target:dependfile(bmifile or opt.objectfile)
- if provide or compiler_support.has_module_extension(opt.cppfile) then
- build, dependinfo = should_build(target, opt.cppfile, bmifile, {name = name, objectfile = opt.objectfile, requires = opt.module.requires})
-
- -- needed to detect rebuild of dependencies
- if provide and build then
- mark_build(target, name)
- end
- end
-
- -- append requires flags
- if opt.module.requires then
- _append_requires_flags(target, opt.module, name, opt.cppfile, bmifile, opt)
- end
-
- -- for cpp file we need to check after appendings the flags
- if build == nil then
- build, dependinfo = should_build(target, opt.cppfile, bmifile, {name = name, objectfile = opt.objectfile, requires = opt.module.requires})
- end
-
- if build then
- -- compile if it's a named module
- if provide or compiler_support.has_module_extension(opt.cppfile) then
- if not dryrun then
- local objectdir = path.directory(opt.objectfile)
- if not os.isdir(objectdir) then
- os.mkdir(objectdir)
- end
- end
-
- local fileconfig = target:fileconfig(opt.cppfile)
- local public = fileconfig and fileconfig.public
- local external = fileconfig and fileconfig.external
- local from_moduleonly = external and external.moduleonly
- local bmifile = mapped_bmi or bmifile
- local is_mapped_bmi = mapped_bmi ~= nil
- if external and not from_moduleonly then
- if not mapped_bmi then
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.bmi.$(mode) %s", target:fullname(), name or opt.cppfile)
- _compile_bmi_step(target, bmifile, opt.cppfile, {std = (name == "std" or name == "std.compat")})
- end
- else
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.module.$(mode) %s", target:fullname(), name or opt.cppfile)
- _compile_one_step(target, bmifile, opt.cppfile, opt.objectfile, {std = (name == "std" or name == "std.compat"), is_mapped_bmi = is_mapped_bmi})
- end
- else
- os.tryrm(opt.objectfile) -- force rebuild for .cpp files
- end
- depend.save(dependinfo, dependfile)
- end
- end)}
-end
-
--- build module file for jobgraph
-function make_module_jobgraph(target, jobgraph, opt)
-
- local name, provide, _ = compiler_support.get_provided_module(opt.module)
- local bmifile = provide and compiler_support.get_bmi_path(provide.bmi)
- local dryrun = option.get("dry-run")
-
- local jobname = target:fullname() .. "/module/" .. (name or opt.cppfile)
- jobgraph:add(jobname, function(index, total, jobopt)
- local mapped_bmi
- if provide and compiler_support.memcache():get2(target:fullname() .. name, "reuse") then
- mapped_bmi = get_from_target_mapper(target, name).bmi
- end
-
- local build, dependinfo
- local dependfile = target:dependfile(bmifile or opt.objectfile)
- if provide or compiler_support.has_module_extension(opt.cppfile) then
- build, dependinfo = should_build(target, opt.cppfile, bmifile, {name = name, objectfile = opt.objectfile, requires = opt.module.requires})
-
- -- needed to detect rebuild of dependencies
- if provide and build then
- mark_build(target, name)
- end
- end
-
- -- append requires flags
- if opt.module.requires then
- _append_requires_flags(target, opt.module, name, opt.cppfile, bmifile, opt)
- end
-
- -- for cpp file we need to check after appendings the flags
- if build == nil then
- build, dependinfo = should_build(target, opt.cppfile, bmifile, {name = name, objectfile = opt.objectfile, requires = opt.module.requires})
- end
-
- if build then
- -- compile if it's a named module
- if provide or compiler_support.has_module_extension(opt.cppfile) then
- if not dryrun then
- local objectdir = path.directory(opt.objectfile)
- if not os.isdir(objectdir) then
- os.mkdir(objectdir)
- end
- end
-
- local fileconfig = target:fileconfig(opt.cppfile)
- local public = fileconfig and fileconfig.public
- local external = fileconfig and fileconfig.external
- local from_moduleonly = external and external.moduleonly
- local bmifile = mapped_bmi or bmifile
- local is_mapped_bmi = mapped_bmi ~= nil
- if external and not from_moduleonly then
- if not mapped_bmi then
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.bmi.$(mode) %s", target:fullname(), name or opt.cppfile)
- _compile_bmi_step(target, bmifile, opt.cppfile, {std = (name == "std" or name == "std.compat")})
- end
- else
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.module.$(mode) %s", target:fullname(), name or opt.cppfile)
- _compile_one_step(target, bmifile, opt.cppfile, opt.objectfile, {std = (name == "std" or name == "std.compat"), is_mapped_bmi = is_mapped_bmi})
- end
- else
- os.tryrm(opt.objectfile) -- force rebuild for .cpp files
- end
- depend.save(dependinfo, dependfile)
- end
- end)
-end
-
-
--- build module file for batchcmds
-function make_module_buildcmds(target, batchcmds, opt)
-
- local name, provide, _ = compiler_support.get_provided_module(opt.module)
- local bmifile = provide and compiler_support.get_bmi_path(provide.bmi)
-
- local mapped_bmi
- if provide and compiler_support.memcache():get2(target:fullname() .. name, "reuse") then
- mapped_bmi = get_from_target_mapper(target, name).bmi
- end
-
- -- append requires flags
- if opt.module.requires then
- _append_requires_flags(target, opt.module, name, opt.cppfile, bmifile, opt)
- end
-
- -- compile if it's a named module
- if provide or compiler_support.has_module_extension(opt.cppfile) then
- batchcmds:mkdir(path.directory(opt.objectfile))
-
- local fileconfig = target:fileconfig(opt.cppfile)
- local public = fileconfig and fileconfig.public
- local external = fileconfig and fileconfig.external
- local bmifile = mapped_bmi or bmifile
- local is_mapped_bmi = mapped_bmi ~= nil
- if external and not from_moduleonly then
- if not mapped_bmi then
- batchcmds:show_progress(opt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.bmi.$(mode) %s", target:fullname(), name or opt.cppfile)
- _compile_bmi_step(target, bmifile, opt.cppfile, {std = (name == "std" or name == "std.compat"), batchcmds = batchcmds})
- end
- else
- batchcmds:show_progress(opt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.module.$(mode) %s", target:fullname(), name or opt.cppfile)
- _compile_one_step(target, bmifile, opt.cppfile, opt.objectfile, {std = (name == "std" or name == "std.compat"), batchcmds = batchcmds, is_mapped_bmi = is_mapped_bmi})
- end
- else
- batchcmds:rm(opt.objectfile) -- force rebuild for .cpp files
- end
- batchcmds:add_depfiles(opt.cppfile)
- return os.mtime(opt.objectfile)
-end
-
--- build headerunit file for batchjobs
-function make_headerunit_buildjobs(target, job_name, batchjobs, headerunit, bmifile, outputdir, opt)
- local already_exists = add_headerunit_to_target_mapper(target, headerunit, bmifile)
- if not already_exists then
- return {
- name = job_name,
- sourcefile = headerunit.path,
- job = batchjobs:newjob(job_name, function(index, total, jobopt)
- if not os.isdir(outputdir) then
- os.mkdir(outputdir)
- end
-
- local compinst = compiler.load("cxx", {target = target})
- local compflags = compinst:compflags({sourcefile = headerunit.path, target = target})
-
- local dependfile = target:dependfile(bmifile)
- local dependinfo = depend.load(dependfile) or {}
- dependinfo.files = {}
- local depvalues = {compinst:program(), compflags}
-
- if opt.build then
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.headerunit.$(mode) %s",
- target:fullname(), headerunit.name)
- _compile(target, _make_headerunitflags(target, headerunit, bmifile), headerunit.path, bmifile)
- end
-
- table.insert(dependinfo.files, headerunit.path)
- dependinfo.values = depvalues
- depend.save(dependinfo, dependfile)
- end)}
- end
-end
-
--- build headerunit file for jobgraph
-function make_headerunit_jobgraph(target, job_name, jobgraph, headerunit, bmifile, outputdir, opt)
- local already_exists = add_headerunit_to_target_mapper(target, headerunit, bmifile)
- if not already_exists then
- jobgraph:add(job_name, function(index, total, jobopt)
- if not os.isdir(outputdir) then
- os.mkdir(outputdir)
- end
-
- local compinst = compiler.load("cxx", {target = target})
- local compflags = compinst:compflags({sourcefile = headerunit.path, target = target})
-
- local dependfile = target:dependfile(bmifile)
- local dependinfo = depend.load(dependfile) or {}
- dependinfo.files = {}
- local depvalues = {compinst:program(), compflags}
-
- if opt.build then
- progress.show(jobopt.progress,
- "${color.build.target}<%s> ${clear}${color.build.object}compiling.headerunit.$(mode) %s",
- target:fullname(), headerunit.name)
- _compile(target, _make_headerunitflags(target, headerunit, bmifile), headerunit.path, bmifile)
- end
-
- table.insert(dependinfo.files, headerunit.path)
- dependinfo.values = depvalues
- depend.save(dependinfo, dependfile)
- end)
- end
-end
-
--- build headerunit file for batchcmds
-function make_headerunit_buildcmds(target, batchcmds, headerunit, bmifile, outputdir, opt)
- batchcmds:mkdir(outputdir)
- add_headerunit_to_target_mapper(target, headerunit, bmifile)
-
- if opt.build then
- local headerfile = headerunit.unique and headerunit.name or headerunit.path
- batchcmds:show_progress(opt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.headerunit.$(mode) %s"
- , target:fullname(), headerfile)
- _batchcmds_compile(batchcmds, target, _make_headerunitflags(target, headerunit, bmifile), bmifile)
- end
- batchcmds:add_depfiles(headerunit.path)
- return os.mtime(bmifile)
-end
-
-function get_requires(target, module)
- local _requires
- local flags = _get_requiresflags(target, module)
- for _, flag in ipairs(flags) do
- _requires = _requires or {}
- table.insert(_requires, flag:split("=")[3])
- end
- return _requires
-end
diff --git a/xmake/rules/c++/modules/modules_support/clang/compiler_support.lua b/xmake/rules/c++/modules/modules_support/clang/compiler_support.lua
deleted file mode 100644
index 6eb4f84b1..000000000
--- a/xmake/rules/c++/modules/modules_support/clang/compiler_support.lua
+++ /dev/null
@@ -1,499 +0,0 @@
---!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 clang/compiler_support.lua
---
-
--- imports
-import("core.base.semver")
-import("core.base.option")
-import("core.base.json")
-import("lib.detect.find_tool")
-import("lib.detect.find_file")
-import(".compiler_support", {inherit = true})
-
--- get includedirs for stl headers
---
--- $ echo '#include <vector>' | clang -x c++ -E - | grep '/vector"'
--- # 1 "/usr/include/c++/11/vector" 1 3
--- # 58 "/usr/include/c++/11/vector" 3
--- # 59 "/usr/include/c++/11/vector" 3
---
-function _get_toolchain_includedirs_for_stlheaders(target, includedirs, clang)
- local tmpfile = os.tmpfile() .. ".cc"
- io.writefile(tmpfile, "#include <vector>")
- local argv = {"-E", "-x", "c++", tmpfile}
- local cpplib = _get_cpplibrary_name(target)
- if cpplib then
- if cpplib == "c++" then
- table.insert(argv, 1, "-stdlib=libc++")
- elseif cpplib == "stdc++" then
- table.insert(argv, 1, "-stdlib=libstdc++")
- end
- end
- local result = try {function () return os.iorunv(clang, argv, {envs = compinst:runenvs()}) end}
- if result then
- for _, line in ipairs(result:split("\n", {plain = true})) do
- line = line:trim()
- if line:startswith("#") and line:find("/vector\"", 1, true) then
- local includedir = line:match("\"(.+)/vector\"")
- if includedir and os.isdir(includedir) then
- table.insert(includedirs, path.normalize(includedir))
- break
- end
- end
- end
- end
- os.tryrm(tmpfile)
-end
-
-function _get_cpplibrary_name(target)
- -- libc++ come first because on windows, if we use libc++ clang will still use msvc crt so MD / MT / MDd / MTd can be set
- if target:has_runtime("c++_shared", "c++_static") then
- return "c++"
- elseif target:has_runtime("stdc++_shared", "stdc++_static") then
- return "stdc++"
- elseif target:has_runtime("MD", "MT", "MDd", "MTd") then
- return "msstl"
- end
- if target:is_plat("macosx") then
- return "c++"
- elseif target:is_plat("linux") then
- return "stdc++"
- elseif target:is_plat("windows") then
- return "msstl"
- end
-end
-
-function _get_std_module_manifest_path(target)
- local print_module_manifest_flag = get_print_library_module_manifest_path_flag(target)
- local clang_path = path.directory(get_clang_path(target))
- if print_module_manifest_flag then
- local compinst = target:compiler("cxx")
- local outdata, _ = try { function() return os.iorunv(compinst:program(), {"-std=c++23", "-stdlib=libc++", "--sysroot=" .. path.join(clang_path, ".."), print_module_manifest_flag}, {envs = compinst:runenvs()}) end }
- if outdata and not outdata:startswith("<NOT PRESENT>") then
- return outdata:trim()
- end
- end
-
- -- fallback on custom detection
- -- manifest can be found in <llvm_path>/lib subdirectory (i.e on debian it should be <llvm_path>/lib/x86_64-unknown-linux-gnu/)
- local clang_lib_path = path.join(clang_path, "..", "lib")
- local modules_json_path = path.join(clang_lib_path, "libc++.modules.json")
- if not os.isfile(modules_json_path) then
- modules_json_path = find_file("*/libc++.modules.json", clang_lib_path)
- end
- return modules_json_path
-end
-
--- load module support for the current target
-function load(target)
- local clangmodulesflag, modulestsflag, withoutflag = get_modulesflag(target)
-
- -- add module flags
- if not withoutflag then
- target:add("cxxflags", modulestsflag)
- end
-
- -- fix default visibility for functions and variables [-fvisibility] differs in PCH file vs. current file
- -- module.pcm cannot be loaded due to a configuration mismatch with the current compilation.
- --
- -- it will happen in binary target depend on library target with modules, and enable release mode at same time.
- --
- -- @see https://github.com/xmake-io/xmake/issues/3358#issuecomment-1432586767
- local dep_symbols
- local has_library_deps = false
- for _, dep in ipairs(target:orderdeps()) do
- if dep:is_shared() or dep:is_static() or dep:is_object() then
- dep_symbols = dep:get("symbols")
- has_library_deps = true
- break
- end
- end
- if has_library_deps then
- target:set("symbols", dep_symbols and dep_symbols or "none")
- end
-
- -- on Windows before llvm18 we need to disable delayed-template-parsing because it's incompatible with modules, from llvm >= 18, it's disabled by default
- local clang_version = get_clang_version(target)
- if semver.compare(clang_version, "18") < 0 then
- target:add("cxxflags", "-fno-delayed-template-parsing")
- end
-end
-
--- strip flags that doesn't affect bmi generation
-function strip_flags(target, flags)
- -- speculative list as there is no resource that list flags that prevent reusability, this list will likely be improve over time
- -- @see https://clang.llvm.org/docs/StandardCPlusPlusModules.html#consistency-requirement
- local strippable_flags = {
- "-I",
- "-isystem",
- "-g",
- "-O",
- "-W",
- "-w",
- "-cxx-isystem",
- "-Q",
- }
- if not target:policy("build.c++.modules.tryreuse.discriminate_on_defines") then
- table.join2(strippable_flags, {"-D", "-U"})
- end
- local output = {}
- local last_flag_I = false
- for _, flag in ipairs(flags) do
- local strip = false
- for _, _flag in ipairs(strippable_flags) do
- if flag:startswith(_flag) or last_flag_I then
- last_flag_I = _flag == "-I"
- strip = true
- break
- end
- end
- if not strip then
- table.insert(output, flag)
- end
- end
- return output
-end
-
--- provide toolchain include directories for stl headerunit when p1689 is not supported
-function toolchain_includedirs(target)
- local includedirs = _g.includedirs
- if includedirs == nil then
- includedirs = {}
- local clang, toolname = target:tool("cxx")
- assert(toolname:startswith("clang"))
- _get_toolchain_includedirs_for_stlheaders(target, includedirs, clang)
- local cpplib = _get_cpplibrary_name(target)
- local runtime_flag
- if cpplib then
- if cpplib == "c++" then
- runtime_flag = "-stdlib=libc++"
- elseif cpplib == "stdc++" then
- runtime_flag = "-stdlib=libstdc++"
- end
- end
- local _, result = try {function () return os.iorunv(clang, table.join({"-E", "-Wp,-v", "-xc", os.nuldev()}, runtime_flag or {})) end}
- if result then
- for _, line in ipairs(result:split("\n", {plain = true})) do
- line = line:trim()
- if os.isdir(line) then
- table.insert(includedirs, path.normalize(line))
- elseif line:startswith("End") then
- break
- end
- end
- end
- _g.includedirs = includedirs
- end
- return includedirs
-end
-
--- get clang path
-function get_clang_path(target)
- local clang_path = _g.clang_path
- if not clang_path then
- local program, toolname = target:tool("cxx")
- if program and toolname:startswith("clang") then
- local clang = find_tool(toolname, {program = program,
- envs = os.getenvs(), cachekey = "modules_support_clang_" .. toolname})
- if clang then
- clang_path = clang.program
- end
- end
- clang_path = clang_path or false
- _g.clang_path = clang_path
- end
- return clang_path or nil
-end
-
--- get clang version
-function get_clang_version(target)
- local clang_version = _g.clang_version
- if not clang_version then
- local program, toolname = target:tool("cxx")
- if program and toolname:startswith("clang") then
- local clang = find_tool(toolname, {program = program, version = true,
- envs = os.getenvs(), cachekey = "modules_support_clang_" .. toolname})
- if clang then
- clang_version = clang.version
- end
- end
- clang_version = clang_version or false
- _g.clang_version = clang_version
- end
- return clang_version or nil
-end
-
--- get clang-scan-deps
-function get_clang_scan_deps(target)
- local clang_scan_deps = _g.clang_scan_deps
- if not clang_scan_deps then
- local program, toolname = target:tool("cxx")
- if program and toolname:startswith("clang") then
- local dir = path.directory(program)
- local basename = path.basename(program)
- if basename == "clang-cl" then
- basename = "clang"
- end
- local extension = path.extension(program)
- program = (basename:rtrim("+"):gsub("clang", "clang-scan-deps")) .. extension
- if dir and dir ~= "." and os.isdir(dir) then
- program = path.join(dir, program)
- end
- local result = find_tool("clang-scan-deps", {program = program, version = true})
- if result then
- clang_scan_deps = result.program
- end
- end
- clang_scan_deps = clang_scan_deps or false
- _g.clang_scan_deps = clang_scan_deps
- end
- return clang_scan_deps or nil
-end
-
-function get_stdmodules(target)
- if target:policy("build.c++.modules.std") then
- local cpplib = _get_cpplibrary_name(target)
- if cpplib then
- if cpplib == "c++" then
- -- libc++ module is found by parsing libc++.modules.json
- local modules_json_path = _get_std_module_manifest_path(target)
- if modules_json_path then
- local modules_json = json.decode(io.readfile(modules_json_path))
- if modules_json and modules_json.modules and #modules_json.modules > 0 then
- local std_module_directory = path.directory(modules_json.modules[1]["source-path"])
- if not path.is_absolute(std_module_directory) then
- std_module_directory = path.join(path.directory(modules_json_path), std_module_directory)
- end
- if os.isdir(std_module_directory) then
- return {path.join(std_module_directory, "std.cppm"), path.join(std_module_directory, "std.compat.cppm")}
- end
- end
- end
- elseif cpplib == "stdc++" then
- -- libstdc++ doesn't have a std module file atm
- elseif cpplib == "msstl" then
- -- msstl std module file is not compatible with llvm < 19
- local clang_version = get_clang_version(target)
- if clang_version and semver.compare(clang_version, "19.0") >= 0 then
- local toolchain = target:toolchain("clang") or target:toolchain("clang-cl")
- local msvc = import("core.tool.toolchain", {anonymous = true}).load("msvc", {plat = toolchain:plat(), arch = toolchain:arch()})
- if msvc and msvc:check() then
- local vcvars = msvc:config("vcvars")
- if vcvars.VCInstallDir and vcvars.VCToolsVersion then
- local stdmodulesdir = path.join(vcvars.VCInstallDir, "Tools", "MSVC", vcvars.VCToolsVersion, "modules")
- if os.isdir(stdmodulesdir) then
- return {path.join(stdmodulesdir, "std.ixx"), path.join(stdmodulesdir, "std.compat.ixx")}
- end
- end
- end
- else
- wprint("msstl std module file is not compatible with llvm < 19, please upgrade clang/clang-cl version!")
- return
- end
- end
- end
- wprint("std and std.compat modules not found! maybe try to add --sdk=<PATH/TO/LLVM> or install libc++")
- end
-end
-
-function get_bmi_extension()
- return ".pcm"
-end
-
-function get_modulesflag(target)
- local clangmodulesflag = _g.clangmodulesflag
- local modulestsflag = _g.modulestsflag
- local withoutflag = _g.withoutflag
- if clangmodulesflag == nil and modulestsflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fmodules", "cxxflags", {flagskey = "clang_modules"}) then
- clangmodulesflag = "-fmodules"
- end
- if compinst:has_flags("-fmodules-ts", "cxxflags", {flagskey = "clang_modules_ts"}) then
- modulestsflag = "-fmodules-ts"
- end
- local clang_version = get_clang_version(target)
- withoutflag = semver.compare(clang_version, "16.0") >= 0
- assert(withoutflag or modulestsflag, "compiler(clang): does not support c++ module!")
- _g.clangmodulesflag = clangmodulesflag or false
- _g.modulestsflag = modulestsflag or false
- _g.withoutflag = withoutflag or false
- end
- return clangmodulesflag or nil, modulestsflag or nil, withoutflag or nil
-end
-
-function get_builtinmodulemapflag(target)
- local builtinmodulemapflag = _g.builtinmodulemapflag
- if builtinmodulemapflag == nil then
- -- this flag seems clang on mingw doesn't distribute it
- -- @see https://github.com/xmake-io/xmake/pull/2833
- if not target:is_plat("mingw") then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fbuiltin-module-map", "cxxflags", {flagskey = "clang_builtin_module_map"}) then
- builtinmodulemapflag = "-fbuiltin-module-map"
- end
- assert(builtinmodulemapflag, "compiler(clang): does not support c++ module!")
- end
- _g.builtinmodulemapflag = builtinmodulemapflag or false
- end
- return builtinmodulemapflag or nil
-end
-
-function get_implicitmodulesflag(target)
- local implicitmodulesflag = _g.implicitmodulesflag
- if implicitmodulesflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fimplicit-modules", "cxxflags", {flagskey = "clang_implicit_modules"}) then
- implicitmodulesflag = "-fimplicit-modules"
- end
- assert(implicitmodulesflag, "compiler(clang): does not support c++ module!")
- _g.implicitmodulesflag = implicitmodulesflag or false
- end
- return implicitmodulesflag or nil
-end
-
-function get_implicitmodulemapsflag(target)
- local implicitmodulemapsflag = _g.implicitmodulemapsflag
- if implicitmodulemapsflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fimplicit-module-maps", "cxxflags", {flagskey = "clang_implicit_module_map"}) then
- implicitmodulemapsflag = "-fimplicit-module-maps"
- end
- assert(implicitmodulemapsflag, "compiler(clang): does not support c++ module!")
- _g.implicitmodulemapsflag = implicitmodulemapsflag or false
- end
- return implicitmodulemapsflag or nil
-end
-
-function get_noimplicitmodulemapsflag(target)
- local noimplicitmodulemapsflag = _g.noimplicitmodulemapsflag
- if noimplicitmodulemapsflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fno-implicit-module-maps", "cxxflags", {flagskey = "clang_no_implicit_module_maps"}) then
- noimplicitmodulemapsflag = "-fno-implicit-module-maps"
- end
- assert(noimplicitmodulemapsflag, "compiler(clang): does not support c++ module!")
- _g.noimplicitmodulemapsflag = noimplicitmodulemapsflag or false
- end
- return noimplicitmodulemapsflag or nil
-end
-
-function get_prebuiltmodulepathflag(target)
- local prebuiltmodulepathflag = _g.prebuiltmodulepathflag
- if prebuiltmodulepathflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fprebuilt-module-path=" .. os.tmpdir(), "cxxflags", {flagskey = "clang_prebuild_module_path"}) then
- prebuiltmodulepathflag = "-fprebuilt-module-path="
- end
- assert(prebuiltmodulepathflag, "compiler(clang): does not support c++ module!")
- _g.prebuiltmodulepathflag = prebuiltmodulepathflag or false
- end
- return prebuiltmodulepathflag or nil
-end
-
-function get_modulecachepathflag(target)
- local modulecachepathflag = _g.modulecachepathflag
- if modulecachepathflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fmodules-cache-path=" .. os.tmpdir(), "cxxflags", {flagskey = "clang_modules_cache_path"}) then
- modulecachepathflag = "-fmodules-cache-path="
- end
- assert(modulecachepathflag, "compiler(clang): does not support c++ module!")
- _g.modulecachepathflag = modulecachepathflag or false
- end
- return modulecachepathflag or nil
-end
-
-function get_modulefileflag(target)
- local modulefileflag = _g.modulefileflag
- if modulefileflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fmodule-file=" .. os.tmpfile() .. get_bmi_extension(), "cxxflags", {flagskey = "clang_module_file"}) then
- modulefileflag = "-fmodule-file="
- end
- assert(modulefileflag, "compiler(clang): does not support c++ module!")
- _g.modulefileflag = modulefileflag or false
- end
- return modulefileflag or nil
-end
-
-function get_moduleheaderflag(target)
- local moduleheaderflag = _g.moduleheaderflag
- if moduleheaderflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fmodule-header=system", "cxxflags", {flagskey = "clang_module_header"}) then
- moduleheaderflag = "-fmodule-header="
- end
- _g.moduleheaderflag = moduleheaderflag or false
- end
- return moduleheaderflag or nil
-end
-
-function has_clangscandepssupport(target)
- local support_clangscandeps = _g.support_clangscandeps
- if support_clangscandeps == nil then
- local clangscandeps = get_clang_scan_deps(target)
- local clang_version = get_clang_version(target)
- if clangscandeps and clang_version and semver.compare(clang_version, "16.0") >= 0 then
- support_clangscandeps = true
- end
- _g.support_clangscandeps = support_clangscandeps or false
- end
- return support_clangscandeps or nil
-end
-
-function get_keepsystemincludesflag(target)
- local keepsystemincludesflag = _g.keepsystemincludesflag
- if keepsystemincludesflag == nil then
- local compinst = target:compiler("cxx")
- local clang_version = get_clang_version(target)
- if compinst:has_flags("-E -fkeep-system-includes", "cxxflags", {flagskey = "clang_keep_system_includes", tryrun = true}) and
- semver.compare(clang_version, "18.0") >= 0 then
- keepsystemincludesflag = "-fkeep-system-includes"
- end
- _g.keepsystemincludesflag = keepsystemincludesflag or false
- end
- return keepsystemincludesflag or nil
-end
-
-function get_moduleoutputflag(target)
- local moduleoutputflag = _g.moduleoutputflag
- if moduleoutputflag == nil then
- local compinst = target:compiler("cxx")
- local clang_version = get_clang_version(target)
- if compinst:has_flags("-fmodule-output=", "cxxflags", {flagskey = "clang_module_output", tryrun = true}) and
- semver.compare(clang_version, "16.0") >= 0 then
- moduleoutputflag = "-fmodule-output="
- end
- _g.moduleoutputflag = moduleoutputflag or false
- end
- return moduleoutputflag or nil
-end
-
-function get_print_library_module_manifest_path_flag(target)
- local print_library_module_manifest_path_flag = _g.print_library_module_manifest_path_flag
- if print_library_module_manifest_path_flag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-print-library-module-manifest-path", "cxxflags", {flagskey = "clang_print_library_module_manifest_path", tryrun = true}) then
- print_library_module_manifest_path_flag = "-print-library-module-manifest-path"
- end
- _g.print_library_module_manifest_path_flag = print_library_module_manifest_path_flag or false
- end
- return print_library_module_manifest_path_flag or nil
-end
diff --git a/xmake/rules/c++/modules/modules_support/clang/dependency_scanner.lua b/xmake/rules/c++/modules/modules_support/clang/dependency_scanner.lua
deleted file mode 100644
index 715e8a07d..000000000
--- a/xmake/rules/c++/modules/modules_support/clang/dependency_scanner.lua
+++ /dev/null
@@ -1,89 +0,0 @@
---!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 clang/dependency_scanner.lua
---
-
--- imports
-import("core.base.json")
-import("core.base.semver")
-import("core.base.option")
-import("core.project.depend")
-import("utils.progress")
-import("compiler_support")
-import(".dependency_scanner", {inherit = true})
-
-function generate_dependency_for(target, sourcefile, opt)
- local compinst = target:compiler("cxx")
- local changed = false
- local dependfile = target:dependfile(sourcefile)
- local flags = compinst:compflags({sourcefile = sourcefile, target = target}) or {}
- local fileconfig = target:fileconfig(sourcefile)
-
- depend.on_changed(function()
- if opt.progress then
- progress.show(opt.progress, "${color.build.target}<%s> generating.module.deps %s", target:fullname(), sourcefile)
- end
-
- local outputdir = compiler_support.get_outputdir(target, sourcefile)
- local jsonfile = path.translate(path.join(outputdir, path.filename(sourcefile) .. ".json"))
- local has_clangscandepssupport = compiler_support.has_clangscandepssupport(target)
- if has_clangscandepssupport and not target:policy("build.c++.clang.fallbackscanner") then
- -- We need absolute path of clang to use clang-scan-deps
- -- See https://clang.llvm.org/docs/StandardCPlusPlusModules.html#possible-issues-failed-to-find-system-headers
- local clang_path = compinst:program()
- if not path.is_absolute(clang_path) then
- clang_path = compiler_support.get_clang_path(target) or compinst:program()
- end
- local clangscandeps = compiler_support.get_clang_scan_deps(target)
- local dependency_flags = table.join({"--format=p1689", "--",
- clang_path, "-x", "c++", "-c", sourcefile, "-o", target:objectfile(sourcefile)}, flags)
- if option.get("verbose") then
- print(os.args(table.join(clangscandeps, dependency_flags)))
- end
- local outdata, errdata = os.iorunv(clangscandeps, dependency_flags)
- assert(outdata, errdata)
-
- io.writefile(jsonfile, outdata)
- else
- if not has_clangscandepssupport then
- wprint("No clang-scan-deps found ! using fallback scanner")
- end
- fallback_generate_dependencies(target, jsonfile, sourcefile, function(file)
- local keepsystemincludesflag = compiler_support.get_keepsystemincludesflag(target)
- local compflags = table.clone(flags)
- -- exclude -fmodule* and -std=c++/gnu++* flags because
- -- when they are set clang try to find bmi of imported modules but they don't exists in this point of compilation
- table.remove_if(compflags, function(_, flag)
- return flag:startswith("-fmodule") or flag:startswith("-std=c++") or flag:startswith("-std=gnu++")
- end)
- local ifile = path.translate(path.join(outputdir, path.filename(file) .. ".i"))
- compflags = table.join(compflags or {}, keepsystemincludesflag or {}, {"-E", "-x", "c++", file, "-o", ifile})
- os.vrunv(compinst:program(), compflags)
- local content = io.readfile(ifile)
- os.rm(ifile)
- return content
- end)
- end
- changed = true
-
- local rawdependinfo = io.readfile(jsonfile)
- return {moduleinfo = rawdependinfo}
- end, {dependfile = dependfile, files = {sourcefile}, changed = target:is_rebuilt(), values = flags})
- return changed
-end
-
diff --git a/xmake/rules/c++/modules/modules_support/compiler_support.lua b/xmake/rules/c++/modules/modules_support/compiler_support.lua
deleted file mode 100644
index fea294ad8..000000000
--- a/xmake/rules/c++/modules/modules_support/compiler_support.lua
+++ /dev/null
@@ -1,274 +0,0 @@
--- 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", "clang_cl") 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)
-
- -- 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
- _compiler_support(target).load(target)
-end
-
--- strip flags not relevent for module reuse
-function strip_flags(target, flags)
- return _compiler_support(target).strip_flags(target, flags)
-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, 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 = _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 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 _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: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
-
diff --git a/xmake/rules/c++/modules/modules_support/dependency_scanner.lua b/xmake/rules/c++/modules/modules_support/dependency_scanner.lua
deleted file mode 100644
index 94b8a4804..000000000
--- a/xmake/rules/c++/modules/modules_support/dependency_scanner.lua
+++ /dev/null
@@ -1,516 +0,0 @@
---!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 dependency_scanner.lua
---
-
--- imports
-import("core.base.json")
-import("core.base.hashset")
-import("core.base.graph")
-import("core.base.option")
-import("async.runjobs")
-import("compiler_support")
-import("stl_headers")
-
-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", "clang_cl") 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 dependency_scanner
-end
-
-function _parse_meta_info(target, metafile)
- local metadata = json.loadfile(metafile)
- if metadata.file and metadata.name then
- return metadata.file, metadata.name, metadata
- end
-
- local filename = path.basename(metafile)
- local metadir = path.directory(metafile)
- for _, ext in ipairs({".mpp", ".mxx", ".cppm", ".ixx"}) do
- if os.isfile(path.join(metadir, filename .. ext)) then
- filename = filename .. ext
- break
- end
- end
-
- local sourcecode = io.readfile(path.join(path.directory(metafile), filename))
- sourcecode = sourcecode:gsub("//.-\n", "\n")
- sourcecode = sourcecode:gsub("/%*.-%*/", "")
-
- local name
- for _, line in ipairs(sourcecode:split("\n", {plain = true})) do
- name = line:match("export%s+module%s+(.+)%s*;") or line:match("export%s+__preprocessed_module%s+(.+)%s*;")
- if name then
- break
- end
- end
- return filename, name, metadata
-end
-
--- parse module dependency data
---[[
-{
- "build/.objs/stl_headerunit/linux/x86_64/release/src/hello.mpp.o" = {
- requires = {
- iostream = {
- method = "include-angle",
- unique = true,
- path = "/usr/include/c++/11/iostream"
- }
- },
- provides = {
- hello = {
- bmi = "build/.gens/stl_headerunit/linux/x86_64/release/rules/modules/cache/hello.gcm",
- sourcefile = "src/hello.mpp"
- }
- }
- },
- "build/.objs/stl_headerunit/linux/x86_64/release/src/main.cpp.o" = {
- requires = {
- hello = {
- method = "by-name",
- unique = false,
- path = "build/.gens/stl_headerunit/linux/x86_64/release/rules/modules/cache/hello.gcm"
- }
- }
- }
-}]]
-function _parse_dependencies_data(target, moduleinfos)
- local modules
- for _, moduleinfo in ipairs(moduleinfos) do
- assert(moduleinfo.version <= 1)
- for _, rule in ipairs(moduleinfo.rules) do
- modules = modules or {}
- local m = {}
- if rule.provides then
- for _, provide in ipairs(rule.provides) do
- m.provides = m.provides or {}
- assert(provide["logical-name"])
- local bmifile = provide["compiled-module-path"]
- -- 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(compiler_support.get_bmi_extension(target)) then
- bmifile = output
- break
- end
- end
-
- -- we didn't found the compiled module path, so we assume it
- if not bmifile then
- 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(compiler_support.get_outputdir(target, name), name)
- end
- end
- m.provides[provide["logical-name"]] = {
- bmi = bmifile,
- sourcefile = moduleinfo.sourcefile,
- interface = provide["is-interface"]
- }
- end
- else
- m.cppfile = moduleinfo.sourcefile
- end
- assert(rule["primary-output"])
- modules[path.translate(rule["primary-output"])] = m
- end
- end
-
- for _, moduleinfo in ipairs(moduleinfos) do
- for _, rule in ipairs(moduleinfo.rules) do
- local m = modules[path.translate(rule["primary-output"])]
- for _, r in ipairs(rule.requires) do
- m.requires = m.requires or {}
- local p = r["source-path"]
- if not p then
- for _, dependency in pairs(modules) do
- if dependency.provides and dependency.provides[r["logical-name"]] then
- p = dependency.provides[r["logical-name"]].bmi
- break
- end
- end
- end
- m.requires[r["logical-name"]] = {
- method = r["lookup-method"] or "by-name",
- path = p and path.translate(p) or nil,
- unique = r["unique-on-source-path"] or false
- }
- end
- end
- end
- return modules
-end
-
--- generate edges for DAG
-function _get_edges(nodes, modules)
- local edges = {}
- local module_names = {}
- local name_filemap = {}
- local named_module_names = hashset.new()
- for _, node in ipairs(table.unique(nodes)) do
- local module = modules[node]
- local module_name, _, cppfile = compiler_support.get_provided_module(module)
- if module_name then
- if named_module_names:has(module_name) then
- raise("duplicate module name detected \"" .. module_name .. "\"\n -> " .. cppfile .. "\n -> " .. name_filemap[module_name])
- end
- named_module_names:insert(module_name)
- name_filemap[module_name] = cppfile
- end
- if module.requires then
- for required_name, _ in table.orderpairs(module.requires) do
- for _, required_node in ipairs(nodes) do
- local name, _, _ = compiler_support.get_provided_module(modules[required_node])
- if name and name == required_name then
- table.insert(edges, {required_node, node})
- end
- end
- end
- end
- end
- return edges
-end
-
-function _get_package_modules(target, package)
- 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)
- local moduleonly = not package:libraryfiles()
- package_modules[name] = {file = path.join(modulesdir, modulefile), metadata = metadata, external = {moduleonly = moduleonly}}
- end
- return package_modules
-end
-
--- generate module dependencies
-function generate_module_dependencies(target, jobgraph, sourcebatch, opt)
- local parsejob = target:fullname() .. "/parse_module_dependencies"
- jobgraph:add(parsejob, function (index, total, opt)
- local changed = compiler_support.memcache():get2("modules", "dependencies_changed")
- if changed then
- local cachekey = target:fullname() .. "/" .. sourcebatch.rulename
- local moduleinfos = compiler_support.load_moduleinfos(target, sourcebatch)
- local modules = _parse_dependencies_data(target, moduleinfos)
- compiler_support.localcache():set2("modules", cachekey, modules)
- compiler_support.localcache():save()
- end
- end)
- for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
- local jobname = target:fullname() .. "/generate_module_dependencies/" .. sourcefile
- jobgraph:add(jobname, function (index, total, opt)
- local changed = _dependency_scanner(target).generate_dependency_for(target, sourcefile, opt)
- if changed then
- compiler_support.memcache():set2("modules", "dependencies_changed", true)
- end
- end)
- jobgraph:add_orders(jobname, parsejob)
- end
-end
-
--- get module dependencies
-function get_module_dependencies(target, sourcebatch)
- local cachekey = target:fullname() .. "/" .. sourcebatch.rulename
- local modules = compiler_support.localcache():get2("modules", cachekey)
- assert(modules, "no module dependencies!")
- 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
- return headerunits, stl_headerunits
-end
-
--- https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html
---[[
-{
- "version": 1,
- "revision": 0,
- "rules": [
- {
- "primary-output": "use-header.mpp.o",
- "requires": [
- {
- "logical-name": "<header.hpp>",
- "source-path": "/path/to/found/header.hpp",
- "unique-on-source-path": true,
- "lookup-method": "include-angle"
- }
- ]
- },
- {
- "primary-output": "header.hpp.bmi",
- "provides": [
- {
- "logical-name": "header.hpp",
- "source-path": "/path/to/found/header.hpp",
- "unique-on-source-path": true,
- }
- ]
- }
- ]
-}]]
-function fallback_generate_dependencies(target, jsonfile, sourcefile, preprocess_file)
- local output = {version = 1, revision = 0, rules = {}}
- local rule = {outputs = {jsonfile}}
- rule["primary-output"] = target:objectfile(sourcefile)
-
- local module_name_export
- local module_name_private
- local module_deps = {}
- local module_deps_set = hashset.new()
- local sourcecode = preprocess_file(sourcefile) or io.readfile(sourcefile)
- local internal = false
- sourcecode = sourcecode:gsub("//.-\n", "\n")
- sourcecode = sourcecode:gsub("/%*.-%*/", "")
- for _, line in ipairs(sourcecode:split("\n", {plain = true})) do
- if line:match("#") then
- goto continue
- end
- if not module_name_export then
- module_name_export = line:match("export%s+module%s+(.+)%s*;") or line:match("export%s+__preprocessed_module%s+(.+)%s*;")
- end
- if not module_name_private then
- module_name_private = line:match("module%s+(.+)%s*;") or line:match("__preprocessed_module%s+(.+)%s*;")
- if module_name_private then
- internal = module_name_private:find(":")
- end
- end
- 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 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
- local module_dep = {}
- -- partition? import :xxx;
- if module_depname:startswith(":") then
- local module_name = (module_name_export or module_name_private or "")
- module_name = module_name:split(":")[1]
- module_dep["unique-on-source-path"] = true
- module_depname = module_name .. module_depname
- elseif module_depname:startswith("\"") then
- module_depname = module_depname:sub(2, -2)
- module_dep["lookup-method"] = "include-quote"
- module_dep["unique-on-source-path"] = true
- 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"] = compiler_support.find_angle_header_file(target, module_depname)
- end
- module_dep["logical-name"] = module_depname
- table.insert(module_deps, module_dep)
- module_deps_set:insert(module_depname)
- end
- ::continue::
- end
-
- if module_name_export or internal then
- 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) .. compiler_support.get_bmi_extension(target))
-
- rule.provides = {}
- table.insert(rule.provides, provide)
- end
-
- rule.requires = module_deps
- table.insert(output.rules, rule)
- local jsondata = json.encode(output)
- io.writefile(jsonfile, jsondata)
-end
-
--- extract packages modules dependencies
-function get_all_packages_modules(target)
-
- -- parse all meta-info and append their informations to the package store
- local packages = target:pkgs() or {}
- for _, deps in ipairs(target:orderdeps()) do
- table.join2(packages, deps:pkgs())
- end
-
- local packages_modules
- for _, package in table.orderpairs(packages) do
- local package_modules = _get_package_modules(target, package)
- if package_modules then
- packages_modules = packages_modules or {}
- table.join2(packages_modules, package_modules)
- end
- end
- return packages_modules
-end
-
--- topological sort
-function sort_modules_by_dependencies(target, objectfiles, modules, opt)
- local build_objectfiles = {}
- local link_objectfiles = {}
- local edges = _get_edges(objectfiles, modules)
- local dag = graph.new(true)
- for _, e in ipairs(edges) do
- dag:add_edge(e[1], e[2])
- end
- local objectfiles_sorted, has_cycle = dag:topo_sort()
- if has_cycle then
- local cycle = dag:find_cycle()
- if cycle then
- local names = {}
- for _, objectfile in ipairs(cycle) do
- local name, _, cppfile = compiler_support.get_provided_module(modules[objectfile])
- table.insert(names, name or cppfile)
- end
- local name, _, cppfile = compiler_support.get_provided_module(modules[cycle[1]])
- table.insert(names, name or cppfile)
- raise("circular modules dependency detected!\n%s", table.concat(names, "\n -> import "))
- end
- end
- objectfiles_sorted = table.reverse(objectfiles_sorted)
- local objectfiles_sorted_set = hashset.from(objectfiles_sorted)
- for _, objectfile in ipairs(objectfiles) do
- if not objectfiles_sorted_set:has(objectfile) then
- table.insert(objectfiles_sorted, objectfile)
- objectfiles_sorted_set:insert(objectfile)
- end
- end
- local culleds
- for _, objectfile in ipairs(objectfiles_sorted) do
- local name, provide, cppfile = compiler_support.get_provided_module(modules[objectfile])
- local fileconfig = target:fileconfig(cppfile)
- local public
- local external
- local can_cull = true
- if fileconfig then
- public = fileconfig.public
- external = fileconfig.external
- can_cull = fileconfig.cull == nil and true or fileconfig.cull
- end
- can_cull = can_cull and target:policy("build.c++.modules.culling")
- local insert = true
- if provide then
- insert = public or (not external or external.moduleonly)
- if insert and not public and can_cull then
- insert = false
- local edges = dag:adjacent_edges(objectfile)
- local public = fileconfig and fileconfig.public
- if edges then
- for _, edge in ipairs(edges) do
- if edge:to() ~= objectfile and objectfiles_sorted_set:has(edge:to()) then
- insert = true
- break
- end
- end
- end
- end
- end
- if insert then
- table.insert(build_objectfiles, objectfile)
- table.insert(link_objectfiles, objectfile)
- elseif external and not external.from_moduleonly then
- table.insert(build_objectfiles, objectfile)
- else
- objectfiles_sorted_set:remove(objectfile)
- if name ~= "std" and name ~= "std.compat" then
- culleds = culleds or {}
- culleds[target:fullname()] = culleds[target:fullname()] or {}
- table.insert(culleds[target:fullname()], format("%s -> %s", name, cppfile))
- end
- end
- end
-
- if culleds then
- if option.get("verbose") then
- local culled_strs = {}
- for target_name, m in pairs(culleds) do
- table.insert(culled_strs, format("%s:\n %s", target_name, table.concat(m, "\n ")))
- end
- wprint("some modules have got culled, because it is not consumed by its target nor flagged as a public module with add_files(\"xxx.mpp\", {public = true})\n %s",
- table.concat(culled_strs, "\n "))
- else
- wprint("some modules have got culled, use verbose (-v) mode to more informations")
- end
- end
-
- return build_objectfiles, link_objectfiles
-end
-
--- 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 = {moduleonly = dep:is_moduleonly()}})
- end
- end
- end
- end
- return sourcefiles
-end
-
diff --git a/xmake/rules/c++/modules/modules_support/gcc/builder.lua b/xmake/rules/c++/modules/modules_support/gcc/builder.lua
deleted file mode 100644
index 1f909623f..000000000
--- a/xmake/rules/c++/modules/modules_support/gcc/builder.lua
+++ /dev/null
@@ -1,469 +0,0 @@
---!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 gcc/builder.lua
---
-
--- imports
-import("core.base.json")
-import("core.base.option")
-import("core.base.semver")
-import("utils.progress")
-import("private.action.build.object", {alias = "objectbuilder"})
-import("core.tool.compiler")
-import("core.project.config")
-import("core.project.depend")
-import("compiler_support")
-import(".builder", {inherit = true})
-
--- get flags for building a headerunit
-function _make_headerunitflags(target, headerunit, headerunit_mapper)
- local module_headerflag = compiler_support.get_moduleheaderflag(target)
- local module_onlyflag = compiler_support.get_moduleonlyflag(target)
- local module_mapperflag = compiler_support.get_modulemapperflag(target)
- assert(module_headerflag and module_onlyflag, "compiler(gcc): does not support c++ header units!")
-
- local local_directory = (headerunit.type == ":quote") and {"-I" .. path.directory(path.normalize(headerunit.path))} or {}
- local headertype = (headerunit.type == ":angle") and "system" or "user"
- local flags = table.join(local_directory, {module_mapperflag .. headerunit_mapper,
- module_headerflag .. headertype,
- module_onlyflag,
- "-xc++-header"})
- return flags
-end
-
--- do compile
-function _compile(target, flags, sourcefile, outputfile)
-
- local dryrun = option.get("dry-run")
- local compinst = target:compiler("cxx")
- local compflags = compinst:compflags({sourcefile = sourcefile, target = target})
- local flags = table.join(compflags or {}, flags)
-
- -- trace
- if option.get("verbose") then
- print(compinst:compcmd(sourcefile, outputfile, {target = target, compflags = flags, rawargs = true}))
- end
-
- -- do compile
- if not dryrun then
- assert(compinst:compile(sourcefile, outputfile, {target = target, compflags = flags}))
- end
-end
-
--- do compile for batchcmds
--- @note we need to use batchcmds:compilev to translate paths in compflags for generator, e.g. -Ixx
-function _batchcmds_compile(batchcmds, target, flags, sourcefile, outputfile)
- local compinst = target:compiler("cxx")
- local compflags = compinst:compflags({sourcefile = sourcefile, target = target})
- local flags = table.join("-c", compflags or {}, flags, {"-o", outputfile, sourcefile})
- batchcmds:compilev(flags, {compiler = compinst, sourcekind = "cxx"})
-end
-
-function _module_map_cachekey(target)
- local mode = config.mode()
- return target:fullname() .. "module_mapper" .. (mode or "")
-end
-
--- generate a module mapper file for build a headerunit
-function _generate_headerunit_modulemapper_file(module)
- local mapper_path = os.tmpfile()
- local mapper_file = io.open(mapper_path, "wb")
- mapper_file:write("root " .. path.unix(os.projectdir()))
- mapper_file:write("\n")
- mapper_file:write(mapper_file, path.unix(module.name) .. " " .. path.unix(module.bmifile))
- mapper_file:write("\n")
- mapper_file:close()
- return mapper_path
-end
-
-function _get_maplines(target, module)
- local maplines = {}
- local m_name, m, _ = compiler_support.get_provided_module(module)
- if m then
- table.insert(maplines, m_name .. " " .. compiler_support.get_bmi_path(m.bmi))
- end
- for required, _ in table.orderpairs(module.requires) do
- local dep_module = get_from_target_mapper(target, required)
- assert(dep_module, "module dependency %s required for %s not found", required, m_name or module.cppfile)
-
- local bmifile = dep_module.bmi
- local mapline
- -- aliased headerunit
- if dep_module.aliasof then
- local aliased = get_from_target_mapper(target, dep_module.aliasof)
- bmifile = aliased.bmi
- mapline = path.unix(dep_module.headerunit.path) .. " " .. path.unix(bmifile)
- -- headerunit
- elseif dep_module.headerunit then
- mapline = path.unix(dep_module.headerunit.path) .. " " .. path.unix(bmifile)
- -- named module
- else
- mapline = required .. " " .. path.unix(bmifile)
- end
- table.insert(maplines, mapline)
-
- -- append deps
- if dep_module.opt and dep_module.opt.deps then
- local deps = _get_maplines(target, {name = dep_module.name, bmi = bmifile, requires = dep_module.opt.deps})
- table.join2(maplines, deps)
- end
- end
-
- -- remove duplicates
- return table.unique(maplines)
-end
-
--- generate a module mapper file for build a module
--- e.g
--- /usr/include/c++/11/iostream build/.gens/stl_headerunit/linux/x86_64/release/stlmodules/cache/iostream.gcm
--- hello build/.gens/stl_headerunit/linux/x86_64/release/rules/modules/cache/hello.gcm
---
-function _generate_modulemapper_file(target, module, cppfile)
- local maplines = _get_maplines(target, module)
- local mapper_path = path.join(os.tmpdir(), target:fullname():replace(" ", "_"), name or cppfile:replace(" ", "_"))
- local mapper_content = {}
- table.insert(mapper_content, "root " .. path.unix(os.projectdir()))
- for _, mapline in ipairs(maplines) do
- table.insert(mapper_content, mapline)
- end
- mapper_content = table.concat(mapper_content, "\n") .. "\n"
- if not os.isfile(mapper_path) or io.readfile(mapper_path, {encoding = "binary"}) ~= mapper_content then
- io.writefile(mapper_path, mapper_content, {encoding = "binary"})
- end
- return mapper_path
-end
-
--- populate module map
-function populate_module_map(target, modules)
- for _, module in pairs(modules) do
- local name, provide = compiler_support.get_provided_module(module)
- if provide then
- local bmifile = compiler_support.get_bmi_path(provide.bmi)
- add_module_to_target_mapper(target, name, provide.sourcefile, bmifile, {deps = module.requires})
- end
- end
-end
-
--- get defines for a module
-function get_module_required_defines(target, sourcefile)
- local compinst = compiler.load("cxx", {target = target})
- local compflags = compinst:compflags({sourcefile = sourcefile, target = target})
- local defines
- for _, flag in ipairs(compflags) do
- if flag:startswith("-D") then
- defines = defines or {}
- table.insert(defines, flag:sub(3))
- end
- end
- return defines
-end
-
--- build module file for batchjobs
-function make_module_buildjobs(target, batchjobs, job_name, deps, opt)
-
- local name, provide, _ = compiler_support.get_provided_module(opt.module)
- local bmifile = provide and compiler_support.get_bmi_path(provide.bmi)
- local module_mapperflag = compiler_support.get_modulemapperflag(target)
-
- return {
- name = job_name,
- deps = table.join(target:fullname() .. "/module/populate_module_map", deps),
- sourcefile = opt.cppfile,
- job = batchjobs:newjob(target:fullname() .. "/module/" .. (name or opt.cppfile), function(index, total, jobopt)
- local mapped_bmi
- if provide and compiler_support.memcache():get2(target:fullname() .. name, "reuse") then
- mapped_bmi = get_from_target_mapper(target, name).bmi
- end
-
- -- generate and append module mapper file
- local module_mapper
- if provide or opt.module.requires then
- module_mapper = _generate_modulemapper_file(target, opt.module, opt.cppfile)
- target:fileconfig_add(opt.cppfile, {force = {cxxflags = {module_mapperflag .. module_mapper}}})
- end
-
- local dependfile = target:dependfile(bmifile or opt.objectfile)
- local build, dependinfo = should_build(target, opt.cppfile, bmifile, {name = name, objectfile = opt.objectfile, requires = opt.module.requires})
-
- -- needed to detect rebuild of dependencies
- if provide and build then
- mark_build(target, name)
- end
-
- if build then
- -- compile if it's a named module
- if provide or compiler_support.has_module_extension(opt.cppfile) then
- local fileconfig = target:fileconfig(opt.cppfile)
- local public = fileconfig and fileconfig.public
- local external = fileconfig and fileconfig.external
- local from_moduleonly = external and external.moduleonly
- local bmifile = mapped_bmi or bmifile
- local flags = {"-x", "c++"}
- local sourcefile
- if external and not from_moduleonly then
- if not mapped_bmi then
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.bmi.$(mode) %s", target:fullname(), name or opt.cppfile)
- local module_onlyflag = compiler_support.get_moduleonlyflag(target)
- table.insert(flags, module_onlyflag)
- sourcefile = opt.cppfile
- end
- else
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.module.$(mode) %s", target:fullname(), name or opt.cppfile)
- sourcefile = opt.cppfile
- end
- if option.get("diagnosis") then
- print("mapper file --------\n%s--------", io.readfile(module_mapper))
- end
- if sourcefile then
- _compile(target, flags, sourcefile, opt.objectfile)
- end
- os.tryrm(module_mapper)
- else
- os.tryrm(opt.objectfile) -- force rebuild for .cpp files
- end
- depend.save(dependinfo, dependfile)
- end
- end)}
-end
-
--- build module file for jobgraph
-function make_module_jobgraph(target, jobgraph, opt)
- local name, provide, _ = compiler_support.get_provided_module(opt.module)
- local bmifile = provide and compiler_support.get_bmi_path(provide.bmi)
- local module_mapperflag = compiler_support.get_modulemapperflag(target)
-
- local jobname = target:fullname() .. "/module/" .. (name or opt.cppfile)
- jobgraph:add(jobname, function(index, total, jobopt)
- local mapped_bmi
- if provide and compiler_support.memcache():get2(target:fullname() .. name, "reuse") then
- mapped_bmi = get_from_target_mapper(target, name).bmi
- end
-
- -- generate and append module mapper file
- local module_mapper
- if provide or opt.module.requires then
- module_mapper = _generate_modulemapper_file(target, opt.module, opt.cppfile)
- target:fileconfig_add(opt.cppfile, {force = {cxxflags = {module_mapperflag .. module_mapper}}})
- end
-
- local dependfile = target:dependfile(bmifile or opt.objectfile)
- local build, dependinfo = should_build(target, opt.cppfile, bmifile, {name = name, objectfile = opt.objectfile, requires = opt.module.requires})
-
- -- needed to detect rebuild of dependencies
- if provide and build then
- mark_build(target, name)
- end
-
- if build then
- -- compile if it's a named module
- if provide or compiler_support.has_module_extension(opt.cppfile) then
- local fileconfig = target:fileconfig(opt.cppfile)
- local public = fileconfig and fileconfig.public
- local external = fileconfig and fileconfig.external
- local from_moduleonly = external and external.moduleonly
- local bmifile = mapped_bmi or bmifile
- local flags = {"-x", "c++"}
- local sourcefile
- if external and not from_moduleonly then
- if not mapped_bmi then
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.bmi.$(mode) %s", target:fullname(), name or opt.cppfile)
- local module_onlyflag = compiler_support.get_moduleonlyflag(target)
- table.insert(flags, module_onlyflag)
- sourcefile = opt.cppfile
- end
- else
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.module.$(mode) %s", target:fullname(), name or opt.cppfile)
- sourcefile = opt.cppfile
- end
- if option.get("diagnosis") then
- print("mapper file --------\n%s--------", io.readfile(module_mapper))
- end
- if sourcefile then
- _compile(target, flags, sourcefile, opt.objectfile)
- end
- os.tryrm(module_mapper)
- else
- os.tryrm(opt.objectfile) -- force rebuild for .cpp files
- end
- depend.save(dependinfo, dependfile)
- end
- end)
-end
-
--- build module file for batchcmds
-function make_module_buildcmds(target, batchcmds, opt)
-
- local name, provide, _ = compiler_support.get_provided_module(opt.module)
- local bmifile = provide and compiler_support.get_bmi_path(provide.bmi)
- local module_mapperflag = compiler_support.get_modulemapperflag(target)
-
- local mapped_bmi
- if provide and compiler_support.memcache():get2(target:fullname() .. name, "reuse") then
- mapped_bmi = get_from_target_mapper(target, name).bmi
- end
-
- -- generate and append module mapper file
- local module_mapper
- if provide or opt.module.requires then
- module_mapper = _generate_modulemapper_file(target, opt.module, opt.cppfile)
- target:fileconfig_add(opt.cppfile, {force = {cxxflags = {module_mapperflag .. module_mapper}}})
- end
-
- -- compile if it's a named module
- if provide or compiler_support.has_module_extension(opt.cppfile) then
- batchcmds:mkdir(path.directory(opt.objectfile))
- local fileconfig = target:fileconfig(opt.cppfile)
- local public = fileconfig and fileconfig.public
- local external = fileconfig and fileconfig.external
- local from_moduleonly = external and external.moduleonly
- local bmifile = mapped_bmi or bmifile
- local flags = {"-x", "c++"}
- local sourcefile
- if external and not from_moduleonly then
- if not mapped_bmi then
- batchcmds:show_progress(opt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.bmi.$(mode) %s", target:fullname(), name or opt.cppfile)
- local module_onlyflag = compiler_support.get_moduleonlyflag(target)
- table.insert(flags, module_onlyflag)
- sourcefile = opt.cppfile
- end
- else
- batchcmds:show_progress(opt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.module.$(mode) %s", target:fullname(), name or opt.cppfile)
- sourcefile = opt.cppfile
- end
- if option.get("diagnosis") then
- batchcmds:print("mapper file: %s", io.readfile(module_mapper))
- end
- if sourcefile then
- _batchcmds_compile(batchcmds, target, flags, sourcefile, opt.objectfile)
- end
- batchcmds:rm(module_mapper)
- else
- batchcmds:rm(opt.objectfile) -- force rebuild for .cpp files
- end
- batchcmds:add_depfiles(opt.cppfile)
- return os.mtime(opt.objectfile)
-end
-
--- build headerunit file for batchjobs
-function make_headerunit_buildjobs(target, job_name, batchjobs, headerunit, bmifile, outputdir, opt)
- local _headerunit = headerunit
- _headerunit.path = headerunit.type == ":quote" and "./" .. path.relative(headerunit.path) or headerunit.path
- local already_exists = add_headerunit_to_target_mapper(target, _headerunit, bmifile)
- if not already_exists then
- return {
- name = job_name,
- sourcefile = headerunit.path,
- job = batchjobs:newjob(job_name, function(index, total, jobopt)
- if not os.isdir(outputdir) then
- os.mkdir(outputdir)
- end
-
- local compinst = compiler.load("cxx", {target = target})
- local compflags = compinst:compflags({sourcefile = headerunit.path, target = target})
-
- local dependfile = target:dependfile(bmifile)
- local dependinfo = depend.load(dependfile) or {}
- dependinfo.files = {}
- local depvalues = {compinst:program(), compflags}
-
- if opt.build then
- local headerunit_mapper = _generate_headerunit_modulemapper_file({name = path.normalize(headerunit.path), bmifile = bmifile})
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.headerunit.$(mode) %s", target:fullname(), headerunit.name)
- if option.get("diagnosis") then
- print("mapper file:\n%s", io.readfile(headerunit_mapper))
- end
- local headerfile = headerunit.unique and headerunit.name or headerunit.path
- _compile(target,
- _make_headerunitflags(target, headerunit, headerunit_mapper, opt),
- path.translate(headerfile), bmifile)
- os.tryrm(headerunit_mapper)
- end
-
- table.insert(dependinfo.files, headerunit.path)
- dependinfo.values = depvalues
- depend.save(dependinfo, dependfile)
- end)}
- end
-end
-
--- build headerunit file for jobgraph
-function make_headerunit_jobgraph(target, job_name, jobgraph, headerunit, bmifile, outputdir, opt)
- local _headerunit = headerunit
- _headerunit.path = headerunit.type == ":quote" and "./" .. path.relative(headerunit.path) or headerunit.path
- local already_exists = add_headerunit_to_target_mapper(target, _headerunit, bmifile)
- if not already_exists then
- jobgraph:add(job_name, function(index, total, jobopt)
- if not os.isdir(outputdir) then
- os.mkdir(outputdir)
- end
-
- local compinst = compiler.load("cxx", {target = target})
- local compflags = compinst:compflags({sourcefile = headerunit.path, target = target})
-
- local dependfile = target:dependfile(bmifile)
- local dependinfo = depend.load(dependfile) or {}
- dependinfo.files = {}
- local depvalues = {compinst:program(), compflags}
-
- if opt.build then
- local headerunit_mapper = _generate_headerunit_modulemapper_file({name = path.normalize(headerunit.path), bmifile = bmifile})
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.headerunit.$(mode) %s",
- target:fullname(), headerunit.name)
- if option.get("diagnosis") then
- print("mapper file:\n%s", io.readfile(headerunit_mapper))
- end
- local headerfile = headerunit.unique and headerunit.name or headerunit.path
- _compile(target,
- _make_headerunitflags(target, headerunit, headerunit_mapper, opt),
- path.translate(headerfile), bmifile)
- os.tryrm(headerunit_mapper)
- end
-
- table.insert(dependinfo.files, headerunit.path)
- dependinfo.values = depvalues
- depend.save(dependinfo, dependfile)
- end)
- end
-end
-
-
-
--- build headerunit file for batchcmds
-function make_headerunit_buildcmds(target, batchcmds, headerunit, bmifile, outputdir, opt)
- local headerunit_mapper = _generate_headerunit_modulemapper_file({name = path.normalize(headerunit.path), bmifile = bmifile})
- batchcmds:mkdir(outputdir)
-
- local _headerunit = headerunit
- _headerunit.path = headerunit.type == ":quote" and "./" .. path.relative(headerunit.path) or headerunit.path
- add_headerunit_to_target_mapper(target, _headerunit, bmifile)
-
- if opt.build then
- local headerfile = headerunit.unique and headerunit.name or headerunit.path
- batchcmds:show_progress(opt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.headerunit.$(mode) %s",
- target:fullname(), headerfile)
- if option.get("diagnosis") then
- batchcmds:print("mapper file:\n%s", io.readfile(headerunit_mapper))
- end
- _batchcmds_compile(batchcmds, target, _make_headerunitflags(target, headerunit, headerunit_mapper), bmifile)
- end
-
- batchcmds:rm(headerunit_mapper)
- batchcmds:add_depfiles(headerunit.path)
- return os.mtime(bmifile)
-end
-
diff --git a/xmake/rules/c++/modules/modules_support/gcc/compiler_support.lua b/xmake/rules/c++/modules/modules_support/gcc/compiler_support.lua
deleted file mode 100644
index 72de29081..000000000
--- a/xmake/rules/c++/modules/modules_support/gcc/compiler_support.lua
+++ /dev/null
@@ -1,325 +0,0 @@
---!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 gcc/compiler_support.lua
---
-
--- imports
-import("core.base.json")
-import("core.base.semver")
-import("core.project.config")
-import("lib.detect.find_tool")
-import(".compiler_support", {inherit = true})
-
--- get includedirs for stl headers
---
--- $ echo '#include <vector>' | gcc -x c++ -E - | grep '/vector"'
--- # 1 "/usr/include/c++/11/vector" 1 3
--- # 58 "/usr/include/c++/11/vector" 3
--- # 59 "/usr/include/c++/11/vector" 3
---
-function _get_toolchain_includedirs_for_stlheaders(includedirs, gcc)
- local tmpfile = os.tmpfile() .. ".cc"
- io.writefile(tmpfile, "#include <vector>")
- local result = try {function () return os.iorunv(gcc, {"-E", "-x", "c++", tmpfile}) end}
- if result then
- for _, line in ipairs(result:split("\n", {plain = true})) do
- line = line:trim()
- if line:startswith("#") and line:find("/vector\"", 1, true) then
- local includedir = line:match("\"(.+)/vector\"")
- if includedir and os.isdir(includedir) then
- table.insert(includedirs, path.normalize(includedir))
- break
- end
- end
- end
- end
- os.tryrm(tmpfile)
-end
-
--- load module support for the current target
-function load(target)
- local modulesflag = get_modulesflag(target)
- target:add("cxxflags", modulesflag, {force = true, expand = false})
-
- -- fix cxxabi issue
- -- @see https://github.com/xmake-io/xmake/issues/2716#issuecomment-1225057760
- -- https://github.com/xmake-io/xmake/issues/3855
-
- if target:policy("build.c++.gcc.modules.cxx11abi") then
- target:add("cxxflags", "-D_GLIBCXX_USE_CXX11_ABI=1")
- else
- target:add("cxxflags", "-D_GLIBCXX_USE_CXX11_ABI=0")
- end
-end
-
--- strip flags that doesn't affect bmi generation
-function strip_flags(target, flags)
- -- speculative list as there is no resource that list flags that prevent reusability, this list will likely be improve over time
- local strippable_flags = {
- "-I",
- "-isystem",
- "-g",
- "-O",
- "-W",
- "-w",
- "-cxx-isystem",
- "-Q",
- "-fmodule-mapper",
- }
- if not target:policy("build.c++.modules.tryreuse.discriminate_on_defines") then
- table.join2(strippable_flags, {"-D", "-U"})
- end
- local output = {}
- local last_flag_I = false
- for _, flag in ipairs(flags) do
- local strip = false
- for _, _flag in ipairs(strippable_flags) do
- if flag:startswith(_flag) or last_flag_I then
- last_flag_I = _flag == "-I"
- strip = true
- break
- end
- end
- if not strip then
- table.insert(output, flag)
- end
- end
- return output
-end
-
--- provide toolchain include directories for stl headerunit when p1689 is not supported
-function toolchain_includedirs(target)
- local includedirs = _g.includedirs
- if includedirs == nil then
- includedirs = {}
- local gcc, toolname = target:tool("cxx")
- assert(toolname == "gcc" or toolname == "gxx")
- _get_toolchain_includedirs_for_stlheaders(includedirs, gcc)
- local _, result = try {function () return os.iorunv(gcc, {"-E", "-Wp,-v", "-xc", os.nuldev()}) end}
- if result then
- for _, line in ipairs(result:split("\n", {plain = true})) do
- line = line:trim()
- if os.isdir(line) then
- table.insert(includedirs, path.normalize(line))
- elseif line:startswith("End") then
- break
- end
- end
- end
- _g.includedirs = includedirs
- end
- return includedirs
-end
-
-function get_target_module_mapperpath(target)
- local path = path.join(modules_cachedir(target, {mkdir = true}), "..", "mapper.txt")
- if not os.isfile(path) then
- io.writefile(path, "")
- end
- return path
-end
-
-function _get_std_module_manifest_path(target)
- local compinst = target:compiler("cxx")
- local modules_json_path, _ = try {
- function()
- return os.iorunv(compinst:program(), {"-print-file-name=libstdc++.modules.json"}, {envs = compinst:runenvs()})
- end
- }
-
- if modules_json_path then
- modules_json_path = modules_json_path:trim()
- if os.isfile(modules_json_path) then
- return modules_json_path
- end
- end
-
- -- fallback on custom detection
- -- manifest can be found alongside libstdc++.so
-
- -- TODO
-end
-
-function get_stdmodules(target)
- if not target:policy("build.c++.modules.std") then
- return
- end
-
- local modules_json_path = _get_std_module_manifest_path(target)
- if not modules_json_path then
- return
- end
-
- local modules_json = json.loadfile(modules_json_path)
- if modules_json and modules_json.modules and #modules_json.modules > 0 then
- local std_module_files = {}
- local modules_json_dir = path.directory(modules_json_path)
- for _, module_file in ipairs(modules_json.modules) do
- local module_file_path = module_file["source-path"]
- if not path.is_absolute(module_file_path) then
- module_file_path = path.join(modules_json_dir, module_file_path)
- end
- table.insert(std_module_files, module_file_path)
- end
- return std_module_files
- end
-end
-
-function get_bmi_extension()
- return ".gcm"
-end
-
-function get_modulesflag(target)
- local modulesflag = _g.modulesflag
- if modulesflag == nil then
- local compinst = target:compiler("cxx")
- local gcc_version = get_gcc_version(target)
- -- GCC 12 and earlier version has a option '-fmodules' for Modula-2
- if gcc_version and semver.compare(gcc_version, "12") > 0 then
- if compinst:has_flags("-fmodules", "cxxflags", {flagskey = "gcc_modules"}) then
- modulesflag = "-fmodules"
- elseif compinst:has_flags("-fmodules-ts", "cxxflags", {flagskey = "gcc_modules_ts"}) then
- modulesflag = "-fmodules-ts"
- end
- elseif compinst:has_flags("-fmodules-ts", "cxxflags", {flagskey = "gcc_modules_ts"}) then
- modulesflag = "-fmodules-ts"
- end
- assert(modulesflag, "compiler(gcc): does not support c++ module!")
- _g.modulesflag = modulesflag or false
- end
- return modulesflag or nil
-end
-
-function get_moduleheaderflag(target)
- local moduleheaderflag = _g.moduleheaderflag
- if moduleheaderflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fmodule-header", "cxxflags", {flagskey = "gcc_module_header"}) then
- moduleheaderflag = "-fmodule-header="
- end
- _g.moduleheaderflag = moduleheaderflag or false
- end
- return moduleheaderflag or nil
-end
-
-function get_moduleonlyflag(target)
- local moduleonlyflag = _g.moduleonlyflag
- if moduleonlyflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fmodule-only", "cxxflags", {flagskey = "gcc_module_only"}) then
- moduleonlyflag = "-fmodule-only"
- end
- _g.moduleonlyflag = moduleonlyflag or false
- end
- return moduleonlyflag or nil
-end
-
-function get_modulemapperflag(target)
- local modulemapperflag = _g.modulemapperflag
- if modulemapperflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fmodule-mapper=" .. os.tmpfile(), "cxxflags", {flagskey = "gcc_module_mapper"}) then
- modulemapperflag = "-fmodule-mapper="
- end
- assert(modulemapperflag, "compiler(gcc): does not support c++ module!")
- _g.modulemapperflag = modulemapperflag or false
- end
- return modulemapperflag or nil
-end
-
-function get_depsflag(target)
- local depflag = _g.depflag
- if depflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fdeps-format=p1689r5", "cxxflags", {flagskey = "gcc_deps_format",
- on_check = function (ok, errors)
- if errors:find("-M") then
- ok = true
- end
- return ok, errors
- end}) then
- depflag = "-fdeps-format=p1689r5"
- end
- _g.depflag = depflag or false
- end
- return depflag or nil
-end
-
-function get_depsfileflag(target)
- local depfileflag = _g.depfileflag
- if depfileflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fdeps-file=" .. os.tmpfile(), "cxxflags", {flagskey = "gcc_deps_file",
- on_check = function (ok, errors)
- if errors:find("-M") then
- ok = true
- end
- return ok, errors
- end}) then
- depfileflag = "-fdeps-file="
- end
- _g.depfileflag = depfileflag or false
- end
- return depfileflag or nil
-end
-
-function get_depstargetflag(target)
- local depoutputflag = _g.depoutputflag
- if depoutputflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fdeps-target=" .. os.tmpfile() .. ".o", "cxxflags", {flagskey = "gcc_deps_output",
- on_check = function (ok, errors)
- if errors:find("-M") then
- ok = true
- end
- return ok, errors
- end}) then
- depoutputflag = "-fdeps-target="
- end
- _g.depoutputflag = depoutputflag or false
- end
- return depoutputflag or nil
-end
-
-function get_cppversionflag(target)
- local cppversionflag = _g.cppversionflag
- if cppversionflag == nil then
- local compinst = target:compiler("cxx")
- local flags = compinst:compflags({target = target})
- cppversionflag = table.find_if(flags, function(v) string.startswith(v, "-std=c++") end) or "-std=c++20"
- _g.cppversionflag = cppversionflag
- end
- return cppversionflag or nil
-end
-
-function get_gcc_version(target)
- local gcc_version = _g.gcc_version
- if not gcc_version then
- local program, toolname = target:tool("cxx")
- if program and toolname:startswith("gcc") then
- local gcc = find_tool(toolname, {program = program, version = true,
- envs = os.getenvs(), cachekey = "modules_support_gcc_" .. toolname})
- if gcc then
- gcc_version = gcc.version
- end
- end
- gcc_version = gcc_version or false
- _g.gcc_version = gcc_version
- end
- return gcc_version or nil
-end
diff --git a/xmake/rules/c++/modules/modules_support/gcc/dependency_scanner.lua b/xmake/rules/c++/modules/modules_support/gcc/dependency_scanner.lua
deleted file mode 100644
index e087c6d4e..000000000
--- a/xmake/rules/c++/modules/modules_support/gcc/dependency_scanner.lua
+++ /dev/null
@@ -1,79 +0,0 @@
---!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 gcc/dependency_scanner.lua
---
-
--- imports
-import("core.base.json")
-import("core.base.semver")
-import("core.project.depend")
-import("utils.progress")
-import("compiler_support")
-import("builder")
-import(".dependency_scanner", {inherit = true})
-
--- generate dependency files
-function generate_dependency_for(target, sourcefile, opt)
- local compinst = target:compiler("cxx")
- local baselineflags = {"-E", "-x", "c++"}
- local depsformatflag = compiler_support.get_depsflag(target, "p1689r5")
- local depsfileflag = compiler_support.get_depsfileflag(target)
- local depstargetflag = compiler_support.get_depstargetflag(target)
- local dependfile = target:dependfile(sourcefile)
- local flags = compinst:compflags({sourcefile = sourcefile, target = target}) or {}
- local changed = false
-
- depend.on_changed(function()
- if opt.progress then
- progress.show(opt.progress, "${color.build.target}<%s> generating.module.deps %s", target:fullname(), sourcefile)
- end
-
- local outputdir = compiler_support.get_outputdir(target, sourcefile)
- local jsonfile = path.translate(path.join(outputdir, path.filename(sourcefile) .. ".json"))
- local has_depsflags = depsformatflag and depsfileflag and depstargetflag
- if has_depsflags and not target:policy("build.c++.gcc.fallbackscanner") then
- local ifile = path.translate(path.join(outputdir, path.filename(sourcefile) .. ".i"))
- local dfile = path.translate(path.join(outputdir, path.filename(sourcefile) .. ".d"))
- local compflags = table.join(flags or {}, baselineflags, {sourcefile, "-MT", jsonfile, "-MD", "-MF", dfile, depsformatflag, depsfileflag .. jsonfile, depstargetflag .. target:objectfile(sourcefile), "-o", ifile})
- os.vrunv(compinst:program(), compflags)
- os.rm(ifile)
- os.rm(dfile)
- else
- if not has_depsflags then
- wprint("GCC doesn't support module scanning ! using fallback scanner")
- end
- fallback_generate_dependencies(target, jsonfile, sourcefile, function(file)
- local compflags = table.clone(flags)
- -- exclude -fmodule* flags because, when they are set gcc try to find bmi of imported modules but they don't exists a this point of compilation
- table.remove_if(compflags, function(_, flag) return flag:startswith("-fmodule") end)
- local ifile = path.translate(path.join(outputdir, path.filename(file) .. ".i"))
- compflags = table.join(baselineflags, compflags or {}, {file, "-o", ifile})
- os.vrunv(compinst:program(), compflags)
- local content = io.readfile(ifile)
- os.rm(ifile)
- return content
- end)
- end
- changed = true
-
- local dependinfo = io.readfile(jsonfile)
- return { moduleinfo = dependinfo }
- end, {dependfile = dependfile, files = {sourcefile}, changed = target:is_rebuilt(), values = flags})
- return changed
-end
-
diff --git a/xmake/rules/c++/modules/modules_support/msvc/builder.lua b/xmake/rules/c++/modules/modules_support/msvc/builder.lua
deleted file mode 100644
index 8f582438b..000000000
--- a/xmake/rules/c++/modules/modules_support/msvc/builder.lua
+++ /dev/null
@@ -1,512 +0,0 @@
---!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 msvc/builder.lua
---
-
--- imports
-import("core.base.json")
-import("core.base.option")
-import("core.base.semver")
-import("utils.progress")
-import("private.action.build.object", {alias = "objectbuilder"})
-import("core.tool.compiler")
-import("core.project.config")
-import("core.project.depend")
-import("private.tools.vstool")
-import("compiler_support")
-import(".builder", {inherit = true})
-
--- get flags for building a module
-function _make_modulebuildflags(target, provide, bmifile, opt)
- local ifcoutputflag = compiler_support.get_ifcoutputflag(target)
- local ifconlyflag = compiler_support.get_ifconlyflag(target)
- local interfaceflag = compiler_support.get_interfaceflag(target)
- local internalpartitionflag = compiler_support.get_internalpartitionflag(target)
- local ifconly = (not opt.build_objectfile and ifconlyflag)
-
- local flags
- if provide then -- named module
- flags = table.join({"-TP", ifcoutputflag, path(bmifile), provide.interface and interfaceflag or internalpartitionflag}, ifconly or {})
- else
- flags = {"-TP"}
- end
- return flags
-end
-function _compile_one_step(target, bmifile, sourcefile, objectfile, provide, opt)
- local ifcoutputflag = compiler_support.get_ifcoutputflag(target)
- local interfaceflag = compiler_support.get_interfaceflag(target)
- local internalpartitionflag = compiler_support.get_internalpartitionflag(target)
- -- get flags
- local flags = {"-TP"}
- if provide then
- table.join2(flags, ifcoutputflag, path(bmifile), provide.interface and interfaceflag or internalpartitionflag)
- end
- if opt and opt.batchcmds then
- _batchcmds_compile(opt.batchcmds, target, flags, sourcefile, objectfile)
- else
- _compile(target, flags, sourcefile, objectfile)
- end
-end
-
-function _compile_bmi_step(target, bmifile, sourcefile, objectfile, provide, opt)
- local ifcoutputflag = compiler_support.get_ifcoutputflag(target)
- local interfaceflag = compiler_support.get_interfaceflag(target)
- local ifconlyflag = compiler_support.get_ifconlyflag(target)
-
- if not ifconlyflag then
- _compile_one_step(target, bmifile, sourcefile, objectfile, provide, opt)
- else
- local flags = {"-TP", ifcoutputflag, path(bmifile), interfaceflag, ifconlyflag}
- if opt and opt.batchcmds then
- _batchcmds_compile(opt.batchcmds, target, flags, sourcefile, bmifile)
- else
- _compile(target, flags, sourcefile, bmifile)
- end
- end
-end
-
-function _compile_objectfile_step(target, bmifile, sourcefile, objectfile, provide, opt)
- local ifconlyflag = compiler_support.get_ifconlyflag(target)
- local interfaceflag = compiler_support.get_interfaceflag(target)
- local internalpartitionflag = compiler_support.get_internalpartitionflag(target)
-
- local flags = {"-TP", (provide and provide.interface) and interfaceflag or internalpartitionflag}
- if not ifconlyflag then
- _compile_one_step(target, bmifile, sourcefile, objectfile, provide, opt)
- else
- if opt and opt.batchcmds then
- _batchcmds_compile(opt.batchcmds, target, flags, sourcefile, objectfile)
- else
- _compile(target, flags, sourcefile, objectfile)
- end
- end
-end
-
-
--- get flags for building a headerunit
-function _make_headerunitflags(target, headerunit, bmifile)
-
- -- get flags
- local exportheaderflag = compiler_support.get_exportheaderflag(target)
- local headernameflag = compiler_support.get_headernameflag(target)
- local ifcoutputflag = compiler_support.get_ifcoutputflag(target)
- local ifconlyflag = compiler_support.get_ifconlyflag(target)
- assert(headernameflag and exportheaderflag, "compiler(msvc): does not support c++ header units!")
-
- local local_directory = (headerunit.type == ":quote") and {"-I" .. path.directory(headerunit.path)} or {}
- local flags = table.join(local_directory, {"-TP",
- exportheaderflag,
- headernameflag .. headerunit.type,
- headerunit.type == ":angle" and headerunit.name or headerunit.path,
- ifcoutputflag,
- bmifile}, ifconlyflag or {})
- return flags
-end
-
--- do compile
-function _compile(target, flags, sourcefile, outputfile, headerunit)
-
- local dryrun = option.get("dry-run")
- local compinst = target:compiler("cxx")
- local compflags = compinst:compflags({sourcefile = sourcefile, target = target})
- local flags = table.join(compflags or {}, flags)
-
- -- trace
- if option.get("verbose") then
- if headerunit then
- print(os.args(compinst:program(), flags))
- else
- print(compinst:compcmd(sourcefile, outputfile, {target = target, compflags = flags, rawargs = true}))
- end
- end
-
- -- do compile
- if not dryrun then
- if headerunit then
- local msvc = target:toolchain("msvc")
- os.vrunv(compinst:program(), flags, {envs = msvc:runenvs()})
- else
- assert(compinst:compile(sourcefile, outputfile, {target = target, compflags = flags}))
- end
- end
-end
-
--- do compile for batchcmds
--- @note we need to use batchcmds:compilev to translate paths in compflags for generator, e.g. -Ixx
-function _batchcmds_compile(batchcmds, target, flags, sourcefile, outputfile)
- opt = opt or {}
- local compinst = target:compiler("cxx")
- local compflags = compinst:compflags({sourcefile = sourcefile, target = target})
- flags = table.join(compflags or {}, flags)
- batchcmds:compile(sourcefile, outputfile, {sourcekind = "cxx", compflags = flags})
-end
-
--- get module requires flags
--- e.g
--- /reference Foo=build/.gens/Foo/rules/modules/cache/Foo.ifc
--- /headerUnit:angle glm/mat4x4.hpp=Users\arthu\AppData\Local\.xmake\packages\g\glm\0.9.9+8\91454f3ee0be416cb9c7452970a2300f\include\glm\mat4x4.hpp.ifc
---
-function _get_requiresflags(target, module, opt)
-
- local referenceflag = compiler_support.get_referenceflag(target)
- local headerunitflag = compiler_support.get_headerunitflag(target)
-
- local name = module.name
- local cachekey = target:fullname() .. name
-
- local requires, requires_changed = is_dependencies_changed(target, module)
- local requiresflags = compiler_support.memcache():get2(cachekey, "requiresflags")
- if not requiresflags or requires_changed then
- local deps_flags = {}
- for required in requires:orderitems() do
- local dep_module = get_from_target_mapper(target, required)
- assert(dep_module, "module dependency %s required for %s not found <%s>", required, name, target:fullname())
-
- local mapflag
- local bmifile = dep_module.bmi
- -- aliased headerunit
- if dep_module.aliasof then
- local aliased = get_from_target_mapper(target, dep_module.aliasof)
- bmifile = aliased.bmi
- mapflag = {headerunitflag .. aliased.headerunit.type, required .. "=" .. bmifile}
- -- headerunit
- elseif dep_module.headerunit then
- mapflag = {headerunitflag .. dep_module.headerunit.type, required .. "=" .. bmifile}
- -- named module
- else
- mapflag = {referenceflag, required .. "=" .. bmifile}
- end
- table.insert(deps_flags, mapflag)
-
- -- append deps
- if dep_module.opt and dep_module.opt.deps then
- local deps = _get_requiresflags(target, { name = dep_module.name or dep_module.sourcefile, bmi = bmifile, requires = dep_module.opt.deps })
- table.join2(deps_flags, deps)
- end
- end
-
- -- remove duplicates
- requiresflags = {}
- local contains = {}
- for _, map in ipairs(deps_flags) do
- local name = map[2]:split("=")[1]
- if name and not contains[name] then
- table.insert(requiresflags, map)
- contains[name] = true
- end
- end
- compiler_support.memcache():set2(cachekey, "requiresflags", requiresflags)
- compiler_support.memcache():set2(cachekey, "oldrequires", requires)
- end
- return requiresflags
-end
-
-function _append_requires_flags(target, module, name, cppfile, bmifile, opt)
- local cxxflags = {}
- local requiresflags = _get_requiresflags(target, {name = (name or cppfile), bmi = bmifile, requires = module.requires})
- for _, flag in ipairs(requiresflags) do
- -- we need to wrap flag to support flag with space
- if type(flag) == "string" and flag:find(" ", 1, true) then
- table.insert(cxxflags, {flag})
- else
- table.insert(cxxflags, flag)
- end
- end
- target:fileconfig_add(cppfile, {force = {cxxflags = cxxflags}})
-end
-
--- populate module map
-function populate_module_map(target, modules)
- for _, module in pairs(modules) do
- local name, provide, cppfile = compiler_support.get_provided_module(module)
- if provide then
- local bmifile = compiler_support.get_bmi_path(provide.bmi)
- add_module_to_target_mapper(target, name, cppfile, bmifile, {deps = module.requires})
- end
- end
-end
-
--- get defines for a module
-function get_module_required_defines(target, sourcefile)
- local compinst = compiler.load("cxx", {target = target})
- local compflags = compinst:compflags({sourcefile = sourcefile, target = target})
- local defines
- for _, flag in ipairs(compflags) do
- if flag:startswith("-D") or flag:startswith("/D") then
- defines = defines or {}
- table.insert(defines, flag:sub(3))
- end
- end
- return defines
-end
-
--- build module file for batchjobs
-function make_module_buildjobs(target, batchjobs, job_name, deps, opt)
- local name, provide, _ = compiler_support.get_provided_module(opt.module)
- local bmifile = provide and compiler_support.get_bmi_path(provide.bmi)
- local dryrun = option.get("dry-run")
-
- return {
- name = job_name,
- deps = table.join(target:fullname() .. "/module/populate_module_map", deps),
- sourcefile = opt.cppfile,
- job = batchjobs:newjob(target:fullname() .. "/module/" .. (name or opt.cppfile), function(index, total, jobopt)
-
- local mapped_bmi
- if provide and compiler_support.memcache():get2(target:fullname() .. name, "reuse") then
- mapped_bmi = get_from_target_mapper(target, name).bmi
- end
-
- local build, dependinfo
- local dependfile = target:dependfile(bmifile or opt.objectfile)
- if provide or compiler_support.has_module_extension(opt.cppfile) then
- build, dependinfo = should_build(target, opt.cppfile, bmifile, {name = name, objectfile = opt.objectfile, requires = opt.module.requires})
-
- -- needed to detect rebuild of dependencies
- if provide and build then
- mark_build(target, name)
- end
- end
-
- -- append requires flags
- if opt.module.requires then
- _append_requires_flags(target, opt.module, name, opt.cppfile, bmifile, opt)
- end
-
- -- for cpp file we need to check after appendings the flags
- if build == nil then
- build, dependinfo = should_build(target, opt.cppfile, bmifile, {name = name, objectfile = opt.objectfile, requires = opt.module.requires})
- end
-
- if build then
- -- compile if it's a named module
- if provide or compiler_support.has_module_extension(opt.cppfile) then
- if not dryrun then
- local objectdir = path.directory(opt.objectfile)
- if not os.isdir(objectdir) then
- os.mkdir(objectdir)
- end
- end
-
- local fileconfig = target:fileconfig(opt.cppfile)
- local public = fileconfig and fileconfig.public
- local external = fileconfig and fileconfig.external
- local from_moduleonly = external and external.moduleonly
- local bmifile = mapped_bmi or bmifile
- if external and not from_moduleonly then
- if not mapped_bmi then
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.bmi.$(mode) %s", target:fullname(), name or opt.cppfile)
- _compile_bmi_step(target, bmifile, opt.cppfile, opt.objectfile, provide)
- end
- else
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.module.$(mode) %s", target:fullname(), name or opt.cppfile)
- _compile_one_step(target, bmifile, opt.cppfile, opt.objectfile, provide)
- end
- else
- os.tryrm(opt.objectfile) -- force rebuild for .cpp files
- end
- depend.save(dependinfo, dependfile)
- end
- end)}
-end
-
--- build module file for jobgraph
-function make_module_jobgraph(target, jobgraph, opt)
- local name, provide, _ = compiler_support.get_provided_module(opt.module)
- local bmifile = provide and compiler_support.get_bmi_path(provide.bmi)
- local dryrun = option.get("dry-run")
-
- local jobname = target:fullname() .. "/module/" .. (name or opt.cppfile)
- jobgraph:add(jobname, function(index, total, jobopt)
- local mapped_bmi
- if provide and compiler_support.memcache():get2(target:fullname() .. name, "reuse") then
- mapped_bmi = get_from_target_mapper(target, name).bmi
- end
-
- local build, dependinfo
- local dependfile = target:dependfile(bmifile or opt.objectfile)
- if provide or compiler_support.has_module_extension(opt.cppfile) then
- build, dependinfo = should_build(target, opt.cppfile, bmifile, {name = name, objectfile = opt.objectfile, requires = opt.module.requires})
-
- -- needed to detect rebuild of dependencies
- if provide and build then
- mark_build(target, name)
- end
- end
-
- -- append requires flags
- if opt.module.requires then
- _append_requires_flags(target, opt.module, name, opt.cppfile, bmifile, opt)
- end
-
- -- for cpp file we need to check after appendings the flags
- if build == nil then
- build, dependinfo = should_build(target, opt.cppfile, bmifile, {name = name, objectfile = opt.objectfile, requires = opt.module.requires})
- end
-
- if build then
- -- compile if it's a named module
- if provide or compiler_support.has_module_extension(opt.cppfile) then
- if not dryrun then
- local objectdir = path.directory(opt.objectfile)
- if not os.isdir(objectdir) then
- os.mkdir(objectdir)
- end
- end
-
- local fileconfig = target:fileconfig(opt.cppfile)
- local public = fileconfig and fileconfig.public
- local external = fileconfig and fileconfig.external
- local from_moduleonly = external and external.moduleonly
- local bmifile = mapped_bmi or bmifile
- if external and not from_moduleonly then
- if not mapped_bmi then
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.bmi.$(mode) %s", target:fullname(), name or opt.cppfile)
- _compile_bmi_step(target, bmifile, opt.cppfile, opt.objectfile, provide)
- end
- else
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.module.$(mode) %s", target:fullname(), name or opt.cppfile)
- _compile_one_step(target, bmifile, opt.cppfile, opt.objectfile, provide)
- end
- else
- os.tryrm(opt.objectfile) -- force rebuild for .cpp files
- end
- depend.save(dependinfo, dependfile)
- end
- end)
-end
-
--- build module file for batchcmds
-function make_module_buildcmds(target, batchcmds, opt)
-
- local name, provide, _ = compiler_support.get_provided_module(opt.module)
- local bmifile = provide and compiler_support.get_bmi_path(provide.bmi)
-
- local mapped_bmi
- if provide and compiler_support.memcache():get2(target:fullname() .. name, "reuse") then
- mapped_bmi = get_from_target_mapper(target, name).bmi
- end
-
- -- append requires flags
- if opt.module.requires then
- _append_requires_flags(target, opt.module, name, opt.cppfile, bmifile, opt)
- end
-
- -- compile if it's a named module
- if provide or compiler_support.has_module_extension(opt.cppfile) then
- batchcmds:mkdir(path.directory(opt.objectfile))
-
- local fileconfig = target:fileconfig(opt.cppfile)
- local public = fileconfig and fileconfig.public
- local external = fileconfig and fileconfig.external
- local from_moduleonly = external and external.moduleonly
- local bmifile = mapped_bmi or bmifile
- if external and not from_moduleonly then
- if not mapped_bmi then
- batchcmds:show_progress(opt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.bmi.$(mode) %s", target:fullname(), name or opt.cppfile)
- _compile_bmi_step(target, bmifile, opt.cppfile, provide, {batchcmds = batchcmds})
- end
- else
- batchcmds:show_progress(opt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.module.$(mode) %s", target:fullname(), name or opt.cppfile)
- _compile_one_step(target, bmifile, opt.cppfile, opt.objectfile, provide, {batchcmds = batchcmds})
- end
- else
- batchcmds:rm(opt.objectfile) -- force rebuild for .cpp files
- end
- batchcmds:add_depfiles(opt.cppfile)
- return os.mtime(opt.objectfile)
-end
-
--- build headerunit file for batchjobs
-function make_headerunit_buildjobs(target, job_name, batchjobs, headerunit, bmifile, outputdir, opt)
- local already_exists = add_headerunit_to_target_mapper(target, headerunit, bmifile)
- if not already_exists then
- return {
- name = job_name,
- sourcefile = headerunit.path,
- job = batchjobs:newjob(job_name, function(index, total, jobopt)
- if not os.isdir(outputdir) then
- os.mkdir(outputdir)
- end
-
- local compinst = compiler.load("cxx", {target = target})
- local compflags = compinst:compflags({sourcefile = headerunit.path, target = target})
-
- local dependfile = target:dependfile(bmifile)
- local dependinfo = depend.load(dependfile) or {}
- dependinfo.files = {}
- local depvalues = {compinst:program(), compflags}
-
- local name = headerunit.unique and headerunit.name or headerunit.path
-
- if opt.build then
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.headerunit.$(mode) %s", target:fullname(), headerunit.name)
- _compile(target, _make_headerunitflags(target, headerunit, bmifile), name, target:objectfile(headerunit.path), true)
- end
-
- table.insert(dependinfo.files, headerunit.path)
- dependinfo.values = depvalues
- depend.save(dependinfo, dependfile)
- end)}
- end
-end
-
--- build headerunit file for jobgraph
-function make_headerunit_jobgraph(target, job_name, jobgraph, headerunit, bmifile, outputdir, opt)
- local already_exists = add_headerunit_to_target_mapper(target, headerunit, bmifile)
- if not already_exists then
- jobgraph:add(job_name, function(index, total, jobopt)
- if not os.isdir(outputdir) then
- os.mkdir(outputdir)
- end
-
- local compinst = compiler.load("cxx", {target = target})
- local compflags = compinst:compflags({sourcefile = headerunit.path, target = target})
-
- local dependfile = target:dependfile(bmifile)
- local dependinfo = depend.load(dependfile) or {}
- dependinfo.files = {}
- local depvalues = {compinst:program(), compflags}
-
- local name = headerunit.unique and headerunit.name or headerunit.path
-
- if opt.build then
- progress.show(jobopt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.headerunit.$(mode) %s", target:fullname(), headerunit.name)
- _compile(target, _make_headerunitflags(target, headerunit, bmifile), name, target:objectfile(headerunit.path), true)
- end
-
- table.insert(dependinfo.files, headerunit.path)
- dependinfo.values = depvalues
- depend.save(dependinfo, dependfile)
- end)
- end
-end
-
--- build headerunit file for batchcmds
-function make_headerunit_buildcmds(target, batchcmds, headerunit, bmifile, outputdir, opt)
- batchcmds:mkdir(outputdir)
- add_headerunit_to_target_mapper(target, headerunit, bmifile)
-
- if opt.build then
- local name = headerunit.unique and headerunit.name or headerunit.path
- batchcmds:show_progress(opt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.headerunit.$(mode) %s", target:fullname(), name)
- _batchcmds_compile(batchcmds, target, _make_headerunitflags(target, headerunit, bmifile), target:objectfile(headerunit.path))
- end
- batchcmds:add_depfiles(headerunit.path)
- return os.mtime(bmifile)
-end
diff --git a/xmake/rules/c++/modules/modules_support/msvc/compiler_support.lua b/xmake/rules/c++/modules/modules_support/msvc/compiler_support.lua
deleted file mode 100644
index ca9e8053d..000000000
--- a/xmake/rules/c++/modules/modules_support/msvc/compiler_support.lua
+++ /dev/null
@@ -1,300 +0,0 @@
---!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 msvc/compiler_support.lua
---
-
--- imports
-import("core.base.semver")
-import("core.project.config")
-import("lib.detect.find_tool")
-import(".compiler_support", {inherit = true})
-
--- load module support for the current target
-function load(target)
-
- local msvc = target:toolchain("msvc")
- local vcvars = msvc:config("vcvars")
-
- -- enable std modules if c++23 by defaults
- if target:data("c++.msvc.enable_std_import") == nil and target:policy("build.c++.modules.std") then
- local languages = target:get("languages")
- local isatleastcpp23 = false
- for _, language in ipairs(languages) do
- if language:startswith("c++") or language:startswith("cxx") then
- isatleastcpp23 = true
- local version = tonumber(language:match("%d+"))
- if (not version or version <= 20) and not language:match("latest") then
- isatleastcpp23 = false
- break
- end
- end
- end
- local stdmodulesdir
- local msvc = target:toolchain("msvc")
- if msvc then
- local vcvars = msvc:config("vcvars")
- if vcvars.VCInstallDir and vcvars.VCToolsVersion and semver.compare(vcvars.VCToolsVersion, "14.35") > 0 then
- stdmodulesdir = path.join(vcvars.VCInstallDir, "Tools", "MSVC", vcvars.VCToolsVersion, "modules")
- end
- end
- target:data_set("c++.msvc.enable_std_import", isatleastcpp23 and os.isdir(stdmodulesdir))
- end
-end
-
--- strip flags that doesn't affect bmi generation
-function strip_flags(target, flags)
- -- speculative list as there is no resource that list flags that prevent reusability, this list will likely be improve over time
- -- @see https://learn.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically?view=msvc-170
- local strippable_flags = {
- "I",
- "TP",
- "errorReport",
- "W",
- "w",
- "sourceDependencies",
- "scanDependencies",
- "reference",
- "PD",
- "nologo",
- "MP",
- "internalPartition",
- "interface",
- "ifcOutput",
- "help",
- "headerUnit",
- "headerName",
- "Fp",
- "Fo",
- "Fm",
- "Fe",
- "Fd",
- "FC",
- "exportHeader",
- "EP",
- "E",
- "doc",
- "diagnostics",
- "cgthreads",
- "C",
- "analyze",
- "?",
- }
- if not target:policy("build.c++.modules.tryreuse.discriminate_on_defines") then
- table.join2(strippable_flags, {"D", "U"})
- end
- local output = {}
- for _, flag in ipairs(flags) do
- local strip = false
- for _, _flag in ipairs(strippable_flags) do
- if flag:startswith("cl::-" .. _flag) or flag:startswith("cl::/" .. _flag) or
- flag:startswith("-" .. _flag) or flag:startswith("/" .. _flag) then
- strip = true
- break
- end
- end
- if not strip then
- table.insert(output, flag)
- end
- end
- return output
-end
-
--- provide toolchain include dir for stl headerunit when p1689 is not supported
-function toolchain_includedirs(target)
- for _, toolchain_inst in ipairs(target:toolchains()) do
- if toolchain_inst:name() == "msvc" then
- local vcvars = toolchain_inst:config("vcvars")
- if vcvars.VCInstallDir and vcvars.VCToolsVersion then
- return { path.join(vcvars.VCInstallDir, "Tools", "MSVC", vcvars.VCToolsVersion, "include") }
- end
- break
- end
- end
- raise("msvc toolchain includedirs not found!")
-end
-
--- build c++23 standard modules if needed
-function get_stdmodules(target)
- if target:policy("build.c++.modules.std") then
- if target:data("c++.msvc.enable_std_import") then
- local msvc = target:toolchain("msvc")
- if msvc then
- local vcvars = msvc:config("vcvars")
- if vcvars.VCInstallDir and vcvars.VCToolsVersion then
- modules = {}
-
- local stdmodulesdir = path.join(vcvars.VCInstallDir, "Tools", "MSVC", vcvars.VCToolsVersion, "modules")
- assert(stdmodulesdir, "Can't enable C++23 std modules, directory missing !")
-
- return {path.join(stdmodulesdir, "std.ixx"), path.join(stdmodulesdir, "std.compat.ixx")}
- end
- end
- end
- wprint("std and std.compat modules not found! disabling them for the build")
- end
-end
-
-function get_bmi_extension()
- return ".ifc"
-end
-
-function get_ifcoutputflag(target)
- local ifcoutputflag = _g.ifcoutputflag
- if ifcoutputflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags({"-ifcOutput", os.tmpfile()}, "cxxflags", {flagskey = "cl_ifc_output"}) then
- ifcoutputflag = "-ifcOutput"
- end
- assert(ifcoutputflag, "compiler(msvc): does not support c++ module flag(/ifcOutput)!")
- _g.ifcoutputflag = ifcoutputflag or false
- end
- return ifcoutputflag or nil
-end
-
-function get_ifconlyflag(target)
- local ifconlyflag = _g.ifconlyflag
- if ifconlyflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags({"-ifcOnly"}, "cxxflags", {flagskey = "cl_ifc_only"}) then
- ifconlyflag = "-ifcOnly"
- end
- _g.ifconlyflag = ifconlyflag or false
- end
- return ifconlyflag or nil
-end
-
-function get_ifcsearchdirflag(target)
- local ifcsearchdirflag = _g.ifcsearchdirflag
- if ifcsearchdirflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags({"-ifcSearchDir", os.tmpdir()}, "cxxflags", {flagskey = "cl_ifc_search_dir"}) then
- ifcsearchdirflag = "-ifcSearchDir"
- end
- assert(ifcsearchdirflag, "compiler(msvc): does not support c++ module flag(/ifcSearchDir)!")
- _g.ifcsearchdirflag = ifcsearchdirflag or false
- end
- return ifcsearchdirflag or nil
-end
-
-function get_interfaceflag(target)
- local interfaceflag = _g.interfaceflag
- if interfaceflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-interface", "cxxflags", {flagskey = "cl_interface"}) then
- interfaceflag = "-interface"
- end
- assert(interfaceflag, "compiler(msvc): does not support c++ module flag(/interface)!")
- _g.interfaceflag = interfaceflag or false
- end
- return interfaceflag
-end
-
-function get_referenceflag(target)
- local referenceflag = _g.referenceflag
- if referenceflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags({"-reference", "Foo=" .. os.tmpfile()}, "cxxflags", {flagskey = "cl_reference"}) then
- referenceflag = "-reference"
- end
- assert(referenceflag, "compiler(msvc): does not support c++ module flag(/reference)!")
- _g.referenceflag = referenceflag or false
- end
- return referenceflag or nil
-end
-
-function get_headernameflag(target)
- local headernameflag = _g.headernameflag
- if headernameflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags({"-std:c++latest", "-exportHeader", "-headerName:quote"}, "cxxflags", {flagskey = "cl_header_name_quote"}) and
- compinst:has_flags({"-std:c++latest", "-exportHeader", "-headerName:angle"}, "cxxflags", {flagskey = "cl_header_name_angle"}) then
- headernameflag = "-headerName"
- end
- _g.headernameflag = headernameflag or false
- end
- return headernameflag or nil
-end
-
-function get_headerunitflag(target)
- local headerunitflag = _g.headerunitflag
- if headerunitflag == nil then
- local compinst = target:compiler("cxx")
- local ifcfile = os.tmpfile()
- if compinst:has_flags({"-std:c++latest", "-headerUnit:quote", "foo.h=" .. ifcfile}, "cxxflags", {flagskey = "cl_header_unit_quote"}) and
- compinst:has_flags({"-std:c++latest", "-headerUnit:angle", "foo.h=" .. ifcfile}, "cxxflags", {flagskey = "cl_header_unit_angle"}) then
- headerunitflag = "-headerUnit"
- end
- _g.headerunitflag = headerunitflag or false
- end
- return headerunitflag or nil
-end
-
-function get_exportheaderflag(target)
- local exportheaderflag = _g.exportheaderflag
- if exportheaderflag == nil then
- if get_headernameflag(target) then
- exportheaderflag = "-exportHeader"
- end
- _g.exportheaderflag = exportheaderflag or false
- end
- return exportheaderflag or nil
-end
-
-function get_scandependenciesflag(target)
- local scandependenciesflag = _g.scandependenciesflag
- if scandependenciesflag == nil then
- local compinst = target:compiler("cxx")
- local scan_dependencies_jsonfile = os.tmpfile() .. ".json"
- if compinst:has_flags("-scanDependencies " .. scan_dependencies_jsonfile, "cxflags", {flagskey = "cl_scan_dependencies",
- on_check = function (ok, errors)
- if os.isfile(scan_dependencies_jsonfile) then
- ok = true
- end
- if ok and not os.isfile(scan_dependencies_jsonfile) then
- ok = false
- end
- return ok, errors
- end}) then
- scandependenciesflag = "-scanDependencies"
- end
- _g.scandependenciesflag = scandependenciesflag or false
- end
- return scandependenciesflag or nil
-end
-
-function get_cppversionflag(target)
- local cppversionflag = _g.cppversionflag
- if cppversionflag == nil then
- local compinst = target:compiler("cxx")
- local flags = compinst:compflags({target = target})
- cppversionflag = table.find_if(flags, function(v) string.startswith(v, "/std:c++") end) or "/std:c++latest"
- end
- return cppversionflag or nil
-end
-
-function get_internalpartitionflag(target)
- local internalpartitionflag = _g.internalpartitionflag
- if internalpartitionflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-internalPartition", "cxxflags", {flagskey = "cl_internal_partition"}) then
- internalpartitionflag = "-internalPartition"
- end
- _g.internalpartitionflag = internalpartitionflag or false
- end
- return internalpartitionflag or nil
-end
diff --git a/xmake/rules/c++/modules/modules_support/msvc/dependency_scanner.lua b/xmake/rules/c++/modules/modules_support/msvc/dependency_scanner.lua
deleted file mode 100644
index 044510fab..000000000
--- a/xmake/rules/c++/modules/modules_support/msvc/dependency_scanner.lua
+++ /dev/null
@@ -1,68 +0,0 @@
---!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 msvc/dependency_scanner.lua
---
-
--- imports
-import("core.base.json")
-import("core.base.semver")
-import("core.project.depend")
-import("private.tools.vstool")
-import("utils.progress")
-import("compiler_support")
-import("builder")
-import(".dependency_scanner", {inherit = true})
-
--- generate dependency files
-function generate_dependency_for(target, sourcefile, opt)
- local msvc = target:toolchain("msvc")
- local scandependenciesflag = compiler_support.get_scandependenciesflag(target)
- local ifcoutputflag = compiler_support.get_ifcoutputflag(target)
- local common_flags = {"-TP", scandependenciesflag}
- local dependfile = target:dependfile(sourcefile)
- local compinst = target:compiler("cxx")
- local flags = compinst:compflags({sourcefile = sourcefile, target = target}) or {}
- local changed = false
-
- depend.on_changed(function ()
- progress.show(opt.progress, "${color.build.target}<%s> generating.module.deps %s", target:fullname(), sourcefile)
- local outputdir = compiler_support.get_outputdir(target, sourcefile)
-
- local jsonfile = path.join(outputdir, path.filename(sourcefile) .. ".module.json")
- if scandependenciesflag and not target:policy("build.c++.msvc.fallbackscanner") then
- local dependency_flags = {jsonfile, sourcefile, ifcoutputflag, outputdir, "-Fo" .. target:objectfile(sourcefile)}
- local compflags = table.join(flags, common_flags, dependency_flags)
- os.vrunv(compinst:program(), winos.cmdargv(compflags), {envs = msvc:runenvs()})
- else
- fallback_generate_dependencies(target, jsonfile, sourcefile, function(file)
- local ifile = path.translate(path.join(outputdir, path.filename(file) .. ".i"))
- os.vrunv(compinst:program(), table.join(flags,
- {"/P", "-TP", file, "/Fi" .. ifile}), {envs = msvc:runenvs()})
- local content = io.readfile(ifile)
- os.rm(ifile)
- return content
- end)
- end
- changed = true
-
- local dependinfo = io.readfile(jsonfile)
- return { moduleinfo = dependinfo }
- end, {dependfile = dependfile, files = {sourcefile}, changed = target:is_rebuilt(), values = flags})
- return changed
-end
-
diff --git a/xmake/rules/c++/modules/modules_support/stl_headers.lua b/xmake/rules/c++/modules/modules_support/stl_headers.lua
deleted file mode 100644
index 4b2d6ed23..000000000
--- a/xmake/rules/c++/modules/modules_support/stl_headers.lua
+++ /dev/null
@@ -1,155 +0,0 @@
---!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 stl_headers.lua
---
-
--- imports
-import("core.base.hashset")
-
--- the stl headers list
-function _stl_headers()
- return {
- "algorithm",
- "forward_list",
- "numbers",
- "stop_token",
- "any",
- "fstream",
- "numeric",
- "streambuf",
- "array",
- "functional",
- "optional",
- "string",
- "atomic",
- "future",
- "ostream",
- "string_view",
- "barrier",
- "initializer_list",
- "queue",
- "bit",
- "iomanip",
- "random",
- "syncstream",
- "bitset",
- "ios",
- "ranges",
- "system_error",
- "charconv",
- "iosfwd",
- "ratio",
- "thread",
- "chrono",
- "iostream",
- "regex",
- "tuple",
- "codecvt",
- "istream",
- "scoped_allocator",
- "typeindex",
- "compare",
- "iterator",
- "semaphore",
- "typeinfo",
- "complex",
- "latch",
- "set",
- "type_traits",
- "concepts",
- "limits",
- "shared_mutex",
- "unordered_map",
- "condition_variable",
- "list",
- "source_location",
- "stacktrace",
- "unordered_set",
- "coroutine",
- "locale",
- "span",
- "utility",
- "deque",
- "map",
- "spanstream",
- "valarray",
- "exception",
- "memory",
- "sstream",
- "variant",
- "execution",
- "memory_resource",
- "stack",
- "vector",
- "filesystem",
- "mutex",
- "version",
- "format",
- "new",
- "type_traits",
- "string_view",
- "stdexcept",
- "condition_variable",
- "print",
- "flat_map",
- "flat_set",
- "mdspan",
- "stdfloat",
- "generator",
- "csetjmp",
- "csignal",
- "cstdarg",
- "cstddef",
- "cstdlib",
- "cfloat",
- "cinttypes",
- "climits",
- "cstdint",
- "cassert",
- "cerrno",
- "cctype",
- "cstring",
- "cuchar",
- "cwchar",
- "cwctype",
- "cfenv",
- "cmath",
- "ctime",
- "clocale",
- "expected",
- "cstdio"}
-end
-
--- get all stl headers
-function get_stl_headers()
- local stl_headers = _g.stl_headers
- if stl_headers == nil then
- stl_headers = hashset.from(_stl_headers())
- _g.stl_headers = stl_headers or false
- end
- return stl_headers or nil
-end
-
--- is stl header?
-function is_stl_header(header)
- if header:startswith("experimental/") then
- header = header:sub(14, -1)
- end
- return get_stl_headers():has(header)
-end
-