Mercurial > repo
diff share/lua/5.2/luarocks/search.lua @ 1132:d137f631bad5
<GreyKnight> (cd luabuild/luarocks-2.0.12; make install)
author | HackBot |
---|---|
date | Fri, 14 Dec 2012 22:24:27 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/lua/5.2/luarocks/search.lua Fri Dec 14 22:24:27 2012 +0000 @@ -0,0 +1,386 @@ + +--- Module implementing the LuaRocks "search" command. +-- Queries LuaRocks servers. +module("luarocks.search", package.seeall) + +local dir = require("luarocks.dir") +local path = require("luarocks.path") +local manif = require("luarocks.manif") +local deps = require("luarocks.deps") +local cfg = require("luarocks.cfg") +local util = require("luarocks.util") + +help_summary = "Query the LuaRocks servers." +help_arguments = "[--source] [--binary] { <name> [<version>] | --all }" +help = [[ +--source Return only rockspecs and source rocks, + to be used with the "build" command. +--binary Return only pure Lua and binary rocks (rocks that can be used + with the "install" command without requiring a C toolchain). +--all List all contents of the server that are suitable to + this platform, do not filter by name. +]] + +--- Convert the arch field of a query table to table format. +-- @param query table: A query table. +local function query_arch_as_table(query) + local format = type(query.arch) + if format == "table" then + return + elseif format == "nil" then + local accept = {} + accept["src"] = true + accept["all"] = true + accept["rockspec"] = true + accept["installed"] = true + accept[cfg.arch] = true + query.arch = accept + elseif format == "string" then + local accept = {} + for a in query.arch:gmatch("[%w_-]+") do + accept[a] = true + end + query.arch = accept + end +end + +--- Store a search result (a rock or rockspec) in the results table. +-- @param results table: The results table, where keys are package names and +-- versions are tables matching version strings to an array of servers. +-- @param name string: Package name. +-- @param version string: Package version. +-- @param arch string: Architecture of rock ("all", "src" or platform +-- identifier), "rockspec" or "installed" +-- @param repo string: Pathname of a local repository of URL of +-- rocks server. +local function store_result(results, name, version, arch, repo) + assert(type(results) == "table") + assert(type(name) == "string") + assert(type(version) == "string") + assert(type(arch) == "string") + assert(type(repo) == "string") + + if not results[name] then results[name] = {} end + if not results[name][version] then results[name][version] = {} end + table.insert(results[name][version], { + arch = arch, + repo = repo + }) +end + +--- Test the name field of a query. +-- If query has a boolean field exact_name set to false, +-- then substring match is performed; otherwise, exact string +-- comparison is done. +-- @param query table: A query in dependency table format. +-- @param name string: A package name. +-- @return boolean: True if names match, false otherwise. +local function match_name(query, name) + assert(type(query) == "table") + assert(type(name) == "string") + if query.exact_name == false then + return name:find(query.name, 0, true) and true or false + else + return name == query.name + end +end + +--- Store a match in a results table if version matches query. +-- Name, version, arch and repository path are stored in a given +-- table, optionally checking if version and arch (if given) match +-- a query. +-- @param results table: The results table, where keys are package names and +-- versions are tables matching version strings to an array of servers. +-- @param repo string: URL or pathname of the repository. +-- @param name string: The name of the package being tested. +-- @param version string: The version of the package being tested. +-- @param arch string: The arch of the package being tested. +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- 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. +local function store_if_match(results, repo, name, version, arch, query) + if match_name(query, name) then + if query.arch[arch] or query.arch["any"] then + if deps.match_constraints(deps.parse_version(version), query.constraints) then + store_result(results, name, version, arch, repo) + end + end + end +end + +--- Perform search on a local repository. +-- @param repo string: The pathname of the local repository. +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- 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 results table or nil: If given, this table will store the +-- results; if not given, a new table will be created. +-- @param table: The results table, where keys are package names and +-- versions are tables matching version strings to an array of servers. +-- If a table was given in the "results" parameter, that is the result value. +function disk_search(repo, query, results) + assert(type(repo) == "string") + assert(type(query) == "table") + assert(type(results) == "table" or not results) + + local fs = require("luarocks.fs") + + if not results then + results = {} + end + query_arch_as_table(query) + + for _, name in pairs(fs.list_dir(repo)) do + local pathname = dir.path(repo, name) + local rname, rversion, rarch = path.parse_name(name) + if fs.is_dir(pathname) then + for _, version in pairs(fs.list_dir(pathname)) do + if version:match("-%d+$") then + store_if_match(results, repo, name, version, "installed", query) + end + end + elseif rname then + store_if_match(results, repo, rname, rversion, rarch, query) + end + end + return results +end + +--- Perform search on a rocks server. +-- @param results table: The results table, where keys are package names and +-- versions are tables matching version strings to an array of servers. +-- @param repo string: The URL of the rocks server. +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- 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. +-- @return true or, in case of errors, nil and an error message. +function manifest_search(results, repo, query) + assert(type(results) == "table") + assert(type(repo) == "string") + assert(type(query) == "table") + + query_arch_as_table(query) + local manifest, err = manif.load_manifest(repo) + if not manifest then + return nil, "Failed loading manifest: "..err + end + for name, versions in pairs(manifest.repository) do + for version, items in pairs(versions) do + for _, item in ipairs(items) do + store_if_match(results, repo, name, version, item.arch, query) + end + end + end + return true +end + +--- Search on all configured rocks servers. +-- @param query table: A dependency query. +-- @return table: A table where keys are package names +-- and values are tables matching version strings to an array of +-- rocks servers; if no results are found, an empty table is returned. +function search_repos(query) + assert(type(query) == "table") + + local results = {} + for _, repo in ipairs(cfg.rocks_servers) do + if type(repo) == "string" then + repo = { repo } + end + for _, mirror in ipairs(repo) do + local protocol, pathname = dir.split_url(mirror) + if protocol == "file" then + mirror = pathname + end + local ok, err = manifest_search(results, mirror, query) + if ok then + break + else + util.warning("Failed searching manifest: "..err) + end + end + end + return results +end + +--- Prepare a query in dependency table format. +-- @param name string: The query name. +-- @param version string or nil: +-- @return table: A query in table format +function make_query(name, version) + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local query = { + name = name, + constraints = {} + } + if version then + table.insert(query.constraints, { op = "==", version = deps.parse_version(version)}) + end + return query +end + +--- Get the URL for the latest in a set of versions. +-- @param name string: The package name to be used in the URL. +-- @param versions table: An array of version informations, as stored +-- in search results tables. +-- @return string or nil: the URL for the latest version if one could +-- be picked, or nil. +local function pick_latest_version(name, versions) + assert(type(name) == "string") + assert(type(versions) == "table") + + local vtables = {} + for v, _ in pairs(versions) do + table.insert(vtables, deps.parse_version(v)) + end + table.sort(vtables) + local version = vtables[#vtables].string + local items = versions[version] + if items then + local pick = 1 + for i, item in ipairs(items) do + if (item.arch == 'src' and items[pick].arch == 'rockspec') + or (item.arch ~= 'src' and item.arch ~= 'rockspec') then + pick = i + end + end + return path.make_url(items[pick].repo, name, version, items[pick].arch) + end + return nil +end + +--- Attempt to get a single URL for a given search. +-- @param query table: A dependency query. +-- @return string or table or (nil, string): URL for matching rock if +-- a single one was found, a table of candidates if it could not narrow to +-- a single result, or nil followed by an error message. +function find_suitable_rock(query) + assert(type(query) == "table") + + local results = search_repos(query) + local first = next(results) + if not first then + return nil, "No results matching query were found." + elseif not next(results, first) then + return pick_latest_version(query.name, results[first]) + else + return results + end +end + +--- Print a list of rocks/rockspecs on standard output. +-- @param results table: A table where keys are package names and versions +-- are tables matching version strings to an array of rocks servers. +-- @param porcelain boolean or nil: A flag to force machine-friendly output. +function print_results(results, porcelain) + assert(type(results) == "table") + assert(type(porcelain) == "boolean" or not porcelain) + + for package, versions in util.sortedpairs(results) do + if not porcelain then + util.printout(package) + end + for version, repos in util.sortedpairs(versions, deps.compare_versions) do + for _, repo in ipairs(repos) do + if porcelain then + util.printout(package, version, repo.arch, repo.repo) + else + util.printout(" "..version.." ("..repo.arch..") - "..repo.repo) + end + end + end + if not porcelain then + util.printout() + end + end +end + +--- Splits a list of search results into two lists, one for "source" results +-- to be used with the "build" command, and one for "binary" results to be +-- used with the "install" command. +-- @param results table: A search results table. +-- @return (table, table): Two tables, one for source and one for binary +-- results. +local function split_source_and_binary_results(results) + local sources, binaries = {}, {} + for name, versions in pairs(results) do + for version, repositories in pairs(versions) do + for _, repo in ipairs(repositories) do + local where = sources + if repo.arch == "all" or repo.arch == cfg.arch then + where = binaries + end + store_result(where, name, version, repo.arch, repo.repo) + end + end + end + return sources, binaries +end + +--- Given a name and optionally a version, try to find in the rocks +-- servers a single .src.rock or .rockspec file that satisfies +-- the request, and run the given function on it; or display to the +-- user possibilities if it couldn't narrow down a single match. +-- @param action function: A function that takes a .src.rock or +-- .rockspec URL as a parameter. +-- @param name string: A rock name +-- @param version string or nil: A version number may also be given. +-- @return The result of the action function, or nil and an error message. +function act_on_src_or_rockspec(action, name, version, ...) + assert(type(action) == "function") + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local query = make_query(name, version) + query.arch = "src|rockspec" + local results, err = find_suitable_rock(query) + if type(results) == "string" then + return action(results, ...) + else + return nil, "Could not find a result named "..name..(version and " "..version or "").."." + end +end + +--- Driver function for "search" command. +-- @param name string: A substring of a rock name to search. +-- @param version string or nil: a version may also be passed. +-- @return boolean or (nil, string): True if build was successful; nil and an +-- error message otherwise. +function run(...) + local flags, name, version = util.parse_flags(...) + + if flags["all"] then + name, version = "", nil + end + + if type(name) ~= "string" and not flags["all"] then + return nil, "Enter name and version or use --all; see help." + end + + local query = make_query(name:lower(), version) + query.exact_name = false + local results, err = search_repos(query) + local porcelain = flags["porcelain"] + util.title("Search results:", porcelain, "=") + local sources, binaries = split_source_and_binary_results(results) + if next(sources) and not flags["binary"] then + util.title("Rockspecs and source rocks:", porcelain) + print_results(sources, porcelain) + end + if next(binaries) and not flags["source"] then + util.title("Binary and pure-Lua rocks:", porcelain) + print_results(binaries, porcelain) + end + return true +end