Mercurial > repo
comparison share/lua/5.2/luarocks/deps.lua @ 1132:d137f631bad5
<GreyKnight> (cd luabuild/luarocks-2.0.12; make install)
author | HackBot |
---|---|
date | Fri, 14 Dec 2012 22:24:27 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
1131:ff65dfb63263 | 1132:d137f631bad5 |
---|---|
1 | |
2 --- Dependency handling functions. | |
3 -- Dependencies are represented in LuaRocks through strings with | |
4 -- a package name followed by a comma-separated list of constraints. | |
5 -- Each constraint consists of an operator and a version number. | |
6 -- In this string format, version numbers are represented as | |
7 -- naturally as possible, like they are used by upstream projects | |
8 -- (e.g. "2.0beta3"). Internally, LuaRocks converts them to a purely | |
9 -- numeric representation, allowing comparison following some | |
10 -- "common sense" heuristics. The precise specification of the | |
11 -- comparison criteria is the source code of this module, but the | |
12 -- test/test_deps.lua file included with LuaRocks provides some | |
13 -- insights on what these criteria are. | |
14 module("luarocks.deps", package.seeall) | |
15 | |
16 local cfg = require("luarocks.cfg") | |
17 local manif_core = require("luarocks.manif_core") | |
18 local path = require("luarocks.path") | |
19 local dir = require("luarocks.dir") | |
20 local util = require("luarocks.util") | |
21 | |
22 local operators = { | |
23 ["=="] = "==", | |
24 ["~="] = "~=", | |
25 [">"] = ">", | |
26 ["<"] = "<", | |
27 [">="] = ">=", | |
28 ["<="] = "<=", | |
29 ["~>"] = "~>", | |
30 -- plus some convenience translations | |
31 [""] = "==", | |
32 ["="] = "==", | |
33 ["!="] = "~=" | |
34 } | |
35 | |
36 local deltas = { | |
37 scm = 1000, | |
38 cvs = 1000, | |
39 rc = -1000, | |
40 pre = -10000, | |
41 beta = -100000, | |
42 alpha = -1000000 | |
43 } | |
44 | |
45 local version_mt = { | |
46 --- Equality comparison for versions. | |
47 -- All version numbers must be equal. | |
48 -- If both versions have revision numbers, they must be equal; | |
49 -- otherwise the revision number is ignored. | |
50 -- @param v1 table: version table to compare. | |
51 -- @param v2 table: version table to compare. | |
52 -- @return boolean: true if they are considered equivalent. | |
53 __eq = function(v1, v2) | |
54 if #v1 ~= #v2 then | |
55 return false | |
56 end | |
57 for i = 1, #v1 do | |
58 if v1[i] ~= v2[i] then | |
59 return false | |
60 end | |
61 end | |
62 if v1.revision and v2.revision then | |
63 return (v1.revision == v2.revision) | |
64 end | |
65 return true | |
66 end, | |
67 --- Size comparison for versions. | |
68 -- All version numbers are compared. | |
69 -- If both versions have revision numbers, they are compared; | |
70 -- otherwise the revision number is ignored. | |
71 -- @param v1 table: version table to compare. | |
72 -- @param v2 table: version table to compare. | |
73 -- @return boolean: true if v1 is considered lower than v2. | |
74 __lt = function(v1, v2) | |
75 for i = 1, math.max(#v1, #v2) do | |
76 local v1i, v2i = v1[i] or 0, v2[i] or 0 | |
77 if v1i ~= v2i then | |
78 return (v1i < v2i) | |
79 end | |
80 end | |
81 if v1.revision and v2.revision then | |
82 return (v1.revision < v2.revision) | |
83 end | |
84 return false | |
85 end | |
86 } | |
87 | |
88 local version_cache = {} | |
89 setmetatable(version_cache, { | |
90 __mode = "kv" | |
91 }) | |
92 | |
93 --- Parse a version string, converting to table format. | |
94 -- A version table contains all components of the version string | |
95 -- converted to numeric format, stored in the array part of the table. | |
96 -- If the version contains a revision, it is stored numerically | |
97 -- in the 'revision' field. The original string representation of | |
98 -- the string is preserved in the 'string' field. | |
99 -- Returned version tables use a metatable | |
100 -- allowing later comparison through relational operators. | |
101 -- @param vstring string: A version number in string format. | |
102 -- @return table or nil: A version table or nil | |
103 -- if the input string contains invalid characters. | |
104 function parse_version(vstring) | |
105 if not vstring then return nil end | |
106 assert(type(vstring) == "string") | |
107 | |
108 local cached = version_cache[vstring] | |
109 if cached then | |
110 return cached | |
111 end | |
112 | |
113 local version = {} | |
114 local i = 1 | |
115 | |
116 local function add_token(number) | |
117 version[i] = version[i] and version[i] + number/100000 or number | |
118 i = i + 1 | |
119 end | |
120 | |
121 -- trim leading and trailing spaces | |
122 vstring = vstring:match("^%s*(.*)%s*$") | |
123 version.string = vstring | |
124 -- store revision separately if any | |
125 local main, revision = vstring:match("(.*)%-(%d+)$") | |
126 if revision then | |
127 vstring = main | |
128 version.revision = tonumber(revision) | |
129 end | |
130 while #vstring > 0 do | |
131 -- extract a number | |
132 local token, rest = vstring:match("^(%d+)[%.%-%_]*(.*)") | |
133 if token then | |
134 add_token(tonumber(token)) | |
135 else | |
136 -- extract a word | |
137 token, rest = vstring:match("^(%a+)[%.%-%_]*(.*)") | |
138 if not token then | |
139 util.printerr("Warning: version number '"..vstring.."' could not be parsed.") | |
140 version[i] = 0 | |
141 break | |
142 end | |
143 local last = #version | |
144 version[i] = deltas[token] or (token:byte() / 1000) | |
145 end | |
146 vstring = rest | |
147 end | |
148 setmetatable(version, version_mt) | |
149 version_cache[vstring] = version | |
150 return version | |
151 end | |
152 | |
153 --- Utility function to compare version numbers given as strings. | |
154 -- @param a string: one version. | |
155 -- @param b string: another version. | |
156 -- @return boolean: True if a > b. | |
157 function compare_versions(a, b) | |
158 return parse_version(a) > parse_version(b) | |
159 end | |
160 | |
161 --- Consumes a constraint from a string, converting it to table format. | |
162 -- For example, a string ">= 1.0, > 2.0" is converted to a table in the | |
163 -- format {op = ">=", version={1,0}} and the rest, "> 2.0", is returned | |
164 -- back to the caller. | |
165 -- @param input string: A list of constraints in string format. | |
166 -- @return (table, string) or nil: A table representing the same | |
167 -- constraints and the string with the unused input, or nil if the | |
168 -- input string is invalid. | |
169 local function parse_constraint(input) | |
170 assert(type(input) == "string") | |
171 | |
172 local no_upgrade, op, version, rest = input:match("^(@?)([<>=~!]*)%s*([%w%.%_%-]+)[%s,]*(.*)") | |
173 op = operators[op] | |
174 version = parse_version(version) | |
175 if not op or not version then return nil end | |
176 return { op = op, version = version, no_upgrade = no_upgrade=="@" and true or nil }, rest | |
177 end | |
178 | |
179 --- Convert a list of constraints from string to table format. | |
180 -- For example, a string ">= 1.0, < 2.0" is converted to a table in the format | |
181 -- {{op = ">=", version={1,0}}, {op = "<", version={2,0}}}. | |
182 -- Version tables use a metatable allowing later comparison through | |
183 -- relational operators. | |
184 -- @param input string: A list of constraints in string format. | |
185 -- @return table or nil: A table representing the same constraints, | |
186 -- or nil if the input string is invalid. | |
187 function parse_constraints(input) | |
188 assert(type(input) == "string") | |
189 | |
190 local constraints, constraint = {}, nil | |
191 while #input > 0 do | |
192 constraint, input = parse_constraint(input) | |
193 if constraint then | |
194 table.insert(constraints, constraint) | |
195 else | |
196 return nil | |
197 end | |
198 end | |
199 return constraints | |
200 end | |
201 | |
202 --- Convert a dependency from string to table format. | |
203 -- For example, a string "foo >= 1.0, < 2.0" | |
204 -- is converted to a table in the format | |
205 -- {name = "foo", constraints = {{op = ">=", version={1,0}}, | |
206 -- {op = "<", version={2,0}}}}. Version tables use a metatable | |
207 -- allowing later comparison through relational operators. | |
208 -- @param dep string: A dependency in string format | |
209 -- as entered in rockspec files. | |
210 -- @return table or nil: A table representing the same dependency relation, | |
211 -- or nil if the input string is invalid. | |
212 function parse_dep(dep) | |
213 assert(type(dep) == "string") | |
214 | |
215 local name, rest = dep:match("^%s*([a-zA-Z][a-zA-Z0-9%.%-%_]*)%s*(.*)") | |
216 if not name then return nil end | |
217 local constraints = parse_constraints(rest) | |
218 if not constraints then return nil end | |
219 return { name = name, constraints = constraints } | |
220 end | |
221 | |
222 --- Convert a version table to a string. | |
223 -- @param v table: The version table | |
224 -- @param internal boolean or nil: Whether to display versions in their | |
225 -- internal representation format or how they were specified. | |
226 -- @return string: The dependency information pretty-printed as a string. | |
227 function show_version(v, internal) | |
228 assert(type(v) == "table") | |
229 assert(type(internal) == "boolean" or not internal) | |
230 | |
231 return (internal | |
232 and table.concat(v, ":")..(v.revision and tostring(v.revision) or "") | |
233 or v.string) | |
234 end | |
235 | |
236 --- Convert a dependency in table format to a string. | |
237 -- @param dep table: The dependency in table format | |
238 -- @param internal boolean or nil: Whether to display versions in their | |
239 -- internal representation format or how they were specified. | |
240 -- @return string: The dependency information pretty-printed as a string. | |
241 function show_dep(dep, internal) | |
242 assert(type(dep) == "table") | |
243 assert(type(internal) == "boolean" or not internal) | |
244 | |
245 local pretty = {} | |
246 for _, c in ipairs(dep.constraints) do | |
247 table.insert(pretty, c.op .. " " .. show_version(c.version, internal)) | |
248 end | |
249 return dep.name.." "..table.concat(pretty, ", ") | |
250 end | |
251 | |
252 --- A more lenient check for equivalence between versions. | |
253 -- This returns true if the requested components of a version | |
254 -- match and ignore the ones that were not given. For example, | |
255 -- when requesting "2", then "2", "2.1", "2.3.5-9"... all match. | |
256 -- When requesting "2.1", then "2.1", "2.1.3" match, but "2.2" | |
257 -- doesn't. | |
258 -- @param version string or table: Version to be tested; may be | |
259 -- in string format or already parsed into a table. | |
260 -- @param requested string or table: Version requested; may be | |
261 -- in string format or already parsed into a table. | |
262 -- @return boolean: True if the tested version matches the requested | |
263 -- version, false otherwise. | |
264 local function partial_match(version, requested) | |
265 assert(type(version) == "string" or type(version) == "table") | |
266 assert(type(requested) == "string" or type(version) == "table") | |
267 | |
268 if type(version) ~= "table" then version = parse_version(version) end | |
269 if type(requested) ~= "table" then requested = parse_version(requested) end | |
270 if not version or not requested then return false end | |
271 | |
272 for i, ri in ipairs(requested) do | |
273 local vi = version[i] or 0 | |
274 if ri ~= vi then return false end | |
275 end | |
276 if requested.revision then | |
277 return requested.revision == version.revision | |
278 end | |
279 return true | |
280 end | |
281 | |
282 --- Check if a version satisfies a set of constraints. | |
283 -- @param version table: A version in table format | |
284 -- @param constraints table: An array of constraints in table format. | |
285 -- @return boolean: True if version satisfies all constraints, | |
286 -- false otherwise. | |
287 function match_constraints(version, constraints) | |
288 assert(type(version) == "table") | |
289 assert(type(constraints) == "table") | |
290 local ok = true | |
291 setmetatable(version, version_mt) | |
292 for _, constr in pairs(constraints) do | |
293 local constr_version = constr.version | |
294 setmetatable(constr.version, version_mt) | |
295 if constr.op == "==" then ok = version == constr_version | |
296 elseif constr.op == "~=" then ok = version ~= constr_version | |
297 elseif constr.op == ">" then ok = version > constr_version | |
298 elseif constr.op == "<" then ok = version < constr_version | |
299 elseif constr.op == ">=" then ok = version >= constr_version | |
300 elseif constr.op == "<=" then ok = version <= constr_version | |
301 elseif constr.op == "~>" then ok = partial_match(version, constr_version) | |
302 end | |
303 if not ok then break end | |
304 end | |
305 return ok | |
306 end | |
307 | |
308 --- Attempt to match a dependency to an installed rock. | |
309 -- @param dep table: A dependency parsed in table format. | |
310 -- @param blacklist table: Versions that can't be accepted. Table where keys | |
311 -- are program versions and values are 'true'. | |
312 -- @return table or nil: A table containing fields 'name' and 'version' | |
313 -- representing an installed rock which matches the given dependency, | |
314 -- or nil if it could not be matched. | |
315 local function match_dep(dep, blacklist, deps_mode) | |
316 assert(type(dep) == "table") | |
317 | |
318 local versions | |
319 if dep.name == "lua" then | |
320 versions = { cfg.lua_version } | |
321 else | |
322 versions = manif_core.get_versions(dep.name, deps_mode) | |
323 end | |
324 if not versions then | |
325 return nil | |
326 end | |
327 if blacklist then | |
328 local i = 1 | |
329 while versions[i] do | |
330 if blacklist[versions[i]] then | |
331 table.remove(versions, i) | |
332 else | |
333 i = i + 1 | |
334 end | |
335 end | |
336 end | |
337 local candidates = {} | |
338 for _, vstring in ipairs(versions) do | |
339 local version = parse_version(vstring) | |
340 if match_constraints(version, dep.constraints) then | |
341 table.insert(candidates, version) | |
342 end | |
343 end | |
344 if #candidates == 0 then | |
345 return nil | |
346 else | |
347 table.sort(candidates) | |
348 return { | |
349 name = dep.name, | |
350 version = candidates[#candidates].string | |
351 } | |
352 end | |
353 end | |
354 | |
355 --- Attempt to match dependencies of a rockspec to installed rocks. | |
356 -- @param rockspec table: The rockspec loaded as a table. | |
357 -- @param blacklist table or nil: Program versions to not use as valid matches. | |
358 -- Table where keys are program names and values are tables where keys | |
359 -- are program versions and values are 'true'. | |
360 -- @return table, table: A table where keys are dependencies parsed | |
361 -- in table format and values are tables containing fields 'name' and | |
362 -- version' representing matches, and a table of missing dependencies | |
363 -- parsed as tables. | |
364 function match_deps(rockspec, blacklist, deps_mode) | |
365 assert(type(rockspec) == "table") | |
366 assert(type(blacklist) == "table" or not blacklist) | |
367 local matched, missing, no_upgrade = {}, {}, {} | |
368 | |
369 for _, dep in ipairs(rockspec.dependencies) do | |
370 local found = match_dep(dep, blacklist and blacklist[dep.name] or nil, deps_mode) | |
371 if found then | |
372 if dep.name ~= "lua" then | |
373 matched[dep] = found | |
374 end | |
375 else | |
376 if dep.constraints[1] and dep.constraints[1].no_upgrade then | |
377 no_upgrade[dep.name] = dep | |
378 else | |
379 missing[dep.name] = dep | |
380 end | |
381 end | |
382 end | |
383 return matched, missing, no_upgrade | |
384 end | |
385 | |
386 --- Return a set of values of a table. | |
387 -- @param tbl table: The input table. | |
388 -- @return table: The array of keys. | |
389 local function values_set(tbl) | |
390 local set = {} | |
391 for _, v in pairs(tbl) do | |
392 set[v] = true | |
393 end | |
394 return set | |
395 end | |
396 | |
397 --- Check dependencies of a rock and attempt to install any missing ones. | |
398 -- Packages are installed using the LuaRocks "install" command. | |
399 -- Aborts the program if a dependency could not be fulfilled. | |
400 -- @param rockspec table: A rockspec in table format. | |
401 -- @return boolean or (nil, string, [string]): True if no errors occurred, or | |
402 -- nil and an error message if any test failed, followed by an optional | |
403 -- error code. | |
404 function fulfill_dependencies(rockspec, deps_mode) | |
405 | |
406 local search = require("luarocks.search") | |
407 local install = require("luarocks.install") | |
408 | |
409 if rockspec.supported_platforms then | |
410 if not platforms_set then | |
411 platforms_set = values_set(cfg.platforms) | |
412 end | |
413 local supported = nil | |
414 for _, plat in pairs(rockspec.supported_platforms) do | |
415 local neg, plat = plat:match("^(!?)(.*)") | |
416 if neg == "!" then | |
417 if platforms_set[plat] then | |
418 return nil, "This rockspec for "..rockspec.package.." does not support "..plat.." platforms." | |
419 end | |
420 else | |
421 if platforms_set[plat] then | |
422 supported = true | |
423 else | |
424 if supported == nil then | |
425 supported = false | |
426 end | |
427 end | |
428 end | |
429 end | |
430 if supported == false then | |
431 local plats = table.concat(cfg.platforms, ", ") | |
432 return nil, "This rockspec for "..rockspec.package.." does not support "..plats.." platforms." | |
433 end | |
434 end | |
435 | |
436 local matched, missing, no_upgrade = match_deps(rockspec, nil, deps_mode) | |
437 | |
438 if next(no_upgrade) then | |
439 util.printerr("Missing dependencies for "..rockspec.name.." "..rockspec.version..":") | |
440 for _, dep in pairs(no_upgrade) do | |
441 util.printerr(show_dep(dep)) | |
442 end | |
443 if next(missing) then | |
444 for _, dep in pairs(missing) do | |
445 util.printerr(show_dep(dep)) | |
446 end | |
447 end | |
448 util.printerr() | |
449 for _, dep in pairs(no_upgrade) do | |
450 util.printerr("This version of "..rockspec.name.." is designed for use with") | |
451 util.printerr(show_dep(dep)..", but is configured to avoid upgrading it") | |
452 util.printerr("automatically. Please upgrade "..dep.name.." with") | |
453 util.printerr(" luarocks install "..dep.name) | |
454 util.printerr("or choose an older version of "..rockspec.name.." with") | |
455 util.printerr(" luarocks search "..rockspec.name) | |
456 end | |
457 return nil, "Failed matching dependencies." | |
458 end | |
459 | |
460 if next(missing) then | |
461 util.printerr() | |
462 util.printerr("Missing dependencies for "..rockspec.name..":") | |
463 for _, dep in pairs(missing) do | |
464 util.printerr(show_dep(dep)) | |
465 end | |
466 util.printerr() | |
467 | |
468 for _, dep in pairs(missing) do | |
469 -- Double-check in case dependency was filled during recursion. | |
470 if not match_dep(dep, nil, deps_mode) then | |
471 local rock = search.find_suitable_rock(dep) | |
472 if not rock then | |
473 return nil, "Could not satisfy dependency: "..show_dep(dep) | |
474 end | |
475 local ok, err, errcode = install.run(rock) | |
476 if not ok then | |
477 return nil, "Failed installing dependency: "..rock.." - "..err, errcode | |
478 end | |
479 end | |
480 end | |
481 end | |
482 return true | |
483 end | |
484 | |
485 --- If filename matches a pattern, return the capture. | |
486 -- For example, given "libfoo.so" and "lib?.so" is a pattern, | |
487 -- returns "foo" (which can then be used to build names | |
488 -- based on other patterns. | |
489 -- @param file string: a filename | |
490 -- @param pattern string: a pattern, where ? is to be matched by the filename. | |
491 -- @return string The pattern, if found, or nil. | |
492 local function deconstruct_pattern(file, pattern) | |
493 local depattern = "^"..(pattern:gsub("%.", "%%."):gsub("%*", ".*"):gsub("?", "(.*)")).."$" | |
494 return (file:match(depattern)) | |
495 end | |
496 | |
497 --- Construct all possible patterns for a name and add to the files array. | |
498 -- Run through the patterns array replacing all occurrences of "?" | |
499 -- with the given file name and store them in the files array. | |
500 -- @param file string A raw name (e.g. "foo") | |
501 -- @param array of string An array of patterns with "?" as the wildcard | |
502 -- (e.g. {"?.so", "lib?.so"}) | |
503 -- @param files The array of constructed names | |
504 local function add_all_patterns(file, patterns, files) | |
505 for _, pattern in ipairs(patterns) do | |
506 table.insert(files, (pattern:gsub("?", file))) | |
507 end | |
508 end | |
509 | |
510 --- Set up path-related variables for external dependencies. | |
511 -- For each key in the external_dependencies table in the | |
512 -- rockspec file, four variables are created: <key>_DIR, <key>_BINDIR, | |
513 -- <key>_INCDIR and <key>_LIBDIR. These are not overwritten | |
514 -- if already set (e.g. by the LuaRocks config file or through the | |
515 -- command-line). Values in the external_dependencies table | |
516 -- are tables that may contain a "header" or a "library" field, | |
517 -- with filenames to be tested for existence. | |
518 -- @param rockspec table: The rockspec table. | |
519 -- @param mode string: if "build" is given, checks all files; | |
520 -- if "install" is given, do not scan for headers. | |
521 -- @return boolean or (nil, string): True if no errors occurred, or | |
522 -- nil and an error message if any test failed. | |
523 function check_external_deps(rockspec, mode) | |
524 assert(type(rockspec) == "table") | |
525 | |
526 local fs = require("luarocks.fs") | |
527 | |
528 local vars = rockspec.variables | |
529 local patterns = cfg.external_deps_patterns | |
530 local subdirs = cfg.external_deps_subdirs | |
531 if mode == "install" then | |
532 patterns = cfg.runtime_external_deps_patterns | |
533 subdirs = cfg.runtime_external_deps_subdirs | |
534 end | |
535 if rockspec.external_dependencies then | |
536 for name, files in pairs(rockspec.external_dependencies) do | |
537 local ok = true | |
538 local failed_file = nil | |
539 local failed_dirname = nil | |
540 for _, extdir in ipairs(cfg.external_deps_dirs) do | |
541 ok = true | |
542 local prefix = vars[name.."_DIR"] | |
543 local dirs = { | |
544 BINDIR = { subdir = subdirs.bin, testfile = "program", pattern = patterns.bin }, | |
545 INCDIR = { subdir = subdirs.include, testfile = "header", pattern = patterns.include }, | |
546 LIBDIR = { subdir = subdirs.lib, testfile = "library", pattern = patterns.lib } | |
547 } | |
548 if mode == "install" then | |
549 dirs.INCDIR = nil | |
550 end | |
551 if not prefix then | |
552 prefix = extdir | |
553 end | |
554 if type(prefix) == "table" then | |
555 if prefix.bin then | |
556 dirs.BINDIR.subdir = prefix.bin | |
557 end | |
558 if prefix.include then | |
559 if dirs.INCDIR then | |
560 dirs.INCDIR.subdir = prefix.include | |
561 end | |
562 end | |
563 if prefix.lib then | |
564 dirs.LIBDIR.subdir = prefix.lib | |
565 end | |
566 prefix = prefix.prefix | |
567 end | |
568 for dirname, dirdata in pairs(dirs) do | |
569 dirdata.dir = vars[name.."_"..dirname] or dir.path(prefix, dirdata.subdir) | |
570 local file = files[dirdata.testfile] | |
571 if file then | |
572 local files = {} | |
573 if not file:match("%.") then | |
574 add_all_patterns(file, dirdata.pattern, files) | |
575 else | |
576 for _, pattern in ipairs(dirdata.pattern) do | |
577 local matched = deconstruct_pattern(file, pattern) | |
578 if matched then | |
579 add_all_patterns(matched, dirdata.pattern, files) | |
580 end | |
581 end | |
582 table.insert(files, file) | |
583 end | |
584 local found = false | |
585 failed_file = nil | |
586 for _, f in pairs(files) do | |
587 -- small convenience hack | |
588 if f:match("%.so$") or f:match("%.dylib$") or f:match("%.dll$") then | |
589 f = f:gsub("%.[^.]+$", "."..cfg.external_lib_extension) | |
590 end | |
591 if f:match("%*") then | |
592 local replaced = f:gsub("%.", "%%."):gsub("%*", ".*") | |
593 for _, entry in ipairs(fs.list_dir(dirdata.dir)) do | |
594 if entry:match(replaced) then | |
595 found = true | |
596 break | |
597 end | |
598 end | |
599 else | |
600 found = fs.is_file(dir.path(dirdata.dir, f)) | |
601 end | |
602 if found then | |
603 break | |
604 else | |
605 if failed_file then | |
606 failed_file = failed_file .. ", or " .. f | |
607 else | |
608 failed_file = f | |
609 end | |
610 end | |
611 end | |
612 if not found then | |
613 ok = false | |
614 failed_dirname = dirname | |
615 break | |
616 end | |
617 end | |
618 end | |
619 if ok then | |
620 for dirname, dirdata in pairs(dirs) do | |
621 vars[name.."_"..dirname] = dirdata.dir | |
622 end | |
623 vars[name.."_DIR"] = prefix | |
624 break | |
625 end | |
626 end | |
627 if not ok then | |
628 return nil, "Could not find expected file "..failed_file.." for "..name.." -- you may have to install "..name.." in your system and/or pass "..name.."_DIR or "..name.."_"..failed_dirname.." to the luarocks command. Example: luarocks install "..rockspec.name.." "..name.."_DIR=/usr/local", "dependency" | |
629 end | |
630 end | |
631 end | |
632 return true | |
633 end | |
634 | |
635 --- Recursively scan dependencies, to build a transitive closure of all | |
636 -- dependent packages. | |
637 -- @param results table: The results table being built. | |
638 -- @param missing table: The table of missing dependencies being recursively built. | |
639 -- @param manifest table: The manifest table containing dependencies. | |
640 -- @param name string: Package name. | |
641 -- @param version string: Package version. | |
642 -- @return (table, table): The results and a table of missing dependencies. | |
643 function scan_deps(results, missing, manifest, name, version, deps_mode) | |
644 assert(type(results) == "table") | |
645 assert(type(missing) == "table") | |
646 assert(type(manifest) == "table") | |
647 assert(type(name) == "string") | |
648 assert(type(version) == "string") | |
649 | |
650 local fetch = require("luarocks.fetch") | |
651 | |
652 local err | |
653 if results[name] then | |
654 return results, missing | |
655 end | |
656 if not manifest.dependencies then manifest.dependencies = {} end | |
657 local dependencies = manifest.dependencies | |
658 if not dependencies[name] then dependencies[name] = {} end | |
659 local dependencies_name = dependencies[name] | |
660 local deplist = dependencies_name[version] | |
661 local rockspec, err | |
662 if not deplist then | |
663 rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version)) | |
664 if err then | |
665 missing[name.." "..version] = err | |
666 return results, missing | |
667 end | |
668 dependencies_name[version] = rockspec.dependencies | |
669 else | |
670 rockspec = { dependencies = deplist } | |
671 end | |
672 local matched, failures = match_deps(rockspec, nil, deps_mode) | |
673 for _, match in pairs(matched) do | |
674 results, missing = scan_deps(results, missing, manifest, match.name, match.version, deps_mode) | |
675 end | |
676 if next(failures) then | |
677 for _, failure in pairs(failures) do | |
678 missing[show_dep(failure)] = "failed" | |
679 end | |
680 end | |
681 results[name] = version | |
682 return results, missing | |
683 end | |
684 | |
685 local valid_deps_modes = { | |
686 one = true, | |
687 order = true, | |
688 all = true, | |
689 none = true, | |
690 } | |
691 | |
692 function check_deps_mode_flag(flag) | |
693 return valid_deps_modes[flag] | |
694 end | |
695 | |
696 function get_deps_mode(flags) | |
697 if flags["deps-mode"] then | |
698 return flags["deps-mode"] | |
699 else | |
700 return cfg.deps_mode | |
701 end | |
702 end | |
703 | |
704 function deps_mode_to_flag(deps_mode) | |
705 return "--deps-mode="..deps_mode | |
706 end |