aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHisham Muhammad <hisham@gobolinux.org>2019-11-08 18:19:17 -0300
committerHisham Muhammad <hisham@gobolinux.org>2019-11-29 18:36:42 -0300
commitacf8bcb82ae697a5bac94bfbe93c3ad9d7487795 (patch)
tree62b7239b8701526d69833d9a64f5757bdee2e1df
parentdfc277954d2fe2586dc491cdc1216bae22399251 (diff)
downloadluarocks-acf8bcb82ae697a5bac94bfbe93c3ad9d7487795.tar.gz
luarocks-acf8bcb82ae697a5bac94bfbe93c3ad9d7487795.tar.bz2
luarocks-acf8bcb82ae697a5bac94bfbe93c3ad9d7487795.zip
dependency pinning: luarocks.lock file and --pin flag
This adds support for pinning dependencies in projects and rocks: * Adds a new flag called `--pin` which creates a `luarocks.lock` when building a rock with `luarocks build` or `luarocks make`. This lock file contains the exact version numbers of every direct or indirect dependency of the rock (in other words, it is the transitive closure of the dependencies.) For `make`, the `luarocks.lock` file is created in the current directory. The lock file is also installed as part of the rock in its metadata directory alongside its rockspec. When using `--pin`, if a lock file already exists, it is ignored and overwritten. * When building a rock with `luarocks make`, if there is a `luarocks.lock` file in the current directory, the exact versions specified there will be used for resolving dependencies. * When building a rock with `luarocks build`, if there is a `luarocks.lock` file in root of its sources, the exact versions specified there will be used for resolving dependencies. * When installing a `.rock` file with `luarocks install`, if the rock contains a `luarocks.lock` file (i.e., if its dependencies were pinned with `--pin` when the rock was built), the exact versions specified there will be used for resolving dependencies.
-rw-r--r--spec/build_spec.lua41
-rw-r--r--spec/doc_spec.lua2
-rw-r--r--spec/fixtures/a_repo/a_rock-2.0-1.src.rockbin0 -> 541 bytes
-rw-r--r--spec/fixtures/a_repo/manifest5
-rw-r--r--spec/fixtures/a_repo/manifest-5.15
-rw-r--r--spec/fixtures/a_repo/manifest-5.1.zipbin350 -> 358 bytes
-rw-r--r--spec/fixtures/a_repo/manifest-5.25
-rw-r--r--spec/fixtures/a_repo/manifest-5.2.zipbin350 -> 358 bytes
-rw-r--r--spec/fixtures/a_repo/manifest-5.35
-rw-r--r--spec/fixtures/a_repo/manifest-5.3.zipbin350 -> 358 bytes
-rw-r--r--spec/install_spec.lua44
-rw-r--r--spec/make_spec.lua119
-rw-r--r--src/luarocks/build.lua25
-rw-r--r--src/luarocks/cmd/build.lua11
-rw-r--r--src/luarocks/cmd/install.lua4
-rw-r--r--src/luarocks/cmd/make.lua8
-rw-r--r--src/luarocks/core/manif.lua2
-rw-r--r--src/luarocks/core/persist.lua4
-rw-r--r--src/luarocks/deplocks.lua106
-rw-r--r--src/luarocks/deps.lua316
-rw-r--r--src/luarocks/persist.lua3
-rw-r--r--src/luarocks/test/busted.lua2
22 files changed, 590 insertions, 117 deletions
diff --git a/spec/build_spec.lua b/spec/build_spec.lua
index eeca75cf..d64ace4d 100644
--- a/spec/build_spec.lua
+++ b/spec/build_spec.lua
@@ -116,11 +116,11 @@ describe("LuaRocks build #integration", function()
116 end) 116 end)
117 117
118 it("with --only-sources", function() 118 it("with --only-sources", function()
119 assert.is_true(run.luarocks_bool("download --server=" .. testing_paths.fixtures_dir .. "/a_repo --rockspec a_rock")) 119 assert.is_true(run.luarocks_bool("download --server=" .. testing_paths.fixtures_dir .. "/a_repo --rockspec a_rock 1.0"))
120 assert.is_false(run.luarocks_bool("build --only-sources=\"http://example.com\" a_rock-1.0-1.rockspec")) 120 assert.is_false(run.luarocks_bool("build --only-sources=\"http://example.com\" a_rock-1.0-1.rockspec"))
121 assert.is.falsy(lfs.attributes(testing_paths.testing_sys_rocks .. "/a_rock/1.0-1/a_rock-1.0-1.rockspec")) 121 assert.is.falsy(lfs.attributes(testing_paths.testing_sys_rocks .. "/a_rock/1.0-1/a_rock-1.0-1.rockspec"))
122 122
123 assert.is_true(run.luarocks_bool("download --server=" .. testing_paths.fixtures_dir .. "/a_repo --source a_rock")) 123 assert.is_true(run.luarocks_bool("download --server=" .. testing_paths.fixtures_dir .. "/a_repo --source a_rock 1.0"))
124 assert.is_true(run.luarocks_bool("build --only-sources=\"http://example.com\" a_rock-1.0-1.src.rock")) 124 assert.is_true(run.luarocks_bool("build --only-sources=\"http://example.com\" a_rock-1.0-1.src.rock"))
125 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_rocks .. "/a_rock/1.0-1/a_rock-1.0-1.rockspec")) 125 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_rocks .. "/a_rock/1.0-1/a_rock-1.0-1.rockspec"))
126 126
@@ -199,7 +199,42 @@ describe("LuaRocks build #integration", function()
199 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_rocks .. "/test/1.0-1/test-1.0-1.rockspec")) 199 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_rocks .. "/test/1.0-1/test-1.0-1.rockspec"))
200 end) 200 end)
201 end) 201 end)
202 202
203 it("supports --pin #pinning", function()
204 test_env.run_in_tmp(function(tmpdir)
205 write_file("test-1.0-1.rockspec", [[
206 package = "test"
207 version = "1.0-1"
208 source = {
209 url = "file://]] .. tmpdir:gsub("\\", "/") .. [[/test.lua"
210 }
211 dependencies = {
212 "a_rock >= 0.8"
213 }
214 build = {
215 type = "builtin",
216 modules = {
217 test = "test.lua"
218 }
219 }
220 ]], finally)
221 write_file("test.lua", "return {}", finally)
222
223 assert.is_true(run.luarocks_bool("build --server=" .. testing_paths.fixtures_dir .. "/a_repo test-1.0-1.rockspec --pin --tree=lua_modules"))
224 assert.is.truthy(lfs.attributes("./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/test/1.0-1/test-1.0-1.rockspec"))
225 assert.is.truthy(lfs.attributes("./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/a_rock/2.0-1/a_rock-2.0-1.rockspec"))
226 local lockfilename = "./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/test/1.0-1/luarocks.lock"
227 assert.is.truthy(lfs.attributes(lockfilename))
228 local lockdata = loadfile(lockfilename)()
229 assert.same({
230 dependencies = {
231 ["a_rock"] = "2.0-1",
232 ["lua"] = test_env.lua_version .. "-1",
233 }
234 }, lockdata)
235 end)
236 end)
237
203 it("lmathx deps partial match", function() 238 it("lmathx deps partial match", function()
204 assert.is_true(run.luarocks_bool("build lmathx")) 239 assert.is_true(run.luarocks_bool("build lmathx"))
205 240
diff --git a/spec/doc_spec.lua b/spec/doc_spec.lua
index bf5214d4..2c0f4bce 100644
--- a/spec/doc_spec.lua
+++ b/spec/doc_spec.lua
@@ -70,7 +70,7 @@ describe("LuaRocks doc tests #integration", function()
70 describe("#namespaces", function() 70 describe("#namespaces", function()
71 it("retrieves docs for a namespaced package from the command-line", function() 71 it("retrieves docs for a namespaced package from the command-line", function()
72 assert(run.luarocks_bool("build a_user/a_rock --server=" .. testing_paths.fixtures_dir .. "/a_repo" )) 72 assert(run.luarocks_bool("build a_user/a_rock --server=" .. testing_paths.fixtures_dir .. "/a_repo" ))
73 assert(run.luarocks_bool("build a_rock --keep --server=" .. testing_paths.fixtures_dir .. "/a_repo" )) 73 assert(run.luarocks_bool("build a_rock 1.0 --keep --server=" .. testing_paths.fixtures_dir .. "/a_repo" ))
74 assert.match("a_rock 2.0", run.luarocks("doc a_user/a_rock")) 74 assert.match("a_rock 2.0", run.luarocks("doc a_user/a_rock"))
75 end) 75 end)
76 end) 76 end)
diff --git a/spec/fixtures/a_repo/a_rock-2.0-1.src.rock b/spec/fixtures/a_repo/a_rock-2.0-1.src.rock
new file mode 100644
index 00000000..5824c767
--- /dev/null
+++ b/spec/fixtures/a_repo/a_rock-2.0-1.src.rock
Binary files differ
diff --git a/spec/fixtures/a_repo/manifest b/spec/fixtures/a_repo/manifest
index 3b01b427..5ab87d25 100644
--- a/spec/fixtures/a_repo/manifest
+++ b/spec/fixtures/a_repo/manifest
@@ -19,6 +19,11 @@ repository = {
19 { 19 {
20 arch = "rockspec" 20 arch = "rockspec"
21 } 21 }
22 },
23 ["2.0-1"] = {
24 {
25 arch = "src"
26 }
22 } 27 }
23 }, 28 },
24 busted_project = { 29 busted_project = {
diff --git a/spec/fixtures/a_repo/manifest-5.1 b/spec/fixtures/a_repo/manifest-5.1
index 3b01b427..5ab87d25 100644
--- a/spec/fixtures/a_repo/manifest-5.1
+++ b/spec/fixtures/a_repo/manifest-5.1
@@ -19,6 +19,11 @@ repository = {
19 { 19 {
20 arch = "rockspec" 20 arch = "rockspec"
21 } 21 }
22 },
23 ["2.0-1"] = {
24 {
25 arch = "src"
26 }
22 } 27 }
23 }, 28 },
24 busted_project = { 29 busted_project = {
diff --git a/spec/fixtures/a_repo/manifest-5.1.zip b/spec/fixtures/a_repo/manifest-5.1.zip
index 7b53aeb0..65e316df 100644
--- a/spec/fixtures/a_repo/manifest-5.1.zip
+++ b/spec/fixtures/a_repo/manifest-5.1.zip
Binary files differ
diff --git a/spec/fixtures/a_repo/manifest-5.2 b/spec/fixtures/a_repo/manifest-5.2
index 3b01b427..5ab87d25 100644
--- a/spec/fixtures/a_repo/manifest-5.2
+++ b/spec/fixtures/a_repo/manifest-5.2
@@ -19,6 +19,11 @@ repository = {
19 { 19 {
20 arch = "rockspec" 20 arch = "rockspec"
21 } 21 }
22 },
23 ["2.0-1"] = {
24 {
25 arch = "src"
26 }
22 } 27 }
23 }, 28 },
24 busted_project = { 29 busted_project = {
diff --git a/spec/fixtures/a_repo/manifest-5.2.zip b/spec/fixtures/a_repo/manifest-5.2.zip
index d2eddc40..b4334a65 100644
--- a/spec/fixtures/a_repo/manifest-5.2.zip
+++ b/spec/fixtures/a_repo/manifest-5.2.zip
Binary files differ
diff --git a/spec/fixtures/a_repo/manifest-5.3 b/spec/fixtures/a_repo/manifest-5.3
index 3b01b427..5ab87d25 100644
--- a/spec/fixtures/a_repo/manifest-5.3
+++ b/spec/fixtures/a_repo/manifest-5.3
@@ -19,6 +19,11 @@ repository = {
19 { 19 {
20 arch = "rockspec" 20 arch = "rockspec"
21 } 21 }
22 },
23 ["2.0-1"] = {
24 {
25 arch = "src"
26 }
22 } 27 }
23 }, 28 },
24 busted_project = { 29 busted_project = {
diff --git a/spec/fixtures/a_repo/manifest-5.3.zip b/spec/fixtures/a_repo/manifest-5.3.zip
index 686fe232..bab15712 100644
--- a/spec/fixtures/a_repo/manifest-5.3.zip
+++ b/spec/fixtures/a_repo/manifest-5.3.zip
Binary files differ
diff --git a/spec/install_spec.lua b/spec/install_spec.lua
index bf50ced7..76dc2201 100644
--- a/spec/install_spec.lua
+++ b/spec/install_spec.lua
@@ -267,6 +267,50 @@ describe("luarocks install #integration", function()
267 end) 267 end)
268 end) 268 end)
269 269
270 it("respects luarocks.lock in package #pinning", function()
271 test_env.run_in_tmp(function(tmpdir)
272 write_file("test-1.0-1.rockspec", [[
273 package = "test"
274 version = "1.0-1"
275 source = {
276 url = "file://]] .. tmpdir:gsub("\\", "/") .. [[/test.lua"
277 }
278 dependencies = {
279 "a_rock >= 0.8"
280 }
281 build = {
282 type = "builtin",
283 modules = {
284 test = "test.lua"
285 }
286 }
287 ]], finally)
288 write_file("test.lua", "return {}", finally)
289 write_file("luarocks.lock", [[
290 return {
291 dependencies = {
292 ["a_rock"] = "1.0-1",
293 }
294 }
295 ]], finally)
296
297 assert.is_true(run.luarocks_bool("make --pack-binary-rock --server=" .. testing_paths.fixtures_dir .. "/a_repo test-1.0-1.rockspec"))
298 assert.is_true(os.remove("luarocks.lock"))
299
300 assert.is.truthy(lfs.attributes("./test-1.0-1.all.rock"))
301
302 assert.is.falsy(lfs.attributes("./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/test/1.0-1/test-1.0-1.rockspec"))
303 assert.is.falsy(lfs.attributes("./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/a_rock/1.0-1/a_rock-1.0-1.rockspec"))
304
305 print(run.luarocks("install ./test-1.0-1.all.rock --tree=lua_modules --server=" .. testing_paths.fixtures_dir .. "/a_repo"))
306
307 assert.is.truthy(lfs.attributes("./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/test/1.0-1/test-1.0-1.rockspec"))
308 assert.is.truthy(lfs.attributes("./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/test/1.0-1/luarocks.lock"))
309 assert.is.truthy(lfs.attributes("./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/a_rock/1.0-1/a_rock-1.0-1.rockspec"))
310 assert.is.falsy(lfs.attributes("./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/a_rock/2.0-1"))
311 end)
312 end)
313
270 describe("#unix install runs build from #git", function() 314 describe("#unix install runs build from #git", function()
271 local git 315 local git
272 316
diff --git a/spec/make_spec.lua b/spec/make_spec.lua
index 5ec99fa7..8baa3561 100644
--- a/spec/make_spec.lua
+++ b/spec/make_spec.lua
@@ -3,6 +3,7 @@ local lfs = require("lfs")
3local run = test_env.run 3local run = test_env.run
4local testing_paths = test_env.testing_paths 4local testing_paths = test_env.testing_paths
5local env_variables = test_env.env_variables 5local env_variables = test_env.env_variables
6local write_file = test_env.write_file
6 7
7test_env.unload_luarocks() 8test_env.unload_luarocks()
8 9
@@ -125,6 +126,124 @@ describe("LuaRocks make tests #integration", function()
125 end) 126 end)
126 end) 127 end)
127 128
129 it("supports --pin #pinning", function()
130 test_env.run_in_tmp(function(tmpdir)
131 write_file("test-1.0-1.rockspec", [[
132 package = "test"
133 version = "1.0-1"
134 source = {
135 url = "file://]] .. tmpdir:gsub("\\", "/") .. [[/test.lua"
136 }
137 dependencies = {
138 "a_rock 1.0"
139 }
140 build = {
141 type = "builtin",
142 modules = {
143 test = "test.lua"
144 }
145 }
146 ]], finally)
147 write_file("test.lua", "return {}", finally)
148
149 assert.is_true(run.luarocks_bool("make --server=" .. testing_paths.fixtures_dir .. "/a_repo --pin --tree=lua_modules"))
150 assert.is.truthy(lfs.attributes("./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/test/1.0-1/test-1.0-1.rockspec"))
151 assert.is.truthy(lfs.attributes("./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/a_rock/1.0-1/a_rock-1.0-1.rockspec"))
152 local lockfilename = "./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/test/1.0-1/luarocks.lock"
153 assert.is.truthy(lfs.attributes(lockfilename))
154 local lockdata = loadfile(lockfilename)()
155 assert.same({
156 dependencies = {
157 ["a_rock"] = "1.0-1",
158 ["lua"] = test_env.lua_version .. "-1",
159 }
160 }, lockdata)
161 end)
162 end)
163
164 it("respects luarocks.lock when present #pinning", function()
165 test_env.run_in_tmp(function(tmpdir)
166 write_file("test-2.0-1.rockspec", [[
167 package = "test"
168 version = "2.0-1"
169 source = {
170 url = "file://]] .. tmpdir:gsub("\\", "/") .. [[/test.lua"
171 }
172 dependencies = {
173 "a_rock >= 0.8"
174 }
175 build = {
176 type = "builtin",
177 modules = {
178 test = "test.lua"
179 }
180 }
181 ]], finally)
182 write_file("test.lua", "return {}", finally)
183 write_file("luarocks.lock", [[
184 return {
185 dependencies = {
186 ["a_rock"] = "1.0-1",
187 }
188 }
189 ]], finally)
190
191 print(run.luarocks("make --server=" .. testing_paths.fixtures_dir .. "/a_repo --tree=lua_modules"))
192 assert.is.truthy(lfs.attributes("./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/test/2.0-1/test-2.0-1.rockspec"))
193 assert.is.truthy(lfs.attributes("./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/a_rock/1.0-1/a_rock-1.0-1.rockspec"))
194 local lockfilename = "./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/test/2.0-1/luarocks.lock"
195 assert.is.truthy(lfs.attributes(lockfilename))
196 local lockdata = loadfile(lockfilename)()
197 assert.same({
198 dependencies = {
199 ["a_rock"] = "1.0-1",
200 }
201 }, lockdata)
202 end)
203 end)
204
205 it("overrides luarocks.lock with --pin #pinning", function()
206 test_env.run_in_tmp(function(tmpdir)
207 write_file("test-2.0-1.rockspec", [[
208 package = "test"
209 version = "2.0-1"
210 source = {
211 url = "file://]] .. tmpdir:gsub("\\", "/") .. [[/test.lua"
212 }
213 dependencies = {
214 "a_rock >= 0.8"
215 }
216 build = {
217 type = "builtin",
218 modules = {
219 test = "test.lua"
220 }
221 }
222 ]], finally)
223 write_file("test.lua", "return {}", finally)
224 write_file("luarocks.lock", [[
225 return {
226 dependencies = {
227 ["a_rock"] = "1.0-1",
228 }
229 }
230 ]], finally)
231
232 print(run.luarocks("make --server=" .. testing_paths.fixtures_dir .. "/a_repo --tree=lua_modules --pin"))
233 assert.is.truthy(lfs.attributes("./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/test/2.0-1/test-2.0-1.rockspec"))
234 assert.is.truthy(lfs.attributes("./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/a_rock/2.0-1/a_rock-2.0-1.rockspec"))
235 local lockfilename = "./lua_modules/lib/luarocks/rocks-" .. test_env.lua_version .. "/test/2.0-1/luarocks.lock"
236 assert.is.truthy(lfs.attributes(lockfilename))
237 local lockdata = loadfile(lockfilename)()
238 assert.same({
239 dependencies = {
240 ["a_rock"] = "2.0-1",
241 ["lua"] = test_env.lua_version .. "-1",
242 }
243 }, lockdata)
244 end)
245 end)
246
128 describe("#ddt LuaRocks make upgrading rockspecs with double deploy types", function() 247 describe("#ddt LuaRocks make upgrading rockspecs with double deploy types", function()
129 local deploy_lib_dir = testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION 248 local deploy_lib_dir = testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION
130 local deploy_lua_dir = testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION 249 local deploy_lua_dir = testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION
diff --git a/src/luarocks/build.lua b/src/luarocks/build.lua
index 9aa70345..ca0237b7 100644
--- a/src/luarocks/build.lua
+++ b/src/luarocks/build.lua
@@ -11,6 +11,7 @@ local deps = require("luarocks.deps")
11local cfg = require("luarocks.core.cfg") 11local cfg = require("luarocks.core.cfg")
12local repos = require("luarocks.repos") 12local repos = require("luarocks.repos")
13local writer = require("luarocks.manif.writer") 13local writer = require("luarocks.manif.writer")
14local deplocks = require("luarocks.deplocks")
14 15
15build.opts = util.opts_table("build.opts", { 16build.opts = util.opts_table("build.opts", {
16 need_to_fetch = "boolean", 17 need_to_fetch = "boolean",
@@ -21,6 +22,7 @@ build.opts = util.opts_table("build.opts", {
21 branch = "string?", 22 branch = "string?",
22 verify = "boolean", 23 verify = "boolean",
23 check_lua_versions = "boolean", 24 check_lua_versions = "boolean",
25 pin = "boolean",
24}) 26})
25 27
26do 28do
@@ -125,6 +127,7 @@ local function process_dependencies(rockspec, opts)
125 if opts.deps_mode == "none" then 127 if opts.deps_mode == "none" then
126 return true 128 return true
127 end 129 end
130
128 if not opts.build_only_deps then 131 if not opts.build_only_deps then
129 if next(rockspec.build_dependencies) then 132 if next(rockspec.build_dependencies) then
130 local ok, err, errcode = deps.fulfill_dependencies(rockspec, "build_dependencies", opts.deps_mode, opts.verify) 133 local ok, err, errcode = deps.fulfill_dependencies(rockspec, "build_dependencies", opts.deps_mode, opts.verify)
@@ -326,6 +329,11 @@ local function write_rock_dir_files(rockspec, opts)
326 local name, version = rockspec.name, rockspec.version 329 local name, version = rockspec.name, rockspec.version
327 330
328 fs.copy(rockspec.local_abs_filename, path.rockspec_file(name, version), "read") 331 fs.copy(rockspec.local_abs_filename, path.rockspec_file(name, version), "read")
332
333 local deplock_file = deplocks.get_abs_filename(rockspec.name)
334 if deplock_file then
335 fs.copy(deplock_file, dir.path(path.install_dir(name, version), "luarocks.lock"), "read")
336 end
329 337
330 local ok, err = writer.make_rock_manifest(name, version) 338 local ok, err = writer.make_rock_manifest(name, version)
331 if not ok then return nil, err end 339 if not ok then return nil, err end
@@ -362,7 +370,14 @@ function build.build_rockspec(rockspec, opts)
362 end 370 end
363 end 371 end
364 372
365 local ok, err = process_dependencies(rockspec, opts) 373 local ok, err = fetch_and_change_to_source_dir(rockspec, opts)
374 if not ok then return nil, err end
375
376 if opts.pin then
377 deplocks.init(rockspec.name, ".")
378 end
379
380 ok, err = process_dependencies(rockspec, opts)
366 if not ok then return nil, err end 381 if not ok then return nil, err end
367 382
368 local name, version = rockspec.name, rockspec.version 383 local name, version = rockspec.name, rockspec.version
@@ -373,10 +388,6 @@ function build.build_rockspec(rockspec, opts)
373 if repos.is_installed(name, version) then 388 if repos.is_installed(name, version) then
374 repos.delete_version(name, version, opts.deps_mode) 389 repos.delete_version(name, version, opts.deps_mode)
375 end 390 end
376
377 ok, err = fetch_and_change_to_source_dir(rockspec, opts)
378 if not ok then return nil, err end
379
380 local dirs, err = prepare_install_dirs(name, version) 391 local dirs, err = prepare_install_dirs(name, version)
381 if not dirs then return nil, err end 392 if not dirs then return nil, err end
382 393
@@ -406,6 +417,10 @@ function build.build_rockspec(rockspec, opts)
406 fs.pop_dir() 417 fs.pop_dir()
407 end 418 end
408 419
420 if opts.pin then
421 deplocks.write_file()
422 end
423
409 ok, err = write_rock_dir_files(rockspec, opts) 424 ok, err = write_rock_dir_files(rockspec, opts)
410 if not ok then return nil, err end 425 if not ok then return nil, err end
411 426
diff --git a/src/luarocks/cmd/build.lua b/src/luarocks/cmd/build.lua
index 6b30666b..3bc1554e 100644
--- a/src/luarocks/cmd/build.lua
+++ b/src/luarocks/cmd/build.lua
@@ -19,6 +19,8 @@ local cmd = require("luarocks.cmd")
19 19
20function cmd_build.add_to_parser(parser) 20function cmd_build.add_to_parser(parser)
21 local cmd = parser:command("build", "Build and install a rock, compiling its C parts if any.\n".. 21 local cmd = parser:command("build", "Build and install a rock, compiling its C parts if any.\n"..
22 "If the sources contain a luarocks.lock file, uses it as an authoritative source for "..
23 "exact version of dependencies.\n"..
22 "If no arguments are given, behaves as luarocks make.", util.see_also()) 24 "If no arguments are given, behaves as luarocks make.", util.see_also())
23 :summary("Build/compile a rock.") 25 :summary("Build/compile a rock.")
24 26
@@ -33,6 +35,10 @@ function cmd_build.add_to_parser(parser)
33 "rockspec. Allows to specify a different branch to fetch. Particularly ".. 35 "rockspec. Allows to specify a different branch to fetch. Particularly "..
34 'for "dev" rocks.') 36 'for "dev" rocks.')
35 :argname("<name>") 37 :argname("<name>")
38 parser:flag("--pin", "Create a luarocks.lock file listing the exact "..
39 "versions of each dependency found for this rock (recursively), "..
40 "and store it in the rock's directory. "..
41 "Ignores any existing luarocks.lock file in the rock's sources.")
36 make.cmd_options(cmd) 42 make.cmd_options(cmd)
37end 43end
38 44
@@ -124,6 +130,7 @@ function cmd_build.command(args)
124 branch = args.branch, 130 branch = args.branch,
125 verify = not not args.verify, 131 verify = not not args.verify,
126 check_lua_versions = not not args.check_lua_versions, 132 check_lua_versions = not not args.check_lua_versions,
133 pin = not not args.pin,
127 }) 134 })
128 135
129 if args.sign and not args.pack_binary_rock then 136 if args.sign and not args.pack_binary_rock then
@@ -167,7 +174,9 @@ function cmd_build.command(args)
167 end 174 end
168 end 175 end
169 176
170 writer.check_dependencies(nil, deps.get_deps_mode(args)) 177 if opts.deps_mode ~= "none" then
178 writer.check_dependencies(nil, deps.get_deps_mode(args))
179 end
171 return name, version 180 return name, version
172end 181end
173 182
diff --git a/src/luarocks/cmd/install.lua b/src/luarocks/cmd/install.lua
index cd1df294..ad2a5ea7 100644
--- a/src/luarocks/cmd/install.lua
+++ b/src/luarocks/cmd/install.lua
@@ -114,7 +114,7 @@ function install.install_binary_rock(rock_file, opts)
114 end 114 end
115 115
116 if deps_mode ~= "none" then 116 if deps_mode ~= "none" then
117 ok, err, errcode = deps.fulfill_dependencies(rockspec, "dependencies", deps_mode, opts.verify) 117 ok, err, errcode = deps.fulfill_dependencies(rockspec, "dependencies", deps_mode, opts.verify, install_dir)
118 if err then return nil, err, errcode end 118 if err then return nil, err, errcode end
119 end 119 end
120 120
@@ -163,7 +163,7 @@ function install.install_binary_rock_deps(rock_file, opts)
163 return nil, "Failed loading rockspec for installed package: "..err, errcode 163 return nil, "Failed loading rockspec for installed package: "..err, errcode
164 end 164 end
165 165
166 ok, err, errcode = deps.fulfill_dependencies(rockspec, "dependencies", opts.deps_mode, opts.verify) 166 ok, err, errcode = deps.fulfill_dependencies(rockspec, "dependencies", opts.deps_mode, opts.verify, install_dir)
167 if err then return nil, err, errcode end 167 if err then return nil, err, errcode end
168 168
169 util.printout() 169 util.printout()
diff --git a/src/luarocks/cmd/make.lua b/src/luarocks/cmd/make.lua
index 74de6f26..a2ae4cd0 100644
--- a/src/luarocks/cmd/make.lua
+++ b/src/luarocks/cmd/make.lua
@@ -13,6 +13,7 @@ local fetch = require("luarocks.fetch")
13local pack = require("luarocks.pack") 13local pack = require("luarocks.pack")
14local remove = require("luarocks.remove") 14local remove = require("luarocks.remove")
15local deps = require("luarocks.deps") 15local deps = require("luarocks.deps")
16local deplocks = require("luarocks.deplocks")
16local writer = require("luarocks.manif.writer") 17local writer = require("luarocks.manif.writer")
17local cmd = require("luarocks.cmd") 18local cmd = require("luarocks.cmd")
18 19
@@ -37,6 +38,8 @@ function make.cmd_options(parser)
37 "signature file for the generated .rock file.") 38 "signature file for the generated .rock file.")
38 parser:flag("--check-lua-versions", "If the rock can't be found, check repository ".. 39 parser:flag("--check-lua-versions", "If the rock can't be found, check repository "..
39 "and report if it is available for another Lua version.") 40 "and report if it is available for another Lua version.")
41 parser:flag("--pin", "Pin the exact dependencies used for the rockspec"..
42 "being built into a luarocks.lock file in the current directory.")
40 util.deps_mode_option(parser) 43 util.deps_mode_option(parser)
41end 44end
42 45
@@ -54,6 +57,10 @@ This command is useful as a tool for debugging rockspecs.
54To install rocks, you'll normally want to use the "install" and "build" 57To install rocks, you'll normally want to use the "install" and "build"
55commands. See the help on those for details. 58commands. See the help on those for details.
56 59
60If the current directory contains a luarocks.lock file, it is used as the
61authoritative source for exact version of dependencies. The --pin flag
62overrides and recreates this file scanning dependency based on ranges.
63
57NB: Use `luarocks install` with the `--only-deps` flag if you want to install 64NB: Use `luarocks install` with the `--only-deps` flag if you want to install
58only dependencies of the rockspec (see `luarocks help install`). 65only dependencies of the rockspec (see `luarocks help install`).
59]], util.see_also()) 66]], util.see_also())
@@ -97,6 +104,7 @@ function make.command(args)
97 branch = args.branch, 104 branch = args.branch,
98 verify = not not args.verify, 105 verify = not not args.verify,
99 check_lua_versions = not not args.check_lua_versions, 106 check_lua_versions = not not args.check_lua_versions,
107 pin = not not args.pin
100 }) 108 })
101 109
102 if args.sign and not args.pack_binary_rock then 110 if args.sign and not args.pack_binary_rock then
diff --git a/src/luarocks/core/manif.lua b/src/luarocks/core/manif.lua
index 4fd35c6c..1e9da75e 100644
--- a/src/luarocks/core/manif.lua
+++ b/src/luarocks/core/manif.lua
@@ -102,7 +102,7 @@ function manif.scan_dependencies(name, version, tree_manifests, dest)
102 if entries then 102 if entries then
103 for ver, _ in util.sortedpairs(entries, vers.compare_versions) do 103 for ver, _ in util.sortedpairs(entries, vers.compare_versions) do
104 if (not constraints) or vers.match_constraints(vers.parse_version(ver), constraints) then 104 if (not constraints) or vers.match_constraints(vers.parse_version(ver), constraints) then
105 manif.scan_dependencies(pkg, version, tree_manifests, dest) 105 manif.scan_dependencies(pkg, ver, tree_manifests, dest)
106 end 106 end
107 end 107 end
108 end 108 end
diff --git a/src/luarocks/core/persist.lua b/src/luarocks/core/persist.lua
index 48979184..59089818 100644
--- a/src/luarocks/core/persist.lua
+++ b/src/luarocks/core/persist.lua
@@ -10,7 +10,7 @@ local require = nil
10-- @return (true, any) or (nil, string, string): true and the return value 10-- @return (true, any) or (nil, string, string): true and the return value
11-- of the file, or nil, an error message and an error code ("open", "load" 11-- of the file, or nil, an error message and an error code ("open", "load"
12-- or "run") in case of errors. 12-- or "run") in case of errors.
13local function run_file(filename, env) 13function persist.run_file(filename, env)
14 local fd, err = io.open(filename) 14 local fd, err = io.open(filename)
15 if not fd then 15 if not fd then
16 return nil, err, "open" 16 return nil, err, "open"
@@ -67,7 +67,7 @@ function persist.load_into_table(filename, tbl)
67 local save_mt = getmetatable(result) 67 local save_mt = getmetatable(result)
68 setmetatable(result, globals_mt) 68 setmetatable(result, globals_mt)
69 69
70 local ok, err, errcode = run_file(filename, result) 70 local ok, err, errcode = persist.run_file(filename, result)
71 71
72 setmetatable(result, save_mt) 72 setmetatable(result, save_mt)
73 73
diff --git a/src/luarocks/deplocks.lua b/src/luarocks/deplocks.lua
new file mode 100644
index 00000000..f6449986
--- /dev/null
+++ b/src/luarocks/deplocks.lua
@@ -0,0 +1,106 @@
1local deplocks = {}
2
3local fs = require("luarocks.fs")
4local dir = require("luarocks.dir")
5local util = require("luarocks.util")
6local persist = require("luarocks.persist")
7
8local deptable = {}
9local deptable_mode = "start"
10local deplock_abs_filename
11local deplock_root_rock_name
12
13function deplocks.init(root_rock_name, dirname)
14 if deptable_mode ~= "start" then
15 return
16 end
17 deptable_mode = "create"
18
19 local filename = dir.path(dirname, "luarocks.lock")
20 deplock_abs_filename = fs.absolute_name(filename)
21 deplock_root_rock_name = root_rock_name
22
23 deptable = {}
24end
25
26function deplocks.get_abs_filename(root_rock_name)
27 if root_rock_name == deplock_root_rock_name then
28 return deplock_abs_filename
29 end
30end
31
32function deplocks.load(root_rock_name, dirname)
33 if deptable_mode ~= "start" then
34 return true, nil
35 end
36 deptable_mode = "locked"
37
38 local filename = dir.path(dirname, "luarocks.lock")
39 local ok, result, errcode = persist.run_file(filename, {})
40 if errcode == "load" or errcode == "run" then
41 -- bad config file or depends on env, so error out
42 return nil, nil, "Could not read existing lockfile " .. filename
43 end
44
45 if errcode == "open" then
46 -- could not open, maybe file does not exist
47 return true, nil
48 end
49
50 deplock_abs_filename = fs.absolute_name(filename)
51 deplock_root_rock_name = root_rock_name
52
53 deptable = result
54 return true, filename
55end
56
57function deplocks.add(depskey, name, version)
58 if deptable_mode == "locked" then
59 return
60 end
61
62 local dk = deptable[depskey]
63 if not dk then
64 dk = {}
65 deptable[depskey] = dk
66 end
67
68 if not dk[name] then
69 dk[name] = version
70 end
71end
72
73function deplocks.get(depskey, name)
74 local dk = deptable[depskey]
75 if not dk then
76 return nil
77 end
78
79 return deptable[name]
80end
81
82function deplocks.write_file()
83 if deptable_mode ~= "create" then
84 return true
85 end
86
87 return persist.save_as_module(deplock_abs_filename, deptable)
88end
89
90-- a table-like interface to deplocks
91function deplocks.proxy(depskey)
92 return setmetatable({}, {
93 __index = function(_, k)
94 return deplocks.get(depskey, k)
95 end,
96 __newindex = function(_, k, v)
97 return deplocks.add(depskey, k, v)
98 end,
99 })
100end
101
102function deplocks.each(depskey)
103 return util.sortedpairs(deptable[depskey] or {})
104end
105
106return deplocks
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)
diff --git a/src/luarocks/persist.lua b/src/luarocks/persist.lua
index 1c4b82b5..b21323ce 100644
--- a/src/luarocks/persist.lua
+++ b/src/luarocks/persist.lua
@@ -8,6 +8,7 @@ local util = require("luarocks.util")
8local dir = require("luarocks.dir") 8local dir = require("luarocks.dir")
9local fs = require("luarocks.fs") 9local fs = require("luarocks.fs")
10 10
11persist.run_file = core.run_file
11persist.load_into_table = core.load_into_table 12persist.load_into_table = core.load_into_table
12 13
13local write_table 14local write_table
@@ -200,7 +201,7 @@ function persist.save_from_table(filename, tbl, field_order)
200end 201end
201 202
202--- Save the contents of a table as a module. 203--- Save the contents of a table as a module.
203-- Each element of the table is saved as a global assignment. 204-- The module contains a 'return' statement that returns the table.
204-- Only numbers, strings and tables (containing numbers, strings 205-- Only numbers, strings and tables (containing numbers, strings
205-- or other recursively processed tables) are supported. 206-- or other recursively processed tables) are supported.
206-- @param filename string: the output filename 207-- @param filename string: the output filename
diff --git a/src/luarocks/test/busted.lua b/src/luarocks/test/busted.lua
index 618054c7..8fa78804 100644
--- a/src/luarocks/test/busted.lua
+++ b/src/luarocks/test/busted.lua
@@ -21,7 +21,7 @@ function busted.run_tests(test, args)
21 test = {} 21 test = {}
22 end 22 end
23 23
24 local ok, bustedver, where = deps.fulfill_dependency(queries.new("busted")) 24 local ok, bustedver, where = deps.fulfill_dependency(queries.new("busted"), nil, nil, nil, "test_dependencies")
25 if not ok then 25 if not ok then
26 return nil, bustedver 26 return nil, bustedver
27 end 27 end