summaryrefslogtreecommitdiff
path: root/src/luarocks/manif.lua
diff options
context:
space:
mode:
authorMike Vink <mike@pionative.com>2025-02-03 21:29:42 +0100
committerMike Vink <mike@pionative.com>2025-02-03 21:29:42 +0100
commit5155816b7b925dec5d5feb1568b1d7ceb00938b9 (patch)
treedeca28ea15e79f6f804c3d90d2ba757881638af5 /src/luarocks/manif.lua
fetch tarballHEADmaster
Diffstat (limited to 'src/luarocks/manif.lua')
-rw-r--r--src/luarocks/manif.lua225
1 files changed, 225 insertions, 0 deletions
diff --git a/src/luarocks/manif.lua b/src/luarocks/manif.lua
new file mode 100644
index 0000000..a4ddda1
--- /dev/null
+++ b/src/luarocks/manif.lua
@@ -0,0 +1,225 @@
+--- Module for handling manifest files and tables.
+-- Manifest files describe the contents of a LuaRocks tree or server.
+-- They are loaded into manifest tables, which are then used for
+-- performing searches, matching dependencies, etc.
+local manif = {}
+
+local core = require("luarocks.core.manif")
+local persist = require("luarocks.persist")
+local fetch = require("luarocks.fetch")
+local dir = require("luarocks.dir")
+local fs = require("luarocks.fs")
+local cfg = require("luarocks.core.cfg")
+local path = require("luarocks.path")
+local util = require("luarocks.util")
+local queries = require("luarocks.queries")
+local type_manifest = require("luarocks.type.manifest")
+
+manif.cache_manifest = core.cache_manifest
+manif.load_rocks_tree_manifests = core.load_rocks_tree_manifests
+manif.scan_dependencies = core.scan_dependencies
+
+manif.rock_manifest_cache = {}
+
+local function check_manifest(repo_url, manifest, globals)
+ local ok, err = type_manifest.check(manifest, globals)
+ if not ok then
+ core.cache_manifest(repo_url, cfg.lua_version, nil)
+ return nil, "Error checking manifest: "..err, "type"
+ end
+ return manifest
+end
+
+local postprocess_dependencies
+do
+ local postprocess_check = setmetatable({}, { __mode = "k" })
+ postprocess_dependencies = function(manifest)
+ if postprocess_check[manifest] then
+ return
+ end
+ if manifest.dependencies then
+ for name, versions in pairs(manifest.dependencies) do
+ for version, entries in pairs(versions) do
+ for k, v in pairs(entries) do
+ entries[k] = queries.from_persisted_table(v)
+ end
+ end
+ end
+ end
+ postprocess_check[manifest] = true
+ end
+end
+
+function manif.load_rock_manifest(name, version, root)
+ assert(type(name) == "string" and not name:match("/"))
+ assert(type(version) == "string")
+
+ local name_version = name.."/"..version
+ if manif.rock_manifest_cache[name_version] then
+ return manif.rock_manifest_cache[name_version].rock_manifest
+ end
+ local pathname = path.rock_manifest_file(name, version, root)
+ local rock_manifest = persist.load_into_table(pathname)
+ if not rock_manifest then
+ return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks tree?"
+ end
+ manif.rock_manifest_cache[name_version] = rock_manifest
+ return rock_manifest.rock_manifest
+end
+
+--- Load a local or remote manifest describing a repository.
+-- All functions that use manifest tables assume they were obtained
+-- through this function.
+-- @param repo_url string: URL or pathname for the repository.
+-- @param lua_version string: Lua version in "5.x" format, defaults to installed version.
+-- @param versioned_only boolean: If true, do not fall back to the main manifest
+-- if a versioned manifest was not found.
+-- @return table or (nil, string, [string]): A table representing the manifest,
+-- or nil followed by an error message and an optional error code.
+function manif.load_manifest(repo_url, lua_version, versioned_only)
+ assert(type(repo_url) == "string")
+ assert(type(lua_version) == "string" or not lua_version)
+ lua_version = lua_version or cfg.lua_version
+
+ local cached_manifest = core.get_cached_manifest(repo_url, lua_version)
+ if cached_manifest then
+ postprocess_dependencies(cached_manifest)
+ return cached_manifest
+ end
+
+ local filenames = {
+ "manifest-"..lua_version..".zip",
+ "manifest-"..lua_version,
+ not versioned_only and "manifest" or nil,
+ }
+
+ local protocol, repodir = dir.split_url(repo_url)
+ local pathname, from_cache
+ if protocol == "file" then
+ for _, filename in ipairs(filenames) do
+ pathname = dir.path(repodir, filename)
+ if fs.exists(pathname) then
+ break
+ end
+ end
+ else
+ local err, errcode
+ for _, filename in ipairs(filenames) do
+ pathname, err, errcode, from_cache = fetch.fetch_caching(dir.path(repo_url, filename), "no_mirror")
+ if pathname then
+ break
+ end
+ end
+ if not pathname then
+ return nil, err, errcode
+ end
+ end
+ if pathname:match(".*%.zip$") then
+ pathname = fs.absolute_name(pathname)
+ local nozip = pathname:match("(.*)%.zip$")
+ if not from_cache then
+ local dirname = dir.dir_name(pathname)
+ fs.change_dir(dirname)
+ fs.delete(nozip)
+ local ok, err = fs.unzip(pathname)
+ fs.pop_dir()
+ if not ok then
+ fs.delete(pathname)
+ fs.delete(pathname..".timestamp")
+ return nil, "Failed extracting manifest file: " .. err
+ end
+ end
+ pathname = nozip
+ end
+ local manifest, err, errcode = core.manifest_loader(pathname, repo_url, lua_version)
+ if not manifest then
+ return nil, err, errcode
+ end
+
+ postprocess_dependencies(manifest)
+ return check_manifest(repo_url, manifest, err)
+end
+
+--- Get type and name of an item (a module or a command) provided by a file.
+-- @param deploy_type string: rock manifest subtree the file comes from ("bin", "lua", or "lib").
+-- @param file_path string: path to the file relatively to deploy_type subdirectory.
+-- @return (string, string): item type ("module" or "command") and name.
+function manif.get_provided_item(deploy_type, file_path)
+ assert(type(deploy_type) == "string")
+ assert(type(file_path) == "string")
+ local item_type = deploy_type == "bin" and "command" or "module"
+ local item_name = item_type == "command" and file_path or path.path_to_module(file_path)
+ return item_type, item_name
+end
+
+local function get_providers(item_type, item_name, repo)
+ assert(type(item_type) == "string")
+ assert(type(item_name) == "string")
+ local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
+ local manifest = manif.load_manifest(rocks_dir)
+ return manifest and manifest[item_type .. "s"][item_name]
+end
+
+--- Given a name of a module or a command, figure out which rock name and version
+-- correspond to it in the rock tree manifest.
+-- @param item_type string: "module" or "command".
+-- @param item_name string: module or command name.
+-- @param root string or nil: A local root dir for a rocks tree. If not given, the default is used.
+-- @return (string, string) or nil: name and version of the provider rock or nil if there
+-- is no provider.
+function manif.get_current_provider(item_type, item_name, repo)
+ local providers = get_providers(item_type, item_name, repo)
+ if providers then
+ return providers[1]:match("([^/]*)/([^/]*)")
+ end
+end
+
+function manif.get_next_provider(item_type, item_name, repo)
+ local providers = get_providers(item_type, item_name, repo)
+ if providers and providers[2] then
+ return providers[2]:match("([^/]*)/([^/]*)")
+ end
+end
+
+--- Get all versions of a package listed in a manifest file.
+-- @param name string: a package name.
+-- @param deps_mode string: "one", to use only the currently
+-- configured tree; "order" to select trees based on order
+-- (use the current tree and all trees below it on the list)
+-- or "all", to use all trees.
+-- @return table: An array of strings listing installed
+-- versions of a package, and a table indicating where they are found.
+function manif.get_versions(dep, deps_mode)
+ assert(type(dep) == "table")
+ assert(type(deps_mode) == "string")
+
+ local name = dep.name
+ local namespace = dep.namespace
+
+ local version_set = {}
+ path.map_trees(deps_mode, function(tree)
+ local manifest = manif.load_manifest(path.rocks_dir(tree))
+
+ if manifest and manifest.repository[name] then
+ for version in pairs(manifest.repository[name]) do
+ if dep.namespace then
+ local ns_file = path.rock_namespace_file(name, version, tree)
+ local fd = io.open(ns_file, "r")
+ if fd then
+ local ns = fd:read("*a")
+ fd:close()
+ if ns == namespace then
+ version_set[version] = tree
+ end
+ end
+ else
+ version_set[version] = tree
+ end
+ end
+ end
+ end)
+
+ return util.keys(version_set), version_set
+end
+
+return manif