diff options
Diffstat (limited to 'src/luarocks/deps.lua')
-rw-r--r-- | src/luarocks/deps.lua | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua new file mode 100644 index 00000000..c960f3b7 --- /dev/null +++ b/src/luarocks/deps.lua | |||
@@ -0,0 +1,877 @@ | |||
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 | ||
2 | |||
3 | local deps = {} | ||
4 | |||
5 | |||
6 | |||
7 | local cfg = require("luarocks.core.cfg") | ||
8 | local manif = require("luarocks.manif") | ||
9 | local path = require("luarocks.path") | ||
10 | local dir = require("luarocks.dir") | ||
11 | local fun = require("luarocks.fun") | ||
12 | local util = require("luarocks.util") | ||
13 | local vers = require("luarocks.core.vers") | ||
14 | local queries = require("luarocks.queries") | ||
15 | local deplocks = require("luarocks.deplocks") | ||
16 | |||
17 | |||
18 | |||
19 | |||
20 | |||
21 | |||
22 | |||
23 | |||
24 | |||
25 | |||
26 | |||
27 | |||
28 | |||
29 | |||
30 | |||
31 | |||
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 | local function prepare_get_versions(deps_mode, rocks_provided, depskey, skip_set) | ||
58 | |||
59 | return function(dep) | ||
60 | local versions, locations | ||
61 | local provided = rocks_provided[dep.name] | ||
62 | if provided then | ||
63 | |||
64 | versions, locations = { provided }, {} | ||
65 | else | ||
66 | if deps_mode == "none" then | ||
67 | deps_mode = "one" | ||
68 | end | ||
69 | versions, locations = manif.get_versions(dep, deps_mode) | ||
70 | end | ||
71 | |||
72 | if skip_set and skip_set[dep.name] then | ||
73 | for i = #versions, 1, -1 do | ||
74 | local v = versions[i] | ||
75 | if skip_set[dep.name][v] then | ||
76 | table.remove(versions, i) | ||
77 | end | ||
78 | end | ||
79 | end | ||
80 | |||
81 | local lockversion = deplocks.get(depskey, dep.name) | ||
82 | |||
83 | return versions, locations, lockversion, provided ~= nil | ||
84 | end | ||
85 | end | ||
86 | |||
87 | |||
88 | |||
89 | |||
90 | |||
91 | |||
92 | |||
93 | |||
94 | |||
95 | |||
96 | |||
97 | |||
98 | |||
99 | local function match_dep(depq, | ||
100 | get_versions) | ||
101 | |||
102 | local versions, locations, lockversion, provided = get_versions(depq) | ||
103 | |||
104 | local latest_version | ||
105 | local latest_vstring | ||
106 | for _, vstring in ipairs(versions) do | ||
107 | local version = vers.parse_version(vstring) | ||
108 | if vers.match_constraints(version, depq.constraints) then | ||
109 | if not latest_version or version > latest_version then | ||
110 | latest_version = version | ||
111 | latest_vstring = vstring | ||
112 | end | ||
113 | end | ||
114 | end | ||
115 | |||
116 | if lockversion and not locations[lockversion] then | ||
117 | local latest_matching_msg = "" | ||
118 | if latest_vstring and latest_vstring ~= lockversion then | ||
119 | latest_matching_msg = " (latest matching is " .. latest_vstring .. ")" | ||
120 | end | ||
121 | util.printout("Forcing " .. depq.name .. " to pinned version " .. lockversion .. latest_matching_msg) | ||
122 | return nil, nil, queries.new(depq.name, depq.namespace, lockversion) | ||
123 | end | ||
124 | |||
125 | return latest_vstring, locations[latest_vstring], depq, provided | ||
126 | end | ||
127 | |||
128 | local function match_all_deps(dependencies, | ||
129 | get_versions) | ||
130 | |||
131 | local matched, missing, no_upgrade = {}, {}, {} | ||
132 | |||
133 | for _, depq in ipairs(dependencies) do | ||
134 | local found, _, provided | ||
135 | found, _, depq, provided = match_dep(depq, get_versions) | ||
136 | if found then | ||
137 | if not provided then | ||
138 | matched[depq] = { name = depq.name, version = found } | ||
139 | end | ||
140 | else | ||
141 | if depq.constraints and depq.constraints[1] and depq.constraints[1].no_upgrade then | ||
142 | no_upgrade[depq.name] = depq | ||
143 | else | ||
144 | missing[depq.name] = depq | ||
145 | end | ||
146 | end | ||
147 | end | ||
148 | return matched, missing, no_upgrade | ||
149 | end | ||
150 | |||
151 | |||
152 | |||
153 | |||
154 | |||
155 | |||
156 | |||
157 | |||
158 | |||
159 | |||
160 | |||
161 | |||
162 | |||
163 | |||
164 | function deps.match_deps(dependencies, rocks_provided, deps_mode, skip_set) | ||
165 | |||
166 | local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies", skip_set) | ||
167 | return match_all_deps(dependencies, get_versions) | ||
168 | end | ||
169 | |||
170 | local function rock_status(dep, get_versions) | ||
171 | local installed, _, _, provided = match_dep(dep, get_versions) | ||
172 | local installation_type = provided and "provided by VM" or "installed" | ||
173 | return installed and installed .. " " .. installation_type .. ": success" or "not installed" | ||
174 | end | ||
175 | |||
176 | |||
177 | |||
178 | |||
179 | |||
180 | |||
181 | |||
182 | |||
183 | |||
184 | |||
185 | function deps.report_missing_dependencies(name, version, dependencies, deps_mode, rocks_provided) | ||
186 | |||
187 | if deps_mode == "none" then | ||
188 | return | ||
189 | end | ||
190 | |||
191 | local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies") | ||
192 | |||
193 | local first_missing_dep = true | ||
194 | |||
195 | for _, depq in ipairs(dependencies) do | ||
196 | local found, _ | ||
197 | found, _, depq = match_dep(depq, get_versions) | ||
198 | if not found then | ||
199 | if first_missing_dep then | ||
200 | util.printout(("Missing dependencies for %s %s:"):format(name, version)) | ||
201 | first_missing_dep = false | ||
202 | end | ||
203 | |||
204 | util.printout((" %s (%s)"):format(tostring(depq), rock_status(depq, get_versions))) | ||
205 | end | ||
206 | end | ||
207 | end | ||
208 | |||
209 | function deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey) | ||
210 | |||
211 | deps_mode = deps_mode or "all" | ||
212 | rocks_provided = rocks_provided or {} | ||
213 | |||
214 | local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey) | ||
215 | |||
216 | local found, where | ||
217 | found, where, dep = match_dep(dep, get_versions) | ||
218 | if found then | ||
219 | local tree_manifests = manif.load_rocks_tree_manifests(deps_mode) | ||
220 | manif.scan_dependencies(dep.name, found, tree_manifests, deplocks.proxy(depskey)) | ||
221 | return true, found, where | ||
222 | end | ||
223 | |||
224 | local search = require("luarocks.search") | ||
225 | |||
226 | local url, search_err = search.find_suitable_rock(dep) | ||
227 | if not url then | ||
228 | return nil, "Could not satisfy dependency " .. tostring(dep) .. ": " .. search_err | ||
229 | end | ||
230 | util.printout("Installing " .. url) | ||
231 | local install_args = { | ||
232 | rock = url, | ||
233 | deps_mode = deps_mode, | ||
234 | namespace = dep.namespace, | ||
235 | verify = verify, | ||
236 | } | ||
237 | local ok, install_err, errcode = deps.installer(install_args) | ||
238 | if not ok then | ||
239 | return nil, "Failed installing dependency: " .. url .. " - " .. install_err, errcode | ||
240 | end | ||
241 | |||
242 | found, where = match_dep(dep, get_versions) | ||
243 | if not found then | ||
244 | return nil, "Repository inconsistency detected (previously unfinished/corrupted installation?)" | ||
245 | end | ||
246 | return true, found, where | ||
247 | end | ||
248 | |||
249 | local function check_supported_platforms(rockspec) | ||
250 | if rockspec.supported_platforms and next(rockspec.supported_platforms) then | ||
251 | local all_negative = true | ||
252 | local supported = false | ||
253 | for _, plat in ipairs(rockspec.supported_platforms) do | ||
254 | local neg | ||
255 | neg, plat = plat:match("^(!?)(.*)") | ||
256 | if neg == "!" then | ||
257 | if cfg.is_platform(plat) then | ||
258 | return nil, "This rockspec for " .. rockspec.package .. " does not support " .. plat .. " platforms." | ||
259 | end | ||
260 | else | ||
261 | all_negative = false | ||
262 | if cfg.is_platform(plat) then | ||
263 | supported = true | ||
264 | break | ||
265 | end | ||
266 | end | ||
267 | end | ||
268 | if supported == false and not all_negative then | ||
269 | local plats = cfg.print_platforms() | ||
270 | return nil, "This rockspec for " .. rockspec.package .. " does not support " .. plats .. " platforms." | ||
271 | end | ||
272 | end | ||
273 | |||
274 | return true | ||
275 | end | ||
276 | |||
277 | |||
278 | |||
279 | |||
280 | |||
281 | |||
282 | |||
283 | |||
284 | |||
285 | |||
286 | |||
287 | |||
288 | |||
289 | function deps.fulfill_dependencies(rockspec, depskey, deps_mode, verify, deplock_dir) | ||
290 | local name = rockspec.name | ||
291 | local version = rockspec.version | ||
292 | local rocks_provided = rockspec.rocks_provided | ||
293 | |||
294 | local ok, filename, err = deplocks.load(name, deplock_dir or ".") | ||
295 | if filename then | ||
296 | util.printout("Using dependencies pinned in lockfile: " .. filename) | ||
297 | |||
298 | local get_versions = prepare_get_versions("none", rocks_provided, depskey) | ||
299 | local dnsnamestr, dversionstr | ||
300 | for dnsname, dversion in deplocks.each(depskey) do | ||
301 | if type(dnsname) == "string" then | ||
302 | dnsnamestr = dnsname | ||
303 | end | ||
304 | if type(dversion) == "string" then | ||
305 | dversionstr = dversion | ||
306 | end | ||
307 | local dname, dnamespace = util.split_namespace(dnsnamestr) | ||
308 | local depq = queries.new(dname, dnamespace, dversionstr) | ||
309 | |||
310 | util.printout(("%s %s is pinned to %s (%s)"):format( | ||
311 | name, version, tostring(depq), rock_status(depq, get_versions))) | ||
312 | |||
313 | local okfullfill, errfullfill = deps.fulfill_dependency(depq, "none", rocks_provided, verify, depskey) | ||
314 | if not okfullfill then | ||
315 | return nil, errfullfill | ||
316 | end | ||
317 | end | ||
318 | util.printout() | ||
319 | return true | ||
320 | elseif err then | ||
321 | util.warning(err) | ||
322 | end | ||
323 | |||
324 | ok, err = check_supported_platforms(rockspec) | ||
325 | if not ok then | ||
326 | return nil, err | ||
327 | end | ||
328 | |||
329 | deps.report_missing_dependencies(name, version, (rockspec)[depskey].queries, deps_mode, rocks_provided) | ||
330 | |||
331 | util.printout() | ||
332 | |||
333 | local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey) | ||
334 | for _, depq in ipairs((rockspec)[depskey].queries) do | ||
335 | |||
336 | util.printout(("%s %s depends on %s (%s)"):format( | ||
337 | name, version, tostring(depq), rock_status(depq, get_versions))) | ||
338 | |||
339 | local okfulfill, found_or_err, _ = deps.fulfill_dependency(depq, deps_mode, rocks_provided, verify, depskey) | ||
340 | if okfulfill then | ||
341 | deplocks.add(depskey, depq.name, found_or_err) | ||
342 | else | ||
343 | |||
344 | |||
345 | |||
346 | |||
347 | |||
348 | |||
349 | |||
350 | |||
351 | return nil, found_or_err | ||
352 | end | ||
353 | end | ||
354 | |||
355 | return true | ||
356 | end | ||
357 | |||
358 | |||
359 | |||
360 | |||
361 | |||
362 | |||
363 | |||
364 | |||
365 | local function deconstruct_pattern(file, pattern) | ||
366 | local depattern = "^" .. (pattern:gsub("%.", "%%."):gsub("%*", ".*"):gsub("?", "(.*)")) .. "$" | ||
367 | return (file:match(depattern)) | ||
368 | end | ||
369 | |||
370 | |||
371 | |||
372 | |||
373 | |||
374 | |||
375 | |||
376 | |||
377 | local function add_all_patterns(file, patterns, files) | ||
378 | for _, pattern in ipairs(patterns) do | ||
379 | table.insert(files, { #files + 1, (pattern:gsub("?", file)) }) | ||
380 | end | ||
381 | end | ||
382 | |||
383 | local function get_external_deps_dirs(mode) | ||
384 | local patterns = cfg.external_deps_patterns | ||
385 | local subdirs = cfg.external_deps_subdirs | ||
386 | if mode == "install" then | ||
387 | patterns = cfg.runtime_external_deps_patterns | ||
388 | subdirs = cfg.runtime_external_deps_subdirs | ||
389 | end | ||
390 | local dirs = { | ||
391 | BINDIR = { subdir = subdirs.bin, testfile = "program", pattern = patterns.bin }, | ||
392 | INCDIR = { subdir = subdirs.include, testfile = "header", pattern = patterns.include }, | ||
393 | LIBDIR = { subdir = subdirs.lib, testfile = "library", pattern = patterns.lib }, | ||
394 | } | ||
395 | if mode == "install" then | ||
396 | dirs.INCDIR = nil | ||
397 | end | ||
398 | return dirs | ||
399 | end | ||
400 | |||
401 | local function resolve_prefix(prefix, dirs) | ||
402 | if type(prefix) == "string" then | ||
403 | return prefix | ||
404 | elseif type(prefix) == "table" then | ||
405 | if prefix.bin then | ||
406 | dirs.BINDIR.subdir = prefix.bin | ||
407 | end | ||
408 | if prefix.include then | ||
409 | if dirs.INCDIR then | ||
410 | dirs.INCDIR.subdir = prefix.include | ||
411 | end | ||
412 | end | ||
413 | if prefix.lib then | ||
414 | dirs.LIBDIR.subdir = prefix.lib | ||
415 | end | ||
416 | return prefix.prefix | ||
417 | end | ||
418 | end | ||
419 | |||
420 | local function add_patterns_for_file(files, file, patterns) | ||
421 | |||
422 | if not (file:match("%.[a-z]+$") or file:match("%.[a-z]+%.")) then | ||
423 | add_all_patterns(file, patterns, files) | ||
424 | else | ||
425 | for _, pattern in ipairs(patterns) do | ||
426 | local matched = deconstruct_pattern(file, pattern) | ||
427 | if matched then | ||
428 | add_all_patterns(matched, patterns, files) | ||
429 | end | ||
430 | end | ||
431 | table.insert(files, { #files + 1, file }) | ||
432 | end | ||
433 | end | ||
434 | |||
435 | local function check_external_dependency_at( | ||
436 | prefix, | ||
437 | name, | ||
438 | ext_files, | ||
439 | vars, | ||
440 | dirs, | ||
441 | err_files, | ||
442 | cache) | ||
443 | |||
444 | local fs = require("luarocks.fs") | ||
445 | cache = cache or {} | ||
446 | |||
447 | for dirname, dirdata in util.sortedpairs(dirs) do | ||
448 | local paths | ||
449 | local path_var_value = vars[name .. "_" .. dirname] | ||
450 | local dirdatastr = dirdata.subdir | ||
451 | if path_var_value then | ||
452 | paths = { path_var_value } | ||
453 | elseif type(dirdatastr) == "table" then | ||
454 | paths = {} | ||
455 | for i, v in ipairs(dirdatastr) do | ||
456 | paths[i] = dir.path(prefix, v) | ||
457 | end | ||
458 | else | ||
459 | paths = { dir.path(prefix, dirdatastr) } | ||
460 | end | ||
461 | local file_or_files = ext_files[dirdata.testfile] | ||
462 | if file_or_files then | ||
463 | local files = {} | ||
464 | if type(file_or_files) == "string" then | ||
465 | add_patterns_for_file(files, file_or_files, dirdata.pattern) | ||
466 | elseif type(file_or_files) == "table" then | ||
467 | for _, f in ipairs(file_or_files) do | ||
468 | add_patterns_for_file(files, f, dirdata.pattern) | ||
469 | end | ||
470 | end | ||
471 | |||
472 | local found = false | ||
473 | table.sort(files, function(a, b) | ||
474 | if (not a[2]:match("%*")) and b[2]:match("%*") then | ||
475 | return true | ||
476 | elseif a[2]:match("%*") and (not b[2]:match("%*")) then | ||
477 | return false | ||
478 | else | ||
479 | return a[1] < b[1] | ||
480 | end | ||
481 | end) | ||
482 | for _, fa in ipairs(files) do | ||
483 | |||
484 | local f = fa[2] | ||
485 | |||
486 | if f:match("%.so$") or f:match("%.dylib$") or f:match("%.dll$") then | ||
487 | f = f:gsub("%.[^.]+$", "." .. cfg.external_lib_extension) | ||
488 | end | ||
489 | |||
490 | local pattern | ||
491 | if f:match("%*") then | ||
492 | pattern = "^" .. f:gsub("([-.+])", "%%%1"):gsub("%*", ".*") .. "$" | ||
493 | f = "matching " .. f | ||
494 | end | ||
495 | |||
496 | for _, d in ipairs(paths) do | ||
497 | if pattern then | ||
498 | if not cache[d] then | ||
499 | cache[d] = fs.list_dir(d) | ||
500 | end | ||
501 | local match = string.match | ||
502 | for _, entry in ipairs(cache[d]) do | ||
503 | if match(entry, pattern) then | ||
504 | found = true | ||
505 | break | ||
506 | end | ||
507 | end | ||
508 | else | ||
509 | found = fs.is_file(dir.path(d, f)) | ||
510 | end | ||
511 | if found then | ||
512 | dirdata.dir = d | ||
513 | dirdata.file = f | ||
514 | break | ||
515 | else | ||
516 | table.insert(err_files[dirdata.testfile], f .. " in " .. d) | ||
517 | end | ||
518 | end | ||
519 | if found then | ||
520 | break | ||
521 | end | ||
522 | end | ||
523 | if not found then | ||
524 | return nil, dirname, dirdata.testfile | ||
525 | end | ||
526 | else | ||
527 | |||
528 | |||
529 | |||
530 | dirdata.dir = paths[1] | ||
531 | for _, p in ipairs(paths) do | ||
532 | if fs.exists(p) then | ||
533 | dirdata.dir = p | ||
534 | break | ||
535 | end | ||
536 | end | ||
537 | end | ||
538 | end | ||
539 | |||
540 | for dirname, dirdata in pairs(dirs) do | ||
541 | vars[name .. "_" .. dirname] = dirdata.dir | ||
542 | vars[name .. "_" .. dirname .. "_FILE"] = dirdata.file | ||
543 | end | ||
544 | vars[name .. "_DIR"] = prefix | ||
545 | return true | ||
546 | end | ||
547 | |||
548 | local function check_external_dependency( | ||
549 | name, | ||
550 | ext_files, | ||
551 | vars, | ||
552 | mode, | ||
553 | cache) | ||
554 | local ok | ||
555 | local err_dirname | ||
556 | local err_testfile | ||
557 | local err_files = { program = {}, header = {}, library = {} } | ||
558 | |||
559 | local dirs = get_external_deps_dirs(mode) | ||
560 | |||
561 | local prefixes | ||
562 | if vars[name .. "_DIR"] then | ||
563 | prefixes = { vars[name .. "_DIR"] } | ||
564 | elseif vars.DEPS_DIR then | ||
565 | prefixes = { vars.DEPS_DIR } | ||
566 | else | ||
567 | prefixes = cfg.external_deps_dirs | ||
568 | end | ||
569 | |||
570 | for _, prefix in ipairs(prefixes) do | ||
571 | prefix = resolve_prefix(prefix, dirs) | ||
572 | if cfg.is_platform("mingw32") and name == "LUA" then | ||
573 | dirs.LIBDIR.pattern = fun.filter(util.deep_copy(dirs.LIBDIR.pattern), function(s) | ||
574 | return not s:match("%.a$") | ||
575 | end) | ||
576 | elseif cfg.is_platform("windows") and name == "LUA" then | ||
577 | dirs.LIBDIR.pattern = fun.filter(util.deep_copy(dirs.LIBDIR.pattern), function(s) | ||
578 | return not s:match("%.dll$") | ||
579 | end) | ||
580 | end | ||
581 | ok, err_dirname, err_testfile = check_external_dependency_at(prefix, name, ext_files, vars, dirs, err_files, cache) | ||
582 | if ok then | ||
583 | return true | ||
584 | end | ||
585 | end | ||
586 | |||
587 | return nil, err_dirname, err_testfile, err_files | ||
588 | end | ||
589 | |||
590 | function deps.autodetect_external_dependencies(build) | ||
591 | |||
592 | if not build or not (build).modules then | ||
593 | return nil | ||
594 | end | ||
595 | |||
596 | local extdeps = {} | ||
597 | local any = false | ||
598 | for _, data in pairs((build).modules) do | ||
599 | if type(data) == "table" and data.libraries then | ||
600 | local libraries | ||
601 | local librariesstr = data.libraries | ||
602 | if type(librariesstr) == "string" then | ||
603 | libraries = { librariesstr } | ||
604 | else | ||
605 | libraries = librariesstr | ||
606 | end | ||
607 | local incdirs = {} | ||
608 | local libdirs = {} | ||
609 | for _, lib in ipairs(libraries) do | ||
610 | local upper = lib:upper():gsub("%+", "P"):gsub("[^%w]", "_") | ||
611 | any = true | ||
612 | extdeps[upper] = { library = lib } | ||
613 | table.insert(incdirs, "$(" .. upper .. "_INCDIR)") | ||
614 | table.insert(libdirs, "$(" .. upper .. "_LIBDIR)") | ||
615 | end | ||
616 | if not data.incdirs then | ||
617 | data.incdirs = incdirs | ||
618 | end | ||
619 | if not data.libdirs then | ||
620 | data.libdirs = libdirs | ||
621 | end | ||
622 | end | ||
623 | end | ||
624 | return any and extdeps or nil | ||
625 | end | ||
626 | |||
627 | |||
628 | |||
629 | |||
630 | |||
631 | |||
632 | |||
633 | |||
634 | |||
635 | |||
636 | |||
637 | |||
638 | |||
639 | |||
640 | function deps.check_external_deps(rockspec, mode) | ||
641 | |||
642 | if not rockspec.external_dependencies then | ||
643 | rockspec.external_dependencies = deps.autodetect_external_dependencies(rockspec.build) | ||
644 | end | ||
645 | if not rockspec.external_dependencies then | ||
646 | return true | ||
647 | end | ||
648 | |||
649 | for name, ext_files in util.sortedpairs(rockspec.external_dependencies) do | ||
650 | local ok, err_dirname, err_testfile, err_files = check_external_dependency(name, ext_files, rockspec.variables, mode) | ||
651 | if not ok then | ||
652 | local lines = { "Could not find " .. err_testfile .. " file for " .. name } | ||
653 | |||
654 | local err_paths = {} | ||
655 | for _, err_file in ipairs(err_files[err_testfile]) do | ||
656 | if not err_paths[err_file] then | ||
657 | err_paths[err_file] = true | ||
658 | table.insert(lines, " No file " .. err_file) | ||
659 | end | ||
660 | end | ||
661 | |||
662 | 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.") | ||
663 | table.insert(lines, "Example: luarocks install " .. rockspec.name .. " " .. name .. "_DIR=/usr/local") | ||
664 | |||
665 | return nil, table.concat(lines, "\n"), "dependency" | ||
666 | end | ||
667 | end | ||
668 | return true | ||
669 | end | ||
670 | |||
671 | |||
672 | |||
673 | |||
674 | |||
675 | |||
676 | |||
677 | |||
678 | function deps.scan_deps(results, mdeps, name, version, deps_mode) | ||
679 | assert(not name:match("/")) | ||
680 | |||
681 | local fetch = require("luarocks.fetch") | ||
682 | |||
683 | if results[name] then | ||
684 | return | ||
685 | end | ||
686 | if not mdeps[name] then mdeps[name] = {} end | ||
687 | local mdn = mdeps[name] | ||
688 | local dependencies = mdn[version] | ||
689 | local rocks_provided | ||
690 | if not dependencies then | ||
691 | local rockspec = fetch.load_local_rockspec(path.rockspec_file(name, version), false) | ||
692 | if not rockspec then | ||
693 | return | ||
694 | end | ||
695 | dependencies = rockspec.dependencies.queries | ||
696 | rocks_provided = rockspec.rocks_provided | ||
697 | mdn[version] = dependencies | ||
698 | else | ||
699 | rocks_provided = util.get_rocks_provided() | ||
700 | end | ||
701 | |||
702 | local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies") | ||
703 | |||
704 | local matched = match_all_deps(dependencies, get_versions) | ||
705 | results[name] = version | ||
706 | for _, match in pairs(matched) do | ||
707 | deps.scan_deps(results, mdeps, match.name, match.version, deps_mode) | ||
708 | end | ||
709 | end | ||
710 | |||
711 | local function lua_h_exists(d, luaver) | ||
712 | local major, minor = luaver:match("(%d+)%.(%d+)") | ||
713 | local luanum = ("%s%02d"):format(major, tonumber(minor)) | ||
714 | |||
715 | local lua_h = dir.path(d, "lua.h") | ||
716 | local fd = io.open(lua_h) | ||
717 | if fd then | ||
718 | local data = fd:read("*a") | ||
719 | fd:close() | ||
720 | if data:match("LUA_VERSION_NUM%s*" .. tostring(luanum)) then | ||
721 | return d ~= nil | ||
722 | end | ||
723 | return nil, "Lua header lua.h found at " .. d .. " does not match Lua version " .. luaver .. ". You can use `luarocks config variables.LUA_INCDIR <path>` to set the correct location.", "dependency", 2 | ||
724 | end | ||
725 | |||
726 | return nil, "Failed finding Lua header lua.h (searched at " .. d .. "). You may need to install Lua development headers. You can use `luarocks config variables.LUA_INCDIR <path>` to set the correct location.", "dependency", 1 | ||
727 | end | ||
728 | |||
729 | local function find_lua_incdir(prefix, luaver, luajitver) | ||
730 | luajitver = luajitver and luajitver:gsub("%-.*", "") | ||
731 | local shortv = luaver:gsub("%.", "") | ||
732 | local incdirs = { | ||
733 | prefix .. "/include/lua/" .. luaver, | ||
734 | prefix .. "/include/lua" .. luaver, | ||
735 | prefix .. "/include/lua-" .. luaver, | ||
736 | prefix .. "/include/lua" .. shortv, | ||
737 | prefix .. "/include", | ||
738 | prefix, | ||
739 | luajitver and (prefix .. "/include/luajit-" .. (luajitver:match("^(%d+%.%d+)") or "")), | ||
740 | } | ||
741 | local errprio = 0 | ||
742 | local mainerr | ||
743 | for _, d in ipairs(incdirs) do | ||
744 | local ok, err, _, prio = lua_h_exists(d, luaver) | ||
745 | if ok then | ||
746 | return d | ||
747 | end | ||
748 | if prio > errprio then | ||
749 | mainerr = err | ||
750 | errprio = prio | ||
751 | end | ||
752 | end | ||
753 | |||
754 | |||
755 | return nil, mainerr | ||
756 | end | ||
757 | |||
758 | function deps.check_lua_incdir(vars) | ||
759 | if vars.LUA_INCDIR_OK == "ok" then | ||
760 | return true | ||
761 | end | ||
762 | |||
763 | local ljv = util.get_luajit_version() | ||
764 | |||
765 | if vars.LUA_INCDIR then | ||
766 | local ok, err = lua_h_exists(vars.LUA_INCDIR, cfg.lua_version) | ||
767 | if ok then | ||
768 | vars.LUA_INCDIR_OK = "ok" | ||
769 | end | ||
770 | return ok, err | ||
771 | end | ||
772 | |||
773 | if vars.LUA_DIR then | ||
774 | local d, err = find_lua_incdir(vars.LUA_DIR, cfg.lua_version, ljv) | ||
775 | if d then | ||
776 | vars.LUA_INCDIR = d | ||
777 | vars.LUA_INCDIR_OK = "ok" | ||
778 | return true | ||
779 | end | ||
780 | return nil, err | ||
781 | end | ||
782 | |||
783 | return nil, "Failed finding Lua headers; neither LUA_DIR or LUA_INCDIR are set. You may need to install them or configure LUA_INCDIR.", "dependency" | ||
784 | end | ||
785 | |||
786 | function deps.check_lua_libdir(vars) | ||
787 | if vars.LUA_LIBDIR_OK == "ok" then | ||
788 | return true | ||
789 | end | ||
790 | |||
791 | local fs = require("luarocks.fs") | ||
792 | local ljv = util.get_luajit_version() | ||
793 | |||
794 | if vars.LUA_LIBDIR and vars.LUALIB and fs.exists(dir.path(vars.LUA_LIBDIR, vars.LUALIB)) then | ||
795 | vars.LUA_LIBDIR_OK = "ok" | ||
796 | return true | ||
797 | end | ||
798 | |||
799 | local shortv = cfg.lua_version:gsub("%.", "") | ||
800 | local libnames = { | ||
801 | "lua" .. cfg.lua_version, | ||
802 | "lua" .. shortv, | ||
803 | "lua-" .. cfg.lua_version, | ||
804 | "lua-" .. shortv, | ||
805 | "lua", | ||
806 | } | ||
807 | if ljv then | ||
808 | table.insert(libnames, 1, "luajit-" .. cfg.lua_version) | ||
809 | table.insert(libnames, 2, "luajit") | ||
810 | end | ||
811 | local cache = {} | ||
812 | local save_LUA_INCDIR = vars.LUA_INCDIR | ||
813 | local ok, _, _, errfiles = check_external_dependency("LUA", { library = libnames }, vars, "build", cache) | ||
814 | vars.LUA_INCDIR = save_LUA_INCDIR | ||
815 | local err | ||
816 | if ok then | ||
817 | local filename = dir.path(vars.LUA_LIBDIR, vars.LUA_LIBDIR_FILE) | ||
818 | local fd = io.open(filename, "r") | ||
819 | if fd then | ||
820 | if not vars.LUA_LIBDIR_FILE:match((cfg.lua_version:gsub("%.", "%%.?"))) then | ||
821 | |||
822 | local txt = fd:read("*a") | ||
823 | ok = txt:find("Lua " .. cfg.lua_version, 1, true) or | ||
824 | txt:find("lua" .. (cfg.lua_version:gsub("%.", "")), 1, true) and | ||
825 | true | ||
826 | if not ok then | ||
827 | err = "Lua library at " .. filename .. " does not match Lua version " .. cfg.lua_version .. ". You can use `luarocks config variables.LUA_LIBDIR <path>` to set the correct location." | ||
828 | end | ||
829 | end | ||
830 | |||
831 | fd:close() | ||
832 | end | ||
833 | end | ||
834 | |||
835 | if ok then | ||
836 | vars.LUALIB = vars.LUA_LIBDIR_FILE | ||
837 | vars.LUA_LIBDIR_OK = "ok" | ||
838 | return true | ||
839 | else | ||
840 | err = err or "Failed finding the Lua library. You can use `luarocks config variables.LUA_LIBDIR <path>` to set the correct location." | ||
841 | return nil, err, "dependency", errfiles | ||
842 | end | ||
843 | end | ||
844 | |||
845 | function deps.get_deps_mode(args) | ||
846 | return args.deps_mode or cfg.deps_mode | ||
847 | end | ||
848 | |||
849 | |||
850 | |||
851 | |||
852 | |||
853 | |||
854 | |||
855 | function deps.check_dependencies(repo, deps_mode) | ||
856 | local rocks_dir = path.rocks_dir(repo or cfg.root_dir) | ||
857 | if deps_mode == "none" then deps_mode = cfg.deps_mode end | ||
858 | |||
859 | local manifest = manif.load_manifest(rocks_dir) | ||
860 | if not manifest then | ||
861 | return | ||
862 | end | ||
863 | |||
864 | for name, versions in util.sortedpairs(manifest.repository) do | ||
865 | for version, version_entries in util.sortedpairs(versions, vers.compare_versions) do | ||
866 | for _, entry in ipairs(version_entries) do | ||
867 | if entry.arch == "installed" then | ||
868 | if manifest.dependencies[name] and manifest.dependencies[name][version] then | ||
869 | deps.report_missing_dependencies(name, version, manifest.dependencies[name][version], deps_mode, util.get_rocks_provided()) | ||
870 | end | ||
871 | end | ||
872 | end | ||
873 | end | ||
874 | end | ||
875 | end | ||
876 | |||
877 | return deps | ||