From d52291f3389b2bf3f4b90e24c4670f41605c3cc8 Mon Sep 17 00:00:00 2001 From: Arthur LAURENT Date: Tue, 23 Jan 2024 18:23:27 +0100 Subject: refactor module common infrastructure --- .../rules/c++/modules/modules_support/builder.lua | 400 +++++++++++++++++++++ 1 file changed, 400 insertions(+) create mode 100644 xmake/rules/c++/modules/modules_support/builder.lua (limited to 'xmake/rules/c++/modules/modules_support/builder.lua') diff --git a/xmake/rules/c++/modules/modules_support/builder.lua b/xmake/rules/c++/modules/modules_support/builder.lua new file mode 100644 index 000000000..481415661 --- /dev/null +++ b/xmake/rules/c++/modules/modules_support/builder.lua @@ -0,0 +1,400 @@ +--!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("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 = dependency_scanner.sort_modules_by_dependencies(sourcebatch.objectfiles, modules) + + -- build modules + for _, objectfile in ipairs(objectfiles) do + local module = modules[objectfile] + if not module then + goto CONTINUE + end + + local name, provide, cppfile = compiler_support.get_provided_module(module) + cppfile = cppfile or module.cppfile + + local fileconfig = target:fileconfig(cppfile) + local bmifile = provide and compiler_support.get_bmi_path(provide.bmi) + local build = _should_build(target, cppfile, bmifile, objectfile, module.requires) + + -- add objectfile if module is not from external dep + if not (fileconfig and fileconfig.external) then + target:add("objectfiles", objectfile) + end + + -- needed to detect rebuild of dependencies + if provide then + compiler_support.memcache():set2(target:name(), name, build) + end + + local deps = {} + for _, dep in ipairs(table.keys(module.requires or {})) do + table.insert(deps, opt.batchjobs and target:name() .. dep or dep) + end + + opt.build_module(deps, build, module, name, provide, objectfile, cppfile, fileconfig) + + ::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, nil, nil, {key = key, headerunit = true}) + + compiler_support.memcache():set2(target:name(), key, build) + + opt.build_headerunit(headerunit, key, bmifile, outputdir, build) + end +end + +-- should we build this module or headerunit ? +function _should_build(target, sourcefile, bmifile, objectfile, requires, opt) + + -- force rebuild a module if any of its module dependency is rebuilt + if requires then + for required, _ in pairs(requires) do + local m = get_from_target_mapper(target, required) + if m then + local rebuild = compiler_support.memcache():get2(target:name(), m.key) + if rebuild then + return true + end + end + end + end + + -- or rebuild it if the file changed for headerunit and namedmodules + if compiler_support.has_module_extension(sourcefile) or (opt and opt.headerunit) then + local dryrun = option.get("dry-run") + 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 {}) + + -- need build this object? + local depvalues = {compinst:program(), compflags} + 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 + return true + end + 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 pairs(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:name() .. "module_mapper" .. (mode or "") +end + +function _is_duplicated_headerunit(target, headerunit) + local mapper = get_target_module_mapper(target) + local key = hash.md5(path.normalize(headerunit.path)) + + -- for _, mapped in pairs(mapper) do + -- print("CHECK", mapped.key, key) + -- if mapped.key == key then + -- return true + -- end + -- end + + return false +end + +-- add populate job +function _init_build_for(target, batch, modules, opt) + + if opt.batchjobs then + local job_name = get_modulemap_populate_jobname(target) + return { modulemap_populatejob_name = { + name = job_name, + job = batch:addjob(job_name, function(index, total) + progress.show((index * 100) / total, "${color.build.target}<%s> populating.%s.map", target:name(), opt.type) + _builder(target).populate_module_map(target, modules) + end)}} + else + batch:show_progress(opt.progress, "${color.build.target}<%s> populating.%s.map", target:name(), opt.type) + _builder(target).populate_module_map(target, modules) + end +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") 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 get_modulemap_populate_jobname(target) + return target:name() .. "_module_map_populate" +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:name() .. "/build_modules", {rootjob = opt.rootjob}) + + local modulesjobs = {} + + _build_modules(target, sourcebatch, modules, table.join(opt, { + build_module = function(deps, build, module, name, provide, objectfile, cppfile, fileconfig) + local job_name = name and target:name() .. name or cppfile + + modulesjobs[job_name] = _builder(target).make_module_build_job(target, batchjobs, job_name, deps, {build = build, module = module, objectfile = objectfile, cppfile = cppfile}) + + if provide and fileconfig and fileconfig.public then + batchjobs:addjob(name .. "_metafile", function(index, total) + local metafilepath = compiler_support.get_metafile(target, cppfile) + depend.on_changed(function() + progress.show((index * 100) / total, "${color.build.target}<%s> generating.module.metadata %s", target:name(), name) + local metadata = _generate_meta_module_info(target, name, cppfile, module.requires) + json.savefile(metafilepath, metadata) + end, {dependfile = target:dependfile(metafilepath), files = {cppfile}, changed = target:is_rebuilt()}) + end, {rootjob = opt.rootjob}) + end + end + })) + + local tailjob = _init_build_for(target, batchjobs, modules, table.join({type = "module"}, opt)) + table.join2(modulesjobs, tailjob) + + -- build batchjobs for modules + build_batchjobs_for_modules(modulesjobs, batchjobs, opt.rootjob) +end + +-- build modules for batchcmds +function build_modules_for_batchcmds(target, batchcmds, sourcebatch, modules, opt) + + local depmtime = 0 + opt.progress = opt.progress or 0 + _init_build_for(target, batchcmds, modules, table.join({type = "module"}, opt)) + + -- build modules + _build_modules(target, sourcebatch, modules, table.join(opt, { + build_module = function(_, build, module, name, provide, objectfile, cppfile, fileconfig) + depmtime = math.max(depmtime, _builder(target).make_module_build_cmds(target, batchcmds, {build = build, module = module, cppfile = cppfile, objectfile = objectfile, progress = opt.progress})) + + if provide and fileconfig and fileconfig.public then + local metafilepath = compiler_support.get_metafile(target, cppfile) + depend.on_changed(function() + progress.show(opt.progress, "${color.build.target}<%s> generating.module.metadata %s", target:name(), name) + local metadata = _generate_meta_module_info(target, name, cppfile, module.requires) + json.savefile(metafilepath, metadata) + end, {dependfile = target:dependfile(metafilepath), files = {cppfile}, changed = target:is_rebuilt()}) + end + end + })) + + batchcmds:set_depmtime(depmtime) +end + +-- generate 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:name() .. "/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:name() .. key + local job = _builder(target).make_headerunit_build_job(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 + +-- generate 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_build_cmds(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 + +-- append headerunits objectfiles to link +function append_dependency_objectfiles(target) + + local cachekey = target:name() .. "dependency_objectfiles" + local cache = compiler_support.localcache():get(cachekey) + if cache then + if target:is_binary() then + target:add("ldflags", cache, {force = true, expand = false}) + elseif target:is_static() then + target:add("arflags", cache, {force = true, expand = false}) + elseif target:is_shared() then + target:add("shflags", cache, {force = true, expand = false}) + end + end +end + +-- get or create a target module mapper +function get_target_module_mapper(target) + + opt = opt or {} + local memcache = compiler_support.memcache() + local mapper = memcache:get2(target:name(), "module_mapper") + if not mapper then + mapper = {} + memcache:set2(target:name(), "module_mapper", mapper) + end + + return mapper +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) + mapper[name] = {name = name, key = name, bmi = bmifile, sourcefile = sourcefile, opt = opt} +end + +-- add a headerunit to target mapper +function add_headerunit_to_target_mapper(target, headerunit, bmifile) + local mapper = get_target_module_mapper(target) + mapper[headerunit.name] = {name = headerunit.name, key = path.normalize(headerunit.path), headerunit = headerunit, bmi = bmifile} + return _is_duplicated_headerunit(target, headerunit) +end + -- cgit v1.3.1 From d50a83fbfcd59db2a8a5fc0f8fe14770f9a0d16b Mon Sep 17 00:00:00 2001 From: Arthur LAURENT Date: Mon, 29 Jan 2024 12:18:01 +0100 Subject: apply PR suggestions --- xmake/rules/c++/modules/modules_support/builder.lua | 13 +++++++++++-- .../c++/modules/modules_support/clang/compiler_support.lua | 6 +++--- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'xmake/rules/c++/modules/modules_support/builder.lua') diff --git a/xmake/rules/c++/modules/modules_support/builder.lua b/xmake/rules/c++/modules/modules_support/builder.lua index 481415661..4592e547d 100644 --- a/xmake/rules/c++/modules/modules_support/builder.lua +++ b/xmake/rules/c++/modules/modules_support/builder.lua @@ -33,6 +33,8 @@ import("dependency_scanner") function _build_modules(target, sourcebatch, modules, opt) local objectfiles = dependency_scanner.sort_modules_by_dependencies(sourcebatch.objectfiles, modules) + compiler_support.memcache():set2(target:name(), "need_to_repopulate", false) + -- build modules for _, objectfile in ipairs(objectfiles) do local module = modules[objectfile] @@ -62,6 +64,9 @@ function _build_modules(target, sourcebatch, modules, opt) table.insert(deps, opt.batchjobs and target:name() .. dep or dep) end + if build then + compiler_support.memcache():set2(target:name(), "need_to_repopulate", true) + end opt.build_module(deps, build, module, name, provide, objectfile, cppfile, fileconfig) ::CONTINUE:: @@ -183,11 +188,15 @@ function _init_build_for(target, batch, modules, opt) return { modulemap_populatejob_name = { name = job_name, job = batch:addjob(job_name, function(index, total) - progress.show((index * 100) / total, "${color.build.target}<%s> populating.%s.map", target:name(), opt.type) + if compiler_support.memcache():get2(target:name(), "need_to_repopulate") then + progress.show((index * 100) / total, "${color.build.target}<%s> populating.%s.map", target:name(), opt.type) + end _builder(target).populate_module_map(target, modules) end)}} else - batch:show_progress(opt.progress, "${color.build.target}<%s> populating.%s.map", target:name(), opt.type) + if compiler_support.memcache():get2(target:name(), "need_to_repopulate") then + batch:show_progress(opt.progress, "${color.build.target}<%s> populating.%s.map", target:name(), opt.type) + end _builder(target).populate_module_map(target, modules) end 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 index 95578acee..6e4b36645 100644 --- a/xmake/rules/c++/modules/modules_support/clang/compiler_support.lua +++ b/xmake/rules/c++/modules/modules_support/clang/compiler_support.lua @@ -60,11 +60,11 @@ function get_cpplibrary_name(target) local runtime = target:runtimes() if not runtime then - if is_plat("windows") then + if target:is_plat("windows") then runtime = "msstl" - elseif is_plat("linux") or is_plat("android") then + elseif target:is_plat("linux") or target:is_plat("android") then runtime = "stdc++_shared" - elseif is_plat("macosx") or is_plat("iphoneos") or is_plat("watchos") then + elseif target:is_plat("macosx") or target:is_plat("iphoneos") or target:is_plat("watchos") then runtime = "c++_shared" end end -- cgit v1.3.1 From 952b8132bc699310d9f2750536079e81603ffe56 Mon Sep 17 00:00:00 2001 From: Arthur LAURENT Date: Mon, 29 Jan 2024 17:43:09 +0100 Subject: populate module map before generating batchjobs --- .../rules/c++/modules/modules_support/builder.lua | 34 +--------------------- .../c++/modules/modules_support/clang/builder.lua | 3 +- .../c++/modules/modules_support/gcc/builder.lua | 3 +- .../c++/modules/modules_support/msvc/builder.lua | 3 +- 4 files changed, 4 insertions(+), 39 deletions(-) (limited to 'xmake/rules/c++/modules/modules_support/builder.lua') diff --git a/xmake/rules/c++/modules/modules_support/builder.lua b/xmake/rules/c++/modules/modules_support/builder.lua index 4592e547d..099c0993e 100644 --- a/xmake/rules/c++/modules/modules_support/builder.lua +++ b/xmake/rules/c++/modules/modules_support/builder.lua @@ -33,7 +33,7 @@ import("dependency_scanner") function _build_modules(target, sourcebatch, modules, opt) local objectfiles = dependency_scanner.sort_modules_by_dependencies(sourcebatch.objectfiles, modules) - compiler_support.memcache():set2(target:name(), "need_to_repopulate", false) + _builder(target).populate_module_map(target, modules) -- build modules for _, objectfile in ipairs(objectfiles) do @@ -64,9 +64,6 @@ function _build_modules(target, sourcebatch, modules, opt) table.insert(deps, opt.batchjobs and target:name() .. dep or dep) end - if build then - compiler_support.memcache():set2(target:name(), "need_to_repopulate", true) - end opt.build_module(deps, build, module, name, provide, objectfile, cppfile, fileconfig) ::CONTINUE:: @@ -180,27 +177,6 @@ function _is_duplicated_headerunit(target, headerunit) return false end --- add populate job -function _init_build_for(target, batch, modules, opt) - - if opt.batchjobs then - local job_name = get_modulemap_populate_jobname(target) - return { modulemap_populatejob_name = { - name = job_name, - job = batch:addjob(job_name, function(index, total) - if compiler_support.memcache():get2(target:name(), "need_to_repopulate") then - progress.show((index * 100) / total, "${color.build.target}<%s> populating.%s.map", target:name(), opt.type) - end - _builder(target).populate_module_map(target, modules) - end)}} - else - if compiler_support.memcache():get2(target:name(), "need_to_repopulate") then - batch:show_progress(opt.progress, "${color.build.target}<%s> populating.%s.map", target:name(), opt.type) - end - _builder(target).populate_module_map(target, modules) - end -end - function _builder(target) local cachekey = tostring(target) @@ -221,10 +197,6 @@ function _builder(target) return builder end -function get_modulemap_populate_jobname(target) - return target:name() .. "_module_map_populate" -end - -- build batchjobs for modules function build_batchjobs_for_modules(modules, batchjobs, rootjob) return buildjobs(modules, batchjobs, rootjob) @@ -257,9 +229,6 @@ function build_modules_for_batchjobs(target, batchjobs, sourcebatch, modules, op end })) - local tailjob = _init_build_for(target, batchjobs, modules, table.join({type = "module"}, opt)) - table.join2(modulesjobs, tailjob) - -- build batchjobs for modules build_batchjobs_for_modules(modulesjobs, batchjobs, opt.rootjob) end @@ -269,7 +238,6 @@ function build_modules_for_batchcmds(target, batchcmds, sourcebatch, modules, op local depmtime = 0 opt.progress = opt.progress or 0 - _init_build_for(target, batchcmds, modules, table.join({type = "module"}, opt)) -- build modules _build_modules(target, sourcebatch, modules, table.join(opt, { diff --git a/xmake/rules/c++/modules/modules_support/clang/builder.lua b/xmake/rules/c++/modules/modules_support/clang/builder.lua index 0e51bd93e..947a03bfc 100644 --- a/xmake/rules/c++/modules/modules_support/clang/builder.lua +++ b/xmake/rules/c++/modules/modules_support/clang/builder.lua @@ -201,11 +201,10 @@ function make_module_build_job(target, batchjobs, job_name, deps, opt) local name, provide, _ = compiler_support.get_provided_module(opt.module) local bmifile = provide and compiler_support.get_bmi_path(provide.bmi) local dryrun = option.get("dry-run") - local populate_job_name = get_modulemap_populate_jobname(target) return { name = job_name, - deps = table.join({populate_job_name}, deps), + deps = deps, sourcefile = cppfile, job = batchjobs:newjob(name or opt.cppfile, function(index, total) diff --git a/xmake/rules/c++/modules/modules_support/gcc/builder.lua b/xmake/rules/c++/modules/modules_support/gcc/builder.lua index 6e76a7646..5776b7010 100644 --- a/xmake/rules/c++/modules/modules_support/gcc/builder.lua +++ b/xmake/rules/c++/modules/modules_support/gcc/builder.lua @@ -235,11 +235,10 @@ function make_module_build_job(target, batchjobs, job_name, deps, opt) local name, provide, _ = compiler_support.get_provided_module(opt.module) local bmifile = provide and compiler_support.get_bmi_path(provide.bmi) local module_mapperflag = compiler_support.get_modulemapperflag(target) - local populate_job_name = get_modulemap_populate_jobname(target) return { name = job_name, - deps = table.join({populate_job_name}, deps), + deps = deps, sourcefile = opt.cppfile, job = batchjobs:newjob(name or opt.cppfile, function(index, total) diff --git a/xmake/rules/c++/modules/modules_support/msvc/builder.lua b/xmake/rules/c++/modules/modules_support/msvc/builder.lua index 0f4f9b735..3b295ca6b 100644 --- a/xmake/rules/c++/modules/modules_support/msvc/builder.lua +++ b/xmake/rules/c++/modules/modules_support/msvc/builder.lua @@ -197,11 +197,10 @@ function make_module_build_job(target, batchjobs, job_name, deps, opt) local name, provide, _ = compiler_support.get_provided_module(opt.module) local bmifile = provide and compiler_support.get_bmi_path(provide.bmi) local dryrun = option.get("dry-run") - local populate_job_name = get_modulemap_populate_jobname(target) return { name = job_name, - deps = table.join({populate_job_name}, deps), + deps = deps, sourcefile = opt.cppfile, job = batchjobs:newjob(name or opt.cppfile, function(index, total) -- cgit v1.3.1 From cc88697bbfe2925c6e290f400974da8c4c2f0e2c Mon Sep 17 00:00:00 2001 From: Arthur LAURENT Date: Wed, 31 Jan 2024 12:20:23 +0100 Subject: remove dead code --- xmake/rules/c++/modules/modules_support/builder.lua | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'xmake/rules/c++/modules/modules_support/builder.lua') diff --git a/xmake/rules/c++/modules/modules_support/builder.lua b/xmake/rules/c++/modules/modules_support/builder.lua index 099c0993e..e0885a2aa 100644 --- a/xmake/rules/c++/modules/modules_support/builder.lua +++ b/xmake/rules/c++/modules/modules_support/builder.lua @@ -324,22 +324,6 @@ function build_headerunits_for_batchcmds(target, batchcmds, sourcebatch, modules end end --- append headerunits objectfiles to link -function append_dependency_objectfiles(target) - - local cachekey = target:name() .. "dependency_objectfiles" - local cache = compiler_support.localcache():get(cachekey) - if cache then - if target:is_binary() then - target:add("ldflags", cache, {force = true, expand = false}) - elseif target:is_static() then - target:add("arflags", cache, {force = true, expand = false}) - elseif target:is_shared() then - target:add("shflags", cache, {force = true, expand = false}) - end - end -end - -- get or create a target module mapper function get_target_module_mapper(target) -- cgit v1.3.1 From 95996d7b59b420f9bc6b9f8e817e48d6be183acc Mon Sep 17 00:00:00 2001 From: Arthur LAURENT Date: Wed, 31 Jan 2024 12:29:37 +0100 Subject: improve _should_build --- .../rules/c++/modules/modules_support/builder.lua | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'xmake/rules/c++/modules/modules_support/builder.lua') diff --git a/xmake/rules/c++/modules/modules_support/builder.lua b/xmake/rules/c++/modules/modules_support/builder.lua index e0885a2aa..b17bb73b3 100644 --- a/xmake/rules/c++/modules/modules_support/builder.lua +++ b/xmake/rules/c++/modules/modules_support/builder.lua @@ -47,7 +47,7 @@ function _build_modules(target, sourcebatch, modules, opt) local fileconfig = target:fileconfig(cppfile) local bmifile = provide and compiler_support.get_bmi_path(provide.bmi) - local build = _should_build(target, cppfile, bmifile, objectfile, module.requires) + local build = _should_build(target, cppfile, bmifile, {objectfile = objectfile, requires = module.requires}) -- add objectfile if module is not from external dep if not (fileconfig and fileconfig.external) then @@ -55,8 +55,8 @@ function _build_modules(target, sourcebatch, modules, opt) end -- needed to detect rebuild of dependencies - if provide then - compiler_support.memcache():set2(target:name(), name, build) + if provide and build then + _mark_build(target, name) end local deps = {} @@ -85,23 +85,26 @@ function _build_headerunits(target, headerunits, opt) 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, nil, nil, {key = key, headerunit = true}) + local build = _should_build(target, headerunit.path, bmifile, {key = key, headerunit = true}) - compiler_support.memcache():set2(target:name(), key, build) + if build then + _mark_build(target, key) + end opt.build_headerunit(headerunit, key, bmifile, outputdir, build) end end -- should we build this module or headerunit ? -function _should_build(target, sourcefile, bmifile, objectfile, requires, opt) +function _should_build(target, sourcefile, bmifile, opt) -- force rebuild a module if any of its module dependency is rebuilt + local requires = opt.requires if requires then for required, _ in pairs(requires) do local m = get_from_target_mapper(target, required) if m then - local rebuild = compiler_support.memcache():get2(target:name(), m.key) + local rebuild = compiler_support.memcache():get2("should_build_in" .. target:name(), m.key) if rebuild then return true end @@ -110,6 +113,7 @@ function _should_build(target, sourcefile, bmifile, objectfile, requires, opt) end -- or rebuild it if the file changed for headerunit and namedmodules + local objectfile = opt.objectfile if compiler_support.has_module_extension(sourcefile) or (opt and opt.headerunit) then local dryrun = option.get("dry-run") local compinst = compiler.load("cxx", {target = target}) @@ -197,6 +201,10 @@ function _builder(target) return builder end +function _mark_build(target, name) + compiler_support.memcache():set2("should_build_in" .. target:name(), name, true) +end + -- build batchjobs for modules function build_batchjobs_for_modules(modules, batchjobs, rootjob) return buildjobs(modules, batchjobs, rootjob) -- cgit v1.3.1 From f3ec9cc0f7cf6d0b48dce8a69c5f6103b9dd6df3 Mon Sep 17 00:00:00 2001 From: Arthur LAURENT Date: Wed, 31 Jan 2024 13:01:40 +0100 Subject: implement missing headerunit aliasing --- .../rules/c++/modules/modules_support/builder.lua | 27 ++++++----- .../c++/modules/modules_support/clang/builder.lua | 56 +++++++++++++--------- .../c++/modules/modules_support/gcc/builder.lua | 19 +++++++- .../c++/modules/modules_support/msvc/builder.lua | 18 ++++++- 4 files changed, 80 insertions(+), 40 deletions(-) (limited to 'xmake/rules/c++/modules/modules_support/builder.lua') diff --git a/xmake/rules/c++/modules/modules_support/builder.lua b/xmake/rules/c++/modules/modules_support/builder.lua index b17bb73b3..5c2ec113f 100644 --- a/xmake/rules/c++/modules/modules_support/builder.lua +++ b/xmake/rules/c++/modules/modules_support/builder.lua @@ -167,18 +167,13 @@ function _target_module_map_cachekey(target) return target:name() .. "module_mapper" .. (mode or "") end -function _is_duplicated_headerunit(target, headerunit) +function _is_duplicated_headerunit(target, key) local mapper = get_target_module_mapper(target) - local key = hash.md5(path.normalize(headerunit.path)) - - -- for _, mapped in pairs(mapper) do - -- print("CHECK", mapped.key, key) - -- if mapped.key == key then - -- return true - -- end - -- end - - return false + for _, mapped in pairs(mapper) do + if mapped.key == key then + return mapped + end + end end function _builder(target) @@ -363,7 +358,13 @@ end -- add a headerunit to target mapper function add_headerunit_to_target_mapper(target, headerunit, bmifile) local mapper = get_target_module_mapper(target) - mapper[headerunit.name] = {name = headerunit.name, key = path.normalize(headerunit.path), headerunit = headerunit, bmi = bmifile} - return _is_duplicated_headerunit(target, headerunit) + 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} + else + mapper[headerunit.name] = {name = headerunit.name, key = key, headerunit = headerunit, bmi = bmifile} + end + return deduplicated and true or false end diff --git a/xmake/rules/c++/modules/modules_support/clang/builder.lua b/xmake/rules/c++/modules/modules_support/clang/builder.lua index 1b414215f..6a2893ea9 100644 --- a/xmake/rules/c++/modules/modules_support/clang/builder.lua +++ b/xmake/rules/c++/modules/modules_support/clang/builder.lua @@ -134,7 +134,15 @@ function _get_requiresflags(target, module, opt) assert(dep_module, "module dependency %s required for %s not found", required, name) - local bmifile = dep_module.bmi + local bmifile + -- aliased headerunit + if dep_module.aliasof then + local aliased = get_from_target_mapper(target, dep_module.aliasof) + bmifile = aliased.bmi + -- named module or headerunit + else + bmifile = dep_module.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) @@ -286,32 +294,34 @@ end -- build headerunit file for batchjobs function make_headerunit_build_job(target, job_name, batchjobs, headerunit, bmifile, outputdir, opt) - add_headerunit_to_target_mapper(target, headerunit, bmifile) - return { - name = job_name, - sourcefile = headerunit.path, - job = batchjobs:newjob(job_name, function(index, total) - if not os.isdir(outputdir) then - os.mkdir(outputdir) - end + local already_exists = add_headerunit_to_target_mapper(target, headerunit, bmifile) + if not already_exists then + return { + name = job_name, + sourcefile = headerunit.path, + job = batchjobs:newjob(job_name, function(index, total) + if not os.isdir(outputdir) then + os.mkdir(outputdir) + end - local compinst = compiler.load("cxx", {target = target}) - local compflags = compinst:compflags({sourcefile = headerunit.path, target = target}) + local 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 dependfile = target:dependfile(bmifile) + local dependinfo = depend.load(dependfile) or {} + dependinfo.files = {} + local depvalues = {compinst:program(), compflags} - if opt.build then - progress.show((index * 100) / total, "${color.build.target}<%s> ${clear}${color.build.object}compiling.headerunit.$(mode) %s", target:name(), headerunit.name) - _compile(target, _make_headerunitflags(target, headerunit, bmifile), headerunit.path) - end + if opt.build then + progress.show((index * 100) / total, "${color.build.target}<%s> ${clear}${color.build.object}compiling.headerunit.$(mode) %s", target:name(), headerunit.name) + _compile(target, _make_headerunitflags(target, headerunit, bmifile), headerunit.path) + end - table.insert(dependinfo.files, headerunit.path) - dependinfo.values = depvalues - depend.save(dependinfo, dependfile) - end)} + table.insert(dependinfo.files, headerunit.path) + dependinfo.values = depvalues + depend.save(dependinfo, dependfile) + end)} + end end -- build headerunit file for batchcmds diff --git a/xmake/rules/c++/modules/modules_support/gcc/builder.lua b/xmake/rules/c++/modules/modules_support/gcc/builder.lua index 13755660d..19f5e813b 100644 --- a/xmake/rules/c++/modules/modules_support/gcc/builder.lua +++ b/xmake/rules/c++/modules/modules_support/gcc/builder.lua @@ -151,12 +151,27 @@ function _get_maplines(target, module) assert(dep_module, "module dependency %s required for %s not found", required, name) - local mapline = (dep_module.headerunit and dep_module.headerunit.path:replace("\\", "/") or required) .. " " .. dep_module.bmi:replace("\\", "/") + local bmifile + local mapline + -- aliased headerunit + if dep_module.aliasof then + local aliased = get_from_target_mapper(target, dep_module.aliasof) + bmifile = aliased.bmi + mapline = dep_module.headerunit.path:replace("\\", "/") .. " " bmifile:replace("\\", "/") + -- headerunit + elseif dep_module.headerunit then + bmifile = dep_module.bmi + mapline = dep_module.headerunit.path:replace("\\", "/") .. " " bmifile:replace("\\", "/") + -- named module + else + bmifile = dep_module.bmi + mapline = required .. " " .. bmifile:replace("\\", "/") + end table.insert(maplines, mapline) -- append deps if dep_module.opt and dep_module.opt.deps then - local deps = _get_maplines(dep_target, { name = dep_module.name, bmi = dep_module.bmifile, requires = dep_module.opt.deps }) + local deps = _get_maplines(dep_target, { name = dep_module.name, bmi = bmifile, requires = dep_module.opt.deps }) table.join2(maplines, deps) end end diff --git a/xmake/rules/c++/modules/modules_support/msvc/builder.lua b/xmake/rules/c++/modules/modules_support/msvc/builder.lua index 8a77a7612..9b7bd330e 100644 --- a/xmake/rules/c++/modules/modules_support/msvc/builder.lua +++ b/xmake/rules/c++/modules/modules_support/msvc/builder.lua @@ -122,8 +122,22 @@ function _get_requiresflags(target, module, opt) assert(dep_module, "module dependency %s required for %s not found <%s>", required, name, target:name()) - local bmifile = dep_module.bmi - local mapflag = {dep_module.headerunit and headerunitflag .. dep_module.headerunit.type or referenceflag, required .. "=" .. bmifile} + local mapflag + local bmifile + -- 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 + bmifile = dep_module.bmi + mapflag = {headerunitflag .. dep_module.headerunit.type, required .. "=" .. bmifile} + -- named module + else + bmifile = dep_module.bmi + mapflag = {referenceflag, required .. "=" .. bmifile} + end table.insert(deps_flags, mapflag) -- append deps -- cgit v1.3.1 From 89f151ab335ad2aa1e5d1cc900f1e3837caa6437 Mon Sep 17 00:00:00 2001 From: Arthur LAURENT Date: Wed, 31 Jan 2024 13:02:11 +0100 Subject: add a test for aliased headerunits --- .../c++/modules/aliased_headerunit/src/foo/hello.mpp | 12 ++++++++++++ tests/projects/c++/modules/aliased_headerunit/src/header.hpp | 5 +++++ tests/projects/c++/modules/aliased_headerunit/src/main.cpp | 7 +++++++ tests/projects/c++/modules/aliased_headerunit/test.lua | 1 + tests/projects/c++/modules/aliased_headerunit/xmake.lua | 8 ++++++++ xmake/rules/c++/modules/modules_support/builder.lua | 2 +- xmake/rules/c++/modules/modules_support/clang/builder.lua | 5 +---- xmake/rules/c++/modules/modules_support/gcc/builder.lua | 12 +++++------- xmake/rules/c++/modules/modules_support/msvc/builder.lua | 4 +--- 9 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 tests/projects/c++/modules/aliased_headerunit/src/foo/hello.mpp create mode 100644 tests/projects/c++/modules/aliased_headerunit/src/header.hpp create mode 100644 tests/projects/c++/modules/aliased_headerunit/src/main.cpp create mode 100644 tests/projects/c++/modules/aliased_headerunit/test.lua create mode 100644 tests/projects/c++/modules/aliased_headerunit/xmake.lua (limited to 'xmake/rules/c++/modules/modules_support/builder.lua') diff --git a/tests/projects/c++/modules/aliased_headerunit/src/foo/hello.mpp b/tests/projects/c++/modules/aliased_headerunit/src/foo/hello.mpp new file mode 100644 index 000000000..0a8de6291 --- /dev/null +++ b/tests/projects/c++/modules/aliased_headerunit/src/foo/hello.mpp @@ -0,0 +1,12 @@ +module; +#include + +export module hello; + +import "../header.hpp"; + +export namespace hello { + void say(const char *arg) { + printf("%s: %s\n", FOO, arg); + } +} diff --git a/tests/projects/c++/modules/aliased_headerunit/src/header.hpp b/tests/projects/c++/modules/aliased_headerunit/src/header.hpp new file mode 100644 index 000000000..ab02a6792 --- /dev/null +++ b/tests/projects/c++/modules/aliased_headerunit/src/header.hpp @@ -0,0 +1,5 @@ +#pragma once + +namespace hello { + inline constexpr auto FOO = "Hello"; +} diff --git a/tests/projects/c++/modules/aliased_headerunit/src/main.cpp b/tests/projects/c++/modules/aliased_headerunit/src/main.cpp new file mode 100644 index 000000000..ad786367e --- /dev/null +++ b/tests/projects/c++/modules/aliased_headerunit/src/main.cpp @@ -0,0 +1,7 @@ +import hello; +import "header.hpp"; + +int main() { + hello::say(hello::FOO); + return 0; +} diff --git a/tests/projects/c++/modules/aliased_headerunit/test.lua b/tests/projects/c++/modules/aliased_headerunit/test.lua new file mode 100644 index 000000000..c18e5a1d0 --- /dev/null +++ b/tests/projects/c++/modules/aliased_headerunit/test.lua @@ -0,0 +1 @@ +inherit(".test_headerunits") diff --git a/tests/projects/c++/modules/aliased_headerunit/xmake.lua b/tests/projects/c++/modules/aliased_headerunit/xmake.lua new file mode 100644 index 000000000..5b4827d09 --- /dev/null +++ b/tests/projects/c++/modules/aliased_headerunit/xmake.lua @@ -0,0 +1,8 @@ +add_rules("mode.release", "mode.debug") +set_languages("c++20") + +-- header.hpp should be built only one time +target("aliased_headerunit") + set_kind("binary") + add_headerfiles("src/*.hpp") + add_files("src/*.cpp", "src/foo/*.mpp") diff --git a/xmake/rules/c++/modules/modules_support/builder.lua b/xmake/rules/c++/modules/modules_support/builder.lua index 5c2ec113f..db986b4ff 100644 --- a/xmake/rules/c++/modules/modules_support/builder.lua +++ b/xmake/rules/c++/modules/modules_support/builder.lua @@ -361,7 +361,7 @@ function add_headerunit_to_target_mapper(target, headerunit, bmifile) 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} + 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 diff --git a/xmake/rules/c++/modules/modules_support/clang/builder.lua b/xmake/rules/c++/modules/modules_support/clang/builder.lua index 6a2893ea9..2c17318d4 100644 --- a/xmake/rules/c++/modules/modules_support/clang/builder.lua +++ b/xmake/rules/c++/modules/modules_support/clang/builder.lua @@ -134,14 +134,11 @@ function _get_requiresflags(target, module, opt) assert(dep_module, "module dependency %s required for %s not found", required, name) - local bmifile + 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 - -- named module or headerunit - else - bmifile = dep_module.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) diff --git a/xmake/rules/c++/modules/modules_support/gcc/builder.lua b/xmake/rules/c++/modules/modules_support/gcc/builder.lua index 19f5e813b..95be560d9 100644 --- a/xmake/rules/c++/modules/modules_support/gcc/builder.lua +++ b/xmake/rules/c++/modules/modules_support/gcc/builder.lua @@ -151,20 +151,18 @@ function _get_maplines(target, module) assert(dep_module, "module dependency %s required for %s not found", required, name) - local bmifile + 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 = dep_module.headerunit.path:replace("\\", "/") .. " " bmifile:replace("\\", "/") + mapline = dep_module.headerunit.path:replace("\\", "/") .. " " .. bmifile:replace("\\", "/") -- headerunit elseif dep_module.headerunit then - bmifile = dep_module.bmi - mapline = dep_module.headerunit.path:replace("\\", "/") .. " " bmifile:replace("\\", "/") + mapline = dep_module.headerunit.path:replace("\\", "/") .. " " .. bmifile:replace("\\", "/") -- named module else - bmifile = dep_module.bmi mapline = required .. " " .. bmifile:replace("\\", "/") end table.insert(maplines, mapline) @@ -214,7 +212,7 @@ function populate_module_map(target, modules) for _, module in pairs(modules) do local name, provide = compiler_support.get_provided_module(module) if provide then - add_module_to_target_mapper(target, name, provide.sourcefile, path.absolute(compiler_support.get_bmi_path(provide.bmi), projectdir)) + add_module_to_target_mapper(target, name, provide.sourcefile, compiler_support.get_bmi_path(provide.bmi)) end end @@ -223,7 +221,7 @@ function populate_module_map(target, modules) local name, provide = compiler_support.get_provided_module(module) if provide then local bmifile = compiler_support.get_bmi_path(provide.bmi) - add_module_to_target_mapper(target, name, provide.sourcefile, path.absolute(bmifile, projectdir), {deps = module.requires}) + add_module_to_target_mapper(target, name, provide.sourcefile, bmifile, {deps = module.requires}) end end end diff --git a/xmake/rules/c++/modules/modules_support/msvc/builder.lua b/xmake/rules/c++/modules/modules_support/msvc/builder.lua index 9b7bd330e..00326143e 100644 --- a/xmake/rules/c++/modules/modules_support/msvc/builder.lua +++ b/xmake/rules/c++/modules/modules_support/msvc/builder.lua @@ -123,7 +123,7 @@ function _get_requiresflags(target, module, opt) assert(dep_module, "module dependency %s required for %s not found <%s>", required, name, target:name()) local mapflag - local bmifile + local bmifile = dep_module.bmi -- aliased headerunit if dep_module.aliasof then local aliased = get_from_target_mapper(target, dep_module.aliasof) @@ -131,11 +131,9 @@ function _get_requiresflags(target, module, opt) mapflag = {headerunitflag .. aliased.headerunit.type, required .. "=" .. bmifile} -- headerunit elseif dep_module.headerunit then - bmifile = dep_module.bmi mapflag = {headerunitflag .. dep_module.headerunit.type, required .. "=" .. bmifile} -- named module else - bmifile = dep_module.bmi mapflag = {referenceflag, required .. "=" .. bmifile} end table.insert(deps_flags, mapflag) -- cgit v1.3.1 From 15f2678a507c933f1f1456a496cb31e0373699f2 Mon Sep 17 00:00:00 2001 From: Arthur LAURENT Date: Wed, 31 Jan 2024 14:15:04 +0100 Subject: use table.orderpair to ensure dependency order stay the same --- xmake/rules/c++/modules/modules_support/builder.lua | 2 +- xmake/rules/c++/modules/modules_support/gcc/builder.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'xmake/rules/c++/modules/modules_support/builder.lua') diff --git a/xmake/rules/c++/modules/modules_support/builder.lua b/xmake/rules/c++/modules/modules_support/builder.lua index db986b4ff..9f695cede 100644 --- a/xmake/rules/c++/modules/modules_support/builder.lua +++ b/xmake/rules/c++/modules/modules_support/builder.lua @@ -101,7 +101,7 @@ function _should_build(target, sourcefile, bmifile, opt) -- force rebuild a module if any of its module dependency is rebuilt local requires = opt.requires if requires then - for required, _ in pairs(requires) do + for required, _ in table.orderpairs(requires) do local m = get_from_target_mapper(target, required) if m then local rebuild = compiler_support.memcache():get2("should_build_in" .. target:name(), m.key) diff --git a/xmake/rules/c++/modules/modules_support/gcc/builder.lua b/xmake/rules/c++/modules/modules_support/gcc/builder.lua index 8b62069b9..7b382e130 100644 --- a/xmake/rules/c++/modules/modules_support/gcc/builder.lua +++ b/xmake/rules/c++/modules/modules_support/gcc/builder.lua @@ -129,7 +129,7 @@ function _get_maplines(target, module) table.insert(maplines, m_name .. " " .. compiler_support.get_bmi_path(m.bmi)) end - for required, _ in pairs(module.requires) do + for required, _ in table.orderpairs(module.requires) do local dep_module local dep_target for _, dep in ipairs(target:orderdeps()) do -- cgit v1.3.1 From 7d5573aa4d49f00e0b3420230841d25c4275f48b Mon Sep 17 00:00:00 2001 From: Arthur LAURENT Date: Wed, 31 Jan 2024 17:16:36 +0100 Subject: optimise header duplication detection --- xmake/rules/c++/modules/modules_support/builder.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'xmake/rules/c++/modules/modules_support/builder.lua') diff --git a/xmake/rules/c++/modules/modules_support/builder.lua b/xmake/rules/c++/modules/modules_support/builder.lua index 9f695cede..f92f2ed7e 100644 --- a/xmake/rules/c++/modules/modules_support/builder.lua +++ b/xmake/rules/c++/modules/modules_support/builder.lua @@ -168,10 +168,10 @@ function _target_module_map_cachekey(target) end function _is_duplicated_headerunit(target, key) - local mapper = get_target_module_mapper(target) - for _, mapped in pairs(mapper) do - if mapped.key == key then - return mapped + local mapper, mapper_keys = get_target_module_mapper(target) + for _, mapped_key in ipairs(mapper_keys) do + if mapped_key == key then + return mapper[mapped_key] end end end @@ -338,7 +338,7 @@ function get_target_module_mapper(target) memcache:set2(target:name(), "module_mapper", mapper) end - return mapper + return mapper, table.keys(mapper) end -- get a module or headerunit from target mapper -- cgit v1.3.1