summaryrefslogtreecommitdiff
path: root/xmake/rules/c++/modules/msvc
diff options
context:
space:
mode:
authorArthur LAURENT <[email protected]>2025-05-05 17:13:25 +0200
committerArthur LAURENT <[email protected]>2025-05-09 15:56:33 +0200
commit5d6d36e0db45d90dfa7f0cdf2937d989de683833 (patch)
tree2d1fbb82ee8d38921af1863fd3ce968f7c5cea75 /xmake/rules/c++/modules/msvc
parentd88bd9c5ace6c64981420569fb886f7c646135c0 (diff)
(C++ module support) improve jobgraph support
Diffstat (limited to 'xmake/rules/c++/modules/msvc')
-rw-r--r--xmake/rules/c++/modules/msvc/builder.lua472
-rw-r--r--xmake/rules/c++/modules/msvc/scanner.lua31
-rw-r--r--xmake/rules/c++/modules/msvc/support.lua87
3 files changed, 188 insertions, 402 deletions
diff --git a/xmake/rules/c++/modules/msvc/builder.lua b/xmake/rules/c++/modules/msvc/builder.lua
index ceb3aa4bc..eca44fb2d 100644
--- a/xmake/rules/c++/modules/msvc/builder.lua
+++ b/xmake/rules/c++/modules/msvc/builder.lua
@@ -29,78 +29,62 @@ import("core.project.config")
import("core.project.depend")
import("private.tools.vstool")
import("support")
+import(".mapper")
import(".builder", {inherit = true})
-- get flags for building a module
-function _make_modulebuildflags(target, provide, bmifile, opt)
+function _make_modulebuildflags(target, module, opt)
local ifcoutputflag = support.get_ifcoutputflag(target)
local ifconlyflag = support.get_ifconlyflag(target)
local interfaceflag = support.get_interfaceflag(target)
local internalpartitionflag = support.get_internalpartitionflag(target)
- local ifconly = (not opt.build_objectfile and ifconlyflag)
+
+ local bmionly = opt and opt.bmionly
local flags
- if provide then -- named module
- flags = table.join({"-TP", ifcoutputflag, path(bmifile), provide.interface and interfaceflag or internalpartitionflag}, ifconly or {})
+ if module.interface or module.implementation then -- named module
+ flags = table.join("-TP", module.interface and interfaceflag or internalpartitionflag, bmionly and ifconlyflag or {}, ifcoutputflag, path(module.bmifile))
else
flags = {"-TP"}
end
return flags
end
-function _compile_one_step(target, bmifile, sourcefile, objectfile, provide, opt)
- local ifcoutputflag = support.get_ifcoutputflag(target)
- local interfaceflag = support.get_interfaceflag(target)
- local internalpartitionflag = support.get_internalpartitionflag(target)
+
+function _compile_one_step(target, module, opt)
-- get flags
- local flags = {"-TP"}
- if provide then
- table.join2(flags, ifcoutputflag, path(bmifile), provide.interface and interfaceflag or internalpartitionflag)
- end
+ local flags = _make_modulebuildflags(target, module)
if opt and opt.batchcmds then
- _batchcmds_compile(opt.batchcmds, target, flags, sourcefile, objectfile)
+ _batchcmds_compile(opt.batchcmds, target, flags, module.sourcefile, module.objectfile)
else
- _compile(target, flags, sourcefile, objectfile)
+ _compile(target, flags, module.sourcefile, module.objectfile)
end
end
-function _compile_bmi_step(target, bmifile, sourcefile, objectfile, provide, opt)
- local ifcoutputflag = support.get_ifcoutputflag(target)
- local interfaceflag = support.get_interfaceflag(target)
+function _compile_bmi_step(target, module, opt)
local ifconlyflag = support.get_ifconlyflag(target)
-
if not ifconlyflag then
- _compile_one_step(target, bmifile, sourcefile, objectfile, provide, opt)
+ _compile_one_step(target, module, opt)
else
- local flags = {"-TP", ifcoutputflag, path(bmifile), interfaceflag, ifconlyflag}
+ local flags = _make_modulebuildflags(target, module, {bmionly = true})
if opt and opt.batchcmds then
- _batchcmds_compile(opt.batchcmds, target, flags, sourcefile, bmifile)
+ _batchcmds_compile(opt.batchcmds, target, flags, module.sourcefile, module.objectfile)
else
- _compile(target, flags, sourcefile, bmifile)
+ _compile(target, flags, module.sourcefile, module.objectfile)
end
end
end
-function _compile_objectfile_step(target, bmifile, sourcefile, objectfile, provide, opt)
- local ifconlyflag = support.get_ifconlyflag(target)
- local interfaceflag = support.get_interfaceflag(target)
- local internalpartitionflag = 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)
+function _compile_objectfile_step(target, module, opt)
+ local flags = _make_modulebuildflags(target, module)
+ if opt and opt.batchcmds then
+ _batchcmds_compile(opt.batchcmds, target, flags, module.sourcefile, module.objectfile)
else
- if opt and opt.batchcmds then
- _batchcmds_compile(opt.batchcmds, target, flags, sourcefile, objectfile)
- else
- _compile(target, flags, sourcefile, objectfile)
- end
+ _compile(target, flags, module.sourcefile, module.objectfile)
end
end
-
-- get flags for building a headerunit
-function _make_headerunitflags(target, headerunit, bmifile)
-
+function _make_headerunitflags(target, headerunit, headertype)
-- get flags
local exportheaderflag = support.get_exportheaderflag(target)
local headernameflag = support.get_headernameflag(target)
@@ -108,41 +92,34 @@ function _make_headerunitflags(target, headerunit, bmifile)
local ifconlyflag = 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 {})
+ local flags = {"-TP",
+ exportheaderflag,
+ ifcoutputflag,
+ headerunit.bmifile,
+ ifconlyflag or {},
+ headernameflag .. headertype} -- keep it at last flag
return flags
end
-- do compile
-function _compile(target, flags, sourcefile, outputfile, headerunit)
-
+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)
+ local compflags = compinst:compflags({sourcefile = sourcefile, target = target, sourcekind = "cxx"})
+ flags = table.join(compflags or {}, flags or {})
-- trace
if option.get("verbose") then
- if headerunit then
- print(os.args(compinst:program(), flags))
+ if not outputfile then
+ print(os.args(table.join(compinst:program(), flags, sourcefile)))
else
- print(compinst:compcmd(sourcefile, outputfile, {target = target, compflags = flags, rawargs = true}))
+ print(compinst:compcmd(sourcefile, outputfile, {target = target, compflags = flags, sourcekind = "cxx", 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
+ assert(compinst:compile(sourcefile, outputfile or target:objectfile(sourcefile), {target = target, compflags = flags}))
end
end
@@ -151,9 +128,9 @@ end
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})
+ local compflags = compinst:compflags({sourcefile = sourcefile, target = target, sourcekind = "cxx"})
+ flags = table.join("/c", compflags or {}, outputfile and "-Fo" .. outputfile or {}, flags or {}, sourcefile or {})
+ batchcmds:compilev(flags, {compiler = compinst, sourcekind = "cxx"})
end
-- get module requires flags
@@ -161,41 +138,40 @@ end
-- /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)
+function _get_requiresflags(target, module)
local referenceflag = support.get_referenceflag(target)
local headerunitflag = support.get_headerunitflag(target)
- local name = module.name
+ local name = module.name or module.sourcefile
local cachekey = target:fullname() .. name
local requires, requires_changed = is_dependencies_changed(target, module)
local requiresflags = 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)
+ for required, dep in pairs(module.deps) do
+ if dep.headerunit then
+ required = required .. dep.key
+ end
+ local dep_module = mapper.get(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
+ local mapflag
+ if dep_module.headerunit then
+ local type = dep_module.method == "include-angle" and ":angle" or ":quote"
+ mapflag = {headerunitflag .. type}
+ table.insert(deps_flags, {headerunitflag, dep_module.sourcefile .. "=" .. dep_module.bmifile})
else
- mapflag = {referenceflag, required .. "=" .. bmifile}
+ mapflag = {referenceflag}
end
+ table.insert(mapflag, dep_module.name .. "=" .. dep_module.bmifile)
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 })
+ if dep_module.deps then
+ local deps = _get_requiresflags(target, dep_module)
table.join2(deps_flags, deps)
end
end
@@ -203,6 +179,7 @@ function _get_requiresflags(target, module, opt)
-- remove duplicates
requiresflags = {}
local contains = {}
+ table.sort(deps_flags, function(a, b) return a[2] > b[2] end)
for _, map in ipairs(deps_flags) do
local name = map[2]:split("=")[1]
if name and not contains[name] then
@@ -216,297 +193,120 @@ function _get_requiresflags(target, module, opt)
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}})
+function _append_requires_flags(target, module)
+ local requiresflags = _get_requiresflags(target, module)
+ target:fileconfig_add(module.sourcefile, {force = {cxxflags = requiresflags}})
end
--- populate module map
-function populate_module_map(target, modules)
- for _, module in pairs(modules) do
- local name, provide, cppfile = support.get_provided_module(module)
- if provide then
- local bmifile = support.get_bmi_path(provide.bmi)
- add_module_to_target_mapper(target, name, cppfile, bmifile, {deps = module.requires})
+function append_requires_flags(target, built_modules)
+ -- append requires flags
+ for _, sourcefile in ipairs(built_modules) do
+ local module = mapper.get(target, sourcefile)
+ if module.deps then
+ _append_requires_flags(target, module)
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 / jobgraph
+function make_module_job(target, module, opt)
--- build module file for batchjobs
-function make_module_buildjobs(target, batchjobs, job_name, deps, opt)
- local name, provide, _ = support.get_provided_module(opt.module)
- local bmifile = provide and 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)
+ -- generate and append module mapper file
+ local build = should_build(target, module)
+ local bmi = opt and opt.bmi
+ local objectfile = opt and opt.objectfile
- local mapped_bmi
- if provide and support.memcache():get2(target:fullname() .. name, "reuse") then
- mapped_bmi = get_from_target_mapper(target, name).bmi
+ if build then
+ if not dryrun then
+ local objectdir = path.directory(module.objectfile)
+ if not os.isdir(objectdir) then
+ os.mkdir(objectdir)
end
-
- local build, dependinfo
- local dependfile = target:dependfile(bmifile or opt.objectfile)
- if provide or 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)
+ if module.bmifile then
+ local bmidir = path.directory(module.bmifile)
+ if not os.isdir(bmidir) then
+ os.mkdir(bmidir)
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 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, _ = support.get_provided_module(opt.module)
- local bmifile = provide and 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 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 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 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
+ if bmi and objectfile then
+ progress.show(opt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.module.$(mode) %s", target:fullname(), module.name)
+ _compile_one_step(target, module)
+ elseif bmi then
+ progress.show(opt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.module.bmi.$(mode) %s", target:fullname(), module.name)
+ _compile_bmi_step(target, module)
+ else
+ if module.interface or module.implementation then
+ progress.show(opt.progress, "compiling.$(mode) %s", module.sourcefile)
+ _compile_objectfile_step(target, module)
else
- os.tryrm(opt.objectfile) -- force rebuild for .cpp files
+ os.tryrm(module.objectfile) -- force rebuild for .cpp files
end
- depend.save(dependinfo, dependfile)
end
- end)
+ end
end
-- build module file for batchcmds
-function make_module_buildcmds(target, batchcmds, opt)
-
- local name, provide, _ = support.get_provided_module(opt.module)
- local bmifile = provide and support.get_bmi_path(provide.bmi)
+function make_module_buildcmds(target, batchcmds, module, opt)
- local mapped_bmi
- if provide and 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 build = should_build(target, module)
+ local bmi = opt and opt.bmi
+ local objectfile = opt and opt.objectfile
- -- 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 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
+ if build then
+ local objectdir = path.directory(module.objectfile)
+ batchcmds:mkdir(objectdir)
+ if module.bmifile then
+ local bmidir = path.directory(module.bmifile)
+ batchcmds:mkdir(bmidir)
+ end
+ if bmi and objectfile then
+ batchcmds:show_progress(opt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.module.$(mode) %s", target:fullname(), module.name)
+ _compile_one_step(target, module, {batchcmds = batchcmds})
+ elseif bmi then
+ batchcmds:show_progress(opt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.module.bmi.$(mode) %s", target:fullname(), module.name)
+ _compile_bmi_step(target, module, {batchcmds = batchcmds})
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})
+ if module.interface or module.implementation then
+ batchcmds:show_progress(opt.progress, "compiling.$(mode) %s", module.sourcefile)
+ _compile_objectfile_step(target, module, {batchcmds = batchcmds})
+ else
+ batchcmds:rm(module.objectfile) -- force rebuild for .cpp files
+ end
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
+ batchcmds:add_depfiles(module.sourcefile)
+ return os.mtime(module.objectfile)
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)
+-- build headerunit file for batchjobs / jobgraph
+function make_headerunit_job(target, headerunit, opt)
+ local build = should_build(target, headerunit)
+ if build then
+ local name = headerunit.unique and path.filename(headerunit.name) or headerunit.name
+ local headertype = (headerunit.method == "include-angle") and ":angle" or ":quote"
+ progress.show(opt.progress, "${color.build.target}<%s> ${clear}${color.build.object}compiling.headerunit.$(mode) %s", target:fullname(), name)
+ _compile(target, _make_headerunitflags(target, headerunit, headertype), (headertype == ":angle") and headerunit.name or headerunit.sourcefile)
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)
+function make_headerunit_buildcmds(target, batchcmds, headerunit, opt)
+ local compinst = compiler.load("cxx", {target = target})
+ local compflags = compinst:compflags({sourcefile = headerunit.sourcefile, target = target, sourcekind = "cxx"})
+ local depvalues = {compinst:program(), compflags}
- if opt.build then
- local name = headerunit.unique and headerunit.name or headerunit.path
+ local build = should_build(target, headerunit)
+ if build then
+ local name = headerunit.unique and path.filename(headerunit.name) or headerunit.name
+ local headertype = (headerunit.method == "include-angle") and ":angle" or ":quote"
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))
+ _batchcmds_compile(batchcmds, target, _make_headerunitflags(target, headerunit, headertype), (headertype == ":angle") and headerunit.name or headerunit.sourcefile)
+ batchcmds:add_depfiles(headerunit.sourcefile)
end
- batchcmds:add_depfiles(headerunit.path)
- return os.mtime(bmifile)
+ batchcmds:add_depvalues(depvalues)
end
diff --git a/xmake/rules/c++/modules/msvc/scanner.lua b/xmake/rules/c++/modules/msvc/scanner.lua
index 07342a55e..20cc29d69 100644
--- a/xmake/rules/c++/modules/msvc/scanner.lua
+++ b/xmake/rules/c++/modules/msvc/scanner.lua
@@ -28,33 +28,36 @@ import("support")
import("builder")
import(".scanner", {inherit = true})
--- generate dependency files
-function generate_dependency_for(target, sourcefile, opt)
+-- scan module dependencies
+function scan_dependency_for(target, sourcefile, opt)
+
local msvc = target:toolchain("msvc")
+ local compinst = target:compiler("cxx")
+ local changed = false
+ local dependfile = target:dependfile(sourcefile)
+ local compflags = compinst:compflags({sourcefile = sourcefile, target = target, sourcekind = "cxx"}) or {}
local scandependenciesflag = support.get_scandependenciesflag(target)
local ifcoutputflag = 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
local fallbackscanner = target:policy("build.c++.modules.fallbackscanner") or
target:policy("build.c++.modules.msvc.fallbackscanner") or
target:policy("build.c++.msvc.fallbackscanner")
depend.on_changed(function ()
- progress.show(opt.progress, "${color.build.target}<%s> generating.module.deps %s", target:fullname(), sourcefile)
- local outputdir = support.get_outputdir(target, sourcefile)
-
- local jsonfile = path.join(outputdir, path.filename(sourcefile) .. ".module.json")
+ if opt.progress and not os.getenv("XMAKE_IN_COMPILE_COMMANDS_PROJECT_GENERATOR") then
+ progress.show(opt.progress, "${color.build.target}<%s> generating.module.deps %s", target:fullname(), sourcefile)
+ end
+
+ local outputdir = support.get_outputdir(target, sourcefile, {scan = true})
+ local jsonfile = path.translate(path.join(outputdir, path.filename(sourcefile) .. ".module.json"))
if scandependenciesflag and not 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()})
+ local dependency_flags = table.join(compflags, common_flags, dependency_flags)
+ os.vrunv(compinst:program(), winos.cmdargv(dependency_flags), {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,
+ os.vrunv(compinst:program(), table.join(compflags,
{"/P", "-TP", file, "/Fi" .. ifile}), {envs = msvc:runenvs()})
local content = io.readfile(ifile)
os.rm(ifile)
@@ -65,7 +68,7 @@ function generate_dependency_for(target, sourcefile, opt)
local dependinfo = io.readfile(jsonfile)
return { moduleinfo = dependinfo }
- end, {dependfile = dependfile, files = {sourcefile}, changed = target:is_rebuilt(), values = flags})
+ end, {dependfile = dependfile, files = {sourcefile}, changed = target:is_rebuilt(), values = compflags})
return changed
end
diff --git a/xmake/rules/c++/modules/msvc/support.lua b/xmake/rules/c++/modules/msvc/support.lua
index 0da24501f..f8a6ca3c4 100644
--- a/xmake/rules/c++/modules/msvc/support.lua
+++ b/xmake/rules/c++/modules/msvc/support.lua
@@ -27,9 +27,6 @@ import(".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")
@@ -56,19 +53,18 @@ function load(target)
end
end
--- strip flags that doesn't affect bmi generation
-function strip_flags(target, flags)
+-- flags that doesn't affect bmi generation
+function strippeable_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",
+ local strippeable_flags = {
"TP",
"errorReport",
"W",
"w",
"sourceDependencies",
"scanDependencies",
- "reference",
"PD",
"nologo",
"MP",
@@ -76,10 +72,8 @@ function strip_flags(target, flags)
"interface",
"ifcOutput",
"help",
- "headerUnit",
"headerName",
"Fp",
- "Fo",
"Fm",
"Fe",
"Fd",
@@ -94,30 +88,18 @@ function strip_flags(target, flags)
"analyze",
"?",
}
- local strict = target:policy("build.c++.modules.reuse.strict") or
- target:policy("build.c++.modules.tryreuse.discriminate_on_defines")
- if not strict 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
+ local splitted_strippeable_flags = {
+ "Fo",
+ "I",
+ "reference",
+ "headerUnit",
+ }
+ return strippeable_flags, splitted_strippeable_flags
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")
@@ -130,20 +112,24 @@ function toolchain_includedirs(target)
raise("msvc toolchain includedirs not found!")
end
+function has_two_phase_compilation_support(_)
+ return false
+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 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 !")
+ local stdmodulesdir = path.join(vcvars.VCInstallDir, "Tools", "MSVC", vcvars.VCToolsVersion, "modules")
- return {path.join(stdmodulesdir, "std.ixx"), path.join(stdmodulesdir, "std.compat.ixx")}
+ if os.isdir(stdmodulesdir) then
+ return {path.normalize(path.join(stdmodulesdir, "std.ixx")), path.normalize(path.join(stdmodulesdir, "std.compat.ixx"))}
end
end
end
@@ -156,6 +142,7 @@ function get_bmi_extension()
end
function get_ifcoutputflag(target)
+
local ifcoutputflag = _g.ifcoutputflag
if ifcoutputflag == nil then
local compinst = target:compiler("cxx")
@@ -169,6 +156,7 @@ function get_ifcoutputflag(target)
end
function get_ifconlyflag(target)
+
local ifconlyflag = _g.ifconlyflag
if ifconlyflag == nil then
local compinst = target:compiler("cxx")
@@ -180,20 +168,8 @@ function get_ifconlyflag(target)
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")
@@ -207,6 +183,7 @@ function get_interfaceflag(target)
end
function get_referenceflag(target)
+
local referenceflag = _g.referenceflag
if referenceflag == nil then
local compinst = target:compiler("cxx")
@@ -220,6 +197,7 @@ function get_referenceflag(target)
end
function get_headernameflag(target)
+
local headernameflag = _g.headernameflag
if headernameflag == nil then
local compinst = target:compiler("cxx")
@@ -233,6 +211,7 @@ function get_headernameflag(target)
end
function get_headerunitflag(target)
+
local headerunitflag = _g.headerunitflag
if headerunitflag == nil then
local compinst = target:compiler("cxx")
@@ -247,6 +226,7 @@ function get_headerunitflag(target)
end
function get_exportheaderflag(target)
+
local exportheaderflag = _g.exportheaderflag
if exportheaderflag == nil then
if get_headernameflag(target) then
@@ -258,6 +238,7 @@ function get_exportheaderflag(target)
end
function get_scandependenciesflag(target)
+
local scandependenciesflag = _g.scandependenciesflag
if scandependenciesflag == nil then
local compinst = target:compiler("cxx")
@@ -280,6 +261,7 @@ function get_scandependenciesflag(target)
end
function get_cppversionflag(target)
+
local cppversionflag = _g.cppversionflag
if cppversionflag == nil then
local compinst = target:compiler("cxx")
@@ -290,6 +272,7 @@ function get_cppversionflag(target)
end
function get_internalpartitionflag(target)
+
local internalpartitionflag = _g.internalpartitionflag
if internalpartitionflag == nil then
local compinst = target:compiler("cxx")