aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHisham Muhammad <hisham@gobolinux.org>2016-11-03 18:16:02 -0200
committerGitHub <noreply@github.com>2016-11-03 18:16:02 -0200
commitbfab09559cdbbc7e163490129ac7d3c4060b905d (patch)
treeb2cb4e9eda30c0ccabad972393dd4435e6ed9573
parent9822682e68b252b049e2fa6ef9d052d7ac739378 (diff)
parentcee2d3abfc1c06f382787d7bf7d2110a52ca0e76 (diff)
downloadluarocks-bfab09559cdbbc7e163490129ac7d3c4060b905d.tar.gz
luarocks-bfab09559cdbbc7e163490129ac7d3c4060b905d.tar.bz2
luarocks-bfab09559cdbbc7e163490129ac7d3c4060b905d.zip
Merge pull request #644 from mpeterv/fix-conflict-resolve
Fix conflict resolution on deploy/remove w.r.t. C/Lua modules with same name
-rw-r--r--spec/make_spec.lua69
-rw-r--r--src/luarocks/manif.lua132
-rw-r--r--src/luarocks/repos.lua256
-rw-r--r--test/test_environment.lua10
-rw-r--r--test/testfiles/mixed_deploy_type/mdt.c6
-rw-r--r--test/testfiles/mixed_deploy_type/mdt.lua1
-rw-r--r--test/testfiles/mixed_deploy_type/mdt_file1
-rw-r--r--test/testfiles/mixed_deploy_type/mixed_deploy_type-0.1.0-1.rockspec21
-rw-r--r--test/testfiles/mixed_deploy_type/mixed_deploy_type-0.2.0-1.rockspec21
9 files changed, 328 insertions, 189 deletions
diff --git a/spec/make_spec.lua b/spec/make_spec.lua
index ae79a29c..89036d26 100644
--- a/spec/make_spec.lua
+++ b/spec/make_spec.lua
@@ -2,11 +2,11 @@ local test_env = require("test/test_environment")
2local lfs = require("lfs") 2local 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
5 6
6test_env.unload_luarocks() 7test_env.unload_luarocks()
7 8
8local extra_rocks = { 9local extra_rocks = {
9 "lpeg-1.0.0-1.rockspec",
10 "/luasocket-3.0rc1-2.src.rock", 10 "/luasocket-3.0rc1-2.src.rock",
11 "/luasocket-3.0rc1-2.rockspec", 11 "/luasocket-3.0rc1-2.rockspec",
12 "/lxsh-0.8.6-2.src.rock", 12 "/lxsh-0.8.6-2.src.rock",
@@ -98,4 +98,71 @@ describe("LuaRocks make tests #blackbox #b_make", function()
98 assert.is.truthy(lfs.attributes("lxsh-0.8.6-2.all.rock")) 98 assert.is.truthy(lfs.attributes("lxsh-0.8.6-2.all.rock"))
99 end) 99 end)
100 end) 100 end)
101
102 describe("LuaRocks make upgrading rockspecs with mixed deploy types", function()
103 setup(function()
104 test_env.copy_dir(testing_paths.testing_dir .. "/testfiles/mixed_deploy_type", "mdt")
105 end)
106
107 teardown(function()
108 test_env.remove_dir("mdt")
109 os.remove("mdt."..test_env.lib_extension)
110 end)
111
112 it("modules with same name from lua/ and lib/ when upgrading", function()
113 assert.is_true(run.luarocks_bool("make mdt/mixed_deploy_type-0.1.0-1.rockspec"))
114 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mdt.lua"))
115 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mdt_file"))
116
117 assert.is_true(run.luarocks_bool("make mdt/mixed_deploy_type-0.2.0-1.rockspec"))
118 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION.."/mdt."..test_env.lib_extension))
119 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION.."/mdt_file"))
120 assert.is.falsy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mdt.lua"))
121 assert.is.falsy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mdt_file"))
122 assert.is.falsy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mixed_deploy_type_0_1_0_1-mdt.lua"))
123 assert.is.falsy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mixed_deploy_type_0_1_0_1-mdt_file"))
124 end)
125
126 it("modules with same name from lua/ and lib/ when upgrading with --keep", function()
127 assert.is_true(run.luarocks_bool("make mdt/mixed_deploy_type-0.1.0-1.rockspec"))
128 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mdt.lua"))
129 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mdt_file"))
130
131 assert.is_true(run.luarocks_bool("make mdt/mixed_deploy_type-0.2.0-1.rockspec --keep"))
132 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION.."/mdt."..test_env.lib_extension))
133 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION.."/mdt_file"))
134 assert.is.falsy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mdt.lua"))
135 assert.is.falsy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mdt_file"))
136 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mixed_deploy_type_0_1_0_1-mdt.lua"))
137 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mixed_deploy_type_0_1_0_1-mdt_file"))
138 end)
139
140 it("modules with same name from lua/ and lib/ when downgrading", function()
141 assert.is_true(run.luarocks_bool("make mdt/mixed_deploy_type-0.2.0-1.rockspec"))
142 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION.."/mdt."..test_env.lib_extension))
143 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION.."/mdt_file"))
144
145 assert.is_true(run.luarocks_bool("make mdt/mixed_deploy_type-0.1.0-1.rockspec"))
146 assert.is.falsy(lfs.attributes(testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION.."/mdt."..test_env.lib_extension))
147 assert.is.falsy(lfs.attributes(testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION.."/mdt_file"))
148 assert.is.falsy(lfs.attributes(testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION.."/mixed_deploy_type_0_1_0_1-mdt."..test_env.lib_extension))
149 assert.is.falsy(lfs.attributes(testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION.."/mixed_deploy_type_0_1_0_1-mdt_file"))
150 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mdt.lua"))
151 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mdt_file"))
152 end)
153
154 it("modules with same name from lua/ and lib/ when downgrading with --keep", function()
155 assert.is_true(run.luarocks_bool("make mdt/mixed_deploy_type-0.2.0-1.rockspec"))
156 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION.."/mdt."..test_env.lib_extension))
157 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION.."/mdt_file"))
158
159 assert.is_true(run.luarocks_bool("make mdt/mixed_deploy_type-0.1.0-1.rockspec --keep"))
160 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION.."/mdt."..test_env.lib_extension))
161 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/lib/lua/"..env_variables.LUA_VERSION.."/mdt_file"))
162 assert.is.falsy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mdt.lua"))
163 assert.is.falsy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mdt_file"))
164 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mixed_deploy_type_0_1_0_1-mdt.lua"))
165 assert.is.truthy(lfs.attributes(testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION.."/mixed_deploy_type_0_1_0_1-mdt_file"))
166 end)
167 end)
101end) 168end)
diff --git a/src/luarocks/manif.lua b/src/luarocks/manif.lua
index 86209d06..88a13f10 100644
--- a/src/luarocks/manif.lua
+++ b/src/luarocks/manif.lua
@@ -540,85 +540,89 @@ function manif.zip_manifests()
540 end 540 end
541end 541end
542 542
543local function relative_path(from_dir, to_file) 543--- Get type and name of an item (a module or a command) provided by a file.
544 -- It is assumed that `from_dir` is prefix of `to_file`. 544-- @param deploy_type string: rock manifest subtree the file comes from ("bin", "lua", or "lib").
545 return (to_file:sub(#from_dir + 1):gsub("^[\\/]*", "")) 545-- @param file_path string: path to the file relatively to deploy_type subdirectory.
546-- @return (string, string): item type ("module" or "command") and name.
547function manif.get_provided_item(deploy_type, file_path)
548 assert(type(deploy_type) == "string")
549 assert(type(file_path) == "string")
550 local item_type = deploy_type == "bin" and "command" or "module"
551 local item_name = item_type == "command" and file_path or path.path_to_module(file_path)
552 return item_type, item_name
546end 553end
547 554
548local function file_manifest_coordinates(manifest, file, root) 555local function get_providers(item_type, item_name, repo)
549 local deploy_bin = path.deploy_bin_dir(root) 556 assert(type(item_type) == "string")
550 local deploy_lua = path.deploy_lua_dir(root) 557 assert(type(item_name) == "string")
551 local deploy_lib = path.deploy_lib_dir(root) 558 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
552 559 local manifest = manif_core.load_local_manifest(rocks_dir)
553 if util.starts_with(file, deploy_lua) then 560 return manifest and manifest[item_type .. "s"][item_name]
554 return "modules", path.path_to_module(relative_path(deploy_lua, file):gsub("\\", "/")), deploy_lua
555 elseif util.starts_with(file, deploy_lib) then
556 return "modules", path.path_to_module(relative_path(deploy_lib, file):gsub("\\", "/")), deploy_lib
557 elseif util.starts_with(file, deploy_bin) then
558 return "commands", relative_path(deploy_bin, file), deploy_bin
559 else
560 assert(false, "Assertion failed: '"..file.."' is not a deployed file.")
561 end
562end 561end
563 562
564local function find_providers(file, root) 563--- Given a name of a module or a command, figure out which rock name and version
565 assert(type(file) == "string") 564-- correspond to it in the rock tree manifest.
566 root = root or cfg.root_dir 565-- @param item_type string: "module" or "command".
567 566-- @param item_name string: module or command name.
568 local manifest, err = manif_core.load_local_manifest(path.rocks_dir(root)) 567-- @param root string or nil: A local root dir for a rocks tree. If not given, the default is used.
569 if not manifest then 568-- @return (string, string) or nil: name and version of the provider rock or nil if there
570 return nil, "untracked" 569-- is no provider.
570function manif.get_current_provider(item_type, item_name, repo)
571 local providers = get_providers(item_type, item_name, repo)
572 if providers then
573 return providers[1]:match("([^/]*)/([^/]*)")
571 end 574 end
575end
572 576
573 local type_key, key = file_manifest_coordinates(manifest, file, root) 577function manif.get_next_provider(item_type, item_name, repo)
574 578 local providers = get_providers(item_type, item_name, repo)
575 local providers = manifest[type_key][key] 579 if providers and providers[2] then
576 if not providers then 580 return providers[2]:match("([^/]*)/([^/]*)")
577 return nil, "untracked"
578 end 581 end
579 return providers
580end 582end
581 583
582--- Given a path of a deployed file, figure out which rock name and version 584--- Given a name of a module or a command provided by a package, figure out
583-- correspond to it in the tree manifest. 585-- which file provides it.
584-- @param file string: The full path of a deployed file. 586-- @param name string: package name.
587-- @param version string: package version.
588-- @param item_type string: "module" or "command".
589-- @param item_name string: module or command name.
585-- @param root string or nil: A local root dir for a rocks tree. If not given, the default is used. 590-- @param root string or nil: A local root dir for a rocks tree. If not given, the default is used.
586-- @return string, string: name and version of the provider rock. 591-- @return (string, string): rock manifest subtree the file comes from ("bin", "lua", or "lib")
587function manif.find_current_provider(file, root) 592-- and path to the providing file relatively to that subtree.
588 local providers, err = find_providers(file, root) 593function manif.get_providing_file(name, version, item_type, item_name, repo)
589 if not providers then return nil, err end 594 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
590 return providers[1]:match("([^/]*)/([^/]*)") 595 local manifest = manif_core.load_local_manifest(rocks_dir)
591end
592 596
593function manif.find_next_provider(file, root) 597 local entry_table = manifest.repository[name][version][1]
594 local providers, err = find_providers(file, root) 598 local file_path = entry_table[item_type .. "s"][item_name]
595 if not providers then return nil, err end 599
596 if providers[2] then 600 if item_type == "command" then
597 return providers[2]:match("([^/]*)/([^/]*)") 601 return "bin", file_path
598 else
599 return nil
600 end 602 end
601end
602 603
603--- Given a file conflicting with a module or command 604 -- A module can be in "lua" or "lib". Decide based on extension first:
604-- provided by a version of a package, return which file 605 -- most likely Lua modules are in "lua/" and C modules are in "lib/".
605-- in that version corresponds to the conflicting item. 606 if file_path:match("%." .. cfg.lua_extension .. "$") then
606-- @param name string: name of the package with conflicting module or command. 607 return "lua", file_path
607-- @param version string: version of the package with conflicting module or command. 608 elseif file_path:match("%." .. cfg.lib_extension .. "$") then
608-- @param file string: full, unversioned path to a deployed file. 609 return "lib", file_path
609-- @return string: full, unversioned path to a deployed file in
610-- given package that conflicts with given file.
611function manif.find_conflicting_file(name, version, file, root)
612 root = root or cfg.root_dir
613
614 local manifest = manif_core.load_local_manifest(path.rocks_dir(root))
615 if not manifest then
616 return
617 end 610 end
618 611
619 local entry_table = manifest.repository[name][version][1] 612 -- Fallback to rock manifest scanning.
620 local type_key, key, deploy_dir = file_manifest_coordinates(manifest, file, root) 613 local rock_manifest = manif.load_rock_manifest(name, version)
621 return dir.path(deploy_dir, entry_table[type_key][key]) 614 local subtree = rock_manifest.lib
615
616 for path_part in file_path:gmatch("[^/]+") do
617 if type(subtree) == "table" then
618 subtree = subtree[path_part]
619 else
620 -- Assume it's in "lua/" if it's not in "lib/".
621 return "lua", file_path
622 end
623 end
624
625 return type(subtree) == "string" and "lib" or "lua", file_path
622end 626end
623 627
624return manif 628return manif
diff --git a/src/luarocks/repos.lua b/src/luarocks/repos.lua
index d4d9694e..5d5eac70 100644
--- a/src/luarocks/repos.lua
+++ b/src/luarocks/repos.lua
@@ -11,6 +11,23 @@ local dir = require("luarocks.dir")
11local manif = require("luarocks.manif") 11local manif = require("luarocks.manif")
12local deps = require("luarocks.deps") 12local deps = require("luarocks.deps")
13 13
14-- Tree of files installed by a package are stored
15-- in its rock manifest. Some of these files have to
16-- be deployed to locations where Lua can load them as
17-- modules or where they can be used as commands.
18-- These files are characterised by pair
19-- (deploy_type, file_path), where deploy_type is the first
20-- component of the file path and file_path is the rest of the
21-- path. Only files with deploy_type in {"lua", "lib", "bin"}
22-- are deployed somewhere.
23-- Each deployed file provides an "item". An item is
24-- characterised by pair (item_type, item_name).
25-- item_type is "command" for files with deploy_type
26-- "bin" and "module" for deploy_type in {"lua", "lib"}.
27-- item_name is same as file_path for commands
28-- and is produced using path.path_to_module(file_path)
29-- for modules.
30
14--- Get all installed versions of a package. 31--- Get all installed versions of a package.
15-- @param name string: a package name. 32-- @param name string: a package name.
16-- @return table or nil: An array of strings listing installed 33-- @return table or nil: An array of strings listing installed
@@ -192,44 +209,56 @@ end
192local function delete_suffixed(file, suffix) 209local function delete_suffixed(file, suffix)
193 local suffixed_file, err = find_suffixed(file, suffix) 210 local suffixed_file, err = find_suffixed(file, suffix)
194 if not suffixed_file then 211 if not suffixed_file then
195 return nil, "Could not remove " .. file .. ": " .. err, "not found" 212 return nil, "Could not remove " .. file .. ": " .. err
196 end 213 end
197 214
198 fs.delete(suffixed_file) 215 fs.delete(suffixed_file)
199 if fs.exists(suffixed_file) then 216 if fs.exists(suffixed_file) then
200 return nil, "Failed deleting " .. suffixed_file .. ": file still exists", "fail" 217 return nil, "Failed deleting " .. suffixed_file .. ": file still exists"
201 end 218 end
202 219
203 return true 220 return true
204end 221end
205 222
206local function resolve_conflict(target, deploy_dir, name, version, cur_name, cur_version, suffix) 223-- Files can be deployed using versioned and non-versioned names.
207 if name < cur_name or (name == cur_name and deps.compare_versions(version, cur_version)) then 224-- Several items with same type and name can exist if they are
225-- provided by different packages or versions. In any case
226-- item from the newest version of lexicographically smallest package
227-- is deployed using non-versioned name and others use versioned names.
228
229local function get_deploy_paths(name, version, deploy_type, file_path)
230 local deploy_dir = cfg["deploy_" .. deploy_type .. "_dir"]
231 local non_versioned = dir.path(deploy_dir, file_path)
232 local versioned = path.versioned_name(non_versioned, deploy_dir, name, version)
233 return non_versioned, versioned
234end
235
236local function prepare_target(name, version, deploy_type, file_path, suffix)
237 local non_versioned, versioned = get_deploy_paths(name, version, deploy_type, file_path)
238 local item_type, item_name = manif.get_provided_item(deploy_type, file_path)
239 local cur_name, cur_version = manif.get_current_provider(item_type, item_name)
240
241 if not cur_name then
242 return non_versioned
243 elseif name < cur_name or (name == cur_name and deps.compare_versions(version, cur_version)) then
208 -- New version has priority. Move currently provided version back using versioned name. 244 -- New version has priority. Move currently provided version back using versioned name.
209 local cur_target = manif.find_conflicting_file(cur_name, cur_version, target) 245 local cur_deploy_type, cur_file_path = manif.get_providing_file(cur_name, cur_version, item_type, item_name)
210 local versioned = path.versioned_name(cur_target, deploy_dir, cur_name, cur_version) 246 local cur_non_versioned, cur_versioned = get_deploy_paths(cur_name, cur_version, cur_deploy_type, cur_file_path)
211 247
212 local ok, err = fs.make_dir(dir.dir_name(versioned)) 248 local dir_ok, dir_err = fs.make_dir(dir.dir_name(cur_versioned))
213 if not ok then 249 if not dir_ok then return nil, dir_err end
214 return nil, err
215 end
216 250
217 ok, err = move_suffixed(cur_target, versioned, suffix) 251 local move_ok, move_err = move_suffixed(cur_non_versioned, cur_versioned, suffix)
218 if not ok then 252 if not move_ok then return nil, move_err end
219 return nil, err
220 end
221 253
222 return target 254 return non_versioned
223 else 255 else
224 -- Current version has priority, deploy new version using versioned name. 256 -- Current version has priority, deploy new version using versioned name.
225 return path.versioned_name(target, deploy_dir, name, version) 257 return versioned
226 end 258 end
227end 259end
228 260
229--- Deploy a package from the rocks subdirectory. 261--- Deploy a package from the rocks subdirectory.
230-- It is maintained that for each module and command the one that is provided
231-- by the newest version of the lexicographically smallest package
232-- is installed using unversioned name, and other versions use versioned names.
233-- @param name string: name of package 262-- @param name string: name of package
234-- @param version string: exact package version in string format 263-- @param version string: exact package version in string format
235-- @param wrap_bin_scripts bool: whether commands written in Lua should be wrapped. 264-- @param wrap_bin_scripts bool: whether commands written in Lua should be wrapped.
@@ -241,49 +270,44 @@ function repos.deploy_files(name, version, wrap_bin_scripts, deps_mode)
241 assert(type(version) == "string") 270 assert(type(version) == "string")
242 assert(type(wrap_bin_scripts) == "boolean") 271 assert(type(wrap_bin_scripts) == "boolean")
243 272
244 local function deploy_file_tree(file_tree, path_fn, deploy_dir, move_fn, suffix) 273 local rock_manifest = manif.load_rock_manifest(name, version)
245 local source_dir = path_fn(name, version)
246 return recurse_rock_manifest_tree(file_tree,
247 function(parent_path, parent_module, file)
248 local source = dir.path(source_dir, parent_path, file)
249 local target = dir.path(deploy_dir, parent_path, file)
250
251 local cur_name, cur_version = manif.find_current_provider(target)
252 if cur_name then
253 local resolve_err
254 target, resolve_err = resolve_conflict(target, deploy_dir, name, version, cur_name, cur_version, suffix)
255 if not target then
256 return nil, resolve_err
257 end
258 end
259 274
260 local ok, err = fs.make_dir(dir.dir_name(target)) 275 local function deploy_file_tree(deploy_type, source_dir, move_fn, suffix)
261 if not ok then return nil, err end 276 if not rock_manifest[deploy_type] then
277 return true
278 end
262 279
263 local suffixed_target, mover = move_fn(source, target, name, version) 280 return recurse_rock_manifest_tree(rock_manifest[deploy_type], function(parent_path, parent_module, file)
264 if fs.exists(suffixed_target) then 281 local file_path = parent_path .. file
265 local backup = suffixed_target 282 local source = dir.path(source_dir, file_path)
266 repeat
267 backup = backup.."~"
268 until not fs.exists(backup) -- Slight race condition here, but shouldn't be a problem.
269
270 util.printerr("Warning: "..suffixed_target.." is not tracked by this installation of LuaRocks. Moving it to "..backup)
271 local ok, err = fs.move(suffixed_target, backup)
272 if not ok then
273 return nil, err
274 end
275 end
276 283
277 ok, err = mover() 284 local target, prepare_err = prepare_target(name, version, deploy_type, file_path, suffix)
278 fs.remove_dir_tree_if_empty(dir.dir_name(source)) 285 if not target then return nil, prepare_err end
279 return ok, err 286
287 local dir_ok, dir_err = fs.make_dir(dir.dir_name(target))
288 if not dir_ok then return nil, dir_err end
289
290 local suffixed_target, mover = move_fn(source, target)
291 if fs.exists(suffixed_target) then
292 local backup = suffixed_target
293 repeat
294 backup = backup.."~"
295 until not fs.exists(backup) -- Slight race condition here, but shouldn't be a problem.
296
297 util.printerr("Warning: "..suffixed_target.." is not tracked by this installation of LuaRocks. Moving it to "..backup)
298 local move_ok, move_err = fs.move(suffixed_target, backup)
299 if not move_ok then return nil, move_err end
280 end 300 end
281 )
282 end
283 301
284 local rock_manifest = manif.load_rock_manifest(name, version) 302 local move_ok, move_err = mover()
303 if not move_ok then return nil, move_err end
304
305 fs.remove_dir_tree_if_empty(dir.dir_name(source))
306 return true
307 end)
308 end
285 309
286 local function install_binary(source, target, name, version) 310 local function install_binary(source, target)
287 if wrap_bin_scripts and fs.is_lua(source) then 311 if wrap_bin_scripts and fs.is_lua(source) then
288 return target .. (cfg.wrapper_suffix or ""), function() return fs.wrap_script(source, target, name, version) end 312 return target .. (cfg.wrapper_suffix or ""), function() return fs.wrap_script(source, target, name, version) end
289 else 313 else
@@ -297,28 +321,19 @@ function repos.deploy_files(name, version, wrap_bin_scripts, deps_mode)
297 end 321 end
298 end 322 end
299 323
300 local ok, err = true 324 local ok, err = deploy_file_tree("bin", path.bin_dir(name, version), install_binary, cfg.wrapper_suffix)
301 if rock_manifest.bin then 325 if not ok then return nil, err end
302 ok, err = deploy_file_tree(rock_manifest.bin, path.bin_dir, cfg.deploy_bin_dir, install_binary, cfg.wrapper_suffix)
303 end
304 if ok and rock_manifest.lua then
305 ok, err = deploy_file_tree(rock_manifest.lua, path.lua_dir, cfg.deploy_lua_dir, make_mover(cfg.perm_read))
306 end
307 if ok and rock_manifest.lib then
308 ok, err = deploy_file_tree(rock_manifest.lib, path.lib_dir, cfg.deploy_lib_dir, make_mover(cfg.perm_exec))
309 end
310 326
311 if not ok then 327 ok, err = deploy_file_tree("lua", path.lua_dir(name, version), make_mover(cfg.perm_read))
312 return nil, err 328 if not ok then return nil, err end
313 end 329
330 ok, err = deploy_file_tree("lib", path.lib_dir(name, version), make_mover(cfg.perm_exec))
331 if not ok then return nil, err end
314 332
315 return manif.add_to_manifest(name, version, nil, deps_mode) 333 return manif.add_to_manifest(name, version, nil, deps_mode)
316end 334end
317 335
318--- Delete a package from the local repository. 336--- Delete a package from the local repository.
319-- It is maintained that for each module and command the one that is provided
320-- by the newest version of the lexicographically smallest package
321-- is installed using unversioned name, and other versions use versioned names.
322-- @param name string: name of package 337-- @param name string: name of package
323-- @param version string: exact package version in string format 338-- @param version string: exact package version in string format
324-- @param deps_mode: string: Which trees to check dependencies for: 339-- @param deps_mode: string: Which trees to check dependencies for:
@@ -333,67 +348,70 @@ function repos.delete_version(name, version, deps_mode, quick)
333 assert(type(version) == "string") 348 assert(type(version) == "string")
334 assert(type(deps_mode) == "string") 349 assert(type(deps_mode) == "string")
335 350
336 local function delete_deployed_file_tree(file_tree, deploy_dir, suffix) 351 local rock_manifest = manif.load_rock_manifest(name, version)
337 return recurse_rock_manifest_tree(file_tree, 352 if not rock_manifest then
338 function(parent_path, parent_module, file) 353 return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?"
339 local target = dir.path(deploy_dir, parent_path, file) 354 end
340 local versioned = path.versioned_name(target, deploy_dir, name, version)
341
342 local ok, err, err_type = delete_suffixed(versioned, suffix)
343 if ok then
344 fs.remove_dir_tree_if_empty(dir.dir_name(versioned))
345 return true
346 elseif err_type == "fail" then
347 return nil, err
348 end
349 355
350 ok, err = delete_suffixed(target, suffix) 356 local function delete_deployed_file_tree(deploy_type, suffix)
351 if not ok then 357 if not rock_manifest[deploy_type] then
352 return nil, err 358 return true
353 end 359 end
360
361 return recurse_rock_manifest_tree(rock_manifest[deploy_type], function(parent_path, parent_module, file)
362 local file_path = parent_path .. file
363 local non_versioned, versioned = get_deploy_paths(name, version, deploy_type, file_path)
364
365 -- Figure out if the file is deployed using versioned or non-versioned name.
366 local target
367 local item_type, item_name = manif.get_provided_item(deploy_type, file_path)
368 local cur_name, cur_version = manif.get_current_provider(item_type, item_name)
354 369
355 if not quick then 370 if cur_name == name and cur_version == version then
356 local next_name, next_version = manif.find_next_provider(target) 371 -- This package has highest priority, should be in non-versioned location.
357 if next_name then 372 target = non_versioned
358 local next_target = manif.find_conflicting_file(next_name, next_version, target) 373 else
359 local next_versioned = path.versioned_name(next_target, deploy_dir, next_name, next_version) 374 target = versioned
375 end
376
377 local ok, err = delete_suffixed(target, suffix)
378 if not ok then return nil, err end
379
380 if not quick and target == non_versioned then
381 -- If another package provides this file, move its version
382 -- into non-versioned location instead.
383 local next_name, next_version = manif.get_next_provider(item_type, item_name)
360 384
361 ok, err = move_suffixed(next_versioned, next_target, suffix) 385 if next_name then
362 if not ok then 386 local next_deploy_type, next_file_path = manif.get_providing_file(next_name, next_version, item_type, item_name)
363 return nil, err 387 local next_non_versioned, next_versioned = get_deploy_paths(next_name, next_version, next_deploy_type, next_file_path)
364 end
365 388
366 fs.remove_dir_tree_if_empty(dir.dir_name(versioned)) 389 local move_ok, move_err = move_suffixed(next_versioned, next_non_versioned, suffix)
367 end 390 if not move_ok then return nil, move_err end
391
392 fs.remove_dir_tree_if_empty(dir.dir_name(next_versioned))
368 end 393 end
369 fs.remove_dir_tree_if_empty(dir.dir_name(target))
370 return true
371 end 394 end
372 )
373 end
374 395
375 local rock_manifest = manif.load_rock_manifest(name, version) 396 fs.remove_dir_tree_if_empty(dir.dir_name(target))
376 if not rock_manifest then 397 return true
377 return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?" 398 end)
378 end
379
380 local ok, err = true
381 if rock_manifest.bin then
382 ok, err = delete_deployed_file_tree(rock_manifest.bin, cfg.deploy_bin_dir, cfg.wrapper_suffix)
383 end
384 if ok and rock_manifest.lua then
385 ok, err = delete_deployed_file_tree(rock_manifest.lua, cfg.deploy_lua_dir)
386 end
387 if ok and rock_manifest.lib then
388 ok, err = delete_deployed_file_tree(rock_manifest.lib, cfg.deploy_lib_dir)
389 end 399 end
400
401 local ok, err = delete_deployed_file_tree("bin", cfg.wrapper_suffix)
402 if not ok then return nil, err end
403
404 ok, err = delete_deployed_file_tree("lua")
405 if not ok then return nil, err end
406
407 ok, err = delete_deployed_file_tree("lib")
390 if not ok then return nil, err end 408 if not ok then return nil, err end
391 409
392 fs.delete(path.install_dir(name, version)) 410 fs.delete(path.install_dir(name, version))
393 if not get_installed_versions(name) then 411 if not get_installed_versions(name) then
394 fs.delete(dir.path(cfg.rocks_dir, name)) 412 fs.delete(dir.path(cfg.rocks_dir, name))
395 end 413 end
396 414
397 if quick then 415 if quick then
398 return true 416 return true
399 end 417 end
diff --git a/test/test_environment.lua b/test/test_environment.lua
index 196202df..6f6f1cfe 100644
--- a/test/test_environment.lua
+++ b/test/test_environment.lua
@@ -214,7 +214,7 @@ function test_env.set_args()
214 return true 214 return true
215end 215end
216 216
217local function copy_dir(source_path, target_path) 217function test_env.copy_dir(source_path, target_path)
218 local testing_paths = test_env.testing_paths 218 local testing_paths = test_env.testing_paths
219 if test_env.TEST_TARGET_OS == "windows" then 219 if test_env.TEST_TARGET_OS == "windows" then
220 execute_bool(testing_paths.win_tools .. "/cp -R ".. source_path .. "/. " .. target_path) 220 execute_bool(testing_paths.win_tools .. "/cp -R ".. source_path .. "/. " .. target_path)
@@ -430,8 +430,8 @@ local function build_environment(rocks, env_variables)
430 end 430 end
431 end 431 end
432 432
433 copy_dir(testing_paths.testing_tree, testing_paths.testing_tree_copy) 433 test_env.copy_dir(testing_paths.testing_tree, testing_paths.testing_tree_copy)
434 copy_dir(testing_paths.testing_sys_tree, testing_paths.testing_sys_tree_copy) 434 test_env.copy_dir(testing_paths.testing_sys_tree, testing_paths.testing_sys_tree_copy)
435end 435end
436 436
437--- Reset testing environment 437--- Reset testing environment
@@ -441,12 +441,12 @@ local function reset_environment(testing_paths, md5sums)
441 441
442 if testing_tree_md5 ~= md5sums.testing_tree_copy_md5 then 442 if testing_tree_md5 ~= md5sums.testing_tree_copy_md5 then
443 test_env.remove_dir(testing_paths.testing_tree) 443 test_env.remove_dir(testing_paths.testing_tree)
444 copy_dir(testing_paths.testing_tree_copy, testing_paths.testing_tree) 444 test_env.copy_dir(testing_paths.testing_tree_copy, testing_paths.testing_tree)
445 end 445 end
446 446
447 if testing_sys_tree_md5 ~= md5sums.testing_sys_tree_copy_md5 then 447 if testing_sys_tree_md5 ~= md5sums.testing_sys_tree_copy_md5 then
448 test_env.remove_dir(testing_paths.testing_sys_tree) 448 test_env.remove_dir(testing_paths.testing_sys_tree)
449 copy_dir(testing_paths.testing_sys_tree_copy, testing_paths.testing_sys_tree) 449 test_env.copy_dir(testing_paths.testing_sys_tree_copy, testing_paths.testing_sys_tree)
450 end 450 end
451 print("\n[ENVIRONMENT RESET]") 451 print("\n[ENVIRONMENT RESET]")
452end 452end
diff --git a/test/testfiles/mixed_deploy_type/mdt.c b/test/testfiles/mixed_deploy_type/mdt.c
new file mode 100644
index 00000000..a162ce23
--- /dev/null
+++ b/test/testfiles/mixed_deploy_type/mdt.c
@@ -0,0 +1,6 @@
1#include "lua.h"
2
3int luaopen_mdt(lua_State *L) {
4 lua_pushstring(L, "mdt.c");
5 return 1;
6}
diff --git a/test/testfiles/mixed_deploy_type/mdt.lua b/test/testfiles/mixed_deploy_type/mdt.lua
new file mode 100644
index 00000000..c9ca9c68
--- /dev/null
+++ b/test/testfiles/mixed_deploy_type/mdt.lua
@@ -0,0 +1 @@
return "mdt.lua"
diff --git a/test/testfiles/mixed_deploy_type/mdt_file b/test/testfiles/mixed_deploy_type/mdt_file
new file mode 100644
index 00000000..1a15f7d7
--- /dev/null
+++ b/test/testfiles/mixed_deploy_type/mdt_file
@@ -0,0 +1 @@
return "mdt_file"
diff --git a/test/testfiles/mixed_deploy_type/mixed_deploy_type-0.1.0-1.rockspec b/test/testfiles/mixed_deploy_type/mixed_deploy_type-0.1.0-1.rockspec
new file mode 100644
index 00000000..91b725da
--- /dev/null
+++ b/test/testfiles/mixed_deploy_type/mixed_deploy_type-0.1.0-1.rockspec
@@ -0,0 +1,21 @@
1package = "mixed_deploy_type"
2version = "0.1.0-1"
3source = {
4 url = "http://example.com"
5}
6description = {
7 homepage = "http://example.com",
8 license = "*** please specify a license ***"
9}
10dependencies = {}
11build = {
12 type = "builtin",
13 modules = {
14 mdt = "mdt/mdt.lua"
15 },
16 install = {
17 lua = {
18 mdt_file = "mdt/mdt_file"
19 }
20 }
21}
diff --git a/test/testfiles/mixed_deploy_type/mixed_deploy_type-0.2.0-1.rockspec b/test/testfiles/mixed_deploy_type/mixed_deploy_type-0.2.0-1.rockspec
new file mode 100644
index 00000000..9ca03180
--- /dev/null
+++ b/test/testfiles/mixed_deploy_type/mixed_deploy_type-0.2.0-1.rockspec
@@ -0,0 +1,21 @@
1package = "mixed_deploy_type"
2version = "0.2.0-1"
3source = {
4 url = "http://example.com"
5}
6description = {
7 homepage = "http://example.com",
8 license = "*** please specify a license ***"
9}
10dependencies = {}
11build = {
12 type = "builtin",
13 modules = {
14 mdt = "mdt/mdt.c"
15 },
16 install = {
17 lib = {
18 mdt_file = "mdt/mdt_file"
19 }
20 }
21}