/src/luarocks/manif.lua

http://github.com/keplerproject/luarocks · Lua · 269 lines · 195 code · 29 blank · 45 comment · 66 complexity · 077c4da2b5789936eb8a009dd393629c MD5 · raw file

  1. --- Module for handling manifest files and tables.
  2. -- Manifest files describe the contents of a LuaRocks tree or server.
  3. -- They are loaded into manifest tables, which are then used for
  4. -- performing searches, matching dependencies, etc.
  5. local manif = {}
  6. local core = require("luarocks.core.manif")
  7. local persist = require("luarocks.persist")
  8. local fetch = require("luarocks.fetch")
  9. local dir = require("luarocks.dir")
  10. local fs = require("luarocks.fs")
  11. local cfg = require("luarocks.core.cfg")
  12. local path = require("luarocks.path")
  13. local util = require("luarocks.util")
  14. local queries = require("luarocks.queries")
  15. local type_manifest = require("luarocks.type.manifest")
  16. manif.cache_manifest = core.cache_manifest
  17. manif.load_rocks_tree_manifests = core.load_rocks_tree_manifests
  18. manif.scan_dependencies = core.scan_dependencies
  19. manif.rock_manifest_cache = {}
  20. local function check_manifest(repo_url, manifest, globals)
  21. local ok, err = type_manifest.check(manifest, globals)
  22. if not ok then
  23. core.cache_manifest(repo_url, cfg.lua_version, nil)
  24. return nil, "Error checking manifest: "..err, "type"
  25. end
  26. return manifest
  27. end
  28. local postprocess_dependencies
  29. do
  30. local postprocess_check = setmetatable({}, { __mode = "k" })
  31. postprocess_dependencies = function(manifest)
  32. if postprocess_check[manifest] then
  33. return
  34. end
  35. if manifest.dependencies then
  36. for name, versions in pairs(manifest.dependencies) do
  37. for version, entries in pairs(versions) do
  38. for k, v in pairs(entries) do
  39. entries[k] = queries.from_persisted_table(v)
  40. end
  41. end
  42. end
  43. end
  44. postprocess_check[manifest] = true
  45. end
  46. end
  47. function manif.load_rock_manifest(name, version, root)
  48. assert(type(name) == "string" and not name:match("/"))
  49. assert(type(version) == "string")
  50. local name_version = name.."/"..version
  51. if manif.rock_manifest_cache[name_version] then
  52. return manif.rock_manifest_cache[name_version].rock_manifest
  53. end
  54. local pathname = path.rock_manifest_file(name, version, root)
  55. local rock_manifest = persist.load_into_table(pathname)
  56. if not rock_manifest then
  57. return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks tree?"
  58. end
  59. manif.rock_manifest_cache[name_version] = rock_manifest
  60. return rock_manifest.rock_manifest
  61. end
  62. --- Load a local or remote manifest describing a repository.
  63. -- All functions that use manifest tables assume they were obtained
  64. -- through this function.
  65. -- @param repo_url string: URL or pathname for the repository.
  66. -- @param lua_version string: Lua version in "5.x" format, defaults to installed version.
  67. -- @param versioned_only boolean: If true, do not fall back to the main manifest
  68. -- if a versioned manifest was not found.
  69. -- @return table or (nil, string, [string]): A table representing the manifest,
  70. -- or nil followed by an error message and an optional error code.
  71. function manif.load_manifest(repo_url, lua_version, versioned_only)
  72. assert(type(repo_url) == "string")
  73. assert(type(lua_version) == "string" or not lua_version)
  74. lua_version = lua_version or cfg.lua_version
  75. local cached_manifest = core.get_cached_manifest(repo_url, lua_version)
  76. if cached_manifest then
  77. postprocess_dependencies(cached_manifest)
  78. return cached_manifest
  79. end
  80. local filenames = {
  81. "manifest-"..lua_version..".zip",
  82. "manifest-"..lua_version,
  83. not versioned_only and "manifest" or nil,
  84. }
  85. local protocol, repodir = dir.split_url(repo_url)
  86. local pathname, from_cache
  87. if protocol == "file" then
  88. for _, filename in ipairs(filenames) do
  89. pathname = dir.path(repodir, filename)
  90. if fs.exists(pathname) then
  91. break
  92. end
  93. end
  94. else
  95. local err, errcode
  96. for _, filename in ipairs(filenames) do
  97. pathname, err, errcode, from_cache = fetch.fetch_caching(dir.path(repo_url, filename))
  98. if pathname then
  99. break
  100. end
  101. end
  102. if not pathname then
  103. return nil, err, errcode
  104. end
  105. end
  106. if pathname:match(".*%.zip$") then
  107. pathname = fs.absolute_name(pathname)
  108. local nozip = pathname:match("(.*)%.zip$")
  109. if not from_cache then
  110. local dirname = dir.dir_name(pathname)
  111. fs.change_dir(dirname)
  112. fs.delete(nozip)
  113. local ok, err = fs.unzip(pathname)
  114. fs.pop_dir()
  115. if not ok then
  116. fs.delete(pathname)
  117. fs.delete(pathname..".timestamp")
  118. return nil, "Failed extracting manifest file: " .. err
  119. end
  120. end
  121. pathname = nozip
  122. end
  123. local manifest, err, errcode = core.manifest_loader(pathname, repo_url, lua_version)
  124. if not manifest then
  125. return nil, err, errcode
  126. end
  127. postprocess_dependencies(manifest)
  128. return check_manifest(repo_url, manifest, err)
  129. end
  130. --- Get type and name of an item (a module or a command) provided by a file.
  131. -- @param deploy_type string: rock manifest subtree the file comes from ("bin", "lua", or "lib").
  132. -- @param file_path string: path to the file relatively to deploy_type subdirectory.
  133. -- @return (string, string): item type ("module" or "command") and name.
  134. function manif.get_provided_item(deploy_type, file_path)
  135. assert(type(deploy_type) == "string")
  136. assert(type(file_path) == "string")
  137. local item_type = deploy_type == "bin" and "command" or "module"
  138. local item_name = item_type == "command" and file_path or path.path_to_module(file_path)
  139. return item_type, item_name
  140. end
  141. local function get_providers(item_type, item_name, repo)
  142. assert(type(item_type) == "string")
  143. assert(type(item_name) == "string")
  144. local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
  145. local manifest = manif.load_manifest(rocks_dir)
  146. return manifest and manifest[item_type .. "s"][item_name]
  147. end
  148. --- Given a name of a module or a command, figure out which rock name and version
  149. -- correspond to it in the rock tree manifest.
  150. -- @param item_type string: "module" or "command".
  151. -- @param item_name string: module or command name.
  152. -- @param root string or nil: A local root dir for a rocks tree. If not given, the default is used.
  153. -- @return (string, string) or nil: name and version of the provider rock or nil if there
  154. -- is no provider.
  155. function manif.get_current_provider(item_type, item_name, repo)
  156. local providers = get_providers(item_type, item_name, repo)
  157. if providers then
  158. return providers[1]:match("([^/]*)/([^/]*)")
  159. end
  160. end
  161. function manif.get_next_provider(item_type, item_name, repo)
  162. local providers = get_providers(item_type, item_name, repo)
  163. if providers and providers[2] then
  164. return providers[2]:match("([^/]*)/([^/]*)")
  165. end
  166. end
  167. --- Given a name of a module or a command provided by a package, figure out
  168. -- which file provides it.
  169. -- @param name string: package name.
  170. -- @param version string: package version.
  171. -- @param item_type string: "module" or "command".
  172. -- @param item_name string: module or command name.
  173. -- @param root string or nil: A local root dir for a rocks tree. If not given, the default is used.
  174. -- @return (string, string): rock manifest subtree the file comes from ("bin", "lua", or "lib")
  175. -- and path to the providing file relatively to that subtree.
  176. function manif.get_providing_file(name, version, item_type, item_name, repo)
  177. local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
  178. local manifest = manif.load_manifest(rocks_dir)
  179. local entry_table = manifest.repository[name][version][1]
  180. local file_path = entry_table[item_type .. "s"][item_name]
  181. if item_type == "command" then
  182. return "bin", file_path
  183. end
  184. -- A module can be in "lua" or "lib". Decide based on extension first:
  185. -- most likely Lua modules are in "lua/" and C modules are in "lib/".
  186. if file_path:match("%." .. cfg.lua_extension .. "$") then
  187. return "lua", file_path
  188. elseif file_path:match("%." .. cfg.lib_extension .. "$") then
  189. return "lib", file_path
  190. end
  191. -- Fallback to rock manifest scanning.
  192. local rock_manifest = manif.load_rock_manifest(name, version, repo and repo.root)
  193. local subtree = rock_manifest.lib
  194. for path_part in file_path:gmatch("[^/]+") do
  195. if type(subtree) == "table" then
  196. subtree = subtree[path_part]
  197. else
  198. -- Assume it's in "lua/" if it's not in "lib/".
  199. return "lua", file_path
  200. end
  201. end
  202. return type(subtree) == "string" and "lib" or "lua", file_path
  203. end
  204. --- Get all versions of a package listed in a manifest file.
  205. -- @param name string: a package name.
  206. -- @param deps_mode string: "one", to use only the currently
  207. -- configured tree; "order" to select trees based on order
  208. -- (use the current tree and all trees below it on the list)
  209. -- or "all", to use all trees.
  210. -- @return table: An array of strings listing installed
  211. -- versions of a package, and a table indicating where they are found.
  212. function manif.get_versions(dep, deps_mode)
  213. assert(type(dep) == "table")
  214. assert(type(deps_mode) == "string")
  215. local name = dep.name
  216. local namespace = dep.namespace
  217. local version_set = {}
  218. path.map_trees(deps_mode, function(tree)
  219. local manifest = manif.load_manifest(path.rocks_dir(tree))
  220. if manifest and manifest.repository[name] then
  221. for version in pairs(manifest.repository[name]) do
  222. if dep.namespace then
  223. local ns_file = path.rock_namespace_file(name, version, tree)
  224. local fd = io.open(ns_file, "r")
  225. if fd then
  226. local ns = fd:read("*a")
  227. fd:close()
  228. if ns == namespace then
  229. version_set[version] = tree
  230. end
  231. end
  232. else
  233. version_set[version] = tree
  234. end
  235. end
  236. end
  237. end)
  238. return util.keys(version_set), version_set
  239. end
  240. return manif