diff options
author | V1K1NGbg <victor@ilchev.com> | 2024-08-06 15:13:11 +0300 |
---|---|---|
committer | V1K1NGbg <victor@ilchev.com> | 2024-08-06 15:13:11 +0300 |
commit | a7c441464aa0ff24900a1cedd455a4cc1c678b50 (patch) | |
tree | 840443fb8ff67311e0c4043026f7b5b816d05a1a | |
parent | 4e40d895c233a90ca5cf7676375f17cb827f250a (diff) | |
download | luarocks-a7c441464aa0ff24900a1cedd455a4cc1c678b50.tar.gz luarocks-a7c441464aa0ff24900a1cedd455a4cc1c678b50.tar.bz2 luarocks-a7c441464aa0ff24900a1cedd455a4cc1c678b50.zip |
deps fetch and search
-rw-r--r-- | src/luarocks/config.tl | 3 | ||||
-rw-r--r-- | src/luarocks/core/cfg.d.tl | 4 | ||||
-rw-r--r-- | src/luarocks/core/types/ordering.d.tl | 2 | ||||
-rw-r--r-- | src/luarocks/core/types/persist.d.tl | 4 | ||||
-rw-r--r-- | src/luarocks/core/types/result.d.tl | 16 | ||||
-rw-r--r-- | src/luarocks/deplocks.tl | 3 | ||||
-rw-r--r-- | src/luarocks/deps.lua | 2 | ||||
-rw-r--r-- | src/luarocks/deps.tl | 43 | ||||
-rw-r--r-- | src/luarocks/fetch.tl | 8 | ||||
-rw-r--r-- | src/luarocks/persist.tl | 22 | ||||
-rw-r--r-- | src/luarocks/remove.lua | 2 | ||||
-rw-r--r-- | src/luarocks/results.tl | 22 | ||||
-rw-r--r-- | src/luarocks/search-original.lua | 393 | ||||
-rw-r--r-- | src/luarocks/search.lua | 233 | ||||
-rw-r--r-- | src/luarocks/search.tl | 399 | ||||
-rw-r--r-- | src/luarocks/util.tl | 8 |
16 files changed, 985 insertions, 179 deletions
diff --git a/src/luarocks/config.tl b/src/luarocks/config.tl index 93347453..7e589fce 100644 --- a/src/luarocks/config.tl +++ b/src/luarocks/config.tl | |||
@@ -10,7 +10,8 @@ local cfg_skip: {string: boolean} = { | |||
10 | upload_servers = true, | 10 | upload_servers = true, |
11 | } | 11 | } |
12 | 12 | ||
13 | local type PersistableTable = persist.PersistableTable | 13 | local type p = require("luarocks.core.types.persist") |
14 | local type PersistableTable = p.PersistableTable | ||
14 | 15 | ||
15 | function config.should_skip(k: string, v: any): boolean | 16 | function config.should_skip(k: string, v: any): boolean |
16 | return v is function or cfg_skip[k] | 17 | return v is function or cfg_skip[k] |
diff --git a/src/luarocks/core/cfg.d.tl b/src/luarocks/core/cfg.d.tl index b8501924..6d993a52 100644 --- a/src/luarocks/core/cfg.d.tl +++ b/src/luarocks/core/cfg.d.tl | |||
@@ -60,7 +60,9 @@ local record cfg | |||
60 | -- rockspecs | 60 | -- rockspecs |
61 | each_platform: function(?string): (function():string) | 61 | each_platform: function(?string): (function():string) |
62 | -- fetch | 62 | -- fetch |
63 | rocks_servers: {{string}} | 63 | rocks_servers: {{string} | string} |
64 | -- search | ||
65 | disabled_servers: {string: boolean} | ||
64 | end | 66 | end |
65 | 67 | ||
66 | return cfg \ No newline at end of file | 68 | return cfg \ No newline at end of file |
diff --git a/src/luarocks/core/types/ordering.d.tl b/src/luarocks/core/types/ordering.d.tl index c16140f8..653e267f 100644 --- a/src/luarocks/core/types/ordering.d.tl +++ b/src/luarocks/core/types/ordering.d.tl | |||
@@ -1,6 +1,6 @@ | |||
1 | local record ordering | 1 | local record ordering |
2 | record Ordering<K> | 2 | record Ordering<K> |
3 | is {K} | 3 | {K} |
4 | sub_orders: {K: Ordering<K>} | 4 | sub_orders: {K: Ordering<K>} |
5 | end | 5 | end |
6 | 6 | ||
diff --git a/src/luarocks/core/types/persist.d.tl b/src/luarocks/core/types/persist.d.tl new file mode 100644 index 00000000..259461de --- /dev/null +++ b/src/luarocks/core/types/persist.d.tl | |||
@@ -0,0 +1,4 @@ | |||
1 | local record persist | ||
2 | type PersistableTable = { string | number : string | number | boolean | PersistableTable } | ||
3 | end | ||
4 | return persist \ No newline at end of file | ||
diff --git a/src/luarocks/core/types/result.d.tl b/src/luarocks/core/types/result.d.tl new file mode 100644 index 00000000..a23339d4 --- /dev/null +++ b/src/luarocks/core/types/result.d.tl | |||
@@ -0,0 +1,16 @@ | |||
1 | local type q = require("luarocks.core.types.query") | ||
2 | local type Query = q.Query | ||
3 | |||
4 | local record result | ||
5 | record Result | ||
6 | name: string | ||
7 | version: string | ||
8 | namespace: string | ||
9 | arch: string | ||
10 | repo: string | ||
11 | type: function(): string | ||
12 | satisfies: function(Result, Query): boolean | ||
13 | end | ||
14 | end | ||
15 | |||
16 | return result \ No newline at end of file | ||
diff --git a/src/luarocks/deplocks.tl b/src/luarocks/deplocks.tl index 693dd04b..d9d176f1 100644 --- a/src/luarocks/deplocks.tl +++ b/src/luarocks/deplocks.tl | |||
@@ -5,7 +5,8 @@ local dir = require("luarocks.dir") | |||
5 | local util = require("luarocks.util") | 5 | local util = require("luarocks.util") |
6 | local persist = require("luarocks.persist") | 6 | local persist = require("luarocks.persist") |
7 | 7 | ||
8 | local type PersistableTable = persist.PersistableTable | 8 | local type p = require("luarocks.core.types.persist") |
9 | local type PersistableTable = p.PersistableTable | ||
9 | 10 | ||
10 | local deptable: PersistableTable = {} | 11 | local deptable: PersistableTable = {} |
11 | local deptable_mode = "start" | 12 | local deptable_mode = "start" |
diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua index 344991f6..1a9eefde 100644 --- a/src/luarocks/deps.lua +++ b/src/luarocks/deps.lua | |||
@@ -142,7 +142,7 @@ end | |||
142 | -- parsed as tables; and a table of "no-upgrade" missing dependencies | 142 | -- parsed as tables; and a table of "no-upgrade" missing dependencies |
143 | -- (to be used in plugin modules so that a plugin does not force upgrade of | 143 | -- (to be used in plugin modules so that a plugin does not force upgrade of |
144 | -- its parent application). | 144 | -- its parent application). |
145 | function deps.match_deps(dependencies, rocks_provided, skip_set, deps_mode) | 145 | function deps.match_deps(dependencies, rocks_provided, deps_mode, skip_set) |
146 | assert(type(dependencies) == "table") | 146 | assert(type(dependencies) == "table") |
147 | assert(type(rocks_provided) == "table") | 147 | assert(type(rocks_provided) == "table") |
148 | assert(type(skip_set) == "table" or skip_set == nil) | 148 | assert(type(skip_set) == "table" or skip_set == nil) |
diff --git a/src/luarocks/deps.tl b/src/luarocks/deps.tl index 104be1b9..53b2d088 100644 --- a/src/luarocks/deps.tl +++ b/src/luarocks/deps.tl | |||
@@ -25,6 +25,12 @@ local type Query = q.Query | |||
25 | local type v = require("luarocks.core.types.version") | 25 | local type v = require("luarocks.core.types.version") |
26 | local type Version = v.Version | 26 | local type Version = v.Version |
27 | 27 | ||
28 | local type p = require("luarocks.core.types.persist") | ||
29 | local type PersistableTable = p.PersistableTable | ||
30 | |||
31 | local type r = require("luarocks.core.types.result") | ||
32 | local type Result = r.Result | ||
33 | |||
28 | --- Generate a function that matches dep queries against the manifest, | 34 | --- Generate a function that matches dep queries against the manifest, |
29 | -- taking into account rocks_provided, the list of versions to skip, | 35 | -- taking into account rocks_provided, the list of versions to skip, |
30 | -- and the lockfile. | 36 | -- and the lockfile. |
@@ -42,9 +48,9 @@ local type Version = v.Version | |||
42 | -- * map of versions to locations | 48 | -- * map of versions to locations |
43 | -- * version matched via lockfile if any | 49 | -- * version matched via lockfile if any |
44 | -- * true if rock matched via rocks_provided | 50 | -- * true if rock matched via rocks_provided |
45 | local function prepare_get_versions(deps_mode: string, rocks_provided: {string : string}, depskey: string, skip_set?): function(Query): {string}, {string: string | Tree}, string, boolean | 51 | local function prepare_get_versions(deps_mode: string, rocks_provided: {string : string}, depskey: string, skip_set?: {string: {string: boolean}}): function(Query): {string}, {string: string | Tree}, string | number | boolean | PersistableTable, boolean |
46 | 52 | ||
47 | return function(dep: Query): {string}, {string: string | Tree}, string, boolean | 53 | return function(dep: Query): {string}, {string: string | Tree}, string | number | boolean | PersistableTable, boolean |
48 | local versions, locations: {string}, {string: string | Tree} | 54 | local versions, locations: {string}, {string: string | Tree} |
49 | local provided = rocks_provided[dep.name] | 55 | local provided = rocks_provided[dep.name] |
50 | if provided then | 56 | if provided then |
@@ -114,7 +120,7 @@ local function match_dep(dep: Query, | |||
114 | end | 120 | end |
115 | 121 | ||
116 | local function match_all_deps(dependencies: {Query}, | 122 | local function match_all_deps(dependencies: {Query}, |
117 | get_versions: function(Query): {string}, {string: string | Tree}, string, boolean): {Query: any}, {string}, {string} | 123 | get_versions: function(Query): {string}, {string: string | Tree}, string, boolean): {Query: Result}, {string: Query}, {string: Query} |
118 | 124 | ||
119 | local matched, missing, no_upgrade = {}, {}, {} | 125 | local matched, missing, no_upgrade = {}, {}, {} |
120 | 126 | ||
@@ -123,7 +129,7 @@ local function match_all_deps(dependencies: {Query}, | |||
123 | found, _, dep, provided = match_dep(dep, get_versions) | 129 | found, _, dep, provided = match_dep(dep, get_versions) |
124 | if found then | 130 | if found then |
125 | if not provided then | 131 | if not provided then |
126 | matched[dep] = {name = dep.name, version = found} | 132 | matched[dep] = {name = dep.name, version = found} as Result |
127 | end | 133 | end |
128 | else | 134 | else |
129 | if dep.constraints[1] and dep.constraints[1].no_upgrade then | 135 | if dep.constraints[1] and dep.constraints[1].no_upgrade then |
@@ -149,20 +155,13 @@ end | |||
149 | -- parsed as tables; and a table of "no-upgrade" missing dependencies | 155 | -- parsed as tables; and a table of "no-upgrade" missing dependencies |
150 | -- (to be used in plugin modules so that a plugin does not force upgrade of | 156 | -- (to be used in plugin modules so that a plugin does not force upgrade of |
151 | -- its parent application). | 157 | -- its parent application). |
152 | function deps.match_deps(dependencies, rocks_provided, skip_set, deps_mode) | 158 | function deps.match_deps(dependencies: {Query}, rocks_provided: {string: string}, deps_mode: string, skip_set?: {string: {string: boolean}}): {Query : Result}, {string : Query}, {string : Query} |
153 | assert(type(dependencies) == "table") | ||
154 | assert(type(rocks_provided) == "table") | ||
155 | assert(type(skip_set) == "table" or skip_set == nil) | ||
156 | assert(type(deps_mode) == "string") | ||
157 | 159 | ||
158 | local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies", skip_set) | 160 | local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies", skip_set) |
159 | return match_all_deps(dependencies, get_versions) | 161 | return match_all_deps(dependencies, get_versions) |
160 | end | 162 | end |
161 | 163 | ||
162 | local function rock_status(dep, get_versions) | 164 | local function rock_status(dep: Query, get_versions: function(Query): {string}, {string : string | Tree}, string, boolean): string, string |
163 | assert(dep:type() == "query") | ||
164 | assert(type(get_versions) == "function") | ||
165 | |||
166 | local installed, _, _, provided = match_dep(dep, get_versions) | 165 | local installed, _, _, provided = match_dep(dep, get_versions) |
167 | local installation_type = provided and "provided by VM" or "installed" | 166 | local installation_type = provided and "provided by VM" or "installed" |
168 | return installed and installed.." "..installation_type..": success" or "not installed" | 167 | return installed and installed.." "..installation_type..": success" or "not installed" |
@@ -177,12 +176,7 @@ end | |||
177 | -- by this Lua implementation for the given dependency. | 176 | -- by this Lua implementation for the given dependency. |
178 | -- "one" for the current default tree, "all" for all trees, | 177 | -- "one" for the current default tree, "all" for all trees, |
179 | -- "order" for all trees with priority >= the current default, "none" for no trees. | 178 | -- "order" for all trees with priority >= the current default, "none" for no trees. |
180 | function deps.report_missing_dependencies(name, version, dependencies, deps_mode, rocks_provided) | 179 | function deps.report_missing_dependencies(name: string, version: string, dependencies: {Query}, deps_mode: string, rocks_provided: {string: string}) |
181 | assert(type(name) == "string") | ||
182 | assert(type(version) == "string") | ||
183 | assert(type(dependencies) == "table") | ||
184 | assert(type(deps_mode) == "string") | ||
185 | assert(type(rocks_provided) == "table") | ||
186 | 180 | ||
187 | if deps_mode == "none" then | 181 | if deps_mode == "none" then |
188 | return | 182 | return |
@@ -193,7 +187,7 @@ function deps.report_missing_dependencies(name, version, dependencies, deps_mode | |||
193 | local first_missing_dep = true | 187 | local first_missing_dep = true |
194 | 188 | ||
195 | for _, dep in ipairs(dependencies) do | 189 | for _, dep in ipairs(dependencies) do |
196 | local found, _ | 190 | local found, _: string, string | Tree |
197 | found, _, dep = match_dep(dep, get_versions) | 191 | found, _, dep = match_dep(dep, get_versions) |
198 | if not found then | 192 | if not found then |
199 | if first_missing_dep then | 193 | if first_missing_dep then |
@@ -206,19 +200,14 @@ function deps.report_missing_dependencies(name, version, dependencies, deps_mode | |||
206 | end | 200 | end |
207 | end | 201 | end |
208 | 202 | ||
209 | function deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey) | 203 | function deps.fulfill_dependency(dep: Query, deps_mode: string, rocks_provided: {string: string}, verify: boolean, depskey?: string): boolean, string, string | Tree |
210 | assert(dep:type() == "query") | ||
211 | assert(type(deps_mode) == "string" or deps_mode == nil) | ||
212 | assert(type(rocks_provided) == "table" or rocks_provided == nil) | ||
213 | assert(type(verify) == "boolean" or verify == nil) | ||
214 | assert(type(depskey) == "string") | ||
215 | 204 | ||
216 | deps_mode = deps_mode or "all" | 205 | deps_mode = deps_mode or "all" |
217 | rocks_provided = rocks_provided or {} | 206 | rocks_provided = rocks_provided or {} |
218 | 207 | ||
219 | local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey) | 208 | local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey) |
220 | 209 | ||
221 | local found, where | 210 | local found, where: string, string | Tree |
222 | found, where, dep = match_dep(dep, get_versions) | 211 | found, where, dep = match_dep(dep, get_versions) |
223 | if found then | 212 | if found then |
224 | local tree_manifests = manif.load_rocks_tree_manifests(deps_mode) | 213 | local tree_manifests = manif.load_rocks_tree_manifests(deps_mode) |
diff --git a/src/luarocks/fetch.tl b/src/luarocks/fetch.tl index 6612b253..bb1fd725 100644 --- a/src/luarocks/fetch.tl +++ b/src/luarocks/fetch.tl | |||
@@ -24,9 +24,9 @@ local function ensure_trailing_slash(url: string): string | |||
24 | return (url:gsub("/*$", "/")) | 24 | return (url:gsub("/*$", "/")) |
25 | end | 25 | end |
26 | 26 | ||
27 | local function is_url_relative_to_rocks_servers(url: string, servers: {{string}}): integer, string, {string} | 27 | local function is_url_relative_to_rocks_servers(url: string, servers: {{string} | string}): integer, string, {string} |
28 | for _, item in ipairs(servers) do | 28 | for _, item in ipairs(servers) do |
29 | if type(item) == "table" then | 29 | if item is {string} then |
30 | for i, s in ipairs(item) do | 30 | for i, s in ipairs(item) do |
31 | local base = ensure_trailing_slash(s) | 31 | local base = ensure_trailing_slash(s) |
32 | if string.find(url, base, 1, true) == 1 then | 32 | if string.find(url, base, 1, true) == 1 then |
@@ -37,7 +37,7 @@ local function is_url_relative_to_rocks_servers(url: string, servers: {{string}} | |||
37 | end | 37 | end |
38 | end | 38 | end |
39 | 39 | ||
40 | local function download_with_mirrors(url: string, filename: string, cache: boolean, servers: {{string}}): string, string, string, boolean | 40 | local function download_with_mirrors(url: string, filename: string, cache: boolean, servers: {{string} | string}): string, string, string, boolean |
41 | local idx, rest, mirrors = is_url_relative_to_rocks_servers(url, servers) | 41 | local idx, rest, mirrors = is_url_relative_to_rocks_servers(url, servers) |
42 | 42 | ||
43 | if not idx then | 43 | if not idx then |
@@ -581,7 +581,7 @@ function fetch.fetch_sources(rockspec: Rockspec, extract: boolean, dest_dir?: st | |||
581 | if dir.is_basic_protocol(protocol) then | 581 | if dir.is_basic_protocol(protocol) then |
582 | proto = fetch as Fetch | 582 | proto = fetch as Fetch |
583 | else | 583 | else |
584 | ok, proto = pcall(require, ("luarocks.fetch."..protocol:gsub("[+-]", "_")) as string) as (boolean, Fetch) | 584 | ok, proto = pcall(require, "luarocks.fetch."..protocol:gsub("[+-]", "_")) as (boolean, Fetch) |
585 | if not ok then | 585 | if not ok then |
586 | return nil, "Unknown protocol "..protocol | 586 | return nil, "Unknown protocol "..protocol |
587 | end | 587 | end |
diff --git a/src/luarocks/persist.tl b/src/luarocks/persist.tl index 5c6b47c3..cc07d295 100644 --- a/src/luarocks/persist.tl +++ b/src/luarocks/persist.tl | |||
@@ -4,9 +4,6 @@ | |||
4 | local record persist | 4 | local record persist |
5 | run_file: function(string, {string:any}): boolean, any | string, string | 5 | run_file: function(string, {string:any}): boolean, any | string, string |
6 | load_into_table: function(string, ?{string:any}) : {any: any}, {any: any} | string, string | 6 | load_into_table: function(string, ?{string:any}) : {any: any}, {any: any} | string, string |
7 | |||
8 | type PersistableTable = { string | number : string | number | boolean | PersistableTable } | ||
9 | |||
10 | end | 7 | end |
11 | 8 | ||
12 | local core = require("luarocks.core.persist") | 9 | local core = require("luarocks.core.persist") |
@@ -16,7 +13,12 @@ local fs = require("luarocks.fs") | |||
16 | local cfg = require("luarocks.core.cfg") | 13 | local cfg = require("luarocks.core.cfg") |
17 | 14 | ||
18 | local type Config = cfg | 15 | local type Config = cfg |
19 | local type PersistableTable = persist.PersistableTable | 16 | |
17 | local type o = require("luarocks.core.types.ordering") | ||
18 | local type SortBy = o.SortBy | ||
19 | |||
20 | local type p = require("luarocks.core.types.persist") | ||
21 | local type PersistableTable = p.PersistableTable | ||
20 | 22 | ||
21 | persist.run_file = core.run_file | 23 | persist.run_file = core.run_file |
22 | persist.load_into_table = core.load_into_table | 24 | persist.load_into_table = core.load_into_table |
@@ -26,7 +28,7 @@ local interface Writer | |||
26 | buffer: {number | string} | 28 | buffer: {number | string} |
27 | end | 29 | end |
28 | 30 | ||
29 | local write_table: function(out: Writer, tbl: PersistableTable, level: integer, sort_by: util.SortBy<number | string>) | 31 | local write_table: function(out: Writer, tbl: PersistableTable, level: integer, sort_by: SortBy<number | string>) |
30 | 32 | ||
31 | --- Write a value as Lua code. | 33 | --- Write a value as Lua code. |
32 | -- This function handles only numbers and strings, invoking write_table | 34 | -- This function handles only numbers and strings, invoking write_table |
@@ -36,7 +38,7 @@ local write_table: function(out: Writer, tbl: PersistableTable, level: integer, | |||
36 | -- @param level number: the indentation level | 38 | -- @param level number: the indentation level |
37 | -- @param sub_order table: optional prioritization table | 39 | -- @param sub_order table: optional prioritization table |
38 | -- @see write_table | 40 | -- @see write_table |
39 | function persist.write_value(out: Writer, v: any, level: integer, sub_order?: util.SortBy<number | string>) | 41 | function persist.write_value(out: Writer, v: any, level: integer, sub_order?: SortBy<number | string>) |
40 | if v is PersistableTable then | 42 | if v is PersistableTable then |
41 | level = level or 0 | 43 | level = level or 0 |
42 | write_table(out, v, level + 1, sub_order) | 44 | write_table(out, v, level + 1, sub_order) |
@@ -110,7 +112,7 @@ end | |||
110 | -- @param tbl table: the table to be written. | 112 | -- @param tbl table: the table to be written. |
111 | -- @param level number: the indentation level | 113 | -- @param level number: the indentation level |
112 | -- @param sort_by table: optional prioritization table | 114 | -- @param sort_by table: optional prioritization table |
113 | write_table = function(out: Writer, tbl: PersistableTable, level: integer, sort_by: util.SortBy<number | string>) | 115 | write_table = function(out: Writer, tbl: PersistableTable, level: integer, sort_by: SortBy<number | string>) |
114 | out:write("{") | 116 | out:write("{") |
115 | local sep = "\n" | 117 | local sep = "\n" |
116 | local indentation = " " | 118 | local indentation = " " |
@@ -149,7 +151,7 @@ end | |||
149 | -- @param tbl table: the table to be written. | 151 | -- @param tbl table: the table to be written. |
150 | -- @param sort_by table: optional prioritization table | 152 | -- @param sort_by table: optional prioritization table |
151 | -- @return true if successful; nil and error message if failed. | 153 | -- @return true if successful; nil and error message if failed. |
152 | local function write_table_as_assignments(out: Writer, tbl: PersistableTable, sort_by: util.SortBy<number | string>): boolean, string | 154 | local function write_table_as_assignments(out: Writer, tbl: PersistableTable, sort_by: SortBy<number | string>): boolean, string |
153 | for k, v, sub_order in util.sortedpairs(tbl, sort_by) do | 155 | for k, v, sub_order in util.sortedpairs(tbl, sort_by) do |
154 | if not (k is string and is_valid_plain_key(k)) then | 156 | if not (k is string and is_valid_plain_key(k)) then |
155 | return nil, "cannot store '"..tostring(k).."' as a plain key." | 157 | return nil, "cannot store '"..tostring(k).."' as a plain key." |
@@ -182,7 +184,7 @@ end | |||
182 | -- @param tbl table: the table containing the data to be written | 184 | -- @param tbl table: the table containing the data to be written |
183 | -- @param sort_by table: an optional array indicating the order of top-level fields. | 185 | -- @param sort_by table: an optional array indicating the order of top-level fields. |
184 | -- @return persisted data as string; or nil and an error message | 186 | -- @return persisted data as string; or nil and an error message |
185 | function persist.save_from_table_to_string(tbl: PersistableTable, sort_by?: util.SortBy<number | string>): string, string | 187 | function persist.save_from_table_to_string(tbl: PersistableTable, sort_by?: SortBy<number | string>): string, string |
186 | local out: Writer = {buffer = {}} | 188 | local out: Writer = {buffer = {}} |
187 | function out:write(data: string) table.insert(self.buffer, data) end | 189 | function out:write(data: string) table.insert(self.buffer, data) end |
188 | local ok, err = write_table_as_assignments(out, tbl, sort_by) | 190 | local ok, err = write_table_as_assignments(out, tbl, sort_by) |
@@ -201,7 +203,7 @@ end | |||
201 | -- @param sort_by table: an optional array indicating the order of top-level fields. | 203 | -- @param sort_by table: an optional array indicating the order of top-level fields. |
202 | -- @return boolean or (nil, string): true if successful, or nil and a | 204 | -- @return boolean or (nil, string): true if successful, or nil and a |
203 | -- message in case of errors. | 205 | -- message in case of errors. |
204 | function persist.save_from_table(filename: string, tbl: PersistableTable, sort_by?: util.SortBy<number | string>): boolean, string | 206 | function persist.save_from_table(filename: string, tbl: PersistableTable, sort_by?: SortBy<number | string>): boolean, string |
205 | local prefix = dir.dir_name(filename) | 207 | local prefix = dir.dir_name(filename) |
206 | fs.make_dir(prefix) | 208 | fs.make_dir(prefix) |
207 | local out = io.open(filename, "w") | 209 | local out = io.open(filename, "w") |
diff --git a/src/luarocks/remove.lua b/src/luarocks/remove.lua index a24b54ba..55390cd0 100644 --- a/src/luarocks/remove.lua +++ b/src/luarocks/remove.lua | |||
@@ -33,7 +33,7 @@ local function check_dependents(name, versions, deps_mode) | |||
33 | for rock_version, _ in pairs(rock_versions) do | 33 | for rock_version, _ in pairs(rock_versions) do |
34 | local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version)) | 34 | local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version)) |
35 | if rockspec then | 35 | if rockspec then |
36 | local _, missing = deps.match_deps(rockspec.dependencies, rockspec.rocks_provided, skip_set, deps_mode) | 36 | local _, missing = deps.match_deps(rockspec.dependencies, rockspec.rocks_provided, deps_mode, skip_set) |
37 | if missing[name] then | 37 | if missing[name] then |
38 | table.insert(dependents, { name = rock_name, version = rock_version }) | 38 | table.insert(dependents, { name = rock_name, version = rock_version }) |
39 | end | 39 | end |
diff --git a/src/luarocks/results.tl b/src/luarocks/results.tl index cbe6704d..cefc5ea2 100644 --- a/src/luarocks/results.tl +++ b/src/luarocks/results.tl | |||
@@ -1,11 +1,4 @@ | |||
1 | local record results | 1 | local record results |
2 | record Results --? name | ||
3 | name: string | ||
4 | version: string | ||
5 | namespace: string | ||
6 | arch: string | ||
7 | repo: string | ||
8 | end | ||
9 | end | 2 | end |
10 | 3 | ||
11 | local vers = require("luarocks.core.vers") | 4 | local vers = require("luarocks.core.vers") |
@@ -13,17 +6,18 @@ local util = require("luarocks.util") | |||
13 | local type q = require("luarocks.core.types.query") | 6 | local type q = require("luarocks.core.types.query") |
14 | local type Query = q.Query | 7 | local type Query = q.Query |
15 | 8 | ||
16 | local type Results = results.Results | 9 | local type r = require("luarocks.core.types.result") |
10 | local type Result = r.Result | ||
17 | 11 | ||
18 | local result_mt: metatable<Results> = {} | 12 | local result_mt: metatable<Result> = {} |
19 | 13 | ||
20 | result_mt.__index = results.Results | 14 | result_mt.__index = r.Result |
21 | 15 | ||
22 | function results.Results.type(): string --? remove later | 16 | function r.Result.type(): string --? remove later |
23 | return "result" | 17 | return "result" |
24 | end | 18 | end |
25 | 19 | ||
26 | function results.new(name: string, version: string, repo: string, arch?: string, namespace?: string): Results, boolean | 20 | function results.new(name: string, version: string, repo: string, arch?: string, namespace?: string): Result, boolean |
27 | 21 | ||
28 | assert(not name:match("/")) | 22 | assert(not name:match("/")) |
29 | -- assert(type(arch) == "string" or not arch) --! arch?: string | 23 | -- assert(type(arch) == "string" or not arch) --! arch?: string |
@@ -34,7 +28,7 @@ function results.new(name: string, version: string, repo: string, arch?: string, | |||
34 | name, namespace = util.split_namespace(name) | 28 | name, namespace = util.split_namespace(name) |
35 | end | 29 | end |
36 | 30 | ||
37 | local self: Results = { | 31 | local self: Result = { |
38 | name = name, | 32 | name = name, |
39 | version = version, | 33 | version = version, |
40 | namespace = namespace, | 34 | namespace = namespace, |
@@ -63,7 +57,7 @@ end | |||
63 | --- Returns true if the result satisfies a given query. | 57 | --- Returns true if the result satisfies a given query. |
64 | -- @param query: a query. | 58 | -- @param query: a query. |
65 | -- @return boolean. | 59 | -- @return boolean. |
66 | function results.Results:satisfies(query: Query): boolean | 60 | function r.Result:satisfies(query: Query): boolean |
67 | return match_name(query, self.name) | 61 | return match_name(query, self.name) |
68 | and (query.arch[self.arch] or query.arch["any"]) | 62 | and (query.arch[self.arch] or query.arch["any"]) |
69 | and ((not query.namespace) or (query.namespace == self.namespace)) | 63 | and ((not query.namespace) or (query.namespace == self.namespace)) |
diff --git a/src/luarocks/search-original.lua b/src/luarocks/search-original.lua new file mode 100644 index 00000000..180f8f45 --- /dev/null +++ b/src/luarocks/search-original.lua | |||
@@ -0,0 +1,393 @@ | |||
1 | local search = {} | ||
2 | |||
3 | local dir = require("luarocks.dir") | ||
4 | local path = require("luarocks.path") | ||
5 | local manif = require("luarocks.manif") | ||
6 | local vers = require("luarocks.core.vers") | ||
7 | local cfg = require("luarocks.core.cfg") | ||
8 | local util = require("luarocks.util") | ||
9 | local queries = require("luarocks.queries") | ||
10 | local results = require("luarocks.results") | ||
11 | |||
12 | --- Store a search result (a rock or rockspec) in the result tree. | ||
13 | -- @param result_tree table: The result tree, where keys are package names and | ||
14 | -- values are tables matching version strings to arrays of | ||
15 | -- tables with fields "arch" and "repo". | ||
16 | -- @param result table: A result. | ||
17 | function search.store_result(result_tree, result) | ||
18 | assert(type(result_tree) == "table") | ||
19 | assert(result:type() == "result") | ||
20 | |||
21 | local name = result.name | ||
22 | local version = result.version | ||
23 | |||
24 | if not result_tree[name] then result_tree[name] = {} end | ||
25 | if not result_tree[name][version] then result_tree[name][version] = {} end | ||
26 | table.insert(result_tree[name][version], { | ||
27 | arch = result.arch, | ||
28 | repo = result.repo, | ||
29 | namespace = result.namespace, | ||
30 | }) | ||
31 | end | ||
32 | |||
33 | --- Store a match in a result tree if version matches query. | ||
34 | -- Name, version, arch and repository path are stored in a given | ||
35 | -- table, optionally checking if version and arch (if given) match | ||
36 | -- a query. | ||
37 | -- @param result_tree table: The result tree, where keys are package names and | ||
38 | -- values are tables matching version strings to arrays of | ||
39 | -- tables with fields "arch" and "repo". | ||
40 | -- @param result table: a result object. | ||
41 | -- @param query table: a query object. | ||
42 | local function store_if_match(result_tree, result, query) | ||
43 | assert(result:type() == "result") | ||
44 | assert(query:type() == "query") | ||
45 | |||
46 | if result:satisfies(query) then | ||
47 | search.store_result(result_tree, result) | ||
48 | end | ||
49 | end | ||
50 | |||
51 | --- Perform search on a local repository. | ||
52 | -- @param repo string: The pathname of the local repository. | ||
53 | -- @param query table: a query object. | ||
54 | -- @param result_tree table or nil: If given, this table will store the | ||
55 | -- result tree; if not given, a new table will be created. | ||
56 | -- @return table: The result tree, where keys are package names and | ||
57 | -- values are tables matching version strings to arrays of | ||
58 | -- tables with fields "arch" and "repo". | ||
59 | -- If a table was given in the "result_tree" parameter, that is the result value. | ||
60 | function search.disk_search(repo, query, result_tree) | ||
61 | assert(type(repo) == "string") | ||
62 | assert(query:type() == "query") | ||
63 | assert(type(result_tree) == "table" or not result_tree) | ||
64 | |||
65 | local fs = require("luarocks.fs") | ||
66 | |||
67 | if not result_tree then | ||
68 | result_tree = {} | ||
69 | end | ||
70 | |||
71 | for name in fs.dir(repo) do | ||
72 | local pathname = dir.path(repo, name) | ||
73 | local rname, rversion, rarch = path.parse_name(name) | ||
74 | |||
75 | if rname and (pathname:match(".rockspec$") or pathname:match(".rock$")) then | ||
76 | local result = results.new(rname, rversion, repo, rarch) | ||
77 | store_if_match(result_tree, result, query) | ||
78 | elseif fs.is_dir(pathname) then | ||
79 | for version in fs.dir(pathname) do | ||
80 | if version:match("-%d+$") then | ||
81 | local namespace = path.read_namespace(name, version, repo) | ||
82 | local result = results.new(name, version, repo, "installed", namespace) | ||
83 | store_if_match(result_tree, result, query) | ||
84 | end | ||
85 | end | ||
86 | end | ||
87 | end | ||
88 | return result_tree | ||
89 | end | ||
90 | |||
91 | --- Perform search on a rocks server or tree. | ||
92 | -- @param result_tree table: The result tree, where keys are package names and | ||
93 | -- values are tables matching version strings to arrays of | ||
94 | -- tables with fields "arch" and "repo". | ||
95 | -- @param repo string: The URL of a rocks server or | ||
96 | -- the pathname of a rocks tree (as returned by path.rocks_dir()). | ||
97 | -- @param query table: a query object. | ||
98 | -- @param lua_version string: Lua version in "5.x" format, defaults to installed version. | ||
99 | -- @param is_local boolean | ||
100 | -- @return true or, in case of errors, nil, an error message and an optional error code. | ||
101 | local function manifest_search(result_tree, repo, query, lua_version, is_local) | ||
102 | assert(type(result_tree) == "table") | ||
103 | assert(type(repo) == "string") | ||
104 | assert(query:type() == "query") | ||
105 | |||
106 | -- FIXME do not add this in local repos | ||
107 | if (not is_local) and query.namespace then | ||
108 | repo = repo .. "/manifests/" .. query.namespace | ||
109 | end | ||
110 | |||
111 | local manifest, err, errcode = manif.load_manifest(repo, lua_version, not is_local) | ||
112 | if not manifest then | ||
113 | return nil, err, errcode | ||
114 | end | ||
115 | for name, versions in pairs(manifest.repository) do | ||
116 | for version, items in pairs(versions) do | ||
117 | local namespace = is_local and path.read_namespace(name, version, repo) or query.namespace | ||
118 | for _, item in ipairs(items) do | ||
119 | local result = results.new(name, version, repo, item.arch, namespace) | ||
120 | store_if_match(result_tree, result, query) | ||
121 | end | ||
122 | end | ||
123 | end | ||
124 | return true | ||
125 | end | ||
126 | |||
127 | local function remote_manifest_search(result_tree, repo, query, lua_version) | ||
128 | return manifest_search(result_tree, repo, query, lua_version, false) | ||
129 | end | ||
130 | |||
131 | function search.local_manifest_search(result_tree, repo, query, lua_version) | ||
132 | return manifest_search(result_tree, repo, query, lua_version, true) | ||
133 | end | ||
134 | |||
135 | --- Search on all configured rocks servers. | ||
136 | -- @param query table: a query object. | ||
137 | -- @param lua_version string: Lua version in "5.x" format, defaults to installed version. | ||
138 | -- @return table: A table where keys are package names | ||
139 | -- and values are tables matching version strings to arrays of | ||
140 | -- tables with fields "arch" and "repo". | ||
141 | function search.search_repos(query, lua_version) | ||
142 | assert(query:type() == "query") | ||
143 | |||
144 | local result_tree = {} | ||
145 | for _, repo in ipairs(cfg.rocks_servers) do | ||
146 | if type(repo) == "string" then | ||
147 | repo = { repo } | ||
148 | end | ||
149 | for _, mirror in ipairs(repo) do | ||
150 | if not cfg.disabled_servers[mirror] then | ||
151 | local protocol, pathname = dir.split_url(mirror) | ||
152 | if protocol == "file" then | ||
153 | mirror = pathname | ||
154 | end | ||
155 | local ok, err, errcode = remote_manifest_search(result_tree, mirror, query, lua_version) | ||
156 | if errcode == "network" then | ||
157 | cfg.disabled_servers[mirror] = true | ||
158 | end | ||
159 | if ok then | ||
160 | break | ||
161 | else | ||
162 | util.warning("Failed searching manifest: "..err) | ||
163 | if errcode == "downloader" then | ||
164 | break | ||
165 | end | ||
166 | end | ||
167 | end | ||
168 | end | ||
169 | end | ||
170 | -- search through rocks in rocks_provided | ||
171 | local provided_repo = "provided by VM or rocks_provided" | ||
172 | for name, version in pairs(util.get_rocks_provided()) do | ||
173 | local result = results.new(name, version, provided_repo, "installed") | ||
174 | store_if_match(result_tree, result, query) | ||
175 | end | ||
176 | return result_tree | ||
177 | end | ||
178 | |||
179 | --- Get the URL for the latest in a set of versions. | ||
180 | -- @param name string: The package name to be used in the URL. | ||
181 | -- @param versions table: An array of version informations, as stored | ||
182 | -- in search result trees. | ||
183 | -- @return string or nil: the URL for the latest version if one could | ||
184 | -- be picked, or nil. | ||
185 | local function pick_latest_version(name, versions) | ||
186 | assert(type(name) == "string" and not name:match("/")) | ||
187 | assert(type(versions) == "table") | ||
188 | |||
189 | local vtables = {} | ||
190 | for v, _ in pairs(versions) do | ||
191 | table.insert(vtables, vers.parse_version(v)) | ||
192 | end | ||
193 | table.sort(vtables) | ||
194 | local version = vtables[#vtables].string | ||
195 | local items = versions[version] | ||
196 | if items then | ||
197 | local pick = 1 | ||
198 | for i, item in ipairs(items) do | ||
199 | if (item.arch == 'src' and items[pick].arch == 'rockspec') | ||
200 | or (item.arch ~= 'src' and item.arch ~= 'rockspec') then | ||
201 | pick = i | ||
202 | end | ||
203 | end | ||
204 | return path.make_url(items[pick].repo, name, version, items[pick].arch) | ||
205 | end | ||
206 | return nil | ||
207 | end | ||
208 | |||
209 | -- Find out which other Lua versions provide rock versions matching a query, | ||
210 | -- @param query table: a query object. | ||
211 | -- @return table: array of Lua versions supported, in "5.x" format. | ||
212 | local function supported_lua_versions(query) | ||
213 | assert(query:type() == "query") | ||
214 | local result_tree = {} | ||
215 | |||
216 | for lua_version in util.lua_versions() do | ||
217 | if lua_version ~= cfg.lua_version then | ||
218 | util.printout("Checking for Lua " .. lua_version .. "...") | ||
219 | if search.search_repos(query, lua_version)[query.name] then | ||
220 | table.insert(result_tree, lua_version) | ||
221 | end | ||
222 | end | ||
223 | end | ||
224 | |||
225 | return result_tree | ||
226 | end | ||
227 | |||
228 | --- Attempt to get a single URL for a given search for a rock. | ||
229 | -- @param query table: a query object. | ||
230 | -- @return string or (nil, string, string): URL for latest matching version | ||
231 | -- of the rock if it was found, or nil followed by an error message | ||
232 | -- and an error code. | ||
233 | function search.find_suitable_rock(query) | ||
234 | assert(query:type() == "query") | ||
235 | |||
236 | local rocks_provided = util.get_rocks_provided() | ||
237 | |||
238 | if rocks_provided[query.name] ~= nil then | ||
239 | -- Do not install versions listed in rocks_provided. | ||
240 | return nil, "Rock "..query.name.." "..rocks_provided[query.name].. | ||
241 | " is already provided by VM or via 'rocks_provided' in the config file.", "provided" | ||
242 | end | ||
243 | |||
244 | local result_tree = search.search_repos(query) | ||
245 | local first_rock = next(result_tree) | ||
246 | if not first_rock then | ||
247 | return nil, "No results matching query were found for Lua " .. cfg.lua_version .. ".", "notfound" | ||
248 | elseif next(result_tree, first_rock) then | ||
249 | -- Shouldn't happen as query must match only one package. | ||
250 | return nil, "Several rocks matched query.", "manyfound" | ||
251 | else | ||
252 | return pick_latest_version(query.name, result_tree[first_rock]) | ||
253 | end | ||
254 | end | ||
255 | |||
256 | function search.find_src_or_rockspec(name, namespace, version, check_lua_versions) | ||
257 | local query = queries.new(name, namespace, version, false, "src|rockspec") | ||
258 | local url, err = search.find_rock_checking_lua_versions(query, check_lua_versions) | ||
259 | if not url then | ||
260 | return nil, "Could not find a result named "..tostring(query)..": "..err | ||
261 | end | ||
262 | return url | ||
263 | end | ||
264 | |||
265 | function search.find_rock_checking_lua_versions(query, check_lua_versions) | ||
266 | local url, err, errcode = search.find_suitable_rock(query) | ||
267 | if url then | ||
268 | return url | ||
269 | end | ||
270 | |||
271 | if errcode == "notfound" then | ||
272 | local add | ||
273 | if check_lua_versions then | ||
274 | util.printout(query.name .. " not found for Lua " .. cfg.lua_version .. ".") | ||
275 | util.printout("Checking if available for other Lua versions...") | ||
276 | |||
277 | -- Check if constraints are satisfiable with other Lua versions. | ||
278 | local lua_versions = supported_lua_versions(query) | ||
279 | |||
280 | if #lua_versions ~= 0 then | ||
281 | -- Build a nice message in "only Lua 5.x and 5.y but not 5.z." format | ||
282 | for i, lua_version in ipairs(lua_versions) do | ||
283 | lua_versions[i] = "Lua "..lua_version | ||
284 | end | ||
285 | |||
286 | local versions_message = "only "..table.concat(lua_versions, " and ").. | ||
287 | " but not Lua "..cfg.lua_version.."." | ||
288 | |||
289 | if #query.constraints == 0 then | ||
290 | add = query.name.." supports "..versions_message | ||
291 | elseif #query.constraints == 1 and query.constraints[1].op == "==" then | ||
292 | add = query.name.." "..query.constraints[1].version.string.." supports "..versions_message | ||
293 | else | ||
294 | add = "Matching "..query.name.." versions support "..versions_message | ||
295 | end | ||
296 | else | ||
297 | add = query.name.." is not available for any Lua versions." | ||
298 | end | ||
299 | else | ||
300 | add = "To check if it is available for other Lua versions, use --check-lua-versions." | ||
301 | end | ||
302 | err = err .. "\n" .. add | ||
303 | end | ||
304 | |||
305 | return nil, err | ||
306 | end | ||
307 | |||
308 | --- Print a list of rocks/rockspecs on standard output. | ||
309 | -- @param result_tree table: A result tree. | ||
310 | -- @param porcelain boolean or nil: A flag to force machine-friendly output. | ||
311 | function search.print_result_tree(result_tree, porcelain) | ||
312 | assert(type(result_tree) == "table") | ||
313 | assert(type(porcelain) == "boolean" or not porcelain) | ||
314 | |||
315 | if porcelain then | ||
316 | for package, versions in util.sortedpairs(result_tree) do | ||
317 | for version, repos in util.sortedpairs(versions, vers.compare_versions) do | ||
318 | for _, repo in ipairs(repos) do | ||
319 | local nrepo = dir.normalize(repo.repo) | ||
320 | util.printout(package, version, repo.arch, nrepo, repo.namespace) | ||
321 | end | ||
322 | end | ||
323 | end | ||
324 | return | ||
325 | end | ||
326 | |||
327 | for package, versions in util.sortedpairs(result_tree) do | ||
328 | local namespaces = {} | ||
329 | for version, repos in util.sortedpairs(versions, vers.compare_versions) do | ||
330 | for _, repo in ipairs(repos) do | ||
331 | local key = repo.namespace or "" | ||
332 | local list = namespaces[key] or {} | ||
333 | namespaces[key] = list | ||
334 | |||
335 | repo.repo = dir.normalize(repo.repo) | ||
336 | table.insert(list, " "..version.." ("..repo.arch..") - "..path.root_dir(repo.repo)) | ||
337 | end | ||
338 | end | ||
339 | for key, list in util.sortedpairs(namespaces) do | ||
340 | util.printout(key == "" and package or key .. "/" .. package) | ||
341 | for _, line in ipairs(list) do | ||
342 | util.printout(line) | ||
343 | end | ||
344 | util.printout() | ||
345 | end | ||
346 | end | ||
347 | end | ||
348 | |||
349 | function search.pick_installed_rock(query, given_tree) | ||
350 | assert(query:type() == "query") | ||
351 | |||
352 | local result_tree = {} | ||
353 | local tree_map = {} | ||
354 | local trees = cfg.rocks_trees | ||
355 | if given_tree then | ||
356 | trees = { given_tree } | ||
357 | end | ||
358 | for _, tree in ipairs(trees) do | ||
359 | local rocks_dir = path.rocks_dir(tree) | ||
360 | tree_map[rocks_dir] = tree | ||
361 | search.local_manifest_search(result_tree, rocks_dir, query) | ||
362 | end | ||
363 | if not next(result_tree) then | ||
364 | return nil, "cannot find package "..tostring(query).."\nUse 'list' to find installed rocks." | ||
365 | end | ||
366 | |||
367 | if not result_tree[query.name] and next(result_tree, next(result_tree)) then | ||
368 | local out = { "multiple installed packages match the name '"..tostring(query).."':\n\n" } | ||
369 | for name, _ in util.sortedpairs(result_tree) do | ||
370 | table.insert(out, " " .. name .. "\n") | ||
371 | end | ||
372 | table.insert(out, "\nPlease specify a single rock.\n") | ||
373 | return nil, table.concat(out) | ||
374 | end | ||
375 | |||
376 | local repo_url | ||
377 | |||
378 | local name, versions | ||
379 | if result_tree[query.name] then | ||
380 | name, versions = query.name, result_tree[query.name] | ||
381 | else | ||
382 | name, versions = util.sortedpairs(result_tree)() | ||
383 | end | ||
384 | |||
385 | local version, repositories = util.sortedpairs(versions, vers.compare_versions)() | ||
386 | for _, rp in ipairs(repositories) do repo_url = rp.repo end | ||
387 | |||
388 | local repo = tree_map[repo_url] | ||
389 | return name, version, repo, repo_url | ||
390 | end | ||
391 | |||
392 | return search | ||
393 | |||
diff --git a/src/luarocks/search.lua b/src/luarocks/search.lua index 180f8f45..1404e303 100644 --- a/src/luarocks/search.lua +++ b/src/luarocks/search.lua | |||
@@ -1,4 +1,4 @@ | |||
1 | local search = {} | 1 | local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table; local search = {} |
2 | 2 | ||
3 | local dir = require("luarocks.dir") | 3 | local dir = require("luarocks.dir") |
4 | local path = require("luarocks.path") | 4 | local path = require("luarocks.path") |
@@ -9,14 +9,25 @@ local util = require("luarocks.util") | |||
9 | local queries = require("luarocks.queries") | 9 | local queries = require("luarocks.queries") |
10 | local results = require("luarocks.results") | 10 | local results = require("luarocks.results") |
11 | 11 | ||
12 | --- Store a search result (a rock or rockspec) in the result tree. | 12 | |
13 | -- @param result_tree table: The result tree, where keys are package names and | 13 | |
14 | -- values are tables matching version strings to arrays of | 14 | |
15 | -- tables with fields "arch" and "repo". | 15 | |
16 | -- @param result table: A result. | 16 | |
17 | |||
18 | |||
19 | |||
20 | |||
21 | |||
22 | |||
23 | |||
24 | |||
25 | |||
26 | |||
27 | |||
28 | |||
17 | function search.store_result(result_tree, result) | 29 | function search.store_result(result_tree, result) |
18 | assert(type(result_tree) == "table") | 30 | assert(type(result_tree) == "table") |
19 | assert(result:type() == "result") | ||
20 | 31 | ||
21 | local name = result.name | 32 | local name = result.name |
22 | local version = result.version | 33 | local version = result.version |
@@ -30,37 +41,32 @@ function search.store_result(result_tree, result) | |||
30 | }) | 41 | }) |
31 | end | 42 | end |
32 | 43 | ||
33 | --- Store a match in a result tree if version matches query. | 44 | |
34 | -- Name, version, arch and repository path are stored in a given | 45 | |
35 | -- table, optionally checking if version and arch (if given) match | 46 | |
36 | -- a query. | 47 | |
37 | -- @param result_tree table: The result tree, where keys are package names and | 48 | |
38 | -- values are tables matching version strings to arrays of | 49 | |
39 | -- tables with fields "arch" and "repo". | 50 | |
40 | -- @param result table: a result object. | 51 | |
41 | -- @param query table: a query object. | 52 | |
42 | local function store_if_match(result_tree, result, query) | 53 | local function store_if_match(result_tree, result, query) |
43 | assert(result:type() == "result") | ||
44 | assert(query:type() == "query") | ||
45 | 54 | ||
46 | if result:satisfies(query) then | 55 | if result:satisfies(query) then |
47 | search.store_result(result_tree, result) | 56 | search.store_result(result_tree, result) |
48 | end | 57 | end |
49 | end | 58 | end |
50 | 59 | ||
51 | --- Perform search on a local repository. | 60 | |
52 | -- @param repo string: The pathname of the local repository. | 61 | |
53 | -- @param query table: a query object. | 62 | |
54 | -- @param result_tree table or nil: If given, this table will store the | 63 | |
55 | -- result tree; if not given, a new table will be created. | 64 | |
56 | -- @return table: The result tree, where keys are package names and | 65 | |
57 | -- values are tables matching version strings to arrays of | 66 | |
58 | -- tables with fields "arch" and "repo". | 67 | |
59 | -- If a table was given in the "result_tree" parameter, that is the result value. | 68 | |
60 | function search.disk_search(repo, query, result_tree) | 69 | function search.disk_search(repo, query, result_tree) |
61 | assert(type(repo) == "string") | ||
62 | assert(query:type() == "query") | ||
63 | assert(type(result_tree) == "table" or not result_tree) | ||
64 | 70 | ||
65 | local fs = require("luarocks.fs") | 71 | local fs = require("luarocks.fs") |
66 | 72 | ||
@@ -88,22 +94,19 @@ function search.disk_search(repo, query, result_tree) | |||
88 | return result_tree | 94 | return result_tree |
89 | end | 95 | end |
90 | 96 | ||
91 | --- Perform search on a rocks server or tree. | 97 | |
92 | -- @param result_tree table: The result tree, where keys are package names and | 98 | |
93 | -- values are tables matching version strings to arrays of | 99 | |
94 | -- tables with fields "arch" and "repo". | 100 | |
95 | -- @param repo string: The URL of a rocks server or | 101 | |
96 | -- the pathname of a rocks tree (as returned by path.rocks_dir()). | 102 | |
97 | -- @param query table: a query object. | 103 | |
98 | -- @param lua_version string: Lua version in "5.x" format, defaults to installed version. | 104 | |
99 | -- @param is_local boolean | 105 | |
100 | -- @return true or, in case of errors, nil, an error message and an optional error code. | 106 | |
101 | local function manifest_search(result_tree, repo, query, lua_version, is_local) | 107 | local function manifest_search(result_tree, repo, query, lua_version, is_local) |
102 | assert(type(result_tree) == "table") | ||
103 | assert(type(repo) == "string") | ||
104 | assert(query:type() == "query") | ||
105 | 108 | ||
106 | -- FIXME do not add this in local repos | 109 | |
107 | if (not is_local) and query.namespace then | 110 | if (not is_local) and query.namespace then |
108 | repo = repo .. "/manifests/" .. query.namespace | 111 | repo = repo .. "/manifests/" .. query.namespace |
109 | end | 112 | end |
@@ -132,19 +135,21 @@ function search.local_manifest_search(result_tree, repo, query, lua_version) | |||
132 | return manifest_search(result_tree, repo, query, lua_version, true) | 135 | return manifest_search(result_tree, repo, query, lua_version, true) |
133 | end | 136 | end |
134 | 137 | ||
135 | --- Search on all configured rocks servers. | 138 | |
136 | -- @param query table: a query object. | 139 | |
137 | -- @param lua_version string: Lua version in "5.x" format, defaults to installed version. | 140 | |
138 | -- @return table: A table where keys are package names | 141 | |
139 | -- and values are tables matching version strings to arrays of | 142 | |
140 | -- tables with fields "arch" and "repo". | 143 | |
141 | function search.search_repos(query, lua_version) | 144 | function search.search_repos(query, lua_version) |
142 | assert(query:type() == "query") | ||
143 | 145 | ||
144 | local result_tree = {} | 146 | local result_tree = {} |
145 | for _, repo in ipairs(cfg.rocks_servers) do | 147 | local repo = {} |
146 | if type(repo) == "string" then | 148 | for _, repostr in ipairs(cfg.rocks_servers) do |
147 | repo = { repo } | 149 | if type(repostr) == "string" then |
150 | repo = { repostr } | ||
151 | else | ||
152 | repo = repostr | ||
148 | end | 153 | end |
149 | for _, mirror in ipairs(repo) do | 154 | for _, mirror in ipairs(repo) do |
150 | if not cfg.disabled_servers[mirror] then | 155 | if not cfg.disabled_servers[mirror] then |
@@ -159,7 +164,7 @@ function search.search_repos(query, lua_version) | |||
159 | if ok then | 164 | if ok then |
160 | break | 165 | break |
161 | else | 166 | else |
162 | util.warning("Failed searching manifest: "..err) | 167 | util.warning("Failed searching manifest: " .. err) |
163 | if errcode == "downloader" then | 168 | if errcode == "downloader" then |
164 | break | 169 | break |
165 | end | 170 | end |
@@ -167,7 +172,7 @@ function search.search_repos(query, lua_version) | |||
167 | end | 172 | end |
168 | end | 173 | end |
169 | end | 174 | end |
170 | -- search through rocks in rocks_provided | 175 | |
171 | local provided_repo = "provided by VM or rocks_provided" | 176 | local provided_repo = "provided by VM or rocks_provided" |
172 | for name, version in pairs(util.get_rocks_provided()) do | 177 | for name, version in pairs(util.get_rocks_provided()) do |
173 | local result = results.new(name, version, provided_repo, "installed") | 178 | local result = results.new(name, version, provided_repo, "installed") |
@@ -176,15 +181,14 @@ function search.search_repos(query, lua_version) | |||
176 | return result_tree | 181 | return result_tree |
177 | end | 182 | end |
178 | 183 | ||
179 | --- Get the URL for the latest in a set of versions. | 184 | |
180 | -- @param name string: The package name to be used in the URL. | 185 | |
181 | -- @param versions table: An array of version informations, as stored | 186 | |
182 | -- in search result trees. | 187 | |
183 | -- @return string or nil: the URL for the latest version if one could | 188 | |
184 | -- be picked, or nil. | 189 | |
185 | local function pick_latest_version(name, versions) | 190 | local function pick_latest_version(name, versions) |
186 | assert(type(name) == "string" and not name:match("/")) | 191 | assert(not name:match("/")) |
187 | assert(type(versions) == "table") | ||
188 | 192 | ||
189 | local vtables = {} | 193 | local vtables = {} |
190 | for v, _ in pairs(versions) do | 194 | for v, _ in pairs(versions) do |
@@ -196,8 +200,8 @@ local function pick_latest_version(name, versions) | |||
196 | if items then | 200 | if items then |
197 | local pick = 1 | 201 | local pick = 1 |
198 | for i, item in ipairs(items) do | 202 | for i, item in ipairs(items) do |
199 | if (item.arch == 'src' and items[pick].arch == 'rockspec') | 203 | if (item.arch == 'src' and items[pick].arch == 'rockspec') or |
200 | or (item.arch ~= 'src' and item.arch ~= 'rockspec') then | 204 | (item.arch ~= 'src' and item.arch ~= 'rockspec') then |
201 | pick = i | 205 | pick = i |
202 | end | 206 | end |
203 | end | 207 | end |
@@ -206,11 +210,10 @@ local function pick_latest_version(name, versions) | |||
206 | return nil | 210 | return nil |
207 | end | 211 | end |
208 | 212 | ||
209 | -- Find out which other Lua versions provide rock versions matching a query, | 213 | |
210 | -- @param query table: a query object. | 214 | |
211 | -- @return table: array of Lua versions supported, in "5.x" format. | 215 | |
212 | local function supported_lua_versions(query) | 216 | local function supported_lua_versions(query) |
213 | assert(query:type() == "query") | ||
214 | local result_tree = {} | 217 | local result_tree = {} |
215 | 218 | ||
216 | for lua_version in util.lua_versions() do | 219 | for lua_version in util.lua_versions() do |
@@ -225,43 +228,33 @@ local function supported_lua_versions(query) | |||
225 | return result_tree | 228 | return result_tree |
226 | end | 229 | end |
227 | 230 | ||
228 | --- Attempt to get a single URL for a given search for a rock. | 231 | |
229 | -- @param query table: a query object. | 232 | |
230 | -- @return string or (nil, string, string): URL for latest matching version | 233 | |
231 | -- of the rock if it was found, or nil followed by an error message | 234 | |
232 | -- and an error code. | 235 | |
233 | function search.find_suitable_rock(query) | 236 | function search.find_suitable_rock(query) |
234 | assert(query:type() == "query") | ||
235 | 237 | ||
236 | local rocks_provided = util.get_rocks_provided() | 238 | local rocks_provided = util.get_rocks_provided() |
237 | 239 | ||
238 | if rocks_provided[query.name] ~= nil then | 240 | if rocks_provided[query.name] ~= nil then |
239 | -- Do not install versions listed in rocks_provided. | 241 | |
240 | return nil, "Rock "..query.name.." "..rocks_provided[query.name].. | 242 | return nil, "Rock " .. query.name .. " " .. rocks_provided[query.name] .. |
241 | " is already provided by VM or via 'rocks_provided' in the config file.", "provided" | 243 | " is already provided by VM or via 'rocks_provided' in the config file.", "provided" |
242 | end | 244 | end |
243 | 245 | ||
244 | local result_tree = search.search_repos(query) | 246 | local result_tree = search.search_repos(query) |
245 | local first_rock = next(result_tree) | 247 | local first_rock = next(result_tree) |
246 | if not first_rock then | 248 | if not first_rock then |
247 | return nil, "No results matching query were found for Lua " .. cfg.lua_version .. ".", "notfound" | 249 | return nil, "No results matching query were found for Lua " .. cfg.lua_version .. ".", "notfound" |
248 | elseif next(result_tree, first_rock) then | 250 | elseif next(result_tree, first_rock) ~= nil then |
249 | -- Shouldn't happen as query must match only one package. | 251 | |
250 | return nil, "Several rocks matched query.", "manyfound" | 252 | return nil, "Several rocks matched query.", "manyfound" |
251 | else | 253 | else |
252 | return pick_latest_version(query.name, result_tree[first_rock]) | 254 | return pick_latest_version(query.name, result_tree[first_rock]) |
253 | end | 255 | end |
254 | end | 256 | end |
255 | 257 | ||
256 | function search.find_src_or_rockspec(name, namespace, version, check_lua_versions) | ||
257 | local query = queries.new(name, namespace, version, false, "src|rockspec") | ||
258 | local url, err = search.find_rock_checking_lua_versions(query, check_lua_versions) | ||
259 | if not url then | ||
260 | return nil, "Could not find a result named "..tostring(query)..": "..err | ||
261 | end | ||
262 | return url | ||
263 | end | ||
264 | |||
265 | function search.find_rock_checking_lua_versions(query, check_lua_versions) | 258 | function search.find_rock_checking_lua_versions(query, check_lua_versions) |
266 | local url, err, errcode = search.find_suitable_rock(query) | 259 | local url, err, errcode = search.find_suitable_rock(query) |
267 | if url then | 260 | if url then |
@@ -274,27 +267,34 @@ function search.find_rock_checking_lua_versions(query, check_lua_versions) | |||
274 | util.printout(query.name .. " not found for Lua " .. cfg.lua_version .. ".") | 267 | util.printout(query.name .. " not found for Lua " .. cfg.lua_version .. ".") |
275 | util.printout("Checking if available for other Lua versions...") | 268 | util.printout("Checking if available for other Lua versions...") |
276 | 269 | ||
277 | -- Check if constraints are satisfiable with other Lua versions. | 270 | |
278 | local lua_versions = supported_lua_versions(query) | 271 | local lua_versions = supported_lua_versions(query) |
279 | 272 | ||
280 | if #lua_versions ~= 0 then | 273 | if #lua_versions ~= 0 then |
281 | -- Build a nice message in "only Lua 5.x and 5.y but not 5.z." format | 274 | |
282 | for i, lua_version in ipairs(lua_versions) do | 275 | for i, lua_version in ipairs(lua_versions) do |
283 | lua_versions[i] = "Lua "..lua_version | 276 | lua_versions[i] = "Lua " .. lua_version |
284 | end | 277 | end |
285 | 278 | ||
286 | local versions_message = "only "..table.concat(lua_versions, " and ").. | 279 | local versions_message = "only " .. table.concat(lua_versions, " and ") .. |
287 | " but not Lua "..cfg.lua_version.."." | 280 | " but not Lua " .. cfg.lua_version .. "." |
288 | 281 | ||
289 | if #query.constraints == 0 then | 282 | if #query.constraints == 0 then |
290 | add = query.name.." supports "..versions_message | 283 | add = query.name .. " supports " .. versions_message |
291 | elseif #query.constraints == 1 and query.constraints[1].op == "==" then | 284 | elseif #query.constraints == 1 and query.constraints[1].op == "==" then |
292 | add = query.name.." "..query.constraints[1].version.string.." supports "..versions_message | 285 | local queryversionstr |
286 | local queryversion = query.constraints[1].version | ||
287 | if type(queryversion) == "table" then | ||
288 | queryversionstr = queryversion.string | ||
289 | else | ||
290 | queryversionstr = queryversion | ||
291 | end | ||
292 | add = query.name .. " " .. queryversionstr .. " supports " .. versions_message | ||
293 | else | 293 | else |
294 | add = "Matching "..query.name.." versions support "..versions_message | 294 | add = "Matching " .. query.name .. " versions support " .. versions_message |
295 | end | 295 | end |
296 | else | 296 | else |
297 | add = query.name.." is not available for any Lua versions." | 297 | add = query.name .. " is not available for any Lua versions." |
298 | end | 298 | end |
299 | else | 299 | else |
300 | add = "To check if it is available for other Lua versions, use --check-lua-versions." | 300 | add = "To check if it is available for other Lua versions, use --check-lua-versions." |
@@ -305,26 +305,33 @@ function search.find_rock_checking_lua_versions(query, check_lua_versions) | |||
305 | return nil, err | 305 | return nil, err |
306 | end | 306 | end |
307 | 307 | ||
308 | --- Print a list of rocks/rockspecs on standard output. | 308 | function search.find_src_or_rockspec(name, namespace, version, check_lua_versions) |
309 | -- @param result_tree table: A result tree. | 309 | local query = queries.new(name, namespace, version, false, "src|rockspec") |
310 | -- @param porcelain boolean or nil: A flag to force machine-friendly output. | 310 | local url, err = search.find_rock_checking_lua_versions(query, check_lua_versions) |
311 | if not url then | ||
312 | return nil, "Could not find a result named " .. tostring(query) .. ": " .. err | ||
313 | end | ||
314 | return url | ||
315 | end | ||
316 | |||
317 | |||
318 | |||
319 | |||
311 | function search.print_result_tree(result_tree, porcelain) | 320 | function search.print_result_tree(result_tree, porcelain) |
312 | assert(type(result_tree) == "table") | ||
313 | assert(type(porcelain) == "boolean" or not porcelain) | ||
314 | 321 | ||
315 | if porcelain then | 322 | if porcelain then |
316 | for package, versions in util.sortedpairs(result_tree) do | 323 | for packagestr, versions in util.sortedpairs(result_tree) do |
317 | for version, repos in util.sortedpairs(versions, vers.compare_versions) do | 324 | for version, repos in util.sortedpairs(versions, vers.compare_versions) do |
318 | for _, repo in ipairs(repos) do | 325 | for _, repo in ipairs(repos) do |
319 | local nrepo = dir.normalize(repo.repo) | 326 | local nrepo = dir.normalize(repo.repo) |
320 | util.printout(package, version, repo.arch, nrepo, repo.namespace) | 327 | util.printout(packagestr, version, repo.arch, nrepo, repo.namespace) |
321 | end | 328 | end |
322 | end | 329 | end |
323 | end | 330 | end |
324 | return | 331 | return |
325 | end | 332 | end |
326 | 333 | ||
327 | for package, versions in util.sortedpairs(result_tree) do | 334 | for packagestr, versions in util.sortedpairs(result_tree) do |
328 | local namespaces = {} | 335 | local namespaces = {} |
329 | for version, repos in util.sortedpairs(versions, vers.compare_versions) do | 336 | for version, repos in util.sortedpairs(versions, vers.compare_versions) do |
330 | for _, repo in ipairs(repos) do | 337 | for _, repo in ipairs(repos) do |
@@ -333,11 +340,11 @@ function search.print_result_tree(result_tree, porcelain) | |||
333 | namespaces[key] = list | 340 | namespaces[key] = list |
334 | 341 | ||
335 | repo.repo = dir.normalize(repo.repo) | 342 | repo.repo = dir.normalize(repo.repo) |
336 | table.insert(list, " "..version.." ("..repo.arch..") - "..path.root_dir(repo.repo)) | 343 | table.insert(list, " " .. version .. " (" .. repo.arch .. ") - " .. path.root_dir(repo.repo)) |
337 | end | 344 | end |
338 | end | 345 | end |
339 | for key, list in util.sortedpairs(namespaces) do | 346 | for key, list in util.sortedpairs(namespaces) do |
340 | util.printout(key == "" and package or key .. "/" .. package) | 347 | util.printout(key == "" and packagestr or key .. "/" .. packagestr) |
341 | for _, line in ipairs(list) do | 348 | for _, line in ipairs(list) do |
342 | util.printout(line) | 349 | util.printout(line) |
343 | end | 350 | end |
@@ -347,7 +354,6 @@ function search.print_result_tree(result_tree, porcelain) | |||
347 | end | 354 | end |
348 | 355 | ||
349 | function search.pick_installed_rock(query, given_tree) | 356 | function search.pick_installed_rock(query, given_tree) |
350 | assert(query:type() == "query") | ||
351 | 357 | ||
352 | local result_tree = {} | 358 | local result_tree = {} |
353 | local tree_map = {} | 359 | local tree_map = {} |
@@ -361,11 +367,11 @@ function search.pick_installed_rock(query, given_tree) | |||
361 | search.local_manifest_search(result_tree, rocks_dir, query) | 367 | search.local_manifest_search(result_tree, rocks_dir, query) |
362 | end | 368 | end |
363 | if not next(result_tree) then | 369 | if not next(result_tree) then |
364 | return nil, "cannot find package "..tostring(query).."\nUse 'list' to find installed rocks." | 370 | return nil, "cannot find package " .. tostring(query) .. "\nUse 'list' to find installed rocks." |
365 | end | 371 | end |
366 | 372 | ||
367 | if not result_tree[query.name] and next(result_tree, next(result_tree)) then | 373 | if not result_tree[query.name] and next(result_tree, next(result_tree)) ~= nil then |
368 | local out = { "multiple installed packages match the name '"..tostring(query).."':\n\n" } | 374 | local out = { "multiple installed packages match the name '" .. tostring(query) .. "':\n\n" } |
369 | for name, _ in util.sortedpairs(result_tree) do | 375 | for name, _ in util.sortedpairs(result_tree) do |
370 | table.insert(out, " " .. name .. "\n") | 376 | table.insert(out, " " .. name .. "\n") |
371 | end | 377 | end |
@@ -390,4 +396,3 @@ function search.pick_installed_rock(query, given_tree) | |||
390 | end | 396 | end |
391 | 397 | ||
392 | return search | 398 | return search |
393 | |||
diff --git a/src/luarocks/search.tl b/src/luarocks/search.tl new file mode 100644 index 00000000..7d8a9055 --- /dev/null +++ b/src/luarocks/search.tl | |||
@@ -0,0 +1,399 @@ | |||
1 | local search = {} | ||
2 | |||
3 | local dir = require("luarocks.dir") | ||
4 | local path = require("luarocks.path") | ||
5 | local manif = require("luarocks.manif") | ||
6 | local vers = require("luarocks.core.vers") | ||
7 | local cfg = require("luarocks.core.cfg") | ||
8 | local util = require("luarocks.util") | ||
9 | local queries = require("luarocks.queries") | ||
10 | local results = require("luarocks.results") | ||
11 | |||
12 | local type r = require("luarocks.core.types.result") | ||
13 | local type Result = r.Result | ||
14 | |||
15 | local type q = require("luarocks.core.types.query") | ||
16 | local type Query = q.Query | ||
17 | |||
18 | local type v = require("luarocks.core.types.version") | ||
19 | local type Version = v.Version | ||
20 | |||
21 | local type t = require("luarocks.core.types.tree") | ||
22 | local type Tree = t.Tree | ||
23 | |||
24 | --- Store a search result (a rock or rockspec) in the result tree. | ||
25 | -- @param result_tree table: The result tree, where keys are package names and | ||
26 | -- values are tables matching version strings to arrays of | ||
27 | -- tables with fields "arch" and "repo". | ||
28 | -- @param result table: A result. | ||
29 | function search.store_result(result_tree: {string: {string: {Result}}}, result: Result) | ||
30 | assert(type(result_tree) == "table") | ||
31 | |||
32 | local name = result.name | ||
33 | local version = result.version | ||
34 | |||
35 | if not result_tree[name] then result_tree[name] = {} end | ||
36 | if not result_tree[name][version] then result_tree[name][version] = {} end | ||
37 | table.insert(result_tree[name][version], { | ||
38 | arch = result.arch, | ||
39 | repo = result.repo, | ||
40 | namespace = result.namespace, | ||
41 | } as Result) | ||
42 | end | ||
43 | |||
44 | --- Store a match in a result tree if version matches query. | ||
45 | -- Name, version, arch and repository path are stored in a given | ||
46 | -- table, optionally checking if version and arch (if given) match | ||
47 | -- a query. | ||
48 | -- @param result_tree table: The result tree, where keys are package names and | ||
49 | -- values are tables matching version strings to arrays of | ||
50 | -- tables with fields "arch" and "repo". | ||
51 | -- @param result table: a result object. | ||
52 | -- @param query table: a query object. | ||
53 | local function store_if_match(result_tree: {string: {string: {Result}}}, result: Result, query: Query) | ||
54 | |||
55 | if result:satisfies(query) then | ||
56 | search.store_result(result_tree, result) | ||
57 | end | ||
58 | end | ||
59 | |||
60 | --- Perform search on a local repository. | ||
61 | -- @param repo string: The pathname of the local repository. | ||
62 | -- @param query table: a query object. | ||
63 | -- @param result_tree table or nil: If given, this table will store the | ||
64 | -- result tree; if not given, a new table will be created. | ||
65 | -- @return table: The result tree, where keys are package names and | ||
66 | -- values are tables matching version strings to arrays of | ||
67 | -- tables with fields "arch" and "repo". | ||
68 | -- If a table was given in the "result_tree" parameter, that is the result value. | ||
69 | function search.disk_search(repo: string, query: Query, result_tree: {string: {string: {Result}}}): {string: {string: {Result}}} | ||
70 | |||
71 | local fs = require("luarocks.fs") | ||
72 | |||
73 | if not result_tree then | ||
74 | result_tree = {} | ||
75 | end | ||
76 | |||
77 | for name in fs.dir(repo) do | ||
78 | local pathname = dir.path(repo, name) | ||
79 | local rname, rversion, rarch = path.parse_name(name) | ||
80 | |||
81 | if rname and (pathname:match(".rockspec$") or pathname:match(".rock$")) then | ||
82 | local result = results.new(rname, rversion, repo, rarch) | ||
83 | store_if_match(result_tree, result, query) | ||
84 | elseif fs.is_dir(pathname) then | ||
85 | for version in fs.dir(pathname) do | ||
86 | if version:match("-%d+$") then | ||
87 | local namespace = path.read_namespace(name, version, repo) | ||
88 | local result = results.new(name, version, repo, "installed", namespace) | ||
89 | store_if_match(result_tree, result, query) | ||
90 | end | ||
91 | end | ||
92 | end | ||
93 | end | ||
94 | return result_tree | ||
95 | end | ||
96 | |||
97 | --- Perform search on a rocks server or tree. | ||
98 | -- @param result_tree table: The result tree, where keys are package names and | ||
99 | -- values are tables matching version strings to arrays of | ||
100 | -- tables with fields "arch" and "repo". | ||
101 | -- @param repo string: The URL of a rocks server or | ||
102 | -- the pathname of a rocks tree (as returned by path.rocks_dir()). | ||
103 | -- @param query table: a query object. | ||
104 | -- @param lua_version string: Lua version in "5.x" format, defaults to installed version. | ||
105 | -- @param is_local boolean | ||
106 | -- @return true or, in case of errors, nil, an error message and an optional error code. | ||
107 | local function manifest_search(result_tree: {string: {string: {Result}}}, repo: string, query: Query, lua_version: string, is_local: boolean): boolean, string, string | ||
108 | |||
109 | -- FIXME do not add this in local repos | ||
110 | if (not is_local) and query.namespace then | ||
111 | repo = repo .. "/manifests/" .. query.namespace | ||
112 | end | ||
113 | |||
114 | local manifest, err, errcode = manif.load_manifest(repo, lua_version, not is_local) | ||
115 | if not manifest then | ||
116 | return nil, err, errcode | ||
117 | end | ||
118 | for name, versions in pairs(manifest.repository) do | ||
119 | for version, items in pairs(versions) do | ||
120 | local namespace = is_local and path.read_namespace(name, version, repo) or query.namespace | ||
121 | for _, item in ipairs(items) do | ||
122 | local result = results.new(name, version, repo, item.arch, namespace) | ||
123 | store_if_match(result_tree, result, query) | ||
124 | end | ||
125 | end | ||
126 | end | ||
127 | return true | ||
128 | end | ||
129 | |||
130 | local function remote_manifest_search(result_tree: {string: {string: {Result}}}, repo: string, query: Query, lua_version?: string): boolean, string, string | ||
131 | return manifest_search(result_tree, repo, query, lua_version, false) | ||
132 | end | ||
133 | |||
134 | function search.local_manifest_search(result_tree: {string: {string: {Result}}}, repo: string, query: Query, lua_version?: string): boolean, string, string | ||
135 | return manifest_search(result_tree, repo, query, lua_version, true) | ||
136 | end | ||
137 | |||
138 | --- Search on all configured rocks servers. | ||
139 | -- @param query table: a query object. | ||
140 | -- @param lua_version string: Lua version in "5.x" format, defaults to installed version. | ||
141 | -- @return table: A table where keys are package names | ||
142 | -- and values are tables matching version strings to arrays of | ||
143 | -- tables with fields "arch" and "repo". | ||
144 | function search.search_repos(query: Query, lua_version?: string): {string : {string : {Result}}} | ||
145 | |||
146 | local result_tree = {} | ||
147 | local repo = {} | ||
148 | for _, repostr in ipairs(cfg.rocks_servers) do | ||
149 | if repostr is string then | ||
150 | repo = { repostr } | ||
151 | else | ||
152 | repo = repostr | ||
153 | end | ||
154 | for _, mirror in ipairs(repo) do | ||
155 | if not cfg.disabled_servers[mirror] then | ||
156 | local protocol, pathname = dir.split_url(mirror) | ||
157 | if protocol == "file" then | ||
158 | mirror = pathname | ||
159 | end | ||
160 | local ok, err, errcode = remote_manifest_search(result_tree, mirror, query, lua_version) | ||
161 | if errcode == "network" then | ||
162 | cfg.disabled_servers[mirror] = true | ||
163 | end | ||
164 | if ok then | ||
165 | break | ||
166 | else | ||
167 | util.warning("Failed searching manifest: "..err) | ||
168 | if errcode == "downloader" then | ||
169 | break | ||
170 | end | ||
171 | end | ||
172 | end | ||
173 | end | ||
174 | end | ||
175 | -- search through rocks in rocks_provided | ||
176 | local provided_repo = "provided by VM or rocks_provided" | ||
177 | for name, version in pairs(util.get_rocks_provided()) do | ||
178 | local result = results.new(name, version, provided_repo, "installed") | ||
179 | store_if_match(result_tree, result, query) | ||
180 | end | ||
181 | return result_tree | ||
182 | end | ||
183 | |||
184 | --- Get the URL for the latest in a set of versions. | ||
185 | -- @param name string: The package name to be used in the URL. | ||
186 | -- @param versions table: An array of version informations, as stored | ||
187 | -- in search result trees. | ||
188 | -- @return string or nil: the URL for the latest version if one could | ||
189 | -- be picked, or nil. | ||
190 | local function pick_latest_version(name: string, versions: {string: {Result}}): string | ||
191 | assert(not name:match("/")) | ||
192 | |||
193 | local vtables = {} | ||
194 | for v, _ in pairs(versions) do | ||
195 | table.insert(vtables, vers.parse_version(v)) | ||
196 | end | ||
197 | table.sort(vtables) | ||
198 | local version = vtables[#vtables].string | ||
199 | local items = versions[version] | ||
200 | if items then | ||
201 | local pick = 1 | ||
202 | for i, item in ipairs(items) do | ||
203 | if (item.arch == 'src' and items[pick].arch == 'rockspec') | ||
204 | or (item.arch ~= 'src' and item.arch ~= 'rockspec') then | ||
205 | pick = i | ||
206 | end | ||
207 | end | ||
208 | return path.make_url(items[pick].repo, name, version, items[pick].arch) | ||
209 | end | ||
210 | return nil | ||
211 | end | ||
212 | |||
213 | -- Find out which other Lua versions provide rock versions matching a query, | ||
214 | -- @param query table: a query object. | ||
215 | -- @return table: array of Lua versions supported, in "5.x" format. | ||
216 | local function supported_lua_versions(query: Query): {string} | ||
217 | local result_tree = {} | ||
218 | |||
219 | for lua_version in util.lua_versions() do | ||
220 | if lua_version ~= cfg.lua_version then | ||
221 | util.printout("Checking for Lua " .. lua_version .. "...") | ||
222 | if search.search_repos(query, lua_version)[query.name] then | ||
223 | table.insert(result_tree, lua_version) | ||
224 | end | ||
225 | end | ||
226 | end | ||
227 | |||
228 | return result_tree | ||
229 | end | ||
230 | |||
231 | --- Attempt to get a single URL for a given search for a rock. | ||
232 | -- @param query table: a query object. | ||
233 | -- @return string or (nil, string, string): URL for latest matching version | ||
234 | -- of the rock if it was found, or nil followed by an error message | ||
235 | -- and an error code. | ||
236 | function search.find_suitable_rock(query: Query): string, string, string | ||
237 | |||
238 | local rocks_provided = util.get_rocks_provided() | ||
239 | |||
240 | if rocks_provided[query.name] ~= nil then | ||
241 | -- Do not install versions listed in rocks_provided. | ||
242 | return nil, "Rock "..query.name.." "..rocks_provided[query.name].. | ||
243 | " is already provided by VM or via 'rocks_provided' in the config file.", "provided" | ||
244 | end | ||
245 | |||
246 | local result_tree = search.search_repos(query) | ||
247 | local first_rock = next(result_tree) | ||
248 | if not first_rock then | ||
249 | return nil, "No results matching query were found for Lua " .. cfg.lua_version .. ".", "notfound" | ||
250 | elseif next(result_tree, first_rock) ~= nil then | ||
251 | -- Shouldn't happen as query must match only one package. | ||
252 | return nil, "Several rocks matched query.", "manyfound" | ||
253 | else | ||
254 | return pick_latest_version(query.name, result_tree[first_rock]) | ||
255 | end | ||
256 | end | ||
257 | |||
258 | function search.find_rock_checking_lua_versions(query: Query, check_lua_versions): string, string | ||
259 | local url, err, errcode = search.find_suitable_rock(query) | ||
260 | if url then | ||
261 | return url | ||
262 | end | ||
263 | |||
264 | if errcode == "notfound" then | ||
265 | local add: string | ||
266 | if check_lua_versions then | ||
267 | util.printout(query.name .. " not found for Lua " .. cfg.lua_version .. ".") | ||
268 | util.printout("Checking if available for other Lua versions...") | ||
269 | |||
270 | -- Check if constraints are satisfiable with other Lua versions. | ||
271 | local lua_versions = supported_lua_versions(query) | ||
272 | |||
273 | if #lua_versions ~= 0 then | ||
274 | -- Build a nice message in "only Lua 5.x and 5.y but not 5.z." format | ||
275 | for i, lua_version in ipairs(lua_versions) do | ||
276 | lua_versions[i] = "Lua "..lua_version | ||
277 | end | ||
278 | |||
279 | local versions_message = "only "..table.concat(lua_versions, " and ").. | ||
280 | " but not Lua "..cfg.lua_version.."." | ||
281 | |||
282 | if #query.constraints == 0 then | ||
283 | add = query.name.." supports "..versions_message | ||
284 | elseif #query.constraints == 1 and query.constraints[1].op == "==" then | ||
285 | local queryversionstr: string | ||
286 | local queryversion = query.constraints[1].version | ||
287 | if queryversion is Version then | ||
288 | queryversionstr = queryversion.string | ||
289 | else | ||
290 | queryversionstr = queryversion | ||
291 | end | ||
292 | add = query.name.." "..queryversionstr.." supports "..versions_message | ||
293 | else | ||
294 | add = "Matching "..query.name.." versions support "..versions_message | ||
295 | end | ||
296 | else | ||
297 | add = query.name.." is not available for any Lua versions." | ||
298 | end | ||
299 | else | ||
300 | add = "To check if it is available for other Lua versions, use --check-lua-versions." | ||
301 | end | ||
302 | err = err .. "\n" .. add | ||
303 | end | ||
304 | |||
305 | return nil, err | ||
306 | end | ||
307 | |||
308 | function search.find_src_or_rockspec(name: string, namespace: string, version: string, check_lua_versions): string, string | ||
309 | local query = queries.new(name, namespace, version, false, "src|rockspec") | ||
310 | local url, err = search.find_rock_checking_lua_versions(query, check_lua_versions) | ||
311 | if not url then | ||
312 | return nil, "Could not find a result named "..tostring(query)..": "..err | ||
313 | end | ||
314 | return url | ||
315 | end | ||
316 | |||
317 | --- Print a list of rocks/rockspecs on standard output. | ||
318 | -- @param result_tree table: A result tree. | ||
319 | -- @param porcelain boolean or nil: A flag to force machine-friendly output. | ||
320 | function search.print_result_tree(result_tree: {string: {string: {Result}}}, porcelain?: boolean) | ||
321 | |||
322 | if porcelain then | ||
323 | for packagestr, versions in util.sortedpairs(result_tree) do | ||
324 | for version, repos in util.sortedpairs(versions, vers.compare_versions) do | ||
325 | for _, repo in ipairs(repos) do | ||
326 | local nrepo = dir.normalize(repo.repo) | ||
327 | util.printout(packagestr, version, repo.arch, nrepo, repo.namespace) | ||
328 | end | ||
329 | end | ||
330 | end | ||
331 | return | ||
332 | end | ||
333 | |||
334 | for packagestr, versions in util.sortedpairs(result_tree) do | ||
335 | local namespaces: {string: {string}} = {} | ||
336 | for version, repos in util.sortedpairs(versions, vers.compare_versions) do | ||
337 | for _, repo in ipairs(repos) do | ||
338 | local key = repo.namespace or "" | ||
339 | local list = namespaces[key] or {} | ||
340 | namespaces[key] = list | ||
341 | |||
342 | repo.repo = dir.normalize(repo.repo) | ||
343 | table.insert(list, " "..version.." ("..repo.arch..") - "..path.root_dir(repo.repo)) | ||
344 | end | ||
345 | end | ||
346 | for key, list in util.sortedpairs(namespaces) do | ||
347 | util.printout(key == "" and packagestr or key .. "/" .. packagestr) | ||
348 | for _, line in ipairs(list) do | ||
349 | util.printout(line) | ||
350 | end | ||
351 | util.printout() | ||
352 | end | ||
353 | end | ||
354 | end | ||
355 | |||
356 | function search.pick_installed_rock(query: Query, given_tree: string | Tree): string, string, string | Tree, string | ||
357 | |||
358 | local result_tree = {} | ||
359 | local tree_map = {} | ||
360 | local trees = cfg.rocks_trees | ||
361 | if given_tree then | ||
362 | trees = { given_tree } | ||
363 | end | ||
364 | for _, tree in ipairs(trees) do | ||
365 | local rocks_dir = path.rocks_dir(tree) | ||
366 | tree_map[rocks_dir] = tree | ||
367 | search.local_manifest_search(result_tree, rocks_dir, query) | ||
368 | end | ||
369 | if not next(result_tree) then | ||
370 | return nil, "cannot find package "..tostring(query).."\nUse 'list' to find installed rocks." | ||
371 | end | ||
372 | |||
373 | if not result_tree[query.name] and next(result_tree, next(result_tree) as string) ~= nil then | ||
374 | local out = { "multiple installed packages match the name '"..tostring(query).."':\n\n" } | ||
375 | for name, _ in util.sortedpairs(result_tree) do | ||
376 | table.insert(out, " " .. name .. "\n") | ||
377 | end | ||
378 | table.insert(out, "\nPlease specify a single rock.\n") | ||
379 | return nil, table.concat(out) | ||
380 | end | ||
381 | |||
382 | local repo_url: string | ||
383 | |||
384 | local name, versions: string, {string : {Result}} | ||
385 | if result_tree[query.name] then | ||
386 | name, versions = query.name, result_tree[query.name] | ||
387 | else | ||
388 | name, versions = util.sortedpairs(result_tree)() | ||
389 | end | ||
390 | |||
391 | local version, repositories = util.sortedpairs(versions, vers.compare_versions)() | ||
392 | for _, rp in ipairs(repositories) do repo_url = rp.repo end | ||
393 | |||
394 | local repo = tree_map[repo_url] | ||
395 | return name, version, repo, repo_url | ||
396 | end | ||
397 | |||
398 | return search | ||
399 | |||
diff --git a/src/luarocks/util.tl b/src/luarocks/util.tl index 9e940f8c..d57de5e8 100644 --- a/src/luarocks/util.tl +++ b/src/luarocks/util.tl | |||
@@ -8,13 +8,13 @@ local core = require("luarocks.core.util") | |||
8 | local cfg = require("luarocks.core.cfg") | 8 | local cfg = require("luarocks.core.cfg") |
9 | 9 | ||
10 | local type o = require("luarocks.core.types.ordering") | 10 | local type o = require("luarocks.core.types.ordering") |
11 | local type Ordering = o.Ordering | 11 | local type Ordering= o.Ordering |
12 | local type SortBy = o.SortBy | 12 | local type SortBy = o.SortBy |
13 | 13 | ||
14 | local record util | 14 | local record util |
15 | cleanup_path: function(string, string, string, boolean): string | 15 | cleanup_path: function(string, string, string, boolean): string |
16 | split_string: function(string, string, number): {string} | 16 | split_string: function(string, string, number): {string} |
17 | sortedpairs: function<K, V>(tbl: {K: V}, sort_by?: SortBy): function(): K, V, Ordering | 17 | sortedpairs: function<K, V>(tbl: {K: V}, sort_by?: SortBy): function(): K, V, Ordering<K> |
18 | deep_merge: function({any : any}, {any : any}) | 18 | deep_merge: function({any : any}, {any : any}) |
19 | deep_merge_under: function({any : any}, {any : any}) | 19 | deep_merge_under: function({any : any}, {any : any}) |
20 | popen_read: function(string, ?string): string | 20 | popen_read: function(string, ?string): string |
@@ -164,7 +164,7 @@ function util.variable_substitutions<K>(tbl: {K: string}, vars: {string: string} | |||
164 | end | 164 | end |
165 | end | 165 | end |
166 | 166 | ||
167 | function util.lua_versions(sort: string): function(): string | 167 | function util.lua_versions(sort?: string): function(): string |
168 | local versions = { "5.1", "5.2", "5.3", "5.4" } | 168 | local versions = { "5.1", "5.2", "5.3", "5.4" } |
169 | local i = 0 | 169 | local i = 0 |
170 | if sort == "descending" then | 170 | if sort == "descending" then |
@@ -560,7 +560,7 @@ end | |||
560 | -- @return a table with rock names as keys and versions and values, | 560 | -- @return a table with rock names as keys and versions and values, |
561 | -- specifying modules that are already provided by the VM (including | 561 | -- specifying modules that are already provided by the VM (including |
562 | -- "lua" for the Lua version and, for format 3.0+, "luajit" if detected). | 562 | -- "lua" for the Lua version and, for format 3.0+, "luajit" if detected). |
563 | function util.get_rocks_provided(rockspec: Rockspec): {string: string} | 563 | function util.get_rocks_provided(rockspec?: Rockspec): {string: string} |
564 | 564 | ||
565 | if not rockspec and cfg.cache.rocks_provided then | 565 | if not rockspec and cfg.cache.rocks_provided then |
566 | return cfg.cache.rocks_provided | 566 | return cfg.cache.rocks_provided |