From 302025e1812ea790248a14b425f8c46b153af622 Mon Sep 17 00:00:00 2001 From: mpeterv Date: Sat, 19 Mar 2016 15:50:20 +0300 Subject: Show if rock is supported on other Lua versions on search fail Supporting changes: * Change structure of manifest cache: map from repo_url to tables mapping from lua versions to manifests. * New manif_core cache_manifest and get_cached_manifest functions to hide cache implementation. * lua_version optional argument for functions between search.find_suitable_rock and manifest loader. Main changes: * Add a helper function supported_lua_versions that checks which Lua versions can satisfy a query. * Use this helper function when a search for a rock failed, in search.find_suitable_rock, if constraints can be satisfied under a different Lua version mention that in the error message. Examples of error messages: * Constraint "sailor": "sailor supports only Lua 5.1 and Lua 5.2 but not Lua 5.3." * Constraint "sailor 0.5": "sailor 0.5 supports only Lua 5.1 and Lua 5.2 but not Lua 5.3." * Constraint "sailor >= 0.5": "Matching sailor versions support only Lua 5.1 and Lua 5.2 but not Lua 5.3." --- src/luarocks/manif.lua | 20 ++++++++++-------- src/luarocks/manif_core.lua | 37 ++++++++++++++++++++++++++------- src/luarocks/search.lua | 50 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 88 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/luarocks/manif.lua b/src/luarocks/manif.lua index f53f8dca..05621315 100644 --- a/src/luarocks/manif.lua +++ b/src/luarocks/manif.lua @@ -105,18 +105,22 @@ end -- All functions that use manifest tables assume they were obtained -- through either this function or load_local_manifest. -- @param repo_url string: URL or pathname for the repository. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. -- @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) +function manif.load_manifest(repo_url, lua_version) assert(type(repo_url) == "string") + assert(type(lua_version) == "string" or not lua_version) + lua_version = lua_version or cfg.lua_version - if manif_core.manifest_cache[repo_url] then - return manif_core.manifest_cache[repo_url] + local cached_manifest = manif_core.get_cached_manifest(repo_url, lua_version) + if cached_manifest then + return cached_manifest end - + local filenames = { - "manifest-"..cfg.lua_version..".zip", - "manifest-"..cfg.lua_version, + "manifest-"..lua_version..".zip", + "manifest-"..lua_version, "manifest", } @@ -156,7 +160,7 @@ function manif.load_manifest(repo_url) end pathname = nozip end - return manif_core.manifest_loader(pathname, repo_url) + return manif_core.manifest_loader(pathname, repo_url, lua_version) end --- Output a table listing items of a package. @@ -381,7 +385,7 @@ function manif.make_manifest(repo, deps_mode, remote) local results = search.disk_search(repo, query) local manifest = { repository = {}, modules = {}, commands = {} } - manif_core.manifest_cache[repo] = manifest + manif_core.cache_manifest(repo, nil, manifest) local dep_handler = nil if not remote then diff --git a/src/luarocks/manif_core.lua b/src/luarocks/manif_core.lua index 87fc16cf..610f9860 100644 --- a/src/luarocks/manif_core.lua +++ b/src/luarocks/manif_core.lua @@ -7,20 +7,43 @@ package.loaded["luarocks.manif_core"] = manif_core local persist = require("luarocks.persist") local type_check = require("luarocks.type_check") +local cfg = require("luarocks.cfg") local dir = require("luarocks.dir") local util = require("luarocks.util") local path = require("luarocks.path") -manif_core.manifest_cache = {} +-- Table with repository identifiers as keys and tables mapping +-- Lua versions to cached loaded manifests as values. +local manifest_cache = {} + +--- Cache a loaded manifest. +-- @param repo_url string: The repository identifier. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @param manifest table: the manifest to be cached. +function manif_core.cache_manifest(repo_url, lua_version, manifest) + lua_version = lua_version or cfg.lua_version + manifest_cache[repo_url] = manifest_cache[repo_url] or {} + manifest_cache[repo_url][lua_version] = manifest +end + +--- Attempt to get cached loaded manifest. +-- @param repo_url string: The repository identifier. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @return table or nil: loaded manifest or nil if cache is empty. +function manif_core.get_cached_manifest(repo_url, lua_version) + lua_version = lua_version or cfg.lua_version + return manifest_cache[repo_url] and manifest_cache[repo_url][lua_version] +end --- Back-end function that actually loads the manifest -- and stores it in the manifest cache. -- @param file string: The local filename of the manifest file. -- @param repo_url string: The repository identifier. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. -- @param quick boolean: If given, skips type checking. -- @return table or (nil, string, string): the manifest or nil, -- error message and error code ("open", "load", "run" or "type"). -function manif_core.manifest_loader(file, repo_url, quick) +function manif_core.manifest_loader(file, repo_url, lua_version, quick) local manifest, err, errcode = persist.load_into_table(file) if not manifest then return nil, "Failed loading manifest for "..repo_url..": "..err, errcode @@ -33,7 +56,7 @@ function manif_core.manifest_loader(file, repo_url, quick) end end - manif_core.manifest_cache[repo_url] = manifest + manif_core.cache_manifest(repo_url, lua_version, manifest) return manifest end @@ -46,13 +69,13 @@ end function manif_core.load_local_manifest(repo_url) assert(type(repo_url) == "string") - if manif_core.manifest_cache[repo_url] then - return manif_core.manifest_cache[repo_url] + local cached_manifest = manif_core.get_cached_manifest(repo_url) + if cached_manifest then + return cached_manifest end local pathname = dir.path(repo_url, "manifest") - - return manif_core.manifest_loader(pathname, repo_url, true) + return manif_core.manifest_loader(pathname, repo_url, nil, true) end --- Get all versions of a package listed in a manifest file. diff --git a/src/luarocks/search.lua b/src/luarocks/search.lua index 0276dfae..6c0020c0 100644 --- a/src/luarocks/search.lua +++ b/src/luarocks/search.lua @@ -170,14 +170,15 @@ end -- If the arch field is omitted, the local architecture (cfg.arch) -- is used. The special value "any" is also recognized, returning all -- matches regardless of architecture. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. -- @return true or, in case of errors, nil, an error message and an optional error code. -function search.manifest_search(results, repo, query) +function search.manifest_search(results, repo, query, lua_version) assert(type(results) == "table") assert(type(repo) == "string") assert(type(query) == "table") query_arch_as_table(query) - local manifest, err, errcode = manif.load_manifest(repo) + local manifest, err, errcode = manif.load_manifest(repo, lua_version) if not manifest then return nil, err, errcode end @@ -193,10 +194,11 @@ end --- Search on all configured rocks servers. -- @param query table: A dependency query. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. -- @return table: A table where keys are package names -- and values are tables matching version strings to arrays of -- tables with fields "arch" and "repo". -function search.search_repos(query) +function search.search_repos(query, lua_version) assert(type(query) == "table") local results = {} @@ -210,7 +212,7 @@ function search.search_repos(query) if protocol == "file" then mirror = pathname end - local ok, err, errcode = search.manifest_search(results, mirror, query) + local ok, err, errcode = search.manifest_search(results, mirror, query, lua_version) if errcode == "network" then cfg.disabled_servers[repo] = true end @@ -278,6 +280,23 @@ local function pick_latest_version(name, versions) return nil end +-- Find out which other Lua versions provide rock versions matching a query, +-- @param query table: A dependency query matching a single rock. +-- @return table: array of Lua versions supported, in "5.x" format. +local function supported_lua_versions(query) + local results = {} + + for lua_version in util.lua_versions() do + if lua_version ~= cfg.lua_version then + if search.search_repos(query, lua_version)[query.name] then + table.insert(results, lua_version) + end + end + end + + return results +end + --- Attempt to get a single URL for a given search for a rock. -- @param query table: A dependency query matching a single rock. -- @return string or (nil, string): URL for latest matching version @@ -288,6 +307,29 @@ function search.find_suitable_rock(query) local results = search.search_repos(query) local first_rock = next(results) if not first_rock then + if cfg.rocks_provided[query.name] == nil then + -- Check if constraints are satisfiable with other Lua versions. + local lua_versions = supported_lua_versions(query) + + if #lua_versions ~= 0 then + -- Build a nice message in "only Lua 5.x and 5.y but not 5.z." format + for i, lua_version in ipairs(lua_versions) do + lua_versions[i] = "Lua "..lua_version + end + + local versions_message = "only "..table.concat(lua_versions, " and ").. + " but not Lua "..cfg.lua_version.."." + + if #query.constraints == 0 then + return nil, query.name.." supports "..versions_message + elseif #query.constraints == 1 and query.constraints[1].op == "==" then + return nil, query.name.." "..query.constraints[1].version.string.." supports "..versions_message + else + return nil, "Matching "..query.name.." versions support "..versions_message + end + end + end + return nil, "No results matching query were found." elseif next(results, first_rock) then -- Shouldn't happen as query must match only one package. -- cgit v1.2.3-55-g6feb