aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHisham <hisham@gobolinux.org>2016-10-28 20:15:15 -0200
committerHisham <hisham@gobolinux.org>2016-10-28 20:15:15 -0200
commit75902ff48a5bdc2cd00b492deb9f8aaa1d35bd3d (patch)
treedef839023a6520ad1c721d4ab955e29196770240 /src
parent825e722029fbc93976c2d882dd21f444f9bffda0 (diff)
downloadluarocks-75902ff48a5bdc2cd00b492deb9f8aaa1d35bd3d.tar.gz
luarocks-75902ff48a5bdc2cd00b492deb9f8aaa1d35bd3d.tar.bz2
luarocks-75902ff48a5bdc2cd00b492deb9f8aaa1d35bd3d.zip
Duplicate files to preserve git-blame
Diffstat (limited to 'src')
-rw-r--r--src/luarocks/base/build.lua (renamed from src/luarocks/build.lua)0
-rw-r--r--src/luarocks/base/config_cmd.lua (renamed from src/luarocks/config_cmd.lua)0
-rw-r--r--src/luarocks/base/doc.lua (renamed from src/luarocks/doc.lua)0
-rw-r--r--src/luarocks/base/download.lua (renamed from src/luarocks/download.lua)0
-rw-r--r--src/luarocks/base/help.lua (renamed from src/luarocks/help.lua)0
-rw-r--r--src/luarocks/base/install.lua (renamed from src/luarocks/install.lua)0
-rw-r--r--src/luarocks/base/lint.lua (renamed from src/luarocks/lint.lua)0
-rw-r--r--src/luarocks/base/list.lua (renamed from src/luarocks/list.lua)0
-rw-r--r--src/luarocks/base/make.lua (renamed from src/luarocks/make.lua)0
-rw-r--r--src/luarocks/base/new_version.lua (renamed from src/luarocks/new_version.lua)0
-rw-r--r--src/luarocks/base/pack.lua (renamed from src/luarocks/pack.lua)0
-rw-r--r--src/luarocks/base/path_cmd.lua (renamed from src/luarocks/path_cmd.lua)0
-rw-r--r--src/luarocks/base/purge.lua (renamed from src/luarocks/purge.lua)0
-rw-r--r--src/luarocks/base/remove.lua (renamed from src/luarocks/remove.lua)0
-rw-r--r--src/luarocks/base/search.lua (renamed from src/luarocks/search.lua)0
-rw-r--r--src/luarocks/base/show.lua (renamed from src/luarocks/show.lua)0
-rw-r--r--src/luarocks/base/unpack.lua (renamed from src/luarocks/unpack.lua)0
-rw-r--r--src/luarocks/base/upload.lua (renamed from src/luarocks/upload.lua)0
-rw-r--r--src/luarocks/base/write_rockspec.lua (renamed from src/luarocks/write_rockspec.lua)0
-rw-r--r--src/luarocks/cmd/build.lua443
-rw-r--r--src/luarocks/cmd/config_cmd.lua71
-rw-r--r--src/luarocks/cmd/doc.lua155
-rw-r--r--src/luarocks/cmd/download.lua107
-rw-r--r--src/luarocks/cmd/help.lua117
-rw-r--r--src/luarocks/cmd/install.lua183
-rw-r--r--src/luarocks/cmd/lint.lua53
-rw-r--r--src/luarocks/cmd/list.lua95
-rw-r--r--src/luarocks/cmd/make.lua86
-rw-r--r--src/luarocks/cmd/new_version.lua199
-rw-r--r--src/luarocks/cmd/pack.lua193
-rw-r--r--src/luarocks/cmd/path_cmd.lua68
-rw-r--r--src/luarocks/cmd/purge.lua77
-rw-r--r--src/luarocks/cmd/remove.lua165
-rw-r--r--src/luarocks/cmd/search.lua482
-rw-r--r--src/luarocks/cmd/show.lua158
-rw-r--r--src/luarocks/cmd/unpack.lua164
-rw-r--r--src/luarocks/cmd/upload.lua94
-rw-r--r--src/luarocks/cmd/write_rockspec.lua376
38 files changed, 3286 insertions, 0 deletions
diff --git a/src/luarocks/build.lua b/src/luarocks/base/build.lua
index f3b054d2..f3b054d2 100644
--- a/src/luarocks/build.lua
+++ b/src/luarocks/base/build.lua
diff --git a/src/luarocks/config_cmd.lua b/src/luarocks/base/config_cmd.lua
index b68f7898..b68f7898 100644
--- a/src/luarocks/config_cmd.lua
+++ b/src/luarocks/base/config_cmd.lua
diff --git a/src/luarocks/doc.lua b/src/luarocks/base/doc.lua
index 5d521276..5d521276 100644
--- a/src/luarocks/doc.lua
+++ b/src/luarocks/base/doc.lua
diff --git a/src/luarocks/download.lua b/src/luarocks/base/download.lua
index 557d1b65..557d1b65 100644
--- a/src/luarocks/download.lua
+++ b/src/luarocks/base/download.lua
diff --git a/src/luarocks/help.lua b/src/luarocks/base/help.lua
index d27c3a50..d27c3a50 100644
--- a/src/luarocks/help.lua
+++ b/src/luarocks/base/help.lua
diff --git a/src/luarocks/install.lua b/src/luarocks/base/install.lua
index c9b085f5..c9b085f5 100644
--- a/src/luarocks/install.lua
+++ b/src/luarocks/base/install.lua
diff --git a/src/luarocks/lint.lua b/src/luarocks/base/lint.lua
index c9ea45ea..c9ea45ea 100644
--- a/src/luarocks/lint.lua
+++ b/src/luarocks/base/lint.lua
diff --git a/src/luarocks/list.lua b/src/luarocks/base/list.lua
index 45f1a26f..45f1a26f 100644
--- a/src/luarocks/list.lua
+++ b/src/luarocks/base/list.lua
diff --git a/src/luarocks/make.lua b/src/luarocks/base/make.lua
index eb38bff0..eb38bff0 100644
--- a/src/luarocks/make.lua
+++ b/src/luarocks/base/make.lua
diff --git a/src/luarocks/new_version.lua b/src/luarocks/base/new_version.lua
index b13dbb97..b13dbb97 100644
--- a/src/luarocks/new_version.lua
+++ b/src/luarocks/base/new_version.lua
diff --git a/src/luarocks/pack.lua b/src/luarocks/base/pack.lua
index 655cbf37..655cbf37 100644
--- a/src/luarocks/pack.lua
+++ b/src/luarocks/base/pack.lua
diff --git a/src/luarocks/path_cmd.lua b/src/luarocks/base/path_cmd.lua
index 516a0c47..516a0c47 100644
--- a/src/luarocks/path_cmd.lua
+++ b/src/luarocks/base/path_cmd.lua
diff --git a/src/luarocks/purge.lua b/src/luarocks/base/purge.lua
index 50f290c8..50f290c8 100644
--- a/src/luarocks/purge.lua
+++ b/src/luarocks/base/purge.lua
diff --git a/src/luarocks/remove.lua b/src/luarocks/base/remove.lua
index e7f37604..e7f37604 100644
--- a/src/luarocks/remove.lua
+++ b/src/luarocks/base/remove.lua
diff --git a/src/luarocks/search.lua b/src/luarocks/base/search.lua
index 44eff694..44eff694 100644
--- a/src/luarocks/search.lua
+++ b/src/luarocks/base/search.lua
diff --git a/src/luarocks/show.lua b/src/luarocks/base/show.lua
index 1ff81e08..1ff81e08 100644
--- a/src/luarocks/show.lua
+++ b/src/luarocks/base/show.lua
diff --git a/src/luarocks/unpack.lua b/src/luarocks/base/unpack.lua
index c50701b0..c50701b0 100644
--- a/src/luarocks/unpack.lua
+++ b/src/luarocks/base/unpack.lua
diff --git a/src/luarocks/upload.lua b/src/luarocks/base/upload.lua
index baee47ab..baee47ab 100644
--- a/src/luarocks/upload.lua
+++ b/src/luarocks/base/upload.lua
diff --git a/src/luarocks/write_rockspec.lua b/src/luarocks/base/write_rockspec.lua
index be563eaa..be563eaa 100644
--- a/src/luarocks/write_rockspec.lua
+++ b/src/luarocks/base/write_rockspec.lua
diff --git a/src/luarocks/cmd/build.lua b/src/luarocks/cmd/build.lua
new file mode 100644
index 00000000..f3b054d2
--- /dev/null
+++ b/src/luarocks/cmd/build.lua
@@ -0,0 +1,443 @@
1
2--- Module implementing the LuaRocks "build" command.
3-- Builds a rock, compiling its C parts if any.
4local build = {}
5
6local pack = require("luarocks.pack")
7local path = require("luarocks.path")
8local util = require("luarocks.util")
9local repos = require("luarocks.repos")
10local fetch = require("luarocks.fetch")
11local fs = require("luarocks.fs")
12local dir = require("luarocks.dir")
13local deps = require("luarocks.deps")
14local writer = require("luarocks.manif.writer")
15local remove = require("luarocks.remove")
16local cfg = require("luarocks.core.cfg")
17
18build.help_summary = "Build/compile a rock."
19build.help_arguments = "[--pack-binary-rock] [--keep] {<rockspec>|<rock>|<name> [<version>]}"
20build.help = [[
21Build and install a rock, compiling its C parts if any.
22Argument may be a rockspec file, a source rock file
23or the name of a rock to be fetched from a repository.
24
25--pack-binary-rock Do not install rock. Instead, produce a .rock file
26 with the contents of compilation in the current
27 directory.
28
29--keep Do not remove previously installed versions of the
30 rock after building a new one. This behavior can
31 be made permanent by setting keep_other_versions=true
32 in the configuration file.
33
34--branch=<name> Override the `source.branch` field in the loaded
35 rockspec. Allows to specify a different branch to
36 fetch. Particularly for SCM rocks.
37
38--only-deps Installs only the dependencies of the rock.
39
40]]..util.deps_mode_help()
41
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.
58local 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
92end
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.
97local 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
103end
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.
111function 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
129end
130
131local 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
147end
148
149local 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
172end
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.
187function 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
362end
363
364--- Build and install a rock.
365-- @param rock_file string: local or remote filename of a rock.
366-- @param need_to_fetch boolean: true if sources need to be fetched,
367-- false if the rockspec was obtained from inside a source rock.
368-- @param deps_mode: string: Which trees to check dependencies for:
369-- "one" for the current default tree, "all" for all trees,
370-- "order" for all trees with priority >= the current default, "none" for no trees.
371-- @param build_only_deps boolean: true to build the listed dependencies only.
372-- @return boolean or (nil, string, [string]): True if build was successful,
373-- or false and an error message and an optional error code.
374function build.build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps)
375 assert(type(rock_file) == "string")
376 assert(type(need_to_fetch) == "boolean")
377
378 local ok, err, errcode
379 local unpack_dir
380 unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file)
381 if not unpack_dir then
382 return nil, err, errcode
383 end
384 local rockspec_file = path.rockspec_name_from_rock(rock_file)
385 ok, err = fs.change_dir(unpack_dir)
386 if not ok then return nil, err end
387 ok, err, errcode = build.build_rockspec(rockspec_file, need_to_fetch, false, deps_mode, build_only_deps)
388 fs.pop_dir()
389 return ok, err, errcode
390end
391
392local function do_build(name, version, deps_mode, build_only_deps)
393 if name:match("%.rockspec$") then
394 return build.build_rockspec(name, true, false, deps_mode, build_only_deps)
395 elseif name:match("%.src%.rock$") then
396 return build.build_rock(name, false, deps_mode, build_only_deps)
397 elseif name:match("%.all%.rock$") then
398 local install = require("luarocks.install")
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
402 return build.build_rock(name, true, deps_mode, build_only_deps)
403 elseif not name:match(dir.separator) then
404 local search = require("luarocks.search")
405 return search.act_on_src_or_rockspec(do_build, name:lower(), version, nil, deps_mode, build_only_deps)
406 end
407 return nil, "Don't know what to do with "..name
408end
409
410--- Driver function for "build" command.
411-- @param name string: A local or remote rockspec or rock file.
412-- If a package name is given, forwards the request to "search" and,
413-- if returned a result, installs the matching rock.
414-- @param version string: When passing a package name, a version number may
415-- also be given.
416-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an
417-- error message otherwise. exitcode is optionally returned.
418function build.command(flags, name, version)
419 if type(name) ~= "string" then
420 return nil, "Argument missing. "..util.see_help("build")
421 end
422 assert(type(version) == "string" or not version)
423
424 if flags["pack-binary-rock"] then
425 return pack.pack_binary_rock(name, version, do_build, name, version, deps.get_deps_mode(flags))
426 else
427 local ok, err = fs.check_command_permissions(flags)
428 if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end
429 ok, err = do_build(name, version, deps.get_deps_mode(flags), flags["only-deps"])
430 if not ok then return nil, err end
431 name, version = ok, err
432 if flags["only-deps"] then
433 return name, version
434 end
435 if (not flags["keep"]) and not cfg.keep_other_versions then
436 local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"])
437 if not ok then util.printerr(err) end
438 end
439 return name, version
440 end
441end
442
443return build
diff --git a/src/luarocks/cmd/config_cmd.lua b/src/luarocks/cmd/config_cmd.lua
new file mode 100644
index 00000000..b68f7898
--- /dev/null
+++ b/src/luarocks/cmd/config_cmd.lua
@@ -0,0 +1,71 @@
1--- Module implementing the LuaRocks "config" command.
2-- Queries information about the LuaRocks configuration.
3local config_cmd = {}
4
5local cfg = require("luarocks.core.cfg")
6local util = require("luarocks.util")
7local dir = require("luarocks.dir")
8
9config_cmd.help_summary = "Query information about the LuaRocks configuration."
10config_cmd.help_arguments = "<flag>"
11config_cmd.help = [[
12--lua-incdir Path to Lua header files.
13
14--lua-libdir Path to Lua library files.
15
16--lua-ver Lua version (in major.minor format). e.g. 5.1
17
18--system-config Location of the system config file.
19
20--user-config Location of the user config file.
21
22--rock-trees Rocks trees in use. First the user tree, then the system tree.
23]]
24
25local function config_file(conf)
26 print(dir.normalize(conf.file))
27 if conf.ok then
28 return true
29 else
30 return nil, "file not found"
31 end
32end
33
34--- Driver function for "config" command.
35-- @return boolean: True if succeeded, nil on errors.
36function config_cmd.command(flags)
37 if flags["lua-incdir"] then
38 print(cfg.variables.LUA_INCDIR)
39 return true
40 end
41 if flags["lua-libdir"] then
42 print(cfg.variables.LUA_LIBDIR)
43 return true
44 end
45 if flags["lua-ver"] then
46 print(cfg.lua_version)
47 return true
48 end
49 local conf = cfg.which_config()
50 if flags["system-config"] then
51 return config_file(conf.system)
52 end
53 if flags["user-config"] then
54 return config_file(conf.user)
55 end
56 if flags["rock-trees"] then
57 for _, tree in ipairs(cfg.rocks_trees) do
58 if type(tree) == "string" then
59 util.printout(dir.normalize(tree))
60 else
61 local name = tree.name and "\t"..tree.name or ""
62 util.printout(dir.normalize(tree.root)..name)
63 end
64 end
65 return true
66 end
67
68 return nil, "Please provide a flag for querying configuration values. "..util.see_help("config")
69end
70
71return config_cmd
diff --git a/src/luarocks/cmd/doc.lua b/src/luarocks/cmd/doc.lua
new file mode 100644
index 00000000..5d521276
--- /dev/null
+++ b/src/luarocks/cmd/doc.lua
@@ -0,0 +1,155 @@
1
2--- Module implementing the LuaRocks "doc" command.
3-- Shows documentation for an installed rock.
4local doc = {}
5
6local util = require("luarocks.util")
7local search = require("luarocks.search")
8local path = require("luarocks.path")
9local dir = require("luarocks.dir")
10local fetch = require("luarocks.fetch")
11local fs = require("luarocks.fs")
12local download = require("luarocks.download")
13
14doc.help_summary = "Show documentation for an installed rock."
15
16doc.help = [[
17<argument> is an existing package name.
18Without any flags, tries to load the documentation
19using a series of heuristics.
20With these flags, return only the desired information:
21
22--home Open the home page of project.
23--list List documentation files only.
24
25For more information about a rock, see the 'show' command.
26]]
27
28local function show_homepage(homepage, name, version)
29 if not homepage then
30 return nil, "No 'homepage' field in rockspec for "..name.." "..version
31 end
32 util.printout("Opening "..homepage.." ...")
33 fs.browser(homepage)
34 return true
35end
36
37local function try_to_open_homepage(name, version)
38 local temp_dir, err = fs.make_temp_dir("doc-"..name.."-"..(version or ""))
39 if not temp_dir then
40 return nil, "Failed creating temporary directory: "..err
41 end
42 util.schedule_function(fs.delete, temp_dir)
43 local ok, err = fs.change_dir(temp_dir)
44 if not ok then return nil, err end
45 local filename, err = download.download("rockspec", name, version)
46 if not filename then return nil, err end
47 local rockspec, err = fetch.load_local_rockspec(filename)
48 if not rockspec then return nil, err end
49 fs.pop_dir()
50 local descript = rockspec.description or {}
51 if not descript.homepage then return nil, "No homepage defined for "..name end
52 return show_homepage(descript.homepage, name, version)
53end
54
55--- Driver function for "doc" command.
56-- @param name or nil: an existing package name.
57-- @param version string or nil: a version may also be passed.
58-- @return boolean: True if succeeded, nil on errors.
59function doc.command(flags, name, version)
60 if not name then
61 return nil, "Argument missing. "..util.see_help("doc")
62 end
63
64 name = name:lower()
65
66 local iname, iversion, repo = search.pick_installed_rock(name, version, flags["tree"])
67 if not iname then
68 util.printout(name..(version and " "..version or "").." is not installed. Looking for it in the rocks servers...")
69 return try_to_open_homepage(name, version)
70 end
71 name, version = iname, iversion
72
73 local rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version, repo))
74 if not rockspec then return nil,err end
75 local descript = rockspec.description or {}
76
77 if flags["home"] then
78 return show_homepage(descript.homepage, name, version)
79 end
80
81 local directory = path.install_dir(name,version,repo)
82
83 local docdir
84 local directories = { "doc", "docs" }
85 for _, d in ipairs(directories) do
86 local dirname = dir.path(directory, d)
87 if fs.is_dir(dirname) then
88 docdir = dirname
89 break
90 end
91 end
92 if not docdir then
93 if descript.homepage and not flags["list"] then
94 util.printout("Local documentation directory not found -- opening "..descript.homepage.." ...")
95 fs.browser(descript.homepage)
96 return true
97 end
98 return nil, "Documentation directory not found for "..name.." "..version
99 end
100
101 docdir = dir.normalize(docdir):gsub("/+", "/")
102 local files = fs.find(docdir)
103 local htmlpatt = "%.html?$"
104 local extensions = { htmlpatt, "%.md$", "%.txt$", "%.textile$", "" }
105 local basenames = { "index", "readme", "manual" }
106
107 local porcelain = flags["porcelain"]
108 if #files > 0 then
109 util.title("Documentation files for "..name.." "..version, porcelain)
110 if porcelain then
111 for _, file in ipairs(files) do
112 util.printout(docdir.."/"..file)
113 end
114 else
115 util.printout(docdir.."/")
116 for _, file in ipairs(files) do
117 util.printout("\t"..file)
118 end
119 end
120 end
121
122 if flags["list"] then
123 return true
124 end
125
126 for _, extension in ipairs(extensions) do
127 for _, basename in ipairs(basenames) do
128 local filename = basename..extension
129 local found
130 for _, file in ipairs(files) do
131 if file:lower():match(filename) and ((not found) or #file < #found) then
132 found = file
133 end
134 end
135 if found then
136 local pathname = dir.path(docdir, found)
137 util.printout()
138 util.printout("Opening "..pathname.." ...")
139 util.printout()
140 local ok = fs.browser(pathname)
141 if not ok and not pathname:match(htmlpatt) then
142 local fd = io.open(pathname, "r")
143 util.printout(fd:read("*a"))
144 fd:close()
145 end
146 return true
147 end
148 end
149 end
150
151 return true
152end
153
154
155return doc
diff --git a/src/luarocks/cmd/download.lua b/src/luarocks/cmd/download.lua
new file mode 100644
index 00000000..557d1b65
--- /dev/null
+++ b/src/luarocks/cmd/download.lua
@@ -0,0 +1,107 @@
1
2--- Module implementing the luarocks "download" command.
3-- Download a rock from the repository.
4local download = {}
5
6local util = require("luarocks.util")
7local path = require("luarocks.path")
8local fetch = require("luarocks.fetch")
9local search = require("luarocks.search")
10local fs = require("luarocks.fs")
11local dir = require("luarocks.dir")
12local cfg = require("luarocks.core.cfg")
13
14download.help_summary = "Download a specific rock file from a rocks server."
15download.help_arguments = "[--all] [--arch=<arch> | --source | --rockspec] [<name> [<version>]]"
16
17download.help = [[
18--all Download all files if there are multiple matches.
19--source Download .src.rock if available.
20--rockspec Download .rockspec if available.
21--arch=<arch> Download rock for a specific architecture.
22]]
23
24local 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
36end
37
38function 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 ".")
78end
79
80--- Driver function for the "download" command.
81-- @param name string: a rock name.
82-- @param version string or nil: if the name of a package is given, a
83-- version may also be passed.
84-- @return boolean or (nil, string): true if successful or nil followed
85-- by an error message.
86function download.command(flags, name, version)
87 assert(type(version) == "string" or not version)
88 if type(name) ~= "string" and not flags["all"] then
89 return nil, "Argument missing. "..util.see_help("download")
90 end
91 if not name then name, version = "", "" end
92
93 local arch
94
95 if flags["source"] then
96 arch = "src"
97 elseif flags["rockspec"] then
98 arch = "rockspec"
99 elseif flags["arch"] then
100 arch = flags["arch"]
101 end
102
103 local dl, err = download.download(arch, name:lower(), version, flags["all"])
104 return dl and true, err
105end
106
107return download
diff --git a/src/luarocks/cmd/help.lua b/src/luarocks/cmd/help.lua
new file mode 100644
index 00000000..d27c3a50
--- /dev/null
+++ b/src/luarocks/cmd/help.lua
@@ -0,0 +1,117 @@
1
2--- Module implementing the LuaRocks "help" command.
3-- This is a generic help display module, which
4-- uses a global table called "commands" to find commands
5-- to show help for; each command should be represented by a
6-- table containing "help" and "help_summary" fields.
7local help = {}
8
9local util = require("luarocks.util")
10local cfg = require("luarocks.core.cfg")
11local dir = require("luarocks.dir")
12
13local program = util.this_program("luarocks")
14
15help.help_summary = "Help on commands. Type '"..program.." help <command>' for more."
16
17help.help_arguments = "[<command>]"
18help.help = [[
19<command> is the command to show help for.
20]]
21
22local function print_banner()
23 util.printout("\nLuaRocks "..cfg.program_version..", a module deployment system for Lua")
24end
25
26local function print_section(section)
27 util.printout("\n"..section)
28end
29
30local function get_status(status)
31 if status then
32 return "ok"
33 else
34 return "not found"
35 end
36end
37
38--- Driver function for the "help" command.
39-- @param command string or nil: command to show help for; if not
40-- given, help summaries for all commands are shown.
41-- @return boolean or (nil, string): true if there were no errors
42-- or nil and an error message if an invalid command was requested.
43function help.command(flags, command)
44 if not command then
45 local conf = cfg.which_config()
46 print_banner()
47 print_section("NAME")
48 util.printout("\t"..program..[[ - ]]..program_description)
49 print_section("SYNOPSIS")
50 util.printout("\t"..program..[[ [--from=<server> | --only-from=<server>] [--to=<tree>] [VAR=VALUE]... <command> [<argument>] ]])
51 print_section("GENERAL OPTIONS")
52 util.printout([[
53 These apply to all commands, as appropriate:
54
55 --server=<server> Fetch rocks/rockspecs from this server
56 (takes priority over config file)
57 --only-server=<server> Fetch rocks/rockspecs from this server only
58 (overrides any entries in the config file)
59 --only-sources=<url> Restrict downloads to paths matching the
60 given URL.
61 --tree=<tree> Which tree to operate on.
62 --local Use the tree in the user's home directory.
63 To enable it, see ']]..program..[[ help path'.
64 --verbose Display verbose output of commands executed.
65 --timeout=<seconds> Timeout on network operations, in seconds.
66 0 means no timeout (wait forever).
67 Default is ]]..tostring(cfg.connection_timeout)..[[.]])
68 print_section("VARIABLES")
69 util.printout([[
70 Variables from the "variables" table of the configuration file
71 can be overriden with VAR=VALUE assignments.]])
72 print_section("COMMANDS")
73 for name, command in util.sortedpairs(commands) do
74 local cmd = require(command)
75 util.printout("", name)
76 util.printout("\t", cmd.help_summary)
77 end
78 print_section("CONFIGURATION")
79 util.printout("\tLua version: " .. cfg.lua_version)
80 util.printout("\tConfiguration files:")
81 util.printout("\t\tSystem: ".. dir.normalize(conf.system.file) .. " (" .. get_status(conf.system.ok) ..")")
82 if conf.user.file then
83 util.printout("\t\tUser : ".. dir.normalize(conf.user.file) .. " (" .. get_status(conf.user.ok) ..")\n")
84 else
85 util.printout("\t\tUser : disabled in this LuaRocks installation.\n")
86 end
87 util.printout("\tRocks trees in use: ")
88 for _, tree in ipairs(cfg.rocks_trees) do
89 if type(tree) == "string" then
90 util.printout("\t\t"..dir.normalize(tree))
91 else
92 local name = tree.name and " (\""..tree.name.."\")" or ""
93 util.printout("\t\t"..dir.normalize(tree.root)..name)
94 end
95 end
96 else
97 command = command:gsub("-", "_")
98 local cmd = commands[command] and require(commands[command])
99 if cmd then
100 local arguments = cmd.help_arguments or "<argument>"
101 print_banner()
102 print_section("NAME")
103 util.printout("\t"..program.." "..command.." - "..cmd.help_summary)
104 print_section("SYNOPSIS")
105 util.printout("\t"..program.." "..command.." "..arguments)
106 print_section("DESCRIPTION")
107 util.printout("",(cmd.help:gsub("\n","\n\t"):gsub("\n\t$","")))
108 print_section("SEE ALSO")
109 util.printout("","'"..program.." help' for general options and configuration.\n")
110 else
111 return nil, "Unknown command: "..command
112 end
113 end
114 return true
115end
116
117return help
diff --git a/src/luarocks/cmd/install.lua b/src/luarocks/cmd/install.lua
new file mode 100644
index 00000000..c9b085f5
--- /dev/null
+++ b/src/luarocks/cmd/install.lua
@@ -0,0 +1,183 @@
1--- Module implementing the LuaRocks "install" command.
2-- Installs binary rocks.
3local install = {}
4
5local path = require("luarocks.path")
6local repos = require("luarocks.repos")
7local fetch = require("luarocks.fetch")
8local util = require("luarocks.util")
9local fs = require("luarocks.fs")
10local deps = require("luarocks.deps")
11local writer = require("luarocks.manif.writer")
12local remove = require("luarocks.remove")
13local cfg = require("luarocks.core.cfg")
14
15install.help_summary = "Install a rock."
16
17install.help_arguments = "{<rock>|<name> [<version>]}"
18
19install.help = [[
20Argument may be the name of a rock to be fetched from a repository
21or a filename of a locally available rock.
22
23--keep Do not remove previously installed versions of the
24 rock after installing a new one. This behavior can
25 be made permanent by setting keep_other_versions=true
26 in the configuration file.
27
28--only-deps Installs only the dependencies of the rock.
29]]..util.deps_mode_help()
30
31
32--- Install a binary rock.
33-- @param rock_file string: local or remote filename of a rock.
34-- @param deps_mode: string: Which trees to check dependencies for:
35-- "one" for the current default tree, "all" for all trees,
36-- "order" for all trees with priority >= the current default, "none" for no trees.
37-- @return (string, string) or (nil, string, [string]): Name and version of
38-- installed rock if succeeded or nil and an error message followed by an error code.
39function install.install_binary_rock(rock_file, deps_mode)
40 assert(type(rock_file) == "string")
41
42 local name, version, arch = path.parse_name(rock_file)
43 if not name then
44 return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'."
45 end
46
47 if arch ~= "all" and arch ~= cfg.arch then
48 return nil, "Incompatible architecture "..arch, "arch"
49 end
50 if repos.is_installed(name, version) then
51 repos.delete_version(name, version, deps_mode)
52 end
53
54 local rollback = util.schedule_function(function()
55 fs.delete(path.install_dir(name, version))
56 fs.remove_dir_if_empty(path.versions_dir(name))
57 end)
58
59 local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version))
60 if not ok then return nil, err, errcode end
61
62 local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version))
63 if err then
64 return nil, "Failed loading rockspec for installed package: "..err, errcode
65 end
66
67 if deps_mode == "none" then
68 util.printerr("Warning: skipping dependency checks.")
69 else
70 ok, err, errcode = deps.check_external_deps(rockspec, "install")
71 if err then return nil, err, errcode end
72 end
73
74 -- For compatibility with .rock files built with LuaRocks 1
75 if not fs.exists(path.rock_manifest_file(name, version)) then
76 ok, err = writer.make_rock_manifest(name, version)
77 if err then return nil, err end
78 end
79
80 if deps_mode ~= "none" then
81 ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode)
82 if err then return nil, err, errcode end
83 end
84
85 ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode)
86 if err then return nil, err end
87
88 util.remove_scheduled_function(rollback)
89 rollback = util.schedule_function(function()
90 repos.delete_version(name, version, deps_mode)
91 end)
92
93 ok, err = repos.run_hook(rockspec, "post_install")
94 if err then return nil, err end
95
96 util.announce_install(rockspec)
97 util.remove_scheduled_function(rollback)
98 return name, version
99end
100
101--- Installs the dependencies of a binary rock.
102-- @param rock_file string: local or remote filename of a rock.
103-- @param deps_mode: string: Which trees to check dependencies for:
104-- "one" for the current default tree, "all" for all trees,
105-- "order" for all trees with priority >= the current default, "none" for no trees.
106-- @return (string, string) or (nil, string, [string]): Name and version of
107-- the rock whose dependencies were installed if succeeded or nil and an error message
108-- followed by an error code.
109function install.install_binary_rock_deps(rock_file, deps_mode)
110 assert(type(rock_file) == "string")
111
112 local name, version, arch = path.parse_name(rock_file)
113 if not name then
114 return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'."
115 end
116
117 if arch ~= "all" and arch ~= cfg.arch then
118 return nil, "Incompatible architecture "..arch, "arch"
119 end
120
121 local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version))
122 if not ok then return nil, err, errcode end
123
124 local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version))
125 if err then
126 return nil, "Failed loading rockspec for installed package: "..err, errcode
127 end
128
129 ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode)
130 if err then return nil, err, errcode end
131
132 util.printout()
133 util.printout("Successfully installed dependencies for " ..name.." "..version)
134
135 return name, version
136end
137
138--- Driver function for the "install" command.
139-- @param name string: name of a binary rock. If an URL or pathname
140-- to a binary rock is given, fetches and installs it. If a rockspec or a
141-- source rock is given, forwards the request to the "build" command.
142-- If a package name is given, forwards the request to "search" and,
143-- if returned a result, installs the matching rock.
144-- @param version string: When passing a package name, a version number
145-- may also be given.
146-- @return boolean or (nil, string, exitcode): True if installation was
147-- successful, nil and an error message otherwise. exitcode is optionally returned.
148function install.command(flags, name, version)
149 if type(name) ~= "string" then
150 return nil, "Argument missing. "..util.see_help("install")
151 end
152
153 local ok, err = fs.check_command_permissions(flags)
154 if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end
155
156 if name:match("%.rockspec$") or name:match("%.src%.rock$") then
157 local build = require("luarocks.build")
158 return build.command(flags, name)
159 elseif name:match("%.rock$") then
160 if flags["only-deps"] then
161 ok, err = install.install_binary_rock_deps(name, deps.get_deps_mode(flags))
162 else
163 ok, err = install.install_binary_rock(name, deps.get_deps_mode(flags))
164 end
165 if not ok then return nil, err end
166 name, version = ok, err
167 if (not flags["only-deps"]) and (not flags["keep"]) and not cfg.keep_other_versions then
168 local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"])
169 if not ok then util.printerr(err) end
170 end
171 return name, version
172 else
173 local search = require("luarocks.search")
174 local url, err = search.find_suitable_rock(search.make_query(name:lower(), version))
175 if not url then
176 return nil, err
177 end
178 util.printout("Installing "..url)
179 return install.command(flags, url)
180 end
181end
182
183return install
diff --git a/src/luarocks/cmd/lint.lua b/src/luarocks/cmd/lint.lua
new file mode 100644
index 00000000..c9ea45ea
--- /dev/null
+++ b/src/luarocks/cmd/lint.lua
@@ -0,0 +1,53 @@
1
2--- Module implementing the LuaRocks "lint" command.
3-- Utility function that checks syntax of the rockspec.
4local lint = {}
5
6local util = require("luarocks.util")
7local download = require("luarocks.download")
8local fetch = require("luarocks.fetch")
9
10lint.help_summary = "Check syntax of a rockspec."
11lint.help_arguments = "<rockspec>"
12lint.help = [[
13This is a utility function that checks the syntax of a rockspec.
14
15It returns success or failure if the text of a rockspec is
16syntactically correct.
17]]
18
19function lint.command(flags, input)
20 if not input then
21 return nil, "Argument missing. "..util.see_help("lint")
22 end
23
24 local filename = input
25 if not input:match(".rockspec$") then
26 local err
27 filename, err = download.download("rockspec", input:lower())
28 if not filename then
29 return nil, err
30 end
31 end
32
33 local rs, err = fetch.load_local_rockspec(filename)
34 if not rs then
35 return nil, "Failed loading rockspec: "..err
36 end
37
38 local ok = true
39
40 -- This should have been done in the type checker,
41 -- but it would break compatibility of other commands.
42 -- Making 'lint' alone be stricter shouldn't be a problem,
43 -- because extra-strict checks is what lint-type commands
44 -- are all about.
45 if not rs.description.license then
46 util.printerr("Rockspec has no license field.")
47 ok = false
48 end
49
50 return ok, ok or filename.." failed consistency checks."
51end
52
53return lint
diff --git a/src/luarocks/cmd/list.lua b/src/luarocks/cmd/list.lua
new file mode 100644
index 00000000..45f1a26f
--- /dev/null
+++ b/src/luarocks/cmd/list.lua
@@ -0,0 +1,95 @@
1
2--- Module implementing the LuaRocks "list" command.
3-- Lists currently installed rocks.
4local list = {}
5
6local search = require("luarocks.search")
7local deps = require("luarocks.deps")
8local cfg = require("luarocks.core.cfg")
9local util = require("luarocks.util")
10local path = require("luarocks.path")
11
12list.help_summary = "List currently installed rocks."
13list.help_arguments = "[--porcelain] <filter>"
14list.help = [[
15<filter> is a substring of a rock name to filter by.
16
17--outdated List only rocks for which there is a
18 higher version available in the rocks server.
19
20--porcelain Produce machine-friendly output.
21]]
22
23local function check_outdated(trees, query)
24 local results_installed = {}
25 for _, tree in ipairs(trees) do
26 search.manifest_search(results_installed, path.rocks_dir(tree), query)
27 end
28 local outdated = {}
29 for name, versions in util.sortedpairs(results_installed) do
30 versions = util.keys(versions)
31 table.sort(versions, deps.compare_versions)
32 local latest_installed = versions[1]
33
34 local query_available = search.make_query(name:lower())
35 query.exact_name = true
36 local results_available, err = search.search_repos(query_available)
37
38 if results_available[name] then
39 local available_versions = util.keys(results_available[name])
40 table.sort(available_versions, deps.compare_versions)
41 local latest_available = available_versions[1]
42 local latest_available_repo = results_available[name][latest_available][1].repo
43
44 if deps.compare_versions(latest_available, latest_installed) then
45 table.insert(outdated, { name = name, installed = latest_installed, available = latest_available, repo = latest_available_repo })
46 end
47 end
48 end
49 return outdated
50end
51
52local function list_outdated(trees, query, porcelain)
53 util.title("Outdated rocks:", porcelain)
54 local outdated = check_outdated(trees, query)
55 for _, item in ipairs(outdated) do
56 if porcelain then
57 util.printout(item.name, item.installed, item.available, item.repo)
58 else
59 util.printout(item.name)
60 util.printout(" "..item.installed.." < "..item.available.." at "..item.repo)
61 util.printout()
62 end
63 end
64 return true
65end
66
67--- Driver function for "list" command.
68-- @param filter string or nil: A substring of a rock name to filter by.
69-- @param version string or nil: a version may also be passed.
70-- @return boolean: True if succeeded, nil on errors.
71function list.command(flags, filter, version)
72 local query = search.make_query(filter and filter:lower() or "", version)
73 query.exact_name = false
74 local trees = cfg.rocks_trees
75 if flags["tree"] then
76 trees = { flags["tree"] }
77 end
78
79 if flags["outdated"] then
80 return list_outdated(trees, query, flags["porcelain"])
81 end
82
83 local results = {}
84 for _, tree in ipairs(trees) do
85 local ok, err, errcode = search.manifest_search(results, path.rocks_dir(tree), query)
86 if not ok and errcode ~= "open" then
87 util.warning(err)
88 end
89 end
90 util.title("Installed rocks:", flags["porcelain"])
91 search.print_results(results, flags["porcelain"])
92 return true
93end
94
95return list
diff --git a/src/luarocks/cmd/make.lua b/src/luarocks/cmd/make.lua
new file mode 100644
index 00000000..eb38bff0
--- /dev/null
+++ b/src/luarocks/cmd/make.lua
@@ -0,0 +1,86 @@
1
2--- Module implementing the LuaRocks "make" command.
3-- Builds sources in the current directory, but unlike "build",
4-- it does not fetch sources, etc., assuming everything is
5-- available in the current directory.
6local make = {}
7
8local build = require("luarocks.build")
9local fs = require("luarocks.fs")
10local util = require("luarocks.util")
11local cfg = require("luarocks.core.cfg")
12local fetch = require("luarocks.fetch")
13local pack = require("luarocks.pack")
14local remove = require("luarocks.remove")
15local deps = require("luarocks.deps")
16
17make.help_summary = "Compile package in current directory using a rockspec."
18make.help_arguments = "[--pack-binary-rock] [<rockspec>]"
19make.help = [[
20Builds sources in the current directory, but unlike "build",
21it does not fetch sources, etc., assuming everything is
22available in the current directory. If no argument is given,
23it looks for a rockspec in the current directory and in "rockspec/"
24and "rockspecs/" subdirectories, picking the rockspec with newest version
25or without version name. If rockspecs for different rocks are found
26or there are several rockspecs without version, you must specify which to use,
27through the command-line.
28
29This command is useful as a tool for debugging rockspecs.
30To install rocks, you'll normally want to use the "install" and
31"build" commands. See the help on those for details.
32
33--pack-binary-rock Do not install rock. Instead, produce a .rock file
34 with the contents of compilation in the current
35 directory.
36
37--keep Do not remove previously installed versions of the
38 rock after installing a new one. This behavior can
39 be made permanent by setting keep_other_versions=true
40 in the configuration file.
41
42--branch=<name> Override the `source.branch` field in the loaded
43 rockspec. Allows to specify a different branch to
44 fetch. Particularly for SCM rocks.
45
46]]
47
48--- Driver function for "make" command.
49-- @param name string: A local rockspec.
50-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an
51-- error message otherwise. exitcode is optionally returned.
52function make.command(flags, rockspec)
53 assert(type(rockspec) == "string" or not rockspec)
54
55 if not rockspec then
56 local err
57 rockspec, err = util.get_default_rockspec()
58 if not rockspec then
59 return nil, err
60 end
61 end
62 if not rockspec:match("rockspec$") then
63 return nil, "Invalid argument: 'make' takes a rockspec as a parameter. "..util.see_help("make")
64 end
65
66 if flags["pack-binary-rock"] then
67 local rspec, err, errcode = fetch.load_rockspec(rockspec)
68 if not rspec then
69 return nil, err
70 end
71 return pack.pack_binary_rock(rspec.name, rspec.version, build.build_rockspec, rockspec, false, true, deps.get_deps_mode(flags))
72 else
73 local ok, err = fs.check_command_permissions(flags)
74 if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end
75 ok, err = build.build_rockspec(rockspec, false, true, deps.get_deps_mode(flags))
76 if not ok then return nil, err end
77 local name, version = ok, err
78 if (not flags["keep"]) and not cfg.keep_other_versions then
79 local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"])
80 if not ok then util.printerr(err) end
81 end
82 return name, version
83 end
84end
85
86return make
diff --git a/src/luarocks/cmd/new_version.lua b/src/luarocks/cmd/new_version.lua
new file mode 100644
index 00000000..b13dbb97
--- /dev/null
+++ b/src/luarocks/cmd/new_version.lua
@@ -0,0 +1,199 @@
1
2--- Module implementing the LuaRocks "new_version" command.
3-- Utility function that writes a new rockspec, updating data from a previous one.
4local new_version = {}
5
6local util = require("luarocks.util")
7local download = require("luarocks.download")
8local fetch = require("luarocks.fetch")
9local persist = require("luarocks.persist")
10local fs = require("luarocks.fs")
11local type_check = require("luarocks.type_check")
12
13new_version.help_summary = "Auto-write a rockspec for a new version of a rock."
14new_version.help_arguments = "[--tag=<tag>] [<package>|<rockspec>] [<new_version>] [<new_url>]"
15new_version.help = [[
16This is a utility function that writes a new rockspec, updating data
17from a previous one.
18
19If a package name is given, it downloads the latest rockspec from the
20default server. If a rockspec is given, it uses it instead. If no argument
21is given, it looks for a rockspec same way 'luarocks make' does.
22
23If the version number is not given and tag is passed using --tag,
24it is used as the version, with 'v' removed from beginning.
25Otherwise, it only increments the revision number of the given
26(or downloaded) rockspec.
27
28If a URL is given, it replaces the one from the old rockspec with the
29given URL. If a URL is not given and a new version is given, it tries
30to guess the new URL by replacing occurrences of the version number
31in the URL or tag. It also tries to download the new URL to determine
32the new MD5 checksum.
33
34If a tag is given, it replaces the one from the old rockspec. If there is
35an old tag but no new one passed, it is guessed in the same way URL is.
36
37WARNING: it writes the new rockspec to the current directory,
38overwriting the file if it already exists.
39]]
40
41local function try_replace(tbl, field, old, new)
42 if not tbl[field] then
43 return false
44 end
45 local old_field = tbl[field]
46 local new_field = tbl[field]:gsub(old, new)
47 if new_field ~= old_field then
48 util.printout("Guessing new '"..field.."' field as "..new_field)
49 tbl[field] = new_field
50 return true
51 end
52 return false
53end
54
55-- Try to download source file using URL from a rockspec.
56-- If it specified MD5, update it.
57-- @return (true, false) if MD5 was not specified or it stayed same,
58-- (true, true) if MD5 changed, (nil, string) on error.
59local function check_url_and_update_md5(out_rs)
60 local file, temp_dir = fetch.fetch_url_at_temp_dir(out_rs.source.url, "luarocks-new-version-"..out_rs.package)
61 if not file then
62 util.printerr("Warning: invalid URL - "..temp_dir)
63 return true, false
64 end
65
66 local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, out_rs.source.url, out_rs.source.dir)
67 if not inferred_dir then
68 return nil, found_dir
69 end
70
71 if found_dir and found_dir ~= inferred_dir then
72 out_rs.source.dir = found_dir
73 end
74
75 if file then
76 if out_rs.source.md5 then
77 util.printout("File successfully downloaded. Updating MD5 checksum...")
78 local new_md5, err = fs.get_md5(file)
79 if not new_md5 then
80 return nil, err
81 end
82 local old_md5 = out_rs.source.md5
83 out_rs.source.md5 = new_md5
84 return true, new_md5 ~= old_md5
85 else
86 util.printout("File successfully downloaded.")
87 return true, false
88 end
89 end
90end
91
92local function update_source_section(out_rs, url, tag, old_ver, new_ver)
93 if tag then
94 out_rs.source.tag = tag
95 end
96 if url then
97 out_rs.source.url = url
98 return check_url_and_update_md5(out_rs)
99 end
100 if new_ver == old_ver then
101 return true
102 end
103 if out_rs.source.dir then
104 try_replace(out_rs.source, "dir", old_ver, new_ver)
105 end
106 if out_rs.source.file then
107 try_replace(out_rs.source, "file", old_ver, new_ver)
108 end
109 if try_replace(out_rs.source, "url", old_ver, new_ver) then
110 return check_url_and_update_md5(out_rs)
111 end
112 if tag or try_replace(out_rs.source, "tag", old_ver, new_ver) then
113 return true
114 end
115 -- Couldn't replace anything significant, use the old URL.
116 local ok, md5_changed = check_url_and_update_md5(out_rs)
117 if not ok then
118 return nil, md5_changed
119 end
120 if md5_changed then
121 util.printerr("Warning: URL is the same, but MD5 has changed. Old rockspec is broken.")
122 end
123 return true
124end
125
126function new_version.command(flags, input, version, url)
127 if not input then
128 local err
129 input, err = util.get_default_rockspec()
130 if not input then
131 return nil, err
132 end
133 end
134 assert(type(input) == "string")
135
136 local filename, err
137 if input:match("rockspec$") then
138 filename, err = fetch.fetch_url(input)
139 if not filename then
140 return nil, err
141 end
142 else
143 filename, err = download.download("rockspec", input:lower())
144 if not filename then
145 return nil, err
146 end
147 end
148
149 local valid_rs, err = fetch.load_rockspec(filename)
150 if not valid_rs then
151 return nil, err
152 end
153
154 local old_ver, old_rev = valid_rs.version:match("(.*)%-(%d+)$")
155 local new_ver, new_rev
156
157 if flags.tag and not version then
158 version = flags.tag:gsub("^v", "")
159 end
160
161 if version then
162 new_ver, new_rev = version:match("(.*)%-(%d+)$")
163 new_rev = tonumber(new_rev)
164 if not new_rev then
165 new_ver = version
166 new_rev = 1
167 end
168 else
169 new_ver = old_ver
170 new_rev = tonumber(old_rev) + 1
171 end
172 local new_rockver = new_ver:gsub("-", "")
173
174 local out_rs, err = persist.load_into_table(filename)
175 local out_name = out_rs.package:lower()
176 out_rs.version = new_rockver.."-"..new_rev
177
178 local ok, err = update_source_section(out_rs, url, flags.tag, old_ver, new_ver)
179 if not ok then return nil, err end
180
181 if out_rs.build and out_rs.build.type == "module" then
182 out_rs.build.type = "builtin"
183 end
184
185 local out_filename = out_name.."-"..new_rockver.."-"..new_rev..".rockspec"
186
187 persist.save_from_table(out_filename, out_rs, type_check.rockspec_order)
188
189 util.printout("Wrote "..out_filename)
190
191 local valid_out_rs, err = fetch.load_local_rockspec(out_filename)
192 if not valid_out_rs then
193 return nil, "Failed loading generated rockspec: "..err
194 end
195
196 return true
197end
198
199return new_version
diff --git a/src/luarocks/cmd/pack.lua b/src/luarocks/cmd/pack.lua
new file mode 100644
index 00000000..655cbf37
--- /dev/null
+++ b/src/luarocks/cmd/pack.lua
@@ -0,0 +1,193 @@
1
2--- Module implementing the LuaRocks "pack" command.
3-- Creates a rock, packing sources or binaries.
4local pack = {}
5
6local unpack = unpack or table.unpack
7
8local path = require("luarocks.path")
9local repos = require("luarocks.repos")
10local fetch = require("luarocks.fetch")
11local fs = require("luarocks.fs")
12local cfg = require("luarocks.core.cfg")
13local util = require("luarocks.util")
14local dir = require("luarocks.dir")
15local manif = require("luarocks.manif")
16local search = require("luarocks.search")
17
18pack.help_summary = "Create a rock, packing sources or binaries."
19pack.help_arguments = "{<rockspec>|<name> [<version>]}"
20pack.help = [[
21Argument may be a rockspec file, for creating a source rock,
22or the name of an installed package, for creating a binary rock.
23In the latter case, the app version may be given as a second
24argument.
25]]
26
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.
34function 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
61end
62
63local 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
82end
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.
89local 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
137end
138
139function 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)
164end
165
166--- Driver function for the "pack" command.
167-- @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.
169-- @param version string or nil: if the name of a package is given, a
170-- version may also be passed.
171-- @return boolean or (nil, string): true if successful or nil followed
172-- by an error message.
173function pack.command(flags, arg, version)
174 assert(type(version) == "string" or not version)
175 if type(arg) ~= "string" then
176 return nil, "Argument missing. "..util.see_help("pack")
177 end
178
179 local file, err
180 if arg:match(".*%.rockspec") then
181 file, err = pack.pack_source_rock(arg)
182 else
183 file, err = do_pack_binary_rock(arg:lower(), version, flags["tree"])
184 end
185 if err then
186 return nil, err
187 else
188 util.printout("Packed: "..file)
189 return true
190 end
191end
192
193return pack
diff --git a/src/luarocks/cmd/path_cmd.lua b/src/luarocks/cmd/path_cmd.lua
new file mode 100644
index 00000000..516a0c47
--- /dev/null
+++ b/src/luarocks/cmd/path_cmd.lua
@@ -0,0 +1,68 @@
1
2--- @module luarocks.path_cmd
3-- Driver for the `luarocks path` command.
4local path_cmd = {}
5
6local util = require("luarocks.util")
7local cfg = require("luarocks.core.cfg")
8
9path_cmd.help_summary = "Return the currently configured package path."
10path_cmd.help_arguments = ""
11path_cmd.help = [[
12Returns the package path currently configured for this installation
13of LuaRocks, formatted as shell commands to update LUA_PATH and LUA_CPATH.
14
15--bin Adds the system path to the output
16
17--append Appends the paths to the existing paths. Default is to prefix
18 the LR paths to the existing paths.
19
20--lr-path Exports the Lua path (not formatted as shell command)
21
22--lr-cpath Exports the Lua cpath (not formatted as shell command)
23
24--lr-bin Exports the system path (not formatted as shell command)
25
26
27On Unix systems, you may run:
28 eval `luarocks path`
29And on Windows:
30 luarocks path > "%temp%\_lrp.bat" && call "%temp%\_lrp.bat" && del "%temp%\_lrp.bat"
31]]
32
33--- Driver function for "path" command.
34-- @return boolean This function always succeeds.
35function path_cmd.command(flags)
36 local lr_path, lr_cpath, lr_bin = cfg.package_paths(flags["tree"])
37 local path_sep = cfg.export_path_separator
38
39 if flags["lr-path"] then
40 util.printout(util.remove_path_dupes(lr_path, ';'))
41 return true
42 elseif flags["lr-cpath"] then
43 util.printout(util.remove_path_dupes(lr_cpath, ';'))
44 return true
45 elseif flags["lr-bin"] then
46 util.printout(util.remove_path_dupes(lr_bin, path_sep))
47 return true
48 end
49
50 if flags["append"] then
51 lr_path = package.path .. ";" .. lr_path
52 lr_cpath = package.cpath .. ";" .. lr_cpath
53 lr_bin = os.getenv("PATH") .. path_sep .. lr_bin
54 else
55 lr_path = lr_path.. ";" .. package.path
56 lr_cpath = lr_cpath .. ";" .. package.cpath
57 lr_bin = lr_bin .. path_sep .. os.getenv("PATH")
58 end
59
60 util.printout(cfg.export_lua_path:format(util.remove_path_dupes(lr_path, ';')))
61 util.printout(cfg.export_lua_cpath:format(util.remove_path_dupes(lr_cpath, ';')))
62 if flags["bin"] then
63 util.printout(cfg.export_path:format(util.remove_path_dupes(lr_bin, path_sep)))
64 end
65 return true
66end
67
68return path_cmd
diff --git a/src/luarocks/cmd/purge.lua b/src/luarocks/cmd/purge.lua
new file mode 100644
index 00000000..50f290c8
--- /dev/null
+++ b/src/luarocks/cmd/purge.lua
@@ -0,0 +1,77 @@
1
2--- Module implementing the LuaRocks "purge" command.
3-- Remove all rocks from a given tree.
4local purge = {}
5
6local util = require("luarocks.util")
7local fs = require("luarocks.fs")
8local path = require("luarocks.path")
9local search = require("luarocks.search")
10local deps = require("luarocks.deps")
11local repos = require("luarocks.repos")
12local writer = require("luarocks.manif.writer")
13local cfg = require("luarocks.core.cfg")
14local remove = require("luarocks.remove")
15
16purge.help_summary = "Remove all installed rocks from a tree."
17purge.help_arguments = "--tree=<tree> [--old-versions]"
18purge.help = [[
19This command removes rocks en masse from a given tree.
20By default, it removes all rocks from a tree.
21
22The --tree argument is mandatory: luarocks purge does not
23assume a default tree.
24
25--old-versions Keep the highest-numbered version of each
26 rock and remove the other ones. By default
27 it only removes old versions if they are
28 not needed as dependencies. This can be
29 overridden with the flag --force.
30]]
31
32function purge.command(flags)
33 local tree = flags["tree"]
34
35 if type(tree) ~= "string" then
36 return nil, "The --tree argument is mandatory. "..util.see_help("purge")
37 end
38
39 local results = {}
40 local query = search.make_query("")
41 query.exact_name = false
42 if not fs.is_dir(tree) then
43 return nil, "Directory not found: "..tree
44 end
45
46 local ok, err = fs.check_command_permissions(flags)
47 if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end
48
49 search.manifest_search(results, path.rocks_dir(tree), query)
50
51 local sort = function(a,b) return deps.compare_versions(b,a) end
52 if flags["old-versions"] then
53 sort = deps.compare_versions
54 end
55
56 for package, versions in util.sortedpairs(results) do
57 for version, _ in util.sortedpairs(versions, sort) do
58 if flags["old-versions"] then
59 util.printout("Keeping "..package.." "..version.."...")
60 local ok, err = remove.remove_other_versions(package, version, flags["force"], flags["force-fast"])
61 if not ok then
62 util.printerr(err)
63 end
64 break
65 else
66 util.printout("Removing "..package.." "..version.."...")
67 local ok, err = repos.delete_version(package, version, "none", true)
68 if not ok then
69 util.printerr(err)
70 end
71 end
72 end
73 end
74 return writer.make_manifest(cfg.rocks_dir, "one")
75end
76
77return purge
diff --git a/src/luarocks/cmd/remove.lua b/src/luarocks/cmd/remove.lua
new file mode 100644
index 00000000..e7f37604
--- /dev/null
+++ b/src/luarocks/cmd/remove.lua
@@ -0,0 +1,165 @@
1
2--- Module implementing the LuaRocks "remove" command.
3-- Uninstalls rocks.
4local remove = {}
5
6local search = require("luarocks.search")
7local deps = require("luarocks.deps")
8local fetch = require("luarocks.fetch")
9local repos = require("luarocks.repos")
10local path = require("luarocks.path")
11local util = require("luarocks.util")
12local cfg = require("luarocks.core.cfg")
13local fs = require("luarocks.fs")
14
15remove.help_summary = "Uninstall a rock."
16remove.help_arguments = "[--force|--force-fast] <name> [<version>]"
17remove.help = [[
18Argument is the name of a rock to be uninstalled.
19If a version is not given, try to remove all versions at once.
20Will only perform the removal if it does not break dependencies.
21To override this check and force the removal, use --force.
22To perform a forced removal without reporting dependency issues,
23use --force-fast.
24
25]]..util.deps_mode_help()
26
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.
33local 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
57end
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.
66local 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
75end
76
77function 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
120end
121
122function 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
129end
130
131--- Driver function for the "remove" command.
132-- @param name string: name of a rock. If a version is given, refer to
133-- a specific version; otherwise, try to remove all versions.
134-- @param version string: When passing a package name, a version number
135-- may also be given.
136-- @return boolean or (nil, string, exitcode): True if removal was
137-- successful, nil and an error message otherwise. exitcode is optionally returned.
138function remove.command(flags, name, version)
139 if type(name) ~= "string" then
140 return nil, "Argument missing. "..util.see_help("remove")
141 end
142
143 local deps_mode = flags["deps-mode"] or cfg.deps_mode
144
145 local ok, err = fs.check_command_permissions(flags)
146 if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end
147
148 local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$")
149 local filename = name
150 if rock_type then
151 name, version = path.parse_name(filename)
152 if not name then return nil, "Invalid "..rock_type.." filename: "..filename end
153 end
154
155 local results = {}
156 name = name:lower()
157 search.manifest_search(results, cfg.rocks_dir, search.make_query(name, version))
158 if not results[name] then
159 return nil, "Could not find rock '"..name..(version and " "..version or "").."' in "..path.rocks_tree_to_string(cfg.root_dir)
160 end
161
162 return remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"])
163end
164
165return remove
diff --git a/src/luarocks/cmd/search.lua b/src/luarocks/cmd/search.lua
new file mode 100644
index 00000000..44eff694
--- /dev/null
+++ b/src/luarocks/cmd/search.lua
@@ -0,0 +1,482 @@
1
2--- Module implementing the LuaRocks "search" command.
3-- Queries LuaRocks servers.
4local search = {}
5
6
7local dir = require("luarocks.dir")
8local path = require("luarocks.path")
9local manif = require("luarocks.manif")
10local deps = require("luarocks.deps")
11local cfg = require("luarocks.core.cfg")
12local util = require("luarocks.util")
13
14search.help_summary = "Query the LuaRocks servers."
15search.help_arguments = "[--source] [--binary] { <name> [<version>] | --all }"
16search.help = [[
17--source Return only rockspecs and source rocks,
18 to be used with the "build" command.
19--binary Return only pure Lua and binary rocks (rocks that can be used
20 with the "install" command without requiring a C toolchain).
21--all List all contents of the server that are suitable to
22 this platform, do not filter by name.
23]]
24
25--- Convert the arch field of a query table to table format.
26-- @param query table: A query table.
27local 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
46end
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.
58local 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 })
71end
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.
80local 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
88end
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.
107local 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
115end
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.
131function 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
158end
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.
174function 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
192end
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".
200function 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
232end
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
238function 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
250end
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.
258local 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
280end
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.
285local 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
297end
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.
303function 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
343end
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.
349function 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
371end
372
373--- 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
375-- used with the "install" command.
376-- @param results table: A search results table.
377-- @return (table, table): Two tables, one for source and one for binary
378-- results.
379local function split_source_and_binary_results(results)
380 local sources, binaries = {}, {}
381 for name, versions in pairs(results) do
382 for version, repositories in pairs(versions) do
383 for _, repo in ipairs(repositories) do
384 local where = sources
385 if repo.arch == "all" or repo.arch == cfg.arch then
386 where = binaries
387 end
388 store_result(where, name, version, repo.arch, repo.repo)
389 end
390 end
391 end
392 return sources, binaries
393end
394
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.
404function 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, ...)
416end
417
418function 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
449end
450
451--- Driver function for "search" command.
452-- @param name string: A substring of a rock name to search.
453-- @param version string or nil: a version may also be passed.
454-- @return boolean or (nil, string): True if build was successful; nil and an
455-- error message otherwise.
456function search.command(flags, name, version)
457 if flags["all"] then
458 name, version = "", nil
459 end
460
461 if type(name) ~= "string" and not flags["all"] then
462 return nil, "Enter name and version or use --all. "..util.see_help("search")
463 end
464
465 local query = search.make_query(name:lower(), version)
466 query.exact_name = false
467 local results, err = search.search_repos(query)
468 local porcelain = flags["porcelain"]
469 util.title("Search results:", porcelain, "=")
470 local sources, binaries = split_source_and_binary_results(results)
471 if next(sources) and not flags["binary"] then
472 util.title("Rockspecs and source rocks:", porcelain)
473 search.print_results(sources, porcelain)
474 end
475 if next(binaries) and not flags["source"] then
476 util.title("Binary and pure-Lua rocks:", porcelain)
477 search.print_results(binaries, porcelain)
478 end
479 return true
480end
481
482return search
diff --git a/src/luarocks/cmd/show.lua b/src/luarocks/cmd/show.lua
new file mode 100644
index 00000000..1ff81e08
--- /dev/null
+++ b/src/luarocks/cmd/show.lua
@@ -0,0 +1,158 @@
1--- Module implementing the LuaRocks "show" command.
2-- Shows information about an installed rock.
3local show = {}
4
5local search = require("luarocks.search")
6local cfg = require("luarocks.core.cfg")
7local util = require("luarocks.util")
8local path = require("luarocks.path")
9local deps = require("luarocks.deps")
10local fetch = require("luarocks.fetch")
11local manif = require("luarocks.manif")
12
13show.help_summary = "Show information about an installed rock."
14
15show.help = [[
16<argument> is an existing package name.
17Without any flags, show all module information.
18With these flags, return only the desired information:
19
20--home home page of project
21--modules all modules provided by this package as used by require()
22--deps packages this package depends on
23--rockspec the full path of the rockspec file
24--mversion the package version
25--rock-tree local tree where rock is installed
26--rock-dir data directory of the installed rock
27]]
28
29local function keys_as_string(t, sep)
30 local keys = util.keys(t)
31 table.sort(keys)
32 return table.concat(keys, sep or " ")
33end
34
35local function word_wrap(line)
36 local width = tonumber(os.getenv("COLUMNS")) or 80
37 if width > 80 then width = 80 end
38 if #line > width then
39 local brk = width
40 while brk > 0 and line:sub(brk, brk) ~= " " do
41 brk = brk - 1
42 end
43 if brk > 0 then
44 return line:sub(1, brk-1) .. "\n" .. word_wrap(line:sub(brk+1))
45 end
46 end
47 return line
48end
49
50local function format_text(text)
51 text = text:gsub("^%s*",""):gsub("%s$", ""):gsub("\n[ \t]+","\n"):gsub("([^\n])\n([^\n])","%1 %2")
52 local paragraphs = util.split_string(text, "\n\n")
53 for n, line in ipairs(paragraphs) do
54 paragraphs[n] = word_wrap(line)
55 end
56 return (table.concat(paragraphs, "\n\n"):gsub("%s$", ""))
57end
58
59local function installed_rock_label(name, tree)
60 local installed, version
61 if cfg.rocks_provided[name] then
62 installed, version = true, cfg.rocks_provided[name]
63 else
64 installed, version = search.pick_installed_rock(name, nil, tree)
65 end
66 return installed and "(using "..version..")" or "(missing)"
67end
68
69--- Driver function for "show" command.
70-- @param name or nil: an existing package name.
71-- @param version string or nil: a version may also be passed.
72-- @return boolean: True if succeeded, nil on errors.
73function show.command(flags, name, version)
74 if not name then
75 return nil, "Argument missing. "..util.see_help("show")
76 end
77
78 local repo, repo_url
79 name, version, repo, repo_url = search.pick_installed_rock(name:lower(), version, flags["tree"])
80 if not name then
81 return nil, version
82 end
83
84 local directory = path.install_dir(name,version,repo)
85 local rockspec_file = path.rockspec_file(name, version, repo)
86 local rockspec, err = fetch.load_local_rockspec(rockspec_file)
87 if not rockspec then return nil,err end
88
89 local descript = rockspec.description or {}
90 local manifest, err = manif.load_manifest(repo_url)
91 if not manifest then return nil,err end
92 local minfo = manifest.repository[name][version][1]
93
94 if flags["rock-tree"] then util.printout(path.rocks_tree_to_string(repo))
95 elseif flags["rock-dir"] then util.printout(directory)
96 elseif flags["home"] then util.printout(descript.homepage)
97 elseif flags["issues"] then util.printout(descript.issues_url)
98 elseif flags["labels"] then util.printout(descript.labels and table.concat(descript.labels, "\n"))
99 elseif flags["modules"] then util.printout(keys_as_string(minfo.modules, "\n"))
100 elseif flags["deps"] then util.printout(keys_as_string(minfo.dependencies))
101 elseif flags["rockspec"] then util.printout(rockspec_file)
102 elseif flags["mversion"] then util.printout(version)
103 else
104 util.printout()
105 util.printout(rockspec.package.." "..rockspec.version.." - "..(descript.summary or ""))
106 util.printout()
107 if descript.detailed then
108 util.printout(format_text(descript.detailed))
109 util.printout()
110 end
111 if descript.license then
112 util.printout("License: ", descript.license)
113 end
114 if descript.homepage then
115 util.printout("Homepage: ", descript.homepage)
116 end
117 if descript.issues_url then
118 util.printout("Issues: ", descript.issues_url)
119 end
120 if descript.labels then
121 util.printout("Labels: ", table.concat(descript.labels, ", "))
122 end
123 util.printout("Installed in: ", path.rocks_tree_to_string(repo))
124 if next(minfo.modules) then
125 util.printout()
126 util.printout("Modules:")
127 for mod, filename in util.sortedpairs(minfo.modules) do
128 util.printout("\t"..mod.." ("..path.which(mod, filename, name, version, repo, manifest)..")")
129 end
130 end
131 local direct_deps = {}
132 if #rockspec.dependencies > 0 then
133 util.printout()
134 util.printout("Depends on:")
135 for _, dep in ipairs(rockspec.dependencies) do
136 direct_deps[dep.name] = true
137 util.printout("\t"..deps.show_dep(dep).." "..installed_rock_label(dep.name, flags["tree"]))
138 end
139 end
140 local has_indirect_deps
141 for dep_name in util.sortedpairs(minfo.dependencies) do
142 if not direct_deps[dep_name] then
143 if not has_indirect_deps then
144 util.printout()
145 util.printout("Indirectly pulling:")
146 has_indirect_deps = true
147 end
148
149 util.printout("\t"..dep_name.." "..installed_rock_label(dep_name, flags["tree"]))
150 end
151 end
152 util.printout()
153 end
154 return true
155end
156
157
158return show
diff --git a/src/luarocks/cmd/unpack.lua b/src/luarocks/cmd/unpack.lua
new file mode 100644
index 00000000..c50701b0
--- /dev/null
+++ b/src/luarocks/cmd/unpack.lua
@@ -0,0 +1,164 @@
1
2--- Module implementing the LuaRocks "unpack" command.
3-- Unpack the contents of a rock.
4local unpack = {}
5
6local fetch = require("luarocks.fetch")
7local fs = require("luarocks.fs")
8local util = require("luarocks.util")
9local build = require("luarocks.build")
10local dir = require("luarocks.dir")
11local cfg = require("luarocks.core.cfg")
12
13unpack.help_summary = "Unpack the contents of a rock."
14unpack.help_arguments = "[--force] {<rock>|<name> [<version>]}"
15unpack.help = [[
16Unpacks the contents of a rock in a newly created directory.
17Argument may be a rock file, or the name of a rock in a rocks server.
18In the latter case, the app version may be given as a second argument.
19
20--force Unpack files even if the output directory already exists.
21]]
22
23--- Load a rockspec file to the given directory, fetches the source
24-- files specified in the rockspec, and unpack them inside the directory.
25-- @param rockspec_file string: The URL for a rockspec file.
26-- @param dir_name string: The directory where to store and unpack files.
27-- @return table or (nil, string): the loaded rockspec table or
28-- nil and an error message.
29local function unpack_rockspec(rockspec_file, dir_name)
30 assert(type(rockspec_file) == "string")
31 assert(type(dir_name) == "string")
32
33 local rockspec, err = fetch.load_rockspec(rockspec_file)
34 if not rockspec then
35 return nil, "Failed loading rockspec "..rockspec_file..": "..err
36 end
37 local ok, err = fs.change_dir(dir_name)
38 if not ok then return nil, err end
39 local ok, sources_dir = fetch.fetch_sources(rockspec, true, ".")
40 if not ok then
41 return nil, sources_dir
42 end
43 ok, err = fs.change_dir(sources_dir)
44 if not ok then return nil, err end
45 ok, err = build.apply_patches(rockspec)
46 fs.pop_dir()
47 if not ok then return nil, err end
48 return rockspec
49end
50
51--- Load a .rock file to the given directory and unpack it inside it.
52-- @param rock_file string: The URL for a .rock file.
53-- @param dir_name string: The directory where to unpack.
54-- @param kind string: the kind of rock file, as in the second-level
55-- extension in the rock filename (eg. "src", "all", "linux-x86")
56-- @return table or (nil, string): the loaded rockspec table or
57-- nil and an error message.
58local function unpack_rock(rock_file, dir_name, kind)
59 assert(type(rock_file) == "string")
60 assert(type(dir_name) == "string")
61
62 local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, dir_name)
63 if not ok then
64 return nil, "Failed unzipping rock "..rock_file, errcode
65 end
66 ok, err = fs.change_dir(dir_name)
67 if not ok then return nil, err end
68 local rockspec_file = dir_name..".rockspec"
69 local rockspec, err = fetch.load_rockspec(rockspec_file)
70 if not rockspec then
71 return nil, "Failed loading rockspec "..rockspec_file..": "..err
72 end
73 if kind == "src" then
74 if rockspec.source.file then
75 local ok, err = fs.unpack_archive(rockspec.source.file)
76 if not ok then
77 return nil, err
78 end
79 ok, err = fs.change_dir(rockspec.source.dir)
80 if not ok then return nil, err end
81 ok, err = build.apply_patches(rockspec)
82 fs.pop_dir()
83 if not ok then return nil, err end
84 end
85 end
86 return rockspec
87end
88
89--- Create a directory and perform the necessary actions so that
90-- the sources for the rock and its rockspec are unpacked inside it,
91-- laid out properly so that the 'make' command is able to build the module.
92-- @param file string: A rockspec or .rock URL.
93-- @return boolean or (nil, string): true if successful or nil followed
94-- by an error message.
95local function run_unpacker(file, force)
96 assert(type(file) == "string")
97
98 local base_name = dir.base_name(file)
99 local dir_name, kind, extension = base_name:match("(.*)%.([^.]+)%.(rock)$")
100 if not extension then
101 dir_name, extension = base_name:match("(.*)%.(rockspec)$")
102 kind = "rockspec"
103 end
104 if not extension then
105 return nil, file.." does not seem to be a valid filename."
106 end
107
108 local exists = fs.exists(dir_name)
109 if exists and not force then
110 return nil, "Directory "..dir_name.." already exists."
111 end
112 if not exists then
113 local ok, err = fs.make_dir(dir_name)
114 if not ok then return nil, err end
115 end
116 local rollback = util.schedule_function(fs.delete, fs.absolute_name(dir_name))
117
118 local rockspec, err
119 if extension == "rock" then
120 rockspec, err = unpack_rock(file, dir_name, kind)
121 elseif extension == "rockspec" then
122 rockspec, err = unpack_rockspec(file, dir_name)
123 end
124 if not rockspec then
125 return nil, err
126 end
127 if kind == "src" or kind == "rockspec" then
128 if rockspec.source.dir ~= "." then
129 local ok = fs.copy(rockspec.local_filename, rockspec.source.dir, cfg.perm_read)
130 if not ok then
131 return nil, "Failed copying unpacked rockspec into unpacked source directory."
132 end
133 end
134 util.printout()
135 util.printout("Done. You may now enter directory ")
136 util.printout(dir.path(dir_name, rockspec.source.dir))
137 util.printout("and type 'luarocks make' to build.")
138 end
139 util.remove_scheduled_function(rollback)
140 return true
141end
142
143--- Driver function for the "unpack" command.
144-- @param name string: may be a rock filename, for unpacking a
145-- rock file or the name of a rock to be fetched and unpacked.
146-- @param version string or nil: if the name of a package is given, a
147-- version may also be passed.
148-- @return boolean or (nil, string): true if successful or nil followed
149-- by an error message.
150function unpack.command(flags, name, version)
151 assert(type(version) == "string" or not version)
152 if type(name) ~= "string" then
153 return nil, "Argument missing. "..util.see_help("unpack")
154 end
155
156 if name:match(".*%.rock") or name:match(".*%.rockspec") then
157 return run_unpacker(name, flags["force"])
158 else
159 local search = require("luarocks.search")
160 return search.act_on_src_or_rockspec(run_unpacker, name:lower(), version)
161 end
162end
163
164return unpack
diff --git a/src/luarocks/cmd/upload.lua b/src/luarocks/cmd/upload.lua
new file mode 100644
index 00000000..baee47ab
--- /dev/null
+++ b/src/luarocks/cmd/upload.lua
@@ -0,0 +1,94 @@
1
2local upload = {}
3
4local util = require("luarocks.util")
5local fetch = require("luarocks.fetch")
6local pack = require("luarocks.pack")
7local cfg = require("luarocks.core.cfg")
8local Api = require("luarocks.upload.api")
9
10upload.help_summary = "Upload a rockspec to the public rocks repository."
11upload.help_arguments = "[--skip-pack] [--api-key=<key>] [--force] <rockspec>"
12upload.help = [[
13<rockspec> Pack a source rock file (.src.rock extension),
14 upload rockspec and source rock to server.
15--skip-pack Do not pack and send source rock.
16--api-key=<key> Give it an API key. It will be stored for subsequent uses.
17--force Replace existing rockspec if the same revision of
18 a module already exists. This should be used only
19 in case of upload mistakes: when updating a rockspec,
20 increment the revision number instead.
21]]
22
23function upload.command(flags, fname)
24 if not fname then
25 return nil, "Missing rockspec. "..util.see_help("upload")
26 end
27
28 local api, err = Api.new(flags)
29 if not api then
30 return nil, err
31 end
32 if cfg.verbose then
33 api.debug = true
34 end
35
36 local rockspec, err, errcode = fetch.load_rockspec(fname)
37 if err then
38 return nil, err, errcode
39 end
40
41 util.printout("Sending " .. tostring(fname) .. " ...")
42 local res, err = api:method("check_rockspec", {
43 package = rockspec.package,
44 version = rockspec.version
45 })
46 if not res then return nil, err end
47
48 if not res.module then
49 util.printout("Will create new module (" .. tostring(rockspec.package) .. ")")
50 end
51 if res.version and not flags["force"] then
52 return nil, "Revision "..rockspec.version.." already exists on the server. "..util.see_help("upload")
53 end
54
55 local rock_fname
56 if not flags["skip-pack"] and not rockspec.version:match("^scm") then
57 util.printout("Packing " .. tostring(rockspec.package))
58 rock_fname, err = pack.pack_source_rock(fname)
59 if not rock_fname then
60 return nil, err
61 end
62 end
63
64 local multipart = require("luarocks.upload.multipart")
65
66 res, err = api:method("upload", nil, {
67 rockspec_file = multipart.new_file(fname)
68 })
69 if not res then return nil, err end
70
71 if res.is_new and #res.manifests == 0 then
72 util.printerr("Warning: module not added to root manifest due to name taken.")
73 end
74
75 local module_url = res.module_url
76
77 if rock_fname then
78 if (not res.version) or (not res.version.id) then
79 return nil, "Invalid response from server."
80 end
81 util.printout(("Sending " .. tostring(rock_fname) .. " ..."))
82 res, err = api:method("upload_rock/" .. ("%d"):format(res.version.id), nil, {
83 rock_file = multipart.new_file(rock_fname)
84 })
85 if not res then return nil, err end
86 end
87
88 util.printout()
89 util.printout("Done: " .. tostring(module_url))
90 util.printout()
91 return true
92end
93
94return upload
diff --git a/src/luarocks/cmd/write_rockspec.lua b/src/luarocks/cmd/write_rockspec.lua
new file mode 100644
index 00000000..be563eaa
--- /dev/null
+++ b/src/luarocks/cmd/write_rockspec.lua
@@ -0,0 +1,376 @@
1
2local write_rockspec = {}
3
4local cfg = require("luarocks.core.cfg")
5local dir = require("luarocks.dir")
6local fetch = require("luarocks.fetch")
7local fs = require("luarocks.fs")
8local path = require("luarocks.path")
9local persist = require("luarocks.persist")
10local type_check = require("luarocks.type_check")
11local util = require("luarocks.util")
12local deps = require("luarocks.deps")
13
14write_rockspec.help_summary = "Write a template for a rockspec file."
15write_rockspec.help_arguments = "[--output=<file> ...] [<name>] [<version>] [<url>|<path>]"
16write_rockspec.help = [[
17This command writes an initial version of a rockspec file,
18based on a name, a version, and a location (an URL or a local path).
19If only two arguments are given, the first one is considered the name and the
20second one is the location.
21If only one argument is given, it must be the location.
22If no arguments are given, current directory is used as location.
23LuaRocks will attempt to infer name and version if not given,
24using 'scm' as default version.
25
26Note that the generated file is a _starting point_ for writing a
27rockspec, and is not guaranteed to be complete or correct.
28
29--output=<file> Write the rockspec with the given filename.
30 If not given, a file is written in the current
31 directory with a filename based on given name and version.
32--license="<string>" A license string, such as "MIT/X11" or "GNU GPL v3".
33--summary="<txt>" A short one-line description summary.
34--detailed="<txt>" A longer description string.
35--homepage=<url> Project homepage.
36--lua-version=<ver> Supported Lua versions. Accepted values are "5.1", "5.2",
37 "5.3", "5.1,5.2", "5.2,5.3", or "5.1,5.2,5.3".
38--rockspec-format=<ver> Rockspec format version, such as "1.0" or "1.1".
39--tag=<tag> Tag to use. Will attempt to extract version number from it.
40--lib=<lib>[,<lib>] A comma-separated list of libraries that C files need to
41 link to.
42]]
43
44local function open_file(name)
45 return io.open(dir.path(fs.current_dir(), name), "r")
46end
47
48local function get_url(rockspec)
49 local file, temp_dir, err_code, err_file, err_temp_dir = fetch.fetch_sources(rockspec, false)
50 if err_code == "source.dir" then
51 file, temp_dir = err_file, err_temp_dir
52 elseif not file then
53 util.warning("Could not fetch sources - "..temp_dir)
54 return false
55 end
56 util.printout("File successfully downloaded. Making checksum and checking base dir...")
57 if fetch.is_basic_protocol(rockspec.source.protocol) then
58 rockspec.source.md5 = fs.get_md5(file)
59 end
60 local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, rockspec.source.url)
61 return true, found_dir or inferred_dir, temp_dir
62end
63
64local function configure_lua_version(rockspec, luaver)
65 if luaver == "5.1" then
66 table.insert(rockspec.dependencies, "lua ~> 5.1")
67 elseif luaver == "5.2" then
68 table.insert(rockspec.dependencies, "lua ~> 5.2")
69 elseif luaver == "5.3" then
70 table.insert(rockspec.dependencies, "lua ~> 5.3")
71 elseif luaver == "5.1,5.2" then
72 table.insert(rockspec.dependencies, "lua >= 5.1, < 5.3")
73 elseif luaver == "5.2,5.3" then
74 table.insert(rockspec.dependencies, "lua >= 5.2, < 5.4")
75 elseif luaver == "5.1,5.2,5.3" then
76 table.insert(rockspec.dependencies, "lua >= 5.1, < 5.4")
77 else
78 util.warning("Please specify supported Lua version with --lua-version=<ver>. "..util.see_help("write_rockspec"))
79 end
80end
81
82local function detect_description()
83 local fd = open_file("README.md") or open_file("README")
84 if not fd then return end
85 local data = fd:read("*a")
86 fd:close()
87 local paragraph = data:match("\n\n([^%[].-)\n\n")
88 if not paragraph then paragraph = data:match("\n\n(.*)") end
89 local summary, detailed
90 if paragraph then
91 detailed = paragraph
92
93 if #paragraph < 80 then
94 summary = paragraph:gsub("\n", "")
95 else
96 summary = paragraph:gsub("\n", " "):match("([^.]*%.) ")
97 end
98 end
99 return summary, detailed
100end
101
102local function detect_mit_license(data)
103 local strip_copyright = (data:gsub("Copyright [^\n]*\n", ""))
104 local sum = 0
105 for i = 1, #strip_copyright do
106 local num = string.byte(strip_copyright:sub(i,i))
107 if num > 32 and num <= 128 then
108 sum = sum + num
109 end
110 end
111 return sum == 78656
112end
113
114local simple_scm_protocols = {
115 git = true, ["git+http"] = true, ["git+https"] = true,
116 hg = true, ["hg+http"] = true, ["hg+https"] = true
117}
118
119local function detect_url_from_command(program, args, directory)
120 local command = fs.Q(cfg.variables[program:upper()]).. " "..args
121 local pipe = io.popen(fs.command_at(directory, fs.quiet_stderr(command)))
122 if not pipe then return nil end
123 local url = pipe:read("*a"):match("^([^\r\n]+)")
124 pipe:close()
125 if not url then return nil end
126 if not util.starts_with(url, program.."://") then
127 url = program.."+"..url
128 end
129
130 if simple_scm_protocols[dir.split_url(url)] then
131 return url
132 end
133end
134
135local function detect_scm_url(directory)
136 return detect_url_from_command("git", "config --get remote.origin.url", directory) or
137 detect_url_from_command("hg", "paths default", directory)
138end
139
140local function show_license(rockspec)
141 local fd = open_file("COPYING") or open_file("LICENSE") or open_file("MIT-LICENSE.txt")
142 if not fd then return nil end
143 local data = fd:read("*a")
144 fd:close()
145 local is_mit = detect_mit_license(data)
146 util.title("License for "..rockspec.package..":")
147 util.printout(data)
148 util.printout()
149 return is_mit
150end
151
152local function get_cmod_name(file)
153 local fd = open_file(file)
154 if not fd then return nil end
155 local data = fd:read("*a")
156 fd:close()
157 return (data:match("int%s+luaopen_([a-zA-Z0-9_]+)"))
158end
159
160local luamod_blacklist = {
161 test = true,
162 tests = true,
163}
164
165local function fill_as_builtin(rockspec, libs)
166 rockspec.build.type = "builtin"
167 rockspec.build.modules = {}
168 local prefix = ""
169
170 for _, parent in ipairs({"src", "lua"}) do
171 if fs.is_dir(parent) then
172 fs.change_dir(parent)
173 prefix = parent.."/"
174 break
175 end
176 end
177
178 local incdirs, libdirs
179 if libs then
180 incdirs, libdirs = {}, {}
181 for _, lib in ipairs(libs) do
182 local upper = lib:upper()
183 incdirs[#incdirs+1] = "$("..upper.."_INCDIR)"
184 libdirs[#libdirs+1] = "$("..upper.."_LIBDIR)"
185 end
186 end
187
188 for _, file in ipairs(fs.find()) do
189 local luamod = file:match("(.*)%.lua$")
190 if luamod and not luamod_blacklist[luamod] then
191 rockspec.build.modules[path.path_to_module(file)] = prefix..file
192 else
193 local cmod = file:match("(.*)%.c$")
194 if cmod then
195 local modname = get_cmod_name(file) or path.path_to_module(file:gsub("%.c$", ".lua"))
196 rockspec.build.modules[modname] = {
197 sources = prefix..file,
198 libraries = libs,
199 incdirs = incdirs,
200 libdirs = libdirs,
201 }
202 end
203 end
204 end
205
206 for _, directory in ipairs({ "doc", "docs", "samples", "tests" }) do
207 if fs.is_dir(directory) then
208 if not rockspec.build.copy_directories then
209 rockspec.build.copy_directories = {}
210 end
211 table.insert(rockspec.build.copy_directories, directory)
212 end
213 end
214
215 if prefix ~= "" then
216 fs.pop_dir()
217 end
218end
219
220local function rockspec_cleanup(rockspec)
221 rockspec.source.file = nil
222 rockspec.source.protocol = nil
223 rockspec.variables = nil
224 rockspec.name = nil
225 rockspec.format_is_at_least = nil
226end
227
228function write_rockspec.command(flags, name, version, url_or_dir)
229 if not name then
230 url_or_dir = "."
231 elseif not version then
232 url_or_dir = name
233 name = nil
234 elseif not url_or_dir then
235 url_or_dir = version
236 version = nil
237 end
238
239 if flags["tag"] then
240 if not version then
241 version = flags["tag"]:gsub("^v", "")
242 end
243 end
244
245 local protocol, pathname = dir.split_url(url_or_dir)
246 if protocol == "file" then
247 if pathname == "." then
248 name = name or dir.base_name(fs.current_dir())
249 end
250 elseif fetch.is_basic_protocol(protocol) then
251 local filename = dir.base_name(url_or_dir)
252 local newname, newversion = filename:match("(.*)-([^-]+)")
253 if newname then
254 name = name or newname
255 version = version or newversion:gsub("%.[a-z]+$", ""):gsub("%.tar$", "")
256 end
257 else
258 name = name or dir.base_name(url_or_dir):gsub("%.[^.]+$", "")
259 end
260
261 if not name then
262 return nil, "Could not infer rock name. "..util.see_help("write_rockspec")
263 end
264 version = version or "scm"
265
266 local filename = flags["output"] or dir.path(fs.current_dir(), name:lower().."-"..version.."-1.rockspec")
267
268 local rockspec = {
269 rockspec_format = flags["rockspec-format"],
270 package = name,
271 name = name:lower(),
272 version = version.."-1",
273 source = {
274 url = "*** please add URL for source tarball, zip or repository here ***",
275 tag = flags["tag"],
276 },
277 description = {
278 summary = flags["summary"] or "*** please specify description summary ***",
279 detailed = flags["detailed"] or "*** please enter a detailed description ***",
280 homepage = flags["homepage"] or "*** please enter a project homepage ***",
281 license = flags["license"] or "*** please specify a license ***",
282 },
283 dependencies = {},
284 build = {},
285 }
286 path.configure_paths(rockspec)
287 rockspec.source.protocol = protocol
288 rockspec.format_is_at_least = deps.format_is_at_least
289
290 configure_lua_version(rockspec, flags["lua-version"])
291
292 local local_dir = url_or_dir
293
294 if url_or_dir:match("://") then
295 rockspec.source.url = url_or_dir
296 rockspec.source.file = dir.base_name(url_or_dir)
297 rockspec.source.dir = "dummy"
298 if not fetch.is_basic_protocol(rockspec.source.protocol) then
299 if version ~= "scm" then
300 rockspec.source.tag = flags["tag"] or "v" .. version
301 end
302 end
303 rockspec.source.dir = nil
304 local ok, base_dir, temp_dir = get_url(rockspec)
305 if ok then
306 if base_dir ~= dir.base_name(url_or_dir) then
307 rockspec.source.dir = base_dir
308 end
309 end
310 if base_dir then
311 local_dir = dir.path(temp_dir, base_dir)
312 else
313 local_dir = nil
314 end
315 else
316 rockspec.source.url = detect_scm_url(local_dir) or rockspec.source.url
317 end
318
319 if not local_dir then
320 local_dir = "."
321 end
322
323 if not flags["homepage"] then
324 local url_protocol, url_path = dir.split_url(rockspec.source.url)
325
326 if simple_scm_protocols[url_protocol] then
327 for _, domain in ipairs({"github.com", "bitbucket.org", "gitlab.com"}) do
328 if util.starts_with(url_path, domain) then
329 rockspec.description.homepage = "https://"..url_path:gsub("%.git$", "")
330 break
331 end
332 end
333 end
334 end
335
336 local libs = nil
337 if flags["lib"] then
338 libs = {}
339 rockspec.external_dependencies = {}
340 for lib in flags["lib"]:gmatch("([^,]+)") do
341 table.insert(libs, lib)
342 rockspec.external_dependencies[lib:upper()] = {
343 library = lib
344 }
345 end
346 end
347
348 local ok, err = fs.change_dir(local_dir)
349 if not ok then return nil, "Failed reaching files from project - error entering directory "..local_dir end
350
351 if (not flags["summary"]) or (not flags["detailed"]) then
352 local summary, detailed = detect_description()
353 rockspec.description.summary = flags["summary"] or summary
354 rockspec.description.detailed = flags["detailed"] or detailed
355 end
356
357 local is_mit = show_license(rockspec)
358
359 if is_mit and not flags["license"] then
360 rockspec.description.license = "MIT"
361 end
362
363 fill_as_builtin(rockspec, libs)
364
365 rockspec_cleanup(rockspec)
366
367 persist.save_from_table(filename, rockspec, type_check.rockspec_order)
368
369 util.printout()
370 util.printout("Wrote template at "..filename.." -- you should now edit and finish it.")
371 util.printout()
372
373 return true
374end
375
376return write_rockspec