diff options
Diffstat (limited to 'src/luarocks/deps.lua')
-rw-r--r-- | src/luarocks/deps.lua | 244 |
1 files changed, 37 insertions, 207 deletions
diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua index dcebec9b..9c737d66 100644 --- a/src/luarocks/deps.lua +++ b/src/luarocks/deps.lua | |||
@@ -12,14 +12,23 @@ | |||
12 | -- test/test_deps.lua file included with LuaRocks provides some | 12 | -- test/test_deps.lua file included with LuaRocks provides some |
13 | -- insights on what these criteria are. | 13 | -- insights on what these criteria are. |
14 | local deps = {} | 14 | local deps = {} |
15 | package.loaded["luarocks.deps"] = deps | 15 | setmetatable(deps, { __index = require("luarocks.core.deps") }) |
16 | 16 | ||
17 | local cfg = require("luarocks.cfg") | 17 | local cfg = require("luarocks.core.cfg") |
18 | local manif_core = require("luarocks.manif_core") | 18 | local manif = require("luarocks.core.manif") |
19 | local path = require("luarocks.path") | 19 | local path = require("luarocks.path") |
20 | local dir = require("luarocks.dir") | 20 | local dir = require("luarocks.dir") |
21 | local util = require("luarocks.util") | 21 | local util = require("luarocks.util") |
22 | 22 | ||
23 | --- Check if rockspec format version satisfies version requirement. | ||
24 | -- @param rockspec table: The rockspec table. | ||
25 | -- @param version string: required version. | ||
26 | -- @return boolean: true if rockspec format matches version or is newer, false otherwise. | ||
27 | function deps.format_is_at_least(rockspec, version) | ||
28 | local rockspec_format = rockspec.rockspec_format or "1.0" | ||
29 | return deps.parse_version(rockspec_format) >= deps.parse_version(version) | ||
30 | end | ||
31 | |||
23 | local operators = { | 32 | local operators = { |
24 | ["=="] = "==", | 33 | ["=="] = "==", |
25 | ["~="] = "~=", | 34 | ["~="] = "~=", |
@@ -34,130 +43,6 @@ local operators = { | |||
34 | ["!="] = "~=" | 43 | ["!="] = "~=" |
35 | } | 44 | } |
36 | 45 | ||
37 | local deltas = { | ||
38 | scm = 1100, | ||
39 | cvs = 1000, | ||
40 | rc = -1000, | ||
41 | pre = -10000, | ||
42 | beta = -100000, | ||
43 | alpha = -1000000 | ||
44 | } | ||
45 | |||
46 | local version_mt = { | ||
47 | --- Equality comparison for versions. | ||
48 | -- All version numbers must be equal. | ||
49 | -- If both versions have revision numbers, they must be equal; | ||
50 | -- otherwise the revision number is ignored. | ||
51 | -- @param v1 table: version table to compare. | ||
52 | -- @param v2 table: version table to compare. | ||
53 | -- @return boolean: true if they are considered equivalent. | ||
54 | __eq = function(v1, v2) | ||
55 | if #v1 ~= #v2 then | ||
56 | return false | ||
57 | end | ||
58 | for i = 1, #v1 do | ||
59 | if v1[i] ~= v2[i] then | ||
60 | return false | ||
61 | end | ||
62 | end | ||
63 | if v1.revision and v2.revision then | ||
64 | return (v1.revision == v2.revision) | ||
65 | end | ||
66 | return true | ||
67 | end, | ||
68 | --- Size comparison for versions. | ||
69 | -- All version numbers are compared. | ||
70 | -- If both versions have revision numbers, they are compared; | ||
71 | -- otherwise the revision number is ignored. | ||
72 | -- @param v1 table: version table to compare. | ||
73 | -- @param v2 table: version table to compare. | ||
74 | -- @return boolean: true if v1 is considered lower than v2. | ||
75 | __lt = function(v1, v2) | ||
76 | for i = 1, math.max(#v1, #v2) do | ||
77 | local v1i, v2i = v1[i] or 0, v2[i] or 0 | ||
78 | if v1i ~= v2i then | ||
79 | return (v1i < v2i) | ||
80 | end | ||
81 | end | ||
82 | if v1.revision and v2.revision then | ||
83 | return (v1.revision < v2.revision) | ||
84 | end | ||
85 | return false | ||
86 | end | ||
87 | } | ||
88 | |||
89 | local version_cache = {} | ||
90 | setmetatable(version_cache, { | ||
91 | __mode = "kv" | ||
92 | }) | ||
93 | |||
94 | --- Parse a version string, converting to table format. | ||
95 | -- A version table contains all components of the version string | ||
96 | -- converted to numeric format, stored in the array part of the table. | ||
97 | -- If the version contains a revision, it is stored numerically | ||
98 | -- in the 'revision' field. The original string representation of | ||
99 | -- the string is preserved in the 'string' field. | ||
100 | -- Returned version tables use a metatable | ||
101 | -- allowing later comparison through relational operators. | ||
102 | -- @param vstring string: A version number in string format. | ||
103 | -- @return table or nil: A version table or nil | ||
104 | -- if the input string contains invalid characters. | ||
105 | function deps.parse_version(vstring) | ||
106 | if not vstring then return nil end | ||
107 | assert(type(vstring) == "string") | ||
108 | |||
109 | local cached = version_cache[vstring] | ||
110 | if cached then | ||
111 | return cached | ||
112 | end | ||
113 | |||
114 | local version = {} | ||
115 | local i = 1 | ||
116 | |||
117 | local function add_token(number) | ||
118 | version[i] = version[i] and version[i] + number/100000 or number | ||
119 | i = i + 1 | ||
120 | end | ||
121 | |||
122 | -- trim leading and trailing spaces | ||
123 | vstring = vstring:match("^%s*(.*)%s*$") | ||
124 | version.string = vstring | ||
125 | -- store revision separately if any | ||
126 | local main, revision = vstring:match("(.*)%-(%d+)$") | ||
127 | if revision then | ||
128 | vstring = main | ||
129 | version.revision = tonumber(revision) | ||
130 | end | ||
131 | while #vstring > 0 do | ||
132 | -- extract a number | ||
133 | local token, rest = vstring:match("^(%d+)[%.%-%_]*(.*)") | ||
134 | if token then | ||
135 | add_token(tonumber(token)) | ||
136 | else | ||
137 | -- extract a word | ||
138 | token, rest = vstring:match("^(%a+)[%.%-%_]*(.*)") | ||
139 | if not token then | ||
140 | util.printerr("Warning: version number '"..vstring.."' could not be parsed.") | ||
141 | version[i] = 0 | ||
142 | break | ||
143 | end | ||
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 deps.compare_versions(a, b) | ||
158 | return deps.parse_version(a) > deps.parse_version(b) | ||
159 | end | ||
160 | |||
161 | --- Consumes a constraint from a string, converting it to table format. | 46 | --- 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 | 47 | -- 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 | 48 | -- format {op = ">=", version={1,0}} and the rest, "> 2.0", is returned |
@@ -192,7 +77,7 @@ end | |||
192 | function deps.parse_constraints(input) | 77 | function deps.parse_constraints(input) |
193 | assert(type(input) == "string") | 78 | assert(type(input) == "string") |
194 | 79 | ||
195 | local constraints, constraint, oinput = {}, nil, input | 80 | local constraints, oinput, constraint = {}, input |
196 | while #input > 0 do | 81 | while #input > 0 do |
197 | constraint, input = parse_constraint(input) | 82 | constraint, input = parse_constraint(input) |
198 | if constraint then | 83 | if constraint then |
@@ -258,80 +143,25 @@ function deps.show_dep(dep, internal) | |||
258 | end | 143 | end |
259 | end | 144 | end |
260 | 145 | ||
261 | --- A more lenient check for equivalence between versions. | ||
262 | -- This returns true if the requested components of a version | ||
263 | -- match and ignore the ones that were not given. For example, | ||
264 | -- when requesting "2", then "2", "2.1", "2.3.5-9"... all match. | ||
265 | -- When requesting "2.1", then "2.1", "2.1.3" match, but "2.2" | ||
266 | -- doesn't. | ||
267 | -- @param version string or table: Version to be tested; may be | ||
268 | -- in string format or already parsed into a table. | ||
269 | -- @param requested string or table: Version requested; may be | ||
270 | -- in string format or already parsed into a table. | ||
271 | -- @return boolean: True if the tested version matches the requested | ||
272 | -- version, false otherwise. | ||
273 | local function partial_match(version, requested) | ||
274 | assert(type(version) == "string" or type(version) == "table") | ||
275 | assert(type(requested) == "string" or type(version) == "table") | ||
276 | |||
277 | if type(version) ~= "table" then version = deps.parse_version(version) end | ||
278 | if type(requested) ~= "table" then requested = deps.parse_version(requested) end | ||
279 | if not version or not requested then return false end | ||
280 | |||
281 | for i, ri in ipairs(requested) do | ||
282 | local vi = version[i] or 0 | ||
283 | if ri ~= vi then return false end | ||
284 | end | ||
285 | if requested.revision then | ||
286 | return requested.revision == version.revision | ||
287 | end | ||
288 | return true | ||
289 | end | ||
290 | |||
291 | --- Check if a version satisfies a set of constraints. | ||
292 | -- @param version table: A version in table format | ||
293 | -- @param constraints table: An array of constraints in table format. | ||
294 | -- @return boolean: True if version satisfies all constraints, | ||
295 | -- false otherwise. | ||
296 | function deps.match_constraints(version, constraints) | ||
297 | assert(type(version) == "table") | ||
298 | assert(type(constraints) == "table") | ||
299 | local ok = true | ||
300 | setmetatable(version, version_mt) | ||
301 | for _, constr in pairs(constraints) do | ||
302 | if type(constr.version) == "string" then | ||
303 | constr.version = deps.parse_version(constr.version) | ||
304 | end | ||
305 | local constr_version, constr_op = constr.version, constr.op | ||
306 | setmetatable(constr_version, version_mt) | ||
307 | if constr_op == "==" then ok = version == constr_version | ||
308 | elseif constr_op == "~=" then ok = version ~= constr_version | ||
309 | elseif constr_op == ">" then ok = version > constr_version | ||
310 | elseif constr_op == "<" then ok = version < constr_version | ||
311 | elseif constr_op == ">=" then ok = version >= constr_version | ||
312 | elseif constr_op == "<=" then ok = version <= constr_version | ||
313 | elseif constr_op == "~>" then ok = partial_match(version, constr_version) | ||
314 | end | ||
315 | if not ok then break end | ||
316 | end | ||
317 | return ok | ||
318 | end | ||
319 | |||
320 | --- Attempt to match a dependency to an installed rock. | 146 | --- Attempt to match a dependency to an installed rock. |
321 | -- @param dep table: A dependency parsed in table format. | 147 | -- @param dep table: A dependency parsed in table format. |
322 | -- @param blacklist table: Versions that can't be accepted. Table where keys | 148 | -- @param blacklist table: Versions that can't be accepted. Table where keys |
323 | -- are program versions and values are 'true'. | 149 | -- are program versions and values are 'true'. |
150 | -- @param provided table: A table of auto-dependencies provided | ||
151 | -- by this Lua implementation for the given dependency. | ||
324 | -- @return string or nil: latest installed version of the rock matching the dependency | 152 | -- @return string or nil: latest installed version of the rock matching the dependency |
325 | -- or nil if it could not be matched. | 153 | -- or nil if it could not be matched. |
326 | local function match_dep(dep, blacklist, deps_mode) | 154 | local function match_dep(dep, blacklist, deps_mode, rocks_provided) |
327 | assert(type(dep) == "table") | 155 | assert(type(dep) == "table") |
328 | 156 | assert(type(rocks_provided) == "table") | |
157 | |||
329 | local versions | 158 | local versions |
330 | if cfg.rocks_provided[dep.name] then | 159 | local provided = rocks_provided[dep.name] |
331 | -- provided rocks have higher priority than manifest's rocks | 160 | if provided then |
332 | versions = { cfg.rocks_provided[dep.name] } | 161 | -- Provided rocks have higher priority than manifest's rocks. |
162 | versions = { provided } | ||
333 | else | 163 | else |
334 | versions = manif_core.get_versions(dep.name, deps_mode) | 164 | versions = manif.get_versions(dep.name, deps_mode) |
335 | end | 165 | end |
336 | 166 | ||
337 | local latest_version | 167 | local latest_version |
@@ -366,9 +196,9 @@ function deps.match_deps(rockspec, blacklist, deps_mode) | |||
366 | local matched, missing, no_upgrade = {}, {}, {} | 196 | local matched, missing, no_upgrade = {}, {}, {} |
367 | 197 | ||
368 | for _, dep in ipairs(rockspec.dependencies) do | 198 | for _, dep in ipairs(rockspec.dependencies) do |
369 | local found = match_dep(dep, blacklist and blacklist[dep.name] or nil, deps_mode) | 199 | local found = match_dep(dep, blacklist and blacklist[dep.name] or nil, deps_mode, rockspec.rocks_provided) |
370 | if found then | 200 | if found then |
371 | if not cfg.rocks_provided[dep.name] then | 201 | if not rockspec.rocks_provided[dep.name] then |
372 | matched[dep] = {name = dep.name, version = found} | 202 | matched[dep] = {name = dep.name, version = found} |
373 | end | 203 | end |
374 | else | 204 | else |
@@ -393,10 +223,10 @@ local function values_set(tbl) | |||
393 | return set | 223 | return set |
394 | end | 224 | end |
395 | 225 | ||
396 | local function rock_status(name, deps_mode) | 226 | local function rock_status(name, deps_mode, rocks_provided) |
397 | local search = require("luarocks.search") | 227 | local search = require("luarocks.search") |
398 | local installed = match_dep(search.make_query(name), nil, deps_mode) | 228 | local installed = match_dep(search.make_query(name), nil, deps_mode, rocks_provided) |
399 | local installation_type = cfg.rocks_provided[name] and "provided by VM" or "installed" | 229 | local installation_type = rocks_provided[name] and "provided by VM" or "installed" |
400 | return installed and installed.." "..installation_type or "not installed" | 230 | return installed and installed.." "..installation_type or "not installed" |
401 | end | 231 | end |
402 | 232 | ||
@@ -432,7 +262,7 @@ end | |||
432 | function deps.fulfill_dependencies(rockspec, deps_mode) | 262 | function deps.fulfill_dependencies(rockspec, deps_mode) |
433 | 263 | ||
434 | local search = require("luarocks.search") | 264 | local search = require("luarocks.search") |
435 | local install = require("luarocks.install") | 265 | local install = require("luarocks.cmd.install") |
436 | 266 | ||
437 | if rockspec.supported_platforms then | 267 | if rockspec.supported_platforms then |
438 | if not deps.platforms_set then | 268 | if not deps.platforms_set then |
@@ -440,7 +270,8 @@ function deps.fulfill_dependencies(rockspec, deps_mode) | |||
440 | end | 270 | end |
441 | local supported = nil | 271 | local supported = nil |
442 | for _, plat in pairs(rockspec.supported_platforms) do | 272 | for _, plat in pairs(rockspec.supported_platforms) do |
443 | local neg, plat = plat:match("^(!?)(.*)") | 273 | local neg |
274 | neg, plat = plat:match("^(!?)(.*)") | ||
444 | if neg == "!" then | 275 | if neg == "!" then |
445 | if deps.platforms_set[plat] then | 276 | if deps.platforms_set[plat] then |
446 | return nil, "This rockspec for "..rockspec.package.." does not support "..plat.." platforms." | 277 | return nil, "This rockspec for "..rockspec.package.." does not support "..plat.." platforms." |
@@ -466,14 +297,14 @@ function deps.fulfill_dependencies(rockspec, deps_mode) | |||
466 | local first_missing_dep = true | 297 | local first_missing_dep = true |
467 | 298 | ||
468 | for _, dep in ipairs(rockspec.dependencies) do | 299 | for _, dep in ipairs(rockspec.dependencies) do |
469 | if not match_dep(dep, nil, deps_mode) then | 300 | if not match_dep(dep, nil, deps_mode, rockspec.rocks_provided) then |
470 | if first_missing_dep then | 301 | if first_missing_dep then |
471 | util.printout() | 302 | util.printout() |
472 | first_missing_dep = false | 303 | first_missing_dep = false |
473 | end | 304 | end |
474 | 305 | ||
475 | util.printout(("%s %s depends on %s (%s)"):format( | 306 | util.printout(("%s %s depends on %s (%s)"):format( |
476 | rockspec.name, rockspec.version, deps.show_dep(dep), rock_status(dep.name, deps_mode))) | 307 | rockspec.name, rockspec.version, deps.show_dep(dep), rock_status(dep.name, deps_mode, rockspec.rocks_provided))) |
477 | 308 | ||
478 | if dep.constraints[1] and dep.constraints[1].no_upgrade then | 309 | if dep.constraints[1] and dep.constraints[1].no_upgrade then |
479 | util.printerr("This version of "..rockspec.name.." is designed for use with") | 310 | util.printerr("This version of "..rockspec.name.." is designed for use with") |
@@ -717,7 +548,10 @@ function deps.scan_deps(results, manifest, name, version, deps_mode) | |||
717 | end | 548 | end |
718 | dependencies_name[version] = rockspec.dependencies | 549 | dependencies_name[version] = rockspec.dependencies |
719 | else | 550 | else |
720 | rockspec = { dependencies = deplist } | 551 | rockspec = { |
552 | dependencies = deplist, | ||
553 | rocks_provided = setmetatable({}, { __index = cfg.rocks_provided_3_0 }) | ||
554 | } | ||
721 | end | 555 | end |
722 | local matched = deps.match_deps(rockspec, nil, deps_mode) | 556 | local matched = deps.match_deps(rockspec, nil, deps_mode) |
723 | results[name] = version | 557 | results[name] = version |
@@ -745,8 +579,4 @@ function deps.get_deps_mode(flags) | |||
745 | end | 579 | end |
746 | end | 580 | end |
747 | 581 | ||
748 | function deps.deps_mode_to_flag(deps_mode) | ||
749 | return "--deps-mode="..deps_mode | ||
750 | end | ||
751 | |||
752 | return deps | 582 | return deps |