aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorV1K1NGbg <victor@ilchev.com>2024-08-18 01:44:35 +0300
committerV1K1NGbg <victor@ilchev.com>2024-08-18 01:44:35 +0300
commit9ada3aebd73450e29c64b2b6b56f85eba98e2634 (patch)
treeb5546d4a00ffc22a913dacefbff7a20d366140b3
parentc9b9fe3dda11afaf5b3a65798bb8072e0c9dc790 (diff)
downloadluarocks-9ada3aebd73450e29c64b2b6b56f85eba98e2634.tar.gz
luarocks-9ada3aebd73450e29c64b2b6b56f85eba98e2634.tar.bz2
luarocks-9ada3aebd73450e29c64b2b6b56f85eba98e2634.zip
some of cmd
-rw-r--r--src/luarocks/build.tl23
-rw-r--r--src/luarocks/cmd/build.tl209
-rw-r--r--src/luarocks/cmd/config.tl403
-rw-r--r--src/luarocks/cmd/doc.tl160
-rw-r--r--src/luarocks/cmd/download.tl63
-rw-r--r--src/luarocks/cmd/init.tl234
-rw-r--r--src/luarocks/cmd/install.tl262
-rw-r--r--src/luarocks/cmd/lint.tl57
-rw-r--r--src/luarocks/cmd/list.tl123
-rw-r--r--src/luarocks/cmd/make.tl176
-rw-r--r--src/luarocks/cmd/new_version.tl228
-rw-r--r--src/luarocks/cmd/pack.tl36
-rw-r--r--src/luarocks/cmd/path.tl83
-rw-r--r--src/luarocks/cmd/purge.tl72
-rw-r--r--src/luarocks/cmd/remove.tl71
-rw-r--r--src/luarocks/cmd/search.tl84
-rw-r--r--src/luarocks/cmd/show.tl314
-rw-r--r--src/luarocks/cmd/test.tl48
-rw-r--r--src/luarocks/cmd/unpack.tl169
-rw-r--r--src/luarocks/cmd/upload.tl128
-rw-r--r--src/luarocks/cmd/which.tl40
-rw-r--r--src/luarocks/cmd/write_rockspec.tl425
-rw-r--r--src/luarocks/core/cfg.d.tl22
-rw-r--r--src/luarocks/core/types/args.d.tl46
-rw-r--r--src/luarocks/core/types/bopts.d.tl17
-rw-r--r--src/luarocks/core/types/installs.d.tl10
-rw-r--r--src/luarocks/deps.tl7
-rw-r--r--src/luarocks/download.tl2
-rw-r--r--src/luarocks/fetch.tl2
-rw-r--r--src/luarocks/fs.d.tl7
-rw-r--r--src/luarocks/pack.tl2
-rw-r--r--src/luarocks/persist.tl14
-rw-r--r--src/luarocks/util.tl10
-rw-r--r--src/luarocks/vendor/argparse.d.tl8
-rw-r--r--src/luarocks/vendor/dkjson.d.tl2
35 files changed, 3512 insertions, 45 deletions
diff --git a/src/luarocks/build.tl b/src/luarocks/build.tl
index 610391b4..ffbc4f01 100644
--- a/src/luarocks/build.tl
+++ b/src/luarocks/build.tl
@@ -1,17 +1,5 @@
1 1
2local record build 2local record build
3 record Op_b
4 build_only_deps: boolean
5 deps_mode: string
6 verify: boolean
7 minimal_mode: boolean
8 need_to_fetch: boolean
9 branch: string
10 no_install: boolean
11 pin: boolean
12 namespace: string
13 end
14
15 record Builds 3 record Builds
16 skip_lua_inc_lib_check: boolean 4 skip_lua_inc_lib_check: boolean
17 run: function(Rockspec, boolean): boolean, string, string 5 run: function(Rockspec, boolean): boolean, string, string
@@ -46,7 +34,10 @@ local type b = require("luarocks.core.types.build")
46local type Build = b.Build 34local type Build = b.Build
47local type Module = b.BuiltinBuild.Module 35local type Module = b.BuiltinBuild.Module
48 36
49local type Op_b = build.Op_b 37local type bo = require("luarocks.core.types.bopts")
38local type BOpts = bo.BOpts
39
40
50local type Builds = build.Builds 41local type Builds = build.Builds
51 42
52do 43do
@@ -125,7 +116,7 @@ local function check_macosx_deployment_target(rockspec: Rockspec): boolean, stri
125 return true 116 return true
126end 117end
127 118
128local function process_dependencies(rockspec: Rockspec, opts: Op_b, cwd: string): boolean, string, string 119local function process_dependencies(rockspec: Rockspec, opts: BOpts, cwd: string): boolean, string, string
129 if not opts.build_only_deps then 120 if not opts.build_only_deps then
130 local ok, err, errcode = deps.check_external_deps(rockspec, "build") 121 local ok, err, errcode = deps.check_external_deps(rockspec, "build")
131 if err then 122 if err then
@@ -181,7 +172,7 @@ local function process_dependencies(rockspec: Rockspec, opts: Op_b, cwd: string)
181 return deps.fulfill_dependencies(rockspec, "dependencies", opts.deps_mode, opts.verify, deplock_dir) 172 return deps.fulfill_dependencies(rockspec, "dependencies", opts.deps_mode, opts.verify, deplock_dir)
182end 173end
183 174
184local function fetch_and_change_to_source_dir(rockspec: Rockspec, opts: Op_b): boolean, string, string 175local function fetch_and_change_to_source_dir(rockspec: Rockspec, opts: BOpts): boolean, string, string
185 if opts.minimal_mode or opts.build_only_deps then 176 if opts.minimal_mode or opts.build_only_deps then
186 return true 177 return true
187 end 178 end
@@ -414,7 +405,7 @@ end
414-- @param cwd string or nil: The current working directory 405-- @param cwd string or nil: The current working directory
415-- @return (string, string) or (nil, string, [string]): Name and version of 406-- @return (string, string) or (nil, string, [string]): Name and version of
416-- installed rock if succeeded or nil and an error message followed by an error code. 407-- installed rock if succeeded or nil and an error message followed by an error code.
417function build.build_rockspec(rockspec: Rockspec, opts: Op_b, cwd: string): string, string 408function build.build_rockspec(rockspec: Rockspec, opts: BOpts, cwd: string): string, string
418 409
419 cwd = cwd or dir.path(".") 410 cwd = cwd or dir.path(".")
420 411
diff --git a/src/luarocks/cmd/build.tl b/src/luarocks/cmd/build.tl
new file mode 100644
index 00000000..13577c1a
--- /dev/null
+++ b/src/luarocks/cmd/build.tl
@@ -0,0 +1,209 @@
1
2--- Module implementing the LuaRocks "build" command.
3-- Builds a rock, compiling its C parts if any.
4local record cmd_build
5 needs_lock: function(Args): boolean
6end
7
8local pack = require("luarocks.pack")
9local path = require("luarocks.path")
10local dir = require("luarocks.dir")
11local util = require("luarocks.util")
12local fetch = require("luarocks.fetch")
13local fs = require("luarocks.fs")
14local deps = require("luarocks.deps")
15local remove = require("luarocks.remove")
16local cfg = require("luarocks.core.cfg")
17local build = require("luarocks.build")
18local search = require("luarocks.search")
19local make = require("luarocks.cmd.make")
20local repos = require("luarocks.repos")
21
22local argparse = require("luarocks.vendor.argparse")
23local type Parser = argparse.Parser
24
25local type a = require("luarocks.core.types.args")
26local type Args = a.Args
27
28local type bo = require("luarocks.core.types.bopts")
29local type BOpts = bo.BOpts
30
31local type r = require("luarocks.core.types.rockspec")
32local type Rockspec = r.Rockspec
33
34function cmd_build.add_to_parser(parser: Parser)
35 local cmd = parser:command("build", "Build and install a rock, compiling its C parts if any.\n".. -- luacheck: ignore 431
36 "If the sources contain a luarocks.lock file, uses it as an authoritative source for "..
37 "exact version of dependencies.\n"..
38 "If no arguments are given, behaves as luarocks make.", util.see_also())
39 :summary("Build/compile a rock.")
40
41 cmd:argument("rock", "A rockspec file, a source rock file, or the name of "..
42 "a rock to be fetched from a repository.")
43 :args("?")
44 :action(util.namespaced_name_action)
45 cmd:argument("version", "Rock version.")
46 :args("?")
47
48 cmd:flag("--only-deps --deps-only", "Install only the dependencies of the rock.")
49 cmd:option("--branch", "Override the `source.branch` field in the loaded "..
50 "rockspec. Allows to specify a different branch to fetch. Particularly "..
51 'for "dev" rocks.')
52 :argname("<name>")
53 cmd:flag("--pin", "Create a luarocks.lock file listing the exact "..
54 "versions of each dependency found for this rock (recursively), "..
55 "and store it in the rock's directory. "..
56 "Ignores any existing luarocks.lock file in the rock's sources.")
57 make.cmd_options(cmd) --!
58end
59
60--- Build and install a rock.
61-- @param rock_filename string: local or remote filename of a rock.
62-- @param opts table: build options
63-- @return boolean or (nil, string, [string]): True if build was successful,
64-- or false and an error message and an optional error code.
65local function build_rock(rock_filename: string, opts: BOpts): boolean, string, string
66
67 local cwd = fs.absolute_name(dir.path("."))
68
69 local ok, err, errcode: boolean, string, string
70
71 local unpack_dir: string
72 unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_filename, nil, opts.verify)
73 if not unpack_dir then
74 return nil, err, errcode
75 end
76
77 local rockspec_filename = path.rockspec_name_from_rock(rock_filename)
78
79 ok, err = fs.change_dir(unpack_dir)
80 if not ok then return nil, err end
81
82 local rockspec: Rockspec
83 rockspec, err, errcode = fetch.load_rockspec(rockspec_filename)
84 if not rockspec then
85 return nil, err, errcode
86 end
87
88 ok, err, errcode = build.build_rockspec(rockspec, opts, cwd)
89
90 fs.pop_dir()
91 return ok, err, errcode
92end
93
94local function do_build(name: string, namespace: string, version: string, opts: BOpts): string, string, string
95
96 local url, err: string, string
97 if name:match("%.rockspec$") or name:match("%.rock$") then
98 url = name
99 else
100 url, err = search.find_src_or_rockspec(name, namespace, version, opts.check_lua_versions)
101 if not url then
102 return nil, err
103 end
104 end
105
106 name, version = path.parse_name(url)
107 if name and repos.is_installed(name, version) then
108 if not opts.rebuild then
109 util.printout(name .. " " .. version .. " is already installed in " .. path.root_dir(cfg.root_dir))
110 util.printout("Use --force to reinstall.")
111 return name, version, "skip"
112 end
113 end
114
115 if url:match("%.rockspec$") then
116 local cwd = fs.absolute_name(dir.path("."))
117 local rockspec, err = fetch.load_rockspec(url, nil, opts.verify)
118 if not rockspec then
119 return nil, err
120 end
121 return build.build_rockspec(rockspec, opts, cwd)
122 end
123
124 if url:match("%.src%.rock$") then
125 opts.need_to_fetch = false
126 end
127
128 return build_rock(url, opts)
129end
130
131--- Driver function for "build" command.
132-- If a package name is given, forwards the request to "search" and,
133-- if returned a result, installs the matching rock.
134-- When passing a package name, a version number may also be given.
135-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an
136-- error message otherwise. exitcode is optionally returned.
137function cmd_build.command(args: Args): boolean, string, string
138 if not args.rock then
139 return make.command(args)
140 end
141
142 local opts = {
143 need_to_fetch = true,
144 minimal_mode = false,
145 deps_mode = deps.get_deps_mode(args),
146 build_only_deps = not not (args.only_deps and not args.pack_binary_rock),
147 namespace = args.namespace,
148 branch = args.branch,
149 verify = not not args.verify,
150 check_lua_versions = not not args.check_lua_versions,
151 pin = not not args.pin,
152 rebuild = not not (args.force or args.force_fast),
153 no_install = false
154 }
155
156 if args.sign and not args.pack_binary_rock then
157 return nil, "In the build command, --sign is meant to be used only with --pack-binary-rock"
158 end
159
160 if args.pack_binary_rock then
161 return pack.pack_binary_rock(args.rock, args.namespace, args.version, args.sign, function(): string, string
162 local name, version = do_build(args.rock, args.namespace, args.version, opts)
163 if name and args.no_doc then
164 util.remove_doc_dir(name, version)
165 end
166 return name, version
167 end)
168 end
169
170 local name, version, skip = do_build(args.rock, args.namespace, args.version, opts)
171 if not name then
172 return nil, version
173 end
174 if skip == "skip" then
175 return name, version
176 end
177
178 if args.no_doc then
179 util.remove_doc_dir(name, version)
180 end
181
182 if opts.build_only_deps then
183 util.printout("Stopping after installing dependencies for " ..name.." "..version)
184 util.printout()
185 else
186 if (not args.keep) and not cfg.keep_other_versions then
187 local ok, err, warn = remove.remove_other_versions(name, version, args.force, args.force_fast)
188 if not ok then
189 return nil, err
190 elseif warn then
191 util.printerr(err)
192 end
193 end
194 end
195
196 if opts.deps_mode ~= "none" then
197 deps.check_dependencies(nil, deps.get_deps_mode(args))
198 end
199 return name, version
200end
201
202cmd_build.needs_lock = function(args: Args): boolean
203 if args.pack_binary_rock then
204 return false
205 end
206 return true
207end
208
209return cmd_build
diff --git a/src/luarocks/cmd/config.tl b/src/luarocks/cmd/config.tl
new file mode 100644
index 00000000..75e99198
--- /dev/null
+++ b/src/luarocks/cmd/config.tl
@@ -0,0 +1,403 @@
1--- Module implementing the LuaRocks "config" command.
2-- Queries information about the LuaRocks configuration.
3local record config_cmd
4end
5
6local persist = require("luarocks.persist")
7local config = require("luarocks.config")
8local cfg = require("luarocks.core.cfg")
9local util = require("luarocks.util")
10local deps = require("luarocks.deps")
11local dir = require("luarocks.dir")
12local fs = require("luarocks.fs")
13local json = require("luarocks.vendor.dkjson")
14
15local argparse = require("luarocks.vendor.argparse")
16local type Parser = argparse.Parser
17
18local type a = require("luarocks.core.types.args")
19local type Args = a.Args
20
21local type p = require("luarocks.core.types.persist")
22local type PersistableTable = p.PersistableTable
23
24function config_cmd.add_to_parser(parser: Parser)
25 local cmd = parser:command("config", [[
26Query information about the LuaRocks configuration.
27
28* When given a configuration key, it prints the value of that key according to
29 the currently active configuration (taking into account all config files and
30 any command-line flags passed)
31
32 Examples:
33 luarocks config variables.LUA_INCDIR
34 luarocks config lua_version
35
36* When given a configuration key and a value, it overwrites the config file (see
37 the --scope option below to determine which) and replaces the value of the
38 given key with the given value.
39
40 * `lua_dir` is a special key as it checks for a valid Lua installation
41 (equivalent to --lua-dir) and sets several keys at once.
42 * `lua_version` is a special key as it changes the default Lua version
43 used by LuaRocks commands (equivalent to passing --lua-version).
44
45 Examples:
46 luarocks config variables.OPENSSL_DIR /usr/local/openssl
47 luarocks config lua_dir /usr/local
48 luarocks config lua_version 5.3
49
50* When given a configuration key and --unset, it overwrites the config file (see
51 the --scope option below to determine which) and deletes that key from the
52 file.
53
54 Example: luarocks config variables.OPENSSL_DIR --unset
55
56* When given no arguments, it prints the entire currently active configuration,
57 resulting from reading the config files from all scopes.
58
59 Example: luarocks config]], util.see_also([[
60 https://github.com/luarocks/luarocks/wiki/Config-file-format
61 for detailed information on the LuaRocks config file format.
62]]))
63 :summary("Query information about the LuaRocks configuration.")
64
65 cmd:argument("key", "The configuration key.")
66 :args("?")
67 cmd:argument("value", "The configuration value.")
68 :args("?")
69
70 cmd:option("--scope", "The scope indicates which config file should be rewritten.\n"..
71 '* Using a wrapper created with `luarocks init`, the default is "project".\n'..
72 '* Using --local (or when `local_by_default` is `true`), the default is "user".\n'..
73 '* Otherwise, the default is "system".')
74 :choices({"system", "user", "project"})
75 cmd:flag("--unset", "Delete the key from the configuration file.")
76 cmd:flag("--json", "Output as JSON.")
77
78 -- Deprecated flags
79 cmd:flag("--lua-incdir"):hidden(true)
80 cmd:flag("--lua-libdir"):hidden(true)
81 cmd:flag("--lua-ver"):hidden(true)
82 cmd:flag("--system-config"):hidden(true)
83 cmd:flag("--user-config"):hidden(true)
84 cmd:flag("--rock-trees"):hidden(true)
85end
86
87local function config_file(conf: cfg.conf): boolean, string
88 print(dir.normalize(conf.file))
89 if conf.found then
90 return true
91 else
92 return nil, "file not found"
93 end
94end
95
96local function traverse_varstring(var: string, tbl: PersistableTable, fn: function(PersistableTable, string): (boolean, string), missing_parent?: function): boolean, string
97 local k, rs = var:match("^%[([0-9]+)%]%.(.*)$")
98 local r: string
99 if k then
100 k = tonumber(k)
101 else
102 k, r = var:match("^([^.[]+)%.(.*)$")
103 if not k then
104 k, r = var:match("^([^[]+)(%[.*)$")
105 end
106 end
107
108 if k then
109 if not tbl[k] and missing_parent then
110 missing_parent(tbl, k)
111 end
112
113 if tbl[k] then
114 return traverse_varstring(r, tbl[k] as PersistableTable, fn, missing_parent)
115 else
116 return nil, "Unknown entry " .. k
117 end
118 end
119
120 local i = var:match("^%[([0-9]+)%]$")
121 if i then
122 local var = tonumber(i)
123 end
124
125 return fn(tbl, var)
126end
127
128local function print_json(value: {string : any}): boolean
129 print(json.encode(value))
130 return true
131end
132
133local function print_entry(var: string, tbl: PersistableTable, is_json: boolean): boolean, string
134 return traverse_varstring(var, tbl, function(t: PersistableTable, k: string): boolean, string
135 if not t[k] then
136 return nil, "Unknown entry " .. k
137 end
138 local val = t[k]
139
140 if not config.should_skip(var, val) then
141 if is_json then
142 return print_json(val as {string : any})
143 elseif type(val) == "string" then
144 print(val)
145 else
146 persist.write_value(io.stdout as persist.Writer, val)
147 end
148 end
149 return true
150 end)
151end
152
153local function infer_type(var: string): string
154 local typ: string
155 traverse_varstring(var, cfg as PersistableTable, function(t: PersistableTable, k: string): boolean, string --!
156 if t[k] ~= nil then
157 typ = type(t[k])
158 end
159 end)
160 return typ
161end
162
163local function write_entries(keys: {string: any}, scope: string, do_unset: boolean): boolean, string
164 if scope == "project" and not cfg.config_files.project then
165 return nil, "Current directory is not part of a project. You may want to run `luarocks init`."
166 end
167
168 local file_name = (cfg.config_files as {string: {string: string}})[scope].file
169
170 local tbl, err = persist.load_config_file_if_basic(file_name, cfg)
171 if not tbl then
172 return nil, err
173 end
174
175 for var, val in util.sortedpairs(keys) do
176 traverse_varstring(var, tbl, function(t: PersistableTable, k: string): boolean, string
177 if do_unset then
178 t[k] = nil
179 else
180 local typ = infer_type(var)
181 local v
182 if typ == "number" and tonumber(val) then
183 v = tonumber(val)
184 elseif typ == "boolean" and val == "true" then
185 v = true
186 elseif typ == "boolean" and val == "false" then
187 v = false
188 else
189 v = val
190 end
191 t[k] = v
192 keys[var] = v
193 end
194 return true
195 end, function(p, k)
196 p[k] = {}
197 end)
198 end
199
200 local ok, err = fs.make_dir(dir.dir_name(file_name))
201 if not ok then
202 return nil, err
203 end
204
205 ok, err = persist.save_from_table(file_name, tbl)
206 if ok then
207 print(do_unset and "Removed" or "Wrote")
208 for var, val in util.sortedpairs(keys) do
209 if do_unset then
210 print(("\t%s"):format(var))
211 else
212 if type(val) == "string" then
213 print(("\t%s = %q"):format(var, val))
214 else
215 print(("\t%s = %s"):format(var, tostring(val)))
216 end
217 end
218 end
219 print(do_unset and "from" or "to")
220 print("\t" .. file_name)
221 return true
222 else
223 return nil, err
224 end
225end
226
227local function get_scope(args: Args): string
228 return args.scope
229 or (args["local"] and "user")
230 or (args.project_tree and "project")
231 or (cfg.local_by_default and "user")
232 or (fs.is_writable(cfg.config_files["system"].file) and "system")
233 or "user"
234end
235
236local function report_on_lua_incdir_config(value: string, lua_version: string): boolean
237 local variables = {
238 ["LUA_DIR"] = cfg.variables.LUA_DIR,
239 ["LUA_BINDIR"] = cfg.variables.LUA_BINDIR,
240 ["LUA_INCDIR"] = value,
241 ["LUA_LIBDIR"] = cfg.variables.LUA_LIBDIR,
242 ["LUA"] = cfg.variables.LUA,
243 }
244
245 local ok, err = deps.check_lua_incdir(variables, lua_version) --!
246 if not ok then
247 util.printerr()
248 util.warning((err:gsub(" You can use.*", "")))
249 end
250 return ok
251end
252
253local function report_on_lua_libdir_config(value: string, lua_version: string): boolean
254 local variables = {
255 ["LUA_DIR"] = cfg.variables.LUA_DIR,
256 ["LUA_BINDIR"] = cfg.variables.LUA_BINDIR,
257 ["LUA_INCDIR"] = cfg.variables.LUA_INCDIR,
258 ["LUA_LIBDIR"] = value,
259 ["LUA"] = cfg.variables.LUA,
260 }
261
262 local ok, err, _, err_files = deps.check_lua_libdir(variables, lua_version) --!
263 if not ok then
264 util.printerr()
265 util.warning((err:gsub(" You can use.*", "")))
266 util.printerr("Tried:")
267 for _, l in pairs(err_files or {}) do
268 for _, d in ipairs(l) do
269 util.printerr("\t" .. d)
270 end
271 end
272 end
273 return ok
274end
275
276local function warn_bad_c_config()
277 util.printerr()
278 util.printerr("LuaRocks may not work correctly when building C modules using this configuration.")
279 util.printerr()
280end
281
282--- Driver function for "config" command.
283-- @return boolean: True if succeeded, nil on errors.
284function config_cmd.command(args: Args): boolean, string
285 local lua_version = args.lua_version or cfg.lua_version
286
287 deps.check_lua_incdir(cfg.variables, lua_version)
288 deps.check_lua_libdir(cfg.variables, lua_version)
289
290 -- deprecated flags
291 if args.lua_incdir then
292 print(cfg.variables.LUA_INCDIR)
293 return true
294 end
295 if args.lua_libdir then
296 print(cfg.variables.LUA_LIBDIR)
297 return true
298 end
299 if args.lua_ver then
300 print(cfg.lua_version)
301 return true
302 end
303 if args.system_config then
304 return config_file(cfg.config_files.system)
305 end
306 if args.user_config then
307 return config_file(cfg.config_files.user)
308 end
309 if args.rock_trees then
310 for _, tree in ipairs(cfg.rocks_trees) do
311 if tree is string then
312 util.printout(dir.normalize(tree))
313 else
314 local name = tree.name and "\t"..tree.name or ""
315 util.printout(dir.normalize(tree.root)..name)
316 end
317 end
318 return true
319 end
320
321 if args.key == "lua_version" and args.value then
322 local scope = get_scope(args)
323 if scope == "project" and not cfg.config_files.project then
324 return nil, "Current directory is not part of a project. You may want to run `luarocks init`."
325 end
326
327 local location = (cfg.config_files as {string: {string: string}})[scope]
328 if (not location) or (not location.file) then
329 return nil, "could not get config file location for " .. tostring(scope) .. " scope"
330 end
331
332 local prefix = dir.dir_name(location.file)
333 local ok, err = persist.save_default_lua_version(prefix, args.value)
334 if not ok then
335 return nil, "could not set default Lua version: " .. err
336 end
337 print("Lua version will default to " .. args.value .. " in " .. prefix)
338 end
339
340 if args.key == "lua_dir" and args.value then
341 local scope = get_scope(args)
342 local keys = {
343 ["variables.LUA_DIR"] = cfg.variables.LUA_DIR,
344 ["variables.LUA_BINDIR"] = cfg.variables.LUA_BINDIR,
345 ["variables.LUA_INCDIR"] = cfg.variables.LUA_INCDIR,
346 ["variables.LUA_LIBDIR"] = cfg.variables.LUA_LIBDIR,
347 ["variables.LUA"] = cfg.variables.LUA,
348 }
349 if args.lua_version then
350 local prefix = dir.dir_name((cfg.config_files as {string: {string: string}})[scope].file)
351 persist.save_default_lua_version(prefix, args.lua_version)
352 end
353 local ok, err = write_entries(keys, scope, args.unset)
354 if ok then
355 local inc_ok = report_on_lua_incdir_config(cfg.variables.LUA_INCDIR, lua_version)
356 local lib_ok = ok and report_on_lua_libdir_config(cfg.variables.LUA_LIBDIR, lua_version)
357 if not (inc_ok and lib_ok) then
358 warn_bad_c_config()
359 end
360 end
361
362 return ok, err
363 end
364
365 if args.key then
366 if args.key:match("^[A-Z]") then
367 args.key = "variables." .. args.key
368 end
369
370 if args.value or args.unset then
371 local scope = get_scope(args)
372
373 local ok, err = write_entries({ [args.key] = args.value or args.unset }, scope, args.unset)
374
375 if ok then
376 if args.key == "variables.LUA_INCDIR" then
377 local ok = report_on_lua_incdir_config(args.value, lua_version)
378 if not ok then
379 warn_bad_c_config()
380 end
381 elseif args.key == "variables.LUA_LIBDIR" then
382 local ok = report_on_lua_libdir_config(args.value, lua_version)
383 if not ok then
384 warn_bad_c_config()
385 end
386 end
387 end
388
389 return ok, err
390 else
391 return print_entry(args.key, cfg as PersistableTable, args.json)
392 end
393 end
394
395 if args.json then
396 return print_json(config.get_config_for_display(cfg as PersistableTable) as {string : any})
397 else
398 print(config.to_string(cfg as PersistableTable))
399 return true
400 end
401end
402
403return config_cmd
diff --git a/src/luarocks/cmd/doc.tl b/src/luarocks/cmd/doc.tl
new file mode 100644
index 00000000..79d8e019
--- /dev/null
+++ b/src/luarocks/cmd/doc.tl
@@ -0,0 +1,160 @@
1
2--- Module implementing the LuaRocks "doc" command.
3-- Shows documentation for an installed rock.
4local record doc
5end
6
7local util = require("luarocks.util")
8local queries = require("luarocks.queries")
9local search = require("luarocks.search")
10local path = require("luarocks.path")
11local dir = require("luarocks.dir")
12local fetch = require("luarocks.fetch")
13local fs = require("luarocks.fs")
14local download = require("luarocks.download")
15
16local argparse = require("luarocks.vendor.argparse")
17local type Parser = argparse.Parser
18
19local type a = require("luarocks.core.types.args")
20local type Args = a.Args
21
22function doc.add_to_parser(parser: Parser)
23 local cmd = parser:command("doc", "Show documentation for an installed rock.\n\n"..
24 "Without any flags, tries to load the documentation using a series of heuristics.\n"..
25 "With flags, return only the desired information.", util.see_also([[
26 For more information about a rock, see the 'show' command.
27]]))
28 :summary("Show documentation for an installed rock.")
29
30 cmd:argument("rock", "Name of the rock.")
31 :action(util.namespaced_name_action)
32 cmd:argument("version", "Version of the rock.")
33 :args("?")
34
35 cmd:flag("--home", "Open the home page of project.")
36 cmd:flag("--list", "List documentation files only.")
37 cmd:flag("--porcelain", "Produce machine-friendly output.")
38end
39
40local function show_homepage(homepage: string, name: string, namespace: string, version: string): boolean, string
41 if not homepage then
42 return nil, "No 'homepage' field in rockspec for "..util.format_rock_name(name, namespace, version)
43 end
44 util.printout("Opening "..homepage.." ...")
45 fs.browser(homepage)
46 return true
47end
48
49local function try_to_open_homepage(name: string, namespace: string, version: string): boolean, string
50 local temp_dir, err = fs.make_temp_dir("doc-"..name.."-"..(version or ""))
51 if not temp_dir then
52 return nil, "Failed creating temporary directory: "..err
53 end
54 util.schedule_function(fs.delete, temp_dir)
55 local ok, err = fs.change_dir(temp_dir)
56 if not ok then return nil, err end
57 local filename, err = download.download_file("rockspec", name, namespace, version)
58 if not filename then return nil, err end
59 local rockspec, err = fetch.load_local_rockspec(filename)
60 if not rockspec then return nil, err end
61 fs.pop_dir()
62 local descript = rockspec.description or {}
63 return show_homepage(descript.homepage, name, namespace, version)
64end
65
66--- Driver function for "doc" command.
67-- @return boolean: True if succeeded, nil on errors.
68function doc.command(args: Args): boolean, string
69 local query = queries.new(args.rock, args.namespace, args.version)
70 local iname, iversion, repo = search.pick_installed_rock(query, args.tree)
71 if not iname then
72 local rock = util.format_rock_name(args.rock, args.namespace, args.version)
73 util.printout(rock.." is not installed. Looking for it in the rocks servers...")
74 return try_to_open_homepage(args.rock, args.namespace, args.version)
75 end
76 local name, version = iname, iversion
77
78 local rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version, repo))
79 if not rockspec then return nil,err end
80 local descript = rockspec.description or {}
81
82 if args.home then
83 return show_homepage(descript.homepage, name, args.namespace, version)
84 end
85
86 local directory = path.install_dir(name, version, repo)
87
88 local docdir: string
89 local directories = { "doc", "docs" }
90 for _, d in ipairs(directories) do
91 local dirname = dir.path(directory, d)
92 if fs.is_dir(dirname) then
93 docdir = dirname
94 break
95 end
96 end
97 if not docdir then
98 if descript.homepage and not args.list then
99 util.printout("Local documentation directory not found -- opening "..descript.homepage.." ...")
100 fs.browser(descript.homepage)
101 return true
102 end
103 return nil, "Documentation directory not found for "..name.." "..version
104 end
105
106 docdir = dir.normalize(docdir)
107 local files = fs.find(docdir)
108 local htmlpatt = "%.html?$"
109 local extensions = { htmlpatt, "%.md$", "%.txt$", "%.textile$", "" }
110 local basenames = { "index", "readme", "manual" }
111
112 local porcelain = args.porcelain
113 if #files > 0 then
114 util.title("Documentation files for "..name.." "..version, porcelain)
115 if porcelain then
116 for _, file in ipairs(files) do
117 util.printout(docdir.."/"..file)
118 end
119 else
120 util.printout(docdir.."/")
121 for _, file in ipairs(files) do
122 util.printout("\t"..file)
123 end
124 end
125 end
126
127 if args.list then
128 return true
129 end
130
131 for _, extension in ipairs(extensions) do
132 for _, basename in ipairs(basenames) do
133 local filename = basename..extension
134 local found: string
135 for _, file in ipairs(files) do
136 if file:lower():match(filename) and ((not found) or #file < #found) then
137 found = file
138 end
139 end
140 if found then
141 local pathname = dir.path(docdir, found)
142 util.printout()
143 util.printout("Opening "..pathname.." ...")
144 util.printout()
145 local ok = fs.browser(pathname)
146 if not ok and not pathname:match(htmlpatt) then
147 local fd = io.open(pathname, "r")
148 util.printout(fd:read("*a"))
149 fd:close()
150 end
151 return true
152 end
153 end
154 end
155
156 return true
157end
158
159
160return doc
diff --git a/src/luarocks/cmd/download.tl b/src/luarocks/cmd/download.tl
new file mode 100644
index 00000000..d088ea71
--- /dev/null
+++ b/src/luarocks/cmd/download.tl
@@ -0,0 +1,63 @@
1
2--- Module implementing the luarocks "download" command.
3-- Download a rock from the repository.
4local record cmd_download
5end
6
7local util = require("luarocks.util")
8local download = require("luarocks.download")
9
10local argparse = require("luarocks.vendor.argparse")
11local type Parser = argparse.Parser
12
13local type a = require("luarocks.core.types.args")
14local type Args = a.Args
15
16function cmd_download.add_to_parser(parser: Parser)
17 local cmd = parser:command("download", "Download a specific rock file from a rocks server.", util.see_also())
18
19 cmd:argument("name", "Name of the rock.")
20 :args("?")
21 :action(util.namespaced_name_action)
22 cmd:argument("version", "Version of the rock.")
23 :args("?")
24
25 cmd:flag("--all", "Download all files if there are multiple matches.")
26 cmd:mutex(
27 cmd:flag("--source", "Download .src.rock if available."),
28 cmd:flag("--rockspec", "Download .rockspec if available."),
29 cmd:option("--arch", "Download rock for a specific architecture."))
30 cmd:flag("--check-lua-versions", "If the rock can't be found, check repository "..
31 "and report if it is available for another Lua version.")
32end
33
34--- Driver function for the "download" command.
35-- @return boolean or (nil, string): true if successful or nil followed
36-- by an error message.
37function cmd_download.command(args: Args): boolean, string
38 if not args.name and not args.all then
39 return nil, "Argument missing. "..util.see_help("download")
40 end
41
42 args.name = args.name or ""
43
44 local arch: string
45
46 if args.source then
47 arch = "src"
48 elseif args.rockspec then
49 arch = "rockspec"
50 elseif args.arch then
51 arch = args.arch
52 end
53
54 if args.all then
55 local ok, err = download.download_all(arch, args.name, args.namespace, args.version)
56 return ok, err
57 else
58 local dl, err = download.download_file(arch, args.name, args.namespace, args.version, args.check_lua_versions)
59 return dl and true, err
60 end
61end
62
63return cmd_download
diff --git a/src/luarocks/cmd/init.tl b/src/luarocks/cmd/init.tl
new file mode 100644
index 00000000..5223d01c
--- /dev/null
+++ b/src/luarocks/cmd/init.tl
@@ -0,0 +1,234 @@
1
2local record init
3 needs_lock: function(Args): boolean
4end
5
6local cfg = require("luarocks.core.cfg")
7local fs = require("luarocks.fs")
8local path = require("luarocks.path")
9local deps = require("luarocks.deps")
10local dir = require("luarocks.dir")
11local util = require("luarocks.util")
12local persist = require("luarocks.persist")
13local write_rockspec = require("luarocks.cmd.write_rockspec")
14
15local argparse = require("luarocks.vendor.argparse")
16local type Parser = argparse.Parser
17
18local type a = require("luarocks.core.types.args")
19local type Args = a.Args
20
21
22local type p = require("luarocks.core.types.persist")
23local type PersistableTable = p.PersistableTable
24
25local type t = require("luarocks.core.types.tree")
26local type Tree = t.Tree
27
28function init.add_to_parser(parser: Parser)
29 local cmd = parser:command("init", "Initialize a directory for a Lua project using LuaRocks.", util.see_also())
30
31 cmd:argument("name", "The project name.")
32 :args("?")
33 cmd:argument("version", "An optional project version.")
34 :args("?")
35 cmd:option("--wrapper-dir", "Location where the 'lua' and 'luarocks' wrapper scripts " ..
36 "should be generated; if not given, the current directory is used as a default.")
37 cmd:flag("--reset", "Delete any .luarocks/config-5.x.lua and ./lua and generate new ones.")
38 cmd:flag("--no-wrapper-scripts", "Do not generate wrapper ./lua and ./luarocks launcher scripts.")
39 cmd:flag("--no-gitignore", "Do not generate a .gitignore file.")
40
41 cmd:group("Options for specifying rockspec data", write_rockspec.cmd_options(cmd)) --!
42end
43
44local function gitignore_path(pwd: string, wrapper_dir: string, filename: string): string
45 local norm_cur = fs.absolute_name(pwd)
46 local norm_file = fs.absolute_name(dir.path(wrapper_dir, filename))
47 if norm_file:sub(1, #norm_cur) == norm_cur then
48 return norm_file:sub(#norm_cur + 2)
49 else
50 return filename
51 end
52end
53
54local function write_gitignore(entries: {string})
55 local gitignore = ""
56 local fd = io.open(".gitignore", "r")
57 if fd then
58 gitignore = fd:read("*a")
59 fd:close()
60 gitignore = "\n" .. gitignore .. "\n"
61 end
62
63 fd = io.open(".gitignore", gitignore and "a" or "w")
64 if fd then
65 for _, entry in ipairs(entries) do
66 entry = "/" .. entry
67 if not gitignore:find("\n"..entry.."\n", 1, true) then
68 fd:write(entry.."\n")
69 end
70 end
71 fd:close()
72 end
73end
74
75local function inject_tree(tree: string | Tree)
76 path.use_tree(tree)
77 local tree_set = false
78 for _, t in ipairs(cfg.rocks_trees) do
79 if t is Tree then
80 if t.name == "project" then
81 t.root = tree as string
82 tree_set = true
83 end
84 end
85 end
86 if not tree_set then
87 table.insert(cfg.rocks_trees, 1, { name = "project", root = tree } as Tree)
88 end
89end
90
91local function write_wrapper_scripts(wrapper_dir: string, luarocks_wrapper: string, lua_wrapper: string)
92 local tree = dir.path(fs.current_dir(), "lua_modules")
93
94 fs.make_dir(wrapper_dir)
95
96 luarocks_wrapper = dir.path(wrapper_dir, luarocks_wrapper)
97 if not fs.exists(luarocks_wrapper) then
98 util.printout("Preparing " .. luarocks_wrapper .. " ...")
99 fs.wrap_script(arg[0], luarocks_wrapper, "none", nil, nil, "--project-tree", tree)
100 else
101 util.printout(luarocks_wrapper .. " already exists. Not overwriting it!")
102 end
103
104 lua_wrapper = dir.path(wrapper_dir, lua_wrapper)
105 local write_lua_wrapper = true
106 if fs.exists(lua_wrapper) then
107 if not util.lua_is_wrapper(lua_wrapper) then
108 util.printout(lua_wrapper .. " already exists and does not look like a wrapper script. Not overwriting.")
109 write_lua_wrapper = false
110 end
111 end
112
113 if write_lua_wrapper then
114 if util.check_lua_version(cfg.variables.LUA, cfg.lua_version) then
115 util.printout("Preparing " .. lua_wrapper .. " for version " .. cfg.lua_version .. "...")
116
117 -- Inject tree so it shows up as a lookup path in the wrappers
118 inject_tree(tree)
119
120 fs.wrap_script(nil, lua_wrapper, "all")
121 else
122 util.warning("No Lua interpreter detected for version " .. cfg.lua_version .. ". Not creating " .. lua_wrapper)
123 end
124 end
125end
126
127--- Driver function for "init" command.
128-- @return boolean: True if succeeded, nil on errors.
129function init.command(args: Args): boolean, string
130 local do_gitignore = not args.no_gitignore
131 local do_wrapper_scripts = not args.no_wrapper_scripts
132 local wrapper_dir = args.wrapper_dir or "."
133
134 local pwd = fs.current_dir()
135
136 if not args.name then
137 args.name = dir.base_name(pwd)
138 if args.name == "/" then
139 return nil, "When running from the root directory, please specify the <name> argument"
140 end
141 end
142
143 util.title("Initializing project '" .. args.name .. "' for Lua " .. cfg.lua_version .. " ...")
144
145 local ok, err = deps.check_lua_incdir(cfg.variables)
146 if not ok then
147 return nil, err
148 end
149
150 local has_rockspec = false
151 for file in fs.dir() do
152 if file:match("%.rockspec$") then
153 has_rockspec = true
154 break
155 end
156 end
157
158 if not has_rockspec then
159 args.version = args.version or "dev"
160 args.location = pwd
161 local ok, err = write_rockspec.command(args)
162 if not ok then
163 util.printerr(err)
164 end
165 end
166
167 local ext = cfg.wrapper_suffix
168 local luarocks_wrapper = "luarocks" .. ext
169 local lua_wrapper = "lua" .. ext
170
171 if do_gitignore then
172 util.printout("Adding entries to .gitignore ...")
173 local ignores = { "lua_modules", ".luarocks" }
174 if do_wrapper_scripts then
175 table.insert(ignores, 1, gitignore_path(pwd, wrapper_dir, luarocks_wrapper))
176 table.insert(ignores, 2, gitignore_path(pwd, wrapper_dir, lua_wrapper))
177 end
178 write_gitignore(ignores)
179 end
180
181 util.printout("Preparing ./.luarocks/ ...")
182 fs.make_dir(".luarocks")
183 local config_file = ".luarocks/config-" .. cfg.lua_version .. ".lua"
184
185 if args.reset then
186 if do_wrapper_scripts then
187 fs.delete(fs.absolute_name(dir.path(wrapper_dir, lua_wrapper)))
188 end
189 fs.delete(fs.absolute_name(config_file))
190 end
191
192 local config_tbl, err = persist.load_config_file_if_basic(config_file, cfg)
193 if config_tbl then
194 local varnames = {
195 "LUA_DIR",
196 "LUA_INCDIR",
197 "LUA_LIBDIR",
198 "LUA_BINDIR",
199 "LUA",
200 }
201 for _, varname in ipairs(varnames) do
202 if cfg.variables[varname] then
203 config_tbl.variables = config_tbl.variables as PersistableTable or {}
204 (config_tbl.variables as PersistableTable)[varname] = cfg.variables[varname]
205 end
206 end
207 local ok, err = persist.save_from_table(config_file, config_tbl)
208 if ok then
209 util.printout("Wrote " .. config_file)
210 else
211 util.printout("Failed writing " .. config_file .. ": " .. err)
212 end
213 else
214 util.printout("Will not attempt to overwrite " .. config_file)
215 end
216
217 ok, err = persist.save_default_lua_version(".luarocks", cfg.lua_version)
218 if not ok then
219 util.printout("Failed setting default Lua version: " .. err)
220 end
221
222 util.printout("Preparing ./lua_modules/ ...")
223 fs.make_dir("lua_modules/lib/luarocks/rocks-" .. cfg.lua_version)
224
225 if do_wrapper_scripts then
226 write_wrapper_scripts(wrapper_dir, luarocks_wrapper, lua_wrapper)
227 end
228
229 return true
230end
231
232init.needs_lock = function(): boolean return true end
233
234return init
diff --git a/src/luarocks/cmd/install.tl b/src/luarocks/cmd/install.tl
new file mode 100644
index 00000000..db72c77d
--- /dev/null
+++ b/src/luarocks/cmd/install.tl
@@ -0,0 +1,262 @@
1--- Module implementing the LuaRocks "install" command.
2-- Installs binary rocks.
3local record install
4 needs_lock: function(Args): boolean
5end
6
7local dir = require("luarocks.dir")
8local path = require("luarocks.path")
9local repos = require("luarocks.repos")
10local fetch = require("luarocks.fetch")
11local util = require("luarocks.util")
12local fs = require("luarocks.fs")
13local deps = require("luarocks.deps")
14local repo_writer = require("luarocks.repo_writer")
15local remove = require("luarocks.remove")
16local search = require("luarocks.search")
17local queries = require("luarocks.queries")
18local cfg = require("luarocks.core.cfg")
19
20local argparse = require("luarocks.vendor.argparse")
21local type Parser = argparse.Parser
22
23local type a = require("luarocks.core.types.args")
24local type Args = a.Args
25
26
27local type i = require("luarocks.core.types.installs")
28local type IOpts = i.IOpts
29
30function install.add_to_parser(parser: Parser)
31 local cmd = parser:command("install", "Install a rock.", util.see_also()) -- luacheck: ignore 431
32
33 cmd:argument("rock", "The name of a rock to be fetched from a repository "..
34 "or a filename of a locally available rock.")
35 :action(util.namespaced_name_action)
36 cmd:argument("version", "Version of the rock.")
37 :args("?")
38
39 cmd:flag("--keep", "Do not remove previously installed versions of the "..
40 "rock after building a new one. This behavior can be made permanent by "..
41 "setting keep_other_versions=true in the configuration file.")
42 cmd:flag("--force", "If --keep is not specified, force removal of "..
43 "previously installed versions if it would break dependencies. "..
44 "If rock is already installed, reinstall it anyway.")
45 cmd:flag("--force-fast", "Like --force, but performs a forced removal "..
46 "without reporting dependency issues.")
47 cmd:flag("--only-deps --deps-only", "Install only the dependencies of the rock.")
48 cmd:flag("--no-doc", "Install the rock without its documentation.")
49 cmd:flag("--verify", "Verify signature of the rockspec or src.rock being "..
50 "built. If the rockspec or src.rock is being downloaded, LuaRocks will "..
51 "attempt to download the signature as well. Otherwise, the signature "..
52 "file should be already available locally in the same directory.\n"..
53 "You need the signer’s public key in your local keyring for this "..
54 "option to work properly.")
55 cmd:flag("--check-lua-versions", "If the rock can't be found, check repository "..
56 "and report if it is available for another Lua version.")
57 util.deps_mode_option(cmd) --!
58 cmd:flag("--no-manifest", "Skip creating/updating the manifest")
59 cmd:flag("--pin", "If the installed rock is a Lua module, create a "..
60 "luarocks.lock file listing the exact versions of each dependency found for "..
61 "this rock (recursively), and store it in the rock's directory. "..
62 "Ignores any existing luarocks.lock file in the rock's sources.")
63 -- luarocks build options
64 parser:flag("--pack-binary-rock"):hidden(true)
65 parser:option("--branch"):hidden(true)
66 parser:flag("--sign"):hidden(true)
67end
68
69
70--- Install a binary rock.
71-- @param rock_file string: local or remote filename of a rock.
72-- @param opts table: installation options
73-- @return (string, string) or (nil, string, [string]): Name and version of
74-- installed rock if succeeded or nil and an error message followed by an error code.
75function install.install_binary_rock(rock_file: string, opts: IOpts): string, string, string
76 assert(type(rock_file) == "string")
77
78 local namespace = opts.namespace
79 local deps_mode = opts.deps_mode
80
81 local name, version, arch = path.parse_name(rock_file)
82 if not name then
83 return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'."
84 end
85
86 if arch ~= "all" and arch ~= cfg.arch then
87 return nil, "Incompatible architecture "..arch, "arch"
88 end
89 if repos.is_installed(name, version) then
90 if not (opts.force or opts.force_fast) then
91 util.printout(name .. " " .. version .. " is already installed in " .. path.root_dir(cfg.root_dir))
92 util.printout("Use --force to reinstall.")
93 return name, version
94 end
95 repo_writer.delete_version(name, version, opts.deps_mode)
96 end
97
98 local install_dir = path.install_dir(name, version)
99
100 local rollback = util.schedule_function(function()
101 fs.delete(install_dir)
102 fs.remove_dir_if_empty(path.versions_dir(name))
103 end)
104 local ok: boolean
105 local oks, err, errcode = fetch.fetch_and_unpack_rock(rock_file, install_dir, opts.verify)
106 if not oks then return nil, err, errcode end
107
108 local rockspec, err = fetch.load_rockspec(path.rockspec_file(name, version))
109 if err then
110 return nil, "Failed loading rockspec for installed package: "..err, errcode
111 end
112
113 if opts.deps_mode ~= "none" then
114 ok, err, errcode = deps.check_external_deps(rockspec, "install")
115 if err then return nil, err, errcode end
116 end
117
118 if deps_mode ~= "none" then
119 local deplock_dir = fs.exists(dir.path(".", "luarocks.lock"))
120 and "."
121 or install_dir
122 ok, err, errcode = deps.fulfill_dependencies(rockspec, "dependencies", deps_mode, opts.verify, deplock_dir)
123 if err then return nil, err, errcode end
124 end
125
126 ok, err = repo_writer.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode, namespace)
127 if err then return nil, err end
128
129 util.remove_scheduled_function(rollback)
130 rollback = util.schedule_function(function()
131 repo_writer.delete_version(name, version, deps_mode)
132 end)
133
134 ok, err = repos.run_hook(rockspec, "post_install")
135 if err then return nil, err end
136
137 util.announce_install(rockspec)
138 util.remove_scheduled_function(rollback)
139 return name, version
140end
141
142--- Installs the dependencies of a binary rock.
143-- @param rock_file string: local or remote filename of a rock.
144-- @param opts table: installation options
145-- @return (string, string) or (nil, string, [string]): Name and version of
146-- the rock whose dependencies were installed if succeeded or nil and an error message
147-- followed by an error code.
148function install.install_binary_rock_deps(rock_file: string, opts: IOpts): string, string, string
149 assert(type(rock_file) == "string")
150
151 local name, version, arch = path.parse_name(rock_file)
152 if not name then
153 return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'."
154 end
155
156 if arch ~= "all" and arch ~= cfg.arch then
157 return nil, "Incompatible architecture "..arch, "arch"
158 end
159
160 local install_dir = path.install_dir(name, version)
161
162 local ok: boolean
163 local oks, err, errcode = fetch.fetch_and_unpack_rock(rock_file, install_dir, opts.verify)
164 if not oks then return nil, err, errcode end
165
166 local rockspec, err = fetch.load_rockspec(path.rockspec_file(name, version))
167 if err then
168 return nil, "Failed loading rockspec for installed package: "..err, errcode
169 end
170
171 local deplock_dir = fs.exists(dir.path(".", "luarocks.lock"))
172 and "."
173 or install_dir
174 ok, err, errcode = deps.fulfill_dependencies(rockspec, "dependencies", opts.deps_mode, opts.verify, deplock_dir)
175 if err then return nil, err, errcode end
176
177 util.printout()
178 util.printout("Successfully installed dependencies for " ..name.." "..version)
179
180 return name, version
181end
182
183local function install_rock_file_deps(filename: string, opts: IOpts): string, string
184
185 local name, version = install.install_binary_rock_deps(filename, opts)
186 if not name then return nil, version end
187
188 deps.check_dependencies(nil, opts.deps_mode)
189 return name, version
190end
191
192local function install_rock_file(filename: string, opts: IOpts): string, string
193
194 local name, version = install.install_binary_rock(filename, opts)
195 if not name then return nil, version end
196
197 if opts.no_doc then
198 util.remove_doc_dir(name, version)
199 end
200
201 if (not opts.keep) and not cfg.keep_other_versions then
202 local ok, err, warn = remove.remove_other_versions(name, version, opts.force, opts.force_fast)
203 if not ok then
204 return nil, err
205 elseif warn then
206 util.printerr(err)
207 end
208 end
209
210 deps.check_dependencies(nil, opts.deps_mode)
211 return name, version
212end
213
214--- Driver function for the "install" command.
215-- If an URL or pathname to a binary rock is given, fetches and installs it.
216-- If a rockspec or a source rock is given, forwards the request to the "build"
217-- command.
218-- If a package name is given, forwards the request to "search" and,
219-- if returned a result, installs the matching rock.
220-- @return boolean or (nil, string, exitcode): True if installation was
221-- successful, nil and an error message otherwise. exitcode is optionally returned.
222function install.command(args: Args): boolean, string, string
223 if args.rock:match("%.rockspec$") or args.rock:match("%.src%.rock$") then
224 local build = require("luarocks.cmd.build")
225 return build.command(args)
226 elseif args.rock:match("%.rock$") then
227 local deps_mode = deps.get_deps_mode(args)
228 local opts = {
229 namespace = args.namespace,
230 keep = not not args.keep,
231 force = not not args.force,
232 force_fast = not not args.force_fast,
233 no_doc = not not args.no_doc,
234 deps_mode = deps_mode,
235 verify = not not args.verify,
236 }
237 if args.only_deps then
238 return install_rock_file_deps(args.rock, opts)
239 else
240 return install_rock_file(args.rock, opts)
241 end
242 else
243 local url, err = search.find_rock_checking_lua_versions(
244 queries.new(args.rock, args.namespace, args.version),
245 args.check_lua_versions)
246 if not url then
247 return nil, err
248 end
249 util.printout("Installing "..url)
250 args.rock = url
251 return install.command(args)
252 end
253end
254
255install.needs_lock = function(args: Args): boolean
256 if args.pack_binary_rock then
257 return false
258 end
259 return true
260end
261
262return install
diff --git a/src/luarocks/cmd/lint.tl b/src/luarocks/cmd/lint.tl
new file mode 100644
index 00000000..41fdf096
--- /dev/null
+++ b/src/luarocks/cmd/lint.tl
@@ -0,0 +1,57 @@
1
2--- Module implementing the LuaRocks "lint" command.
3-- Utility function that checks syntax of the rockspec.
4local record lint
5end
6
7local util = require("luarocks.util")
8local download = require("luarocks.download")
9local fetch = require("luarocks.fetch")
10
11local argparse = require("luarocks.vendor.argparse")
12local type Parser = argparse.Parser
13
14local type a = require("luarocks.core.types.args")
15local type Args = a.Args
16
17function lint.add_to_parser(parser: Parser)
18 local cmd = parser:command("lint", "Check syntax of a rockspec.\n\n"..
19 "Returns success if the text of the rockspec is syntactically correct, else failure.",
20 util.see_also())
21 :summary("Check syntax of a rockspec.")
22
23 cmd:argument("rockspec", "The rockspec to check.")
24end
25
26function lint.command(args: Args): boolean, string, string
27
28 local filename = args.rockspec
29 if not filename:match(".rockspec$") then
30 local err: string
31 filename, err = download.download_file("rockspec", filename:lower())
32 if not filename then
33 return nil, err
34 end
35 end
36
37 local rs, err = fetch.load_local_rockspec(filename)
38 if not rs then
39 return nil, "Failed loading rockspec: "..err
40 end
41
42 local ok = true
43
44 -- This should have been done in the type checker,
45 -- but it would break compatibility of other commands.
46 -- Making 'lint' alone be stricter shouldn't be a problem,
47 -- because extra-strict checks is what lint-type commands
48 -- are all about.
49 if not rs.description or not rs.description.license then
50 util.printerr("Rockspec has no description.license field.")
51 ok = false
52 end
53
54 return ok, ok or filename.." failed consistency checks."
55end
56
57return lint
diff --git a/src/luarocks/cmd/list.tl b/src/luarocks/cmd/list.tl
new file mode 100644
index 00000000..56f4de02
--- /dev/null
+++ b/src/luarocks/cmd/list.tl
@@ -0,0 +1,123 @@
1
2--- Module implementing the LuaRocks "list" command.
3-- Lists currently installed rocks.
4local record list
5end
6
7local search = require("luarocks.search")
8local queries = require("luarocks.queries")
9local vers = require("luarocks.core.vers")
10local cfg = require("luarocks.core.cfg")
11local util = require("luarocks.util")
12local path = require("luarocks.path")
13
14local argparse = require("luarocks.vendor.argparse")
15local type Parser = argparse.Parser
16local type Option = argparse.Option
17
18local type a = require("luarocks.core.types.args")
19local type Args = a.Args
20
21local type bo = require("luarocks.core.types.bopts")
22local type BOpts = bo.BOpts
23
24local type i = require("luarocks.core.types.installs")
25local type IOpts = i.IOpts
26
27local type p = require("luarocks.core.types.persist")
28local type PersistableTable = p.PersistableTable
29
30local type t = require("luarocks.core.types.tree")
31local type Tree = t.Tree
32
33local type b = require("luarocks.core.types.build")
34local type BuiltinBuild = b.BuiltinBuild
35
36local type r = require("luarocks.core.types.rockspec")
37local type Rockspec = r.Rockspec
38local type Dependencies = r.Dependencies
39
40function list.add_to_parser(parser)
41 local cmd = parser:command("list", "List currently installed rocks.", util.see_also())
42
43 cmd:argument("filter", "A substring of a rock name to filter by.")
44 :args("?")
45 cmd:argument("version", "Rock version to filter by.")
46 :args("?")
47
48 cmd:flag("--outdated", "List only rocks for which there is a higher "..
49 "version available in the rocks server.")
50 cmd:flag("--porcelain", "Produce machine-friendly output.")
51end
52
53local function check_outdated(trees, query)
54 local results_installed = {}
55 for _, tree in ipairs(trees) do
56 search.local_manifest_search(results_installed, path.rocks_dir(tree), query)
57 end
58 local outdated = {}
59 for name, versions in util.sortedpairs(results_installed) do
60 versions = util.keys(versions)
61 table.sort(versions, vers.compare_versions)
62 local latest_installed = versions[1]
63
64 local query_available = queries.new(name:lower())
65 local results_available, err = search.search_repos(query_available)
66
67 if results_available[name] then
68 local available_versions = util.keys(results_available[name])
69 table.sort(available_versions, vers.compare_versions)
70 local latest_available = available_versions[1]
71 local latest_available_repo = results_available[name][latest_available][1].repo
72
73 if vers.compare_versions(latest_available, latest_installed) then
74 table.insert(outdated, { name = name, installed = latest_installed, available = latest_available, repo = latest_available_repo })
75 end
76 end
77 end
78 return outdated
79end
80
81local function list_outdated(trees, query, porcelain)
82 util.title("Outdated rocks:", porcelain)
83 local outdated = check_outdated(trees, query)
84 for _, item in ipairs(outdated) do
85 if porcelain then
86 util.printout(item.name, item.installed, item.available, item.repo)
87 else
88 util.printout(item.name)
89 util.printout(" "..item.installed.." < "..item.available.." at "..item.repo)
90 util.printout()
91 end
92 end
93 return true
94end
95
96--- Driver function for "list" command.
97-- @return boolean: True if succeeded, nil on errors.
98function list.command(args)
99 local query = queries.new(args.filter and args.filter:lower() or "", args.namespace, args.version, true)
100 local trees = cfg.rocks_trees
101 local title = "Rocks installed for Lua "..cfg.lua_version
102 if args.tree then
103 trees = { args.tree }
104 title = title .. " in " .. args.tree
105 end
106
107 if args.outdated then
108 return list_outdated(trees, query, args.porcelain)
109 end
110
111 local results = {}
112 for _, tree in ipairs(trees) do
113 local ok, err, errcode = search.local_manifest_search(results, path.rocks_dir(tree), query)
114 if not ok and errcode ~= "open" then
115 util.warning(err)
116 end
117 end
118 util.title(title, args.porcelain)
119 search.print_result_tree(results, args.porcelain)
120 return true
121end
122
123return list
diff --git a/src/luarocks/cmd/make.tl b/src/luarocks/cmd/make.tl
new file mode 100644
index 00000000..ab6c83da
--- /dev/null
+++ b/src/luarocks/cmd/make.tl
@@ -0,0 +1,176 @@
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 record make
7 needs_lock: function(Args): boolean
8end
9
10local build = require("luarocks.build")
11local util = require("luarocks.util")
12local cfg = require("luarocks.core.cfg")
13local fetch = require("luarocks.fetch")
14local pack = require("luarocks.pack")
15local remove = require("luarocks.remove")
16local deps = require("luarocks.deps")
17local dir = require("luarocks.dir")
18local fs = require("luarocks.fs")
19
20local argparse = require("luarocks.vendor.argparse")
21local type Parser = argparse.Parser
22
23local type a = require("luarocks.core.types.args")
24local type Args = a.Args
25
26local type bo = require("luarocks.core.types.bopts")
27local type BOpts = bo.BOpts
28
29function make.cmd_options(parser: Parser)
30 parser:flag("--no-install", "Do not install the rock.")
31 parser:flag("--no-doc", "Install the rock without its documentation.")
32 parser:flag("--pack-binary-rock", "Do not install rock. Instead, produce a "..
33 ".rock file with the contents of compilation in the current directory.")
34 parser:flag("--keep", "Do not remove previously installed versions of the "..
35 "rock after building a new one. This behavior can be made permanent by "..
36 "setting keep_other_versions=true in the configuration file.")
37 parser:flag("--force", "If --keep is not specified, force removal of "..
38 "previously installed versions if it would break dependencies. "..
39 "If rock is already installed, reinstall it anyway.")
40 parser:flag("--force-fast", "Like --force, but performs a forced removal "..
41 "without reporting dependency issues.")
42 parser:flag("--verify", "Verify signature of the rockspec or src.rock being "..
43 "built. If the rockspec or src.rock is being downloaded, LuaRocks will "..
44 "attempt to download the signature as well. Otherwise, the signature "..
45 "file should be already available locally in the same directory.\n"..
46 "You need the signer’s public key in your local keyring for this "..
47 "option to work properly.")
48 parser:flag("--sign", "To be used with --pack-binary-rock. Also produce a "..
49 "signature file for the generated .rock file.")
50 parser:flag("--check-lua-versions", "If the rock can't be found, check repository "..
51 "and report if it is available for another Lua version.")
52 parser:flag("--pin", "Pin the exact dependencies used for the rockspec"..
53 "being built into a luarocks.lock file in the current directory.")
54 parser:flag("--no-manifest", "Skip creating/updating the manifest")
55 parser:flag("--only-deps --deps-only", "Install only the dependencies of the rock.")
56 util.deps_mode_option(parser)
57end
58
59function make.add_to_parser(parser: Parser)
60 -- luacheck: push ignore 431
61 local cmd = parser:command("make", [[
62Builds sources in the current directory, but unlike "build", it does not fetch
63sources, etc., assuming everything is available in the current directory. If no
64argument is given, it looks for a rockspec in the current directory and in
65"rockspec/" and "rockspecs/" subdirectories, picking the rockspec with newest
66version or without version name. If rockspecs for different rocks are found or
67there are several rockspecs without version, you must specify which to use,
68through the command-line.
69
70This command is useful as a tool for debugging rockspecs.
71To install rocks, you'll normally want to use the "install" and "build"
72commands. See the help on those for details.
73
74If the current directory contains a luarocks.lock file, it is used as the
75authoritative source for exact version of dependencies. The --pin flag
76overrides and recreates this file scanning dependency based on ranges.
77]], util.see_also())
78 :summary("Compile package in current directory using a rockspec.")
79 -- luacheck: pop
80
81 cmd:argument("rockspec", "Rockspec for the rock to build.")
82 :args("?")
83
84 make.cmd_options(cmd) --!
85end
86
87--- Driver function for "make" command.
88-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an
89-- error message otherwise. exitcode is optionally returned.
90function make.command(args: Args): boolean, string
91 local rockspec_filename = args.rockspec
92 if not rockspec_filename then
93 local err: string
94 rockspec_filename, err = util.get_default_rockspec()
95 if not rockspec_filename then
96 return nil, err
97 end
98 end
99 if not rockspec_filename:match("rockspec$") then
100 return nil, "Invalid argument: 'make' takes a rockspec as a parameter. "..util.see_help("make")
101 end
102
103 local cwd = fs.absolute_name(dir.path("."))
104 local rockspec, err, errcode = fetch.load_rockspec(rockspec_filename)
105 if not rockspec then
106 return nil, err
107 end
108
109 local name, namespace = util.split_namespace(rockspec.name)
110 namespace = namespace or args.namespace
111
112 local opts: BOpts = {
113 need_to_fetch = false,
114 minimal_mode = true,
115 deps_mode = deps.get_deps_mode(args),
116 build_only_deps = not not (args.only_deps and not args.pack_binary_rock),
117 namespace = namespace,
118 branch = args.branch,
119 verify = not not args.verify,
120 check_lua_versions = not not args.check_lua_versions,
121 pin = not not args.pin,
122 rebuild = true,
123 no_install = not not args.no_install
124 }
125
126 if args.sign and not args.pack_binary_rock then
127 return nil, "In the make command, --sign is meant to be used only with --pack-binary-rock"
128 end
129
130 if args.no_install then
131 return build.build_rockspec(rockspec, opts, cwd)
132 elseif args.pack_binary_rock then
133 return pack.pack_binary_rock(name, namespace, rockspec.version, args.sign, function(): string, string
134 local name, version = build.build_rockspec(rockspec, opts, cwd) -- luacheck: ignore 431
135 if name and args.no_doc then
136 util.remove_doc_dir(name, version)
137 end
138 return name, version
139 end)
140 else
141 local ok, err = build.build_rockspec(rockspec, opts, cwd)
142 if not ok then return nil, err end
143 local name, version = ok, err -- luacheck: ignore 421
144
145 if opts.build_only_deps then
146 util.printout("Stopping after installing dependencies for " ..name.." "..version)
147 util.printout()
148 return name, version
149 end
150
151 if args.no_doc then
152 util.remove_doc_dir(name, version)
153 end
154
155 if (not args.keep) and not cfg.keep_other_versions then
156 local ok, err, warn = remove.remove_other_versions(name, version, args.force, args.force_fast)
157 if not ok then
158 return nil, err
159 elseif warn then
160 util.printerr(warn)
161 end
162 end
163
164 deps.check_dependencies(nil, deps.get_deps_mode(args))
165 return name, version
166 end
167end
168
169make.needs_lock = function(args: Args): boolean
170 if args.pack_binary_rock or args.no_install then
171 return false
172 end
173 return true
174end
175
176return make
diff --git a/src/luarocks/cmd/new_version.tl b/src/luarocks/cmd/new_version.tl
new file mode 100644
index 00000000..2ec084e0
--- /dev/null
+++ b/src/luarocks/cmd/new_version.tl
@@ -0,0 +1,228 @@
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 dir = require("luarocks.dir")
12local type_rockspec = require("luarocks.type.rockspec")
13
14function new_version.add_to_parser(parser)
15 local cmd = parser:command("new_version", [[
16This is a utility function that writes a new rockspec, updating data from a
17previous one.
18
19If a package name is given, it downloads the latest rockspec from the default
20server. If a rockspec is given, it uses it instead. If no argument is given, it
21looks for a rockspec same way 'luarocks make' does.
22
23If the version number is not given and tag is passed using --tag, it is used as
24the version, with 'v' removed from beginning. Otherwise, it only increments the
25revision number of the given (or downloaded) rockspec.
26
27If a URL is given, it replaces the one from the old rockspec with the given URL.
28If a URL is not given and a new version is given, it tries to guess the new URL
29by replacing occurrences of the version number in the URL or tag; if the guessed
30URL is invalid, the old URL is restored. It also tries to download the new URL
31to determine the new MD5 checksum.
32
33If a tag is given, it replaces the one from the old rockspec. If there is an old
34tag but no new one passed, it is guessed in the same way URL is.
35
36If a directory is not given, it defaults to the current directory.
37
38WARNING: it writes the new rockspec to the given directory, overwriting the file
39if it already exists.]], util.see_also())
40 :summary("Auto-write a rockspec for a new version of a rock.")
41
42 cmd:argument("rock", "Package name or rockspec.")
43 :args("?")
44 cmd:argument("new_version", "New version of the rock.")
45 :args("?")
46 cmd:argument("new_url", "New URL of the rock.")
47 :args("?")
48
49 cmd:option("--dir", "Output directory for the new rockspec.")
50 cmd:option("--tag", "New SCM tag.")
51end
52
53
54local function try_replace(tbl, field, old, new)
55 if not tbl[field] then
56 return false
57 end
58 local old_field = tbl[field]
59 local new_field = tbl[field]:gsub(old, new)
60 if new_field ~= old_field then
61 util.printout("Guessing new '"..field.."' field as "..new_field)
62 tbl[field] = new_field
63 return true
64 end
65 return false
66end
67
68-- Try to download source file using URL from a rockspec.
69-- If it specified MD5, update it.
70-- @return (true, false) if MD5 was not specified or it stayed same,
71-- (true, true) if MD5 changed, (nil, string) on error.
72local function check_url_and_update_md5(out_rs, invalid_is_error)
73 local file, temp_dir = fetch.fetch_url_at_temp_dir(out_rs.source.url, "luarocks-new-version-"..out_rs.package)
74 if not file then
75 if invalid_is_error then
76 return nil, "invalid URL - "..temp_dir
77 end
78 util.warning("invalid URL - "..temp_dir)
79 return true, false
80 end
81 do
82 local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, out_rs.source.url, out_rs.source.dir)
83 if not inferred_dir then
84 return nil, found_dir
85 end
86
87 if found_dir and found_dir ~= inferred_dir then
88 out_rs.source.dir = found_dir
89 end
90 end
91 if file then
92 if out_rs.source.md5 then
93 util.printout("File successfully downloaded. Updating MD5 checksum...")
94 local new_md5, err = fs.get_md5(file)
95 if not new_md5 then
96 return nil, err
97 end
98 local old_md5 = out_rs.source.md5
99 out_rs.source.md5 = new_md5
100 return true, new_md5 ~= old_md5
101 else
102 util.printout("File successfully downloaded.")
103 return true, false
104 end
105 end
106end
107
108local function update_source_section(out_rs, url, tag, old_ver, new_ver)
109 if tag then
110 out_rs.source.tag = tag
111 end
112 if url then
113 out_rs.source.url = url
114 return check_url_and_update_md5(out_rs)
115 end
116 if new_ver == old_ver then
117 return true
118 end
119 if out_rs.source.dir then
120 try_replace(out_rs.source, "dir", old_ver, new_ver)
121 end
122 if out_rs.source.file then
123 try_replace(out_rs.source, "file", old_ver, new_ver)
124 end
125
126 local old_url = out_rs.source.url
127 if try_replace(out_rs.source, "url", old_ver, new_ver) then
128 local ok, md5_changed = check_url_and_update_md5(out_rs, true)
129 if ok then
130 return ok, md5_changed
131 end
132 out_rs.source.url = old_url
133 end
134 if tag or try_replace(out_rs.source, "tag", old_ver, new_ver) then
135 return true
136 end
137 -- Couldn't replace anything significant, use the old URL.
138 local ok, md5_changed = check_url_and_update_md5(out_rs)
139 if not ok then
140 return nil, md5_changed
141 end
142 if md5_changed then
143 util.warning("URL is the same, but MD5 has changed. Old rockspec is broken.")
144 end
145 return true
146end
147
148function new_version.command(args)
149 if not args.rock then
150 local err
151 args.rock, err = util.get_default_rockspec()
152 if not args.rock then
153 return nil, err
154 end
155 end
156
157 local filename, err
158 if args.rock:match("rockspec$") then
159 filename, err = fetch.fetch_url(args.rock)
160 if not filename then
161 return nil, err
162 end
163 else
164 filename, err = download.download_file("rockspec", args.rock:lower())
165 if not filename then
166 return nil, err
167 end
168 end
169
170 local valid_rs, err = fetch.load_rockspec(filename)
171 if not valid_rs then
172 return nil, err
173 end
174
175 local old_ver, old_rev = valid_rs.version:match("(.*)%-(%d+)$")
176 local new_ver, new_rev
177
178 if args.tag and not args.new_version then
179 args.new_version = args.tag:gsub("^v", "")
180 end
181
182 local out_dir
183 if args.dir then
184 out_dir = dir.normalize(args.dir)
185 end
186
187 if args.new_version then
188 new_ver, new_rev = args.new_version:match("(.*)%-(%d+)$")
189 new_rev = tonumber(new_rev)
190 if not new_rev then
191 new_ver = args.new_version
192 new_rev = 1
193 end
194 else
195 new_ver = old_ver
196 new_rev = tonumber(old_rev) + 1
197 end
198 local new_rockver = new_ver:gsub("-", "")
199
200 local out_rs, err = persist.load_into_table(filename)
201 local out_name = out_rs.package:lower()
202 out_rs.version = new_rockver.."-"..new_rev
203
204 local ok, err = update_source_section(out_rs, args.new_url, args.tag, old_ver, new_ver)
205 if not ok then return nil, err end
206
207 if out_rs.build and out_rs.build.type == "module" then
208 out_rs.build.type = "builtin"
209 end
210
211 local out_filename = out_name.."-"..new_rockver.."-"..new_rev..".rockspec"
212 if out_dir then
213 out_filename = dir.path(out_dir, out_filename)
214 fs.make_dir(out_dir)
215 end
216 persist.save_from_table(out_filename, out_rs, type_rockspec.order)
217
218 util.printout("Wrote "..out_filename)
219
220 local valid_out_rs, err = fetch.load_local_rockspec(out_filename)
221 if not valid_out_rs then
222 return nil, "Failed loading generated rockspec: "..err
223 end
224
225 return true
226end
227
228return new_version
diff --git a/src/luarocks/cmd/pack.tl b/src/luarocks/cmd/pack.tl
new file mode 100644
index 00000000..29a43e7b
--- /dev/null
+++ b/src/luarocks/cmd/pack.tl
@@ -0,0 +1,36 @@
1
2--- Module implementing the LuaRocks "pack" command.
3-- Creates a rock, packing sources or binaries.
4local cmd_pack = {}
5
6local util = require("luarocks.util")
7local pack = require("luarocks.pack")
8local queries = require("luarocks.queries")
9
10function cmd_pack.add_to_parser(parser)
11 local cmd = parser:command("pack", "Create a rock, packing sources or binaries.", util.see_also())
12
13 cmd:argument("rock", "A rockspec file, for creating a source rock, or the "..
14 "name of an installed package, for creating a binary rock.")
15 :action(util.namespaced_name_action)
16 cmd:argument("version", "A version may be given if the first argument is a rock name.")
17 :args("?")
18
19 cmd:flag("--sign", "Produce a signature file as well.")
20end
21
22--- Driver function for the "pack" command.
23-- @return boolean or (nil, string): true if successful or nil followed
24-- by an error message.
25function cmd_pack.command(args)
26 local file, err
27 if args.rock:match(".*%.rockspec") then
28 file, err = pack.pack_source_rock(args.rock)
29 else
30 local query = queries.new(args.rock, args.namespace, args.version)
31 file, err = pack.pack_installed_rock(query, args.tree)
32 end
33 return pack.report_and_sign_local_file(file, err, args.sign)
34end
35
36return cmd_pack
diff --git a/src/luarocks/cmd/path.tl b/src/luarocks/cmd/path.tl
new file mode 100644
index 00000000..ba346550
--- /dev/null
+++ b/src/luarocks/cmd/path.tl
@@ -0,0 +1,83 @@
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")
8local fs = require("luarocks.fs")
9
10function path_cmd.add_to_parser(parser)
11 local cmd = parser:command("path", [[
12Returns the package path currently configured for this installation
13of LuaRocks, formatted as shell commands to update LUA_PATH and LUA_CPATH.
14
15On Unix systems, you may run:
16 eval `luarocks path`
17And on Windows:
18 luarocks path > "%temp%\_lrp.bat"
19 call "%temp%\_lrp.bat" && del "%temp%\_lrp.bat"]],
20 util.see_also())
21 :summary("Return the currently configured package path.")
22
23 cmd:flag("--no-bin", "Do not export the PATH variable.")
24 cmd:flag("--append", "Appends the paths to the existing paths. Default is "..
25 "to prefix the LR paths to the existing paths.")
26 cmd:flag("--lr-path", "Prints Lua path components defined by the configured rocks trees " ..
27 "(not formatted as a shell command)")
28 cmd:flag("--lr-cpath", "Prints Lua cpath components defined by the configured rocks trees " ..
29 "(not formatted as a shell command)")
30 cmd:flag("--full", "By default, --lr-path and --lr-cpath only include the paths " ..
31 "derived by the LuaRocks rocks_trees. Using --full includes any other components " ..
32 "defined in your system's package.(c)path, either via the running interpreter's " ..
33 "default paths or via LUA_(C)PATH(_5_x) environment variables (in short, using " ..
34 "--full produces the same lists as shown in the shell outputs of 'luarocks path').")
35 cmd:flag("--lr-bin", "Exports the system path (not formatted as shell command).")
36 cmd:flag("--bin"):hidden(true)
37end
38
39--- Driver function for "path" command.
40-- @return boolean This function always succeeds.
41function path_cmd.command(args)
42 local lr_path, lr_cpath, lr_bin = cfg.package_paths(args.tree)
43 local path_sep = cfg.export_path_separator
44
45 local full_list = ((not args.lr_path) and (not args.lr_cpath) and (not args.lr_bin))
46 or args.full
47
48 local clean_path = util.cleanup_path(os.getenv("PATH") or "", path_sep, nil, true)
49
50 if full_list then
51 if args.append then
52 lr_path = package.path .. ";" .. lr_path
53 lr_cpath = package.cpath .. ";" .. lr_cpath
54 lr_bin = clean_path .. path_sep .. lr_bin
55 else
56 lr_path = lr_path.. ";" .. package.path
57 lr_cpath = lr_cpath .. ";" .. package.cpath
58 lr_bin = lr_bin .. path_sep .. clean_path
59 end
60 end
61
62 if args.lr_path then
63 util.printout(util.cleanup_path(lr_path, ';', cfg.lua_version, true))
64 return true
65 elseif args.lr_cpath then
66 util.printout(util.cleanup_path(lr_cpath, ';', cfg.lua_version, true))
67 return true
68 elseif args.lr_bin then
69 util.printout(util.cleanup_path(lr_bin, path_sep, nil, true))
70 return true
71 end
72
73 local lpath_var, lcpath_var = util.lua_path_variables()
74
75 util.printout(fs.export_cmd(lpath_var, util.cleanup_path(lr_path, ';', cfg.lua_version, args.append)))
76 util.printout(fs.export_cmd(lcpath_var, util.cleanup_path(lr_cpath, ';', cfg.lua_version, args.append)))
77 if not args.no_bin then
78 util.printout(fs.export_cmd("PATH", util.cleanup_path(lr_bin, path_sep, nil, args.append)))
79 end
80 return true
81end
82
83return path_cmd
diff --git a/src/luarocks/cmd/purge.tl b/src/luarocks/cmd/purge.tl
new file mode 100644
index 00000000..fda8ab88
--- /dev/null
+++ b/src/luarocks/cmd/purge.tl
@@ -0,0 +1,72 @@
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 path = require("luarocks.path")
8local search = require("luarocks.search")
9local vers = require("luarocks.core.vers")
10local repo_writer = require("luarocks.repo_writer")
11local cfg = require("luarocks.core.cfg")
12local remove = require("luarocks.remove")
13local queries = require("luarocks.queries")
14
15function purge.add_to_parser(parser)
16 -- luacheck: push ignore 431
17 local cmd = parser:command("purge", [[
18This command removes rocks en masse from a given tree.
19By default, it removes all rocks from a tree.
20
21The --tree option is mandatory: luarocks purge does not assume a default tree.]],
22 util.see_also())
23 :summary("Remove all installed rocks from a tree.")
24 -- luacheck: pop
25
26 cmd:flag("--old-versions", "Keep the highest-numbered version of each "..
27 "rock and remove the other ones. By default it only removes old "..
28 "versions if they are not needed as dependencies. This can be "..
29 "overridden with the flag --force.")
30 cmd:flag("--force", "If --old-versions is specified, force removal of "..
31 "previously installed versions if it would break dependencies.")
32 cmd:flag("--force-fast", "Like --force, but performs a forced removal "..
33 "without reporting dependency issues.")
34end
35
36function purge.command(args)
37 local tree = args.tree
38
39 local results = {}
40 search.local_manifest_search(results, path.rocks_dir(tree), queries.all())
41
42 local sort = function(a,b) return vers.compare_versions(b,a) end
43 if args.old_versions then
44 sort = vers.compare_versions
45 end
46
47 for package, versions in util.sortedpairs(results) do
48 for version, _ in util.sortedpairs(versions, sort) do
49 if args.old_versions then
50 util.printout("Keeping "..package.." "..version.."...")
51 local ok, err, warn = remove.remove_other_versions(package, version, args.force, args.force_fast)
52 if not ok then
53 util.printerr(err)
54 elseif warn then
55 util.printerr(err)
56 end
57 break
58 else
59 util.printout("Removing "..package.." "..version.."...")
60 local ok, err = repo_writer.delete_version(package, version, "none", true)
61 if not ok then
62 util.printerr(err)
63 end
64 end
65 end
66 end
67 return repo_writer.refresh_manifest(cfg.rocks_dir)
68end
69
70purge.needs_lock = function() return true end
71
72return purge
diff --git a/src/luarocks/cmd/remove.tl b/src/luarocks/cmd/remove.tl
new file mode 100644
index 00000000..630303ca
--- /dev/null
+++ b/src/luarocks/cmd/remove.tl
@@ -0,0 +1,71 @@
1
2--- Module implementing the LuaRocks "remove" command.
3-- Uninstalls rocks.
4local cmd_remove = {}
5
6local remove = require("luarocks.remove")
7local util = require("luarocks.util")
8local cfg = require("luarocks.core.cfg")
9local search = require("luarocks.search")
10local path = require("luarocks.path")
11local deps = require("luarocks.deps")
12local queries = require("luarocks.queries")
13
14function cmd_remove.add_to_parser(parser)
15 -- luacheck: push ignore 431
16 local cmd = parser:command("remove", [[
17Uninstall a rock.
18
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 or --force-fast.]],
22 util.see_also())
23 :summary("Uninstall a rock.")
24 -- luacheck: pop
25
26 cmd:argument("rock", "Name of the rock to be uninstalled.")
27 :action(util.namespaced_name_action)
28 cmd:argument("version", "Version of the rock to uninstall.")
29 :args("?")
30
31 cmd:flag("--force", "Force removal if it would break dependencies.")
32 cmd:flag("--force-fast", "Perform a forced removal without reporting dependency issues.")
33 util.deps_mode_option(cmd)
34end
35
36--- Driver function for the "remove" command.
37-- @return boolean or (nil, string, exitcode): True if removal was
38-- successful, nil and an error message otherwise. exitcode is optionally returned.
39function cmd_remove.command(args)
40 local name = args.rock
41 local deps_mode = deps.get_deps_mode(args)
42
43 local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$")
44 local version = args.version
45 local filename = name
46 if rock_type then
47 name, version = path.parse_name(filename)
48 if not name then return nil, "Invalid "..rock_type.." filename: "..filename end
49 end
50
51 name = name:lower()
52
53 local results = {}
54 search.local_manifest_search(results, cfg.rocks_dir, queries.new(name, args.namespace, version))
55 if not results[name] then
56 local rock = util.format_rock_name(name, args.namespace, version)
57 return nil, "Could not find rock '"..rock.."' in "..path.rocks_tree_to_string(cfg.root_dir)
58 end
59
60 local ok, err = remove.remove_search_results(results, name, deps_mode, args.force, args.force_fast)
61 if not ok then
62 return nil, err
63 end
64
65 deps.check_dependencies(nil, deps.get_deps_mode(args))
66 return true
67end
68
69cmd_remove.needs_lock = function() return true end
70
71return cmd_remove
diff --git a/src/luarocks/cmd/search.tl b/src/luarocks/cmd/search.tl
new file mode 100644
index 00000000..6cab6d80
--- /dev/null
+++ b/src/luarocks/cmd/search.tl
@@ -0,0 +1,84 @@
1
2--- Module implementing the LuaRocks "search" command.
3-- Queries LuaRocks servers.
4local cmd_search = {}
5
6local cfg = require("luarocks.core.cfg")
7local util = require("luarocks.util")
8local search = require("luarocks.search")
9local queries = require("luarocks.queries")
10local results = require("luarocks.results")
11
12function cmd_search.add_to_parser(parser)
13 local cmd = parser:command("search", "Query the LuaRocks servers.", util.see_also())
14
15 cmd:argument("name", "Name of the rock to search for.")
16 :args("?")
17 :action(util.namespaced_name_action)
18 cmd:argument("version", "Rock version to search for.")
19 :args("?")
20
21 cmd:flag("--source", "Return only rockspecs and source rocks, to be used "..
22 'with the "build" command.')
23 cmd:flag("--binary", "Return only pure Lua and binary rocks (rocks that "..
24 'can be used with the "install" command without requiring a C toolchain).')
25 cmd:flag("--all", "List all contents of the server that are suitable to "..
26 "this platform, do not filter by name.")
27 cmd:flag("--porcelain", "Return a machine readable format.")
28end
29
30--- Splits a list of search results into two lists, one for "source" results
31-- to be used with the "build" command, and one for "binary" results to be
32-- used with the "install" command.
33-- @param result_tree table: A search results table.
34-- @return (table, table): Two tables, one for source and one for binary
35-- results.
36local function split_source_and_binary_results(result_tree)
37 local sources, binaries = {}, {}
38 for name, versions in pairs(result_tree) do
39 for version, repositories in pairs(versions) do
40 for _, repo in ipairs(repositories) do
41 local where = sources
42 if repo.arch == "all" or repo.arch == cfg.arch then
43 where = binaries
44 end
45 local entry = results.new(name, version, repo.repo, repo.arch)
46 search.store_result(where, entry)
47 end
48 end
49 end
50 return sources, binaries
51end
52
53--- Driver function for "search" command.
54-- @return boolean or (nil, string): True if build was successful; nil and an
55-- error message otherwise.
56function cmd_search.command(args)
57 local name = args.name
58
59 if args.all then
60 name, args.version = "", nil
61 end
62
63 if not args.name and not args.all then
64 return nil, "Enter name and version or use --all. "..util.see_help("search")
65 end
66
67 local query = queries.new(name, args.namespace, args.version, true)
68 local result_tree, err = search.search_repos(query)
69 local porcelain = args.porcelain
70 local full_name = util.format_rock_name(name, args.namespace, args.version)
71 util.title(full_name .. " - Search results for Lua "..cfg.lua_version..":", porcelain, "=")
72 local sources, binaries = split_source_and_binary_results(result_tree)
73 if next(sources) and not args.binary then
74 util.title("Rockspecs and source rocks:", porcelain)
75 search.print_result_tree(sources, porcelain)
76 end
77 if next(binaries) and not args.source then
78 util.title("Binary and pure-Lua rocks:", porcelain)
79 search.print_result_tree(binaries, porcelain)
80 end
81 return true
82end
83
84return cmd_search
diff --git a/src/luarocks/cmd/show.tl b/src/luarocks/cmd/show.tl
new file mode 100644
index 00000000..88cbbada
--- /dev/null
+++ b/src/luarocks/cmd/show.tl
@@ -0,0 +1,314 @@
1--- Module implementing the LuaRocks "show" command.
2-- Shows information about an installed rock.
3local show = {}
4
5local queries = require("luarocks.queries")
6local search = require("luarocks.search")
7local dir = require("luarocks.core.dir")
8local fs = require("luarocks.fs")
9local cfg = require("luarocks.core.cfg")
10local util = require("luarocks.util")
11local path = require("luarocks.path")
12local fetch = require("luarocks.fetch")
13local manif = require("luarocks.manif")
14local repos = require("luarocks.repos")
15
16function show.add_to_parser(parser)
17 local cmd = parser:command("show", [[
18Show information about an installed rock.
19
20Without any flags, show all module information.
21With flags, return only the desired information.]], util.see_also())
22 :summary("Show information about an installed rock.")
23
24 cmd:argument("rock", "Name of an installed rock.")
25 :action(util.namespaced_name_action)
26 cmd:argument("version", "Rock version.")
27 :args("?")
28
29 cmd:flag("--home", "Show home page of project.")
30 cmd:flag("--modules", "Show all modules provided by the package as used by require().")
31 cmd:flag("--deps", "Show packages the package depends on.")
32 cmd:flag("--build-deps", "Show build-only dependencies for the package.")
33 cmd:flag("--test-deps", "Show dependencies for testing the package.")
34 cmd:flag("--rockspec", "Show the full path of the rockspec file.")
35 cmd:flag("--mversion", "Show the package version.")
36 cmd:flag("--rock-tree", "Show local tree where rock is installed.")
37 cmd:flag("--rock-namespace", "Show rock namespace.")
38 cmd:flag("--rock-dir", "Show data directory of the installed rock.")
39 cmd:flag("--rock-license", "Show rock license.")
40 cmd:flag("--issues", "Show URL for project's issue tracker.")
41 cmd:flag("--labels", "List the labels of the rock.")
42 cmd:flag("--porcelain", "Produce machine-friendly output.")
43end
44
45local friendly_template = [[
46 :
47?namespace:${namespace}/${package} ${version} - ${summary}
48!namespace:${package} ${version} - ${summary}
49 :
50*detailed :${detailed}
51?detailed :
52?license :License: \t${license}
53?homepage :Homepage: \t${homepage}
54?issues :Issues: \t${issues}
55?labels :Labels: \t${labels}
56?location :Installed in: \t${location}
57?commands :
58?commands :Commands:
59*commands :\t${name} (${file})
60?modules :
61?modules :Modules:
62*modules :\t${name} (${file})
63?bdeps :
64?bdeps :Has build dependency on:
65*bdeps :\t${name} (${label})
66?tdeps :
67?tdeps :Tests depend on:
68*tdeps :\t${name} (${label})
69?deps :
70?deps :Depends on:
71*deps :\t${name} (${label})
72?ideps :
73?ideps :Indirectly pulling:
74*ideps :\t${name} (${label})
75 :
76]]
77
78local porcelain_template = [[
79?namespace:namespace\t${namespace}
80?package :package\t${package}
81?version :version\t${version}
82?summary :summary\t${summary}
83*detailed :detailed\t${detailed}
84?license :license\t${license}
85?homepage :homepage\t${homepage}
86?issues :issues\t${issues}
87?labels :labels\t${labels}
88?location :location\t${location}
89*commands :command\t${name}\t${file}
90*modules :module\t${name}\t${file}
91*bdeps :build_dependency\t${name}\t${label}
92*tdeps :test_dependency\t${name}\t${label}
93*deps :dependency\t${name}\t${label}
94*ideps :indirect_dependency\t${name}\t${label}
95]]
96
97local function keys_as_string(t, sep)
98 local keys = util.keys(t)
99 table.sort(keys)
100 return table.concat(keys, sep or " ")
101end
102
103local function word_wrap(line)
104 local width = tonumber(os.getenv("COLUMNS")) or 80
105 if width > 80 then width = 80 end
106 if #line > width then
107 local brk = width
108 while brk > 0 and line:sub(brk, brk) ~= " " do
109 brk = brk - 1
110 end
111 if brk > 0 then
112 return line:sub(1, brk-1) .. "\n" .. word_wrap(line:sub(brk+1))
113 end
114 end
115 return line
116end
117
118local function format_text(text)
119 text = text:gsub("^%s*",""):gsub("%s$", ""):gsub("\n[ \t]+","\n"):gsub("([^\n])\n([^\n])","%1 %2")
120 local paragraphs = util.split_string(text, "\n\n")
121 for n, line in ipairs(paragraphs) do
122 paragraphs[n] = word_wrap(line)
123 end
124 return (table.concat(paragraphs, "\n\n"):gsub("%s$", ""))
125end
126
127local function installed_rock_label(dep, tree)
128 local installed, version
129 local rocks_provided = util.get_rocks_provided()
130 if rocks_provided[dep.name] then
131 installed, version = true, rocks_provided[dep.name]
132 else
133 installed, version = search.pick_installed_rock(dep, tree)
134 end
135 return installed and "using "..version or "missing"
136end
137
138local function render(template, data)
139 local out = {}
140 for cmd, var, line in template:gmatch("(.)([a-z]*)%s*:([^\n]*)\n") do
141 line = line:gsub("\\t", "\t")
142 local d = data[var]
143 if cmd == " " then
144 table.insert(out, line)
145 elseif cmd == "?" or cmd == "*" or cmd == "!" then
146 if (cmd == "!" and d == nil)
147 or (cmd ~= "!" and (type(d) == "string"
148 or (type(d) == "table" and next(d)))) then
149 local n = cmd == "*" and #d or 1
150 for i = 1, n do
151 local tbl = cmd == "*" and d[i] or data
152 if type(tbl) == "string" then
153 tbl = tbl:gsub("%%", "%%%%")
154 end
155 table.insert(out, (line:gsub("${([a-z]+)}", tbl)))
156 end
157 end
158 end
159 end
160 return table.concat(out, "\n")
161end
162
163local function adjust_path(name, version, basedir, pathname, suffix)
164 pathname = dir.path(basedir, pathname)
165 local vpathname = path.versioned_name(pathname, basedir, name, version)
166 return (fs.exists(vpathname)
167 and vpathname
168 or pathname) .. (suffix or "")
169end
170
171local function modules_to_list(name, version, repo)
172 local ret = {}
173 local rock_manifest = manif.load_rock_manifest(name, version, repo)
174
175 local lua_dir = path.deploy_lua_dir(repo)
176 local lib_dir = path.deploy_lib_dir(repo)
177 repos.recurse_rock_manifest_entry(rock_manifest.lua, function(pathname)
178 table.insert(ret, {
179 name = path.path_to_module(pathname),
180 file = adjust_path(name, version, lua_dir, pathname),
181 })
182 end)
183 repos.recurse_rock_manifest_entry(rock_manifest.lib, function(pathname)
184 table.insert(ret, {
185 name = path.path_to_module(pathname),
186 file = adjust_path(name, version, lib_dir, pathname),
187 })
188 end)
189 table.sort(ret, function(a, b)
190 if a.name == b.name then
191 return a.file < b.file
192 end
193 return a.name < b.name
194 end)
195 return ret
196end
197
198local function commands_to_list(name, version, repo)
199 local ret = {}
200 local rock_manifest = manif.load_rock_manifest(name, version, repo)
201
202 local bin_dir = path.deploy_bin_dir(repo)
203 repos.recurse_rock_manifest_entry(rock_manifest.bin, function(pathname)
204 table.insert(ret, {
205 name = name,
206 file = adjust_path(name, version, bin_dir, pathname, cfg.wrapper_suffix),
207 })
208 end)
209 table.sort(ret, function(a, b)
210 if a.name == b.name then
211 return a.file < b.file
212 end
213 return a.name < b.name
214 end)
215 return ret
216end
217
218local function deps_to_list(dependencies, tree)
219 local ret = {}
220 for _, dep in ipairs(dependencies or {}) do
221 table.insert(ret, { name = tostring(dep), label = installed_rock_label(dep, tree) })
222 end
223 return ret
224end
225
226local function indirect_deps(mdeps, rdeps, tree)
227 local ret = {}
228 local direct_deps = {}
229 for _, dep in ipairs(rdeps) do
230 direct_deps[dep] = true
231 end
232 for dep_name in util.sortedpairs(mdeps or {}) do
233 if not direct_deps[dep_name] then
234 table.insert(ret, { name = tostring(dep_name), label = installed_rock_label(queries.new(dep_name), tree) })
235 end
236 end
237 return ret
238end
239
240local function show_rock(template, namespace, name, version, rockspec, repo, minfo, tree)
241 local desc = rockspec.description or {}
242 local data = {
243 namespace = namespace,
244 package = rockspec.package,
245 version = rockspec.version,
246 summary = desc.summary or "",
247 detailed = desc.detailed and util.split_string(format_text(desc.detailed), "\n"),
248 license = desc.license,
249 homepage = desc.homepage,
250 issues = desc.issues_url,
251 labels = desc.labels and table.concat(desc.labels, ", "),
252 location = path.rocks_tree_to_string(repo),
253 commands = commands_to_list(name, version, repo),
254 modules = modules_to_list(name, version, repo),
255 bdeps = deps_to_list(rockspec.build_dependencies, tree),
256 tdeps = deps_to_list(rockspec.test_dependencies, tree),
257 deps = deps_to_list(rockspec.dependencies, tree),
258 ideps = indirect_deps(minfo.dependencies, rockspec.dependencies, tree),
259 }
260 util.printout(render(template, data))
261end
262
263--- Driver function for "show" command.
264-- @return boolean: True if succeeded, nil on errors.
265function show.command(args)
266 local query = queries.new(args.rock, args.namespace, args.version, true)
267
268 local name, version, repo, repo_url = search.pick_installed_rock(query, args.tree)
269 if not name then
270 return nil, version
271 end
272 local tree = path.rocks_tree_to_string(repo)
273 local directory = path.install_dir(name, version, repo)
274 local namespace = path.read_namespace(name, version, tree)
275 local rockspec_file = path.rockspec_file(name, version, repo)
276 local rockspec, err = fetch.load_local_rockspec(rockspec_file)
277 if not rockspec then return nil,err end
278
279 local descript = rockspec.description or {}
280 local manifest, err = manif.load_manifest(repo_url)
281 if not manifest then return nil,err end
282 local minfo = manifest.repository[name][version][1]
283
284 if args.rock_tree then util.printout(tree)
285 elseif args.rock_namespace then util.printout(namespace)
286 elseif args.rock_dir then util.printout(directory)
287 elseif args.home then util.printout(descript.homepage)
288 elseif args.rock_license then util.printout(descript.license)
289 elseif args.issues then util.printout(descript.issues_url)
290 elseif args.labels then util.printout(descript.labels and table.concat(descript.labels, "\n"))
291 elseif args.modules then util.printout(keys_as_string(minfo.modules, "\n"))
292 elseif args.deps then
293 for _, dep in ipairs(rockspec.dependencies) do
294 util.printout(tostring(dep))
295 end
296 elseif args.build_deps then
297 for _, dep in ipairs(rockspec.build_dependencies) do
298 util.printout(tostring(dep))
299 end
300 elseif args.test_deps then
301 for _, dep in ipairs(rockspec.test_dependencies) do
302 util.printout(tostring(dep))
303 end
304 elseif args.rockspec then util.printout(rockspec_file)
305 elseif args.mversion then util.printout(version)
306 elseif args.porcelain then
307 show_rock(porcelain_template, namespace, name, version, rockspec, repo, minfo, args.tree)
308 else
309 show_rock(friendly_template, namespace, name, version, rockspec, repo, minfo, args.tree)
310 end
311 return true
312end
313
314return show
diff --git a/src/luarocks/cmd/test.tl b/src/luarocks/cmd/test.tl
new file mode 100644
index 00000000..b353bd80
--- /dev/null
+++ b/src/luarocks/cmd/test.tl
@@ -0,0 +1,48 @@
1
2--- Module implementing the LuaRocks "test" command.
3-- Tests a rock, compiling its C parts if any.
4local cmd_test = {}
5
6local util = require("luarocks.util")
7local test = require("luarocks.test")
8
9function cmd_test.add_to_parser(parser)
10 local cmd = parser:command("test", [[
11Run the test suite for the Lua project in the current directory.
12
13If the first argument is a rockspec, it will use it to determine the parameters
14for running tests; otherwise, it will attempt to detect the rockspec.
15
16Any additional arguments are forwarded to the test suite.
17To make sure that test suite flags are not interpreted as LuaRocks flags, use --
18to separate LuaRocks arguments from test suite arguments.]],
19 util.see_also())
20 :summary("Run the test suite in the current directory.")
21
22 cmd:argument("rockspec", "Project rockspec.")
23 :args("?")
24 cmd:argument("args", "Test suite arguments.")
25 :args("*")
26 cmd:flag("--prepare", "Only install dependencies needed for testing only, but do not run the test")
27
28 cmd:option("--test-type", "Specify the test suite type manually if it was "..
29 "not specified in the rockspec and it could not be auto-detected.")
30 :argname("<type>")
31end
32
33function cmd_test.command(args)
34 if args.rockspec and args.rockspec:match("rockspec$") then
35 return test.run_test_suite(args.rockspec, args.test_type, args.args, args.prepare)
36 end
37
38 table.insert(args.args, 1, args.rockspec)
39
40 local rockspec, err = util.get_default_rockspec()
41 if not rockspec then
42 return nil, err
43 end
44
45 return test.run_test_suite(rockspec, args.test_type, args.args, args.prepare)
46end
47
48return cmd_test
diff --git a/src/luarocks/cmd/unpack.tl b/src/luarocks/cmd/unpack.tl
new file mode 100644
index 00000000..a0ade4f3
--- /dev/null
+++ b/src/luarocks/cmd/unpack.tl
@@ -0,0 +1,169 @@
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 search = require("luarocks.search")
12
13function unpack.add_to_parser(parser)
14 local cmd = parser:command("unpack", [[
15Unpacks the contents of a rock in a newly created directory.
16Argument may be a rock file, or the name of a rock in a rocks server.
17In the latter case, the rock version may be given as a second argument.]],
18 util.see_also())
19 :summary("Unpack the contents of a rock.")
20
21 cmd:argument("rock", "A rock file or the name of a rock.")
22 :action(util.namespaced_name_action)
23 cmd:argument("version", "Rock version.")
24 :args("?")
25
26 cmd:flag("--force", "Unpack files even if the output directory already exists.")
27 cmd:flag("--check-lua-versions", "If the rock can't be found, check repository "..
28 "and report if it is available for another Lua version.")
29end
30
31--- Load a rockspec file to the given directory, fetches the source
32-- files specified in the rockspec, and unpack them inside the directory.
33-- @param rockspec_file string: The URL for a rockspec file.
34-- @param dir_name string: The directory where to store and unpack files.
35-- @return table or (nil, string): the loaded rockspec table or
36-- nil and an error message.
37local function unpack_rockspec(rockspec_file, dir_name)
38 assert(type(rockspec_file) == "string")
39 assert(type(dir_name) == "string")
40
41 local rockspec, err = fetch.load_rockspec(rockspec_file)
42 if not rockspec then
43 return nil, "Failed loading rockspec "..rockspec_file..": "..err
44 end
45 local ok, err = fs.change_dir(dir_name)
46 if not ok then return nil, err end
47 local ok, sources_dir = fetch.fetch_sources(rockspec, true, ".")
48 if not ok then
49 return nil, sources_dir
50 end
51 ok, err = fs.change_dir(sources_dir)
52 if not ok then return nil, err end
53 ok, err = build.apply_patches(rockspec)
54 fs.pop_dir()
55 if not ok then return nil, err end
56 return rockspec
57end
58
59--- Load a .rock file to the given directory and unpack it inside it.
60-- @param rock_file string: The URL for a .rock file.
61-- @param dir_name string: The directory where to unpack.
62-- @param kind string: the kind of rock file, as in the second-level
63-- extension in the rock filename (eg. "src", "all", "linux-x86")
64-- @return table or (nil, string): the loaded rockspec table or
65-- nil and an error message.
66local function unpack_rock(rock_file, dir_name, kind)
67 assert(type(rock_file) == "string")
68 assert(type(dir_name) == "string")
69
70 local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, dir_name)
71 if not ok then
72 return nil, err, errcode
73 end
74 ok, err = fs.change_dir(dir_name)
75 if not ok then return nil, err end
76 local rockspec_file = dir_name..".rockspec"
77 local rockspec, err = fetch.load_rockspec(rockspec_file)
78 if not rockspec then
79 return nil, "Failed loading rockspec "..rockspec_file..": "..err
80 end
81 if kind == "src" then
82 if rockspec.source.file then
83 local ok, err = fs.unpack_archive(rockspec.source.file)
84 if not ok then return nil, err end
85 ok, err = fetch.find_rockspec_source_dir(rockspec, ".")
86 if not ok then return nil, err end
87 ok, err = fs.change_dir(rockspec.source.dir)
88 if not ok then return nil, err end
89 ok, err = build.apply_patches(rockspec)
90 fs.pop_dir()
91 if not ok then return nil, err end
92 end
93 end
94 return rockspec
95end
96
97--- Create a directory and perform the necessary actions so that
98-- the sources for the rock and its rockspec are unpacked inside it,
99-- laid out properly so that the 'make' command is able to build the module.
100-- @param file string: A rockspec or .rock URL.
101-- @return boolean or (nil, string): true if successful or nil followed
102-- by an error message.
103local function run_unpacker(file, force)
104 assert(type(file) == "string")
105
106 local base_name = dir.base_name(file)
107 local dir_name, kind, extension = base_name:match("(.*)%.([^.]+)%.(rock)$")
108 if not extension then
109 dir_name, extension = base_name:match("(.*)%.(rockspec)$")
110 kind = "rockspec"
111 end
112 if not extension then
113 return nil, file.." does not seem to be a valid filename."
114 end
115
116 local exists = fs.exists(dir_name)
117 if exists and not force then
118 return nil, "Directory "..dir_name.." already exists."
119 end
120 if not exists then
121 local ok, err = fs.make_dir(dir_name)
122 if not ok then return nil, err end
123 end
124 local rollback = util.schedule_function(fs.delete, fs.absolute_name(dir_name))
125
126 local rockspec, err
127 if extension == "rock" then
128 rockspec, err = unpack_rock(file, dir_name, kind)
129 elseif extension == "rockspec" then
130 rockspec, err = unpack_rockspec(file, dir_name)
131 end
132 if not rockspec then
133 return nil, err
134 end
135 if kind == "src" or kind == "rockspec" then
136 fetch.find_rockspec_source_dir(rockspec, ".")
137 if rockspec.source.dir ~= "." then
138 local ok = fs.copy(rockspec.local_abs_filename, rockspec.source.dir, "read")
139 if not ok then
140 return nil, "Failed copying unpacked rockspec into unpacked source directory."
141 end
142 end
143 util.printout()
144 util.printout("Done. You may now enter directory ")
145 util.printout(dir.path(dir_name, rockspec.source.dir))
146 util.printout("and type 'luarocks make' to build.")
147 end
148 util.remove_scheduled_function(rollback)
149 return true
150end
151
152--- Driver function for the "unpack" command.
153-- @return boolean or (nil, string): true if successful or nil followed
154-- by an error message.
155function unpack.command(args)
156 local url, err
157 if args.rock:match(".*%.rock") or args.rock:match(".*%.rockspec") then
158 url = args.rock
159 else
160 url, err = search.find_src_or_rockspec(args.rock, args.namespace, args.version, args.check_lua_versions)
161 if not url then
162 return nil, err
163 end
164 end
165
166 return run_unpacker(url, args.force)
167end
168
169return unpack
diff --git a/src/luarocks/cmd/upload.tl b/src/luarocks/cmd/upload.tl
new file mode 100644
index 00000000..6b84e452
--- /dev/null
+++ b/src/luarocks/cmd/upload.tl
@@ -0,0 +1,128 @@
1
2local upload = {}
3
4local signing = require("luarocks.signing")
5local util = require("luarocks.util")
6local fetch = require("luarocks.fetch")
7local pack = require("luarocks.pack")
8local cfg = require("luarocks.core.cfg")
9local Api = require("luarocks.upload.api")
10
11function upload.add_to_parser(parser)
12 local cmd = parser:command("upload", "Pack a source rock file (.src.rock extension) "..
13 "and upload it and the rockspec to the public rocks repository.", util.see_also())
14 :summary("Upload a rockspec to the public rocks repository.")
15
16 cmd:argument("rockspec", "Rockspec for the rock to upload.")
17 cmd:argument("src-rock", "A corresponding .src.rock file; if not given it will be generated.")
18 :args("?")
19
20 cmd:flag("--skip-pack", "Do not pack and send source rock.")
21 cmd:option("--api-key", "Pass an API key. It will be stored for subsequent uses.")
22 :argname("<key>")
23 cmd:option("--temp-key", "Use the given a temporary API key in this "..
24 "invocation only. It will not be stored.")
25 :argname("<key>")
26 cmd:flag("--force", "Replace existing rockspec if the same revision of a "..
27 "module already exists. This should be used only in case of upload "..
28 "mistakes: when updating a rockspec, increment the revision number "..
29 "instead.")
30 cmd:flag("--sign", "Upload a signature file alongside each file as well.")
31 cmd:flag("--debug"):hidden(true)
32end
33
34local function is_dev_version(version)
35 return version:match("^dev") or version:match("^scm")
36end
37
38function upload.command(args)
39 local api, err = Api.new(args)
40 if not api then
41 return nil, err
42 end
43 if cfg.verbose then
44 api.debug = true
45 end
46
47 local rockspec, err, errcode = fetch.load_rockspec(args.rockspec)
48 if err then
49 return nil, err, errcode
50 end
51
52 util.printout("Sending " .. tostring(args.rockspec) .. " ...")
53 local res, err = api:method("check_rockspec", {
54 package = rockspec.package,
55 version = rockspec.version
56 })
57 if not res then return nil, err end
58
59 if not res.module then
60 util.printout("Will create new module (" .. tostring(rockspec.package) .. ")")
61 end
62 if res.version and not args.force then
63 return nil, "Revision "..rockspec.version.." already exists on the server. "..util.see_help("upload")
64 end
65
66 local sigfname
67 local rock_sigfname
68
69 if args.sign then
70 sigfname, err = signing.sign_file(args.rockspec)
71 if err then
72 return nil, "Failed signing rockspec: " .. err
73 end
74 util.printout("Signed rockspec: "..sigfname)
75 end
76
77 local rock_fname
78 if args.src_rock then
79 rock_fname = args.src_rock
80 elseif not args.skip_pack and not is_dev_version(rockspec.version) then
81 util.printout("Packing " .. tostring(rockspec.package))
82 rock_fname, err = pack.pack_source_rock(args.rockspec)
83 if not rock_fname then
84 return nil, err
85 end
86 end
87
88 if rock_fname and args.sign then
89 rock_sigfname, err = signing.sign_file(rock_fname)
90 if err then
91 return nil, "Failed signing rock: " .. err
92 end
93 util.printout("Signed packed rock: "..rock_sigfname)
94 end
95
96 local multipart = require("luarocks.upload.multipart")
97
98 res, err = api:method("upload", nil, {
99 rockspec_file = multipart.new_file(args.rockspec),
100 rockspec_sig = sigfname and multipart.new_file(sigfname),
101 })
102 if not res then return nil, err end
103
104 if res.is_new and #res.manifests == 0 then
105 util.printerr("Warning: module not added to root manifest due to name taken.")
106 end
107
108 local module_url = res.module_url
109
110 if rock_fname then
111 if (not res.version) or (not res.version.id) then
112 return nil, "Invalid response from server."
113 end
114 util.printout(("Sending " .. tostring(rock_fname) .. " ..."))
115 res, err = api:method("upload_rock/" .. ("%d"):format(res.version.id), nil, {
116 rock_file = multipart.new_file(rock_fname),
117 rock_sig = rock_sigfname and multipart.new_file(rock_sigfname),
118 })
119 if not res then return nil, err end
120 end
121
122 util.printout()
123 util.printout("Done: " .. tostring(module_url))
124 util.printout()
125 return true
126end
127
128return upload
diff --git a/src/luarocks/cmd/which.tl b/src/luarocks/cmd/which.tl
new file mode 100644
index 00000000..f50a43c3
--- /dev/null
+++ b/src/luarocks/cmd/which.tl
@@ -0,0 +1,40 @@
1
2--- @module luarocks.which_cmd
3-- Driver for the `luarocks which` command.
4local which_cmd = {}
5
6local loader = require("luarocks.loader")
7local cfg = require("luarocks.core.cfg")
8local util = require("luarocks.util")
9
10function which_cmd.add_to_parser(parser)
11 local cmd = parser:command("which", 'Given a module name like "foo.bar", '..
12 "output which file would be loaded to resolve that module by "..
13 'luarocks.loader, like "/usr/local/lua/'..cfg.lua_version..'/foo/bar.lua".',
14 util.see_also())
15 :summary("Tell which file corresponds to a given module name.")
16
17 cmd:argument("modname", "Module name.")
18end
19
20--- Driver function for "which" command.
21-- @return boolean This function terminates the interpreter.
22function which_cmd.command(args)
23 local pathname, rock_name, rock_version, where = loader.which(args.modname, "lp")
24
25 if pathname then
26 util.printout(pathname)
27 if where == "l" then
28 util.printout("(provided by " .. tostring(rock_name) .. " " .. tostring(rock_version) .. ")")
29 else
30 local key = rock_name
31 util.printout("(found directly via package." .. key.. " -- not installed as a rock?)")
32 end
33 return true
34 end
35
36 return nil, "Module '" .. args.modname .. "' not found."
37end
38
39return which_cmd
40
diff --git a/src/luarocks/cmd/write_rockspec.tl b/src/luarocks/cmd/write_rockspec.tl
new file mode 100644
index 00000000..e8d65cba
--- /dev/null
+++ b/src/luarocks/cmd/write_rockspec.tl
@@ -0,0 +1,425 @@
1
2local record write_rockspec
3end
4
5local builtin = require("luarocks.build.builtin")
6local cfg = require("luarocks.core.cfg")
7local dir = require("luarocks.dir")
8local fetch = require("luarocks.fetch")
9local fs = require("luarocks.fs")
10local persist = require("luarocks.persist")
11local rockspecs = require("luarocks.rockspecs")
12local type_rockspec = require("luarocks.type.rockspec")
13local util = require("luarocks.util")
14
15local argparse = require("luarocks.vendor.argparse")
16local type Parser = argparse.Parser
17local type Option = argparse.Option
18
19local type a = require("luarocks.core.types.args")
20local type Args = a.Args
21
22local type p = require("luarocks.core.types.persist")
23local type PersistableTable = p.PersistableTable
24
25local type b = require("luarocks.core.types.build")
26local type BuiltinBuild = b.BuiltinBuild
27
28local type r = require("luarocks.core.types.rockspec")
29local type Rockspec = r.Rockspec
30local type Dependencies = r.Dependencies
31
32local lua_versions = {
33 "5.1",
34 "5.2",
35 "5.3",
36 "5.4",
37 "5.1,5.2",
38 "5.2,5.3",
39 "5.3,5.4",
40 "5.1,5.2,5.3",
41 "5.2,5.3,5.4",
42 "5.1,5.2,5.3,5.4"
43}
44
45function write_rockspec.cmd_options(parser: Parser): Option, Option, Option, Option, Option, Option, Option, Option, Option
46 return parser:option("--output", "Write the rockspec with the given filename.\n"..
47 "If not given, a file is written in the current directory with a "..
48 "filename based on given name and version.")
49 :argname("<file>"),
50 parser:option("--license", 'A license string, such as "MIT/X11" or "GNU GPL v3".')
51 :argname("<string>"),
52 parser:option("--summary", "A short one-line description summary.")
53 :argname("<txt>"),
54 parser:option("--detailed", "A longer description string.")
55 :argname("<txt>"),
56 parser:option("--homepage", "Project homepage.")
57 :argname("<txt>"),
58 parser:option("--lua-versions", 'Supported Lua versions. Accepted values are: "'..
59 table.concat(lua_versions, '", "')..'".')
60 :argname("<ver>")
61 :choices(lua_versions),
62 parser:option("--rockspec-format", 'Rockspec format version, such as "1.0" or "1.1".')
63 :argname("<ver>"),
64 parser:option("--tag", "Tag to use. Will attempt to extract version number from it."),
65 parser:option("--lib", "A comma-separated list of libraries that C files need to link to.")
66 :argname("<libs>")
67end
68
69function write_rockspec.add_to_parser(parser: Parser)
70 local cmd = parser:command("write_rockspec", [[
71This command writes an initial version of a rockspec file,
72based on a name, a version, and a location (an URL or a local path).
73If only two arguments are given, the first one is considered the name and the
74second one is the location.
75If only one argument is given, it must be the location.
76If no arguments are given, current directory is used as the location.
77LuaRocks will attempt to infer name and version if not given,
78using 'dev' as a fallback default version.
79
80Note that the generated file is a _starting point_ for writing a
81rockspec, and is not guaranteed to be complete or correct. ]], util.see_also())
82 :summary("Write a template for a rockspec file.")
83
84 cmd:argument("name", "Name of the rock.")
85 :args("?")
86 cmd:argument("version", "Rock version.")
87 :args("?")
88 cmd:argument("location", "URL or path to the rock sources.")
89 :args("?")
90
91 write_rockspec.cmd_options(cmd) --!
92end
93
94local function open_file(name: string): FILE, string, integer
95 return io.open(dir.path(fs.current_dir(), name), "r")
96end
97
98local function fetch_url(rockspec: Rockspec): boolean, string, string
99 local file, temp_dir, err_code, err_file, err_temp_dir = fetch.fetch_sources(rockspec, false)
100 if err_code == "source.dir" then
101 file, temp_dir = err_file, err_temp_dir
102 elseif not file then
103 util.warning("Could not fetch sources - "..temp_dir)
104 return false
105 end
106 util.printout("File successfully downloaded. Making checksum and checking base dir...")
107 if dir.is_basic_protocol(rockspec.source.protocol) then
108 rockspec.source.md5 = fs.get_md5(file)
109 end
110 local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, rockspec.source.url)
111 return true, found_dir or inferred_dir, temp_dir
112end
113
114local lua_version_dep = {
115 ["5.1"] = "lua ~> 5.1",
116 ["5.2"] = "lua ~> 5.2",
117 ["5.3"] = "lua ~> 5.3",
118 ["5.4"] = "lua ~> 5.4",
119 ["5.1,5.2"] = "lua >= 5.1, < 5.3",
120 ["5.2,5.3"] = "lua >= 5.2, < 5.4",
121 ["5.3,5.4"] = "lua >= 5.3, < 5.5",
122 ["5.1,5.2,5.3"] = "lua >= 5.1, < 5.4",
123 ["5.2,5.3,5.4"] = "lua >= 5.2, < 5.5",
124 ["5.1,5.2,5.3,5.4"] = "lua >= 5.1, < 5.5",
125}
126
127local simple_scm_protocols = {
128 git = true,
129 ["git+http"] = true,
130 ["git+https"] = true,
131 ["git+ssh"] = true,
132 hg = true,
133 ["hg+http"] = true,
134 ["hg+https"] = true,
135 ["hg+ssh"] = true,
136}
137
138local detect_url: function(string): string
139do
140 local function detect_url_from_command(program: string, args: string, directory: string): string
141 local command = fs.Q(cfg.variables[program:upper()]).. " "..args
142 local pipe = io.popen(fs.command_at(directory, fs.quiet_stderr(command)))
143 if not pipe then return nil end
144 local url = pipe:read("*a"):match("^([^\r\n]+)")
145 pipe:close()
146 if not url then return nil end
147 if url:match("^[^@:/]+@[^@:/]+:.*$") then
148 local u, h, p = url:match("^([^@]+)@([^:]+):(.*)$")
149 url = program.."+ssh://"..u.."@"..h.."/"..p
150 elseif not util.starts_with(url, program.."://") then
151 url = program.."+"..url
152 end
153
154 if (simple_scm_protocols as {string: boolean})[dir.split_url(url)] then
155 return url
156 end
157 end
158
159 local function detect_scm_url(directory: string): string
160 return detect_url_from_command("git", "config --get remote.origin.url", directory) or
161 detect_url_from_command("hg", "paths default", directory)
162 end
163
164 detect_url = function(url_or_dir: string): string
165 if url_or_dir:match("://") then
166 return url_or_dir
167 else
168 return detect_scm_url(url_or_dir) or "*** please add URL for source tarball, zip or repository here ***"
169 end
170 end
171end
172
173local function detect_homepage(url: string, homepage: string): string
174 if homepage then
175 return homepage
176 end
177 local url_protocol, url_path = dir.split_url(url)
178
179 if (simple_scm_protocols as {string: boolean})[url_protocol] then
180 for _, domain in ipairs({"github.com", "bitbucket.org", "gitlab.com"}) do
181 if util.starts_with(url_path, domain) then
182 return "https://"..url_path:gsub("%.git$", "")
183 end
184 end
185 end
186
187 return "*** please enter a project homepage ***"
188end
189
190local function detect_description(): string, string
191 local fd = open_file("README.md") or open_file("README")
192 if not fd then return end
193 local data = fd:read("*a")
194 fd:close()
195 local paragraph = data:match("\n\n([^%[].-)\n\n")
196 if not paragraph then paragraph = data:match("\n\n(.*)") end
197 local summary, detailed: string, string
198 if paragraph then
199 detailed = paragraph
200
201 if #paragraph < 80 then
202 summary = paragraph:gsub("\n", "")
203 else
204 summary = paragraph:gsub("\n", " "):match("([^.]*%.) ")
205 end
206 end
207 return summary, detailed
208end
209
210local licenses = {
211 [78656] = "MIT",
212 [49311] = "ISC",
213}
214
215local function detect_license(data: string): string
216 local strip_copyright = (data:gsub("^Copyright [^\n]*\n", ""))
217 local sum = 0
218 for i = 1, #strip_copyright do
219 local num = string.byte(strip_copyright:sub(i,i))
220 if num > 32 and num <= 128 then
221 sum = sum + num
222 end
223 end
224 return licenses[sum]
225end
226
227local function check_license(): string, string
228 local fd = open_file("COPYING") or open_file("LICENSE") or open_file("MIT-LICENSE.txt")
229 if not fd then return nil end
230 local data = fd:read("*a")
231 fd:close()
232 local license = detect_license(data)
233 if license then
234 return license, data
235 end
236 return nil, data
237end
238
239local function fill_as_builtin(rockspec: Rockspec, libs: {string})
240 rockspec.build.type = "builtin"
241
242 local incdirs, libdirs: {string}, {string}
243 if libs then
244 incdirs, libdirs = {}, {}
245 for _, lib in ipairs(libs) do
246 local upper = lib:upper()
247 incdirs[#incdirs+1] = "$("..upper.."_INCDIR)"
248 libdirs[#libdirs+1] = "$("..upper.."_LIBDIR)"
249 end
250 end
251 (rockspec.build as BuiltinBuild).modules, rockspec.build.install, rockspec.build.copy_directories = builtin.autodetect_modules(libs, incdirs, libdirs)
252end
253
254local function rockspec_cleanup(rockspec: Rockspec)
255 rockspec.source.file = nil
256 rockspec.source.protocol = nil
257 rockspec.source.identifier = nil
258 rockspec.source.dir = nil
259 rockspec.source.dir_set = nil
260 rockspec.source.pathname = nil
261 rockspec.variables = nil
262 rockspec.name = nil
263 rockspec.format_is_at_least = nil
264 rockspec.local_abs_filename = nil
265 rockspec.rocks_provided = nil
266 for _, list in ipairs({"dependencies", "build_dependencies", "test_dependencies"}) do
267 if (rockspec as {string: Dependencies})[list] and not next((rockspec as {string: Dependencies})[list]) then
268 (rockspec as {string: Dependencies})[list] = nil
269 end
270 end
271 for _, list in ipairs({"dependencies", "build_dependencies", "test_dependencies"}) do
272 if (rockspec as {string: Dependencies})[list] then
273 for i, entry in ipairs((rockspec as {string: Dependencies})[list]) do
274 (rockspec as {string: Dependencies})[list][i] = tostring(entry)
275 end
276 end
277 end
278end
279
280function write_rockspec.command(args: Args): boolean, string
281 local name, version = args.name, args.version
282 local location = args.location
283
284 if not name then
285 location = "."
286 elseif not version then
287 location = name
288 name = nil
289 elseif not location then
290 location = version
291 version = nil
292 end
293
294 if args.tag then
295 if not version then
296 version = args.tag:gsub("^v", "")
297 end
298 end
299
300 local protocol, pathname = dir.split_url(location)
301 if protocol == "file" then
302 if pathname == "." then
303 name = name or dir.base_name(fs.current_dir())
304 end
305 elseif dir.is_basic_protocol(protocol) then
306 local filename = dir.base_name(location)
307 local newname, newversion = filename:match("(.*)-([^-]+)")
308 if newname then
309 name = name or newname
310 version = version or newversion:gsub("%.[a-z]+$", ""):gsub("%.tar$", "")
311 end
312 else
313 name = name or dir.base_name(location):gsub("%.[^.]+$", "")
314 end
315
316 if not name then
317 return nil, "Could not infer rock name. "..util.see_help("write_rockspec")
318 end
319 version = version or "dev"
320
321 local filename = args.output or dir.path(fs.current_dir(), name:lower().."-"..version.."-1.rockspec")
322
323 local url = detect_url(location)
324 local homepage = detect_homepage(url, args.homepage)
325
326 local rockspec, err = rockspecs.from_persisted_table(filename, {
327 rockspec_format = args.rockspec_format,
328 package = name,
329 version = version.."-1",
330 source = {
331 url = url,
332 tag = args.tag,
333 },
334 description = {
335 summary = args.summary or "*** please specify description summary ***",
336 detailed = args.detailed or "*** please enter a detailed description ***",
337 homepage = homepage,
338 license = args.license or "*** please specify a license ***",
339 },
340 dependencies = {
341 (lua_version_dep as {string: string})[args.lua_versions],
342 },
343 build = {},
344 })
345 assert(not err, err)
346 rockspec.source.protocol = protocol
347
348 if not next(rockspec.dependencies) then
349 util.warning("Please specify supported Lua versions with --lua-versions=<ver>. "..util.see_help("write_rockspec"))
350 end
351
352 local local_dir = location
353
354 if location:match("://") then
355 rockspec.source.file = dir.base_name(location)
356 if not dir.is_basic_protocol(rockspec.source.protocol) then
357 if version ~= "dev" then
358 rockspec.source.tag = args.tag or "v" .. version
359 end
360 end
361 rockspec.source.dir = nil
362 local ok, base_dir, temp_dir = fetch_url(rockspec)
363 if ok then
364 if base_dir ~= dir.base_name(location) then
365 rockspec.source.dir = base_dir
366 end
367 end
368 if base_dir then
369 local_dir = dir.path(temp_dir, base_dir)
370 else
371 local_dir = nil
372 end
373 end
374
375 if not local_dir then
376 local_dir = "."
377 end
378
379 local libs: {string} = nil
380 if args.lib then
381 libs = {}
382 rockspec.external_dependencies = {}
383 for lib in args.lib:gmatch("([^,]+)") do
384 table.insert(libs, lib)
385 rockspec.external_dependencies[lib:upper()] = {
386 library = lib
387 }
388 end
389 end
390
391 local ok, err = fs.change_dir(local_dir)
392 if not ok then return nil, "Failed reaching files from project - error entering directory "..local_dir end
393
394 if not (args.summary and args.detailed) then
395 local summary, detailed = detect_description()
396 rockspec.description.summary = args.summary or summary
397 rockspec.description.detailed = args.detailed or detailed
398 end
399
400 if not args.license then
401 local license, fulltext = check_license()
402 if license then
403 rockspec.description.license = license
404 elseif license then
405 util.title("Could not auto-detect type for project license:")
406 util.printout(fulltext)
407 util.printout()
408 util.title("Please fill in the source.license field manually or use --license.")
409 end
410 end
411
412 fill_as_builtin(rockspec, libs)
413
414 rockspec_cleanup(rockspec)
415
416 persist.save_from_table(filename, rockspec as PersistableTable, type_rockspec.order)
417
418 util.printout()
419 util.printout("Wrote template at "..filename.." -- you should now edit and finish it.")
420 util.printout()
421
422 return true
423end
424
425return write_rockspec
diff --git a/src/luarocks/core/cfg.d.tl b/src/luarocks/core/cfg.d.tl
index cbe53663..7dea5b32 100644
--- a/src/luarocks/core/cfg.d.tl
+++ b/src/luarocks/core/cfg.d.tl
@@ -38,19 +38,15 @@ local record cfg
38 -- queries 38 -- queries
39 arch: string 39 arch: string
40 -- api 40 -- api
41 record conf
42 file: string
43 found: boolean
44 end
45
41 record config_files 46 record config_files
42 record system 47 system: conf
43 file: string 48 user: conf
44 found: boolean 49 project: conf
45 end
46 record user
47 file: string
48 found: boolean
49 end
50 record project
51 file: string
52 found: boolean
53 end
54 end 50 end
55 -- type_check 51 -- type_check
56 accept_unknown_fields: boolean 52 accept_unknown_fields: boolean
@@ -109,6 +105,8 @@ local record cfg
109 project_dir: string 105 project_dir: string
110 verbose: boolean 106 verbose: boolean
111 project_tree: string 107 project_tree: string
108 -- cmd make
109 keep_other_versions: boolean
112end 110end
113 111
114return cfg \ No newline at end of file 112return cfg \ No newline at end of file
diff --git a/src/luarocks/core/types/args.d.tl b/src/luarocks/core/types/args.d.tl
index a8e4ef88..a4f240cc 100644
--- a/src/luarocks/core/types/args.d.tl
+++ b/src/luarocks/core/types/args.d.tl
@@ -21,6 +21,52 @@ local record args
21 only_sources: string 21 only_sources: string
22 no_manifest: boolean 22 no_manifest: boolean
23 force_lock: boolean 23 force_lock: boolean
24 rockspec: string
25 namespace: string
26 pack_binary_rock: boolean
27 only_deps: boolean
28 branch: string
29 verify: boolean --!
30 check_lua_versions: boolean --!
31 pin: boolean
32 no_install: boolean
33 sign: boolean
34 no_doc: boolean
35 keep: boolean
36 force: boolean
37 force_fast: boolean
38 rock: string
39 version: string
40 scope: string
41 lua_incdir: string
42 lua_libdir: string
43 lua_ver: string
44 system_config: string
45 user_config: string
46 rock_trees: string
47 unset: boolean
48 json: boolean
49 home: boolean
50 porcelain: boolean
51 list: boolean --!
52 name: string
53 all: boolean
54 source: string
55 arch: string
56 location: string
57 tag: string
58 output: string
59 homepage: string
60 rockspec_format: string
61 summary: string
62 detailed: string
63 license: string
64 lua_versions: string
65 lib: string
66 no_gitignore: boolean
67 no_wrapper_scripts: boolean
68 wrapper_dir: string
69 reset: boolean
24 end 70 end
25end 71end
26 72
diff --git a/src/luarocks/core/types/bopts.d.tl b/src/luarocks/core/types/bopts.d.tl
new file mode 100644
index 00000000..81575c0c
--- /dev/null
+++ b/src/luarocks/core/types/bopts.d.tl
@@ -0,0 +1,17 @@
1local record bopts
2 record BOpts
3 build_only_deps: boolean
4 deps_mode: string
5 verify: boolean
6 minimal_mode: boolean
7 need_to_fetch: boolean
8 branch: string
9 no_install: boolean
10 pin: boolean
11 namespace: string
12 check_lua_versions: boolean
13 rebuild: boolean
14 end
15end
16
17return bopts \ No newline at end of file
diff --git a/src/luarocks/core/types/installs.d.tl b/src/luarocks/core/types/installs.d.tl
index 57ad750a..ec406760 100644
--- a/src/luarocks/core/types/installs.d.tl
+++ b/src/luarocks/core/types/installs.d.tl
@@ -12,6 +12,16 @@ local record installs
12 bin: InstallDir 12 bin: InstallDir
13 end 13 end
14 14
15 record IOpts
16 namespace: string
17 deps_mode: string
18 force: boolean
19 force_fast: boolean
20 verify: boolean
21 no_doc: boolean
22 keep: boolean
23 end
24
15end 25end
16 26
17return installs \ No newline at end of file 27return installs \ No newline at end of file
diff --git a/src/luarocks/deps.tl b/src/luarocks/deps.tl
index 69c739ca..1f5f24aa 100644
--- a/src/luarocks/deps.tl
+++ b/src/luarocks/deps.tl
@@ -41,6 +41,9 @@ local type dr = require("luarocks.core.types.dir")
41local type Dir = dr.Dir 41local type Dir = dr.Dir
42local type Dirs = dr.Dirs 42local type Dirs = dr.Dirs
43 43
44local type arg = require("luarocks.core.types.args")
45local type Args = arg.Args
46
44--- Generate a function that matches dep queries against the manifest, 47--- Generate a function that matches dep queries against the manifest,
45-- taking into account rocks_provided, the list of versions to skip, 48-- taking into account rocks_provided, the list of versions to skip,
46-- and the lockfile. 49-- and the lockfile.
@@ -761,7 +764,7 @@ local function find_lua_incdir(prefix: string, luaver: string, luajitver: string
761end 764end
762 765
763function deps.check_lua_incdir(vars: {string: string}): boolean, string, string 766function deps.check_lua_incdir(vars: {string: string}): boolean, string, string
764 if vars.LUA_INCDIR_OK == "ok" 767 if vars.LUA_INCDIR_OK == "ok" --! fix later
765 then return true 768 then return true
766 end 769 end
767 770
@@ -846,7 +849,7 @@ function deps.check_lua_libdir(vars: {string: string}): boolean, string, string,
846 end 849 end
847end 850end
848 851
849function deps.get_deps_mode(args: {string: string}): string 852function deps.get_deps_mode(args: Args): string
850 return args.deps_mode or cfg.deps_mode 853 return args.deps_mode or cfg.deps_mode
851end 854end
852 855
diff --git a/src/luarocks/download.tl b/src/luarocks/download.tl
index 508f74b4..f993da0f 100644
--- a/src/luarocks/download.tl
+++ b/src/luarocks/download.tl
@@ -59,7 +59,7 @@ function download.download_all(arch: string, name: string, namespace: string, ve
59 return nil, "Could not find a result named "..rock..(search_err and ": "..search_err or ".") 59 return nil, "Could not find a result named "..rock..(search_err and ": "..search_err or ".")
60end 60end
61 61
62function download.download_file(arch: string, name: string, namespace: string, version: string, check_lua_versions?: boolean): string, string 62function download.download_file(arch: string, name: string, namespace?: string, version?: string, check_lua_versions?: boolean): string, string
63 local query = queries.new(name, namespace, version, false, arch) 63 local query = queries.new(name, namespace, version, false, arch)
64 local search_err: string 64 local search_err: string
65 65
diff --git a/src/luarocks/fetch.tl b/src/luarocks/fetch.tl
index 30c45b56..ccd8680e 100644
--- a/src/luarocks/fetch.tl
+++ b/src/luarocks/fetch.tl
@@ -311,7 +311,7 @@ end
311-- be nil if not found), or nil followed by an error message. 311-- be nil if not found), or nil followed by an error message.
312-- The inferred dir is returned first to avoid confusion with errors, 312-- The inferred dir is returned first to avoid confusion with errors,
313-- because it is never nil. 313-- because it is never nil.
314function fetch.find_base_dir(file: string, temp_dir: string, src_url: string, src_dir: string): string, string 314function fetch.find_base_dir(file: string, temp_dir: string, src_url: string, src_dir?: string): string, string
315 local ok, err = fs.change_dir(temp_dir) 315 local ok, err = fs.change_dir(temp_dir)
316 if not ok then return nil, err end 316 if not ok then return nil, err end
317 fs.unpack_archive(file) 317 fs.unpack_archive(file)
diff --git a/src/luarocks/fs.d.tl b/src/luarocks/fs.d.tl
index f3ff6eb2..a0fca017 100644
--- a/src/luarocks/fs.d.tl
+++ b/src/luarocks/fs.d.tl
@@ -75,6 +75,13 @@ local record fs
75 modules: function(string): {string} 75 modules: function(string): {string}
76 system_cache_dir: function(): string 76 system_cache_dir: function(): string
77 check_command_permissions: function(Args): boolean, string 77 check_command_permissions: function(Args): boolean, string
78 -- cmd config
79 is_writable: function(string): boolean
80 browser: function(string): boolean
81 -- cmd write_rockspec
82 quiet_stderr: function(string): string
83 -- cmd innit
84 wrap_script: function(string, string, string, ...:string): boolean, string
78end 85end
79 86
80return fs 87return fs
diff --git a/src/luarocks/pack.tl b/src/luarocks/pack.tl
index 7b257161..037a1069 100644
--- a/src/luarocks/pack.tl
+++ b/src/luarocks/pack.tl
@@ -159,7 +159,7 @@ function pack.report_and_sign_local_file(file: string, err: string, sign: boolea
159 return true 159 return true
160end 160end
161 161
162function pack.pack_binary_rock(name: string, namespace: string, version: string, sign: boolean, cmd: function(): (boolean, string)): boolean, string 162function pack.pack_binary_rock(name: string, namespace: string, version: string, sign: boolean, cmd: function(): (string, string)): boolean, string
163 163
164 -- The --pack-binary-rock option for "luarocks build" basically performs 164 -- The --pack-binary-rock option for "luarocks build" basically performs
165 -- "luarocks build" on a temporary tree and then "luarocks pack". The 165 -- "luarocks build" on a temporary tree and then "luarocks pack". The
diff --git a/src/luarocks/persist.tl b/src/luarocks/persist.tl
index cc07d295..a2666563 100644
--- a/src/luarocks/persist.tl
+++ b/src/luarocks/persist.tl
@@ -4,6 +4,11 @@
4local record persist 4local record persist
5 run_file: function(string, {string:any}): boolean, any | string, string 5 run_file: function(string, {string:any}): boolean, any | string, string
6 load_into_table: function(string, ?{string:any}) : {any: any}, {any: any} | string, string 6 load_into_table: function(string, ?{string:any}) : {any: any}, {any: any} | string, string
7
8 interface Writer
9 write: function(self: Writer, data: string)
10 buffer: {number | string}
11 end
7end 12end
8 13
9local core = require("luarocks.core.persist") 14local core = require("luarocks.core.persist")
@@ -20,14 +25,11 @@ local type SortBy = o.SortBy
20local type p = require("luarocks.core.types.persist") 25local type p = require("luarocks.core.types.persist")
21local type PersistableTable = p.PersistableTable 26local type PersistableTable = p.PersistableTable
22 27
28local type Writer = persist.Writer
29
23persist.run_file = core.run_file 30persist.run_file = core.run_file
24persist.load_into_table = core.load_into_table 31persist.load_into_table = core.load_into_table
25 32
26local interface Writer
27 write: function(self: Writer, data: string)
28 buffer: {number | string}
29end
30
31local write_table: function(out: Writer, tbl: PersistableTable, level: integer, sort_by: SortBy<number | string>) 33local write_table: function(out: Writer, tbl: PersistableTable, level: integer, sort_by: SortBy<number | string>)
32 34
33--- Write a value as Lua code. 35--- Write a value as Lua code.
@@ -38,7 +40,7 @@ local write_table: function(out: Writer, tbl: PersistableTable, level: integer,
38-- @param level number: the indentation level 40-- @param level number: the indentation level
39-- @param sub_order table: optional prioritization table 41-- @param sub_order table: optional prioritization table
40-- @see write_table 42-- @see write_table
41function persist.write_value(out: Writer, v: any, level: integer, sub_order?: SortBy<number | string>) 43function persist.write_value(out: Writer, v: any, level?: integer, sub_order?: SortBy<number | string>)
42 if v is PersistableTable then 44 if v is PersistableTable then
43 level = level or 0 45 level = level or 0
44 write_table(out, v, level + 1, sub_order) 46 write_table(out, v, level + 1, sub_order)
diff --git a/src/luarocks/util.tl b/src/luarocks/util.tl
index 27a9c117..53e363a5 100644
--- a/src/luarocks/util.tl
+++ b/src/luarocks/util.tl
@@ -53,7 +53,9 @@ util.matchquote = core.matchquote
53local type Fn = util.Fn 53local type Fn = util.Fn
54local type r = require("luarocks.core.types.rockspec") 54local type r = require("luarocks.core.types.rockspec")
55local type Rockspec = r.Rockspec 55local type Rockspec = r.Rockspec
56local type Parser = util.Parser 56
57local argparse = require("luarocks.vendor.argparse")
58local type Parser = argparse.Parser
57 59
58 60
59local scheduled_functions: {Fn} = {} 61local scheduled_functions: {Fn} = {}
@@ -207,7 +209,7 @@ function util.printout(...: string)
207 io.stdout:write("\n") 209 io.stdout:write("\n")
208end 210end
209 211
210function util.title(msg: string, porcelain: boolean, underline: string) 212function util.title(msg: string, porcelain?: boolean, underline?: string)
211 if porcelain then return end 213 if porcelain then return end
212 util.printout() 214 util.printout()
213 util.printout(msg) 215 util.printout(msg)
@@ -241,7 +243,7 @@ function util.format_rock_name(name: string, namespace: string, version: string)
241 return (namespace and namespace.."/" or "")..name..(version and " "..version or "") 243 return (namespace and namespace.."/" or "")..name..(version and " "..version or "")
242end 244end
243 245
244function util.deps_mode_option(parser: Parser, program: string) 246function util.deps_mode_option(parser: Parser, program?: string)
245 247
246 parser:option("--deps-mode", "How to handle dependencies. Four modes are supported:\n".. 248 parser:option("--deps-mode", "How to handle dependencies. Four modes are supported:\n"..
247 "* all - use all trees from the rocks_trees list for finding dependencies\n".. 249 "* all - use all trees from the rocks_trees list for finding dependencies\n"..
@@ -258,7 +260,7 @@ function util.deps_mode_option(parser: Parser, program: string)
258 parser:flag("--nodeps"):hidden(true) 260 parser:flag("--nodeps"):hidden(true)
259end 261end
260 262
261function util.see_help(command: string, program: string): string 263function util.see_help(command: string, program?: string): string
262 return "See '"..util.this_program(program or "luarocks")..' help'..(command and " "..command or "").."'." 264 return "See '"..util.this_program(program or "luarocks")..' help'..(command and " "..command or "").."'."
263end 265end
264 266
diff --git a/src/luarocks/vendor/argparse.d.tl b/src/luarocks/vendor/argparse.d.tl
index 42e35787..812786c4 100644
--- a/src/luarocks/vendor/argparse.d.tl
+++ b/src/luarocks/vendor/argparse.d.tl
@@ -34,6 +34,8 @@ local record argparse
34 add_help_command: function(self: Parser, ?string | {string: any}): Parser 34 add_help_command: function(self: Parser, ?string | {string: any}): Parser
35 add_complete_command: function(self: Parser, ?string | {string: any}): Parser 35 add_complete_command: function(self: Parser, ?string | {string: any}): Parser
36 36
37 group: function(self: Parser, ...:any): Parser
38
37 -- TODO: should be Argument | Option 39 -- TODO: should be Argument | Option
38 mutex: function(self: Parser, ...: any) 40 mutex: function(self: Parser, ...: any)
39 41
@@ -94,9 +96,13 @@ local record argparse
94 96
95 option: function(self: Command, name: string, description: string): Option 97 option: function(self: Command, name: string, description: string): Option
96 98
97 flag: function(self: Command, string, string): Option 99 flag: function(self: Command, string, ?string): Option
98 100
99 handle_options: function(self: Command, boolean): Command 101 handle_options: function(self: Command, boolean): Command
102
103 mutex: function(self: Command, ...: any) --! copied over from Parser
104
105 group: function(self: Command, ...:any): Command --! copied over from Parser
100 end 106 end
101 107
102 metamethod __call: function(self: argparse, name: string, description: string, epilog: string): Parser 108 metamethod __call: function(self: argparse, name: string, description: string, epilog: string): Parser
diff --git a/src/luarocks/vendor/dkjson.d.tl b/src/luarocks/vendor/dkjson.d.tl
index 4dee43d3..a7c76389 100644
--- a/src/luarocks/vendor/dkjson.d.tl
+++ b/src/luarocks/vendor/dkjson.d.tl
@@ -13,7 +13,7 @@ local record dkjson
13 tables: {table:boolean} 13 tables: {table:boolean}
14 exception: function(string, string, string, string): boolean|string, string 14 exception: function(string, string, string, string): boolean|string, string
15 end 15 end
16 encode: function({string:any}, JsonState): string 16 encode: function({string:any}, ?JsonState): string
17 17
18 decode: function(string, ?number, ?any, ?table): {string:any} 18 decode: function(string, ?number, ?any, ?table): {string:any}
19 19