diff options
| author | Arthur LAURENT <[email protected]> | 2025-05-05 17:13:25 +0200 |
|---|---|---|
| committer | Arthur LAURENT <[email protected]> | 2025-05-09 15:56:33 +0200 |
| commit | 5d6d36e0db45d90dfa7f0cdf2937d989de683833 (patch) | |
| tree | 2d1fbb82ee8d38921af1863fd3ce968f7c5cea75 /xmake/rules/c++/modules/msvc | |
| parent | d88bd9c5ace6c64981420569fb886f7c646135c0 (diff) | |
(C++ module support) improve jobgraph support
Diffstat (limited to 'xmake/rules/c++/modules/msvc')
| -rw-r--r-- | xmake/rules/c++/modules/msvc/builder.lua | 472 | ||||
| -rw-r--r-- | xmake/rules/c++/modules/msvc/scanner.lua | 31 | ||||
| -rw-r--r-- | xmake/rules/c++/modules/msvc/support.lua | 87 |
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") |
