Skip to content

Commit

Permalink
Show if rock is supported on other Lua versions on search fail
Browse files Browse the repository at this point in the history
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."
  • Loading branch information
mpeterv committed Apr 18, 2016
1 parent 608467a commit 302025e
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 19 deletions.
20 changes: 12 additions & 8 deletions src/luarocks/manif.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
37 changes: 30 additions & 7 deletions src/luarocks/manif_core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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.
Expand Down
50 changes: 46 additions & 4 deletions src/luarocks/search.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 = {}
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down

0 comments on commit 302025e

Please sign in to comment.