diff options
author | Hisham <hisham@gobolinux.org> | 2016-10-28 20:59:42 -0200 |
---|---|---|
committer | Hisham <hisham@gobolinux.org> | 2016-10-28 20:59:42 -0200 |
commit | 5c140a5da73d304653621a0ac40f363d234928ed (patch) | |
tree | 5bf34d6c10e8d4bb692feb9589477ac08f7f4863 /src | |
parent | 5bd20308b881c63dd886b71395dbc9baaab25dfd (diff) | |
download | luarocks-5c140a5da73d304653621a0ac40f363d234928ed.tar.gz luarocks-5c140a5da73d304653621a0ac40f363d234928ed.tar.bz2 luarocks-5c140a5da73d304653621a0ac40f363d234928ed.zip |
Keep only command driver functions in luarocks.cmd
Diffstat (limited to 'src')
-rw-r--r-- | src/luarocks/cmd/build.lua | 347 | ||||
-rw-r--r-- | src/luarocks/cmd/download.lua | 76 | ||||
-rw-r--r-- | src/luarocks/cmd/pack.lua | 164 | ||||
-rw-r--r-- | src/luarocks/cmd/remove.lua | 124 | ||||
-rw-r--r-- | src/luarocks/cmd/search.lua | 411 |
5 files changed, 36 insertions, 1086 deletions
diff --git a/src/luarocks/cmd/build.lua b/src/luarocks/cmd/build.lua index f3b054d2..d050e6a3 100644 --- a/src/luarocks/cmd/build.lua +++ b/src/luarocks/cmd/build.lua | |||
@@ -1,23 +1,22 @@ | |||
1 | 1 | ||
2 | --- Module implementing the LuaRocks "build" command. | 2 | --- Module implementing the LuaRocks "build" command. |
3 | -- Builds a rock, compiling its C parts if any. | 3 | -- Builds a rock, compiling its C parts if any. |
4 | local build = {} | 4 | local cmd_build = {} |
5 | 5 | ||
6 | local pack = require("luarocks.pack") | 6 | local pack = require("luarocks.pack") |
7 | local path = require("luarocks.path") | 7 | local path = require("luarocks.path") |
8 | local util = require("luarocks.util") | 8 | local util = require("luarocks.util") |
9 | local repos = require("luarocks.repos") | ||
10 | local fetch = require("luarocks.fetch") | 9 | local fetch = require("luarocks.fetch") |
11 | local fs = require("luarocks.fs") | 10 | local fs = require("luarocks.fs") |
12 | local dir = require("luarocks.dir") | 11 | local dir = require("luarocks.dir") |
13 | local deps = require("luarocks.deps") | 12 | local deps = require("luarocks.deps") |
14 | local writer = require("luarocks.manif.writer") | ||
15 | local remove = require("luarocks.remove") | 13 | local remove = require("luarocks.remove") |
16 | local cfg = require("luarocks.core.cfg") | 14 | local cfg = require("luarocks.core.cfg") |
15 | local build = require("luarocks.build") | ||
17 | 16 | ||
18 | build.help_summary = "Build/compile a rock." | 17 | cmd_build.help_summary = "build/compile a rock." |
19 | build.help_arguments = "[--pack-binary-rock] [--keep] {<rockspec>|<rock>|<name> [<version>]}" | 18 | cmd_build.help_arguments = "[--pack-binary-rock] [--keep] {<rockspec>|<rock>|<name> [<version>]}" |
20 | build.help = [[ | 19 | cmd_build.help = [[ |
21 | Build and install a rock, compiling its C parts if any. | 20 | Build and install a rock, compiling its C parts if any. |
22 | Argument may be a rockspec file, a source rock file | 21 | Argument may be a rockspec file, a source rock file |
23 | or the name of a rock to be fetched from a repository. | 22 | or the name of a rock to be fetched from a repository. |
@@ -39,328 +38,6 @@ or the name of a rock to be fetched from a repository. | |||
39 | 38 | ||
40 | ]]..util.deps_mode_help() | 39 | ]]..util.deps_mode_help() |
41 | 40 | ||
42 | --- Install files to a given location. | ||
43 | -- Takes a table where the array part is a list of filenames to be copied. | ||
44 | -- In the hash part, other keys, if is_module_path is set, are identifiers | ||
45 | -- in Lua module format, to indicate which subdirectory the file should be | ||
46 | -- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo") | ||
47 | -- will copy src/bar.lua to boo/foo. | ||
48 | -- @param files table or nil: A table containing a list of files to copy in | ||
49 | -- the format described above. If nil is passed, this function is a no-op. | ||
50 | -- Directories should be delimited by forward slashes as in internet URLs. | ||
51 | -- @param location string: The base directory files should be copied to. | ||
52 | -- @param is_module_path boolean: True if string keys in files should be | ||
53 | -- interpreted as dotted module paths. | ||
54 | -- @param perms string: Permissions of the newly created files installed. | ||
55 | -- Directories are always created with the default permissions. | ||
56 | -- @return boolean or (nil, string): True if succeeded or | ||
57 | -- nil and an error message. | ||
58 | local function install_files(files, location, is_module_path, perms) | ||
59 | assert(type(files) == "table" or not files) | ||
60 | assert(type(location) == "string") | ||
61 | if files then | ||
62 | for k, file in pairs(files) do | ||
63 | local dest = location | ||
64 | local filename = dir.base_name(file) | ||
65 | if type(k) == "string" then | ||
66 | local modname = k | ||
67 | if is_module_path then | ||
68 | dest = dir.path(location, path.module_to_path(modname)) | ||
69 | local ok, err = fs.make_dir(dest) | ||
70 | if not ok then return nil, err end | ||
71 | if filename:match("%.lua$") then | ||
72 | local basename = modname:match("([^.]+)$") | ||
73 | filename = basename..".lua" | ||
74 | end | ||
75 | else | ||
76 | dest = dir.path(location, dir.dir_name(modname)) | ||
77 | local ok, err = fs.make_dir(dest) | ||
78 | if not ok then return nil, err end | ||
79 | filename = dir.base_name(modname) | ||
80 | end | ||
81 | else | ||
82 | local ok, err = fs.make_dir(dest) | ||
83 | if not ok then return nil, err end | ||
84 | end | ||
85 | local ok = fs.copy(dir.path(file), dir.path(dest, filename), perms) | ||
86 | if not ok then | ||
87 | return nil, "Failed copying "..file | ||
88 | end | ||
89 | end | ||
90 | end | ||
91 | return true | ||
92 | end | ||
93 | |||
94 | --- Write to the current directory the contents of a table, | ||
95 | -- where each key is a file name and its value is the file content. | ||
96 | -- @param files table: The table of files to be written. | ||
97 | local function extract_from_rockspec(files) | ||
98 | for name, content in pairs(files) do | ||
99 | local fd = io.open(dir.path(fs.current_dir(), name), "w+") | ||
100 | fd:write(content) | ||
101 | fd:close() | ||
102 | end | ||
103 | end | ||
104 | |||
105 | --- Applies patches inlined in the build.patches section | ||
106 | -- and extracts files inlined in the build.extra_files section | ||
107 | -- of a rockspec. | ||
108 | -- @param rockspec table: A rockspec table. | ||
109 | -- @return boolean or (nil, string): True if succeeded or | ||
110 | -- nil and an error message. | ||
111 | function build.apply_patches(rockspec) | ||
112 | assert(type(rockspec) == "table") | ||
113 | |||
114 | local build_spec = rockspec.build | ||
115 | if build_spec.extra_files then | ||
116 | extract_from_rockspec(build_spec.extra_files) | ||
117 | end | ||
118 | if build_spec.patches then | ||
119 | extract_from_rockspec(build_spec.patches) | ||
120 | for patch, patchdata in util.sortedpairs(build_spec.patches) do | ||
121 | util.printout("Applying patch "..patch.."...") | ||
122 | local ok, err = fs.apply_patch(tostring(patch), patchdata) | ||
123 | if not ok then | ||
124 | return nil, "Failed applying patch "..patch | ||
125 | end | ||
126 | end | ||
127 | end | ||
128 | return true | ||
129 | end | ||
130 | |||
131 | local function install_default_docs(name, version) | ||
132 | local patterns = { "readme", "license", "copying", ".*%.md" } | ||
133 | local dest = dir.path(path.install_dir(name, version), "doc") | ||
134 | local has_dir = false | ||
135 | for file in fs.dir() do | ||
136 | for _, pattern in ipairs(patterns) do | ||
137 | if file:lower():match("^"..pattern) then | ||
138 | if not has_dir then | ||
139 | fs.make_dir(dest) | ||
140 | has_dir = true | ||
141 | end | ||
142 | fs.copy(file, dest, cfg.perm_read) | ||
143 | break | ||
144 | end | ||
145 | end | ||
146 | end | ||
147 | end | ||
148 | |||
149 | local function check_macosx_deployment_target(rockspec) | ||
150 | local target = rockspec.build.macosx_deployment_target | ||
151 | local function minor(version) | ||
152 | return tonumber(version and version:match("^[^.]+%.([^.]+)")) | ||
153 | end | ||
154 | local function patch_variable(var, target) | ||
155 | if rockspec.variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then | ||
156 | rockspec.variables[var] = (rockspec.variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET="..target) | ||
157 | else | ||
158 | rockspec.variables[var] = "env MACOSX_DEPLOYMENT_TARGET="..target.." "..rockspec.variables[var] | ||
159 | end | ||
160 | end | ||
161 | if cfg.platforms.macosx and rockspec:format_is_at_least("3.0") and target then | ||
162 | local version = util.popen_read("sw_vers -productVersion") | ||
163 | local versionminor = minor(version) | ||
164 | local targetminor = minor(target) | ||
165 | if targetminor > versionminor then | ||
166 | return nil, ("This rock requires Mac OSX 10.%d, and you are running 10.%d."):format(targetminor, versionminor) | ||
167 | end | ||
168 | patch_variable("CC", target) | ||
169 | patch_variable("LD", target) | ||
170 | end | ||
171 | return true | ||
172 | end | ||
173 | |||
174 | --- Build and install a rock given a rockspec. | ||
175 | -- @param rockspec_file string: local or remote filename of a rockspec. | ||
176 | -- @param need_to_fetch boolean: true if sources need to be fetched, | ||
177 | -- false if the rockspec was obtained from inside a source rock. | ||
178 | -- @param minimal_mode boolean: true if there's no need to fetch, | ||
179 | -- unpack or change dir (this is used by "luarocks make"). Implies | ||
180 | -- need_to_fetch = false. | ||
181 | -- @param deps_mode string: Dependency mode: "one" for the current default tree, | ||
182 | -- "all" for all trees, "order" for all trees with priority >= the current default, | ||
183 | -- "none" for no trees. | ||
184 | -- @param build_only_deps boolean: true to build the listed dependencies only. | ||
185 | -- @return (string, string) or (nil, string, [string]): Name and version of | ||
186 | -- installed rock if succeeded or nil and an error message followed by an error code. | ||
187 | function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode, build_only_deps) | ||
188 | assert(type(rockspec_file) == "string") | ||
189 | assert(type(need_to_fetch) == "boolean") | ||
190 | |||
191 | local rockspec, err, errcode = fetch.load_rockspec(rockspec_file) | ||
192 | if err then | ||
193 | return nil, err, errcode | ||
194 | elseif not rockspec.build then | ||
195 | return nil, "Rockspec error: build table not specified" | ||
196 | elseif not rockspec.build.type then | ||
197 | return nil, "Rockspec error: build type not specified" | ||
198 | end | ||
199 | |||
200 | local ok | ||
201 | if not build_only_deps then | ||
202 | ok, err, errcode = deps.check_external_deps(rockspec, "build") | ||
203 | if err then | ||
204 | return nil, err, errcode | ||
205 | end | ||
206 | end | ||
207 | |||
208 | if deps_mode == "none" then | ||
209 | util.printerr("Warning: skipping dependency checks.") | ||
210 | else | ||
211 | local ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) | ||
212 | if err then | ||
213 | return nil, err, errcode | ||
214 | end | ||
215 | end | ||
216 | |||
217 | local name, version = rockspec.name, rockspec.version | ||
218 | if build_only_deps then | ||
219 | util.printout("Stopping after installing dependencies for " ..name.." "..version) | ||
220 | util.printout() | ||
221 | return name, version | ||
222 | end | ||
223 | |||
224 | if repos.is_installed(name, version) then | ||
225 | repos.delete_version(name, version, deps_mode) | ||
226 | end | ||
227 | |||
228 | if not minimal_mode then | ||
229 | local source_dir | ||
230 | if need_to_fetch then | ||
231 | ok, source_dir, errcode = fetch.fetch_sources(rockspec, true) | ||
232 | if not ok then | ||
233 | return nil, source_dir, errcode | ||
234 | end | ||
235 | local ok, err = fs.change_dir(source_dir) | ||
236 | if not ok then return nil, err end | ||
237 | elseif rockspec.source.file then | ||
238 | local ok, err = fs.unpack_archive(rockspec.source.file) | ||
239 | if not ok then | ||
240 | return nil, err | ||
241 | end | ||
242 | end | ||
243 | fs.change_dir(rockspec.source.dir) | ||
244 | end | ||
245 | |||
246 | local dirs = { | ||
247 | lua = { name = path.lua_dir(name, version), is_module_path = true, perms = cfg.perm_read }, | ||
248 | lib = { name = path.lib_dir(name, version), is_module_path = true, perms = cfg.perm_exec }, | ||
249 | conf = { name = path.conf_dir(name, version), is_module_path = false, perms = cfg.perm_read }, | ||
250 | bin = { name = path.bin_dir(name, version), is_module_path = false, perms = cfg.perm_exec }, | ||
251 | } | ||
252 | |||
253 | for _, d in pairs(dirs) do | ||
254 | local ok, err = fs.make_dir(d.name) | ||
255 | if not ok then return nil, err end | ||
256 | end | ||
257 | local rollback = util.schedule_function(function() | ||
258 | fs.delete(path.install_dir(name, version)) | ||
259 | fs.remove_dir_if_empty(path.versions_dir(name)) | ||
260 | end) | ||
261 | |||
262 | local build_spec = rockspec.build | ||
263 | |||
264 | if not minimal_mode then | ||
265 | ok, err = build.apply_patches(rockspec) | ||
266 | if err then | ||
267 | return nil, err | ||
268 | end | ||
269 | end | ||
270 | |||
271 | ok, err = check_macosx_deployment_target(rockspec) | ||
272 | if not ok then | ||
273 | return nil, err | ||
274 | end | ||
275 | |||
276 | if build_spec.type ~= "none" then | ||
277 | |||
278 | -- Temporary compatibility | ||
279 | if build_spec.type == "module" then | ||
280 | util.printout("Do not use 'module' as a build type. Use 'builtin' instead.") | ||
281 | build_spec.type = "builtin" | ||
282 | end | ||
283 | |||
284 | if cfg.accepted_build_types and util.array_contains(cfg.accepted_build_types, build_spec.type) then | ||
285 | return nil, "This rockspec uses the '"..build_spec.type.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration." | ||
286 | end | ||
287 | |||
288 | local build_type | ||
289 | ok, build_type = pcall(require, "luarocks.build." .. build_spec.type) | ||
290 | if not ok or not type(build_type) == "table" then | ||
291 | return nil, "Failed initializing build back-end for build type '"..build_spec.type.."': "..build_type | ||
292 | end | ||
293 | |||
294 | ok, err = build_type.run(rockspec) | ||
295 | if not ok then | ||
296 | return nil, "Build error: " .. err | ||
297 | end | ||
298 | end | ||
299 | |||
300 | if build_spec.install then | ||
301 | for id, install_dir in pairs(dirs) do | ||
302 | ok, err = install_files(build_spec.install[id], install_dir.name, install_dir.is_module_path, install_dir.perms) | ||
303 | if not ok then | ||
304 | return nil, err | ||
305 | end | ||
306 | end | ||
307 | end | ||
308 | |||
309 | local copy_directories = build_spec.copy_directories | ||
310 | local copying_default = false | ||
311 | if not copy_directories then | ||
312 | copy_directories = {"doc"} | ||
313 | copying_default = true | ||
314 | end | ||
315 | |||
316 | local any_docs = false | ||
317 | for _, copy_dir in pairs(copy_directories) do | ||
318 | if fs.is_dir(copy_dir) then | ||
319 | local dest = dir.path(path.install_dir(name, version), copy_dir) | ||
320 | fs.make_dir(dest) | ||
321 | fs.copy_contents(copy_dir, dest) | ||
322 | any_docs = true | ||
323 | else | ||
324 | if not copying_default then | ||
325 | return nil, "Directory '"..copy_dir.."' not found" | ||
326 | end | ||
327 | end | ||
328 | end | ||
329 | |||
330 | if not any_docs then | ||
331 | install_default_docs(name, version) | ||
332 | end | ||
333 | |||
334 | for _, d in pairs(dirs) do | ||
335 | fs.remove_dir_if_empty(d.name) | ||
336 | end | ||
337 | |||
338 | fs.pop_dir() | ||
339 | |||
340 | fs.copy(rockspec.local_filename, path.rockspec_file(name, version), cfg.perm_read) | ||
341 | if need_to_fetch then | ||
342 | fs.pop_dir() | ||
343 | end | ||
344 | |||
345 | ok, err = writer.make_rock_manifest(name, version) | ||
346 | if err then return nil, err end | ||
347 | |||
348 | ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) | ||
349 | if err then return nil, err end | ||
350 | |||
351 | util.remove_scheduled_function(rollback) | ||
352 | rollback = util.schedule_function(function() | ||
353 | repos.delete_version(name, version, deps_mode) | ||
354 | end) | ||
355 | |||
356 | ok, err = repos.run_hook(rockspec, "post_install") | ||
357 | if err then return nil, err end | ||
358 | |||
359 | util.announce_install(rockspec) | ||
360 | util.remove_scheduled_function(rollback) | ||
361 | return name, version | ||
362 | end | ||
363 | |||
364 | --- Build and install a rock. | 41 | --- Build and install a rock. |
365 | -- @param rock_file string: local or remote filename of a rock. | 42 | -- @param rock_file string: local or remote filename of a rock. |
366 | -- @param need_to_fetch boolean: true if sources need to be fetched, | 43 | -- @param need_to_fetch boolean: true if sources need to be fetched, |
@@ -371,7 +48,7 @@ end | |||
371 | -- @param build_only_deps boolean: true to build the listed dependencies only. | 48 | -- @param build_only_deps boolean: true to build the listed dependencies only. |
372 | -- @return boolean or (nil, string, [string]): True if build was successful, | 49 | -- @return boolean or (nil, string, [string]): True if build was successful, |
373 | -- or false and an error message and an optional error code. | 50 | -- or false and an error message and an optional error code. |
374 | function build.build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps) | 51 | local function build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps) |
375 | assert(type(rock_file) == "string") | 52 | assert(type(rock_file) == "string") |
376 | assert(type(need_to_fetch) == "boolean") | 53 | assert(type(need_to_fetch) == "boolean") |
377 | 54 | ||
@@ -393,13 +70,11 @@ local function do_build(name, version, deps_mode, build_only_deps) | |||
393 | if name:match("%.rockspec$") then | 70 | if name:match("%.rockspec$") then |
394 | return build.build_rockspec(name, true, false, deps_mode, build_only_deps) | 71 | return build.build_rockspec(name, true, false, deps_mode, build_only_deps) |
395 | elseif name:match("%.src%.rock$") then | 72 | elseif name:match("%.src%.rock$") then |
396 | return build.build_rock(name, false, deps_mode, build_only_deps) | 73 | return build_rock(name, false, deps_mode, build_only_deps) |
397 | elseif name:match("%.all%.rock$") then | 74 | elseif name:match("%.all%.rock$") then |
398 | local install = require("luarocks.install") | 75 | return build_rock(name, true, deps_mode, build_only_deps) |
399 | local install_fun = build_only_deps and install.install_binary_rock_deps or install.install_binary_rock | ||
400 | return install_fun(name, deps_mode) | ||
401 | elseif name:match("%.rock$") then | 76 | elseif name:match("%.rock$") then |
402 | return build.build_rock(name, true, deps_mode, build_only_deps) | 77 | return build_rock(name, true, deps_mode, build_only_deps) |
403 | elseif not name:match(dir.separator) then | 78 | elseif not name:match(dir.separator) then |
404 | local search = require("luarocks.search") | 79 | local search = require("luarocks.search") |
405 | return search.act_on_src_or_rockspec(do_build, name:lower(), version, nil, deps_mode, build_only_deps) | 80 | return search.act_on_src_or_rockspec(do_build, name:lower(), version, nil, deps_mode, build_only_deps) |
@@ -415,7 +90,7 @@ end | |||
415 | -- also be given. | 90 | -- also be given. |
416 | -- @return boolean or (nil, string, exitcode): True if build was successful; nil and an | 91 | -- @return boolean or (nil, string, exitcode): True if build was successful; nil and an |
417 | -- error message otherwise. exitcode is optionally returned. | 92 | -- error message otherwise. exitcode is optionally returned. |
418 | function build.command(flags, name, version) | 93 | function cmd_build.command(flags, name, version) |
419 | if type(name) ~= "string" then | 94 | if type(name) ~= "string" then |
420 | return nil, "Argument missing. "..util.see_help("build") | 95 | return nil, "Argument missing. "..util.see_help("build") |
421 | end | 96 | end |
@@ -440,4 +115,4 @@ function build.command(flags, name, version) | |||
440 | end | 115 | end |
441 | end | 116 | end |
442 | 117 | ||
443 | return build | 118 | return cmd_build |
diff --git a/src/luarocks/cmd/download.lua b/src/luarocks/cmd/download.lua index 557d1b65..9c119f6e 100644 --- a/src/luarocks/cmd/download.lua +++ b/src/luarocks/cmd/download.lua | |||
@@ -1,89 +1,27 @@ | |||
1 | 1 | ||
2 | --- Module implementing the luarocks "download" command. | 2 | --- Module implementing the luarocks "download" command. |
3 | -- Download a rock from the repository. | 3 | -- Download a rock from the repository. |
4 | local download = {} | 4 | local cmd_download = {} |
5 | 5 | ||
6 | local util = require("luarocks.util") | 6 | local util = require("luarocks.util") |
7 | local path = require("luarocks.path") | 7 | local download = require("luarocks.download") |
8 | local fetch = require("luarocks.fetch") | ||
9 | local search = require("luarocks.search") | ||
10 | local fs = require("luarocks.fs") | ||
11 | local dir = require("luarocks.dir") | ||
12 | local cfg = require("luarocks.core.cfg") | ||
13 | 8 | ||
14 | download.help_summary = "Download a specific rock file from a rocks server." | 9 | cmd_download.help_summary = "Download a specific rock file from a rocks server." |
15 | download.help_arguments = "[--all] [--arch=<arch> | --source | --rockspec] [<name> [<version>]]" | 10 | cmd_download.help_arguments = "[--all] [--arch=<arch> | --source | --rockspec] [<name> [<version>]]" |
16 | 11 | cmd_download.help = [[ | |
17 | download.help = [[ | ||
18 | --all Download all files if there are multiple matches. | 12 | --all Download all files if there are multiple matches. |
19 | --source Download .src.rock if available. | 13 | --source Download .src.rock if available. |
20 | --rockspec Download .rockspec if available. | 14 | --rockspec Download .rockspec if available. |
21 | --arch=<arch> Download rock for a specific architecture. | 15 | --arch=<arch> Download rock for a specific architecture. |
22 | ]] | 16 | ]] |
23 | 17 | ||
24 | local function get_file(filename) | ||
25 | local protocol, pathname = dir.split_url(filename) | ||
26 | if protocol == "file" then | ||
27 | local ok, err = fs.copy(pathname, fs.current_dir(), cfg.perm_read) | ||
28 | if ok then | ||
29 | return pathname | ||
30 | else | ||
31 | return nil, err | ||
32 | end | ||
33 | else | ||
34 | return fetch.fetch_url(filename) | ||
35 | end | ||
36 | end | ||
37 | |||
38 | function download.download(arch, name, version, all) | ||
39 | local query = search.make_query(name, version) | ||
40 | if arch then query.arch = arch end | ||
41 | local search_err | ||
42 | |||
43 | if all then | ||
44 | if name == "" then query.exact_name = false end | ||
45 | local results = search.search_repos(query) | ||
46 | local has_result = false | ||
47 | local all_ok = true | ||
48 | local any_err = "" | ||
49 | for name, result in pairs(results) do | ||
50 | for version, items in pairs(result) do | ||
51 | for _, item in ipairs(items) do | ||
52 | -- Ignore provided rocks. | ||
53 | if item.arch ~= "installed" then | ||
54 | has_result = true | ||
55 | local filename = path.make_url(item.repo, name, version, item.arch) | ||
56 | local ok, err = get_file(filename) | ||
57 | if not ok then | ||
58 | all_ok = false | ||
59 | any_err = any_err .. "\n" .. err | ||
60 | end | ||
61 | end | ||
62 | end | ||
63 | end | ||
64 | end | ||
65 | |||
66 | if has_result then | ||
67 | return all_ok, any_err | ||
68 | end | ||
69 | else | ||
70 | local url | ||
71 | url, search_err = search.find_suitable_rock(query) | ||
72 | if url then | ||
73 | return get_file(url) | ||
74 | end | ||
75 | end | ||
76 | return nil, "Could not find a result named "..name..(version and " "..version or "").. | ||
77 | (search_err and ": "..search_err or ".") | ||
78 | end | ||
79 | |||
80 | --- Driver function for the "download" command. | 18 | --- Driver function for the "download" command. |
81 | -- @param name string: a rock name. | 19 | -- @param name string: a rock name. |
82 | -- @param version string or nil: if the name of a package is given, a | 20 | -- @param version string or nil: if the name of a package is given, a |
83 | -- version may also be passed. | 21 | -- version may also be passed. |
84 | -- @return boolean or (nil, string): true if successful or nil followed | 22 | -- @return boolean or (nil, string): true if successful or nil followed |
85 | -- by an error message. | 23 | -- by an error message. |
86 | function download.command(flags, name, version) | 24 | function cmd_download.command(flags, name, version) |
87 | assert(type(version) == "string" or not version) | 25 | assert(type(version) == "string" or not version) |
88 | if type(name) ~= "string" and not flags["all"] then | 26 | if type(name) ~= "string" and not flags["all"] then |
89 | return nil, "Argument missing. "..util.see_help("download") | 27 | return nil, "Argument missing. "..util.see_help("download") |
@@ -104,4 +42,4 @@ function download.command(flags, name, version) | |||
104 | return dl and true, err | 42 | return dl and true, err |
105 | end | 43 | end |
106 | 44 | ||
107 | return download | 45 | return cmd_download |
diff --git a/src/luarocks/cmd/pack.lua b/src/luarocks/cmd/pack.lua index 655cbf37..e43e5b3f 100644 --- a/src/luarocks/cmd/pack.lua +++ b/src/luarocks/cmd/pack.lua | |||
@@ -1,168 +1,20 @@ | |||
1 | 1 | ||
2 | --- Module implementing the LuaRocks "pack" command. | 2 | --- Module implementing the LuaRocks "pack" command. |
3 | -- Creates a rock, packing sources or binaries. | 3 | -- Creates a rock, packing sources or binaries. |
4 | local pack = {} | 4 | local cmd_pack = {} |
5 | 5 | ||
6 | local unpack = unpack or table.unpack | ||
7 | |||
8 | local path = require("luarocks.path") | ||
9 | local repos = require("luarocks.repos") | ||
10 | local fetch = require("luarocks.fetch") | ||
11 | local fs = require("luarocks.fs") | ||
12 | local cfg = require("luarocks.core.cfg") | ||
13 | local util = require("luarocks.util") | 6 | local util = require("luarocks.util") |
14 | local dir = require("luarocks.dir") | 7 | local pack = require("luarocks.pack") |
15 | local manif = require("luarocks.manif") | ||
16 | local search = require("luarocks.search") | ||
17 | 8 | ||
18 | pack.help_summary = "Create a rock, packing sources or binaries." | 9 | cmd_pack.help_summary = "Create a rock, packing sources or binaries." |
19 | pack.help_arguments = "{<rockspec>|<name> [<version>]}" | 10 | cmd_pack.help_arguments = "{<rockspec>|<name> [<version>]}" |
20 | pack.help = [[ | 11 | cmd_pack.help = [[ |
21 | Argument may be a rockspec file, for creating a source rock, | 12 | Argument may be a rockspec file, for creating a source rock, |
22 | or the name of an installed package, for creating a binary rock. | 13 | or the name of an installed package, for creating a binary rock. |
23 | In the latter case, the app version may be given as a second | 14 | In the latter case, the app version may be given as a second |
24 | argument. | 15 | argument. |
25 | ]] | 16 | ]] |
26 | 17 | ||
27 | --- Create a source rock. | ||
28 | -- Packages a rockspec and its required source files in a rock | ||
29 | -- file with the .src.rock extension, which can later be built and | ||
30 | -- installed with the "build" command. | ||
31 | -- @param rockspec_file string: An URL or pathname for a rockspec file. | ||
32 | -- @return string or (nil, string): The filename of the resulting | ||
33 | -- .src.rock file; or nil and an error message. | ||
34 | function pack.pack_source_rock(rockspec_file) | ||
35 | assert(type(rockspec_file) == "string") | ||
36 | |||
37 | local rockspec, err = fetch.load_rockspec(rockspec_file) | ||
38 | if err then | ||
39 | return nil, "Error loading rockspec: "..err | ||
40 | end | ||
41 | rockspec_file = rockspec.local_filename | ||
42 | |||
43 | local name_version = rockspec.name .. "-" .. rockspec.version | ||
44 | local rock_file = fs.absolute_name(name_version .. ".src.rock") | ||
45 | |||
46 | local source_file, source_dir = fetch.fetch_sources(rockspec, false) | ||
47 | if not source_file then | ||
48 | return nil, source_dir | ||
49 | end | ||
50 | local ok, err = fs.change_dir(source_dir) | ||
51 | if not ok then return nil, err end | ||
52 | |||
53 | fs.delete(rock_file) | ||
54 | fs.copy(rockspec_file, source_dir, cfg.perm_read) | ||
55 | if not fs.zip(rock_file, dir.base_name(rockspec_file), dir.base_name(source_file)) then | ||
56 | return nil, "Failed packing "..rock_file | ||
57 | end | ||
58 | fs.pop_dir() | ||
59 | |||
60 | return rock_file | ||
61 | end | ||
62 | |||
63 | local function copy_back_files(name, version, file_tree, deploy_dir, pack_dir, perms) | ||
64 | local ok, err = fs.make_dir(pack_dir) | ||
65 | if not ok then return nil, err end | ||
66 | for file, sub in pairs(file_tree) do | ||
67 | local source = dir.path(deploy_dir, file) | ||
68 | local target = dir.path(pack_dir, file) | ||
69 | if type(sub) == "table" then | ||
70 | local ok, err = copy_back_files(name, version, sub, source, target) | ||
71 | if not ok then return nil, err end | ||
72 | else | ||
73 | local versioned = path.versioned_name(source, deploy_dir, name, version) | ||
74 | if fs.exists(versioned) then | ||
75 | fs.copy(versioned, target, perms) | ||
76 | else | ||
77 | fs.copy(source, target, perms) | ||
78 | end | ||
79 | end | ||
80 | end | ||
81 | return true | ||
82 | end | ||
83 | |||
84 | -- @param name string: Name of package to pack. | ||
85 | -- @param version string or nil: A version number may also be passed. | ||
86 | -- @param tree string or nil: An optional tree to pick the package from. | ||
87 | -- @return string or (nil, string): The filename of the resulting | ||
88 | -- .src.rock file; or nil and an error message. | ||
89 | local function do_pack_binary_rock(name, version, tree) | ||
90 | assert(type(name) == "string") | ||
91 | assert(type(version) == "string" or not version) | ||
92 | |||
93 | local repo, repo_url | ||
94 | name, version, repo, repo_url = search.pick_installed_rock(name, version, tree) | ||
95 | if not name then | ||
96 | return nil, version | ||
97 | end | ||
98 | |||
99 | local root = path.root_dir(repo_url) | ||
100 | local prefix = path.install_dir(name, version, root) | ||
101 | if not fs.exists(prefix) then | ||
102 | return nil, "'"..name.." "..version.."' does not seem to be an installed rock." | ||
103 | end | ||
104 | |||
105 | local rock_manifest, err = manif.load_rock_manifest(name, version, root) | ||
106 | if not rock_manifest then return nil, err end | ||
107 | |||
108 | local name_version = name .. "-" .. version | ||
109 | local rock_file = fs.absolute_name(name_version .. "."..cfg.arch..".rock") | ||
110 | |||
111 | local temp_dir = fs.make_temp_dir("pack") | ||
112 | fs.copy_contents(prefix, temp_dir) | ||
113 | |||
114 | local is_binary = false | ||
115 | if rock_manifest.lib then | ||
116 | local ok, err = copy_back_files(name, version, rock_manifest.lib, path.deploy_lib_dir(root), dir.path(temp_dir, "lib"), cfg.perm_exec) | ||
117 | if not ok then return nil, "Failed copying back files: " .. err end | ||
118 | is_binary = true | ||
119 | end | ||
120 | if rock_manifest.lua then | ||
121 | local ok, err = copy_back_files(name, version, rock_manifest.lua, path.deploy_lua_dir(root), dir.path(temp_dir, "lua"), cfg.perm_read) | ||
122 | if not ok then return nil, "Failed copying back files: " .. err end | ||
123 | end | ||
124 | |||
125 | local ok, err = fs.change_dir(temp_dir) | ||
126 | if not ok then return nil, err end | ||
127 | if not is_binary and not repos.has_binaries(name, version) then | ||
128 | rock_file = rock_file:gsub("%."..cfg.arch:gsub("%-","%%-").."%.", ".all.") | ||
129 | end | ||
130 | fs.delete(rock_file) | ||
131 | if not fs.zip(rock_file, unpack(fs.list_dir())) then | ||
132 | return nil, "Failed packing "..rock_file | ||
133 | end | ||
134 | fs.pop_dir() | ||
135 | fs.delete(temp_dir) | ||
136 | return rock_file | ||
137 | end | ||
138 | |||
139 | function pack.pack_binary_rock(name, version, cmd, ...) | ||
140 | |||
141 | -- The --pack-binary-rock option for "luarocks build" basically performs | ||
142 | -- "luarocks build" on a temporary tree and then "luarocks pack". The | ||
143 | -- alternative would require refactoring parts of luarocks.build and | ||
144 | -- luarocks.pack, which would save a few file operations: the idea would be | ||
145 | -- to shave off the final deploy steps from the build phase and the initial | ||
146 | -- collect steps from the pack phase. | ||
147 | |||
148 | local temp_dir, err = fs.make_temp_dir("luarocks-build-pack-"..dir.base_name(name)) | ||
149 | if not temp_dir then | ||
150 | return nil, "Failed creating temporary directory: "..err | ||
151 | end | ||
152 | util.schedule_function(fs.delete, temp_dir) | ||
153 | |||
154 | path.use_tree(temp_dir) | ||
155 | local ok, err = cmd(...) | ||
156 | if not ok then | ||
157 | return nil, err | ||
158 | end | ||
159 | local rname, rversion = path.parse_name(name) | ||
160 | if not rname then | ||
161 | rname, rversion = name, version | ||
162 | end | ||
163 | return do_pack_binary_rock(rname, rversion, temp_dir) | ||
164 | end | ||
165 | |||
166 | --- Driver function for the "pack" command. | 18 | --- Driver function for the "pack" command. |
167 | -- @param arg string: may be a rockspec file, for creating a source rock, | 19 | -- @param arg string: may be a rockspec file, for creating a source rock, |
168 | -- or the name of an installed package, for creating a binary rock. | 20 | -- or the name of an installed package, for creating a binary rock. |
@@ -170,7 +22,7 @@ end | |||
170 | -- version may also be passed. | 22 | -- version may also be passed. |
171 | -- @return boolean or (nil, string): true if successful or nil followed | 23 | -- @return boolean or (nil, string): true if successful or nil followed |
172 | -- by an error message. | 24 | -- by an error message. |
173 | function pack.command(flags, arg, version) | 25 | function cmd_pack.command(flags, arg, version) |
174 | assert(type(version) == "string" or not version) | 26 | assert(type(version) == "string" or not version) |
175 | if type(arg) ~= "string" then | 27 | if type(arg) ~= "string" then |
176 | return nil, "Argument missing. "..util.see_help("pack") | 28 | return nil, "Argument missing. "..util.see_help("pack") |
@@ -180,7 +32,7 @@ function pack.command(flags, arg, version) | |||
180 | if arg:match(".*%.rockspec") then | 32 | if arg:match(".*%.rockspec") then |
181 | file, err = pack.pack_source_rock(arg) | 33 | file, err = pack.pack_source_rock(arg) |
182 | else | 34 | else |
183 | file, err = do_pack_binary_rock(arg:lower(), version, flags["tree"]) | 35 | file, err = pack.pack_installed_rock(arg:lower(), version, flags["tree"]) |
184 | end | 36 | end |
185 | if err then | 37 | if err then |
186 | return nil, err | 38 | return nil, err |
@@ -190,4 +42,4 @@ function pack.command(flags, arg, version) | |||
190 | end | 42 | end |
191 | end | 43 | end |
192 | 44 | ||
193 | return pack | 45 | return cmd_pack |
diff --git a/src/luarocks/cmd/remove.lua b/src/luarocks/cmd/remove.lua index e7f37604..08d49058 100644 --- a/src/luarocks/cmd/remove.lua +++ b/src/luarocks/cmd/remove.lua | |||
@@ -1,20 +1,18 @@ | |||
1 | 1 | ||
2 | --- Module implementing the LuaRocks "remove" command. | 2 | --- Module implementing the LuaRocks "remove" command. |
3 | -- Uninstalls rocks. | 3 | -- Uninstalls rocks. |
4 | local remove = {} | 4 | local cmd_remove = {} |
5 | 5 | ||
6 | local search = require("luarocks.search") | 6 | local remove = require("luarocks.remove") |
7 | local deps = require("luarocks.deps") | ||
8 | local fetch = require("luarocks.fetch") | ||
9 | local repos = require("luarocks.repos") | ||
10 | local path = require("luarocks.path") | ||
11 | local util = require("luarocks.util") | 7 | local util = require("luarocks.util") |
12 | local cfg = require("luarocks.core.cfg") | 8 | local cfg = require("luarocks.core.cfg") |
13 | local fs = require("luarocks.fs") | 9 | local fs = require("luarocks.fs") |
10 | local search = require("luarocks.search") | ||
11 | local path = require("luarocks.path") | ||
14 | 12 | ||
15 | remove.help_summary = "Uninstall a rock." | 13 | cmd_remove.help_summary = "Uninstall a rock." |
16 | remove.help_arguments = "[--force|--force-fast] <name> [<version>]" | 14 | cmd_remove.help_arguments = "[--force|--force-fast] <name> [<version>]" |
17 | remove.help = [[ | 15 | cmd_remove.help = [[ |
18 | Argument is the name of a rock to be uninstalled. | 16 | Argument is the name of a rock to be uninstalled. |
19 | If a version is not given, try to remove all versions at once. | 17 | If a version is not given, try to remove all versions at once. |
20 | Will only perform the removal if it does not break dependencies. | 18 | Will only perform the removal if it does not break dependencies. |
@@ -24,110 +22,6 @@ use --force-fast. | |||
24 | 22 | ||
25 | ]]..util.deps_mode_help() | 23 | ]]..util.deps_mode_help() |
26 | 24 | ||
27 | --- Obtain a list of packages that depend on the given set of packages | ||
28 | -- (where all packages of the set are versions of one program). | ||
29 | -- @param name string: the name of a program | ||
30 | -- @param versions array of string: the versions to be deleted. | ||
31 | -- @return array of string: an empty table if no packages depend on any | ||
32 | -- of the given list, or an array of strings in "name/version" format. | ||
33 | local function check_dependents(name, versions, deps_mode) | ||
34 | local dependents = {} | ||
35 | local blacklist = {} | ||
36 | blacklist[name] = {} | ||
37 | for version, _ in pairs(versions) do | ||
38 | blacklist[name][version] = true | ||
39 | end | ||
40 | local local_rocks = {} | ||
41 | local query_all = search.make_query("") | ||
42 | query_all.exact_name = false | ||
43 | search.manifest_search(local_rocks, cfg.rocks_dir, query_all) | ||
44 | local_rocks[name] = nil | ||
45 | for rock_name, rock_versions in pairs(local_rocks) do | ||
46 | for rock_version, _ in pairs(rock_versions) do | ||
47 | local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version)) | ||
48 | if rockspec then | ||
49 | local _, missing = deps.match_deps(rockspec, blacklist, deps_mode) | ||
50 | if missing[name] then | ||
51 | table.insert(dependents, { name = rock_name, version = rock_version }) | ||
52 | end | ||
53 | end | ||
54 | end | ||
55 | end | ||
56 | return dependents | ||
57 | end | ||
58 | |||
59 | --- Delete given versions of a program. | ||
60 | -- @param name string: the name of a program | ||
61 | -- @param versions array of string: the versions to be deleted. | ||
62 | -- @param deps_mode: string: Which trees to check dependencies for: | ||
63 | -- "one" for the current default tree, "all" for all trees, | ||
64 | -- "order" for all trees with priority >= the current default, "none" for no trees. | ||
65 | -- @return boolean or (nil, string): true on success or nil and an error message. | ||
66 | local function delete_versions(name, versions, deps_mode) | ||
67 | |||
68 | for version, _ in pairs(versions) do | ||
69 | util.printout("Removing "..name.." "..version.."...") | ||
70 | local ok, err = repos.delete_version(name, version, deps_mode) | ||
71 | if not ok then return nil, err end | ||
72 | end | ||
73 | |||
74 | return true | ||
75 | end | ||
76 | |||
77 | function remove.remove_search_results(results, name, deps_mode, force, fast) | ||
78 | local versions = results[name] | ||
79 | |||
80 | local version = next(versions) | ||
81 | local second = next(versions, version) | ||
82 | |||
83 | local dependents = {} | ||
84 | if not fast then | ||
85 | util.printout("Checking stability of dependencies in the absence of") | ||
86 | util.printout(name.." "..table.concat(util.keys(versions), ", ").."...") | ||
87 | util.printout() | ||
88 | dependents = check_dependents(name, versions, deps_mode) | ||
89 | end | ||
90 | |||
91 | if #dependents > 0 then | ||
92 | if force or fast then | ||
93 | util.printerr("The following packages may be broken by this forced removal:") | ||
94 | for _, dependent in ipairs(dependents) do | ||
95 | util.printerr(dependent.name.." "..dependent.version) | ||
96 | end | ||
97 | util.printerr() | ||
98 | else | ||
99 | if not second then | ||
100 | util.printerr("Will not remove "..name.." "..version..".") | ||
101 | util.printerr("Removing it would break dependencies for: ") | ||
102 | else | ||
103 | util.printerr("Will not remove installed versions of "..name..".") | ||
104 | util.printerr("Removing them would break dependencies for: ") | ||
105 | end | ||
106 | for _, dependent in ipairs(dependents) do | ||
107 | util.printerr(dependent.name.." "..dependent.version) | ||
108 | end | ||
109 | util.printerr() | ||
110 | util.printerr("Use --force to force removal (warning: this may break modules).") | ||
111 | return nil, "Failed removing." | ||
112 | end | ||
113 | end | ||
114 | |||
115 | local ok, err = delete_versions(name, versions, deps_mode) | ||
116 | if not ok then return nil, err end | ||
117 | |||
118 | util.printout("Removal successful.") | ||
119 | return true | ||
120 | end | ||
121 | |||
122 | function remove.remove_other_versions(name, version, force, fast) | ||
123 | local results = {} | ||
124 | search.manifest_search(results, cfg.rocks_dir, { name = name, exact_name = true, constraints = {{ op = "~=", version = version}} }) | ||
125 | if results[name] then | ||
126 | return remove.remove_search_results(results, name, cfg.deps_mode, force, fast) | ||
127 | end | ||
128 | return true | ||
129 | end | ||
130 | |||
131 | --- Driver function for the "remove" command. | 25 | --- Driver function for the "remove" command. |
132 | -- @param name string: name of a rock. If a version is given, refer to | 26 | -- @param name string: name of a rock. If a version is given, refer to |
133 | -- a specific version; otherwise, try to remove all versions. | 27 | -- a specific version; otherwise, try to remove all versions. |
@@ -135,7 +29,7 @@ end | |||
135 | -- may also be given. | 29 | -- may also be given. |
136 | -- @return boolean or (nil, string, exitcode): True if removal was | 30 | -- @return boolean or (nil, string, exitcode): True if removal was |
137 | -- successful, nil and an error message otherwise. exitcode is optionally returned. | 31 | -- successful, nil and an error message otherwise. exitcode is optionally returned. |
138 | function remove.command(flags, name, version) | 32 | function cmd_remove.command(flags, name, version) |
139 | if type(name) ~= "string" then | 33 | if type(name) ~= "string" then |
140 | return nil, "Argument missing. "..util.see_help("remove") | 34 | return nil, "Argument missing. "..util.see_help("remove") |
141 | end | 35 | end |
@@ -162,4 +56,4 @@ function remove.command(flags, name, version) | |||
162 | return remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"]) | 56 | return remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"]) |
163 | end | 57 | end |
164 | 58 | ||
165 | return remove | 59 | return cmd_remove |
diff --git a/src/luarocks/cmd/search.lua b/src/luarocks/cmd/search.lua index 44eff694..109fe805 100644 --- a/src/luarocks/cmd/search.lua +++ b/src/luarocks/cmd/search.lua | |||
@@ -3,11 +3,6 @@ | |||
3 | -- Queries LuaRocks servers. | 3 | -- Queries LuaRocks servers. |
4 | local search = {} | 4 | local search = {} |
5 | 5 | ||
6 | |||
7 | local dir = require("luarocks.dir") | ||
8 | local path = require("luarocks.path") | ||
9 | local manif = require("luarocks.manif") | ||
10 | local deps = require("luarocks.deps") | ||
11 | local cfg = require("luarocks.core.cfg") | 6 | local cfg = require("luarocks.core.cfg") |
12 | local util = require("luarocks.util") | 7 | local util = require("luarocks.util") |
13 | 8 | ||
@@ -22,354 +17,6 @@ search.help = [[ | |||
22 | this platform, do not filter by name. | 17 | this platform, do not filter by name. |
23 | ]] | 18 | ]] |
24 | 19 | ||
25 | --- Convert the arch field of a query table to table format. | ||
26 | -- @param query table: A query table. | ||
27 | local function query_arch_as_table(query) | ||
28 | local format = type(query.arch) | ||
29 | if format == "table" then | ||
30 | return | ||
31 | elseif format == "nil" then | ||
32 | local accept = {} | ||
33 | accept["src"] = true | ||
34 | accept["all"] = true | ||
35 | accept["rockspec"] = true | ||
36 | accept["installed"] = true | ||
37 | accept[cfg.arch] = true | ||
38 | query.arch = accept | ||
39 | elseif format == "string" then | ||
40 | local accept = {} | ||
41 | for a in query.arch:gmatch("[%w_-]+") do | ||
42 | accept[a] = true | ||
43 | end | ||
44 | query.arch = accept | ||
45 | end | ||
46 | end | ||
47 | |||
48 | --- Store a search result (a rock or rockspec) in the results table. | ||
49 | -- @param results table: The results table, where keys are package names and | ||
50 | -- values are tables matching version strings to arrays of | ||
51 | -- tables with fields "arch" and "repo". | ||
52 | -- @param name string: Package name. | ||
53 | -- @param version string: Package version. | ||
54 | -- @param arch string: Architecture of rock ("all", "src" or platform | ||
55 | -- identifier), "rockspec" or "installed" | ||
56 | -- @param repo string: Pathname of a local repository of URL of | ||
57 | -- rocks server. | ||
58 | local function store_result(results, name, version, arch, repo) | ||
59 | assert(type(results) == "table") | ||
60 | assert(type(name) == "string") | ||
61 | assert(type(version) == "string") | ||
62 | assert(type(arch) == "string") | ||
63 | assert(type(repo) == "string") | ||
64 | |||
65 | if not results[name] then results[name] = {} end | ||
66 | if not results[name][version] then results[name][version] = {} end | ||
67 | table.insert(results[name][version], { | ||
68 | arch = arch, | ||
69 | repo = repo | ||
70 | }) | ||
71 | end | ||
72 | |||
73 | --- Test the name field of a query. | ||
74 | -- If query has a boolean field exact_name set to false, | ||
75 | -- then substring match is performed; otherwise, exact string | ||
76 | -- comparison is done. | ||
77 | -- @param query table: A query in dependency table format. | ||
78 | -- @param name string: A package name. | ||
79 | -- @return boolean: True if names match, false otherwise. | ||
80 | local function match_name(query, name) | ||
81 | assert(type(query) == "table") | ||
82 | assert(type(name) == "string") | ||
83 | if query.exact_name == false then | ||
84 | return name:find(query.name, 0, true) and true or false | ||
85 | else | ||
86 | return name == query.name | ||
87 | end | ||
88 | end | ||
89 | |||
90 | --- Store a match in a results table if version matches query. | ||
91 | -- Name, version, arch and repository path are stored in a given | ||
92 | -- table, optionally checking if version and arch (if given) match | ||
93 | -- a query. | ||
94 | -- @param results table: The results table, where keys are package names and | ||
95 | -- values are tables matching version strings to arrays of | ||
96 | -- tables with fields "arch" and "repo". | ||
97 | -- @param repo string: URL or pathname of the repository. | ||
98 | -- @param name string: The name of the package being tested. | ||
99 | -- @param version string: The version of the package being tested. | ||
100 | -- @param arch string: The arch of the package being tested. | ||
101 | -- @param query table: A table describing the query in dependency | ||
102 | -- format (for example, {name = "filesystem", exact_name = false, | ||
103 | -- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). | ||
104 | -- If the arch field is omitted, the local architecture (cfg.arch) | ||
105 | -- is used. The special value "any" is also recognized, returning all | ||
106 | -- matches regardless of architecture. | ||
107 | local function store_if_match(results, repo, name, version, arch, query) | ||
108 | if match_name(query, name) then | ||
109 | if query.arch[arch] or query.arch["any"] then | ||
110 | if deps.match_constraints(deps.parse_version(version), query.constraints) then | ||
111 | store_result(results, name, version, arch, repo) | ||
112 | end | ||
113 | end | ||
114 | end | ||
115 | end | ||
116 | |||
117 | --- Perform search on a local repository. | ||
118 | -- @param repo string: The pathname of the local repository. | ||
119 | -- @param query table: A table describing the query in dependency | ||
120 | -- format (for example, {name = "filesystem", exact_name = false, | ||
121 | -- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). | ||
122 | -- If the arch field is omitted, the local architecture (cfg.arch) | ||
123 | -- is used. The special value "any" is also recognized, returning all | ||
124 | -- matches regardless of architecture. | ||
125 | -- @param results table or nil: If given, this table will store the | ||
126 | -- results; if not given, a new table will be created. | ||
127 | -- @return table: The results table, where keys are package names and | ||
128 | -- values are tables matching version strings to arrays of | ||
129 | -- tables with fields "arch" and "repo". | ||
130 | -- If a table was given in the "results" parameter, that is the result value. | ||
131 | function search.disk_search(repo, query, results) | ||
132 | assert(type(repo) == "string") | ||
133 | assert(type(query) == "table") | ||
134 | assert(type(results) == "table" or not results) | ||
135 | |||
136 | local fs = require("luarocks.fs") | ||
137 | |||
138 | if not results then | ||
139 | results = {} | ||
140 | end | ||
141 | query_arch_as_table(query) | ||
142 | |||
143 | for name in fs.dir(repo) do | ||
144 | local pathname = dir.path(repo, name) | ||
145 | local rname, rversion, rarch = path.parse_name(name) | ||
146 | |||
147 | if rname and (pathname:match(".rockspec$") or pathname:match(".rock$")) then | ||
148 | store_if_match(results, repo, rname, rversion, rarch, query) | ||
149 | elseif fs.is_dir(pathname) then | ||
150 | for version in fs.dir(pathname) do | ||
151 | if version:match("-%d+$") then | ||
152 | store_if_match(results, repo, name, version, "installed", query) | ||
153 | end | ||
154 | end | ||
155 | end | ||
156 | end | ||
157 | return results | ||
158 | end | ||
159 | |||
160 | --- Perform search on a rocks server or tree. | ||
161 | -- @param results table: The results table, where keys are package names and | ||
162 | -- values are tables matching version strings to arrays of | ||
163 | -- tables with fields "arch" and "repo". | ||
164 | -- @param repo string: The URL of a rocks server or | ||
165 | -- the pathname of a rocks tree (as returned by path.rocks_dir()). | ||
166 | -- @param query table: A table describing the query in dependency | ||
167 | -- format (for example, {name = "filesystem", exact_name = false, | ||
168 | -- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). | ||
169 | -- If the arch field is omitted, the local architecture (cfg.arch) | ||
170 | -- is used. The special value "any" is also recognized, returning all | ||
171 | -- matches regardless of architecture. | ||
172 | -- @param lua_version string: Lua version in "5.x" format, defaults to installed version. | ||
173 | -- @return true or, in case of errors, nil, an error message and an optional error code. | ||
174 | function search.manifest_search(results, repo, query, lua_version) | ||
175 | assert(type(results) == "table") | ||
176 | assert(type(repo) == "string") | ||
177 | assert(type(query) == "table") | ||
178 | |||
179 | query_arch_as_table(query) | ||
180 | local manifest, err, errcode = manif.load_manifest(repo, lua_version) | ||
181 | if not manifest then | ||
182 | return nil, err, errcode | ||
183 | end | ||
184 | for name, versions in pairs(manifest.repository) do | ||
185 | for version, items in pairs(versions) do | ||
186 | for _, item in ipairs(items) do | ||
187 | store_if_match(results, repo, name, version, item.arch, query) | ||
188 | end | ||
189 | end | ||
190 | end | ||
191 | return true | ||
192 | end | ||
193 | |||
194 | --- Search on all configured rocks servers. | ||
195 | -- @param query table: A dependency query. | ||
196 | -- @param lua_version string: Lua version in "5.x" format, defaults to installed version. | ||
197 | -- @return table: A table where keys are package names | ||
198 | -- and values are tables matching version strings to arrays of | ||
199 | -- tables with fields "arch" and "repo". | ||
200 | function search.search_repos(query, lua_version) | ||
201 | assert(type(query) == "table") | ||
202 | |||
203 | local results = {} | ||
204 | for _, repo in ipairs(cfg.rocks_servers) do | ||
205 | if not cfg.disabled_servers[repo] then | ||
206 | if type(repo) == "string" then | ||
207 | repo = { repo } | ||
208 | end | ||
209 | for _, mirror in ipairs(repo) do | ||
210 | local protocol, pathname = dir.split_url(mirror) | ||
211 | if protocol == "file" then | ||
212 | mirror = pathname | ||
213 | end | ||
214 | local ok, err, errcode = search.manifest_search(results, mirror, query, lua_version) | ||
215 | if errcode == "network" then | ||
216 | cfg.disabled_servers[repo] = true | ||
217 | end | ||
218 | if ok then | ||
219 | break | ||
220 | else | ||
221 | util.warning("Failed searching manifest: "..err) | ||
222 | end | ||
223 | end | ||
224 | end | ||
225 | end | ||
226 | -- search through rocks in cfg.rocks_provided | ||
227 | local provided_repo = "provided by VM or rocks_provided" | ||
228 | for name, versions in pairs(cfg.rocks_provided) do | ||
229 | store_if_match(results, provided_repo, name, versions, "installed", query) | ||
230 | end | ||
231 | return results | ||
232 | end | ||
233 | |||
234 | --- Prepare a query in dependency table format. | ||
235 | -- @param name string: The query name. | ||
236 | -- @param version string or nil: | ||
237 | -- @return table: A query in table format | ||
238 | function search.make_query(name, version) | ||
239 | assert(type(name) == "string") | ||
240 | assert(type(version) == "string" or not version) | ||
241 | |||
242 | local query = { | ||
243 | name = name, | ||
244 | constraints = {} | ||
245 | } | ||
246 | if version then | ||
247 | table.insert(query.constraints, { op = "==", version = deps.parse_version(version)}) | ||
248 | end | ||
249 | return query | ||
250 | end | ||
251 | |||
252 | --- Get the URL for the latest in a set of versions. | ||
253 | -- @param name string: The package name to be used in the URL. | ||
254 | -- @param versions table: An array of version informations, as stored | ||
255 | -- in search results tables. | ||
256 | -- @return string or nil: the URL for the latest version if one could | ||
257 | -- be picked, or nil. | ||
258 | local function pick_latest_version(name, versions) | ||
259 | assert(type(name) == "string") | ||
260 | assert(type(versions) == "table") | ||
261 | |||
262 | local vtables = {} | ||
263 | for v, _ in pairs(versions) do | ||
264 | table.insert(vtables, deps.parse_version(v)) | ||
265 | end | ||
266 | table.sort(vtables) | ||
267 | local version = vtables[#vtables].string | ||
268 | local items = versions[version] | ||
269 | if items then | ||
270 | local pick = 1 | ||
271 | for i, item in ipairs(items) do | ||
272 | if (item.arch == 'src' and items[pick].arch == 'rockspec') | ||
273 | or (item.arch ~= 'src' and item.arch ~= 'rockspec') then | ||
274 | pick = i | ||
275 | end | ||
276 | end | ||
277 | return path.make_url(items[pick].repo, name, version, items[pick].arch) | ||
278 | end | ||
279 | return nil | ||
280 | end | ||
281 | |||
282 | -- Find out which other Lua versions provide rock versions matching a query, | ||
283 | -- @param query table: A dependency query matching a single rock. | ||
284 | -- @return table: array of Lua versions supported, in "5.x" format. | ||
285 | local function supported_lua_versions(query) | ||
286 | local results = {} | ||
287 | |||
288 | for lua_version in util.lua_versions() do | ||
289 | if lua_version ~= cfg.lua_version then | ||
290 | if search.search_repos(query, lua_version)[query.name] then | ||
291 | table.insert(results, lua_version) | ||
292 | end | ||
293 | end | ||
294 | end | ||
295 | |||
296 | return results | ||
297 | end | ||
298 | |||
299 | --- Attempt to get a single URL for a given search for a rock. | ||
300 | -- @param query table: A dependency query matching a single rock. | ||
301 | -- @return string or (nil, string): URL for latest matching version | ||
302 | -- of the rock if it was found, or nil followed by an error message. | ||
303 | function search.find_suitable_rock(query) | ||
304 | assert(type(query) == "table") | ||
305 | |||
306 | local results = search.search_repos(query) | ||
307 | local first_rock = next(results) | ||
308 | if not first_rock then | ||
309 | if cfg.rocks_provided[query.name] == nil then | ||
310 | -- Check if constraints are satisfiable with other Lua versions. | ||
311 | local lua_versions = supported_lua_versions(query) | ||
312 | |||
313 | if #lua_versions ~= 0 then | ||
314 | -- Build a nice message in "only Lua 5.x and 5.y but not 5.z." format | ||
315 | for i, lua_version in ipairs(lua_versions) do | ||
316 | lua_versions[i] = "Lua "..lua_version | ||
317 | end | ||
318 | |||
319 | local versions_message = "only "..table.concat(lua_versions, " and ").. | ||
320 | " but not Lua "..cfg.lua_version.."." | ||
321 | |||
322 | if #query.constraints == 0 then | ||
323 | return nil, query.name.." supports "..versions_message | ||
324 | elseif #query.constraints == 1 and query.constraints[1].op == "==" then | ||
325 | return nil, query.name.." "..query.constraints[1].version.string.." supports "..versions_message | ||
326 | else | ||
327 | return nil, "Matching "..query.name.." versions support "..versions_message | ||
328 | end | ||
329 | end | ||
330 | end | ||
331 | |||
332 | return nil, "No results matching query were found." | ||
333 | elseif next(results, first_rock) then | ||
334 | -- Shouldn't happen as query must match only one package. | ||
335 | return nil, "Several rocks matched query." | ||
336 | elseif cfg.rocks_provided[query.name] ~= nil then | ||
337 | -- Do not install versions listed in cfg.rocks_provided. | ||
338 | return nil, "Rock "..query.name.." "..cfg.rocks_provided[query.name].. | ||
339 | " was found but it is provided by VM or 'rocks_provided' in the config file." | ||
340 | else | ||
341 | return pick_latest_version(query.name, results[first_rock]) | ||
342 | end | ||
343 | end | ||
344 | |||
345 | --- Print a list of rocks/rockspecs on standard output. | ||
346 | -- @param results table: A table where keys are package names and versions | ||
347 | -- are tables matching version strings to an array of rocks servers. | ||
348 | -- @param porcelain boolean or nil: A flag to force machine-friendly output. | ||
349 | function search.print_results(results, porcelain) | ||
350 | assert(type(results) == "table") | ||
351 | assert(type(porcelain) == "boolean" or not porcelain) | ||
352 | |||
353 | for package, versions in util.sortedpairs(results) do | ||
354 | if not porcelain then | ||
355 | util.printout(package) | ||
356 | end | ||
357 | for version, repos in util.sortedpairs(versions, deps.compare_versions) do | ||
358 | for _, repo in ipairs(repos) do | ||
359 | repo.repo = dir.normalize(repo.repo) | ||
360 | if porcelain then | ||
361 | util.printout(package, version, repo.arch, repo.repo) | ||
362 | else | ||
363 | util.printout(" "..version.." ("..repo.arch..") - "..repo.repo) | ||
364 | end | ||
365 | end | ||
366 | end | ||
367 | if not porcelain then | ||
368 | util.printout() | ||
369 | end | ||
370 | end | ||
371 | end | ||
372 | |||
373 | --- Splits a list of search results into two lists, one for "source" results | 20 | --- Splits a list of search results into two lists, one for "source" results |
374 | -- to be used with the "build" command, and one for "binary" results to be | 21 | -- to be used with the "build" command, and one for "binary" results to be |
375 | -- used with the "install" command. | 22 | -- used with the "install" command. |
@@ -385,69 +32,13 @@ local function split_source_and_binary_results(results) | |||
385 | if repo.arch == "all" or repo.arch == cfg.arch then | 32 | if repo.arch == "all" or repo.arch == cfg.arch then |
386 | where = binaries | 33 | where = binaries |
387 | end | 34 | end |
388 | store_result(where, name, version, repo.arch, repo.repo) | 35 | search.store_result(where, name, version, repo.arch, repo.repo) |
389 | end | 36 | end |
390 | end | 37 | end |
391 | end | 38 | end |
392 | return sources, binaries | 39 | return sources, binaries |
393 | end | 40 | end |
394 | 41 | ||
395 | --- Given a name and optionally a version, try to find in the rocks | ||
396 | -- servers a single .src.rock or .rockspec file that satisfies | ||
397 | -- the request, and run the given function on it; or display to the | ||
398 | -- user possibilities if it couldn't narrow down a single match. | ||
399 | -- @param action function: A function that takes a .src.rock or | ||
400 | -- .rockspec URL as a parameter. | ||
401 | -- @param name string: A rock name | ||
402 | -- @param version string or nil: A version number may also be given. | ||
403 | -- @return The result of the action function, or nil and an error message. | ||
404 | function search.act_on_src_or_rockspec(action, name, version, ...) | ||
405 | assert(type(action) == "function") | ||
406 | assert(type(name) == "string") | ||
407 | assert(type(version) == "string" or not version) | ||
408 | |||
409 | local query = search.make_query(name, version) | ||
410 | query.arch = "src|rockspec" | ||
411 | local url, err = search.find_suitable_rock(query) | ||
412 | if not url then | ||
413 | return nil, "Could not find a result named "..name..(version and " "..version or "")..": "..err | ||
414 | end | ||
415 | return action(url, ...) | ||
416 | end | ||
417 | |||
418 | function search.pick_installed_rock(name, version, given_tree) | ||
419 | local results = {} | ||
420 | local query = search.make_query(name, version) | ||
421 | query.exact_name = true | ||
422 | local tree_map = {} | ||
423 | local trees = cfg.rocks_trees | ||
424 | if given_tree then | ||
425 | trees = { given_tree } | ||
426 | end | ||
427 | for _, tree in ipairs(trees) do | ||
428 | local rocks_dir = path.rocks_dir(tree) | ||
429 | tree_map[rocks_dir] = tree | ||
430 | search.manifest_search(results, rocks_dir, query) | ||
431 | end | ||
432 | |||
433 | if not next(results) then -- | ||
434 | return nil,"cannot find package "..name.." "..(version or "").."\nUse 'list' to find installed rocks." | ||
435 | end | ||
436 | |||
437 | version = nil | ||
438 | local repo_url | ||
439 | local package, versions = util.sortedpairs(results)() | ||
440 | --question: what do we do about multiple versions? This should | ||
441 | --give us the latest version on the last repo (which is usually the global one) | ||
442 | for vs, repositories in util.sortedpairs(versions, deps.compare_versions) do | ||
443 | if not version then version = vs end | ||
444 | for _, rp in ipairs(repositories) do repo_url = rp.repo end | ||
445 | end | ||
446 | |||
447 | local repo = tree_map[repo_url] | ||
448 | return name, version, repo, repo_url | ||
449 | end | ||
450 | |||
451 | --- Driver function for "search" command. | 42 | --- Driver function for "search" command. |
452 | -- @param name string: A substring of a rock name to search. | 43 | -- @param name string: A substring of a rock name to search. |
453 | -- @param version string or nil: a version may also be passed. | 44 | -- @param version string or nil: a version may also be passed. |