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