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.lua316
1 files changed, 216 insertions, 100 deletions
diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua
index 7f695d9c..d54c30de 100644
--- a/src/luarocks/deps.lua
+++ b/src/luarocks/deps.lua
@@ -11,64 +11,114 @@ local util = require("luarocks.util")
11local vers = require("luarocks.core.vers") 11local vers = require("luarocks.core.vers")
12local queries = require("luarocks.queries") 12local queries = require("luarocks.queries")
13local builtin = require("luarocks.build.builtin") 13local builtin = require("luarocks.build.builtin")
14local deplocks = require("luarocks.deplocks")
14 15
15--- Attempt to match a dependency to an installed rock. 16--- Generate a function that matches dep queries against the manifest,
16-- @param dep table: A dependency parsed in table format. 17-- taking into account rocks_provided, the blacklist and the lockfile.
17-- @param blacklist table: Versions that can't be accepted. Table where keys 18-- @param deps_mode "one", "none", "all" or "order"
18-- are program versions and values are 'true'. 19-- @param rocks_provided a one-level table mapping names to versions,
20-- listing rocks to consider provided by the VM
19-- @param rocks_provided table: A table of auto-provided dependencies. 21-- @param rocks_provided table: A table of auto-provided dependencies.
20-- by this Lua implementation for the given dependency. 22-- by this Lua implementation for the given dependency.
21-- @return string or nil: latest installed version of the rock matching the dependency 23-- @param depskey key to use when matching the lockfile ("dependencies",
22-- or nil if it could not be matched. 24-- "build_dependencies", etc.)
23local function match_dep(dep, blacklist, deps_mode, rocks_provided) 25-- @param blacklist a two-level table mapping names to versions to boolean,
24 assert(type(dep) == "table") 26-- listing rocks to not match
27-- @return function(dep): {string}, {string:string}, string, boolean
28-- * array of matching versions
29-- * map of versions to locations
30-- * version matched via lockfile if any
31-- * true if rock matched via rocks_provided
32local function prepare_get_versions(deps_mode, rocks_provided, depskey, blacklist)
33 assert(type(deps_mode) == "string")
25 assert(type(rocks_provided) == "table") 34 assert(type(rocks_provided) == "table")
26 35 assert(type(depskey) == "string")
27 local versions, locations 36 assert(type(blacklist) == "table" or blacklist == nil)
28 local provided = rocks_provided[dep.name] 37
29 if provided then 38 return function(dep)
30 -- Provided rocks have higher priority than manifest's rocks. 39 local versions, locations
31 versions, locations = { provided }, {} 40 local provided = rocks_provided[dep.name]
32 else 41 if provided then
33 versions, locations = manif.get_versions(dep, deps_mode) 42 -- Provided rocks have higher priority than manifest's rocks.
43 versions, locations = { provided }, {}
44 else
45 if deps_mode == "none" then
46 deps_mode = "one"
47 end
48 versions, locations = manif.get_versions(dep, deps_mode)
49 end
50
51 if blacklist and blacklist[dep.name] then
52 local orig_versions = versions
53 versions = {}
54 for _, v in ipairs(orig_versions) do
55 if not blacklist[dep.name][v] then
56 table.insert(versions, v)
57 end
58 end
59 end
60
61 local lockversion = deplocks.get(depskey, dep.name)
62
63 return versions, locations, lockversion, provided ~= nil
34 end 64 end
65end
35 66
67--- Attempt to match a dependency to an installed rock.
68-- @param blacklist table: Versions that can't be accepted. Table where keys
69-- are program versions and values are 'true'.
70-- @param get_versions a getter function obtained via prepare_get_versions
71-- @return (string, string, table) or (nil, nil, table):
72-- 1. latest installed version of the rock matching the dependency
73-- 2. location where the installed version is installed
74-- 3. the 'dep' query table
75-- 4. true if provided via VM
76-- or
77-- 1. nil
78-- 2. nil
79-- 3. either 'dep' or an alternative query to be used
80-- 4. false
81local function match_dep(dep, get_versions)
82 assert(type(dep) == "table")
83 assert(type(get_versions) == "function")
84
85 local versions, locations, lockversion, provided = get_versions(dep)
86
36 local latest_version 87 local latest_version
37 local latest_vstring 88 local latest_vstring
38 for _, vstring in ipairs(versions) do 89 for _, vstring in ipairs(versions) do
39 if not blacklist or not blacklist[vstring] then 90 local version = vers.parse_version(vstring)
40 local version = vers.parse_version(vstring) 91 if vers.match_constraints(version, dep.constraints) then
41 if vers.match_constraints(version, dep.constraints) then 92 if not latest_version or version > latest_version then
42 if not latest_version or version > latest_version then 93 latest_version = version
43 latest_version = version 94 latest_vstring = vstring
44 latest_vstring = vstring
45 end
46 end 95 end
47 end 96 end
48 end 97 end
49 return latest_vstring, locations[latest_vstring] 98
99 if lockversion and not locations[lockversion] then
100 local latest_matching_msg = ""
101 if latest_vstring and latest_vstring ~= lockversion then
102 latest_matching_msg = " (latest matching is " .. latest_vstring .. ")"
103 end
104 util.printout("Forcing " .. dep.name .. " to pinned version " .. lockversion .. latest_matching_msg)
105 return nil, nil, queries.new(dep.name, lockversion)
106 end
107
108 return latest_vstring, locations[latest_vstring], dep, provided
50end 109end
51 110
52--- Attempt to match dependencies of a rockspec to installed rocks. 111local function match_all_deps(dependencies, get_versions)
53-- @param dependencies table: The table of dependencies. 112 assert(type(dependencies) == "table")
54-- @param rocks_provided table: The table of auto-provided dependencies. 113 assert(type(get_versions) == "function")
55-- @param blacklist table or nil: Program versions to not use as valid matches. 114
56-- Table where keys are program names and values are tables where keys
57-- are program versions and values are 'true'.
58-- @return table, table, table: A table where keys are dependencies parsed
59-- in table format and values are tables containing fields 'name' and
60-- version' representing matches; a table of missing dependencies
61-- parsed as tables; and a table of "no-upgrade" missing dependencies
62-- (to be used in plugin modules so that a plugin does not force upgrade of
63-- its parent application).
64function deps.match_deps(dependencies, rocks_provided, blacklist, deps_mode)
65 assert(type(blacklist) == "table" or not blacklist)
66 local matched, missing, no_upgrade = {}, {}, {} 115 local matched, missing, no_upgrade = {}, {}, {}
67 116
68 for _, dep in ipairs(dependencies) do 117 for _, dep in ipairs(dependencies) do
69 local found = match_dep(dep, blacklist and blacklist[dep.name] or nil, deps_mode, rocks_provided) 118 local found, _, provided
119 found, _, dep, provided = match_dep(dep, get_versions)
70 if found then 120 if found then
71 if not rocks_provided[dep.name] then 121 if not provided then
72 matched[dep] = {name = dep.name, version = found} 122 matched[dep] = {name = dep.name, version = found}
73 end 123 end
74 else 124 else
@@ -82,20 +132,35 @@ function deps.match_deps(dependencies, rocks_provided, blacklist, deps_mode)
82 return matched, missing, no_upgrade 132 return matched, missing, no_upgrade
83end 133end
84 134
85--- Return a set of values of a table. 135--- Attempt to match dependencies of a rockspec to installed rocks.
86-- @param tbl table: The input table. 136-- @param dependencies table: The table of dependencies.
87-- @return table: The array of keys. 137-- @param rocks_provided table: The table of auto-provided dependencies.
88local function values_set(tbl) 138-- @param blacklist table or nil: Program versions to not use as valid matches.
89 local set = {} 139-- Table where keys are program names and values are tables where keys
90 for _, v in pairs(tbl) do 140-- are program versions and values are 'true'.
91 set[v] = true 141-- @param deps_mode string: Which trees to check dependencies for
92 end 142-- @return table, table, table: A table where keys are dependencies parsed
93 return set 143-- in table format and values are tables containing fields 'name' and
144-- version' representing matches; a table of missing dependencies
145-- parsed as tables; and a table of "no-upgrade" missing dependencies
146-- (to be used in plugin modules so that a plugin does not force upgrade of
147-- its parent application).
148function deps.match_deps(dependencies, rocks_provided, blacklist, deps_mode)
149 assert(type(dependencies) == "table")
150 assert(type(rocks_provided) == "table")
151 assert(type(blacklist) == "table" or blacklist == nil)
152 assert(type(deps_mode) == "string")
153
154 local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies", blacklist)
155 return match_all_deps(dependencies, get_versions)
94end 156end
95 157
96local function rock_status(name, deps_mode, rocks_provided) 158local function rock_status(name, get_versions)
97 local installed = match_dep(queries.new(name), nil, deps_mode, rocks_provided) 159 assert(type(name) == "string")
98 local installation_type = rocks_provided[name] and "provided by VM" or "installed" 160 assert(type(get_versions) == "function")
161
162 local installed, _, _, provided = match_dep(queries.new(name), get_versions)
163 local installation_type = provided and "provided by VM" or "installed"
99 return installed and installed.." "..installation_type or "not installed" 164 return installed and installed.." "..installation_type or "not installed"
100end 165end
101 166
@@ -103,7 +168,7 @@ end
103-- @param name string: package name. 168-- @param name string: package name.
104-- @param version string: package version. 169-- @param version string: package version.
105-- @param dependencies table: array of dependencies. 170-- @param dependencies table: array of dependencies.
106-- @param deps_mode string: Which trees to check dependencies for: 171-- @param deps_mode string: Which trees to check dependencies for
107-- @param rocks_provided table: A table of auto-dependencies provided 172-- @param rocks_provided table: A table of auto-dependencies provided
108-- by this Lua implementation for the given dependency. 173-- by this Lua implementation for the given dependency.
109-- "one" for the current default tree, "all" for all trees, 174-- "one" for the current default tree, "all" for all trees,
@@ -114,58 +179,53 @@ function deps.report_missing_dependencies(name, version, dependencies, deps_mode
114 assert(type(dependencies) == "table") 179 assert(type(dependencies) == "table")
115 assert(type(deps_mode) == "string") 180 assert(type(deps_mode) == "string")
116 assert(type(rocks_provided) == "table") 181 assert(type(rocks_provided) == "table")
182
183 if deps_mode == "none" then
184 return
185 end
186
187 local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies")
117 188
118 local first_missing_dep = true 189 local first_missing_dep = true
119 190
120 for _, dep in ipairs(dependencies) do 191 for _, dep in ipairs(dependencies) do
121 if not match_dep(dep, nil, deps_mode, rocks_provided) then 192 local found, _
193 found, _, dep = match_dep(dep, get_versions)
194 if not found then
122 if first_missing_dep then 195 if first_missing_dep then
123 util.printout(("Missing dependencies for %s %s:"):format(name, version)) 196 util.printout(("Missing dependencies for %s %s:"):format(name, version))
124 first_missing_dep = false 197 first_missing_dep = false
125 end 198 end
126 199
127 util.printout((" %s (%s)"):format(tostring(dep), rock_status(dep.name, deps_mode, rocks_provided))) 200 util.printout((" %s (%s)"):format(tostring(dep), rock_status(dep.name, get_versions)))
128 end 201 end
129 end 202 end
130end 203end
131 204
132function deps.fulfill_dependency(dep, deps_mode, name, version, rocks_provided, verify) 205function deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey)
133 assert(dep:type() == "query") 206 assert(dep:type() == "query")
134 assert(type(deps_mode) == "string" or deps_mode == nil) 207 assert(type(deps_mode) == "string" or deps_mode == nil)
135 assert(type(name) == "string" or name == nil)
136 assert(type(version) == "string" or version == nil)
137 assert(type(rocks_provided) == "table" or rocks_provided == nil) 208 assert(type(rocks_provided) == "table" or rocks_provided == nil)
138 assert(type(verify) == "boolean" or verify == nil) 209 assert(type(verify) == "boolean" or verify == nil)
210 assert(type(depskey) == "string")
211
139 deps_mode = deps_mode or "all" 212 deps_mode = deps_mode or "all"
140 rocks_provided = rocks_provided or {} 213 rocks_provided = rocks_provided or {}
141 214
142 local found, where = match_dep(dep, nil, deps_mode, rocks_provided) 215 local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey)
216
217 local found, where
218 found, where, dep = match_dep(dep, get_versions)
143 if found then 219 if found then
220 local tree_manifests = manif.load_rocks_tree_manifests(deps_mode)
221 manif.scan_dependencies(dep.name, found, tree_manifests, deplocks.proxy(depskey))
144 return true, found, where 222 return true, found, where
145 end 223 end
146 224
147 local search = require("luarocks.search") 225 local search = require("luarocks.search")
148 local install = require("luarocks.cmd.install") 226 local install = require("luarocks.cmd.install")
149 227
150 if name and version then 228 local url, search_err = search.find_suitable_rock(dep, true)
151 util.printout(("%s %s depends on %s (%s)"):format(
152 name, version, tostring(dep), rock_status(dep.name, deps_mode, rocks_provided)))
153 else
154 util.printout(("Fulfilling dependency on %s (%s)"):format(
155 tostring(dep), rock_status(dep.name, deps_mode, rocks_provided)))
156 end
157
158 if dep.constraints[1] and dep.constraints[1].no_upgrade then
159 util.printerr("This version of "..name.." is designed for use with")
160 util.printerr(tostring(dep)..", but is configured to avoid upgrading it")
161 util.printerr("automatically. Please upgrade "..dep.name.." with")
162 util.printerr(" luarocks install "..dep.name)
163 util.printerr("or choose an older version of "..name.." with")
164 util.printerr(" luarocks search "..name)
165 return nil, "Failed matching dependencies"
166 end
167
168 local url, search_err = search.find_suitable_rock(dep)
169 if not url then 229 if not url then
170 return nil, "Could not satisfy dependency "..tostring(dep)..": "..search_err 230 return nil, "Could not satisfy dependency "..tostring(dep)..": "..search_err
171 end 231 end
@@ -181,27 +241,12 @@ function deps.fulfill_dependency(dep, deps_mode, name, version, rocks_provided,
181 return nil, "Failed installing dependency: "..url.." - "..install_err, errcode 241 return nil, "Failed installing dependency: "..url.." - "..install_err, errcode
182 end 242 end
183 243
184 found, where = match_dep(dep, nil, deps_mode, rocks_provided) 244 found, where = match_dep(dep, get_versions)
185 assert(found) 245 assert(found)
186 return true, found, where 246 return true, found, where
187end 247end
188 248
189--- Check dependencies of a rock and attempt to install any missing ones. 249local function check_supported_platforms(rockspec)
190-- Packages are installed using the LuaRocks "install" command.
191-- Aborts the program if a dependency could not be fulfilled.
192-- @param rockspec table: A rockspec in table format.
193-- @param depskey string: Rockspec key to fetch to get dependency table.
194-- @param deps_mode string
195-- @param verify boolean
196-- @return boolean or (nil, string, [string]): True if no errors occurred, or
197-- nil and an error message if any test failed, followed by an optional
198-- error code.
199function deps.fulfill_dependencies(rockspec, depskey, deps_mode, verify)
200 assert(type(rockspec) == "table")
201 assert(type(depskey) == "string")
202 assert(type(deps_mode) == "string")
203 assert(type(verify) == "boolean" or verify == nil)
204
205 if rockspec.supported_platforms and next(rockspec.supported_platforms) then 250 if rockspec.supported_platforms and next(rockspec.supported_platforms) then
206 local all_negative = true 251 local all_negative = true
207 local supported = false 252 local supported = false
@@ -225,14 +270,82 @@ function deps.fulfill_dependencies(rockspec, depskey, deps_mode, verify)
225 return nil, "This rockspec for "..rockspec.package.." does not support "..plats.." platforms." 270 return nil, "This rockspec for "..rockspec.package.." does not support "..plats.." platforms."
226 end 271 end
227 end 272 end
273
274 return true
275end
228 276
229 deps.report_missing_dependencies(rockspec.name, rockspec.version, rockspec[depskey], deps_mode, rockspec.rocks_provided) 277--- Check dependencies of a rock and attempt to install any missing ones.
278-- Packages are installed using the LuaRocks "install" command.
279-- Aborts the program if a dependency could not be fulfilled.
280-- @param rockspec table: A rockspec in table format.
281-- @param depskey string: Rockspec key to fetch to get dependency table.
282-- @param deps_mode string
283-- @param verify boolean
284-- @param deplock_dir string: dirname of the deplock file
285-- @return boolean or (nil, string, [string]): True if no errors occurred, or
286-- nil and an error message if any test failed, followed by an optional
287-- error code.
288function deps.fulfill_dependencies(rockspec, depskey, deps_mode, verify, deplock_dir)
289 assert(type(rockspec) == "table")
290 assert(type(depskey) == "string")
291 assert(type(deps_mode) == "string")
292 assert(type(verify) == "boolean" or verify == nil)
293 assert(type(deplock_dir) == "string" or deplock_dir == nil)
294
295 local name = rockspec.name
296 local version = rockspec.version
297 local rocks_provided = rockspec.rocks_provided
298
299 local ok, filename, err = deplocks.load(name, deplock_dir or ".")
300 if filename then
301 util.printout("Using dependencies pinned in lockfile: " .. filename)
302
303 local get_versions = prepare_get_versions("none", rocks_provided, depskey)
304 for dname, dversion in deplocks.each(depskey) do
305 local dep = queries.new(dname, dversion)
306
307 util.printout(("%s %s is pinned to %s (%s)"):format(
308 name, version, tostring(dep), rock_status(dep.name, get_versions)))
309
310 local ok, err = deps.fulfill_dependency(dep, "none", rocks_provided, verify, depskey)
311 if not ok then
312 return nil, err
313 end
314 end
315 util.printout()
316 return true
317 elseif err then
318 util.warning(err)
319 end
320
321 ok, err = check_supported_platforms(rockspec)
322 if not ok then
323 return nil, err
324 end
325
326 deps.report_missing_dependencies(name, version, rockspec[depskey], deps_mode, rocks_provided)
230 327
231 util.printout() 328 util.printout()
329
330 local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey)
232 for _, dep in ipairs(rockspec[depskey]) do 331 for _, dep in ipairs(rockspec[depskey]) do
233 local ok, err = deps.fulfill_dependency(dep, deps_mode, rockspec.name, rockspec.version, rockspec.rocks_provided, verify) 332
234 if not ok then 333 util.printout(("%s %s depends on %s (%s)"):format(
235 return nil, err 334 name, version, tostring(dep), rock_status(dep.name, get_versions)))
335
336 local ok, found_or_err, _, no_upgrade = deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey)
337 if ok then
338 deplocks.add(depskey, dep.name, found_or_err)
339 else
340 if no_upgrade then
341 util.printerr("This version of "..name.." is designed for use with")
342 util.printerr(tostring(dep)..", but is configured to avoid upgrading it")
343 util.printerr("automatically. Please upgrade "..dep.name.." with")
344 util.printerr(" luarocks install "..dep.name)
345 util.printerr("or look for a suitable version of "..name.." with")
346 util.printerr(" luarocks search "..name)
347 end
348 return nil, found_or_err
236 end 349 end
237 end 350 end
238 351
@@ -500,7 +613,10 @@ function deps.scan_deps(results, manifest, name, version, deps_mode)
500 else 613 else
501 rocks_provided = util.get_rocks_provided() 614 rocks_provided = util.get_rocks_provided()
502 end 615 end
503 local matched = deps.match_deps(dependencies, rocks_provided, nil, deps_mode) 616
617 local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies")
618
619 local matched = match_all_deps(dependencies, get_versions)
504 results[name] = version 620 results[name] = version
505 for _, match in pairs(matched) do 621 for _, match in pairs(matched) do
506 deps.scan_deps(results, manifest, match.name, match.version, deps_mode) 622 deps.scan_deps(results, manifest, match.name, match.version, deps_mode)