diff options
Diffstat (limited to 'xmake/rules/c++/modules/modules_support/builder.lua')
| -rw-r--r-- | xmake/rules/c++/modules/modules_support/builder.lua | 618 |
1 files changed, 0 insertions, 618 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 - |
