aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorV1K1NGbg <victor@ilchev.com>2024-08-06 22:33:57 +0300
committerV1K1NGbg <victor@ilchev.com>2024-08-06 22:33:57 +0300
commitd8c414c18467205bb6c6bca9fd316fc0e055c005 (patch)
tree8f871b0b1c06e1336031014401c285557e77a6bd
parentc878294ceb8e9b2e6d6034f5ab09646e4009107f (diff)
downloadluarocks-d8c414c18467205bb6c6bca9fd316fc0e055c005.tar.gz
luarocks-d8c414c18467205bb6c6bca9fd316fc0e055c005.tar.bz2
luarocks-d8c414c18467205bb6c6bca9fd316fc0e055c005.zip
deps
-rw-r--r--src/luarocks/deps.lua427
1 files changed, 223 insertions, 204 deletions
diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua
index 1a9eefde..df2fbbd5 100644
--- a/src/luarocks/deps.lua
+++ b/src/luarocks/deps.lua
@@ -1,7 +1,8 @@
1local _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 io = _tl_compat and _tl_compat.io or io; 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
1 2
2--- High-level dependency related functions.
3local deps = {} 3local deps = {}
4 4
5
5local cfg = require("luarocks.core.cfg") 6local cfg = require("luarocks.core.cfg")
6local manif = require("luarocks.manif") 7local manif = require("luarocks.manif")
7local path = require("luarocks.path") 8local path = require("luarocks.path")
@@ -12,34 +13,55 @@ local vers = require("luarocks.core.vers")
12local queries = require("luarocks.queries") 13local queries = require("luarocks.queries")
13local deplocks = require("luarocks.deplocks") 14local deplocks = require("luarocks.deplocks")
14 15
15--- Generate a function that matches dep queries against the manifest, 16
16-- taking into account rocks_provided, the list of versions to skip, 17
17-- and the lockfile. 18
18-- @param deps_mode "one", "none", "all" or "order" 19
19-- @param rocks_provided a one-level table mapping names to versions, 20
20-- listing rocks to consider provided by the VM 21
21-- @param rocks_provided table: A table of auto-provided dependencies. 22
22-- by this Lua implementation for the given dependency. 23
23-- @param depskey key to use when matching the lockfile ("dependencies", 24
24-- "build_dependencies", etc.) 25
25-- @param skip_set a two-level table mapping names to versions to 26
26-- boolean, listing rocks that should not be matched 27
27-- @return function(dep): {string}, {string:string}, string, boolean 28
28-- * array of matching versions 29
29-- * map of versions to locations 30
30-- * version matched via lockfile if any 31
31-- * true if rock matched via rocks_provided 32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
32local function prepare_get_versions(deps_mode, rocks_provided, depskey, skip_set) 58local function prepare_get_versions(deps_mode, rocks_provided, depskey, skip_set)
33 assert(type(deps_mode) == "string")
34 assert(type(rocks_provided) == "table")
35 assert(type(depskey) == "string")
36 assert(type(skip_set) == "table" or skip_set == nil)
37 59
38 return function(dep) 60 return function(dep)
39 local versions, locations 61 local versions, locations
40 local provided = rocks_provided[dep.name] 62 local provided = rocks_provided[dep.name]
41 if provided then 63 if provided then
42 -- Provided rocks have higher priority than manifest's rocks. 64
43 versions, locations = { provided }, {} 65 versions, locations = { provided }, {}
44 else 66 else
45 if deps_mode == "none" then 67 if deps_mode == "none" then
@@ -63,21 +85,20 @@ local function prepare_get_versions(deps_mode, rocks_provided, depskey, skip_set
63 end 85 end
64end 86end
65 87
66--- Attempt to match a dependency to an installed rock. 88
67-- @param get_versions a getter function obtained via prepare_get_versions 89
68-- @return (string, string, table) or (nil, nil, table): 90
69-- 1. latest installed version of the rock matching the dependency 91
70-- 2. location where the installed version is installed 92
71-- 3. the 'dep' query table 93
72-- 4. true if provided via VM 94
73-- or 95
74-- 1. nil 96
75-- 2. nil 97
76-- 3. either 'dep' or an alternative query to be used 98
77-- 4. false 99
78local function match_dep(dep, get_versions) 100local function match_dep(dep,
79 assert(type(dep) == "table") 101 get_versions)
80 assert(type(get_versions) == "function")
81 102
82 local versions, locations, lockversion, provided = get_versions(dep) 103 local versions, locations, lockversion, provided = get_versions(dep)
83 104
@@ -105,9 +126,8 @@ local function match_dep(dep, get_versions)
105 return latest_vstring, locations[latest_vstring], dep, provided 126 return latest_vstring, locations[latest_vstring], dep, provided
106end 127end
107 128
108local function match_all_deps(dependencies, get_versions) 129local function match_all_deps(dependencies,
109 assert(type(dependencies) == "table") 130 get_versions)
110 assert(type(get_versions) == "function")
111 131
112 local matched, missing, no_upgrade = {}, {}, {} 132 local matched, missing, no_upgrade = {}, {}, {}
113 133
@@ -116,7 +136,7 @@ local function match_all_deps(dependencies, get_versions)
116 found, _, dep, provided = match_dep(dep, get_versions) 136 found, _, dep, provided = match_dep(dep, get_versions)
117 if found then 137 if found then
118 if not provided then 138 if not provided then
119 matched[dep] = {name = dep.name, version = found} 139 matched[dep] = { name = dep.name, version = found }
120 end 140 end
121 else 141 else
122 if dep.constraints[1] and dep.constraints[1].no_upgrade then 142 if dep.constraints[1] and dep.constraints[1].no_upgrade then
@@ -129,53 +149,41 @@ local function match_all_deps(dependencies, get_versions)
129 return matched, missing, no_upgrade 149 return matched, missing, no_upgrade
130end 150end
131 151
132--- Attempt to match dependencies of a rockspec to installed rocks. 152
133-- @param dependencies table: The table of dependencies. 153
134-- @param rocks_provided table: The table of auto-provided dependencies. 154
135-- @param skip_set table or nil: Program versions to not use as valid matches. 155
136-- Table where keys are program names and values are tables where keys 156
137-- are program versions and values are 'true'. 157
138-- @param deps_mode string: Which trees to check dependencies for 158
139-- @return table, table, table: A table where keys are dependencies parsed 159
140-- in table format and values are tables containing fields 'name' and 160
141-- version' representing matches; a table of missing dependencies 161
142-- parsed as tables; and a table of "no-upgrade" missing dependencies 162
143-- (to be used in plugin modules so that a plugin does not force upgrade of 163
144-- its parent application). 164
145function deps.match_deps(dependencies, rocks_provided, deps_mode, skip_set) 165function deps.match_deps(dependencies, rocks_provided, deps_mode, skip_set)
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 166
151 local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies", skip_set) 167 local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies", skip_set)
152 return match_all_deps(dependencies, get_versions) 168 return match_all_deps(dependencies, get_versions)
153end 169end
154 170
155local function rock_status(dep, get_versions) 171local function rock_status(dep, get_versions)
156 assert(dep:type() == "query")
157 assert(type(get_versions) == "function")
158
159 local installed, _, _, provided = match_dep(dep, get_versions) 172 local installed, _, _, provided = match_dep(dep, get_versions)
160 local installation_type = provided and "provided by VM" or "installed" 173 local installation_type = provided and "provided by VM" or "installed"
161 return installed and installed.." "..installation_type..": success" or "not installed" 174 return installed and installed .. " " .. installation_type .. ": success" or "not installed"
162end 175end
163 176
164--- Check depenendencies of a package and report any missing ones. 177
165-- @param name string: package name. 178
166-- @param version string: package version. 179
167-- @param dependencies table: array of dependencies. 180
168-- @param deps_mode string: Which trees to check dependencies for 181
169-- @param rocks_provided table: A table of auto-dependencies provided 182
170-- by this Lua implementation for the given dependency. 183
171-- "one" for the current default tree, "all" for all trees, 184
172-- "order" for all trees with priority >= the current default, "none" for no trees. 185
173function deps.report_missing_dependencies(name, version, dependencies, deps_mode, rocks_provided) 186function deps.report_missing_dependencies(name, version, dependencies, deps_mode, rocks_provided)
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 187
180 if deps_mode == "none" then 188 if deps_mode == "none" then
181 return 189 return
@@ -200,11 +208,6 @@ function deps.report_missing_dependencies(name, version, dependencies, deps_mode
200end 208end
201 209
202function deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey) 210function deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey)
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 211
209 deps_mode = deps_mode or "all" 212 deps_mode = deps_mode or "all"
210 rocks_provided = rocks_provided or {} 213 rocks_provided = rocks_provided or {}
@@ -224,9 +227,9 @@ function deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey
224 227
225 local url, search_err = search.find_suitable_rock(dep) 228 local url, search_err = search.find_suitable_rock(dep)
226 if not url then 229 if not url then
227 return nil, "Could not satisfy dependency "..tostring(dep)..": "..search_err 230 return nil, "Could not satisfy dependency " .. tostring(dep) .. ": " .. search_err
228 end 231 end
229 util.printout("Installing "..url) 232 util.printout("Installing " .. url)
230 local install_args = { 233 local install_args = {
231 rock = url, 234 rock = url,
232 deps_mode = deps_mode, 235 deps_mode = deps_mode,
@@ -235,7 +238,7 @@ function deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey
235 } 238 }
236 local ok, install_err, errcode = install.command(install_args) 239 local ok, install_err, errcode = install.command(install_args)
237 if not ok then 240 if not ok then
238 return nil, "Failed installing dependency: "..url.." - "..install_err, errcode 241 return nil, "Failed installing dependency: " .. url .. " - " .. install_err, errcode
239 end 242 end
240 243
241 found, where = match_dep(dep, get_versions) 244 found, where = match_dep(dep, get_versions)
@@ -246,15 +249,15 @@ function deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey
246end 249end
247 250
248local function check_supported_platforms(rockspec) 251local function check_supported_platforms(rockspec)
249 if rockspec.supported_platforms and next(rockspec.supported_platforms) then 252 if rockspec.supported_platforms and next(rockspec.supported_platforms) ~= nil then
250 local all_negative = true 253 local all_negative = true
251 local supported = false 254 local supported = false
252 for _, plat in pairs(rockspec.supported_platforms) do 255 for _, plat in ipairs(rockspec.supported_platforms) do
253 local neg 256 local neg
254 neg, plat = plat:match("^(!?)(.*)") 257 neg, plat = plat:match("^(!?)(.*)")
255 if neg == "!" then 258 if neg == "!" then
256 if cfg.is_platform(plat) then 259 if cfg.is_platform(plat) then
257 return nil, "This rockspec for "..rockspec.package.." does not support "..plat.." platforms." 260 return nil, "This rockspec for " .. rockspec.package .. " does not support " .. plat .. " platforms."
258 end 261 end
259 else 262 else
260 all_negative = false 263 all_negative = false
@@ -266,32 +269,26 @@ local function check_supported_platforms(rockspec)
266 end 269 end
267 if supported == false and not all_negative then 270 if supported == false and not all_negative then
268 local plats = cfg.print_platforms() 271 local plats = cfg.print_platforms()
269 return nil, "This rockspec for "..rockspec.package.." does not support "..plats.." platforms." 272 return nil, "This rockspec for " .. rockspec.package .. " does not support " .. plats .. " platforms."
270 end 273 end
271 end 274 end
272 275
273 return true 276 return true
274end 277end
275 278
276--- Check dependencies of a rock and attempt to install any missing ones.
277-- Packages are installed using the LuaRocks "install" command.
278-- Aborts the program if a dependency could not be fulfilled.
279-- @param rockspec table: A rockspec in table format.
280-- @param depskey string: Rockspec key to fetch to get dependency table
281-- ("dependencies", "build_dependencies", etc.).
282-- @param deps_mode string
283-- @param verify boolean
284-- @param deplock_dir string: dirname of the deplock file
285-- @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-- error code.
288function deps.fulfill_dependencies(rockspec, depskey, deps_mode, verify, deplock_dir)
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 279
280
281
282
283
284
285
286
287
288
289
290
291function deps.fulfill_dependencies(rockspec, depskey, deps_mode, verify, deplock_dir)
295 local name = rockspec.name 292 local name = rockspec.name
296 local version = rockspec.version 293 local version = rockspec.version
297 local rocks_provided = rockspec.rocks_provided 294 local rocks_provided = rockspec.rocks_provided
@@ -301,16 +298,23 @@ function deps.fulfill_dependencies(rockspec, depskey, deps_mode, verify, deplock
301 util.printout("Using dependencies pinned in lockfile: " .. filename) 298 util.printout("Using dependencies pinned in lockfile: " .. filename)
302 299
303 local get_versions = prepare_get_versions("none", rocks_provided, depskey) 300 local get_versions = prepare_get_versions("none", rocks_provided, depskey)
301 local dnsnamestr, dversionstr
304 for dnsname, dversion in deplocks.each(depskey) do 302 for dnsname, dversion in deplocks.each(depskey) do
305 local dname, dnamespace = util.split_namespace(dnsname) 303 if type(dnsname) == "string" then
306 local dep = queries.new(dname, dnamespace, dversion) 304 dnsnamestr = dnsname
305 end
306 if type(dversion) == "string" then
307 dversionstr = dversion
308 end
309 local dname, dnamespace = util.split_namespace(dnsnamestr)
310 local dep = queries.new(dname, dnamespace, dversionstr)
307 311
308 util.printout(("%s %s is pinned to %s (%s)"):format( 312 util.printout(("%s %s is pinned to %s (%s)"):format(
309 name, version, tostring(dep), rock_status(dep, get_versions))) 313 name, version, tostring(dep), rock_status(dep, get_versions)))
310 314
311 local ok, err = deps.fulfill_dependency(dep, "none", rocks_provided, verify, depskey) 315 local okfullfill, errfullfill = deps.fulfill_dependency(dep, "none", rocks_provided, verify, depskey)
312 if not ok then 316 if not okfullfill then
313 return nil, err 317 return nil, errfullfill
314 end 318 end
315 end 319 end
316 util.printout() 320 util.printout()
@@ -324,28 +328,28 @@ function deps.fulfill_dependencies(rockspec, depskey, deps_mode, verify, deplock
324 return nil, err 328 return nil, err
325 end 329 end
326 330
327 deps.report_missing_dependencies(name, version, rockspec[depskey], deps_mode, rocks_provided) 331 deps.report_missing_dependencies(name, version, (rockspec)[depskey], deps_mode, rocks_provided)
328 332
329 util.printout() 333 util.printout()
330 334
331 local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey) 335 local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey)
332 for _, dep in ipairs(rockspec[depskey]) do 336 for _, dep in ipairs((rockspec)[depskey]) do
333 337
334 util.printout(("%s %s depends on %s (%s)"):format( 338 util.printout(("%s %s depends on %s (%s)"):format(
335 name, version, tostring(dep), rock_status(dep, get_versions))) 339 name, version, tostring(dep), rock_status(dep, get_versions)))
336 340
337 local ok, found_or_err, _, no_upgrade = deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey) 341 local okfulfill, found_or_err, _ = deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey)
338 if ok then 342 if okfulfill then
339 deplocks.add(depskey, dep.name, found_or_err) 343 deplocks.add(depskey, dep.name, found_or_err)
340 else 344 else
341 if no_upgrade then 345
342 util.printerr("This version of "..name.." is designed for use with") 346
343 util.printerr(tostring(dep)..", but is configured to avoid upgrading it") 347
344 util.printerr("automatically. Please upgrade "..dep.name.." with") 348
345 util.printerr(" luarocks install "..dep.name) 349
346 util.printerr("or look for a suitable version of "..name.." with") 350
347 util.printerr(" luarocks search "..name) 351
348 end 352
349 return nil, found_or_err 353 return nil, found_or_err
350 end 354 end
351 end 355 end
@@ -353,28 +357,28 @@ function deps.fulfill_dependencies(rockspec, depskey, deps_mode, verify, deplock
353 return true 357 return true
354end 358end
355 359
356--- If filename matches a pattern, return the capture. 360
357-- For example, given "libfoo.so" and "lib?.so" is a pattern, 361
358-- returns "foo" (which can then be used to build names 362
359-- based on other patterns. 363
360-- @param file string: a filename 364
361-- @param pattern string: a pattern, where ? is to be matched by the filename. 365
362-- @return string The pattern, if found, or nil. 366
363local function deconstruct_pattern(file, pattern) 367local function deconstruct_pattern(file, pattern)
364 local depattern = "^"..(pattern:gsub("%.", "%%."):gsub("%*", ".*"):gsub("?", "(.*)")).."$" 368 local depattern = "^" .. (pattern:gsub("%.", "%%."):gsub("%*", ".*"):gsub("?", "(.*)")) .. "$"
365 return (file:match(depattern)) 369 return (file:match(depattern))
366end 370end
367 371
368--- Construct all possible patterns for a name and add to the files array. 372
369-- Run through the patterns array replacing all occurrences of "?" 373
370-- with the given file name and store them in the files array. 374
371-- @param file string A raw name (e.g. "foo") 375
372-- @param array of string An array of patterns with "?" as the wildcard 376
373-- (e.g. {"?.so", "lib?.so"}) 377
374-- @param files The array of constructed names 378
375local function add_all_patterns(file, patterns, files) 379local function add_all_patterns(file, patterns, files)
376 for _, pattern in ipairs(patterns) do 380 for _, pattern in ipairs(patterns) do
377 table.insert(files, {#files + 1, (pattern:gsub("?", file))}) 381 table.insert(files, { #files + 1, (pattern:gsub("?", file)) })
378 end 382 end
379end 383end
380 384
@@ -388,7 +392,7 @@ local function get_external_deps_dirs(mode)
388 local dirs = { 392 local dirs = {
389 BINDIR = { subdir = subdirs.bin, testfile = "program", pattern = patterns.bin }, 393 BINDIR = { subdir = subdirs.bin, testfile = "program", pattern = patterns.bin },
390 INCDIR = { subdir = subdirs.include, testfile = "header", pattern = patterns.include }, 394 INCDIR = { subdir = subdirs.include, testfile = "header", pattern = patterns.include },
391 LIBDIR = { subdir = subdirs.lib, testfile = "library", pattern = patterns.lib } 395 LIBDIR = { subdir = subdirs.lib, testfile = "library", pattern = patterns.lib },
392 } 396 }
393 if mode == "install" then 397 if mode == "install" then
394 dirs.INCDIR = nil 398 dirs.INCDIR = nil
@@ -416,7 +420,7 @@ local function resolve_prefix(prefix, dirs)
416end 420end
417 421
418local function add_patterns_for_file(files, file, patterns) 422local function add_patterns_for_file(files, file, patterns)
419 -- If it doesn't look like it contains a filename extension 423
420 if not (file:match("%.[a-z]+$") or file:match("%.[a-z]+%.")) then 424 if not (file:match("%.[a-z]+$") or file:match("%.[a-z]+%.")) then
421 add_all_patterns(file, patterns, files) 425 add_all_patterns(file, patterns, files)
422 else 426 else
@@ -426,26 +430,35 @@ local function add_patterns_for_file(files, file, patterns)
426 add_all_patterns(matched, patterns, files) 430 add_all_patterns(matched, patterns, files)
427 end 431 end
428 end 432 end
429 table.insert(files, {#files + 1, file}) 433 table.insert(files, { #files + 1, file })
430 end 434 end
431end 435end
432 436
433local function check_external_dependency_at(prefix, name, ext_files, vars, dirs, err_files, cache) 437local function check_external_dependency_at(
438 prefix,
439 name,
440 ext_files,
441 vars,
442 dirs,
443 err_files,
444 cache)
445
434 local fs = require("luarocks.fs") 446 local fs = require("luarocks.fs")
435 cache = cache or {} 447 cache = cache or {}
436 448
437 for dirname, dirdata in util.sortedpairs(dirs) do 449 for dirname, dirdata in util.sortedpairs(dirs) do
438 local paths 450 local paths
439 local path_var_value = vars[name.."_"..dirname] 451 local path_var_value = vars[name .. "_" .. dirname]
452 local dirdatastr = dirdata.subdir
440 if path_var_value then 453 if path_var_value then
441 paths = { path_var_value } 454 paths = { path_var_value }
442 elseif type(dirdata.subdir) == "table" then 455 elseif type(dirdatastr) == "table" then
443 paths = {} 456 paths = {}
444 for i,v in ipairs(dirdata.subdir) do 457 for i, v in ipairs(dirdatastr) do
445 paths[i] = dir.path(prefix, v) 458 paths[i] = dir.path(prefix, v)
446 end 459 end
447 else 460 else
448 paths = { dir.path(prefix, dirdata.subdir) } 461 paths = { dir.path(prefix, dirdatastr) }
449 end 462 end
450 local file_or_files = ext_files[dirdata.testfile] 463 local file_or_files = ext_files[dirdata.testfile]
451 if file_or_files then 464 if file_or_files then
@@ -471,15 +484,15 @@ local function check_external_dependency_at(prefix, name, ext_files, vars, dirs,
471 for _, fa in ipairs(files) do 484 for _, fa in ipairs(files) do
472 485
473 local f = fa[2] 486 local f = fa[2]
474 -- small convenience hack 487
475 if f:match("%.so$") or f:match("%.dylib$") or f:match("%.dll$") then 488 if f:match("%.so$") or f:match("%.dylib$") or f:match("%.dll$") then
476 f = f:gsub("%.[^.]+$", "."..cfg.external_lib_extension) 489 f = f:gsub("%.[^.]+$", "." .. cfg.external_lib_extension)
477 end 490 end
478 491
479 local pattern 492 local pattern
480 if f:match("%*") then 493 if f:match("%*") then
481 pattern = "^" .. f:gsub("([-.+])", "%%%1"):gsub("%*", ".*") .. "$" 494 pattern = "^" .. f:gsub("([-.+])", "%%%1"):gsub("%*", ".*") .. "$"
482 f = "matching "..f 495 f = "matching " .. f
483 end 496 end
484 497
485 for _, d in ipairs(paths) do 498 for _, d in ipairs(paths) do
@@ -502,7 +515,7 @@ local function check_external_dependency_at(prefix, name, ext_files, vars, dirs,
502 dirdata.file = f 515 dirdata.file = f
503 break 516 break
504 else 517 else
505 table.insert(err_files[dirdata.testfile], f.." in "..d) 518 table.insert(err_files[dirdata.testfile], f .. " in " .. d)
506 end 519 end
507 end 520 end
508 if found then 521 if found then
@@ -513,9 +526,9 @@ local function check_external_dependency_at(prefix, name, ext_files, vars, dirs,
513 return nil, dirname, dirdata.testfile 526 return nil, dirname, dirdata.testfile
514 end 527 end
515 else 528 else
516 -- When we have a set of subdir suffixes, look for one that exists. 529
517 -- For these reason, we now put "lib" ahead of "" on Windows in our 530
518 -- default set. 531
519 dirdata.dir = paths[1] 532 dirdata.dir = paths[1]
520 for _, p in ipairs(paths) do 533 for _, p in ipairs(paths) do
521 if fs.exists(p) then 534 if fs.exists(p) then
@@ -527,18 +540,23 @@ local function check_external_dependency_at(prefix, name, ext_files, vars, dirs,
527 end 540 end
528 541
529 for dirname, dirdata in pairs(dirs) do 542 for dirname, dirdata in pairs(dirs) do
530 vars[name.."_"..dirname] = dirdata.dir 543 vars[name .. "_" .. dirname] = dirdata.dir
531 vars[name.."_"..dirname.."_FILE"] = dirdata.file 544 vars[name .. "_" .. dirname .. "_FILE"] = dirdata.file
532 end 545 end
533 vars[name.."_DIR"] = prefix 546 vars[name .. "_DIR"] = prefix
534 return true 547 return true
535end 548end
536 549
537local function check_external_dependency(name, ext_files, vars, mode, cache) 550local function check_external_dependency(
551 name,
552 ext_files,
553 vars,
554 mode,
555 cache)
538 local ok 556 local ok
539 local err_dirname 557 local err_dirname
540 local err_testfile 558 local err_testfile
541 local err_files = {program = {}, header = {}, library = {}} 559 local err_files = { program = {}, header = {}, library = {} }
542 560
543 local dirs = get_external_deps_dirs(mode) 561 local dirs = get_external_deps_dirs(mode)
544 562
@@ -572,7 +590,7 @@ local function check_external_dependency(name, ext_files, vars, mode, cache)
572end 590end
573 591
574function deps.autodetect_external_dependencies(build) 592function deps.autodetect_external_dependencies(build)
575 -- only applies to the 'builtin' build type 593
576 if not build or not build.modules then 594 if not build or not build.modules then
577 return nil 595 return nil
578 end 596 end
@@ -581,9 +599,12 @@ function deps.autodetect_external_dependencies(build)
581 local any = false 599 local any = false
582 for _, data in pairs(build.modules) do 600 for _, data in pairs(build.modules) do
583 if type(data) == "table" and data.libraries then 601 if type(data) == "table" and data.libraries then
584 local libraries = data.libraries 602 local libraries
585 if type(libraries) == "string" then 603 local librariesstr = data.libraries
586 libraries = { libraries } 604 if type(librariesstr) == "string" then
605 libraries = { librariesstr }
606 else
607 libraries = librariesstr
587 end 608 end
588 local incdirs = {} 609 local incdirs = {}
589 local libdirs = {} 610 local libdirs = {}
@@ -605,21 +626,20 @@ function deps.autodetect_external_dependencies(build)
605 return any and extdeps or nil 626 return any and extdeps or nil
606end 627end
607 628
608--- Set up path-related variables for external dependencies. 629
609-- For each key in the external_dependencies table in the 630
610-- rockspec file, four variables are created: <key>_DIR, <key>_BINDIR, 631
611-- <key>_INCDIR and <key>_LIBDIR. These are not overwritten 632
612-- if already set (e.g. by the LuaRocks config file or through the 633
613-- command-line). Values in the external_dependencies table 634
614-- are tables that may contain a "header" or a "library" field, 635
615-- with filenames to be tested for existence. 636
616-- @param rockspec table: The rockspec table. 637
617-- @param mode string: if "build" is given, checks all files; 638
618-- if "install" is given, do not scan for headers. 639
619-- @return boolean or (nil, string): True if no errors occurred, or 640
620-- nil and an error message if any test failed. 641
621function deps.check_external_deps(rockspec, mode) 642function deps.check_external_deps(rockspec, mode)
622 assert(rockspec:type() == "rockspec")
623 643
624 if not rockspec.external_dependencies then 644 if not rockspec.external_dependencies then
625 rockspec.external_dependencies = deps.autodetect_external_dependencies(rockspec.build) 645 rockspec.external_dependencies = deps.autodetect_external_dependencies(rockspec.build)
@@ -628,21 +648,21 @@ function deps.check_external_deps(rockspec, mode)
628 return true 648 return true
629 end 649 end
630 650
631 for name, ext_files in util.sortedpairs(rockspec.external_dependencies) do 651 for name, ext_files in util.sortedpairs(rockspec.external_dependencies.queries) do
632 local ok, err_dirname, err_testfile, err_files = check_external_dependency(name, ext_files, rockspec.variables, mode) 652 local ok, err_dirname, err_testfile, err_files = check_external_dependency(name, ext_files, rockspec.variables, mode)
633 if not ok then 653 if not ok then
634 local lines = {"Could not find "..err_testfile.." file for "..name} 654 local lines = { "Could not find " .. err_testfile .. " file for " .. name }
635 655
636 local err_paths = {} 656 local err_paths = {}
637 for _, err_file in ipairs(err_files[err_testfile]) do 657 for _, err_file in ipairs(err_files[err_testfile]) do
638 if not err_paths[err_file] then 658 if not err_paths[err_file] then
639 err_paths[err_file] = true 659 err_paths[err_file] = true
640 table.insert(lines, " No file "..err_file) 660 table.insert(lines, " No file " .. err_file)
641 end 661 end
642 end 662 end
643 663
644 table.insert(lines, "You may have to install "..name.." in your system and/or pass "..name.."_DIR or "..name.."_"..err_dirname.." to the luarocks command.") 664 table.insert(lines, "You may have to install " .. name .. " in your system and/or pass " .. name .. "_DIR or " .. name .. "_" .. err_dirname .. " to the luarocks command.")
645 table.insert(lines, "Example: luarocks install "..rockspec.name.." "..name.."_DIR=/usr/local") 665 table.insert(lines, "Example: luarocks install " .. rockspec.name .. " " .. name .. "_DIR=/usr/local")
646 666
647 return nil, table.concat(lines, "\n"), "dependency" 667 return nil, table.concat(lines, "\n"), "dependency"
648 end 668 end
@@ -650,18 +670,17 @@ function deps.check_external_deps(rockspec, mode)
650 return true 670 return true
651end 671end
652 672
653--- Recursively add satisfied dependencies of a package to a table, 673
654-- to build a transitive closure of all dependent packages. 674
655-- Additionally ensures that `dependencies` table of the manifest is up-to-date. 675
656-- @param results table: The results table being built, maps package names to versions. 676
657-- @param mdeps table: The manifest dependencies table. 677
658-- @param name string: Package name. 678
659-- @param version string: Package version. 679
660function deps.scan_deps(results, mdeps, name, version, deps_mode) 680function deps.scan_deps(results, mdeps, name, version, deps_mode)
661 assert(type(results) == "table") 681 assert(type(results) == "table")
662 assert(type(mdeps) == "table") 682 assert(type(mdeps) == "table")
663 assert(type(name) == "string" and not name:match("/")) 683 assert(not name:match("/"))
664 assert(type(version) == "string")
665 684
666 local fetch = require("luarocks.fetch") 685 local fetch = require("luarocks.fetch")
667 686
@@ -673,11 +692,11 @@ function deps.scan_deps(results, mdeps, name, version, deps_mode)
673 local dependencies = mdn[version] 692 local dependencies = mdn[version]
674 local rocks_provided 693 local rocks_provided
675 if not dependencies then 694 if not dependencies then
676 local rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version), false) 695 local rockspec = fetch.load_local_rockspec(path.rockspec_file(name, version), false)
677 if not rockspec then 696 if not rockspec then
678 return 697 return
679 end 698 end
680 dependencies = rockspec.dependencies 699 dependencies = rockspec.dependencies.queries
681 rocks_provided = rockspec.rocks_provided 700 rocks_provided = rockspec.rocks_provided
682 mdn[version] = dependencies 701 mdn[version] = dependencies
683 else 702 else
@@ -736,13 +755,13 @@ local function find_lua_incdir(prefix, luaver, luajitver)
736 end 755 end
737 end 756 end
738 757
739 -- not found, will fallback to a default 758
740 return nil, mainerr 759 return nil, mainerr
741end 760end
742 761
743function deps.check_lua_incdir(vars) 762function deps.check_lua_incdir(vars)
744 if vars.LUA_INCDIR_OK == true 763 if vars.LUA_INCDIR_OK == true then
745 then return true 764 return true
746 end 765 end
747 766
748 local ljv = util.get_luajit_version() 767 local ljv = util.get_luajit_version()
@@ -769,8 +788,8 @@ function deps.check_lua_incdir(vars)
769end 788end
770 789
771function deps.check_lua_libdir(vars) 790function deps.check_lua_libdir(vars)
772 if vars.LUA_LIBDIR_OK == true 791 if vars.LUA_LIBDIR_OK == true then
773 then return true 792 return true
774 end 793 end
775 794
776 local fs = require("luarocks.fs") 795 local fs = require("luarocks.fs")
@@ -803,10 +822,10 @@ function deps.check_lua_libdir(vars)
803 local fd = io.open(filename, "r") 822 local fd = io.open(filename, "r")
804 if fd then 823 if fd then
805 if not vars.LUA_LIBDIR_FILE:match((cfg.lua_version:gsub("%.", "%%.?"))) then 824 if not vars.LUA_LIBDIR_FILE:match((cfg.lua_version:gsub("%.", "%%.?"))) then
806 -- if filename isn't versioned, check file contents 825
807 local txt = fd:read("*a") 826 local txt = fd:read("*a")
808 ok = txt:match("Lua " .. cfg.lua_version, 1, true) 827 ok = txt:find("Lua " .. cfg.lua_version, 1, true) or
809 or txt:match("lua" .. (cfg.lua_version:gsub("%.", "")), 1, true) 828 txt:find("lua" .. (cfg.lua_version:gsub("%.", "")), 1, true)
810 if not ok then 829 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." 830 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 831 end