diff options
author | Hisham Muhammad <hisham@gobolinux.org> | 2016-10-20 01:59:08 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-10-20 01:59:08 -0400 |
commit | 02dc970620ea88d63093a3f848eac69bb9e3e460 (patch) | |
tree | 2f2e15f68cb797776430f493e0341fae8d1daa3f /src | |
parent | 78e84d2c903e85ec620c08275519dac65e9cbf97 (diff) | |
parent | c7b91ce031a3b7877f59951fc8570c97f598e771 (diff) | |
download | luarocks-02dc970620ea88d63093a3f848eac69bb9e3e460.tar.gz luarocks-02dc970620ea88d63093a3f848eac69bb9e3e460.tar.bz2 luarocks-02dc970620ea88d63093a3f848eac69bb9e3e460.zip |
Merge pull request #631 from mpeterv/fix-conflict-resolution
Fix conflict resolution
Diffstat (limited to 'src')
-rw-r--r-- | src/luarocks/manif.lua | 54 | ||||
-rw-r--r-- | src/luarocks/repos.lua | 205 |
2 files changed, 163 insertions, 96 deletions
diff --git a/src/luarocks/manif.lua b/src/luarocks/manif.lua index a8bbf279..1b75452b 100644 --- a/src/luarocks/manif.lua +++ b/src/luarocks/manif.lua | |||
@@ -469,33 +469,34 @@ local function relative_path(from_dir, to_file) | |||
469 | return (to_file:sub(#from_dir + 1):gsub("^[\\/]*", "")) | 469 | return (to_file:sub(#from_dir + 1):gsub("^[\\/]*", "")) |
470 | end | 470 | end |
471 | 471 | ||
472 | local function find_providers(file, root) | 472 | local function file_manifest_coordinates(manifest, file, root) |
473 | assert(type(file) == "string") | ||
474 | root = root or cfg.root_dir | ||
475 | |||
476 | local manifest, err = manif_core.load_local_manifest(path.rocks_dir(root)) | ||
477 | if not manifest then | ||
478 | return nil, "untracked" | ||
479 | end | ||
480 | local deploy_bin = path.deploy_bin_dir(root) | 473 | local deploy_bin = path.deploy_bin_dir(root) |
481 | local deploy_lua = path.deploy_lua_dir(root) | 474 | local deploy_lua = path.deploy_lua_dir(root) |
482 | local deploy_lib = path.deploy_lib_dir(root) | 475 | local deploy_lib = path.deploy_lib_dir(root) |
483 | local key, manifest_tbl | ||
484 | 476 | ||
485 | if util.starts_with(file, deploy_lua) then | 477 | if util.starts_with(file, deploy_lua) then |
486 | manifest_tbl = manifest.modules | 478 | return "modules", path.path_to_module(relative_path(deploy_lua, file):gsub("\\", "/")), deploy_lua |
487 | key = path.path_to_module(relative_path(deploy_lua, file):gsub("\\", "/")) | ||
488 | elseif util.starts_with(file, deploy_lib) then | 479 | elseif util.starts_with(file, deploy_lib) then |
489 | manifest_tbl = manifest.modules | 480 | return "modules", path.path_to_module(relative_path(deploy_lib, file):gsub("\\", "/")), deploy_lib |
490 | key = path.path_to_module(relative_path(deploy_lib, file):gsub("\\", "/")) | ||
491 | elseif util.starts_with(file, deploy_bin) then | 481 | elseif util.starts_with(file, deploy_bin) then |
492 | manifest_tbl = manifest.commands | 482 | return "commands", relative_path(deploy_bin, file), deploy_bin |
493 | key = relative_path(deploy_bin, file) | ||
494 | else | 483 | else |
495 | assert(false, "Assertion failed: '"..file.."' is not a deployed file.") | 484 | assert(false, "Assertion failed: '"..file.."' is not a deployed file.") |
496 | end | 485 | end |
486 | end | ||
487 | |||
488 | local function find_providers(file, root) | ||
489 | assert(type(file) == "string") | ||
490 | root = root or cfg.root_dir | ||
491 | |||
492 | local manifest, err = manif_core.load_local_manifest(path.rocks_dir(root)) | ||
493 | if not manifest then | ||
494 | return nil, "untracked" | ||
495 | end | ||
496 | |||
497 | local type_key, key = file_manifest_coordinates(manifest, file, root) | ||
497 | 498 | ||
498 | local providers = manifest_tbl[key] | 499 | local providers = manifest[type_key][key] |
499 | if not providers then | 500 | if not providers then |
500 | return nil, "untracked" | 501 | return nil, "untracked" |
501 | end | 502 | end |
@@ -523,4 +524,25 @@ function manif.find_next_provider(file, root) | |||
523 | end | 524 | end |
524 | end | 525 | end |
525 | 526 | ||
527 | --- Given a file conflicting with a module or command | ||
528 | -- provided by a version of a package, return which file | ||
529 | -- in that version corresponds to the conflicting item. | ||
530 | -- @param name string: name of the package with conflicting module or command. | ||
531 | -- @param version string: version of the package with conflicting module or command. | ||
532 | -- @param file string: full, unversioned path to a deployed file. | ||
533 | -- @return string: full, unversioned path to a deployed file in | ||
534 | -- given package that conflicts with given file. | ||
535 | function manif.find_conflicting_file(name, version, file, root) | ||
536 | root = root or cfg.root_dir | ||
537 | |||
538 | local manifest = manif_core.load_local_manifest(path.rocks_dir(root)) | ||
539 | if not manifest then | ||
540 | return | ||
541 | end | ||
542 | |||
543 | local entry_table = manifest.repository[name][version][1] | ||
544 | local type_key, key, deploy_dir = file_manifest_coordinates(manifest, file, root) | ||
545 | return dir.path(deploy_dir, entry_table[type_key][key]) | ||
546 | end | ||
547 | |||
526 | return manif | 548 | return manif |
diff --git a/src/luarocks/repos.lua b/src/luarocks/repos.lua index a1cf2bb4..161cdd8a 100644 --- a/src/luarocks/repos.lua +++ b/src/luarocks/repos.lua | |||
@@ -151,50 +151,83 @@ function repos.run_hook(rockspec, hook_name) | |||
151 | return true | 151 | return true |
152 | end | 152 | end |
153 | 153 | ||
154 | local function install_binary(source, target, name, version) | 154 | function repos.should_wrap_bin_scripts(rockspec) |
155 | assert(type(source) == "string") | 155 | assert(type(rockspec) == "table") |
156 | assert(type(target) == "string") | 156 | |
157 | 157 | if cfg.wrap_bin_scripts ~= nil then | |
158 | if fs.is_lua(source) then | 158 | return cfg.wrap_bin_scripts |
159 | return fs.wrap_script(source, target, name, version) | 159 | end |
160 | else | 160 | if rockspec.deploy and rockspec.deploy.wrap_bin_scripts == false then |
161 | return fs.copy_binary(source, target) | 161 | return false |
162 | end | 162 | end |
163 | return true | ||
163 | end | 164 | end |
164 | 165 | ||
165 | local function resolve_conflict(target, deploy_dir, name, version) | 166 | local function find_suffixed(file, suffix) |
166 | local cname, cversion = manif.find_current_provider(target) | 167 | local filenames = {file} |
167 | if not cname then | 168 | if suffix and suffix ~= "" then |
168 | return nil, cversion | 169 | table.insert(filenames, 1, file .. suffix) |
169 | end | 170 | end |
170 | if name ~= cname or deps.compare_versions(version, cversion) then | 171 | |
171 | local versioned = path.versioned_name(target, deploy_dir, cname, cversion) | 172 | for _, filename in ipairs(filenames) do |
172 | local ok, err = fs.make_dir(dir.dir_name(versioned)) | 173 | if fs.exists(filename) then |
173 | if not ok then return nil, err end | 174 | return filename |
174 | fs.move(target, versioned) | 175 | end |
175 | return target | ||
176 | else | ||
177 | return path.versioned_name(target, deploy_dir, name, version) | ||
178 | end | 176 | end |
179 | end | 177 | end |
180 | 178 | ||
181 | function repos.should_wrap_bin_scripts(rockspec) | 179 | local function move_suffixed(from_file, to_file, suffix) |
182 | assert(type(rockspec) == "table") | 180 | local suffixed_from_file = find_suffixed(from_file, suffix) |
181 | if not suffixed_from_file then | ||
182 | return nil, "File not found" | ||
183 | end | ||
183 | 184 | ||
184 | if cfg.wrap_bin_scripts ~= nil then | 185 | suffix = suffixed_from_file:sub(#from_file + 1) |
185 | return cfg.wrap_bin_scripts | 186 | local suffixed_to_file = to_file .. suffix |
187 | return fs.move(suffixed_from_file, suffixed_to_file) | ||
188 | end | ||
189 | |||
190 | local function delete_suffixed(file, suffix) | ||
191 | local suffixed_file = find_suffixed(file, suffix) | ||
192 | if not suffixed_file then | ||
193 | return nil, "File not found", "not found" | ||
186 | end | 194 | end |
187 | if rockspec.deploy and rockspec.deploy.wrap_bin_scripts == false then | 195 | |
188 | return false | 196 | fs.delete(suffixed_file) |
197 | if fs.exists(suffixed_file) then | ||
198 | return nil, "Failed deleting " .. suffixed_file, "fail" | ||
189 | end | 199 | end |
200 | |||
190 | return true | 201 | return true |
191 | end | 202 | end |
192 | 203 | ||
204 | local function resolve_conflict(target, deploy_dir, name, version, cur_name, cur_version, suffix) | ||
205 | if name < cur_name or (name == cur_name and deps.compare_versions(version, cur_version)) then | ||
206 | -- New version has priority. Move currently provided version back using versioned name. | ||
207 | local cur_target = manif.find_conflicting_file(cur_name, cur_version, target) | ||
208 | local versioned = path.versioned_name(cur_target, deploy_dir, cur_name, cur_version) | ||
209 | |||
210 | local ok, err = fs.make_dir(dir.dir_name(versioned)) | ||
211 | if not ok then | ||
212 | return nil, err | ||
213 | end | ||
214 | |||
215 | ok, err = move_suffixed(cur_target, versioned, suffix) | ||
216 | if not ok then | ||
217 | return nil, err | ||
218 | end | ||
219 | |||
220 | return target | ||
221 | else | ||
222 | -- Current version has priority, deploy new version using versioned name. | ||
223 | return path.versioned_name(target, deploy_dir, name, version) | ||
224 | end | ||
225 | end | ||
226 | |||
193 | --- Deploy a package from the rocks subdirectory. | 227 | --- Deploy a package from the rocks subdirectory. |
194 | -- It is maintained that for each file the one that is provided | 228 | -- It is maintained that for each module and command the one that is provided |
195 | -- by the newest version of the lexicographically smallest package | 229 | -- by the newest version of the lexicographically smallest package |
196 | -- is installed using unversioned name, and other versions of the file | 230 | -- is installed using unversioned name, and other versions use versioned names. |
197 | -- use versioned names. | ||
198 | -- @param name string: name of package | 231 | -- @param name string: name of package |
199 | -- @param version string: exact package version in string format | 232 | -- @param version string: exact package version in string format |
200 | -- @param wrap_bin_scripts bool: whether commands written in Lua should be wrapped. | 233 | -- @param wrap_bin_scripts bool: whether commands written in Lua should be wrapped. |
@@ -206,50 +239,66 @@ function repos.deploy_files(name, version, wrap_bin_scripts, deps_mode) | |||
206 | assert(type(version) == "string") | 239 | assert(type(version) == "string") |
207 | assert(type(wrap_bin_scripts) == "boolean") | 240 | assert(type(wrap_bin_scripts) == "boolean") |
208 | 241 | ||
209 | local function deploy_file_tree(file_tree, path_fn, deploy_dir, move_fn) | 242 | local function deploy_file_tree(file_tree, path_fn, deploy_dir, move_fn, suffix) |
210 | local source_dir = path_fn(name, version) | 243 | local source_dir = path_fn(name, version) |
211 | return recurse_rock_manifest_tree(file_tree, | 244 | return recurse_rock_manifest_tree(file_tree, |
212 | function(parent_path, parent_module, file) | 245 | function(parent_path, parent_module, file) |
213 | local source = dir.path(source_dir, parent_path, file) | 246 | local source = dir.path(source_dir, parent_path, file) |
214 | local target = dir.path(deploy_dir, parent_path, file) | 247 | local target = dir.path(deploy_dir, parent_path, file) |
215 | local ok, err | 248 | |
216 | if fs.exists(target) then | 249 | local cur_name, cur_version = manif.find_current_provider(target) |
217 | local new_target, err = resolve_conflict(target, deploy_dir, name, version) | 250 | if cur_name then |
218 | if err == "untracked" then | 251 | local resolve_err |
219 | local backup = target | 252 | target, resolve_err = resolve_conflict(target, deploy_dir, name, version, cur_name, cur_version, suffix) |
220 | repeat | 253 | if not target then |
221 | backup = backup.."~" | 254 | return nil, resolve_err |
222 | until not fs.exists(backup) -- slight race condition here, but shouldn't be a problem. | ||
223 | util.printerr("Warning: "..target.." is not tracked by this installation of LuaRocks. Moving it to "..backup) | ||
224 | fs.move(target, backup) | ||
225 | elseif err then | ||
226 | return nil, err.." Cannot install new version." | ||
227 | else | ||
228 | target = new_target | ||
229 | end | 255 | end |
230 | end | 256 | end |
231 | ok, err = fs.make_dir(dir.dir_name(target)) | 257 | |
258 | local ok, err = fs.make_dir(dir.dir_name(target)) | ||
232 | if not ok then return nil, err end | 259 | if not ok then return nil, err end |
233 | ok, err = move_fn(source, target, name, version) | 260 | |
261 | local suffixed_target, mover = move_fn(source, target, name, version) | ||
262 | if fs.exists(suffixed_target) then | ||
263 | local backup = suffixed_target | ||
264 | repeat | ||
265 | backup = backup.."~" | ||
266 | until not fs.exists(backup) -- Slight race condition here, but shouldn't be a problem. | ||
267 | |||
268 | util.printerr("Warning: "..suffixed_target.." is not tracked by this installation of LuaRocks. Moving it to "..backup) | ||
269 | local ok, err = fs.move(suffixed_target, backup) | ||
270 | if not ok then | ||
271 | return nil, err | ||
272 | end | ||
273 | end | ||
274 | |||
275 | ok, err = mover() | ||
234 | fs.remove_dir_tree_if_empty(dir.dir_name(source)) | 276 | fs.remove_dir_tree_if_empty(dir.dir_name(source)) |
235 | if not ok then return nil, err end | 277 | return ok, err |
236 | return true | ||
237 | end | 278 | end |
238 | ) | 279 | ) |
239 | end | 280 | end |
240 | 281 | ||
241 | local rock_manifest = manif.load_rock_manifest(name, version) | 282 | local rock_manifest = manif.load_rock_manifest(name, version) |
242 | 283 | ||
243 | local ok, err = true | 284 | local function install_binary(source, target, name, version) |
244 | if rock_manifest.bin then | 285 | if wrap_bin_scripts and fs.is_lua(source) then |
245 | local move_bin_fn = wrap_bin_scripts and install_binary or fs.copy_binary | 286 | return target .. (cfg.wrapper_suffix or ""), function() return fs.wrap_script(source, target, name, version) end |
246 | ok, err = deploy_file_tree(rock_manifest.bin, path.bin_dir, cfg.deploy_bin_dir, move_bin_fn) | 287 | else |
288 | return target, function() return fs.copy_binary(source, target) end | ||
289 | end | ||
247 | end | 290 | end |
291 | |||
248 | local function make_mover(perms) | 292 | local function make_mover(perms) |
249 | return function (src, dest) | 293 | return function(source, target) |
250 | return fs.move(src, dest, perms) | 294 | return target, function() return fs.move(source, target, perms) end |
251 | end | 295 | end |
252 | end | 296 | end |
297 | |||
298 | local ok, err = true | ||
299 | if rock_manifest.bin then | ||
300 | ok, err = deploy_file_tree(rock_manifest.bin, path.bin_dir, cfg.deploy_bin_dir, install_binary, cfg.wrapper_suffix) | ||
301 | end | ||
253 | if ok and rock_manifest.lua then | 302 | if ok and rock_manifest.lua then |
254 | ok, err = deploy_file_tree(rock_manifest.lua, path.lua_dir, cfg.deploy_lua_dir, make_mover(cfg.perm_read)) | 303 | ok, err = deploy_file_tree(rock_manifest.lua, path.lua_dir, cfg.deploy_lua_dir, make_mover(cfg.perm_read)) |
255 | end | 304 | end |
@@ -264,26 +313,10 @@ function repos.deploy_files(name, version, wrap_bin_scripts, deps_mode) | |||
264 | return manif.update_manifest(name, version, nil, deps_mode) | 313 | return manif.update_manifest(name, version, nil, deps_mode) |
265 | end | 314 | end |
266 | 315 | ||
267 | local function delete_suffixed(filename, suffix) | ||
268 | local filenames = { filename } | ||
269 | if suffix and suffix ~= "" then filenames = { filename..suffix, filename } end | ||
270 | for _, name in ipairs(filenames) do | ||
271 | if fs.exists(name) then | ||
272 | fs.delete(name) | ||
273 | if fs.exists(name) then | ||
274 | return nil, "Failed deleting "..name, "fail" | ||
275 | end | ||
276 | return true, name | ||
277 | end | ||
278 | end | ||
279 | return false, "File not found", "not found" | ||
280 | end | ||
281 | |||
282 | --- Delete a package from the local repository. | 316 | --- Delete a package from the local repository. |
283 | -- It is maintained that for each file the one that is provided | 317 | -- It is maintained that for each module and command the one that is provided |
284 | -- by the newest version of the lexicographically smallest package | 318 | -- by the newest version of the lexicographically smallest package |
285 | -- is installed using unversioned name, and other versions of the file | 319 | -- is installed using unversioned name, and other versions use versioned names. |
286 | -- use versioned names. | ||
287 | -- @param name string: name of package | 320 | -- @param name string: name of package |
288 | -- @param version string: exact package version in string format | 321 | -- @param version string: exact package version in string format |
289 | -- @param deps_mode: string: Which trees to check dependencies for: | 322 | -- @param deps_mode: string: Which trees to check dependencies for: |
@@ -303,19 +336,31 @@ function repos.delete_version(name, version, deps_mode, quick) | |||
303 | function(parent_path, parent_module, file) | 336 | function(parent_path, parent_module, file) |
304 | local target = dir.path(deploy_dir, parent_path, file) | 337 | local target = dir.path(deploy_dir, parent_path, file) |
305 | local versioned = path.versioned_name(target, deploy_dir, name, version) | 338 | local versioned = path.versioned_name(target, deploy_dir, name, version) |
306 | local ok, name, err = delete_suffixed(versioned, suffix) | 339 | |
340 | local ok, err, err_type = delete_suffixed(versioned, suffix) | ||
307 | if ok then | 341 | if ok then |
308 | fs.remove_dir_tree_if_empty(dir.dir_name(versioned)) | 342 | fs.remove_dir_tree_if_empty(dir.dir_name(versioned)) |
309 | return true | 343 | return true |
344 | elseif err_type == "fail" then | ||
345 | return nil, err | ||
346 | end | ||
347 | |||
348 | ok, err = delete_suffixed(target, suffix) | ||
349 | if not ok then | ||
350 | return nil, err | ||
310 | end | 351 | end |
311 | if err == "fail" then return nil, name end | 352 | |
312 | ok, name, err = delete_suffixed(target, suffix) | ||
313 | if err == "fail" then return nil, name end | ||
314 | if not quick then | 353 | if not quick then |
315 | local next_name, next_version = manif.find_next_provider(target) | 354 | local next_name, next_version = manif.find_next_provider(target) |
316 | if next_name then | 355 | if next_name then |
317 | local versioned = path.versioned_name(name, deploy_dir, next_name, next_version) | 356 | local next_target = manif.find_conflicting_file(next_name, next_version, target) |
318 | fs.move(versioned, name) | 357 | local next_versioned = path.versioned_name(next_target, deploy_dir, next_name, next_version) |
358 | |||
359 | ok, err = move_suffixed(next_versioned, next_target, suffix) | ||
360 | if not ok then | ||
361 | return nil, err | ||
362 | end | ||
363 | |||
319 | fs.remove_dir_tree_if_empty(dir.dir_name(versioned)) | 364 | fs.remove_dir_tree_if_empty(dir.dir_name(versioned)) |
320 | end | 365 | end |
321 | end | 366 | end |
@@ -340,7 +385,7 @@ function repos.delete_version(name, version, deps_mode, quick) | |||
340 | if ok and rock_manifest.lib then | 385 | if ok and rock_manifest.lib then |
341 | ok, err = delete_deployed_file_tree(rock_manifest.lib, cfg.deploy_lib_dir) | 386 | ok, err = delete_deployed_file_tree(rock_manifest.lib, cfg.deploy_lib_dir) |
342 | end | 387 | end |
343 | if err then return nil, err end | 388 | if not ok then return nil, err end |
344 | 389 | ||
345 | fs.delete(path.install_dir(name, version)) | 390 | fs.delete(path.install_dir(name, version)) |
346 | if not get_installed_versions(name) then | 391 | if not get_installed_versions(name) then |