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.lua150
1 files changed, 8 insertions, 142 deletions
diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua
index 1c75a694..6548e56e 100644
--- a/src/luarocks/deps.lua
+++ b/src/luarocks/deps.lua
@@ -1,147 +1,13 @@
1 1
2--- Dependency handling functions. 2--- High-level dependency related 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.
14local deps = {} 3local deps = {}
15setmetatable(deps, { __index = require("luarocks.core.deps") })
16 4
17local cfg = require("luarocks.core.cfg") 5local cfg = require("luarocks.core.cfg")
18local manif = require("luarocks.core.manif") 6local manif = require("luarocks.core.manif")
19local path = require("luarocks.path") 7local path = require("luarocks.path")
20local dir = require("luarocks.dir") 8local dir = require("luarocks.dir")
21local util = require("luarocks.util") 9local util = require("luarocks.util")
22 10local vers = require("luarocks.vers")
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
32local operators = {
33 ["=="] = "==",
34 ["~="] = "~=",
35 [">"] = ">",
36 ["<"] = "<",
37 [">="] = ">=",
38 ["<="] = "<=",
39 ["~>"] = "~>",
40 -- plus some convenience translations
41 [""] = "==",
42 ["="] = "==",
43 ["!="] = "~="
44}
45
46--- Consumes a constraint from a string, converting it to table format.
47-- For example, a string ">= 1.0, > 2.0" is converted to a table in the
48-- format {op = ">=", version={1,0}} and the rest, "> 2.0", is returned
49-- back to the caller.
50-- @param input string: A list of constraints in string format.
51-- @return (table, string) or nil: A table representing the same
52-- constraints and the string with the unused input, or nil if the
53-- input string is invalid.
54local function parse_constraint(input)
55 assert(type(input) == "string")
56
57 local no_upgrade, op, version, rest = input:match("^(@?)([<>=~!]*)%s*([%w%.%_%-]+)[%s,]*(.*)")
58 local _op = operators[op]
59 version = deps.parse_version(version)
60 if not _op then
61 return nil, "Encountered bad constraint operator: '"..tostring(op).."' in '"..input.."'"
62 end
63 if not version then
64 return nil, "Could not parse version from constraint: '"..input.."'"
65 end
66 return { op = _op, version = version, no_upgrade = no_upgrade=="@" and true or nil }, rest
67end
68
69--- Convert a list of constraints from string to table format.
70-- For example, a string ">= 1.0, < 2.0" is converted to a table in the format
71-- {{op = ">=", version={1,0}}, {op = "<", version={2,0}}}.
72-- Version tables use a metatable allowing later comparison through
73-- relational operators.
74-- @param input string: A list of constraints in string format.
75-- @return table or nil: A table representing the same constraints,
76-- or nil if the input string is invalid.
77function deps.parse_constraints(input)
78 assert(type(input) == "string")
79
80 local constraints, oinput, constraint = {}, input
81 while #input > 0 do
82 constraint, input = parse_constraint(input)
83 if constraint then
84 table.insert(constraints, constraint)
85 else
86 return nil, "Failed to parse constraint '"..tostring(oinput).."' with error: ".. input
87 end
88 end
89 return constraints
90end
91
92--- Convert a dependency from string to table format.
93-- For example, a string "foo >= 1.0, < 2.0"
94-- is converted to a table in the format
95-- {name = "foo", constraints = {{op = ">=", version={1,0}},
96-- {op = "<", version={2,0}}}}. Version tables use a metatable
97-- allowing later comparison through relational operators.
98-- @param dep string: A dependency in string format
99-- as entered in rockspec files.
100-- @return table or nil: A table representing the same dependency relation,
101-- or nil if the input string is invalid.
102function deps.parse_dep(dep)
103 assert(type(dep) == "string")
104
105 local name, rest = dep:match("^%s*([a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*(.*)")
106 if not name then return nil, "failed to extract dependency name from '"..tostring(dep).."'" end
107 local constraints, err = deps.parse_constraints(rest)
108 if not constraints then return nil, err end
109 return { name = name, constraints = constraints }
110end
111
112--- Convert a version table to a string.
113-- @param v table: The version table
114-- @param internal boolean or nil: Whether to display versions in their
115-- internal representation format or how they were specified.
116-- @return string: The dependency information pretty-printed as a string.
117function deps.show_version(v, internal)
118 assert(type(v) == "table")
119 assert(type(internal) == "boolean" or not internal)
120
121 return (internal
122 and table.concat(v, ":")..(v.revision and tostring(v.revision) or "")
123 or v.string)
124end
125
126--- Convert a dependency in table format to a string.
127-- @param dep table: The dependency in table format
128-- @param internal boolean or nil: Whether to display versions in their
129-- internal representation format or how they were specified.
130-- @return string: The dependency information pretty-printed as a string.
131function deps.show_dep(dep, internal)
132 assert(type(dep) == "table")
133 assert(type(internal) == "boolean" or not internal)
134
135 if #dep.constraints > 0 then
136 local pretty = {}
137 for _, c in ipairs(dep.constraints) do
138 table.insert(pretty, c.op .. " " .. deps.show_version(c.version, internal))
139 end
140 return dep.name.." "..table.concat(pretty, ", ")
141 else
142 return dep.name
143 end
144end
145 11
146--- Attempt to match a dependency to an installed rock. 12--- Attempt to match a dependency to an installed rock.
147-- @param dep table: A dependency parsed in table format. 13-- @param dep table: A dependency parsed in table format.
@@ -167,8 +33,8 @@ local function match_dep(dep, blacklist, deps_mode, rocks_provided)
167 local latest_version 33 local latest_version
168 for _, vstring in ipairs(versions) do 34 for _, vstring in ipairs(versions) do
169 if not blacklist or not blacklist[vstring] then 35 if not blacklist or not blacklist[vstring] then
170 local version = deps.parse_version(vstring) 36 local version = vers.parse_version(vstring)
171 if deps.match_constraints(version, dep.constraints) then 37 if vers.match_constraints(version, dep.constraints) then
172 if not latest_version or version > latest_version then 38 if not latest_version or version > latest_version then
173 latest_version = version 39 latest_version = version
174 end 40 end
@@ -249,7 +115,7 @@ function deps.report_missing_dependencies(name, version, dependencies, deps_mode
249 first_missing_dep = false 115 first_missing_dep = false
250 end 116 end
251 117
252 util.printout((" %s (%s)"):format(deps.show_dep(dep), rock_status(dep.name, deps_mode, rocks_provided))) 118 util.printout((" %s (%s)"):format(vers.show_dep(dep), rock_status(dep.name, deps_mode, rocks_provided)))
253 end 119 end
254 end 120 end
255end 121end
@@ -306,11 +172,11 @@ function deps.fulfill_dependencies(rockspec, deps_mode)
306 end 172 end
307 173
308 util.printout(("%s %s depends on %s (%s)"):format( 174 util.printout(("%s %s depends on %s (%s)"):format(
309 rockspec.name, rockspec.version, deps.show_dep(dep), rock_status(dep.name, deps_mode, rockspec.rocks_provided))) 175 rockspec.name, rockspec.version, vers.show_dep(dep), rock_status(dep.name, deps_mode, rockspec.rocks_provided)))
310 176
311 if dep.constraints[1] and dep.constraints[1].no_upgrade then 177 if dep.constraints[1] and dep.constraints[1].no_upgrade then
312 util.printerr("This version of "..rockspec.name.." is designed for use with") 178 util.printerr("This version of "..rockspec.name.." is designed for use with")
313 util.printerr(deps.show_dep(dep)..", but is configured to avoid upgrading it") 179 util.printerr(vers.show_dep(dep)..", but is configured to avoid upgrading it")
314 util.printerr("automatically. Please upgrade "..dep.name.." with") 180 util.printerr("automatically. Please upgrade "..dep.name.." with")
315 util.printerr(" luarocks install "..dep.name) 181 util.printerr(" luarocks install "..dep.name)
316 util.printerr("or choose an older version of "..rockspec.name.." with") 182 util.printerr("or choose an older version of "..rockspec.name.." with")
@@ -320,7 +186,7 @@ function deps.fulfill_dependencies(rockspec, deps_mode)
320 186
321 local url, search_err = search.find_suitable_rock(dep) 187 local url, search_err = search.find_suitable_rock(dep)
322 if not url then 188 if not url then
323 return nil, "Could not satisfy dependency "..deps.show_dep(dep)..": "..search_err 189 return nil, "Could not satisfy dependency "..vers.show_dep(dep)..": "..search_err
324 end 190 end
325 util.printout("Installing "..url) 191 util.printout("Installing "..url)
326 local ok, install_err, errcode = install.command({deps_mode = deps_mode}, url) 192 local ok, install_err, errcode = install.command({deps_mode = deps_mode}, url)