summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArthur LAURENT <[email protected]>2024-01-23 18:23:59 +0100
committerArthur LAURENT <[email protected]>2024-01-26 18:20:58 +0100
commitda5f589a7ea6dfce340b92aad88db8b84aebd5bd (patch)
treead148ac32f4caf5dd5bd62eeefcc4e1b8c5d2a02
parent2148980164af18f1cf362f535c5894c368349f54 (diff)
refactor gcc module infrastructure
-rw-r--r--xmake/rules/c++/modules/modules_support/gcc.lua622
-rw-r--r--xmake/rules/c++/modules/modules_support/gcc/builder.lua375
-rw-r--r--xmake/rules/c++/modules/modules_support/gcc/compiler_support.lua225
-rw-r--r--xmake/rules/c++/modules/modules_support/gcc/dependency_scanner.lua77
4 files changed, 677 insertions, 622 deletions
diff --git a/xmake/rules/c++/modules/modules_support/gcc.lua b/xmake/rules/c++/modules/modules_support/gcc.lua
deleted file mode 100644
index 6cc5c29d6..000000000
--- a/xmake/rules/c++/modules/modules_support/gcc.lua
+++ /dev/null
@@ -1,622 +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
--- @file gcc.lua
---
-
--- imports
-import("core.base.option")
-import("core.base.json")
-import("core.tool.compiler")
-import("core.project.project")
-import("core.project.depend")
-import("core.project.config")
-import("utils.progress")
-import("private.action.build.object", {alias = "objectbuilder"})
-import("common")
-
--- get and create the path of module mapper
-function _get_module_mapper(target)
- local mapper_file = path.join(config.buildir(), target:name(), "mapper.txt")
- if not os.isfile(mapper_file) then
- io.writefile(mapper_file, "")
- end
- return mapper_file
-end
-
--- add a module or header unit into the mapper
---
--- 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 _add_module_to_mapper(file, modulepath, bmi)
- for line in io.lines(file) do
- if line:startswith(modulepath .. " ") then
- return false
- end
- end
- local f = io.open(file, "a")
- f:print("%s %s", modulepath, bmi)
- f:close()
- return true
-end
-
--- get a module from mapper
-function _get_module_from_mapper(file, module)
- for line in io.lines(file) do
- if line:startswith(module .. " ") then
- return line:split(" ", {plain = true})
- end
- end
-end
-
--- load module support for the current target
-function load(target)
- local modulesflag = get_modulesflag(target)
- local modulemapperflag = get_modulemapperflag(target)
- if os.isfile(_get_module_mapper(target)) then
- os.rm(_get_module_mapper(target))
- end
- target:add("cxxflags", {modulesflag, modulemapperflag .. path.translate(_get_module_mapper(target))}, {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
-
--- 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
-
--- 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)
- local compinst = target:compiler("cxx")
- local compflags = compinst:compflags({sourcefile = sourcefile, target = target})
- batchcmds:compilev(table.join(compflags or {}, flags), {compiler = compinst, sourcekind = "cxx"})
-end
-
--- build module file
-function _build_modulefile(target, sourcefile, opt)
- local objectfile = opt.objectfile
- local dependfile = opt.dependfile
- local compinst = compiler.load("cxx", {target = target})
- local compflags = table.join("-x", "c++", compinst:compflags({sourcefile = sourcefile, target = target}))
- local dependinfo = target:is_rebuilt() and {} or (depend.load(dependfile, {target = target}) or {})
-
- -- need build this object?
- local dryrun = option.get("dry-run")
- local depvalues = {compinst:program(), compflags}
- local lastmtime = os.isfile(objectfile) and os.mtime(dependfile) or 0
- if not dryrun and not depend.is_changed(dependinfo, {lastmtime = lastmtime, values = depvalues}) then
- return
- end
-
- -- trace
- progress.show(opt.progress, "${color.build.object}compiling.module.$(mode) %s", opt.name)
- vprint(compinst:compcmd(sourcefile, objectfile, {compflags = compflags, rawargs = true}))
-
- if not dryrun then
-
- -- do compile
- dependinfo.files = {}
- assert(compinst:compile(sourcefile, objectfile, {dependinfo = dependinfo, compflags = compflags}))
-
- -- update files and values to the dependent file
- dependinfo.values = depvalues
- table.join2(dependinfo.files, sourcefile)
- depend.save(dependinfo, dependfile)
- end
-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")
- _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
-
--- generate dependency files
-function generate_dependencies(target, sourcebatch, opt)
- local compinst = target:compiler("cxx")
- local common_args = {"-E", "-x", "c++"}
- local depformatflag = get_depflag(target, "p1689r5") or get_depflag(target, "trtbd")
- local depfileflag = get_depfileflag(target)
- local depoutputflag = get_depoutputflag(target)
- local changed = false
- for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
- local dependfile = target:dependfile(sourcefile)
- depend.on_changed(function()
- if opt.progress then
- progress.show(opt.progress, "${color.build.object}generating.module.deps %s", sourcefile)
- end
-
- local outputdir = common.get_outputdir(target, sourcefile)
- local jsonfile = path.translate(path.join(outputdir, path.filename(sourcefile) .. ".json"))
- if depformatflag and depfileflag and depoutputflag 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 args = {sourcefile, "-MT", jsonfile, "-MD", "-MF", dfile, depformatflag, depfileflag .. jsonfile, depoutputflag .. target:objectfile(sourcefile), "-o", ifile}
- local compflags = compinst:compflags({sourcefile = sourcefile, target = target})
- -- module mapper flag force gcc to check the imports but this is not wanted at this stage
- local modulemapperflag = get_modulemapperflag(target) .. path.translate(_get_module_mapper(target))
- table.remove(compflags, table.unpack(table.find(compflags, modulemapperflag)))
- os.vrunv(compinst:program(), table.join(compflags, common_args, args))
- os.rm(ifile)
- os.rm(dfile)
- else
- common.fallback_generate_dependencies(target, jsonfile, sourcefile, function(file)
- local compinst = target:compiler("cxx")
- local compflags = compinst:compflags({sourcefile = file, target = target})
- -- 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"))
- os.vrunv(compinst:program(), table.join(common_args, compflags, {file, "-o", ifile}))
- 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()})
- end
- return changed
-end
-
--- generate target stl header units for batchjobs
-function generate_stl_headerunits_for_batchjobs(target, batchjobs, headerunits, opt)
- local compinst = target:compiler("cxx")
- local mapper_file = _get_module_mapper(target)
- local stlcachedir = common.stlmodules_cachedir(target, {mkdir = true})
- local modulemapperflag = get_modulemapperflag(target)
-
- -- build headerunits
- local projectdir = os.projectdir()
- for _, headerunit in ipairs(headerunits) do
- local bmifile = path.join(stlcachedir, headerunit.name .. get_bmi_extension())
- if not os.isfile(bmifile) then
- batchjobs:addjob(headerunit.name, function (index, total)
- depend.on_changed(function()
- progress.show((index * 100) / total, "${color.build.object}compiling.headerunit.$(mode) %s", headerunit.name)
- local args = {"-c", "-x", "c++-system-header", headerunit.name}
- local flags = table.join(compinst:compflags({target = target}), args)
- -- we need to support reading and writing mapperfile in parallel, otherwise it will be broken
- -- @see tests/c++/modules/stl_headerunit_cpp_only
- local mapper_file_tmp = os.tmpfile()
- os.cp(mapper_file, mapper_file_tmp)
- _add_module_to_mapper(mapper_file_tmp, headerunit.path, path.absolute(bmifile, projectdir))
- for idx, flag in ipairs(flags) do
- if flag:startswith(modulemapperflag) then
- flags[idx] = modulemapperflag .. mapper_file_tmp
- break
- end
- end
- os.vrunv(compinst:program(), flags)
- _add_module_to_mapper(mapper_file, headerunit.path, path.absolute(bmifile, projectdir))
- os.tryrm(mapper_file_tmp)
- end, {dependfile = target:dependfile(bmifile), files = {headerunit.path}, changed = target:is_rebuilt()})
- end, {rootjob = opt.rootjob})
- else
- _add_module_to_mapper(mapper_file, headerunit.path, path.absolute(bmifile, projectdir))
- end
- end
-end
-
--- generate target stl header units for batchcmds
-function generate_stl_headerunits_for_batchcmds(target, batchcmds, headerunits, opt)
- local mapper_file = _get_module_mapper(target)
- local stlcachedir = common.stlmodules_cachedir(target, {mkdir = true})
-
- -- build headerunits
- local projectdir = os.projectdir()
- local depmtime = 0
- for _, headerunit in ipairs(headerunits) do
- local bmifile = path.join(stlcachedir, headerunit.name .. get_bmi_extension())
- if not os.isfile(bmifile) then
- local flags = {"-c", "-x", "c++-system-header", headerunit.name}
- batchcmds:show_progress(opt.progress, "${color.build.object}compiling.headerunit.$(mode) %s", headerunit.name)
- _batchcmds_compile(batchcmds, target, flags)
- end
- batchcmds:add_depfiles(headerunit.path)
- _add_module_to_mapper(mapper_file, headerunit.path, path.absolute(bmifile, projectdir))
- depmtime = math.max(depmtime, os.mtime(bmifile))
- end
- batchcmds:set_depmtime(depmtime)
-end
-
--- generate target user header units for batchjobs
-function generate_user_headerunits_for_batchjobs(target, batchjobs, headerunits, opt)
- local compinst = target:compiler("cxx")
- local mapper_file = _get_module_mapper(target)
-
- -- build headerunits
- local projectdir = os.projectdir()
- for _, headerunit in ipairs(headerunits) do
- local headerunit_path
- if headerunit.type == ":quote" then
- headerunit_path = path.join(".", path.relative(headerunit.path, projectdir))
- elseif headerunit.type == ":angle" then
- -- if path is relative then its a subtarget path
- headerunit_path = path.is_absolute(headerunit.path) and headerunit.path or path.join(".", headerunit.path)
- end
- local objectfile = target:objectfile(headerunit_path)
- local outputdir = common.get_outputdir(target, headerunit.path)
- local bmifilename = path.basename(objectfile) .. get_bmi_extension()
- local bmifile = path.join(outputdir, bmifilename)
- batchjobs:addjob(headerunit.name, function (index, total)
- depend.on_changed(function()
- progress.show((index * 100) / total, "${color.build.object}compiling.headerunit.$(mode) %s", headerunit.name)
- local objectdir = path.directory(objectfile)
- if not os.isdir(objectdir) then
- os.mkdir(objectdir)
- end
-
- -- generate headerunit
- local args = { "-c" }
- if headerunit.type == ":quote" then
- local includedir
- local p = headerunit.path
- if p:endswith(headerunit.name) then
- includedir = p:sub(1, #p - #headerunit.name - 1)
- else
- includedir = path.directory(p)
- end
- if path.is_absolute(includedir) then
- includedir = path.relative(includedir, projectdir)
- end
- table.join2(args, { "-I", includedir, "-x", "c++-user-header", headerunit.name })
- elseif headerunit.type == ":angle" then
- table.join2(args, { "-x", "c++-system-header", headerunit.name })
- end
- os.vrunv(compinst:program(), table.join(compinst:compflags({target = target}), args))
-
- end, {dependfile = target:dependfile(bmifile), files = {headerunit.path}, changed = target:is_rebuilt()})
- end, {rootjob = opt.rootjob})
- _add_module_to_mapper(mapper_file, headerunit_path, path.absolute(bmifile, projectdir))
- end
-end
-
--- generate target user header units for batchcmds
-function generate_user_headerunits_for_batchcmds(target, batchcmds, headerunits, opt)
- local mapper_file = _get_module_mapper(target)
-
- -- build headerunits
- local projectdir = os.projectdir()
- local depmtime = 0
- for _, headerunit in ipairs(headerunits) do
- local flags = {"-c"}
- local headerunit_path
- if headerunit.type == ":quote" then
- local includedir
- local p = headerunit.path
- if p:endswith(headerunit.name) then
- includedir = p:sub(1, #p - #headerunit.name - 1)
- else
- includedir = path.directory(p)
- end
- if path.is_absolute(includedir) then
- includedir = path.relative(includedir, projectdir)
- end
- table.join2(flags, {"-I", path(includedir), "-x", "c++-user-header", headerunit.name})
- headerunit_path = path.join(".", path.relative(headerunit.path, projectdir))
- elseif headerunit.type == ":angle" then
- table.join2(flags, {"-x", "c++-system-header", headerunit.name})
- -- if path is relative then its a subtarget path
- headerunit_path = path.is_absolute(headerunit.path) and headerunit.path or path.join(".", headerunit.path)
- end
- local objectfile = target:objectfile(headerunit_path)
- local outputdir = common.get_outputdir(target, headerunit.path)
- batchcmds:mkdir(outputdir)
-
- local bmifilename = path.basename(objectfile) .. get_bmi_extension()
- local bmifile = (outputdir and path.join(outputdir, bmifilename) or bmifilename)
- batchcmds:mkdir(path.directory(objectfile))
-
- batchcmds:show_progress(opt.progress, "${color.build.object}compiling.headerunit.$(mode) %s", headerunit.name)
- _batchcmds_compile(batchcmds, target, flags)
- batchcmds:add_depfiles(headerunit.path)
-
- _add_module_to_mapper(mapper_file, headerunit_path, path.absolute(bmifile, projectdir))
- depmtime = math.max(depmtime, os.mtime(bmifile))
- end
- batchcmds:set_depmtime(depmtime)
-end
-
--- build module files for batchjobs
-function build_modules_for_batchjobs(target, batchjobs, objectfiles, modules, opt)
- local mapper_file = _get_module_mapper(target)
-
- -- build modules
- local projectdir = os.projectdir()
- local modulesjobs = {}
- for _, objectfile in ipairs(objectfiles) do
- local module = modules[objectfile]
- if module then
- local cppfile = module.cppfile
- local name, provide
- if module.provides then
- -- assume there that provides is only one, until we encounter the case
- 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
- local moduleinfo = table.copy(provide) or {}
- local dependfile = (provide and provide.bmi) and target:dependfile(provide.bmi) or target:dependfile(objectfile)
-
- if provide then
- local fileconfig = target:fileconfig(cppfile)
- if fileconfig and fileconfig.install then
- batchjobs:addjob(name .. "_metafile", function(index, total)
- local metafilepath = common.get_metafile(target, cppfile)
- depend.on_changed(function()
- progress.show((index * 100) / total, "${color.build.object}generating.module.metadata %s", name)
- local metadata = common.generate_meta_module_info(target, name, cppfile, module.requires)
- json.savefile(metafilepath, metadata)
- end, {dependfile = target:dependfile(metafilepath), files = {cppfile}, changed = target:is_rebuilt()})
- end, {rootjob = opt.rootjob})
- end
- end
-
- table.join2(moduleinfo, {
- name = name or cppfile,
- deps = table.keys(module.requires or {}),
- sourcefile = cppfile,
- job = batchjobs:newjob(name or cppfile, function(index, total)
- -- append dependencies module now to ensures deps modulemap is filled
- for required, _ in pairs(module.requires) do
- local m
- for _, dep in ipairs(target:orderdeps()) do
- m = _get_module_from_mapper(_get_module_mapper(dep), required)
- if m then
- break
- end
- end
- if m then
- _add_module_to_mapper(mapper_file, m[1], m[2])
- break
- end
- end
-
- if provide or common.has_module_extension(cppfile) then
- if not common.memcache():get2(name or cppfile, "compiling") then
- if name and module.external then
- common.memcache():set2(name or cppfile, "compiling", true)
- end
- _build_modulefile(target, cppfile, {
- objectfile = objectfile,
- dependfile = dependfile,
- name = name or cppfile,
- progress = (index * 100) / total})
- end
- target:add("objectfiles", objectfile)
- end
- end)})
- if provide then
- _add_module_to_mapper(mapper_file, name, path.absolute(provide.bmi, projectdir))
- end
- modulesjobs[name or cppfile] = moduleinfo
- end
- end
-
- -- build batchjobs for modules
- common.build_batchjobs_for_modules(modulesjobs, batchjobs, opt.rootjob)
-end
-
--- build module files for batchcmds
-function build_modules_for_batchcmds(target, batchcmds, objectfiles, modules, opt)
- local modulemapperflag = get_modulemapperflag(target)
- local mapper_file = _get_module_mapper(target)
-
- -- build modules
- local projectdir = os.projectdir()
- local depmtime = 0
- for _, objectfile in ipairs(objectfiles) do
- local module = modules[objectfile]
- if module then
- local cppfile = module.cppfile
- local name, provide
- if module.provides then
- 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
- -- append dependencies module now to ensures deps modulemap is filled
- for required, _ in pairs(module.requires) do
- local m
- for _, dep in ipairs(target:orderdeps()) do
- m = _get_module_from_mapper(_get_module_mapper(dep), required)
- if m then
- break
- end
- end
- if m then
- _add_module_to_mapper(mapper_file, m[1], m[2])
- break
- end
- end
- if provide or common.has_module_extension(cppfile) then
- local flags = {"-x", "c++", "-c", path(cppfile), "-o", path(objectfile)}
- batchcmds:show_progress(opt.progress, "${color.build.object}compiling.module.$(mode) %s", name or cppfile)
- batchcmds:mkdir(path.directory(objectfile))
- _batchcmds_compile(batchcmds, target, flags, cppfile)
- batchcmds:add_depfiles(cppfile)
- target:add("objectfiles", objectfile)
- _add_module_to_mapper(mapper_file, name, path.absolute(provide.bmi, projectdir))
- end
- depmtime = math.max(depmtime, os.mtime(provide and provide.bmi or objectfile))
- end
- end
- batchcmds:set_depmtime(depmtime)
-end
-
--- not supported atm
-function get_stdmodules(target)
- local modules = {}
- return modules
-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")
- if 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_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_depflag(target, format)
- local depflag = _g.depflag
- if depflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fdep-format=" .. format, "cxxflags", {flagskey = "gcc_dep_format"}) then
- depflag = "-fdep-format=" .. format
- end
- _g.depflag = depflag or false
- end
- return depflag or nil
-end
-
-function get_depfileflag(target)
- local depfileflag = _g.depfileflag
- if depfileflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fdep-file=" .. os.tmpfile(), "cxxflags", {flagskey = "gcc_dep_file",
- on_check = function (ok, errors)
- if errors:find("cc1plus: error: to generate dependencies") then
- ok = true
- end
- return ok, errors
- end}) then
- depfileflag = "-fdep-file="
- end
- _g.depfileflag = depfileflag or false
- end
- return depfileflag or nil
-end
-
-function get_depoutputflag(target)
- local depoutputflag = _g.depoutputflag
- if depoutputflag == nil then
- local compinst = target:compiler("cxx")
- if compinst:has_flags("-fdep-output=" .. os.tmpfile() .. ".o", "cxxflags", {flagskey = "gcc_dep_output",
- on_check = function (ok, errors)
- if errors:find("cc1plus: error: to generate dependencies") then
- ok = true
- end
- return ok, errors
- end}) then
- depoutputflag = "-fdep-output="
- 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
diff --git a/xmake/rules/c++/modules/modules_support/gcc/builder.lua b/xmake/rules/c++/modules/modules_support/gcc/builder.lua
new file mode 100644
index 000000000..6e76a7646
--- /dev/null
+++ b/xmake/rules/c++/modules/modules_support/gcc/builder.lua
@@ -0,0 +1,375 @@
+--!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 module
+function _make_modulebuildflags(target, opt)
+
+ local flags = {"-x", "c++", "-c"}
+ if opt.batchcmds then
+ table.join2(flags, "-o", target:objectfile(opt.sourcefile), opt.sourcefile)
+ end
+
+ return flags
+end
+
+-- get flags for building a headerunit
+function _make_headerunitflags(target, headerunit, headerunit_mapper, opt)
+
+ -- get flags
+ 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",
+ "-c"})
+ if opt.batchcmds then
+ table.join2(flags, {"-o", opt.bmifile, path.filename(headerunit.path)})
+ end
+
+ 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
+ vprint(compinst:compcmd(sourcefile, outputfile, {compflags = flags, rawargs = true}))
+
+ if not dryrun then
+ -- do compile
+ assert(compinst:compile(sourcefile, outputfile, {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)
+
+ local compinst = target:compiler("cxx")
+ local compflags = compinst:compflags({sourcefile = sourcefile, target = target})
+ local flags = table.join(compflags or {}, flags)
+
+ batchcmds:compilev(flags, {compiler = compinst, sourcekind = "cxx"})
+end
+
+function _module_map_cachekey(target)
+
+ local mode = config.mode()
+ return target:name() .. "module_mapper" .. (mode or "")
+end
+
+-- generate a module mapper file for build a headerunit
+function _generate_headerunit_modulemapper_file(module)
+
+ local dryrun = option.get("dry-run")
+
+ local path = os.tmpfile()
+ local mapper_file = io.open(path, "wb")
+
+ mapper_file:write("root " .. os.projectdir())
+ -- mapper_file:write("root " .. os.projectdir():replace("\\", "/"))
+ mapper_file:write("\n")
+
+ mapper_file:write(mapper_file, module.name:replace("\\", "/") .. " " .. module.bmifile:replace("\\", "/"))
+ -- mapper_file:write(mapper_file, module.name .. " " .. module.bmifile)
+ mapper_file:write("\n")
+
+ mapper_file:close()
+
+ return 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 pairs(module.requires) do
+ local dep_module
+ local dep_target
+ for _, dep in ipairs(target:orderdeps()) do
+ dep_module = get_from_target_mapper(dep, required)
+ if dep_module then
+ dep_target = dep
+ break
+ end
+ end
+
+ -- if not in target dep
+ if not dep_module then
+ dep_module = get_from_target_mapper(target, required)
+ if dep_module then
+ dep_target = target
+ end
+ end
+
+ assert(dep_module, "module dependency %s required for %s not found", required, name)
+
+ local mapline = (dep_module.headerunit and dep_module.headerunit.path:replace("\\", "/") or required) .. " " .. dep_module.bmi:replace("\\", "/")
+ table.insert(maplines, mapline)
+
+ -- append deps
+ if dep_module.opt and dep_module.opt.deps then
+ local deps = _get_maplines(dep_target, { name = dep_module.name, bmi = dep_module.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)
+
+ local maplines = _get_maplines(target, module)
+
+ local path = os.tmpfile()
+ local mapper_file = io.open(path, "wb")
+
+ mapper_file:write("root " .. os.projectdir():replace("\\", "/"))
+ mapper_file:write("\n")
+
+ for _, mapline in ipairs(maplines) do
+ mapper_file:write(mapline)
+ mapper_file:write("\n")
+ end
+
+ mapper_file:close()
+
+ return path
+end
+
+-- populate module map
+function populate_module_map(target, modules)
+
+ local projectdir = os.projectdir()
+
+ -- append all modules
+ for _, module in pairs(modules) do
+ local name, provide = compiler_support.get_provided_module(module)
+ if provide then
+ add_module_to_target_mapper(target, name, provide.sourcefile, path.absolute(compiler_support.get_bmi_path(provide.bmi), projectdir))
+ end
+ end
+
+ -- then update their deps
+ 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, path.absolute(bmifile, projectdir), {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_build_job(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)
+ local populate_job_name = get_modulemap_populate_jobname(target)
+
+ return {
+ name = job_name,
+ deps = table.join({populate_job_name}, deps),
+ sourcefile = opt.cppfile,
+ job = batchjobs:newjob(name or opt.cppfile, function(index, total)
+
+ local compinst = compiler.load("cxx", {target = target})
+ local compflags = compinst:compflags({sourcefile = opt.cppfile, target = target})
+
+ -- generate and append module mapper file
+ local module_mapper
+ if provide or opt.module.requires then
+ module_mapper = _generate_modulemapper_file(target, opt.module)
+ target:fileconfig_add(opt.cppfile, {force = {cxxflags = {module_mapperflag .. module_mapper}}})
+ end
+
+ local dependfile = target:dependfile(bmifile or opt.objectfile)
+ local dependinfo = depend.load(dependfile) or {}
+ dependinfo.files = {}
+ local depvalues = {compinst:program(), compflags}
+
+ if opt.build then
+ -- compile if it's a named module
+ if provide or compiler_support.has_module_extension(opt.cppfile) then
+ progress.show((index * 100) / total, "${color.build.target}<%s> ${clear}${color.build.object}compiling.module.$(mode) %s", target:name(), name or opt.cppfile)
+ if option.get("diagnosis") then
+ print("mapper file --------\n%s--------", io.readfile(module_mapper))
+ end
+
+ local flags = _make_modulebuildflags(target, opt)
+ _compile(target, flags, opt.cppfile, opt.objectfile)
+ os.tryrm(module_mapper)
+ end
+ end
+ table.insert(dependinfo.files, opt.cppfile)
+ dependinfo.values = depvalues
+ depend.save(dependinfo, dependfile)
+ end)}
+end
+
+-- build module file for batchcmds
+function make_module_build_cmds(target, batchcmds, opt)
+
+ local name, provide, _ = compiler_support.get_provided_module(opt.module)
+ local module_mapperflag = compiler_support.get_modulemapperflag(target)
+
+ -- generate and append module mapper file
+ local module_mapper
+ if provide or opt.module.requires then
+ module_mapper = _generate_modulemapper_file(target, opt.module)
+ target:fileconfig_add(opt.cppfile, {force = {cxxflags = {module_mapperflag .. module_mapper}}})
+ end
+
+ if opt.build then
+ -- compile if it's a named module
+ if provide or compiler_support.has_module_extension(opt.cppfile) then
+ batchcmds:show_progress(opt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.module.$(mode) %s", target:name(), name or opt.cppfile)
+ if option.get("diagnosis") then
+ batchcmds:print("mapper file: %s", io.readfile(module_mapper))
+ end
+ batchcmds:mkdir(path.directory(opt.objectfile))
+
+ _batchcmds_compile(batchcmds, target, _make_modulebuildflags(target, {batchcmds = true, sourcefile = opt.cppfile}), opt.cppfile)
+ end
+ end
+
+ batchcmds:add_depfiles(opt.cppfile)
+
+ return os.mtime(opt.objectfile)
+end
+
+-- build headerunit file for batchjobs
+function make_headerunit_build_job(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)
+ 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((index * 100) / total, "${color.build.target}<%s> ${clear}${color.build.object}compiling.headerunit.$(mode) %s", target:name(), headerunit.name)
+ if option.get("diagnosis") then
+ print("mapper file --------\n%s--------", io.readfile(headerunit_mapper))
+ end
+ _compile(target, _make_headerunitflags(target, headerunit, headerunit_mapper, opt), path.translate(path.filename(headerunit.name)), 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_build_cmds(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 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:name(), name)
+ if option.get("diagnosis") then
+ batchcmds:print("mapper file: %s", io.readfile(headerunit_mapper))
+ end
+ _batchcmds_compile(batchcmds, target, _make_headerunitflags(target, headerunit, headerunit_mapper, {batchcmds = true, bmifile = 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
new file mode 100644
index 000000000..147c8eeb1
--- /dev/null
+++ b/xmake/rules/c++/modules/modules_support/gcc/compiler_support.lua
@@ -0,0 +1,225 @@
+--!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.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
+
+-- 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
+
+-- not supported atm
+function get_stdmodules(_)
+ return {}
+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")
+ if 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
+
diff --git a/xmake/rules/c++/modules/modules_support/gcc/dependency_scanner.lua b/xmake/rules/c++/modules/modules_support/gcc/dependency_scanner.lua
new file mode 100644
index 000000000..1e89f0244
--- /dev/null
+++ b/xmake/rules/c++/modules/modules_support/gcc/dependency_scanner.lua
@@ -0,0 +1,77 @@
+--!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_dependencies(target, sourcebatch, 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 changed = false
+ for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
+ local dependfile = target:dependfile(sourcefile)
+ depend.on_changed(function()
+ if opt.progress then
+ progress.show(opt.progress, "${color.build.target}<%s> generating.module.deps %s", target:name(), sourcefile)
+ end
+
+ local outputdir = compiler_support.get_outputdir(target, sourcefile)
+ local jsonfile = path.translate(path.join(outputdir, path.filename(sourcefile) .. ".json"))
+ if depsformatflag and depsfileflag and depstargetflag 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 = compinst:compflags({sourcefile = sourcefile, target = target})
+ local flags = table.join(compflags or {}, baselineflags, {sourcefile, "-MT", jsonfile, "-MD", "-MF", dfile, depsformatflag, depsfileflag .. jsonfile, depstargetflag .. target:objectfile(sourcefile), "-o", ifile})
+ os.vrunv(path.translate(compinst:program()), flags)
+ os.rm(ifile)
+ os.rm(dfile)
+ else
+ fallback_generate_dependencies(target, jsonfile, sourcefile, function(file)
+ local compinst = target:compiler("cxx")
+ local compflags = compinst:compflags({sourcefile = file, target = target})
+ -- 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"))
+ local flags = table.join(baselineflags, compflags or {}, {file, "-o", ifile})
+ os.vrunv(compinst:program(), flags)
+ 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()})
+ end
+ return changed
+end
+