aboutsummaryrefslogtreecommitdiff
path: root/src/luarocks/deps.lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/luarocks/deps.lua')
-rw-r--r--src/luarocks/deps.lua244
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.
14local deps = {} 14local deps = {}
15package.loaded["luarocks.deps"] = deps 15setmetatable(deps, { __index = require("luarocks.core.deps") })
16 16
17local cfg = require("luarocks.cfg") 17local cfg = require("luarocks.core.cfg")
18local manif_core = require("luarocks.manif_core") 18local manif = require("luarocks.core.manif")
19local path = require("luarocks.path") 19local path = require("luarocks.path")
20local dir = require("luarocks.dir") 20local dir = require("luarocks.dir")
21local util = require("luarocks.util") 21local 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.
27function 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)
30end
31
23local operators = { 32local operators = {
24 ["=="] = "==", 33 ["=="] = "==",
25 ["~="] = "~=", 34 ["~="] = "~=",
@@ -34,130 +43,6 @@ local operators = {
34 ["!="] = "~=" 43 ["!="] = "~="
35} 44}
36 45
37local deltas = {
38 scm = 1100,
39 cvs = 1000,
40 rc = -1000,
41 pre = -10000,
42 beta = -100000,
43 alpha = -1000000
44}
45
46local 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
89local version_cache = {}
90setmetatable(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.
105function 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
151end
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.
157function deps.compare_versions(a, b)
158 return deps.parse_version(a) > deps.parse_version(b)
159end
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
192function deps.parse_constraints(input) 77function 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
259end 144end
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.
273local 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
289end
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.
296function 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
318end
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.
326local function match_dep(dep, blacklist, deps_mode) 154local 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
394end 224end
395 225
396local function rock_status(name, deps_mode) 226local 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"
401end 231end
402 232
@@ -432,7 +262,7 @@ end
432function deps.fulfill_dependencies(rockspec, deps_mode) 262function 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
746end 580end
747 581
748function deps.deps_mode_to_flag(deps_mode)
749 return "--deps-mode="..deps_mode
750end
751
752return deps 582return deps