aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/luarocks/manif.lua133
-rw-r--r--src/luarocks/persist.lua5
-rw-r--r--src/luarocks/repos.lua254
3 files changed, 207 insertions, 185 deletions
diff --git a/src/luarocks/manif.lua b/src/luarocks/manif.lua
index 8277b3a5..7fdfecda 100644
--- a/src/luarocks/manif.lua
+++ b/src/luarocks/manif.lua
@@ -9,7 +9,6 @@ local persist = require("luarocks.persist")
9local fetch = require("luarocks.fetch") 9local fetch = require("luarocks.fetch")
10local dir = require("luarocks.dir") 10local dir = require("luarocks.dir")
11local fs = require("luarocks.fs") 11local fs = require("luarocks.fs")
12local util = require("luarocks.util")
13local cfg = require("luarocks.core.cfg") 12local cfg = require("luarocks.core.cfg")
14local path = require("luarocks.path") 13local path = require("luarocks.path")
15 14
@@ -110,85 +109,89 @@ function manif.load_manifest(repo_url, lua_version)
110 return manif.manifest_loader(pathname, repo_url, lua_version) 109 return manif.manifest_loader(pathname, repo_url, lua_version)
111end 110end
112 111
113local function relative_path(from_dir, to_file) 112--- Get type and name of an item (a module or a command) provided by a file.
114 -- It is assumed that `from_dir` is prefix of `to_file`. 113-- @param deploy_type string: rock manifest subtree the file comes from ("bin", "lua", or "lib").
115 return (to_file:sub(#from_dir + 1):gsub("^[\\/]*", "")) 114-- @param file_path string: path to the file relatively to deploy_type subdirectory.
115-- @return (string, string): item type ("module" or "command") and name.
116function manif.get_provided_item(deploy_type, file_path)
117 assert(type(deploy_type) == "string")
118 assert(type(file_path) == "string")
119 local item_type = deploy_type == "bin" and "command" or "module"
120 local item_name = item_type == "command" and file_path or path.path_to_module(file_path)
121 return item_type, item_name
116end 122end
117 123
118local function file_manifest_coordinates(manifest, file, root) 124local function get_providers(item_type, item_name, repo)
119 local deploy_bin = path.deploy_bin_dir(root) 125 assert(type(item_type) == "string")
120 local deploy_lua = path.deploy_lua_dir(root) 126 assert(type(item_name) == "string")
121 local deploy_lib = path.deploy_lib_dir(root) 127 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
122 128 local manifest = manif.load_local_manifest(rocks_dir)
123 if util.starts_with(file, deploy_lua) then 129 return manifest and manifest[item_type .. "s"][item_name]
124 return "modules", path.path_to_module(relative_path(deploy_lua, file):gsub("\\", "/")), deploy_lua
125 elseif util.starts_with(file, deploy_lib) then
126 return "modules", path.path_to_module(relative_path(deploy_lib, file):gsub("\\", "/")), deploy_lib
127 elseif util.starts_with(file, deploy_bin) then
128 return "commands", relative_path(deploy_bin, file), deploy_bin
129 else
130 assert(false, "Assertion failed: '"..file.."' is not a deployed file.")
131 end
132end 130end
133 131
134local function find_providers(file, root) 132--- Given a name of a module or a command, figure out which rock name and version
135 assert(type(file) == "string") 133-- correspond to it in the rock tree manifest.
136 root = root or cfg.root_dir 134-- @param item_type string: "module" or "command".
137 135-- @param item_name string: module or command name.
138 local manifest, err = manif.load_local_manifest(path.rocks_dir(root)) 136-- @param root string or nil: A local root dir for a rocks tree. If not given, the default is used.
139 if not manifest then 137-- @return (string, string) or nil: name and version of the provider rock or nil if there
140 return nil, "untracked" 138-- is no provider.
139function manif.get_current_provider(item_type, item_name, repo)
140 local providers = get_providers(item_type, item_name, repo)
141 if providers then
142 return providers[1]:match("([^/]*)/([^/]*)")
141 end 143 end
144end
142 145
143 local type_key, key = file_manifest_coordinates(manifest, file, root) 146function manif.get_next_provider(item_type, item_name, repo)
144 147 local providers = get_providers(item_type, item_name, repo)
145 local providers = manifest[type_key][key] 148 if providers and providers[2] then
146 if not providers then 149 return providers[2]:match("([^/]*)/([^/]*)")
147 return nil, "untracked"
148 end 150 end
149 return providers
150end 151end
151 152
152--- Given a path of a deployed file, figure out which rock name and version 153--- Given a name of a module or a command provided by a package, figure out
153-- correspond to it in the tree manifest. 154-- which file provides it.
154-- @param file string: The full path of a deployed file. 155-- @param name string: package name.
156-- @param version string: package version.
157-- @param item_type string: "module" or "command".
158-- @param item_name string: module or command name.
155-- @param root string or nil: A local root dir for a rocks tree. If not given, the default is used. 159-- @param root string or nil: A local root dir for a rocks tree. If not given, the default is used.
156-- @return string, string: name and version of the provider rock. 160-- @return (string, string): rock manifest subtree the file comes from ("bin", "lua", or "lib")
157function manif.find_current_provider(file, root) 161-- and path to the providing file relatively to that subtree.
158 local providers, err = find_providers(file, root) 162function manif.get_providing_file(name, version, item_type, item_name, repo)
159 if not providers then return nil, err end 163 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
160 return providers[1]:match("([^/]*)/([^/]*)") 164 local manifest = manif.load_local_manifest(rocks_dir)
161end
162 165
163function manif.find_next_provider(file, root) 166 local entry_table = manifest.repository[name][version][1]
164 local providers, err = find_providers(file, root) 167 local file_path = entry_table[item_type .. "s"][item_name]
165 if not providers then return nil, err end 168
166 if providers[2] then 169 if item_type == "command" then
167 return providers[2]:match("([^/]*)/([^/]*)") 170 return "bin", file_path
168 else
169 return nil
170 end 171 end
171end
172 172
173--- Given a file conflicting with a module or command 173 -- A module can be in "lua" or "lib". Decide based on extension first:
174-- provided by a version of a package, return which file 174 -- most likely Lua modules are in "lua/" and C modules are in "lib/".
175-- in that version corresponds to the conflicting item. 175 if file_path:match("%." .. cfg.lua_extension .. "$") then
176-- @param name string: name of the package with conflicting module or command. 176 return "lua", file_path
177-- @param version string: version of the package with conflicting module or command. 177 elseif file_path:match("%." .. cfg.lib_extension .. "$") then
178-- @param file string: full, unversioned path to a deployed file. 178 return "lib", file_path
179-- @return string: full, unversioned path to a deployed file in
180-- given package that conflicts with given file.
181function manif.find_conflicting_file(name, version, file, root)
182 root = root or cfg.root_dir
183
184 local manifest = manif.load_local_manifest(path.rocks_dir(root))
185 if not manifest then
186 return
187 end 179 end
188 180
189 local entry_table = manifest.repository[name][version][1] 181 -- Fallback to rock manifest scanning.
190 local type_key, key, deploy_dir = file_manifest_coordinates(manifest, file, root) 182 local rock_manifest = manif.load_rock_manifest(name, version)
191 return dir.path(deploy_dir, entry_table[type_key][key]) 183 local subtree = rock_manifest.lib
184
185 for path_part in file_path:gmatch("[^/]+") do
186 if type(subtree) == "table" then
187 subtree = subtree[path_part]
188 else
189 -- Assume it's in "lua/" if it's not in "lib/".
190 return "lua", file_path
191 end
192 end
193
194 return type(subtree) == "string" and "lib" or "lua", file_path
192end 195end
193 196
194return manif 197return manif
diff --git a/src/luarocks/persist.lua b/src/luarocks/persist.lua
index 16ff5065..6d5e917b 100644
--- a/src/luarocks/persist.lua
+++ b/src/luarocks/persist.lua
@@ -25,14 +25,15 @@ local function write_value(out, v, level, sub_order)
25 if v:match("[\r\n]") then 25 if v:match("[\r\n]") then
26 local open, close = "[[", "]]" 26 local open, close = "[[", "]]"
27 local equals = 0 27 local equals = 0
28 while v:find(close, 1, true) do 28 local v_with_bracket = v.."]"
29 while v_with_bracket:find(close, 1, true) do
29 equals = equals + 1 30 equals = equals + 1
30 local eqs = ("="):rep(equals) 31 local eqs = ("="):rep(equals)
31 open, close = "["..eqs.."[", "]"..eqs.."]" 32 open, close = "["..eqs.."[", "]"..eqs.."]"
32 end 33 end
33 out:write(open.."\n"..v..close) 34 out:write(open.."\n"..v..close)
34 else 35 else
35 out:write("\""..v:gsub("\\", "\\\\"):gsub("\"", "\\\"").."\"") 36 out:write(("%q"):format(v))
36 end 37 end
37 else 38 else
38 out:write(tostring(v)) 39 out:write(tostring(v))
diff --git a/src/luarocks/repos.lua b/src/luarocks/repos.lua
index bbc9da79..41e8f344 100644
--- a/src/luarocks/repos.lua
+++ b/src/luarocks/repos.lua
@@ -10,6 +10,23 @@ local dir = require("luarocks.dir")
10local manif = require("luarocks.manif") 10local manif = require("luarocks.manif")
11local deps = require("luarocks.deps") 11local deps = require("luarocks.deps")
12 12
13-- Tree of files installed by a package are stored
14-- in its rock manifest. Some of these files have to
15-- be deployed to locations where Lua can load them as
16-- modules or where they can be used as commands.
17-- These files are characterised by pair
18-- (deploy_type, file_path), where deploy_type is the first
19-- component of the file path and file_path is the rest of the
20-- path. Only files with deploy_type in {"lua", "lib", "bin"}
21-- are deployed somewhere.
22-- Each deployed file provides an "item". An item is
23-- characterised by pair (item_type, item_name).
24-- item_type is "command" for files with deploy_type
25-- "bin" and "module" for deploy_type in {"lua", "lib"}.
26-- item_name is same as file_path for commands
27-- and is produced using path.path_to_module(file_path)
28-- for modules.
29
13--- Get all installed versions of a package. 30--- Get all installed versions of a package.
14-- @param name string: a package name. 31-- @param name string: a package name.
15-- @return table or nil: An array of strings listing installed 32-- @return table or nil: An array of strings listing installed
@@ -193,44 +210,56 @@ end
193local function delete_suffixed(file, suffix) 210local function delete_suffixed(file, suffix)
194 local suffixed_file, err = find_suffixed(file, suffix) 211 local suffixed_file, err = find_suffixed(file, suffix)
195 if not suffixed_file then 212 if not suffixed_file then
196 return nil, "Could not remove " .. file .. ": " .. err, "not found" 213 return nil, "Could not remove " .. file .. ": " .. err
197 end 214 end
198 215
199 fs.delete(suffixed_file) 216 fs.delete(suffixed_file)
200 if fs.exists(suffixed_file) then 217 if fs.exists(suffixed_file) then
201 return nil, "Failed deleting " .. suffixed_file .. ": file still exists", "fail" 218 return nil, "Failed deleting " .. suffixed_file .. ": file still exists"
202 end 219 end
203 220
204 return true 221 return true
205end 222end
206 223
207local function resolve_conflict(target, deploy_dir, name, version, cur_name, cur_version, suffix) 224-- Files can be deployed using versioned and non-versioned names.
208 if name < cur_name or (name == cur_name and deps.compare_versions(version, cur_version)) then 225-- Several items with same type and name can exist if they are
226-- provided by different packages or versions. In any case
227-- item from the newest version of lexicographically smallest package
228-- is deployed using non-versioned name and others use versioned names.
229
230local function get_deploy_paths(name, version, deploy_type, file_path)
231 local deploy_dir = cfg["deploy_" .. deploy_type .. "_dir"]
232 local non_versioned = dir.path(deploy_dir, file_path)
233 local versioned = path.versioned_name(non_versioned, deploy_dir, name, version)
234 return non_versioned, versioned
235end
236
237local function prepare_target(name, version, deploy_type, file_path, suffix)
238 local non_versioned, versioned = get_deploy_paths(name, version, deploy_type, file_path)
239 local item_type, item_name = manif.get_provided_item(deploy_type, file_path)
240 local cur_name, cur_version = manif.get_current_provider(item_type, item_name)
241
242 if not cur_name then
243 return non_versioned
244 elseif name < cur_name or (name == cur_name and deps.compare_versions(version, cur_version)) then
209 -- New version has priority. Move currently provided version back using versioned name. 245 -- New version has priority. Move currently provided version back using versioned name.
210 local cur_target = manif.find_conflicting_file(cur_name, cur_version, target) 246 local cur_deploy_type, cur_file_path = manif.get_providing_file(cur_name, cur_version, item_type, item_name)
211 local versioned = path.versioned_name(cur_target, deploy_dir, cur_name, cur_version) 247 local cur_non_versioned, cur_versioned = get_deploy_paths(cur_name, cur_version, cur_deploy_type, cur_file_path)
212 248
213 local ok, err = fs.make_dir(dir.dir_name(versioned)) 249 local dir_ok, dir_err = fs.make_dir(dir.dir_name(cur_versioned))
214 if not ok then 250 if not dir_ok then return nil, dir_err end
215 return nil, err
216 end
217 251
218 ok, err = move_suffixed(cur_target, versioned, suffix) 252 local move_ok, move_err = move_suffixed(cur_non_versioned, cur_versioned, suffix)
219 if not ok then 253 if not move_ok then return nil, move_err end
220 return nil, err
221 end
222 254
223 return target 255 return non_versioned
224 else 256 else
225 -- Current version has priority, deploy new version using versioned name. 257 -- Current version has priority, deploy new version using versioned name.
226 return path.versioned_name(target, deploy_dir, name, version) 258 return versioned
227 end 259 end
228end 260end
229 261
230--- Deploy a package from the rocks subdirectory. 262--- Deploy a package from the rocks subdirectory.
231-- It is maintained that for each module and command the one that is provided
232-- by the newest version of the lexicographically smallest package
233-- is installed using unversioned name, and other versions use versioned names.
234-- @param name string: name of package 263-- @param name string: name of package
235-- @param version string: exact package version in string format 264-- @param version string: exact package version in string format
236-- @param wrap_bin_scripts bool: whether commands written in Lua should be wrapped. 265-- @param wrap_bin_scripts bool: whether commands written in Lua should be wrapped.
@@ -242,50 +271,45 @@ function repos.deploy_files(name, version, wrap_bin_scripts, deps_mode)
242 assert(type(version) == "string") 271 assert(type(version) == "string")
243 assert(type(wrap_bin_scripts) == "boolean") 272 assert(type(wrap_bin_scripts) == "boolean")
244 273
245 local function deploy_file_tree(file_tree, path_fn, deploy_dir, move_fn, suffix) 274 local rock_manifest, load_err = manif.load_rock_manifest(name, version)
246 local source_dir = path_fn(name, version) 275 if not rock_manifest then return nil, load_err end
247 return recurse_rock_manifest_tree(file_tree,
248 function(parent_path, parent_module, file)
249 local source = dir.path(source_dir, parent_path, file)
250 local target = dir.path(deploy_dir, parent_path, file)
251
252 local cur_name, cur_version = manif.find_current_provider(target)
253 if cur_name then
254 local resolve_err
255 target, resolve_err = resolve_conflict(target, deploy_dir, name, version, cur_name, cur_version, suffix)
256 if not target then
257 return nil, resolve_err
258 end
259 end
260 276
261 local ok, err = fs.make_dir(dir.dir_name(target)) 277 local function deploy_file_tree(deploy_type, source_dir, move_fn, suffix)
262 if not ok then return nil, err end 278 if not rock_manifest[deploy_type] then
279 return true
280 end
263 281
264 local suffixed_target, mover = move_fn(source, target, name, version) 282 return recurse_rock_manifest_tree(rock_manifest[deploy_type], function(parent_path, parent_module, file)
265 if fs.exists(suffixed_target) then 283 local file_path = parent_path .. file
266 local backup = suffixed_target 284 local source = dir.path(source_dir, file_path)
267 repeat 285
268 backup = backup.."~" 286 local target, prepare_err = prepare_target(name, version, deploy_type, file_path, suffix)
269 until not fs.exists(backup) -- Slight race condition here, but shouldn't be a problem. 287 if not target then return nil, prepare_err end
270 288
271 util.printerr("Warning: "..suffixed_target.." is not tracked by this installation of LuaRocks. Moving it to "..backup) 289 local dir_ok, dir_err = fs.make_dir(dir.dir_name(target))
272 local ok, err = fs.move(suffixed_target, backup) 290 if not dir_ok then return nil, dir_err end
273 if not ok then
274 return nil, err
275 end
276 end
277 291
278 ok, err = mover() 292 local suffixed_target, mover = move_fn(source, target)
279 fs.remove_dir_tree_if_empty(dir.dir_name(source)) 293 if fs.exists(suffixed_target) then
280 return ok, err 294 local backup = suffixed_target
295 repeat
296 backup = backup.."~"
297 until not fs.exists(backup) -- Slight race condition here, but shouldn't be a problem.
298
299 util.printerr("Warning: "..suffixed_target.." is not tracked by this installation of LuaRocks. Moving it to "..backup)
300 local move_ok, move_err = fs.move(suffixed_target, backup)
301 if not move_ok then return nil, move_err end
281 end 302 end
282 )
283 end
284 303
285 local rock_manifest, err = manif.load_rock_manifest(name, version) 304 local move_ok, move_err = mover()
286 if not rock_manifest then return nil, err end 305 if not move_ok then return nil, move_err end
306
307 fs.remove_dir_tree_if_empty(dir.dir_name(source))
308 return true
309 end)
310 end
287 311
288 local function install_binary(source, target, name, version) 312 local function install_binary(source, target)
289 if wrap_bin_scripts and fs.is_lua(source) then 313 if wrap_bin_scripts and fs.is_lua(source) then
290 return target .. (cfg.wrapper_suffix or ""), function() return fs.wrap_script(source, target, name, version) end 314 return target .. (cfg.wrapper_suffix or ""), function() return fs.wrap_script(source, target, name, version) end
291 else 315 else
@@ -299,29 +323,20 @@ function repos.deploy_files(name, version, wrap_bin_scripts, deps_mode)
299 end 323 end
300 end 324 end
301 325
302 local ok, err = true 326 local ok, err = deploy_file_tree("bin", path.bin_dir(name, version), install_binary, cfg.wrapper_suffix)
303 if rock_manifest.bin then 327 if not ok then return nil, err end
304 ok, err = deploy_file_tree(rock_manifest.bin, path.bin_dir, cfg.deploy_bin_dir, install_binary, cfg.wrapper_suffix)
305 end
306 if ok and rock_manifest.lua then
307 ok, err = deploy_file_tree(rock_manifest.lua, path.lua_dir, cfg.deploy_lua_dir, make_mover(cfg.perm_read))
308 end
309 if ok and rock_manifest.lib then
310 ok, err = deploy_file_tree(rock_manifest.lib, path.lib_dir, cfg.deploy_lib_dir, make_mover(cfg.perm_exec))
311 end
312 328
313 if not ok then 329 ok, err = deploy_file_tree("lua", path.lua_dir(name, version), make_mover(cfg.perm_read))
314 return nil, err 330 if not ok then return nil, err end
315 end 331
332 ok, err = deploy_file_tree("lib", path.lib_dir(name, version), make_mover(cfg.perm_exec))
333 if not ok then return nil, err end
316 334
317 local writer = require("luarocks.manif.writer") 335 local writer = require("luarocks.manif.writer")
318 return writer.add_to_manifest(name, version, nil, deps_mode) 336 return writer.add_to_manifest(name, version, nil, deps_mode)
319end 337end
320 338
321--- Delete a package from the local repository. 339--- Delete a package from the local repository.
322-- It is maintained that for each module and command the one that is provided
323-- by the newest version of the lexicographically smallest package
324-- is installed using unversioned name, and other versions use versioned names.
325-- @param name string: name of package 340-- @param name string: name of package
326-- @param version string: exact package version in string format 341-- @param version string: exact package version in string format
327-- @param deps_mode: string: Which trees to check dependencies for: 342-- @param deps_mode: string: Which trees to check dependencies for:
@@ -336,65 +351,68 @@ function repos.delete_version(name, version, deps_mode, quick)
336 assert(type(version) == "string") 351 assert(type(version) == "string")
337 assert(type(deps_mode) == "string") 352 assert(type(deps_mode) == "string")
338 353
339 local function delete_deployed_file_tree(file_tree, deploy_dir, suffix) 354 local rock_manifest, load_err = manif.load_rock_manifest(name, version)
340 return recurse_rock_manifest_tree(file_tree, 355 if not rock_manifest then return nil, load_err end
341 function(parent_path, parent_module, file)
342 local target = dir.path(deploy_dir, parent_path, file)
343 local versioned = path.versioned_name(target, deploy_dir, name, version)
344
345 local ok, err, err_type = delete_suffixed(versioned, suffix)
346 if ok then
347 fs.remove_dir_tree_if_empty(dir.dir_name(versioned))
348 return true
349 elseif err_type == "fail" then
350 return nil, err
351 end
352 356
353 ok, err = delete_suffixed(target, suffix) 357 local function delete_deployed_file_tree(deploy_type, suffix)
354 if not ok then 358 if not rock_manifest[deploy_type] then
355 return nil, err 359 return true
356 end 360 end
357 361
358 if not quick then 362 return recurse_rock_manifest_tree(rock_manifest[deploy_type], function(parent_path, parent_module, file)
359 local next_name, next_version = manif.find_next_provider(target) 363 local file_path = parent_path .. file
360 if next_name then 364 local non_versioned, versioned = get_deploy_paths(name, version, deploy_type, file_path)
361 local next_target = manif.find_conflicting_file(next_name, next_version, target)
362 local next_versioned = path.versioned_name(next_target, deploy_dir, next_name, next_version)
363 365
364 ok, err = move_suffixed(next_versioned, next_target, suffix) 366 -- Figure out if the file is deployed using versioned or non-versioned name.
365 if not ok then 367 local target
366 return nil, err 368 local item_type, item_name = manif.get_provided_item(deploy_type, file_path)
367 end 369 local cur_name, cur_version = manif.get_current_provider(item_type, item_name)
368 370
369 fs.remove_dir_tree_if_empty(dir.dir_name(versioned)) 371 if cur_name == name and cur_version == version then
370 end 372 -- This package has highest priority, should be in non-versioned location.
373 target = non_versioned
374 else
375 target = versioned
376 end
377
378 local ok, err = delete_suffixed(target, suffix)
379 if not ok then return nil, err end
380
381 if not quick and target == non_versioned then
382 -- If another package provides this file, move its version
383 -- into non-versioned location instead.
384 local next_name, next_version = manif.get_next_provider(item_type, item_name)
385
386 if next_name then
387 local next_deploy_type, next_file_path = manif.get_providing_file(next_name, next_version, item_type, item_name)
388 local next_non_versioned, next_versioned = get_deploy_paths(next_name, next_version, next_deploy_type, next_file_path)
389
390 local move_ok, move_err = move_suffixed(next_versioned, next_non_versioned, suffix)
391 if not move_ok then return nil, move_err end
392
393 fs.remove_dir_tree_if_empty(dir.dir_name(next_versioned))
371 end 394 end
372 fs.remove_dir_tree_if_empty(dir.dir_name(target))
373 return true
374 end 395 end
375 )
376 end
377 396
378 local rock_manifest, err = manif.load_rock_manifest(name, version) 397 fs.remove_dir_tree_if_empty(dir.dir_name(target))
379 if not rock_manifest then return nil, err end 398 return true
380 399 end)
381 local ok = true
382 if rock_manifest.bin then
383 ok, err = delete_deployed_file_tree(rock_manifest.bin, cfg.deploy_bin_dir, cfg.wrapper_suffix)
384 end
385 if ok and rock_manifest.lua then
386 ok, err = delete_deployed_file_tree(rock_manifest.lua, cfg.deploy_lua_dir)
387 end
388 if ok and rock_manifest.lib then
389 ok, err = delete_deployed_file_tree(rock_manifest.lib, cfg.deploy_lib_dir)
390 end 400 end
401
402 local ok, err = delete_deployed_file_tree("bin", cfg.wrapper_suffix)
403 if not ok then return nil, err end
404
405 ok, err = delete_deployed_file_tree("lua")
406 if not ok then return nil, err end
407
408 ok, err = delete_deployed_file_tree("lib")
391 if not ok then return nil, err end 409 if not ok then return nil, err end
392 410
393 fs.delete(path.install_dir(name, version)) 411 fs.delete(path.install_dir(name, version))
394 if not get_installed_versions(name) then 412 if not get_installed_versions(name) then
395 fs.delete(dir.path(cfg.rocks_dir, name)) 413 fs.delete(dir.path(cfg.rocks_dir, name))
396 end 414 end
397 415
398 if quick then 416 if quick then
399 return true 417 return true
400 end 418 end