summaryrefslogtreecommitdiff
path: root/xmake/rules/c++/modules/modules_support/common.lua
diff options
context:
space:
mode:
authorArthur LAURENT <[email protected]>2024-01-23 18:23:27 +0100
committerArthur LAURENT <[email protected]>2024-01-26 18:20:57 +0100
commitd52291f3389b2bf3f4b90e24c4670f41605c3cc8 (patch)
treeb2a2d5eefca2ce9da55bba6dedbf2413c4f610f6 /xmake/rules/c++/modules/modules_support/common.lua
parente20c6cafdb241ad6f858556c33463da1a092abdc (diff)
refactor module common infrastructure
Diffstat (limited to 'xmake/rules/c++/modules/modules_support/common.lua')
-rw-r--r--xmake/rules/c++/modules/modules_support/common.lua737
1 files changed, 0 insertions, 737 deletions
diff --git a/xmake/rules/c++/modules/modules_support/common.lua b/xmake/rules/c++/modules/modules_support/common.lua
deleted file mode 100644
index d42d0f958..000000000
--- a/xmake/rules/c++/modules/modules_support/common.lua
+++ /dev/null
@@ -1,737 +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 Arthapz, ruki
--- @file common.lua
---
-
--- imports
-import("core.base.json")
-import("core.base.bytes")
-import("core.base.hashset")
-import("core.project.config")
-import("core.tool.compiler")
-import("core.cache.memcache", {alias = "_memcache"})
-import("core.cache.localcache", {alias = "_localcache"})
-import("core.project.project")
-import("lib.detect.find_file")
-import("private.async.buildjobs")
-import("stl_headers")
-
--- get memcache
-function memcache()
- return _memcache.cache("cxxmodules")
-end
-
--- get localcache
-function localcache()
- return _localcache.cache("cxxmodules")
-end
-
--- get stl modules cache directory
-function stlmodules_cachedir(target, opt)
- opt = opt or {}
- local stlcachedir = path.join(config.buildir(), "stlmodules", "cache", target:arch(), config.mode() or "release")
- if opt.mkdir and not os.isdir(stlcachedir) then
- os.mkdir(stlcachedir)
- os.mkdir(path.join(stlcachedir, "experimental"))
- end
- return stlcachedir
-end
-
--- get modules cache directory
-function modules_cachedir(target, opt)
- opt = opt or {}
- local cachedir = path.join(target:autogendir(), "rules", "modules", "cache")
- if opt.mkdir and not os.isdir(cachedir) then
- os.mkdir(cachedir)
- end
- return cachedir
-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
-
--- patch sourcebatch
-function patch_sourcebatch(target, sourcebatch)
- sourcebatch.sourcekind = "cxx"
- sourcebatch.objectfiles = {}
- sourcebatch.dependfiles = {}
- for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
- local objectfile = target:objectfile(sourcefile)
- local dependfile = target:dependfile(objectfile)
- table.insert(sourcebatch.objectfiles, objectfile)
- table.insert(sourcebatch.dependfiles, dependfile)
- end
-end
-
-function parse_meta_info(target, metafile)
- local metadata = json.loadfile(metafile)
- if metadata._VENDOR_extension.xmake then
- return metadata._VENDOR_extension.xmake.file, metadata._VENDOR_extension.xmake.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
-
--- extract packages modules dependencies
-function get_all_package_modules(target, modules, opt)
- local package_modules
-
- -- parse all meta-info and append their informations to the package store
- for _, package in pairs(target:pkgs()) do
- package_modules = package_modules or {}
- local modulesdir = path.join(package:installdir(), "modules")
- local metafiles = os.files(path.join(modulesdir, "*", "*.meta-info"))
- for _, metafile in ipairs(metafiles) do
- local modulefile, name, metadata = parse_meta_info(target, metafile)
- package_modules[name] = {
- file = path.join(modulesdir, modulefile),
- metadata = metadata
- }
- end
- end
- return package_modules
-end
-
--- cull unused modules
-function cull_unused_modules(target, modules, package_modules_data)
- local needed_modules = {}
- -- append all target dependencies
- for _, module in pairs(modules) do
- if module.requires then
- for required, _ in pairs(module.requires) do
- table.insert(needed_modules, required)
- end
- end
- end
-
- -- append all package dependencies
- local culled
- local module_names = table.keys(package_modules_data)
- for _, name in ipairs(module_names) do
- culled = culled or {}
- if table.find(needed_modules, name) and package_modules_data[name] and not culled[name] then
- culled[name] = package_modules_data[name]
-
- if culled[name].metadata.imports then
- table.join2(needed_modules, culled[name].metadata.imports)
- table.join2(module_names, culled[name].metadata.imports)
- end
- end
- end
- return culled
-end
-
--- get modules support
-function modules_support(target)
- local cachekey = tostring(target)
- local module_builder = memcache():get2("modules_support", cachekey)
- if module_builder == nil then
- if target:has_tool("cxx", "clang", "clangxx") then
- module_builder = import("clang", {anonymous = true})
- elseif target:has_tool("cxx", "gcc", "gxx") then
- module_builder = import("gcc", {anonymous = true})
- elseif target:has_tool("cxx", "cl") then
- module_builder = import("msvc", {anonymous = true})
- else
- local _, toolname = target:tool("cxx")
- raise("compiler(%s): does not support c++ module!", toolname)
- end
- memcache():set2("modules_support", cachekey, module_builder)
- end
- return module_builder
-end
-
--- get bmi extension
-function bmi_extension(target)
- return modules_support(target).get_bmi_extension()
-end
-
--- has module extension? e.g. *.mpp, ...
-function has_module_extension(sourcefile)
- local modulexts = _g.modulexts
- if modulexts == nil then
- modulexts = hashset.of(".mpp", ".mxx", ".cppm", ".ixx")
- _g.modulexts = modulexts
- end
- local extension = path.extension(sourcefile)
- return modulexts:has(extension:lower())
-end
-
--- this target contains module files?
-function contains_modules(target)
- -- we can not use `"c++.build.modules.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
-
--- 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(".ifc") or output:endswith(".pcm") or output:endswith(".bmi") 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"] .. 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(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
-
--- check circular dependencies for the given module
-function _check_circular_dependencies_of_module(name, moduledeps, modulesources, depspath)
- for _, dep in ipairs(moduledeps[name]) do
- local depinfo = moduledeps[dep]
- if depinfo then
- local depspath_sub
- if depspath then
- for idx, name in ipairs(depspath) do
- if name == dep then
- local circular_deps = table.slice(depspath, idx)
- table.insert(circular_deps, dep)
- local sourceinfo = ""
- for _, circular_depname in ipairs(circular_deps) do
- local sourcefile = modulesources[circular_depname]
- if sourcefile then
- sourceinfo = sourceinfo .. ("\n -> module(%s) in %s"):format(circular_depname, sourcefile)
- end
- end
- os.raise("circular modules dependency(%s) detected!%s", table.concat(circular_deps, ", "), sourceinfo)
- end
- end
- depspath_sub = table.join(depspath, dep)
- end
- _check_circular_dependencies_of_module(dep, moduledeps, modulesources, depspath_sub)
- end
- end
-end
-
--- check circular dependencies
--- @see https://github.com/xmake-io/xmake/issues/3031
-function _check_circular_dependencies(modules)
- local moduledeps = {}
- local modulesources = {}
- for _, mod in pairs(modules) do
- if mod then
- if mod.provides and mod.requires then
- for name, provide in pairs(mod.provides) do
- modulesources[name] = provide.sourcefile
- local deps = moduledeps[name]
- if deps then
- table.join2(deps, mod.requires)
- else
- moduledeps[name] = table.keys(mod.requires)
- end
- end
- end
- end
- end
- for name, _ in pairs(moduledeps) do
- _check_circular_dependencies_of_module(name, moduledeps, modulesources, {name})
- end
-end
-
-function _topological_sort_visit(node, nodes, modules, output)
- if node.marked then
- return
- end
- assert(not node.tempmarked)
- node.tempmarked = true
- local m1 = modules[node.objectfile]
- for _, n in ipairs(nodes) do
- if not n.tempmarked then
- local m2 = modules[n.objectfile]
- if m2 then
- for name, provide in pairs(m1.provides) do
- if m2.requires and m2.requires[name] then
- _topological_sort_visit(n, nodes, modules, output)
- end
- end
- end
- end
- end
- node.tempmarked = false
- node.marked = true
- table.insert(output, 1, node.objectfile)
-end
-
-function _topological_sort_has_node_without_mark(nodes)
- for _, node in ipairs(nodes) do
- if not node.marked then
- return true
- end
- end
- return false
-end
-
-function _topological_sort_get_first_unmarked_node(nodes)
- for _, node in ipairs(nodes) do
- if not node.marked and not node.tempmarked then
- return node
- end
- end
-end
-
--- topological sort
-function sort_modules_by_dependencies(objectfiles, modules)
- local output = {}
- local nodes = {}
- for _, objectfile in ipairs(objectfiles) do
- local m = modules[objectfile]
- if m then
- table.insert(nodes, {marked = false, tempmarked = false, objectfile = objectfile})
- end
- end
- while _topological_sort_has_node_without_mark(nodes) do
- local node = _topological_sort_get_first_unmarked_node(nodes)
- _topological_sort_visit(node, nodes, modules, output)
- end
- return output
-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))
- return p
-end
-
-function find_angle_header_file(target, file)
- local headerpaths = modules_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 pairs(target:pkgs()) 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, "find <%s> not found!", file)
- return p
-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 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"] = 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"] = 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 = 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) .. 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
-
--- get module dependencies
-function get_module_dependencies(target, sourcebatch, opt)
- local cachekey = target:name() .. "/" .. sourcebatch.rulename
- local modules = memcache():get2("modules", cachekey)
- if modules == nil or opt.regenerate then
- modules = localcache():get2("modules", cachekey)
- opt.progress = opt.progress or 0
- local changed = modules_support(target).generate_dependencies(target, sourcebatch, opt)
- if changed or modules == nil then
- local moduleinfos = load_moduleinfos(target, sourcebatch)
- modules = _parse_dependencies_data(target, moduleinfos)
- modules = table.join(modules or {}, modules_support(target).get_stdmodules(target))
- if modules then
- _check_circular_dependencies(modules)
- end
- localcache():set2("modules", cachekey, modules)
- localcache():save()
- end
- memcache():set2("modules", cachekey, modules)
- end
- return modules
-end
-
--- generate headerunits for batchcmds
-function generate_headerunits_for_batchcmds(target, batchcmds, sourcebatch, modules, opt)
- local user_headerunits, stl_headerunits = get_headerunits(target, sourcebatch, modules)
- -- build stl header units as other headerunits may need them
- if stl_headerunits then
- modules_support(target).generate_stl_headerunits_for_batchcmds(target, batchcmds, stl_headerunits, opt)
- end
- if user_headerunits then
- modules_support(target).generate_user_headerunits_for_batchcmds(target, batchcmds, user_headerunits, opt)
- end
-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)
- local objectfiles = sort_modules_by_dependencies(sourcebatch.objectfiles, modules)
- modules_support(target).build_modules_for_batchjobs(target, batchjobs, objectfiles, modules, opt)
-end
-
--- build modules for batchcmds
-function build_modules_for_batchcmds(target, batchcmds, sourcebatch, modules, opt)
- local objectfiles = sort_modules_by_dependencies(sourcebatch.objectfiles, modules)
- modules_support(target).build_modules_for_batchcmds(target, batchcmds, objectfiles, modules, opt)
-end
-
--- append headerunits objectfiles to link
-function append_dependency_objectfiles(target)
- local cachekey = target:name() .. "dependency_objectfiles"
- local cache = 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
-
--- generate meta module informations for package / other buildsystems import
--- based on https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2473r1.pdf
---
--- e.g
--- {
--- "include_paths": ["foo/", "bar/"]
--- "definitions": ["FOO=BAR"]
--- "imports": ["std", "bar"]
--- "_VENDOR_extension": {}
--- }
-function generate_meta_module_info(target, name, sourcefile, requires)
- local module_metadata = {}
-
- -- add include paths
- module_metadata.include_paths = table.wrap(target:get("includedirs")) or {}
- for _, deps in ipairs(target:orderdeps()) do
- table.join2(module_metadata.include_paths, deps:get("includedirs") or {})
- end
-
- -- add definitions
- module_metadata.definitions = table.wrap(target:get("defines")) or {}
- for _, deps in ipairs(target:orderdeps()) do
- table.join2(module_metadata.definitions, deps:get("defines") or {})
- end
-
- -- 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
-
- local modulehash = get_modulehash(target, sourcefile)
- module_metadata._VENDOR_extension = { xmake = { name = name, file = path.join(modulehash, path.filename(sourcefile)) }}
- return module_metadata
-end
-
-function install_module_target(target)
- local sourcebatch = target:sourcebatches()["c++.build.modules.install"]
- if sourcebatch and sourcebatch.sourcefiles then
- for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
- local install = (fileconfig and not fileconfig.install) and false or true
- if install then
- local modulehash = get_modulehash(target, sourcefile)
- 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
-
-function get_modulehash(target, modulepath)
- local key = path.directory(modulepath) .. target:name()
- 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 = modules_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