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