PageRenderTime 18ms CodeModel.GetById 2ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 1ms

/src/luarocks/manif.lua

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