aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/luarocks/deps.tl (renamed from src/luarocks/deps.lua)346
1 files changed, 195 insertions, 151 deletions
diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.tl
index 344991f6..e26ba60f 100644
--- a/src/luarocks/deps.lua
+++ b/src/luarocks/deps.tl
@@ -1,6 +1,8 @@
1 1
2--- High-level dependency related functions. 2--- High-level dependency related functions.
3local deps = {} 3local record deps
4 installer: function(Args): boolean, string, string
5end
4 6
5local cfg = require("luarocks.core.cfg") 7local cfg = require("luarocks.core.cfg")
6local manif = require("luarocks.manif") 8local manif = require("luarocks.manif")
@@ -12,6 +14,29 @@ local vers = require("luarocks.core.vers")
12local queries = require("luarocks.queries") 14local queries = require("luarocks.queries")
13local deplocks = require("luarocks.deplocks") 15local deplocks = require("luarocks.deplocks")
14 16
17local type Rockspec = require("luarocks.core.types.rockspec").Rockspec
18local type Dependencies = require("luarocks.core.types.rockspec").Dependencies
19
20local type BuiltinBuild = require("luarocks.core.types.build").BuiltinBuild
21local type Module = BuiltinBuild.Module
22
23local type Build = require("luarocks.core.types.build").Build
24
25local type Tree = require("luarocks.core.types.tree").Tree
26
27local type Query = require("luarocks.core.types.query").Query
28
29local type Version = require("luarocks.core.types.version").Version
30
31local type PersistableTable = require("luarocks.core.types.persist").PersistableTable
32
33local type Result = require("luarocks.core.types.result").Result
34
35local type Dir = require("luarocks.core.types.dir").Dir
36local type Dirs = require("luarocks.core.types.dir").Dirs
37
38local type Args = require("luarocks.core.types.args").Args
39
15--- Generate a function that matches dep queries against the manifest, 40--- Generate a function that matches dep queries against the manifest,
16-- taking into account rocks_provided, the list of versions to skip, 41-- taking into account rocks_provided, the list of versions to skip,
17-- and the lockfile. 42-- and the lockfile.
@@ -29,14 +54,10 @@ local deplocks = require("luarocks.deplocks")
29-- * map of versions to locations 54-- * map of versions to locations
30-- * version matched via lockfile if any 55-- * version matched via lockfile if any
31-- * true if rock matched via rocks_provided 56-- * true if rock matched via rocks_provided
32local function prepare_get_versions(deps_mode, rocks_provided, depskey, skip_set) 57local 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
33 assert(type(deps_mode) == "string") 58
34 assert(type(rocks_provided) == "table") 59 return function(dep: Query): {string}, {string: string | Tree}, string | number | boolean | PersistableTable, boolean
35 assert(type(depskey) == "string") 60 local versions, locations: {string}, {string: string | Tree}
36 assert(type(skip_set) == "table" or skip_set == nil)
37
38 return function(dep)
39 local versions, locations
40 local provided = rocks_provided[dep.name] 61 local provided = rocks_provided[dep.name]
41 if provided then 62 if provided then
42 -- Provided rocks have higher priority than manifest's rocks. 63 -- Provided rocks have higher priority than manifest's rocks.
@@ -57,7 +78,7 @@ local function prepare_get_versions(deps_mode, rocks_provided, depskey, skip_set
57 end 78 end
58 end 79 end
59 80
60 local lockversion = deplocks.get(depskey, dep.name) 81 local lockversion = deplocks.get(depskey, dep.name) --! cast?
61 82
62 return versions, locations, lockversion, provided ~= nil 83 return versions, locations, lockversion, provided ~= nil
63 end 84 end
@@ -75,17 +96,16 @@ end
75-- 2. nil 96-- 2. nil
76-- 3. either 'dep' or an alternative query to be used 97-- 3. either 'dep' or an alternative query to be used
77-- 4. false 98-- 4. false
78local function match_dep(dep, get_versions) 99local function match_dep(depq: Query,
79 assert(type(dep) == "table") 100 get_versions: function(Query): {string}, {string: string | Tree}, string, boolean): string, string | Tree, Query, boolean
80 assert(type(get_versions) == "function")
81 101
82 local versions, locations, lockversion, provided = get_versions(dep) 102 local versions, locations, lockversion, provided = get_versions(depq)
83 103
84 local latest_version 104 local latest_version: Version
85 local latest_vstring 105 local latest_vstring: string
86 for _, vstring in ipairs(versions) do 106 for _, vstring in ipairs(versions) do
87 local version = vers.parse_version(vstring) 107 local version = vers.parse_version(vstring)
88 if vers.match_constraints(version, dep.constraints) then 108 if vers.match_constraints(version, depq.constraints) then
89 if not latest_version or version > latest_version then 109 if not latest_version or version > latest_version then
90 latest_version = version 110 latest_version = version
91 latest_vstring = vstring 111 latest_vstring = vstring
@@ -98,31 +118,30 @@ local function match_dep(dep, get_versions)
98 if latest_vstring and latest_vstring ~= lockversion then 118 if latest_vstring and latest_vstring ~= lockversion then
99 latest_matching_msg = " (latest matching is " .. latest_vstring .. ")" 119 latest_matching_msg = " (latest matching is " .. latest_vstring .. ")"
100 end 120 end
101 util.printout("Forcing " .. dep.name .. " to pinned version " .. lockversion .. latest_matching_msg) 121 util.printout("Forcing " .. depq.name .. " to pinned version " .. lockversion .. latest_matching_msg)
102 return nil, nil, queries.new(dep.name, dep.namespace, lockversion) 122 return nil, nil, queries.new(depq.name, depq.namespace, lockversion)
103 end 123 end
104 124
105 return latest_vstring, locations[latest_vstring], dep, provided 125 return latest_vstring, locations[latest_vstring], depq, provided
106end 126end
107 127
108local function match_all_deps(dependencies, get_versions) 128local function match_all_deps(dependencies: {Query},
109 assert(type(dependencies) == "table") 129 get_versions: function(Query): {string}, {string: string | Tree}, string, boolean): {Query: Result}, {string: Query}, {string: Query}
110 assert(type(get_versions) == "function")
111 130
112 local matched, missing, no_upgrade = {}, {}, {} 131 local matched, missing, no_upgrade = {}, {}, {}
113 132
114 for _, dep in ipairs(dependencies) do 133 for _, depq in ipairs(dependencies) do
115 local found, _, provided 134 local found, _, provided: string, string | Tree, boolean
116 found, _, dep, provided = match_dep(dep, get_versions) 135 found, _, depq, provided = match_dep(depq, get_versions)
117 if found then 136 if found then
118 if not provided then 137 if not provided then
119 matched[dep] = {name = dep.name, version = found} 138 matched[depq] = {name = depq.name, version = found} as Result
120 end 139 end
121 else 140 else
122 if dep.constraints[1] and dep.constraints[1].no_upgrade then 141 if depq.constraints and depq.constraints[1] and depq.constraints[1].no_upgrade then
123 no_upgrade[dep.name] = dep 142 no_upgrade[depq.name] = depq
124 else 143 else
125 missing[dep.name] = dep 144 missing[depq.name] = depq
126 end 145 end
127 end 146 end
128 end 147 end
@@ -142,20 +161,13 @@ end
142-- parsed as tables; and a table of "no-upgrade" missing dependencies 161-- 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 162-- (to be used in plugin modules so that a plugin does not force upgrade of
144-- its parent application). 163-- its parent application).
145function deps.match_deps(dependencies, rocks_provided, skip_set, deps_mode) 164function deps.match_deps(dependencies: {Query}, rocks_provided: {string: string}, deps_mode: string, skip_set?: {string: {string: boolean}}): {Query : Result}, {string : Query}, {string : Query}
146 assert(type(dependencies) == "table")
147 assert(type(rocks_provided) == "table")
148 assert(type(skip_set) == "table" or skip_set == nil)
149 assert(type(deps_mode) == "string")
150 165
151 local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies", skip_set) 166 local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies", skip_set)
152 return match_all_deps(dependencies, get_versions) 167 return match_all_deps(dependencies, get_versions)
153end 168end
154 169
155local function rock_status(dep, get_versions) 170local function rock_status(dep: Query, get_versions: function(Query): {string}, {string : string | Tree}, string, boolean): string, string
156 assert(dep:type() == "query")
157 assert(type(get_versions) == "function")
158
159 local installed, _, _, provided = match_dep(dep, get_versions) 171 local installed, _, _, provided = match_dep(dep, get_versions)
160 local installation_type = provided and "provided by VM" or "installed" 172 local installation_type = provided and "provided by VM" or "installed"
161 return installed and installed.." "..installation_type..": success" or "not installed" 173 return installed and installed.." "..installation_type..": success" or "not installed"
@@ -170,12 +182,7 @@ end
170-- by this Lua implementation for the given dependency. 182-- by this Lua implementation for the given dependency.
171-- "one" for the current default tree, "all" for all trees, 183-- "one" for the current default tree, "all" for all trees,
172-- "order" for all trees with priority >= the current default, "none" for no trees. 184-- "order" for all trees with priority >= the current default, "none" for no trees.
173function deps.report_missing_dependencies(name, version, dependencies, deps_mode, rocks_provided) 185function deps.report_missing_dependencies(name: string, version: string, dependencies: {Query}, deps_mode: string, rocks_provided: {string: string})
174 assert(type(name) == "string")
175 assert(type(version) == "string")
176 assert(type(dependencies) == "table")
177 assert(type(deps_mode) == "string")
178 assert(type(rocks_provided) == "table")
179 186
180 if deps_mode == "none" then 187 if deps_mode == "none" then
181 return 188 return
@@ -185,33 +192,28 @@ function deps.report_missing_dependencies(name, version, dependencies, deps_mode
185 192
186 local first_missing_dep = true 193 local first_missing_dep = true
187 194
188 for _, dep in ipairs(dependencies) do 195 for _, depq in ipairs(dependencies) do
189 local found, _ 196 local found, _: string, string | Tree
190 found, _, dep = match_dep(dep, get_versions) 197 found, _, depq = match_dep(depq, get_versions)
191 if not found then 198 if not found then
192 if first_missing_dep then 199 if first_missing_dep then
193 util.printout(("Missing dependencies for %s %s:"):format(name, version)) 200 util.printout(("Missing dependencies for %s %s:"):format(name, version))
194 first_missing_dep = false 201 first_missing_dep = false
195 end 202 end
196 203
197 util.printout((" %s (%s)"):format(tostring(dep), rock_status(dep, get_versions))) 204 util.printout((" %s (%s)"):format(tostring(depq), rock_status(depq, get_versions)))
198 end 205 end
199 end 206 end
200end 207end
201 208
202function deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey) 209function deps.fulfill_dependency(dep: Query, deps_mode: string, rocks_provided: {string: string}, verify: boolean, depskey?: string): boolean, string, string | Tree
203 assert(dep:type() == "query")
204 assert(type(deps_mode) == "string" or deps_mode == nil)
205 assert(type(rocks_provided) == "table" or rocks_provided == nil)
206 assert(type(verify) == "boolean" or verify == nil)
207 assert(type(depskey) == "string")
208 210
209 deps_mode = deps_mode or "all" 211 deps_mode = deps_mode or "all"
210 rocks_provided = rocks_provided or {} 212 rocks_provided = rocks_provided or {}
211 213
212 local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey) 214 local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey)
213 215
214 local found, where 216 local found, where: string, string | Tree
215 found, where, dep = match_dep(dep, get_versions) 217 found, where, dep = match_dep(dep, get_versions)
216 if found then 218 if found then
217 local tree_manifests = manif.load_rocks_tree_manifests(deps_mode) 219 local tree_manifests = manif.load_rocks_tree_manifests(deps_mode)
@@ -220,7 +222,6 @@ function deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey
220 end 222 end
221 223
222 local search = require("luarocks.search") 224 local search = require("luarocks.search")
223 local install = require("luarocks.cmd.install")
224 225
225 local url, search_err = search.find_suitable_rock(dep) 226 local url, search_err = search.find_suitable_rock(dep)
226 if not url then 227 if not url then
@@ -233,24 +234,24 @@ function deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey
233 namespace = dep.namespace, 234 namespace = dep.namespace,
234 verify = verify, 235 verify = verify,
235 } 236 }
236 local ok, install_err, errcode = install.command(install_args) 237 local ok, install_err, errcode = deps.installer(install_args)
237 if not ok then 238 if not ok then
238 return nil, "Failed installing dependency: "..url.." - "..install_err, errcode 239 return nil, "Failed installing dependency: "..url.." - "..install_err, errcode
239 end 240 end
240 241
241 found, where = match_dep(dep, get_versions) 242 found, where = match_dep(dep, get_versions)
242 if not found then 243 if not found then
243 return nil, "Repository inconsistency detected (previously unfinished/corrupted installation?)" 244 return nil, "Repository inconsistency detected (previously unfinished/corrupted installation?)"
244 end 245 end
245 return true, found, where 246 return true, found, where
246end 247end
247 248
248local function check_supported_platforms(rockspec) 249local function check_supported_platforms(rockspec: Rockspec): boolean, string
249 if rockspec.supported_platforms and next(rockspec.supported_platforms) then 250 if rockspec.supported_platforms and next(rockspec.supported_platforms) then
250 local all_negative = true 251 local all_negative = true
251 local supported = false 252 local supported = false
252 for _, plat in pairs(rockspec.supported_platforms) do 253 for _, plat in ipairs(rockspec.supported_platforms) do
253 local neg 254 local neg: string
254 neg, plat = plat:match("^(!?)(.*)") 255 neg, plat = plat:match("^(!?)(.*)")
255 if neg == "!" then 256 if neg == "!" then
256 if cfg.is_platform(plat) then 257 if cfg.is_platform(plat) then
@@ -285,13 +286,7 @@ end
285-- @return boolean or (nil, string, [string]): True if no errors occurred, or 286-- @return boolean or (nil, string, [string]): True if no errors occurred, or
286-- nil and an error message if any test failed, followed by an optional 287-- nil and an error message if any test failed, followed by an optional
287-- error code. 288-- error code.
288function deps.fulfill_dependencies(rockspec, depskey, deps_mode, verify, deplock_dir) 289function deps.fulfill_dependencies(rockspec: Rockspec, depskey: string, deps_mode: string, verify?: boolean, deplock_dir?: string): boolean, string, string
289 assert(type(rockspec) == "table")
290 assert(type(depskey) == "string")
291 assert(type(deps_mode) == "string")
292 assert(type(verify) == "boolean" or verify == nil)
293 assert(type(deplock_dir) == "string" or deplock_dir == nil)
294
295 local name = rockspec.name 290 local name = rockspec.name
296 local version = rockspec.version 291 local version = rockspec.version
297 local rocks_provided = rockspec.rocks_provided 292 local rocks_provided = rockspec.rocks_provided
@@ -301,16 +296,23 @@ function deps.fulfill_dependencies(rockspec, depskey, deps_mode, verify, deplock
301 util.printout("Using dependencies pinned in lockfile: " .. filename) 296 util.printout("Using dependencies pinned in lockfile: " .. filename)
302 297
303 local get_versions = prepare_get_versions("none", rocks_provided, depskey) 298 local get_versions = prepare_get_versions("none", rocks_provided, depskey)
299 local dnsnamestr, dversionstr: string, string
304 for dnsname, dversion in deplocks.each(depskey) do 300 for dnsname, dversion in deplocks.each(depskey) do
305 local dname, dnamespace = util.split_namespace(dnsname) 301 if dnsname is string then
306 local dep = queries.new(dname, dnamespace, dversion) 302 dnsnamestr = dnsname
303 end
304 if dversion is string then
305 dversionstr = dversion
306 end
307 local dname, dnamespace = util.split_namespace(dnsnamestr)
308 local depq = queries.new(dname, dnamespace, dversionstr)
307 309
308 util.printout(("%s %s is pinned to %s (%s)"):format( 310 util.printout(("%s %s is pinned to %s (%s)"):format(
309 name, version, tostring(dep), rock_status(dep, get_versions))) 311 name, version, tostring(depq), rock_status(depq, get_versions)))
310 312
311 local ok, err = deps.fulfill_dependency(dep, "none", rocks_provided, verify, depskey) 313 local okfullfill, errfullfill = deps.fulfill_dependency(depq, "none", rocks_provided, verify, depskey)
312 if not ok then 314 if not okfullfill then
313 return nil, err 315 return nil, errfullfill
314 end 316 end
315 end 317 end
316 util.printout() 318 util.printout()
@@ -324,28 +326,28 @@ function deps.fulfill_dependencies(rockspec, depskey, deps_mode, verify, deplock
324 return nil, err 326 return nil, err
325 end 327 end
326 328
327 deps.report_missing_dependencies(name, version, rockspec[depskey], deps_mode, rocks_provided) 329 deps.report_missing_dependencies(name, version, (rockspec as {string: Dependencies})[depskey].queries, deps_mode, rocks_provided)
328 330
329 util.printout() 331 util.printout()
330 332
331 local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey) 333 local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey)
332 for _, dep in ipairs(rockspec[depskey]) do 334 for _, depq in ipairs((rockspec as {string: Dependencies})[depskey].queries) do
333 335
334 util.printout(("%s %s depends on %s (%s)"):format( 336 util.printout(("%s %s depends on %s (%s)"):format(
335 name, version, tostring(dep), rock_status(dep, get_versions))) 337 name, version, tostring(depq), rock_status(depq, get_versions)))
336 338
337 local ok, found_or_err, _, no_upgrade = deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey) 339 local okfulfill, found_or_err, _ = deps.fulfill_dependency(depq, deps_mode, rocks_provided, verify, depskey)
338 if ok then 340 if okfulfill then
339 deplocks.add(depskey, dep.name, found_or_err) 341 deplocks.add(depskey, depq.name, found_or_err)
340 else 342 else
341 if no_upgrade then 343 -- if no_upgrade then
342 util.printerr("This version of "..name.." is designed for use with") 344 -- util.printerr("This version of "..name.." is designed for use with")
343 util.printerr(tostring(dep)..", but is configured to avoid upgrading it") 345 -- util.printerr(tostring(dep)..", but is configured to avoid upgrading it")
344 util.printerr("automatically. Please upgrade "..dep.name.." with") 346 -- util.printerr("automatically. Please upgrade "..dep.name.." with")
345 util.printerr(" luarocks install "..dep.name) 347 -- util.printerr(" luarocks install "..dep.name)
346 util.printerr("or look for a suitable version of "..name.." with") 348 -- util.printerr("or look for a suitable version of "..name.." with")
347 util.printerr(" luarocks search "..name) 349 -- util.printerr(" luarocks search "..name)
348 end 350 -- end
349 return nil, found_or_err 351 return nil, found_or_err
350 end 352 end
351 end 353 end
@@ -360,7 +362,7 @@ end
360-- @param file string: a filename 362-- @param file string: a filename
361-- @param pattern string: a pattern, where ? is to be matched by the filename. 363-- @param pattern string: a pattern, where ? is to be matched by the filename.
362-- @return string The pattern, if found, or nil. 364-- @return string The pattern, if found, or nil.
363local function deconstruct_pattern(file, pattern) 365local function deconstruct_pattern(file: string, pattern: string): string
364 local depattern = "^"..(pattern:gsub("%.", "%%."):gsub("%*", ".*"):gsub("?", "(.*)")).."$" 366 local depattern = "^"..(pattern:gsub("%.", "%%."):gsub("%*", ".*"):gsub("?", "(.*)")).."$"
365 return (file:match(depattern)) 367 return (file:match(depattern))
366end 368end
@@ -372,13 +374,13 @@ end
372-- @param array of string An array of patterns with "?" as the wildcard 374-- @param array of string An array of patterns with "?" as the wildcard
373-- (e.g. {"?.so", "lib?.so"}) 375-- (e.g. {"?.so", "lib?.so"})
374-- @param files The array of constructed names 376-- @param files The array of constructed names
375local function add_all_patterns(file, patterns, files) 377local function add_all_patterns(file: string, patterns: {string}, files: {{integer, string}}): Dirs
376 for _, pattern in ipairs(patterns) do 378 for _, pattern in ipairs(patterns) do
377 table.insert(files, {#files + 1, (pattern:gsub("?", file))}) 379 table.insert(files, {#files + 1, (pattern:gsub("?", file))})
378 end 380 end
379end 381end
380 382
381local function get_external_deps_dirs(mode) 383local function get_external_deps_dirs(mode: string): Dirs
382 local patterns = cfg.external_deps_patterns 384 local patterns = cfg.external_deps_patterns
383 local subdirs = cfg.external_deps_subdirs 385 local subdirs = cfg.external_deps_subdirs
384 if mode == "install" then 386 if mode == "install" then
@@ -393,13 +395,13 @@ local function get_external_deps_dirs(mode)
393 if mode == "install" then 395 if mode == "install" then
394 dirs.INCDIR = nil 396 dirs.INCDIR = nil
395 end 397 end
396 return dirs 398 return dirs as Dirs
397end 399end
398 400
399local function resolve_prefix(prefix, dirs) 401local function resolve_prefix(prefix: string | {string: string}, dirs: Dirs): string
400 if type(prefix) == "string" then 402 if prefix is string then
401 return prefix 403 return prefix
402 elseif type(prefix) == "table" then 404 elseif prefix is {string: string} then
403 if prefix.bin then 405 if prefix.bin then
404 dirs.BINDIR.subdir = prefix.bin 406 dirs.BINDIR.subdir = prefix.bin
405 end 407 end
@@ -415,7 +417,7 @@ local function resolve_prefix(prefix, dirs)
415 end 417 end
416end 418end
417 419
418local function add_patterns_for_file(files, file, patterns) 420local function add_patterns_for_file(files: {{integer, string}}, file: string, patterns: {string})
419 -- If it doesn't look like it contains a filename extension 421 -- If it doesn't look like it contains a filename extension
420 if not (file:match("%.[a-z]+$") or file:match("%.[a-z]+%.")) then 422 if not (file:match("%.[a-z]+$") or file:match("%.[a-z]+%.")) then
421 add_all_patterns(file, patterns, files) 423 add_all_patterns(file, patterns, files)
@@ -430,36 +432,45 @@ local function add_patterns_for_file(files, file, patterns)
430 end 432 end
431end 433end
432 434
433local function check_external_dependency_at(prefix, name, ext_files, vars, dirs, err_files, cache) 435local function check_external_dependency_at(
436 prefix: string,
437 name: string,
438 ext_files: {string: string | {string}},
439 vars: {string: string},
440 dirs: Dirs,
441 err_files: {string: {string}},
442 cache: {string: {string}}): boolean, string, string
443
434 local fs = require("luarocks.fs") 444 local fs = require("luarocks.fs")
435 cache = cache or {} 445 cache = cache or {}
436 446
437 for dirname, dirdata in util.sortedpairs(dirs) do 447 for dirname, dirdata in util.sortedpairs(dirs) do
438 local paths 448 local paths: {string}
439 local path_var_value = vars[name.."_"..dirname] 449 local path_var_value = vars[name.."_"..dirname]
450 local dirdatastr = dirdata.subdir
440 if path_var_value then 451 if path_var_value then
441 paths = { path_var_value } 452 paths = { path_var_value }
442 elseif type(dirdata.subdir) == "table" then 453 elseif dirdatastr is {string} then
443 paths = {} 454 paths = {}
444 for i,v in ipairs(dirdata.subdir) do 455 for i,v in ipairs(dirdatastr) do
445 paths[i] = dir.path(prefix, v) 456 paths[i] = dir.path(prefix, v)
446 end 457 end
447 else 458 else
448 paths = { dir.path(prefix, dirdata.subdir) } 459 paths = { dir.path(prefix, dirdatastr) }
449 end 460 end
450 local file_or_files = ext_files[dirdata.testfile] 461 local file_or_files = ext_files[dirdata.testfile]
451 if file_or_files then 462 if file_or_files then
452 local files = {} 463 local files = {}
453 if type(file_or_files) == "string" then 464 if file_or_files is string then
454 add_patterns_for_file(files, file_or_files, dirdata.pattern) 465 add_patterns_for_file(files, file_or_files, dirdata.pattern)
455 elseif type(file_or_files) == "table" then 466 elseif file_or_files is {string} then
456 for _, f in ipairs(file_or_files) do 467 for _, f in ipairs(file_or_files) do
457 add_patterns_for_file(files, f, dirdata.pattern) 468 add_patterns_for_file(files, f, dirdata.pattern)
458 end 469 end
459 end 470 end
460 471
461 local found = false 472 local found = false
462 table.sort(files, function(a, b) 473 table.sort(files, function(a: {integer, string}, b: {integer, string}): boolean
463 if (not a[2]:match("%*")) and b[2]:match("%*") then 474 if (not a[2]:match("%*")) and b[2]:match("%*") then
464 return true 475 return true
465 elseif a[2]:match("%*") and (not b[2]:match("%*")) then 476 elseif a[2]:match("%*") and (not b[2]:match("%*")) then
@@ -476,7 +487,7 @@ local function check_external_dependency_at(prefix, name, ext_files, vars, dirs,
476 f = f:gsub("%.[^.]+$", "."..cfg.external_lib_extension) 487 f = f:gsub("%.[^.]+$", "."..cfg.external_lib_extension)
477 end 488 end
478 489
479 local pattern 490 local pattern: string
480 if f:match("%*") then 491 if f:match("%*") then
481 pattern = "^" .. f:gsub("([-.+])", "%%%1"):gsub("%*", ".*") .. "$" 492 pattern = "^" .. f:gsub("([-.+])", "%%%1"):gsub("%*", ".*") .. "$"
482 f = "matching "..f 493 f = "matching "..f
@@ -526,7 +537,7 @@ local function check_external_dependency_at(prefix, name, ext_files, vars, dirs,
526 end 537 end
527 end 538 end
528 539
529 for dirname, dirdata in pairs(dirs) do 540 for dirname, dirdata in pairs(dirs as {string: Dir}) do
530 vars[name.."_"..dirname] = dirdata.dir 541 vars[name.."_"..dirname] = dirdata.dir
531 vars[name.."_"..dirname.."_FILE"] = dirdata.file 542 vars[name.."_"..dirname.."_FILE"] = dirdata.file
532 end 543 end
@@ -534,15 +545,20 @@ local function check_external_dependency_at(prefix, name, ext_files, vars, dirs,
534 return true 545 return true
535end 546end
536 547
537local function check_external_dependency(name, ext_files, vars, mode, cache) 548local function check_external_dependency(
538 local ok 549 name: string,
539 local err_dirname 550 ext_files: {string: string | {string}},
540 local err_testfile 551 vars: {string: string},
541 local err_files = {program = {}, header = {}, library = {}} 552 mode: string,
553 cache?: {string : {string}}): boolean, string, string, {string : {string}}
554 local ok: boolean
555 local err_dirname: string
556 local err_testfile: string
557 local err_files: {string: {string}} = {program = {}, header = {}, library = {}}
542 558
543 local dirs = get_external_deps_dirs(mode) 559 local dirs = get_external_deps_dirs(mode)
544 560
545 local prefixes 561 local prefixes: {string}
546 if vars[name .. "_DIR"] then 562 if vars[name .. "_DIR"] then
547 prefixes = { vars[name .. "_DIR"] } 563 prefixes = { vars[name .. "_DIR"] }
548 elseif vars.DEPS_DIR then 564 elseif vars.DEPS_DIR then
@@ -554,11 +570,11 @@ local function check_external_dependency(name, ext_files, vars, mode, cache)
554 for _, prefix in ipairs(prefixes) do 570 for _, prefix in ipairs(prefixes) do
555 prefix = resolve_prefix(prefix, dirs) 571 prefix = resolve_prefix(prefix, dirs)
556 if cfg.is_platform("mingw32") and name == "LUA" then 572 if cfg.is_platform("mingw32") and name == "LUA" then
557 dirs.LIBDIR.pattern = fun.filter(util.deep_copy(dirs.LIBDIR.pattern), function(s) 573 dirs.LIBDIR.pattern = fun.filter(util.deep_copy(dirs.LIBDIR.pattern) as {string}, function(s: string): boolean
558 return not s:match("%.a$") 574 return not s:match("%.a$")
559 end) 575 end)
560 elseif cfg.is_platform("windows") and name == "LUA" then 576 elseif cfg.is_platform("windows") and name == "LUA" then
561 dirs.LIBDIR.pattern = fun.filter(util.deep_copy(dirs.LIBDIR.pattern), function(s) 577 dirs.LIBDIR.pattern = fun.filter(util.deep_copy(dirs.LIBDIR.pattern) as {string}, function(s: string): boolean
562 return not s:match("%.dll$") 578 return not s:match("%.dll$")
563 end) 579 end)
564 end 580 end
@@ -571,19 +587,22 @@ local function check_external_dependency(name, ext_files, vars, mode, cache)
571 return nil, err_dirname, err_testfile, err_files 587 return nil, err_dirname, err_testfile, err_files
572end 588end
573 589
574function deps.autodetect_external_dependencies(build) 590function deps.autodetect_external_dependencies(build: Build): {string : {string : string}}
575 -- only applies to the 'builtin' build type 591 -- only applies to the 'builtin' build type
576 if not build or not build.modules then 592 if not build or not (build as BuiltinBuild).modules then
577 return nil 593 return nil
578 end 594 end
579 595
580 local extdeps = {} 596 local extdeps: {string: {string: string}} = {}
581 local any = false 597 local any = false
582 for _, data in pairs(build.modules) do 598 for _, data in pairs((build as BuiltinBuild).modules) do
583 if type(data) == "table" and data.libraries then 599 if data is Module and data.libraries then
584 local libraries = data.libraries 600 local libraries: {string}
585 if type(libraries) == "string" then 601 local librariesstr: string | {string} = data.libraries
586 libraries = { libraries } 602 if librariesstr is string then
603 libraries = { librariesstr }
604 else
605 libraries = librariesstr
587 end 606 end
588 local incdirs = {} 607 local incdirs = {}
589 local libdirs = {} 608 local libdirs = {}
@@ -618,18 +637,17 @@ end
618-- if "install" is given, do not scan for headers. 637-- if "install" is given, do not scan for headers.
619-- @return boolean or (nil, string): True if no errors occurred, or 638-- @return boolean or (nil, string): True if no errors occurred, or
620-- nil and an error message if any test failed. 639-- nil and an error message if any test failed.
621function deps.check_external_deps(rockspec, mode) 640function deps.check_external_deps(rockspec: Rockspec, mode: string): boolean, string, string
622 assert(rockspec:type() == "rockspec")
623 641
624 if not rockspec.external_dependencies then 642 if not rockspec.external_dependencies then
625 rockspec.external_dependencies = deps.autodetect_external_dependencies(rockspec.build) 643 rockspec.external_dependencies = deps.autodetect_external_dependencies(rockspec.build) as {string: {string: string}}
626 end 644 end
627 if not rockspec.external_dependencies then 645 if not rockspec.external_dependencies then
628 return true 646 return true
629 end 647 end
630 648
631 for name, ext_files in util.sortedpairs(rockspec.external_dependencies) do 649 for name, ext_files in util.sortedpairs(rockspec.external_dependencies) do
632 local ok, err_dirname, err_testfile, err_files = check_external_dependency(name, ext_files, rockspec.variables, mode) 650 local ok, err_dirname, err_testfile, err_files = check_external_dependency(name, ext_files as {string : string | {string}}, rockspec.variables, mode)
633 if not ok then 651 if not ok then
634 local lines = {"Could not find "..err_testfile.." file for "..name} 652 local lines = {"Could not find "..err_testfile.." file for "..name}
635 653
@@ -657,11 +675,8 @@ end
657-- @param mdeps table: The manifest dependencies table. 675-- @param mdeps table: The manifest dependencies table.
658-- @param name string: Package name. 676-- @param name string: Package name.
659-- @param version string: Package version. 677-- @param version string: Package version.
660function deps.scan_deps(results, mdeps, name, version, deps_mode) 678function deps.scan_deps(results: {string: string}, mdeps: {string: {string: {Query}}}, name: string, version: string, deps_mode: string)
661 assert(type(results) == "table") 679 assert(not name:match("/"))
662 assert(type(mdeps) == "table")
663 assert(type(name) == "string" and not name:match("/"))
664 assert(type(version) == "string")
665 680
666 local fetch = require("luarocks.fetch") 681 local fetch = require("luarocks.fetch")
667 682
@@ -671,13 +686,13 @@ function deps.scan_deps(results, mdeps, name, version, deps_mode)
671 if not mdeps[name] then mdeps[name] = {} end 686 if not mdeps[name] then mdeps[name] = {} end
672 local mdn = mdeps[name] 687 local mdn = mdeps[name]
673 local dependencies = mdn[version] 688 local dependencies = mdn[version]
674 local rocks_provided 689 local rocks_provided: {string : string}
675 if not dependencies then 690 if not dependencies then
676 local rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version), false) 691 local rockspec = fetch.load_local_rockspec(path.rockspec_file(name, version), false)
677 if not rockspec then 692 if not rockspec then
678 return 693 return
679 end 694 end
680 dependencies = rockspec.dependencies 695 dependencies = rockspec.dependencies.queries
681 rocks_provided = rockspec.rocks_provided 696 rocks_provided = rockspec.rocks_provided
682 mdn[version] = dependencies 697 mdn[version] = dependencies
683 else 698 else
@@ -693,7 +708,7 @@ function deps.scan_deps(results, mdeps, name, version, deps_mode)
693 end 708 end
694end 709end
695 710
696local function lua_h_exists(d, luaver) 711local function lua_h_exists(d: string, luaver: string): boolean, string, string, integer
697 local major, minor = luaver:match("(%d+)%.(%d+)") 712 local major, minor = luaver:match("(%d+)%.(%d+)")
698 local luanum = ("%s%02d"):format(major, tonumber(minor)) 713 local luanum = ("%s%02d"):format(major, tonumber(minor))
699 714
@@ -703,7 +718,7 @@ local function lua_h_exists(d, luaver)
703 local data = fd:read("*a") 718 local data = fd:read("*a")
704 fd:close() 719 fd:close()
705 if data:match("LUA_VERSION_NUM%s*" .. tostring(luanum)) then 720 if data:match("LUA_VERSION_NUM%s*" .. tostring(luanum)) then
706 return d 721 return d ~= nil
707 end 722 end
708 return nil, "Lua header lua.h found at " .. d .. " does not match Lua version " .. luaver .. ". You can use `luarocks config variables.LUA_INCDIR <path>` to set the correct location.", "dependency", 2 723 return nil, "Lua header lua.h found at " .. d .. " does not match Lua version " .. luaver .. ". You can use `luarocks config variables.LUA_INCDIR <path>` to set the correct location.", "dependency", 2
709 end 724 end
@@ -711,7 +726,7 @@ local function lua_h_exists(d, luaver)
711 return nil, "Failed finding Lua header lua.h (searched at " .. d .. "). You may need to install Lua development headers. You can use `luarocks config variables.LUA_INCDIR <path>` to set the correct location.", "dependency", 1 726 return nil, "Failed finding Lua header lua.h (searched at " .. d .. "). You may need to install Lua development headers. You can use `luarocks config variables.LUA_INCDIR <path>` to set the correct location.", "dependency", 1
712end 727end
713 728
714local function find_lua_incdir(prefix, luaver, luajitver) 729local function find_lua_incdir(prefix: string, luaver: string, luajitver: string): string, string
715 luajitver = luajitver and luajitver:gsub("%-.*", "") 730 luajitver = luajitver and luajitver:gsub("%-.*", "")
716 local shortv = luaver:gsub("%.", "") 731 local shortv = luaver:gsub("%.", "")
717 local incdirs = { 732 local incdirs = {
@@ -724,7 +739,7 @@ local function find_lua_incdir(prefix, luaver, luajitver)
724 luajitver and (prefix .. "/include/luajit-" .. (luajitver:match("^(%d+%.%d+)") or "")), 739 luajitver and (prefix .. "/include/luajit-" .. (luajitver:match("^(%d+%.%d+)") or "")),
725 } 740 }
726 local errprio = 0 741 local errprio = 0
727 local mainerr 742 local mainerr: string
728 for _, d in ipairs(incdirs) do 743 for _, d in ipairs(incdirs) do
729 local ok, err, _, prio = lua_h_exists(d, luaver) 744 local ok, err, _, prio = lua_h_exists(d, luaver)
730 if ok then 745 if ok then
@@ -740,8 +755,8 @@ local function find_lua_incdir(prefix, luaver, luajitver)
740 return nil, mainerr 755 return nil, mainerr
741end 756end
742 757
743function deps.check_lua_incdir(vars) 758function deps.check_lua_incdir(vars: {string: string}): boolean, string, string
744 if vars.LUA_INCDIR_OK == true 759 if vars.LUA_INCDIR_OK == "ok"
745 then return true 760 then return true
746 end 761 end
747 762
@@ -750,7 +765,7 @@ function deps.check_lua_incdir(vars)
750 if vars.LUA_INCDIR then 765 if vars.LUA_INCDIR then
751 local ok, err = lua_h_exists(vars.LUA_INCDIR, cfg.lua_version) 766 local ok, err = lua_h_exists(vars.LUA_INCDIR, cfg.lua_version)
752 if ok then 767 if ok then
753 vars.LUA_INCDIR_OK = true 768 vars.LUA_INCDIR_OK = "ok"
754 end 769 end
755 return ok, err 770 return ok, err
756 end 771 end
@@ -759,7 +774,7 @@ function deps.check_lua_incdir(vars)
759 local d, err = find_lua_incdir(vars.LUA_DIR, cfg.lua_version, ljv) 774 local d, err = find_lua_incdir(vars.LUA_DIR, cfg.lua_version, ljv)
760 if d then 775 if d then
761 vars.LUA_INCDIR = d 776 vars.LUA_INCDIR = d
762 vars.LUA_INCDIR_OK = true 777 vars.LUA_INCDIR_OK = "ok"
763 return true 778 return true
764 end 779 end
765 return nil, err 780 return nil, err
@@ -768,8 +783,8 @@ function deps.check_lua_incdir(vars)
768 return nil, "Failed finding Lua headers; neither LUA_DIR or LUA_INCDIR are set. You may need to install them or configure LUA_INCDIR.", "dependency" 783 return nil, "Failed finding Lua headers; neither LUA_DIR or LUA_INCDIR are set. You may need to install them or configure LUA_INCDIR.", "dependency"
769end 784end
770 785
771function deps.check_lua_libdir(vars) 786function deps.check_lua_libdir(vars: {string: string}): boolean, string, string, {string : {string}}
772 if vars.LUA_LIBDIR_OK == true 787 if vars.LUA_LIBDIR_OK == "ok"
773 then return true 788 then return true
774 end 789 end
775 790
@@ -777,7 +792,7 @@ function deps.check_lua_libdir(vars)
777 local ljv = util.get_luajit_version() 792 local ljv = util.get_luajit_version()
778 793
779 if vars.LUA_LIBDIR and vars.LUALIB and fs.exists(dir.path(vars.LUA_LIBDIR, vars.LUALIB)) then 794 if vars.LUA_LIBDIR and vars.LUALIB and fs.exists(dir.path(vars.LUA_LIBDIR, vars.LUALIB)) then
780 vars.LUA_LIBDIR_OK = true 795 vars.LUA_LIBDIR_OK = "ok"
781 return true 796 return true
782 end 797 end
783 798
@@ -797,7 +812,7 @@ function deps.check_lua_libdir(vars)
797 local save_LUA_INCDIR = vars.LUA_INCDIR 812 local save_LUA_INCDIR = vars.LUA_INCDIR
798 local ok, _, _, errfiles = check_external_dependency("LUA", { library = libnames }, vars, "build", cache) 813 local ok, _, _, errfiles = check_external_dependency("LUA", { library = libnames }, vars, "build", cache)
799 vars.LUA_INCDIR = save_LUA_INCDIR 814 vars.LUA_INCDIR = save_LUA_INCDIR
800 local err 815 local err: string
801 if ok then 816 if ok then
802 local filename = dir.path(vars.LUA_LIBDIR, vars.LUA_LIBDIR_FILE) 817 local filename = dir.path(vars.LUA_LIBDIR, vars.LUA_LIBDIR_FILE)
803 local fd = io.open(filename, "r") 818 local fd = io.open(filename, "r")
@@ -805,8 +820,9 @@ function deps.check_lua_libdir(vars)
805 if not vars.LUA_LIBDIR_FILE:match((cfg.lua_version:gsub("%.", "%%.?"))) then 820 if not vars.LUA_LIBDIR_FILE:match((cfg.lua_version:gsub("%.", "%%.?"))) then
806 -- if filename isn't versioned, check file contents 821 -- if filename isn't versioned, check file contents
807 local txt = fd:read("*a") 822 local txt = fd:read("*a")
808 ok = txt:match("Lua " .. cfg.lua_version, 1, true) 823 ok = txt:find("Lua " .. cfg.lua_version, 1, true)
809 or txt:match("lua" .. (cfg.lua_version:gsub("%.", "")), 1, true) 824 or txt:find("lua" .. (cfg.lua_version:gsub("%.", "")), 1, true)
825 and true
810 if not ok then 826 if not ok then
811 err = "Lua library at " .. filename .. " does not match Lua version " .. cfg.lua_version .. ". You can use `luarocks config variables.LUA_LIBDIR <path>` to set the correct location." 827 err = "Lua library at " .. filename .. " does not match Lua version " .. cfg.lua_version .. ". You can use `luarocks config variables.LUA_LIBDIR <path>` to set the correct location."
812 end 828 end
@@ -818,7 +834,7 @@ function deps.check_lua_libdir(vars)
818 834
819 if ok then 835 if ok then
820 vars.LUALIB = vars.LUA_LIBDIR_FILE 836 vars.LUALIB = vars.LUA_LIBDIR_FILE
821 vars.LUA_LIBDIR_OK = true 837 vars.LUA_LIBDIR_OK = "ok"
822 return true 838 return true
823 else 839 else
824 err = err or "Failed finding the Lua library. You can use `luarocks config variables.LUA_LIBDIR <path>` to set the correct location." 840 err = err or "Failed finding the Lua library. You can use `luarocks config variables.LUA_LIBDIR <path>` to set the correct location."
@@ -826,8 +842,36 @@ function deps.check_lua_libdir(vars)
826 end 842 end
827end 843end
828 844
829function deps.get_deps_mode(args) 845function deps.get_deps_mode(args: Args): string
830 return args.deps_mode or cfg.deps_mode 846 return args.deps_mode or cfg.deps_mode
831end 847end
832 848
849--- Report missing dependencies for all rocks installed in a repository.
850-- @param repo string or nil: Pathname of a local repository. If not given,
851-- the default local repository is used.
852-- @param deps_mode string: Dependency mode: "one" for the current default tree,
853-- "all" for all trees, "order" for all trees with priority >= the current default,
854-- "none" for using the default dependency mode from the configuration.
855function deps.check_dependencies(repo: string, deps_mode: string)
856 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
857 if deps_mode == "none" then deps_mode = cfg.deps_mode end
858
859 local manifest = manif.load_manifest(rocks_dir)
860 if not manifest then
861 return
862 end
863
864 for name, versions in util.sortedpairs(manifest.repository) do
865 for version, version_entries in util.sortedpairs(versions, vers.compare_versions) do
866 for _, entry in ipairs(version_entries) do
867 if entry.arch == "installed" then
868 if manifest.dependencies[name] and manifest.dependencies[name][version] then
869 deps.report_missing_dependencies(name, version, manifest.dependencies[name][version], deps_mode, util.get_rocks_provided())
870 end
871 end
872 end
873 end
874 end
875end
876
833return deps 877return deps