diff options
author | daurnimator <quae@daurnimator.com> | 2019-08-24 12:15:34 +1000 |
---|---|---|
committer | daurnimator <quae@daurnimator.com> | 2019-08-24 12:15:34 +1000 |
commit | cffcc251be58914c49a1acbdaf5c38e42f93976c (patch) | |
tree | dffd799f90d06016e1b43cbd6f96c385e1ef0a64 | |
parent | 8a957d68133a8af6650c4f8349646fa9420258f3 (diff) | |
parent | 8e602d751e79c97eb664682b5b2441495d9ad974 (diff) | |
download | luarocks-cffcc251be58914c49a1acbdaf5c38e42f93976c.tar.gz luarocks-cffcc251be58914c49a1acbdaf5c38e42f93976c.tar.bz2 luarocks-cffcc251be58914c49a1acbdaf5c38e42f93976c.zip |
Merge PR #1035
37 files changed, 3092 insertions, 1226 deletions
@@ -1,6 +1,7 @@ | |||
1 | /config.* | 1 | /config.* |
2 | /src/luarocks/core/hardcoded.lua | 2 | /src/luarocks/core/hardcoded.lua |
3 | /testrun/* | 3 | /testrun/* |
4 | /lua_install | ||
4 | # Stuff that pops up during development but shouldn't be in the repo (helps clean up `git status`) | 5 | # Stuff that pops up during development but shouldn't be in the repo (helps clean up `git status`) |
5 | /build | 6 | /build |
6 | /build-binary | 7 | /build-binary |
@@ -15,4 +16,5 @@ | |||
15 | /lua | 16 | /lua |
16 | /lua_modules | 17 | /lua_modules |
17 | /luarocks | 18 | /luarocks |
19 | /luarocks-admin | ||
18 | /.luarocks | 20 | /.luarocks |
diff --git a/spec/which_spec.lua b/spec/which_spec.lua index d5bcfab9..79b9ef7e 100644 --- a/spec/which_spec.lua +++ b/spec/which_spec.lua | |||
@@ -22,7 +22,7 @@ describe("LuaRocks which tests #integration", function() | |||
22 | 22 | ||
23 | it("fails on missing arguments", function() | 23 | it("fails on missing arguments", function() |
24 | local output = run.luarocks("which") | 24 | local output = run.luarocks("which") |
25 | assert.match("Missing module name", output, 1, true) | 25 | assert.match("missing argument 'modname'", output, 1, true) |
26 | end) | 26 | end) |
27 | 27 | ||
28 | it("finds modules found in package.path", function() | 28 | it("finds modules found in package.path", function() |
diff --git a/src/bin/luarocks b/src/bin/luarocks index d982530c..56caaa60 100755 --- a/src/bin/luarocks +++ b/src/bin/luarocks | |||
@@ -9,7 +9,6 @@ local cmd = require("luarocks.cmd") | |||
9 | local description = "LuaRocks main command-line interface" | 9 | local description = "LuaRocks main command-line interface" |
10 | 10 | ||
11 | local commands = { | 11 | local commands = { |
12 | help = "luarocks.cmd.help", | ||
13 | init = "luarocks.cmd.init", | 12 | init = "luarocks.cmd.init", |
14 | pack = "luarocks.cmd.pack", | 13 | pack = "luarocks.cmd.pack", |
15 | unpack = "luarocks.cmd.unpack", | 14 | unpack = "luarocks.cmd.unpack", |
diff --git a/src/bin/luarocks-admin b/src/bin/luarocks-admin index 71e17b55..4a85e45b 100755 --- a/src/bin/luarocks-admin +++ b/src/bin/luarocks-admin | |||
@@ -9,7 +9,6 @@ local cmd = require("luarocks.cmd") | |||
9 | local description = "LuaRocks repository administration interface" | 9 | local description = "LuaRocks repository administration interface" |
10 | 10 | ||
11 | local commands = { | 11 | local commands = { |
12 | help = "luarocks.cmd.help", | ||
13 | make_manifest = "luarocks.admin.cmd.make_manifest", | 12 | make_manifest = "luarocks.admin.cmd.make_manifest", |
14 | add = "luarocks.admin.cmd.add", | 13 | add = "luarocks.admin.cmd.add", |
15 | remove = "luarocks.admin.cmd.remove", | 14 | remove = "luarocks.admin.cmd.remove", |
diff --git a/src/luarocks/admin/cmd/add.lua b/src/luarocks/admin/cmd/add.lua index 19990b3c..5011c680 100644 --- a/src/luarocks/admin/cmd/add.lua +++ b/src/luarocks/admin/cmd/add.lua | |||
@@ -11,20 +11,20 @@ local fs = require("luarocks.fs") | |||
11 | local cache = require("luarocks.admin.cache") | 11 | local cache = require("luarocks.admin.cache") |
12 | local index = require("luarocks.admin.index") | 12 | local index = require("luarocks.admin.index") |
13 | 13 | ||
14 | add.help_summary = "Add a rock or rockspec to a rocks server." | 14 | function add.add_to_parser(parser) |
15 | add.help_arguments = "[--server=<server>] [--no-refresh] {<rockspec>|<rock>...}" | 15 | local cmd = parser:command("add", "Add a rock or rockspec to a rocks server.", util.see_also()) |
16 | add.help = [[ | 16 | |
17 | Arguments are local files, which may be rockspecs or rocks. | 17 | cmd:argument("rock", "A local rockspec or rock file.") |
18 | The flag --server indicates which server to use. | 18 | :args("+") |
19 | If not given, the default server set in the upload_server variable | 19 | |
20 | from the configuration file is used instead. | 20 | cmd:option("--server", "The server to use. If not given, the default server ".. |
21 | 21 | "set in the upload_server variable from the configuration file is used instead.") | |
22 | --no-refresh The local cache should not be refreshed | 22 | :target("add_server") |
23 | prior to generation of the updated manifest. | 23 | cmd:flag("--no-refresh", "Do not refresh the local cache prior to ".. |
24 | --index Produce an index.html file for the manifest. | 24 | "generation of the updated manifest.") |
25 | This flag is automatically set if an index.html | 25 | cmd:flag("--index", "Produce an index.html file for the manifest. This ".. |
26 | file already exists. | 26 | "flag is automatically set if an index.html file already exists.") |
27 | ]] | 27 | end |
28 | 28 | ||
29 | local function zip_manifests() | 29 | local function zip_manifests() |
30 | for ver in util.lua_versions() do | 30 | for ver in util.lua_versions() do |
@@ -124,14 +124,10 @@ local function add_files_to_server(refresh, rockfiles, server, upload_server, do | |||
124 | return fs.execute(cmd) | 124 | return fs.execute(cmd) |
125 | end | 125 | end |
126 | 126 | ||
127 | function add.command(flags, ...) | 127 | function add.command(args) |
128 | local files = {...} | 128 | local server, server_table = cache.get_upload_server(args.add_server or args.server) |
129 | if #files < 1 then | ||
130 | return nil, "Argument missing. "..util.see_help("add", "luarocks-admin") | ||
131 | end | ||
132 | local server, server_table = cache.get_upload_server(flags["server"]) | ||
133 | if not server then return nil, server_table end | 129 | if not server then return nil, server_table end |
134 | return add_files_to_server(not flags["no-refresh"], files, server, server_table, flags["index"]) | 130 | return add_files_to_server(not args.no_refresh, args.rock, server, server_table, args.index) |
135 | end | 131 | end |
136 | 132 | ||
137 | 133 | ||
diff --git a/src/luarocks/admin/cmd/make_manifest.lua b/src/luarocks/admin/cmd/make_manifest.lua index f0b64135..ab940f9e 100644 --- a/src/luarocks/admin/cmd/make_manifest.lua +++ b/src/luarocks/admin/cmd/make_manifest.lua | |||
@@ -11,36 +11,36 @@ local deps = require("luarocks.deps") | |||
11 | local fs = require("luarocks.fs") | 11 | local fs = require("luarocks.fs") |
12 | local dir = require("luarocks.dir") | 12 | local dir = require("luarocks.dir") |
13 | 13 | ||
14 | make_manifest.help_summary = "Compile a manifest file for a repository." | 14 | function make_manifest.add_to_parser(parser) |
15 | local cmd = parser:command("make_manifest", "Compile a manifest file for a repository.", util.see_also()) | ||
16 | parser:command("make-manifest"):hidden(true):action(function(args) args.command = "make_manifest" end) | ||
15 | 17 | ||
16 | make_manifest.help = [[ | 18 | cmd:argument("repository", "Local repository pathname.") |
17 | <argument>, if given, is a local repository pathname. | 19 | :args("?") |
18 | 20 | ||
19 | --local-tree If given, do not write versioned versions of the manifest file. | 21 | cmd:flag("--local-tree", "If given, do not write versioned versions of the manifest file.\n".. |
20 | Use this when rebuilding the manifest of a local rocks tree. | 22 | "Use this when rebuilding the manifest of a local rocks tree.") |
21 | ]] | 23 | util.deps_mode_option(cmd) |
24 | end | ||
22 | 25 | ||
23 | --- Driver function for "make_manifest" command. | 26 | --- Driver function for "make_manifest" command. |
24 | -- @param repo string or nil: Pathname of a local repository. If not given, | ||
25 | -- the default local repository configured as cfg.rocks_dir is used. | ||
26 | -- @return boolean or (nil, string): True if manifest was generated, | 27 | -- @return boolean or (nil, string): True if manifest was generated, |
27 | -- or nil and an error message. | 28 | -- or nil and an error message. |
28 | function make_manifest.command(flags, repo) | 29 | function make_manifest.command(args) |
29 | assert(type(repo) == "string" or not repo) | 30 | local repo = args.repository or cfg.rocks_dir |
30 | repo = repo or cfg.rocks_dir | ||
31 | 31 | ||
32 | util.printout("Making manifest for "..repo) | 32 | util.printout("Making manifest for "..repo) |
33 | 33 | ||
34 | if repo:match("/lib/luarocks") and not flags["local-tree"] then | 34 | if repo:match("/lib/luarocks") and not args.local_tree then |
35 | util.warning("This looks like a local rocks tree, but you did not pass --local-tree.") | 35 | util.warning("This looks like a local rocks tree, but you did not pass --local-tree.") |
36 | end | 36 | end |
37 | 37 | ||
38 | local ok, err = writer.make_manifest(repo, deps.get_deps_mode(flags), not flags["local-tree"]) | 38 | local ok, err = writer.make_manifest(repo, deps.get_deps_mode(args), not args.local_tree) |
39 | if ok and not flags["local-tree"] then | 39 | if ok and not args.local_tree then |
40 | util.printout("Generating index.html for "..repo) | 40 | util.printout("Generating index.html for "..repo) |
41 | index.make_index(repo) | 41 | index.make_index(repo) |
42 | end | 42 | end |
43 | if flags["local-tree"] then | 43 | if args.local_tree then |
44 | for luaver in util.lua_versions() do | 44 | for luaver in util.lua_versions() do |
45 | fs.delete(dir.path(repo, "manifest-"..luaver)) | 45 | fs.delete(dir.path(repo, "manifest-"..luaver)) |
46 | end | 46 | end |
diff --git a/src/luarocks/admin/cmd/refresh_cache.lua b/src/luarocks/admin/cmd/refresh_cache.lua index 3ffe56d9..40c9ff09 100644 --- a/src/luarocks/admin/cmd/refresh_cache.lua +++ b/src/luarocks/admin/cmd/refresh_cache.lua | |||
@@ -3,18 +3,20 @@ | |||
3 | local refresh_cache = {} | 3 | local refresh_cache = {} |
4 | 4 | ||
5 | local cfg = require("luarocks.core.cfg") | 5 | local cfg = require("luarocks.core.cfg") |
6 | local util = require("luarocks.util") | ||
6 | local cache = require("luarocks.admin.cache") | 7 | local cache = require("luarocks.admin.cache") |
7 | 8 | ||
8 | refresh_cache.help_summary = "Refresh local cache of a remote rocks server." | 9 | function refresh_cache.add_to_parser(parser) |
9 | refresh_cache.help_arguments = "[--from=<server>]" | 10 | local cmd = parser:command("refresh_cache", "Refresh local cache of a remote rocks server.", util.see_also()) |
10 | refresh_cache.help = [[ | 11 | parser:command("refresh-cache"):hidden(true):action(function(args) args.command = "refresh_cache" end) |
11 | The flag --from indicates which server to use. | ||
12 | If not given, the default server set in the upload_server variable | ||
13 | from the configuration file is used instead. | ||
14 | ]] | ||
15 | 12 | ||
16 | function refresh_cache.command(flags) | 13 | cmd:option("--from", "The server to use. If not given, the default server ".. |
17 | local server, upload_server = cache.get_upload_server(flags["server"]) | 14 | "set in the upload_server variable from the configuration file is used instead.") |
15 | :argname("<server>") | ||
16 | end | ||
17 | |||
18 | function refresh_cache.command(args) | ||
19 | local server, upload_server = cache.get_upload_server(args.server) | ||
18 | if not server then return nil, upload_server end | 20 | if not server then return nil, upload_server end |
19 | local download_url = cache.get_server_urls(server, upload_server) | 21 | local download_url = cache.get_server_urls(server, upload_server) |
20 | 22 | ||
diff --git a/src/luarocks/admin/cmd/remove.lua b/src/luarocks/admin/cmd/remove.lua index f005c83c..de58f7a3 100644 --- a/src/luarocks/admin/cmd/remove.lua +++ b/src/luarocks/admin/cmd/remove.lua | |||
@@ -11,16 +11,17 @@ local fs = require("luarocks.fs") | |||
11 | local cache = require("luarocks.admin.cache") | 11 | local cache = require("luarocks.admin.cache") |
12 | local index = require("luarocks.admin.index") | 12 | local index = require("luarocks.admin.index") |
13 | 13 | ||
14 | admin_remove.help_summary = "Remove a rock or rockspec from a rocks server." | 14 | function admin_remove.add_to_parser(parser) |
15 | admin_remove.help_arguments = "[--server=<server>] [--no-refresh] {<rockspec>|<rock>...}" | 15 | local cmd = parser:command("remove", "Remove a rock or rockspec from a rocks server.", util.see_also()) |
16 | admin_remove.help = [[ | 16 | |
17 | Arguments are local files, which may be rockspecs or rocks. | 17 | cmd:argument("rock", "A local rockspec or rock file.") |
18 | The flag --server indicates which server to use. | 18 | :args("+") |
19 | If not given, the default server set in the upload_server variable | 19 | |
20 | from the configuration file is used instead. | 20 | cmd:option("--server", "The server to use. If not given, the default server ".. |
21 | The flag --no-refresh indicates the local cache should not be refreshed | 21 | "set in the upload_server variable from the configuration file is used instead.") |
22 | prior to generation of the updated manifest. | 22 | cmd:flag("--no-refresh", "Do not refresh the local cache prior to ".. |
23 | ]] | 23 | "generation of the updated manifest.") |
24 | end | ||
24 | 25 | ||
25 | local function remove_files_from_server(refresh, rockfiles, server, upload_server) | 26 | local function remove_files_from_server(refresh, rockfiles, server, upload_server) |
26 | assert(type(refresh) == "boolean" or not refresh) | 27 | assert(type(refresh) == "boolean" or not refresh) |
@@ -76,14 +77,10 @@ local function remove_files_from_server(refresh, rockfiles, server, upload_serve | |||
76 | return true | 77 | return true |
77 | end | 78 | end |
78 | 79 | ||
79 | function admin_remove.command(flags, ...) | 80 | function admin_remove.command(args) |
80 | local files = {...} | 81 | local server, server_table = cache.get_upload_server(args.server) |
81 | if #files < 1 then | ||
82 | return nil, "Argument missing. "..util.see_help("remove", "luarocks-admin") | ||
83 | end | ||
84 | local server, server_table = cache.get_upload_server(flags["server"]) | ||
85 | if not server then return nil, server_table end | 82 | if not server then return nil, server_table end |
86 | return remove_files_from_server(not flags["no-refresh"], files, server, server_table) | 83 | return remove_files_from_server(not args.no_refresh, args.rock, server, server_table) |
87 | end | 84 | end |
88 | 85 | ||
89 | 86 | ||
diff --git a/src/luarocks/argparse.lua b/src/luarocks/argparse.lua new file mode 100644 index 00000000..dc6cdb0d --- /dev/null +++ b/src/luarocks/argparse.lua | |||
@@ -0,0 +1,2078 @@ | |||
1 | -- The MIT License (MIT) | ||
2 | |||
3 | -- Copyright (c) 2013 - 2018 Peter Melnichenko | ||
4 | -- 2019 Paul Ouellette | ||
5 | |||
6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
7 | -- this software and associated documentation files (the "Software"), to deal in | ||
8 | -- the Software without restriction, including without limitation the rights to | ||
9 | -- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
10 | -- the Software, and to permit persons to whom the Software is furnished to do so, | ||
11 | -- subject to the following conditions: | ||
12 | |||
13 | -- The above copyright notice and this permission notice shall be included in all | ||
14 | -- copies or substantial portions of the Software. | ||
15 | |||
16 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
18 | -- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
19 | -- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
20 | -- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
21 | -- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
22 | |||
23 | local function deep_update(t1, t2) | ||
24 | for k, v in pairs(t2) do | ||
25 | if type(v) == "table" then | ||
26 | v = deep_update({}, v) | ||
27 | end | ||
28 | |||
29 | t1[k] = v | ||
30 | end | ||
31 | |||
32 | return t1 | ||
33 | end | ||
34 | |||
35 | -- A property is a tuple {name, callback}. | ||
36 | -- properties.args is number of properties that can be set as arguments | ||
37 | -- when calling an object. | ||
38 | local function class(prototype, properties, parent) | ||
39 | -- Class is the metatable of its instances. | ||
40 | local cl = {} | ||
41 | cl.__index = cl | ||
42 | |||
43 | if parent then | ||
44 | cl.__prototype = deep_update(deep_update({}, parent.__prototype), prototype) | ||
45 | else | ||
46 | cl.__prototype = prototype | ||
47 | end | ||
48 | |||
49 | if properties then | ||
50 | local names = {} | ||
51 | |||
52 | -- Create setter methods and fill set of property names. | ||
53 | for _, property in ipairs(properties) do | ||
54 | local name, callback = property[1], property[2] | ||
55 | |||
56 | cl[name] = function(self, value) | ||
57 | if not callback(self, value) then | ||
58 | self["_" .. name] = value | ||
59 | end | ||
60 | |||
61 | return self | ||
62 | end | ||
63 | |||
64 | names[name] = true | ||
65 | end | ||
66 | |||
67 | function cl.__call(self, ...) | ||
68 | -- When calling an object, if the first argument is a table, | ||
69 | -- interpret keys as property names, else delegate arguments | ||
70 | -- to corresponding setters in order. | ||
71 | if type((...)) == "table" then | ||
72 | for name, value in pairs((...)) do | ||
73 | if names[name] then | ||
74 | self[name](self, value) | ||
75 | end | ||
76 | end | ||
77 | else | ||
78 | local nargs = select("#", ...) | ||
79 | |||
80 | for i, property in ipairs(properties) do | ||
81 | if i > nargs or i > properties.args then | ||
82 | break | ||
83 | end | ||
84 | |||
85 | local arg = select(i, ...) | ||
86 | |||
87 | if arg ~= nil then | ||
88 | self[property[1]](self, arg) | ||
89 | end | ||
90 | end | ||
91 | end | ||
92 | |||
93 | return self | ||
94 | end | ||
95 | end | ||
96 | |||
97 | -- If indexing class fails, fallback to its parent. | ||
98 | local class_metatable = {} | ||
99 | class_metatable.__index = parent | ||
100 | |||
101 | function class_metatable.__call(self, ...) | ||
102 | -- Calling a class returns its instance. | ||
103 | -- Arguments are delegated to the instance. | ||
104 | local object = deep_update({}, self.__prototype) | ||
105 | setmetatable(object, self) | ||
106 | return object(...) | ||
107 | end | ||
108 | |||
109 | return setmetatable(cl, class_metatable) | ||
110 | end | ||
111 | |||
112 | local function typecheck(name, types, value) | ||
113 | for _, type_ in ipairs(types) do | ||
114 | if type(value) == type_ then | ||
115 | return true | ||
116 | end | ||
117 | end | ||
118 | |||
119 | error(("bad property '%s' (%s expected, got %s)"):format(name, table.concat(types, " or "), type(value))) | ||
120 | end | ||
121 | |||
122 | local function typechecked(name, ...) | ||
123 | local types = {...} | ||
124 | return {name, function(_, value) typecheck(name, types, value) end} | ||
125 | end | ||
126 | |||
127 | local multiname = {"name", function(self, value) | ||
128 | typecheck("name", {"string"}, value) | ||
129 | |||
130 | for alias in value:gmatch("%S+") do | ||
131 | self._name = self._name or alias | ||
132 | table.insert(self._aliases, alias) | ||
133 | end | ||
134 | |||
135 | -- Do not set _name as with other properties. | ||
136 | return true | ||
137 | end} | ||
138 | |||
139 | local function parse_boundaries(str) | ||
140 | if tonumber(str) then | ||
141 | return tonumber(str), tonumber(str) | ||
142 | end | ||
143 | |||
144 | if str == "*" then | ||
145 | return 0, math.huge | ||
146 | end | ||
147 | |||
148 | if str == "+" then | ||
149 | return 1, math.huge | ||
150 | end | ||
151 | |||
152 | if str == "?" then | ||
153 | return 0, 1 | ||
154 | end | ||
155 | |||
156 | if str:match "^%d+%-%d+$" then | ||
157 | local min, max = str:match "^(%d+)%-(%d+)$" | ||
158 | return tonumber(min), tonumber(max) | ||
159 | end | ||
160 | |||
161 | if str:match "^%d+%+$" then | ||
162 | local min = str:match "^(%d+)%+$" | ||
163 | return tonumber(min), math.huge | ||
164 | end | ||
165 | end | ||
166 | |||
167 | local function boundaries(name) | ||
168 | return {name, function(self, value) | ||
169 | typecheck(name, {"number", "string"}, value) | ||
170 | |||
171 | local min, max = parse_boundaries(value) | ||
172 | |||
173 | if not min then | ||
174 | error(("bad property '%s'"):format(name)) | ||
175 | end | ||
176 | |||
177 | self["_min" .. name], self["_max" .. name] = min, max | ||
178 | end} | ||
179 | end | ||
180 | |||
181 | local actions = {} | ||
182 | |||
183 | local option_action = {"action", function(_, value) | ||
184 | typecheck("action", {"function", "string"}, value) | ||
185 | |||
186 | if type(value) == "string" and not actions[value] then | ||
187 | error(("unknown action '%s'"):format(value)) | ||
188 | end | ||
189 | end} | ||
190 | |||
191 | local option_init = {"init", function(self) | ||
192 | self._has_init = true | ||
193 | end} | ||
194 | |||
195 | local option_default = {"default", function(self, value) | ||
196 | if type(value) ~= "string" then | ||
197 | self._init = value | ||
198 | self._has_init = true | ||
199 | return true | ||
200 | end | ||
201 | end} | ||
202 | |||
203 | local add_help = {"add_help", function(self, value) | ||
204 | typecheck("add_help", {"boolean", "string", "table"}, value) | ||
205 | |||
206 | if self._help_option_idx then | ||
207 | table.remove(self._options, self._help_option_idx) | ||
208 | self._help_option_idx = nil | ||
209 | end | ||
210 | |||
211 | if value then | ||
212 | local help = self:flag() | ||
213 | :description "Show this help message and exit." | ||
214 | :action(function() | ||
215 | print(self:get_help()) | ||
216 | os.exit(0) | ||
217 | end) | ||
218 | |||
219 | if value ~= true then | ||
220 | help = help(value) | ||
221 | end | ||
222 | |||
223 | if not help._name then | ||
224 | help "-h" "--help" | ||
225 | end | ||
226 | |||
227 | self._help_option_idx = #self._options | ||
228 | end | ||
229 | end} | ||
230 | |||
231 | local Parser = class({ | ||
232 | _arguments = {}, | ||
233 | _options = {}, | ||
234 | _commands = {}, | ||
235 | _mutexes = {}, | ||
236 | _groups = {}, | ||
237 | _require_command = true, | ||
238 | _handle_options = true | ||
239 | }, { | ||
240 | args = 3, | ||
241 | typechecked("name", "string"), | ||
242 | typechecked("description", "string"), | ||
243 | typechecked("epilog", "string"), | ||
244 | typechecked("usage", "string"), | ||
245 | typechecked("help", "string"), | ||
246 | typechecked("require_command", "boolean"), | ||
247 | typechecked("handle_options", "boolean"), | ||
248 | typechecked("action", "function"), | ||
249 | typechecked("command_target", "string"), | ||
250 | typechecked("help_vertical_space", "number"), | ||
251 | typechecked("usage_margin", "number"), | ||
252 | typechecked("usage_max_width", "number"), | ||
253 | typechecked("help_usage_margin", "number"), | ||
254 | typechecked("help_description_margin", "number"), | ||
255 | typechecked("help_max_width", "number"), | ||
256 | add_help | ||
257 | }) | ||
258 | |||
259 | local Command = class({ | ||
260 | _aliases = {} | ||
261 | }, { | ||
262 | args = 3, | ||
263 | multiname, | ||
264 | typechecked("description", "string"), | ||
265 | typechecked("epilog", "string"), | ||
266 | typechecked("summary", "string"), | ||
267 | typechecked("target", "string"), | ||
268 | typechecked("usage", "string"), | ||
269 | typechecked("help", "string"), | ||
270 | typechecked("require_command", "boolean"), | ||
271 | typechecked("handle_options", "boolean"), | ||
272 | typechecked("action", "function"), | ||
273 | typechecked("command_target", "string"), | ||
274 | typechecked("help_vertical_space", "number"), | ||
275 | typechecked("usage_margin", "number"), | ||
276 | typechecked("usage_max_width", "number"), | ||
277 | typechecked("help_usage_margin", "number"), | ||
278 | typechecked("help_description_margin", "number"), | ||
279 | typechecked("help_max_width", "number"), | ||
280 | typechecked("hidden", "boolean"), | ||
281 | add_help | ||
282 | }, Parser) | ||
283 | |||
284 | local Argument = class({ | ||
285 | _minargs = 1, | ||
286 | _maxargs = 1, | ||
287 | _mincount = 1, | ||
288 | _maxcount = 1, | ||
289 | _defmode = "unused", | ||
290 | _show_default = true | ||
291 | }, { | ||
292 | args = 5, | ||
293 | typechecked("name", "string"), | ||
294 | typechecked("description", "string"), | ||
295 | option_default, | ||
296 | typechecked("convert", "function", "table"), | ||
297 | boundaries("args"), | ||
298 | typechecked("target", "string"), | ||
299 | typechecked("defmode", "string"), | ||
300 | typechecked("show_default", "boolean"), | ||
301 | typechecked("argname", "string", "table"), | ||
302 | typechecked("choices", "table"), | ||
303 | typechecked("hidden", "boolean"), | ||
304 | option_action, | ||
305 | option_init | ||
306 | }) | ||
307 | |||
308 | local Option = class({ | ||
309 | _aliases = {}, | ||
310 | _mincount = 0, | ||
311 | _overwrite = true | ||
312 | }, { | ||
313 | args = 6, | ||
314 | multiname, | ||
315 | typechecked("description", "string"), | ||
316 | option_default, | ||
317 | typechecked("convert", "function", "table"), | ||
318 | boundaries("args"), | ||
319 | boundaries("count"), | ||
320 | typechecked("target", "string"), | ||
321 | typechecked("defmode", "string"), | ||
322 | typechecked("show_default", "boolean"), | ||
323 | typechecked("overwrite", "boolean"), | ||
324 | typechecked("argname", "string", "table"), | ||
325 | typechecked("choices", "table"), | ||
326 | typechecked("hidden", "boolean"), | ||
327 | option_action, | ||
328 | option_init | ||
329 | }, Argument) | ||
330 | |||
331 | function Parser:_inherit_property(name, default) | ||
332 | local element = self | ||
333 | |||
334 | while true do | ||
335 | local value = element["_" .. name] | ||
336 | |||
337 | if value ~= nil then | ||
338 | return value | ||
339 | end | ||
340 | |||
341 | if not element._parent then | ||
342 | return default | ||
343 | end | ||
344 | |||
345 | element = element._parent | ||
346 | end | ||
347 | end | ||
348 | |||
349 | function Argument:_get_argument_list() | ||
350 | local buf = {} | ||
351 | local i = 1 | ||
352 | |||
353 | while i <= math.min(self._minargs, 3) do | ||
354 | local argname = self:_get_argname(i) | ||
355 | |||
356 | if self._default and self._defmode:find "a" then | ||
357 | argname = "[" .. argname .. "]" | ||
358 | end | ||
359 | |||
360 | table.insert(buf, argname) | ||
361 | i = i+1 | ||
362 | end | ||
363 | |||
364 | while i <= math.min(self._maxargs, 3) do | ||
365 | table.insert(buf, "[" .. self:_get_argname(i) .. "]") | ||
366 | i = i+1 | ||
367 | |||
368 | if self._maxargs == math.huge then | ||
369 | break | ||
370 | end | ||
371 | end | ||
372 | |||
373 | if i < self._maxargs then | ||
374 | table.insert(buf, "...") | ||
375 | end | ||
376 | |||
377 | return buf | ||
378 | end | ||
379 | |||
380 | function Argument:_get_usage() | ||
381 | local usage = table.concat(self:_get_argument_list(), " ") | ||
382 | |||
383 | if self._default and self._defmode:find "u" then | ||
384 | if self._maxargs > 1 or (self._minargs == 1 and not self._defmode:find "a") then | ||
385 | usage = "[" .. usage .. "]" | ||
386 | end | ||
387 | end | ||
388 | |||
389 | return usage | ||
390 | end | ||
391 | |||
392 | function actions.store_true(result, target) | ||
393 | result[target] = true | ||
394 | end | ||
395 | |||
396 | function actions.store_false(result, target) | ||
397 | result[target] = false | ||
398 | end | ||
399 | |||
400 | function actions.store(result, target, argument) | ||
401 | result[target] = argument | ||
402 | end | ||
403 | |||
404 | function actions.count(result, target, _, overwrite) | ||
405 | if not overwrite then | ||
406 | result[target] = result[target] + 1 | ||
407 | end | ||
408 | end | ||
409 | |||
410 | function actions.append(result, target, argument, overwrite) | ||
411 | result[target] = result[target] or {} | ||
412 | table.insert(result[target], argument) | ||
413 | |||
414 | if overwrite then | ||
415 | table.remove(result[target], 1) | ||
416 | end | ||
417 | end | ||
418 | |||
419 | function actions.concat(result, target, arguments, overwrite) | ||
420 | if overwrite then | ||
421 | error("'concat' action can't handle too many invocations") | ||
422 | end | ||
423 | |||
424 | result[target] = result[target] or {} | ||
425 | |||
426 | for _, argument in ipairs(arguments) do | ||
427 | table.insert(result[target], argument) | ||
428 | end | ||
429 | end | ||
430 | |||
431 | function Argument:_get_action() | ||
432 | local action, init | ||
433 | |||
434 | if self._maxcount == 1 then | ||
435 | if self._maxargs == 0 then | ||
436 | action, init = "store_true", nil | ||
437 | else | ||
438 | action, init = "store", nil | ||
439 | end | ||
440 | else | ||
441 | if self._maxargs == 0 then | ||
442 | action, init = "count", 0 | ||
443 | else | ||
444 | action, init = "append", {} | ||
445 | end | ||
446 | end | ||
447 | |||
448 | if self._action then | ||
449 | action = self._action | ||
450 | end | ||
451 | |||
452 | if self._has_init then | ||
453 | init = self._init | ||
454 | end | ||
455 | |||
456 | if type(action) == "string" then | ||
457 | action = actions[action] | ||
458 | end | ||
459 | |||
460 | return action, init | ||
461 | end | ||
462 | |||
463 | -- Returns placeholder for `narg`-th argument. | ||
464 | function Argument:_get_argname(narg) | ||
465 | local argname = self._argname or self:_get_default_argname() | ||
466 | |||
467 | if type(argname) == "table" then | ||
468 | return argname[narg] | ||
469 | else | ||
470 | return argname | ||
471 | end | ||
472 | end | ||
473 | |||
474 | function Argument:_get_choices_list() | ||
475 | return "{" .. table.concat(self._choices, ",") .. "}" | ||
476 | end | ||
477 | |||
478 | function Argument:_get_default_argname() | ||
479 | if self._choices then | ||
480 | return self:_get_choices_list() | ||
481 | else | ||
482 | return "<" .. self._name .. ">" | ||
483 | end | ||
484 | end | ||
485 | |||
486 | function Option:_get_default_argname() | ||
487 | if self._choices then | ||
488 | return self:_get_choices_list() | ||
489 | else | ||
490 | return "<" .. self:_get_default_target() .. ">" | ||
491 | end | ||
492 | end | ||
493 | |||
494 | -- Returns labels to be shown in the help message. | ||
495 | function Argument:_get_label_lines() | ||
496 | if self._choices then | ||
497 | return {self:_get_choices_list()} | ||
498 | else | ||
499 | return {self._name} | ||
500 | end | ||
501 | end | ||
502 | |||
503 | function Option:_get_label_lines() | ||
504 | local argument_list = self:_get_argument_list() | ||
505 | |||
506 | if #argument_list == 0 then | ||
507 | -- Don't put aliases for simple flags like `-h` on different lines. | ||
508 | return {table.concat(self._aliases, ", ")} | ||
509 | end | ||
510 | |||
511 | local longest_alias_length = -1 | ||
512 | |||
513 | for _, alias in ipairs(self._aliases) do | ||
514 | longest_alias_length = math.max(longest_alias_length, #alias) | ||
515 | end | ||
516 | |||
517 | local argument_list_repr = table.concat(argument_list, " ") | ||
518 | local lines = {} | ||
519 | |||
520 | for i, alias in ipairs(self._aliases) do | ||
521 | local line = (" "):rep(longest_alias_length - #alias) .. alias .. " " .. argument_list_repr | ||
522 | |||
523 | if i ~= #self._aliases then | ||
524 | line = line .. "," | ||
525 | end | ||
526 | |||
527 | table.insert(lines, line) | ||
528 | end | ||
529 | |||
530 | return lines | ||
531 | end | ||
532 | |||
533 | function Command:_get_label_lines() | ||
534 | return {table.concat(self._aliases, ", ")} | ||
535 | end | ||
536 | |||
537 | function Argument:_get_description() | ||
538 | if self._default and self._show_default then | ||
539 | if self._description then | ||
540 | return ("%s (default: %s)"):format(self._description, self._default) | ||
541 | else | ||
542 | return ("default: %s"):format(self._default) | ||
543 | end | ||
544 | else | ||
545 | return self._description or "" | ||
546 | end | ||
547 | end | ||
548 | |||
549 | function Command:_get_description() | ||
550 | return self._summary or self._description or "" | ||
551 | end | ||
552 | |||
553 | function Option:_get_usage() | ||
554 | local usage = self:_get_argument_list() | ||
555 | table.insert(usage, 1, self._name) | ||
556 | usage = table.concat(usage, " ") | ||
557 | |||
558 | if self._mincount == 0 or self._default then | ||
559 | usage = "[" .. usage .. "]" | ||
560 | end | ||
561 | |||
562 | return usage | ||
563 | end | ||
564 | |||
565 | function Argument:_get_default_target() | ||
566 | return self._name | ||
567 | end | ||
568 | |||
569 | function Option:_get_default_target() | ||
570 | local res | ||
571 | |||
572 | for _, alias in ipairs(self._aliases) do | ||
573 | if alias:sub(1, 1) == alias:sub(2, 2) then | ||
574 | res = alias:sub(3) | ||
575 | break | ||
576 | end | ||
577 | end | ||
578 | |||
579 | res = res or self._name:sub(2) | ||
580 | return (res:gsub("-", "_")) | ||
581 | end | ||
582 | |||
583 | function Option:_is_vararg() | ||
584 | return self._maxargs ~= self._minargs | ||
585 | end | ||
586 | |||
587 | function Parser:_get_fullname(exclude_root) | ||
588 | local parent = self._parent | ||
589 | if exclude_root and not parent then | ||
590 | return "" | ||
591 | end | ||
592 | local buf = {self._name} | ||
593 | |||
594 | while parent do | ||
595 | if not exclude_root or parent._parent then | ||
596 | table.insert(buf, 1, parent._name) | ||
597 | end | ||
598 | parent = parent._parent | ||
599 | end | ||
600 | |||
601 | return table.concat(buf, " ") | ||
602 | end | ||
603 | |||
604 | function Parser:_update_charset(charset) | ||
605 | charset = charset or {} | ||
606 | |||
607 | for _, command in ipairs(self._commands) do | ||
608 | command:_update_charset(charset) | ||
609 | end | ||
610 | |||
611 | for _, option in ipairs(self._options) do | ||
612 | for _, alias in ipairs(option._aliases) do | ||
613 | charset[alias:sub(1, 1)] = true | ||
614 | end | ||
615 | end | ||
616 | |||
617 | return charset | ||
618 | end | ||
619 | |||
620 | function Parser:argument(...) | ||
621 | local argument = Argument(...) | ||
622 | table.insert(self._arguments, argument) | ||
623 | return argument | ||
624 | end | ||
625 | |||
626 | function Parser:option(...) | ||
627 | local option = Option(...) | ||
628 | table.insert(self._options, option) | ||
629 | return option | ||
630 | end | ||
631 | |||
632 | function Parser:flag(...) | ||
633 | return self:option():args(0)(...) | ||
634 | end | ||
635 | |||
636 | function Parser:command(...) | ||
637 | local command = Command():add_help(true)(...) | ||
638 | command._parent = self | ||
639 | table.insert(self._commands, command) | ||
640 | return command | ||
641 | end | ||
642 | |||
643 | function Parser:mutex(...) | ||
644 | local elements = {...} | ||
645 | |||
646 | for i, element in ipairs(elements) do | ||
647 | local mt = getmetatable(element) | ||
648 | assert(mt == Option or mt == Argument, ("bad argument #%d to 'mutex' (Option or Argument expected)"):format(i)) | ||
649 | end | ||
650 | |||
651 | table.insert(self._mutexes, elements) | ||
652 | return self | ||
653 | end | ||
654 | |||
655 | function Parser:group(name, ...) | ||
656 | assert(type(name) == "string", ("bad argument #1 to 'group' (string expected, got %s)"):format(type(name))) | ||
657 | |||
658 | local group = {name = name, ...} | ||
659 | |||
660 | for i, element in ipairs(group) do | ||
661 | local mt = getmetatable(element) | ||
662 | assert(mt == Option or mt == Argument or mt == Command, | ||
663 | ("bad argument #%d to 'group' (Option or Argument or Command expected)"):format(i + 1)) | ||
664 | end | ||
665 | |||
666 | table.insert(self._groups, group) | ||
667 | return self | ||
668 | end | ||
669 | |||
670 | local usage_welcome = "Usage: " | ||
671 | |||
672 | function Parser:get_usage() | ||
673 | if self._usage then | ||
674 | return self._usage | ||
675 | end | ||
676 | |||
677 | local usage_margin = self:_inherit_property("usage_margin", #usage_welcome) | ||
678 | local max_usage_width = self:_inherit_property("usage_max_width", 70) | ||
679 | local lines = {usage_welcome .. self:_get_fullname()} | ||
680 | |||
681 | local function add(s) | ||
682 | if #lines[#lines]+1+#s <= max_usage_width then | ||
683 | lines[#lines] = lines[#lines] .. " " .. s | ||
684 | else | ||
685 | lines[#lines+1] = (" "):rep(usage_margin) .. s | ||
686 | end | ||
687 | end | ||
688 | |||
689 | -- Normally options are before positional arguments in usage messages. | ||
690 | -- However, vararg options should be after, because they can't be reliable used | ||
691 | -- before a positional argument. | ||
692 | -- Mutexes come into play, too, and are shown as soon as possible. | ||
693 | -- Overall, output usages in the following order: | ||
694 | -- 1. Mutexes that don't have positional arguments or vararg options. | ||
695 | -- 2. Options that are not in any mutexes and are not vararg. | ||
696 | -- 3. Positional arguments - on their own or as a part of a mutex. | ||
697 | -- 4. Remaining mutexes. | ||
698 | -- 5. Remaining options. | ||
699 | |||
700 | local elements_in_mutexes = {} | ||
701 | local added_elements = {} | ||
702 | local added_mutexes = {} | ||
703 | local argument_to_mutexes = {} | ||
704 | |||
705 | local function add_mutex(mutex, main_argument) | ||
706 | if added_mutexes[mutex] then | ||
707 | return | ||
708 | end | ||
709 | |||
710 | added_mutexes[mutex] = true | ||
711 | local buf = {} | ||
712 | |||
713 | for _, element in ipairs(mutex) do | ||
714 | if not element._hidden and not added_elements[element] then | ||
715 | if getmetatable(element) == Option or element == main_argument then | ||
716 | table.insert(buf, element:_get_usage()) | ||
717 | added_elements[element] = true | ||
718 | end | ||
719 | end | ||
720 | end | ||
721 | |||
722 | if #buf == 1 then | ||
723 | add(buf[1]) | ||
724 | elseif #buf > 1 then | ||
725 | add("(" .. table.concat(buf, " | ") .. ")") | ||
726 | end | ||
727 | end | ||
728 | |||
729 | local function add_element(element) | ||
730 | if not element._hidden and not added_elements[element] then | ||
731 | add(element:_get_usage()) | ||
732 | added_elements[element] = true | ||
733 | end | ||
734 | end | ||
735 | |||
736 | for _, mutex in ipairs(self._mutexes) do | ||
737 | local is_vararg = false | ||
738 | local has_argument = false | ||
739 | |||
740 | for _, element in ipairs(mutex) do | ||
741 | if getmetatable(element) == Option then | ||
742 | if element:_is_vararg() then | ||
743 | is_vararg = true | ||
744 | end | ||
745 | else | ||
746 | has_argument = true | ||
747 | argument_to_mutexes[element] = argument_to_mutexes[element] or {} | ||
748 | table.insert(argument_to_mutexes[element], mutex) | ||
749 | end | ||
750 | |||
751 | elements_in_mutexes[element] = true | ||
752 | end | ||
753 | |||
754 | if not is_vararg and not has_argument then | ||
755 | add_mutex(mutex) | ||
756 | end | ||
757 | end | ||
758 | |||
759 | for _, option in ipairs(self._options) do | ||
760 | if not elements_in_mutexes[option] and not option:_is_vararg() then | ||
761 | add_element(option) | ||
762 | end | ||
763 | end | ||
764 | |||
765 | -- Add usages for positional arguments, together with one mutex containing them, if they are in a mutex. | ||
766 | for _, argument in ipairs(self._arguments) do | ||
767 | -- Pick a mutex as a part of which to show this argument, take the first one that's still available. | ||
768 | local mutex | ||
769 | |||
770 | if elements_in_mutexes[argument] then | ||
771 | for _, argument_mutex in ipairs(argument_to_mutexes[argument]) do | ||
772 | if not added_mutexes[argument_mutex] then | ||
773 | mutex = argument_mutex | ||
774 | end | ||
775 | end | ||
776 | end | ||
777 | |||
778 | if mutex then | ||
779 | add_mutex(mutex, argument) | ||
780 | else | ||
781 | add_element(argument) | ||
782 | end | ||
783 | end | ||
784 | |||
785 | for _, mutex in ipairs(self._mutexes) do | ||
786 | add_mutex(mutex) | ||
787 | end | ||
788 | |||
789 | for _, option in ipairs(self._options) do | ||
790 | add_element(option) | ||
791 | end | ||
792 | |||
793 | if #self._commands > 0 then | ||
794 | if self._require_command then | ||
795 | add("<command>") | ||
796 | else | ||
797 | add("[<command>]") | ||
798 | end | ||
799 | |||
800 | add("...") | ||
801 | end | ||
802 | |||
803 | return table.concat(lines, "\n") | ||
804 | end | ||
805 | |||
806 | local function split_lines(s) | ||
807 | if s == "" then | ||
808 | return {} | ||
809 | end | ||
810 | |||
811 | local lines = {} | ||
812 | |||
813 | if s:sub(-1) ~= "\n" then | ||
814 | s = s .. "\n" | ||
815 | end | ||
816 | |||
817 | for line in s:gmatch("([^\n]*)\n") do | ||
818 | table.insert(lines, line) | ||
819 | end | ||
820 | |||
821 | return lines | ||
822 | end | ||
823 | |||
824 | local function autowrap_line(line, max_length) | ||
825 | -- Algorithm for splitting lines is simple and greedy. | ||
826 | local result_lines = {} | ||
827 | |||
828 | -- Preserve original indentation of the line, put this at the beginning of each result line. | ||
829 | -- If the first word looks like a list marker ('*', '+', or '-'), add spaces so that starts | ||
830 | -- of the second and the following lines vertically align with the start of the second word. | ||
831 | local indentation = line:match("^ *") | ||
832 | |||
833 | if line:find("^ *[%*%+%-]") then | ||
834 | indentation = indentation .. " " .. line:match("^ *[%*%+%-]( *)") | ||
835 | end | ||
836 | |||
837 | -- Parts of the last line being assembled. | ||
838 | local line_parts = {} | ||
839 | |||
840 | -- Length of the current line. | ||
841 | local line_length = 0 | ||
842 | |||
843 | -- Index of the next character to consider. | ||
844 | local index = 1 | ||
845 | |||
846 | while true do | ||
847 | local word_start, word_finish, word = line:find("([^ ]+)", index) | ||
848 | |||
849 | if not word_start then | ||
850 | -- Ignore trailing spaces, if any. | ||
851 | break | ||
852 | end | ||
853 | |||
854 | local preceding_spaces = line:sub(index, word_start - 1) | ||
855 | index = word_finish + 1 | ||
856 | |||
857 | if (#line_parts == 0) or (line_length + #preceding_spaces + #word <= max_length) then | ||
858 | -- Either this is the very first word or it fits as an addition to the current line, add it. | ||
859 | table.insert(line_parts, preceding_spaces) -- For the very first word this adds the indentation. | ||
860 | table.insert(line_parts, word) | ||
861 | line_length = line_length + #preceding_spaces + #word | ||
862 | else | ||
863 | -- Does not fit, finish current line and put the word into a new one. | ||
864 | table.insert(result_lines, table.concat(line_parts)) | ||
865 | line_parts = {indentation, word} | ||
866 | line_length = #indentation + #word | ||
867 | end | ||
868 | end | ||
869 | |||
870 | if #line_parts > 0 then | ||
871 | table.insert(result_lines, table.concat(line_parts)) | ||
872 | end | ||
873 | |||
874 | if #result_lines == 0 then | ||
875 | -- Preserve empty lines. | ||
876 | result_lines[1] = "" | ||
877 | end | ||
878 | |||
879 | return result_lines | ||
880 | end | ||
881 | |||
882 | -- Automatically wraps lines within given array, | ||
883 | -- attempting to limit line length to `max_length`. | ||
884 | -- Existing line splits are preserved. | ||
885 | local function autowrap(lines, max_length) | ||
886 | local result_lines = {} | ||
887 | |||
888 | for _, line in ipairs(lines) do | ||
889 | local autowrapped_lines = autowrap_line(line, max_length) | ||
890 | |||
891 | for _, autowrapped_line in ipairs(autowrapped_lines) do | ||
892 | table.insert(result_lines, autowrapped_line) | ||
893 | end | ||
894 | end | ||
895 | |||
896 | return result_lines | ||
897 | end | ||
898 | |||
899 | function Parser:_get_element_help(element) | ||
900 | local label_lines = element:_get_label_lines() | ||
901 | local description_lines = split_lines(element:_get_description()) | ||
902 | |||
903 | local result_lines = {} | ||
904 | |||
905 | -- All label lines should have the same length (except the last one, it has no comma). | ||
906 | -- If too long, start description after all the label lines. | ||
907 | -- Otherwise, combine label and description lines. | ||
908 | |||
909 | local usage_margin_len = self:_inherit_property("help_usage_margin", 3) | ||
910 | local usage_margin = (" "):rep(usage_margin_len) | ||
911 | local description_margin_len = self:_inherit_property("help_description_margin", 25) | ||
912 | local description_margin = (" "):rep(description_margin_len) | ||
913 | |||
914 | local help_max_width = self:_inherit_property("help_max_width") | ||
915 | |||
916 | if help_max_width then | ||
917 | local description_max_width = math.max(help_max_width - description_margin_len, 10) | ||
918 | description_lines = autowrap(description_lines, description_max_width) | ||
919 | end | ||
920 | |||
921 | if #label_lines[1] >= (description_margin_len - usage_margin_len) then | ||
922 | for _, label_line in ipairs(label_lines) do | ||
923 | table.insert(result_lines, usage_margin .. label_line) | ||
924 | end | ||
925 | |||
926 | for _, description_line in ipairs(description_lines) do | ||
927 | table.insert(result_lines, description_margin .. description_line) | ||
928 | end | ||
929 | else | ||
930 | for i = 1, math.max(#label_lines, #description_lines) do | ||
931 | local label_line = label_lines[i] | ||
932 | local description_line = description_lines[i] | ||
933 | |||
934 | local line = "" | ||
935 | |||
936 | if label_line then | ||
937 | line = usage_margin .. label_line | ||
938 | end | ||
939 | |||
940 | if description_line and description_line ~= "" then | ||
941 | line = line .. (" "):rep(description_margin_len - #line) .. description_line | ||
942 | end | ||
943 | |||
944 | table.insert(result_lines, line) | ||
945 | end | ||
946 | end | ||
947 | |||
948 | return table.concat(result_lines, "\n") | ||
949 | end | ||
950 | |||
951 | local function get_group_types(group) | ||
952 | local types = {} | ||
953 | |||
954 | for _, element in ipairs(group) do | ||
955 | types[getmetatable(element)] = true | ||
956 | end | ||
957 | |||
958 | return types | ||
959 | end | ||
960 | |||
961 | function Parser:_add_group_help(blocks, added_elements, label, elements) | ||
962 | local buf = {label} | ||
963 | |||
964 | for _, element in ipairs(elements) do | ||
965 | if not element._hidden and not added_elements[element] then | ||
966 | added_elements[element] = true | ||
967 | table.insert(buf, self:_get_element_help(element)) | ||
968 | end | ||
969 | end | ||
970 | |||
971 | if #buf > 1 then | ||
972 | table.insert(blocks, table.concat(buf, ("\n"):rep(self:_inherit_property("help_vertical_space", 0) + 1))) | ||
973 | end | ||
974 | end | ||
975 | |||
976 | function Parser:get_help() | ||
977 | if self._help then | ||
978 | return self._help | ||
979 | end | ||
980 | |||
981 | local blocks = {self:get_usage()} | ||
982 | |||
983 | local help_max_width = self:_inherit_property("help_max_width") | ||
984 | |||
985 | if self._description then | ||
986 | local description = self._description | ||
987 | |||
988 | if help_max_width then | ||
989 | description = table.concat(autowrap(split_lines(description), help_max_width), "\n") | ||
990 | end | ||
991 | |||
992 | table.insert(blocks, description) | ||
993 | end | ||
994 | |||
995 | -- 1. Put groups containing arguments first, then other arguments. | ||
996 | -- 2. Put remaining groups containing options, then other options. | ||
997 | -- 3. Put remaining groups containing commands, then other commands. | ||
998 | -- Assume that an element can't be in several groups. | ||
999 | local groups_by_type = { | ||
1000 | [Argument] = {}, | ||
1001 | [Option] = {}, | ||
1002 | [Command] = {} | ||
1003 | } | ||
1004 | |||
1005 | for _, group in ipairs(self._groups) do | ||
1006 | local group_types = get_group_types(group) | ||
1007 | |||
1008 | for _, mt in ipairs({Argument, Option, Command}) do | ||
1009 | if group_types[mt] then | ||
1010 | table.insert(groups_by_type[mt], group) | ||
1011 | break | ||
1012 | end | ||
1013 | end | ||
1014 | end | ||
1015 | |||
1016 | local default_groups = { | ||
1017 | {name = "Arguments", type = Argument, elements = self._arguments}, | ||
1018 | {name = "Options", type = Option, elements = self._options}, | ||
1019 | {name = "Commands", type = Command, elements = self._commands} | ||
1020 | } | ||
1021 | |||
1022 | local added_elements = {} | ||
1023 | |||
1024 | for _, default_group in ipairs(default_groups) do | ||
1025 | local type_groups = groups_by_type[default_group.type] | ||
1026 | |||
1027 | for _, group in ipairs(type_groups) do | ||
1028 | self:_add_group_help(blocks, added_elements, group.name .. ":", group) | ||
1029 | end | ||
1030 | |||
1031 | local default_label = default_group.name .. ":" | ||
1032 | |||
1033 | if #type_groups > 0 then | ||
1034 | default_label = "Other " .. default_label:gsub("^.", string.lower) | ||
1035 | end | ||
1036 | |||
1037 | self:_add_group_help(blocks, added_elements, default_label, default_group.elements) | ||
1038 | end | ||
1039 | |||
1040 | if self._epilog then | ||
1041 | local epilog = self._epilog | ||
1042 | |||
1043 | if help_max_width then | ||
1044 | epilog = table.concat(autowrap(split_lines(epilog), help_max_width), "\n") | ||
1045 | end | ||
1046 | |||
1047 | table.insert(blocks, epilog) | ||
1048 | end | ||
1049 | |||
1050 | return table.concat(blocks, "\n\n") | ||
1051 | end | ||
1052 | |||
1053 | function Parser:add_help_command(value) | ||
1054 | if value then | ||
1055 | assert(type(value) == "string" or type(value) == "table", | ||
1056 | ("bad argument #1 to 'add_help_command' (string or table expected, got %s)"):format(type(value))) | ||
1057 | end | ||
1058 | |||
1059 | local help = self:command() | ||
1060 | :description "Show help for commands." | ||
1061 | help:argument "command" | ||
1062 | :description "The command to show help for." | ||
1063 | :args "?" | ||
1064 | :action(function(_, _, cmd) | ||
1065 | if not cmd then | ||
1066 | print(self:get_help()) | ||
1067 | os.exit(0) | ||
1068 | else | ||
1069 | for _, command in ipairs(self._commands) do | ||
1070 | for _, alias in ipairs(command._aliases) do | ||
1071 | if alias == cmd then | ||
1072 | print(command:get_help()) | ||
1073 | os.exit(0) | ||
1074 | end | ||
1075 | end | ||
1076 | end | ||
1077 | end | ||
1078 | help:error(("unknown command '%s'"):format(cmd)) | ||
1079 | end) | ||
1080 | |||
1081 | if value then | ||
1082 | help = help(value) | ||
1083 | end | ||
1084 | |||
1085 | if not help._name then | ||
1086 | help "help" | ||
1087 | end | ||
1088 | |||
1089 | help._is_help_command = true | ||
1090 | return self | ||
1091 | end | ||
1092 | |||
1093 | function Parser:_is_shell_safe() | ||
1094 | if self._basename then | ||
1095 | if self._basename:find("[^%w_%-%+%.]") then | ||
1096 | return false | ||
1097 | end | ||
1098 | else | ||
1099 | for _, alias in ipairs(self._aliases) do | ||
1100 | if alias:find("[^%w_%-%+%.]") then | ||
1101 | return false | ||
1102 | end | ||
1103 | end | ||
1104 | end | ||
1105 | for _, option in ipairs(self._options) do | ||
1106 | for _, alias in ipairs(option._aliases) do | ||
1107 | if alias:find("[^%w_%-%+%.]") then | ||
1108 | return false | ||
1109 | end | ||
1110 | end | ||
1111 | if option._choices then | ||
1112 | for _, choice in ipairs(option._choices) do | ||
1113 | if choice:find("[%s'\"]") then | ||
1114 | return false | ||
1115 | end | ||
1116 | end | ||
1117 | end | ||
1118 | end | ||
1119 | for _, argument in ipairs(self._arguments) do | ||
1120 | if argument._choices then | ||
1121 | for _, choice in ipairs(argument._choices) do | ||
1122 | if choice:find("[%s'\"]") then | ||
1123 | return false | ||
1124 | end | ||
1125 | end | ||
1126 | end | ||
1127 | end | ||
1128 | for _, command in ipairs(self._commands) do | ||
1129 | if not command:_is_shell_safe() then | ||
1130 | return false | ||
1131 | end | ||
1132 | end | ||
1133 | return true | ||
1134 | end | ||
1135 | |||
1136 | function Parser:add_complete(value) | ||
1137 | if value then | ||
1138 | assert(type(value) == "string" or type(value) == "table", | ||
1139 | ("bad argument #1 to 'add_complete' (string or table expected, got %s)"):format(type(value))) | ||
1140 | end | ||
1141 | |||
1142 | local complete = self:option() | ||
1143 | :description "Output a shell completion script for the specified shell." | ||
1144 | :args(1) | ||
1145 | :choices {"bash", "zsh", "fish"} | ||
1146 | :action(function(_, _, shell) | ||
1147 | io.write(self["get_" .. shell .. "_complete"](self)) | ||
1148 | os.exit(0) | ||
1149 | end) | ||
1150 | |||
1151 | if value then | ||
1152 | complete = complete(value) | ||
1153 | end | ||
1154 | |||
1155 | if not complete._name then | ||
1156 | complete "--completion" | ||
1157 | end | ||
1158 | |||
1159 | return self | ||
1160 | end | ||
1161 | |||
1162 | function Parser:add_complete_command(value) | ||
1163 | if value then | ||
1164 | assert(type(value) == "string" or type(value) == "table", | ||
1165 | ("bad argument #1 to 'add_complete_command' (string or table expected, got %s)"):format(type(value))) | ||
1166 | end | ||
1167 | |||
1168 | local complete = self:command() | ||
1169 | :description "Output a shell completion script." | ||
1170 | complete:argument "shell" | ||
1171 | :description "The shell to output a completion script for." | ||
1172 | :choices {"bash", "zsh", "fish"} | ||
1173 | :action(function(_, _, shell) | ||
1174 | io.write(self["get_" .. shell .. "_complete"](self)) | ||
1175 | os.exit(0) | ||
1176 | end) | ||
1177 | |||
1178 | if value then | ||
1179 | complete = complete(value) | ||
1180 | end | ||
1181 | |||
1182 | if not complete._name then | ||
1183 | complete "completion" | ||
1184 | end | ||
1185 | |||
1186 | return self | ||
1187 | end | ||
1188 | |||
1189 | local function base_name(pathname) | ||
1190 | return pathname:gsub("[/\\]*$", ""):match(".*[/\\]([^/\\]*)") or pathname | ||
1191 | end | ||
1192 | |||
1193 | local function get_short_description(element) | ||
1194 | local short = element:_get_description():match("^(.-)%.%s") | ||
1195 | return short or element:_get_description():match("^(.-)%.?$") | ||
1196 | end | ||
1197 | |||
1198 | function Parser:_get_options() | ||
1199 | local options = {} | ||
1200 | for _, option in ipairs(self._options) do | ||
1201 | for _, alias in ipairs(option._aliases) do | ||
1202 | table.insert(options, alias) | ||
1203 | end | ||
1204 | end | ||
1205 | return table.concat(options, " ") | ||
1206 | end | ||
1207 | |||
1208 | function Parser:_get_commands() | ||
1209 | local commands = {} | ||
1210 | for _, command in ipairs(self._commands) do | ||
1211 | for _, alias in ipairs(command._aliases) do | ||
1212 | table.insert(commands, alias) | ||
1213 | end | ||
1214 | end | ||
1215 | return table.concat(commands, " ") | ||
1216 | end | ||
1217 | |||
1218 | function Parser:_bash_option_args(buf, indent) | ||
1219 | local opts = {} | ||
1220 | for _, option in ipairs(self._options) do | ||
1221 | if option._choices or option._minargs > 0 then | ||
1222 | local compreply | ||
1223 | if option._choices then | ||
1224 | compreply = 'COMPREPLY=($(compgen -W "' .. table.concat(option._choices, " ") .. '" -- "$cur"))' | ||
1225 | else | ||
1226 | compreply = 'COMPREPLY=($(compgen -f -- "$cur"))' | ||
1227 | end | ||
1228 | table.insert(opts, (" "):rep(indent + 4) .. table.concat(option._aliases, "|") .. ")") | ||
1229 | table.insert(opts, (" "):rep(indent + 8) .. compreply) | ||
1230 | table.insert(opts, (" "):rep(indent + 8) .. "return 0") | ||
1231 | table.insert(opts, (" "):rep(indent + 8) .. ";;") | ||
1232 | end | ||
1233 | end | ||
1234 | |||
1235 | if #opts > 0 then | ||
1236 | table.insert(buf, (" "):rep(indent) .. 'case "$prev" in') | ||
1237 | table.insert(buf, table.concat(opts, "\n")) | ||
1238 | table.insert(buf, (" "):rep(indent) .. "esac\n") | ||
1239 | end | ||
1240 | end | ||
1241 | |||
1242 | function Parser:_bash_get_cmd(buf, indent) | ||
1243 | if #self._commands == 0 then | ||
1244 | return | ||
1245 | end | ||
1246 | |||
1247 | table.insert(buf, (" "):rep(indent) .. 'args=("${args[@]:1}")') | ||
1248 | table.insert(buf, (" "):rep(indent) .. 'for arg in "${args[@]}"; do') | ||
1249 | table.insert(buf, (" "):rep(indent + 4) .. 'case "$arg" in') | ||
1250 | |||
1251 | for _, command in ipairs(self._commands) do | ||
1252 | table.insert(buf, (" "):rep(indent + 8) .. table.concat(command._aliases, "|") .. ")") | ||
1253 | if self._parent then | ||
1254 | table.insert(buf, (" "):rep(indent + 12) .. 'cmd="$cmd ' .. command._name .. '"') | ||
1255 | else | ||
1256 | table.insert(buf, (" "):rep(indent + 12) .. 'cmd="' .. command._name .. '"') | ||
1257 | end | ||
1258 | table.insert(buf, (" "):rep(indent + 12) .. 'opts="$opts ' .. command:_get_options() .. '"') | ||
1259 | command:_bash_get_cmd(buf, indent + 12) | ||
1260 | table.insert(buf, (" "):rep(indent + 12) .. "break") | ||
1261 | table.insert(buf, (" "):rep(indent + 12) .. ";;") | ||
1262 | end | ||
1263 | |||
1264 | table.insert(buf, (" "):rep(indent + 4) .. "esac") | ||
1265 | table.insert(buf, (" "):rep(indent) .. "done") | ||
1266 | end | ||
1267 | |||
1268 | function Parser:_bash_cmd_completions(buf) | ||
1269 | local cmd_buf = {} | ||
1270 | if self._parent then | ||
1271 | self:_bash_option_args(cmd_buf, 12) | ||
1272 | end | ||
1273 | if #self._commands > 0 then | ||
1274 | table.insert(cmd_buf, (" "):rep(12) .. 'COMPREPLY=($(compgen -W "' .. self:_get_commands() .. '" -- "$cur"))') | ||
1275 | elseif self._is_help_command then | ||
1276 | table.insert(cmd_buf, (" "):rep(12) | ||
1277 | .. 'COMPREPLY=($(compgen -W "' | ||
1278 | .. self._parent:_get_commands() | ||
1279 | .. '" -- "$cur"))') | ||
1280 | end | ||
1281 | if #cmd_buf > 0 then | ||
1282 | table.insert(buf, (" "):rep(8) .. "'" .. self:_get_fullname(true) .. "')") | ||
1283 | table.insert(buf, table.concat(cmd_buf, "\n")) | ||
1284 | table.insert(buf, (" "):rep(12) .. ";;") | ||
1285 | end | ||
1286 | |||
1287 | for _, command in ipairs(self._commands) do | ||
1288 | command:_bash_cmd_completions(buf) | ||
1289 | end | ||
1290 | end | ||
1291 | |||
1292 | function Parser:get_bash_complete() | ||
1293 | self._basename = base_name(self._name) | ||
1294 | assert(self:_is_shell_safe()) | ||
1295 | local buf = {([[ | ||
1296 | _%s() { | ||
1297 | local IFS=$' \t\n' | ||
1298 | local args cur prev cmd opts arg | ||
1299 | args=("${COMP_WORDS[@]}") | ||
1300 | cur="${COMP_WORDS[COMP_CWORD]}" | ||
1301 | prev="${COMP_WORDS[COMP_CWORD-1]}" | ||
1302 | opts="%s" | ||
1303 | ]]):format(self._basename, self:_get_options())} | ||
1304 | |||
1305 | self:_bash_option_args(buf, 4) | ||
1306 | self:_bash_get_cmd(buf, 4) | ||
1307 | if #self._commands > 0 then | ||
1308 | table.insert(buf, "") | ||
1309 | table.insert(buf, (" "):rep(4) .. 'case "$cmd" in') | ||
1310 | self:_bash_cmd_completions(buf) | ||
1311 | table.insert(buf, (" "):rep(4) .. "esac\n") | ||
1312 | end | ||
1313 | |||
1314 | table.insert(buf, ([=[ | ||
1315 | if [[ "$cur" = -* ]]; then | ||
1316 | COMPREPLY=($(compgen -W "$opts" -- "$cur")) | ||
1317 | fi | ||
1318 | } | ||
1319 | |||
1320 | complete -F _%s -o bashdefault -o default %s | ||
1321 | ]=]):format(self._basename, self._basename)) | ||
1322 | |||
1323 | return table.concat(buf, "\n") | ||
1324 | end | ||
1325 | |||
1326 | function Parser:_zsh_arguments(buf, cmd_name, indent) | ||
1327 | if self._parent then | ||
1328 | table.insert(buf, (" "):rep(indent) .. "options=(") | ||
1329 | table.insert(buf, (" "):rep(indent + 2) .. "$options") | ||
1330 | else | ||
1331 | table.insert(buf, (" "):rep(indent) .. "local -a options=(") | ||
1332 | end | ||
1333 | |||
1334 | for _, option in ipairs(self._options) do | ||
1335 | local line = {} | ||
1336 | if #option._aliases > 1 then | ||
1337 | if option._maxcount > 1 then | ||
1338 | table.insert(line, '"*"') | ||
1339 | end | ||
1340 | table.insert(line, "{" .. table.concat(option._aliases, ",") .. '}"') | ||
1341 | else | ||
1342 | table.insert(line, '"') | ||
1343 | if option._maxcount > 1 then | ||
1344 | table.insert(line, "*") | ||
1345 | end | ||
1346 | table.insert(line, option._name) | ||
1347 | end | ||
1348 | if option._description then | ||
1349 | local description = get_short_description(option):gsub('["%]:`$]', "\\%0") | ||
1350 | table.insert(line, "[" .. description .. "]") | ||
1351 | end | ||
1352 | if option._maxargs == math.huge then | ||
1353 | table.insert(line, ":*") | ||
1354 | end | ||
1355 | if option._choices then | ||
1356 | table.insert(line, ": :(" .. table.concat(option._choices, " ") .. ")") | ||
1357 | elseif option._maxargs > 0 then | ||
1358 | table.insert(line, ": :_files") | ||
1359 | end | ||
1360 | table.insert(line, '"') | ||
1361 | table.insert(buf, (" "):rep(indent + 2) .. table.concat(line)) | ||
1362 | end | ||
1363 | |||
1364 | table.insert(buf, (" "):rep(indent) .. ")") | ||
1365 | table.insert(buf, (" "):rep(indent) .. "_arguments -s -S \\") | ||
1366 | table.insert(buf, (" "):rep(indent + 2) .. "$options \\") | ||
1367 | |||
1368 | if self._is_help_command then | ||
1369 | table.insert(buf, (" "):rep(indent + 2) .. '": :(' .. self._parent:_get_commands() .. ')" \\') | ||
1370 | else | ||
1371 | for _, argument in ipairs(self._arguments) do | ||
1372 | local spec | ||
1373 | if argument._choices then | ||
1374 | spec = ": :(" .. table.concat(argument._choices, " ") .. ")" | ||
1375 | else | ||
1376 | spec = ": :_files" | ||
1377 | end | ||
1378 | if argument._maxargs == math.huge then | ||
1379 | table.insert(buf, (" "):rep(indent + 2) .. '"*' .. spec .. '" \\') | ||
1380 | break | ||
1381 | end | ||
1382 | for _ = 1, argument._maxargs do | ||
1383 | table.insert(buf, (" "):rep(indent + 2) .. '"' .. spec .. '" \\') | ||
1384 | end | ||
1385 | end | ||
1386 | |||
1387 | if #self._commands > 0 then | ||
1388 | table.insert(buf, (" "):rep(indent + 2) .. '": :_' .. cmd_name .. '_cmds" \\') | ||
1389 | table.insert(buf, (" "):rep(indent + 2) .. '"*:: :->args" \\') | ||
1390 | end | ||
1391 | end | ||
1392 | |||
1393 | table.insert(buf, (" "):rep(indent + 2) .. "&& return 0") | ||
1394 | end | ||
1395 | |||
1396 | function Parser:_zsh_cmds(buf, cmd_name) | ||
1397 | table.insert(buf, "\n_" .. cmd_name .. "_cmds() {") | ||
1398 | table.insert(buf, " local -a commands=(") | ||
1399 | |||
1400 | for _, command in ipairs(self._commands) do | ||
1401 | local line = {} | ||
1402 | if #command._aliases > 1 then | ||
1403 | table.insert(line, "{" .. table.concat(command._aliases, ",") .. '}"') | ||
1404 | else | ||
1405 | table.insert(line, '"' .. command._name) | ||
1406 | end | ||
1407 | if command._description then | ||
1408 | table.insert(line, ":" .. get_short_description(command):gsub('["`$]', "\\%0")) | ||
1409 | end | ||
1410 | table.insert(buf, " " .. table.concat(line) .. '"') | ||
1411 | end | ||
1412 | |||
1413 | table.insert(buf, ' )\n _describe "command" commands\n}') | ||
1414 | end | ||
1415 | |||
1416 | function Parser:_zsh_complete_help(buf, cmds_buf, cmd_name, indent) | ||
1417 | if #self._commands == 0 then | ||
1418 | return | ||
1419 | end | ||
1420 | |||
1421 | self:_zsh_cmds(cmds_buf, cmd_name) | ||
1422 | table.insert(buf, "\n" .. (" "):rep(indent) .. "case $words[1] in") | ||
1423 | |||
1424 | for _, command in ipairs(self._commands) do | ||
1425 | local name = cmd_name .. "_" .. command._name | ||
1426 | table.insert(buf, (" "):rep(indent + 2) .. table.concat(command._aliases, "|") .. ")") | ||
1427 | command:_zsh_arguments(buf, name, indent + 4) | ||
1428 | command:_zsh_complete_help(buf, cmds_buf, name, indent + 4) | ||
1429 | table.insert(buf, (" "):rep(indent + 4) .. ";;\n") | ||
1430 | end | ||
1431 | |||
1432 | table.insert(buf, (" "):rep(indent) .. "esac") | ||
1433 | end | ||
1434 | |||
1435 | function Parser:get_zsh_complete() | ||
1436 | self._basename = base_name(self._name) | ||
1437 | assert(self:_is_shell_safe()) | ||
1438 | local buf = {("#compdef %s\n"):format(self._basename)} | ||
1439 | local cmds_buf = {} | ||
1440 | table.insert(buf, "_" .. self._basename .. "() {") | ||
1441 | if #self._commands > 0 then | ||
1442 | table.insert(buf, " local context state state_descr line") | ||
1443 | table.insert(buf, " typeset -A opt_args\n") | ||
1444 | end | ||
1445 | self:_zsh_arguments(buf, self._basename, 2) | ||
1446 | self:_zsh_complete_help(buf, cmds_buf, self._basename, 2) | ||
1447 | table.insert(buf, "\n return 1") | ||
1448 | table.insert(buf, "}") | ||
1449 | |||
1450 | local result = table.concat(buf, "\n") | ||
1451 | if #cmds_buf > 0 then | ||
1452 | result = result .. "\n" .. table.concat(cmds_buf, "\n") | ||
1453 | end | ||
1454 | return result .. "\n\n_" .. self._basename .. "\n" | ||
1455 | end | ||
1456 | |||
1457 | local function fish_escape(string) | ||
1458 | return string:gsub("[\\']", "\\%0") | ||
1459 | end | ||
1460 | |||
1461 | function Parser:_fish_get_cmd(buf, indent) | ||
1462 | if #self._commands == 0 then | ||
1463 | return | ||
1464 | end | ||
1465 | |||
1466 | table.insert(buf, (" "):rep(indent) .. "set -e cmdline[1]") | ||
1467 | table.insert(buf, (" "):rep(indent) .. "for arg in $cmdline") | ||
1468 | table.insert(buf, (" "):rep(indent + 4) .. "switch $arg") | ||
1469 | |||
1470 | for _, command in ipairs(self._commands) do | ||
1471 | table.insert(buf, (" "):rep(indent + 8) .. "case " .. table.concat(command._aliases, " ")) | ||
1472 | table.insert(buf, (" "):rep(indent + 12) .. "set cmd $cmd " .. command._name) | ||
1473 | command:_fish_get_cmd(buf, indent + 12) | ||
1474 | table.insert(buf, (" "):rep(indent + 12) .. "break") | ||
1475 | end | ||
1476 | |||
1477 | table.insert(buf, (" "):rep(indent + 4) .. "end") | ||
1478 | table.insert(buf, (" "):rep(indent) .. "end") | ||
1479 | end | ||
1480 | |||
1481 | function Parser:_fish_complete_help(buf, basename) | ||
1482 | local prefix = "complete -c " .. basename | ||
1483 | table.insert(buf, "") | ||
1484 | |||
1485 | for _, command in ipairs(self._commands) do | ||
1486 | local aliases = table.concat(command._aliases, " ") | ||
1487 | local line | ||
1488 | if self._parent then | ||
1489 | line = ("%s -n '__fish_%s_using_command %s' -xa '%s'") | ||
1490 | :format(prefix, basename, self:_get_fullname(true), aliases) | ||
1491 | else | ||
1492 | line = ("%s -n '__fish_%s_using_command' -xa '%s'"):format(prefix, basename, aliases) | ||
1493 | end | ||
1494 | if command._description then | ||
1495 | line = ("%s -d '%s'"):format(line, fish_escape(get_short_description(command))) | ||
1496 | end | ||
1497 | table.insert(buf, line) | ||
1498 | end | ||
1499 | |||
1500 | if self._is_help_command then | ||
1501 | local line = ("%s -n '__fish_%s_using_command %s' -xa '%s'") | ||
1502 | :format(prefix, basename, self:_get_fullname(true), self._parent:_get_commands()) | ||
1503 | table.insert(buf, line) | ||
1504 | end | ||
1505 | |||
1506 | for _, option in ipairs(self._options) do | ||
1507 | local parts = {prefix} | ||
1508 | |||
1509 | if self._parent then | ||
1510 | table.insert(parts, "-n '__fish_" .. basename .. "_seen_command " .. self:_get_fullname(true) .. "'") | ||
1511 | end | ||
1512 | |||
1513 | for _, alias in ipairs(option._aliases) do | ||
1514 | if alias:match("^%-.$") then | ||
1515 | table.insert(parts, "-s " .. alias:sub(2)) | ||
1516 | elseif alias:match("^%-%-.+") then | ||
1517 | table.insert(parts, "-l " .. alias:sub(3)) | ||
1518 | end | ||
1519 | end | ||
1520 | |||
1521 | if option._choices then | ||
1522 | table.insert(parts, "-xa '" .. table.concat(option._choices, " ") .. "'") | ||
1523 | elseif option._minargs > 0 then | ||
1524 | table.insert(parts, "-r") | ||
1525 | end | ||
1526 | |||
1527 | if option._description then | ||
1528 | table.insert(parts, "-d '" .. fish_escape(get_short_description(option)) .. "'") | ||
1529 | end | ||
1530 | |||
1531 | table.insert(buf, table.concat(parts, " ")) | ||
1532 | end | ||
1533 | |||
1534 | for _, command in ipairs(self._commands) do | ||
1535 | command:_fish_complete_help(buf, basename) | ||
1536 | end | ||
1537 | end | ||
1538 | |||
1539 | function Parser:get_fish_complete() | ||
1540 | self._basename = base_name(self._name) | ||
1541 | assert(self:_is_shell_safe()) | ||
1542 | local buf = {} | ||
1543 | |||
1544 | if #self._commands > 0 then | ||
1545 | table.insert(buf, ([[ | ||
1546 | function __fish_%s_print_command | ||
1547 | set -l cmdline (commandline -poc) | ||
1548 | set -l cmd]]):format(self._basename)) | ||
1549 | self:_fish_get_cmd(buf, 4) | ||
1550 | table.insert(buf, ([[ | ||
1551 | echo "$cmd" | ||
1552 | end | ||
1553 | |||
1554 | function __fish_%s_using_command | ||
1555 | test (__fish_%s_print_command) = "$argv" | ||
1556 | and return 0 | ||
1557 | or return 1 | ||
1558 | end | ||
1559 | |||
1560 | function __fish_%s_seen_command | ||
1561 | string match -q "$argv*" (__fish_%s_print_command) | ||
1562 | and return 0 | ||
1563 | or return 1 | ||
1564 | end]]):format(self._basename, self._basename, self._basename, self._basename)) | ||
1565 | end | ||
1566 | |||
1567 | self:_fish_complete_help(buf, self._basename) | ||
1568 | return table.concat(buf, "\n") .. "\n" | ||
1569 | end | ||
1570 | |||
1571 | local function get_tip(context, wrong_name) | ||
1572 | local context_pool = {} | ||
1573 | local possible_name | ||
1574 | local possible_names = {} | ||
1575 | |||
1576 | for name in pairs(context) do | ||
1577 | if type(name) == "string" then | ||
1578 | for i = 1, #name do | ||
1579 | possible_name = name:sub(1, i - 1) .. name:sub(i + 1) | ||
1580 | |||
1581 | if not context_pool[possible_name] then | ||
1582 | context_pool[possible_name] = {} | ||
1583 | end | ||
1584 | |||
1585 | table.insert(context_pool[possible_name], name) | ||
1586 | end | ||
1587 | end | ||
1588 | end | ||
1589 | |||
1590 | for i = 1, #wrong_name + 1 do | ||
1591 | possible_name = wrong_name:sub(1, i - 1) .. wrong_name:sub(i + 1) | ||
1592 | |||
1593 | if context[possible_name] then | ||
1594 | possible_names[possible_name] = true | ||
1595 | elseif context_pool[possible_name] then | ||
1596 | for _, name in ipairs(context_pool[possible_name]) do | ||
1597 | possible_names[name] = true | ||
1598 | end | ||
1599 | end | ||
1600 | end | ||
1601 | |||
1602 | local first = next(possible_names) | ||
1603 | |||
1604 | if first then | ||
1605 | if next(possible_names, first) then | ||
1606 | local possible_names_arr = {} | ||
1607 | |||
1608 | for name in pairs(possible_names) do | ||
1609 | table.insert(possible_names_arr, "'" .. name .. "'") | ||
1610 | end | ||
1611 | |||
1612 | table.sort(possible_names_arr) | ||
1613 | return "\nDid you mean one of these: " .. table.concat(possible_names_arr, " ") .. "?" | ||
1614 | else | ||
1615 | return "\nDid you mean '" .. first .. "'?" | ||
1616 | end | ||
1617 | else | ||
1618 | return "" | ||
1619 | end | ||
1620 | end | ||
1621 | |||
1622 | local ElementState = class({ | ||
1623 | invocations = 0 | ||
1624 | }) | ||
1625 | |||
1626 | function ElementState:__call(state, element) | ||
1627 | self.state = state | ||
1628 | self.result = state.result | ||
1629 | self.element = element | ||
1630 | self.target = element._target or element:_get_default_target() | ||
1631 | self.action, self.result[self.target] = element:_get_action() | ||
1632 | return self | ||
1633 | end | ||
1634 | |||
1635 | function ElementState:error(fmt, ...) | ||
1636 | self.state:error(fmt, ...) | ||
1637 | end | ||
1638 | |||
1639 | function ElementState:convert(argument, index) | ||
1640 | local converter = self.element._convert | ||
1641 | |||
1642 | if converter then | ||
1643 | local ok, err | ||
1644 | |||
1645 | if type(converter) == "function" then | ||
1646 | ok, err = converter(argument) | ||
1647 | elseif type(converter[index]) == "function" then | ||
1648 | ok, err = converter[index](argument) | ||
1649 | else | ||
1650 | ok = converter[argument] | ||
1651 | end | ||
1652 | |||
1653 | if ok == nil then | ||
1654 | self:error(err and "%s" or "malformed argument '%s'", err or argument) | ||
1655 | end | ||
1656 | |||
1657 | argument = ok | ||
1658 | end | ||
1659 | |||
1660 | return argument | ||
1661 | end | ||
1662 | |||
1663 | function ElementState:default(mode) | ||
1664 | return self.element._defmode:find(mode) and self.element._default | ||
1665 | end | ||
1666 | |||
1667 | local function bound(noun, min, max, is_max) | ||
1668 | local res = "" | ||
1669 | |||
1670 | if min ~= max then | ||
1671 | res = "at " .. (is_max and "most" or "least") .. " " | ||
1672 | end | ||
1673 | |||
1674 | local number = is_max and max or min | ||
1675 | return res .. tostring(number) .. " " .. noun .. (number == 1 and "" or "s") | ||
1676 | end | ||
1677 | |||
1678 | function ElementState:set_name(alias) | ||
1679 | self.name = ("%s '%s'"):format(alias and "option" or "argument", alias or self.element._name) | ||
1680 | end | ||
1681 | |||
1682 | function ElementState:invoke() | ||
1683 | self.open = true | ||
1684 | self.overwrite = false | ||
1685 | |||
1686 | if self.invocations >= self.element._maxcount then | ||
1687 | if self.element._overwrite then | ||
1688 | self.overwrite = true | ||
1689 | else | ||
1690 | local num_times_repr = bound("time", self.element._mincount, self.element._maxcount, true) | ||
1691 | self:error("%s must be used %s", self.name, num_times_repr) | ||
1692 | end | ||
1693 | else | ||
1694 | self.invocations = self.invocations + 1 | ||
1695 | end | ||
1696 | |||
1697 | self.args = {} | ||
1698 | |||
1699 | if self.element._maxargs <= 0 then | ||
1700 | self:close() | ||
1701 | end | ||
1702 | |||
1703 | return self.open | ||
1704 | end | ||
1705 | |||
1706 | function ElementState:check_choices(argument) | ||
1707 | if self.element._choices then | ||
1708 | for _, choice in ipairs(self.element._choices) do | ||
1709 | if argument == choice then | ||
1710 | return | ||
1711 | end | ||
1712 | end | ||
1713 | local choices_list = "'" .. table.concat(self.element._choices, "', '") .. "'" | ||
1714 | local is_option = getmetatable(self.element) == Option | ||
1715 | self:error("%s%s must be one of %s", is_option and "argument for " or "", self.name, choices_list) | ||
1716 | end | ||
1717 | end | ||
1718 | |||
1719 | function ElementState:pass(argument) | ||
1720 | self:check_choices(argument) | ||
1721 | argument = self:convert(argument, #self.args + 1) | ||
1722 | table.insert(self.args, argument) | ||
1723 | |||
1724 | if #self.args >= self.element._maxargs then | ||
1725 | self:close() | ||
1726 | end | ||
1727 | |||
1728 | return self.open | ||
1729 | end | ||
1730 | |||
1731 | function ElementState:complete_invocation() | ||
1732 | while #self.args < self.element._minargs do | ||
1733 | self:pass(self.element._default) | ||
1734 | end | ||
1735 | end | ||
1736 | |||
1737 | function ElementState:close() | ||
1738 | if self.open then | ||
1739 | self.open = false | ||
1740 | |||
1741 | if #self.args < self.element._minargs then | ||
1742 | if self:default("a") then | ||
1743 | self:complete_invocation() | ||
1744 | else | ||
1745 | if #self.args == 0 then | ||
1746 | if getmetatable(self.element) == Argument then | ||
1747 | self:error("missing %s", self.name) | ||
1748 | elseif self.element._maxargs == 1 then | ||
1749 | self:error("%s requires an argument", self.name) | ||
1750 | end | ||
1751 | end | ||
1752 | |||
1753 | self:error("%s requires %s", self.name, bound("argument", self.element._minargs, self.element._maxargs)) | ||
1754 | end | ||
1755 | end | ||
1756 | |||
1757 | local args | ||
1758 | |||
1759 | if self.element._maxargs == 0 then | ||
1760 | args = self.args[1] | ||
1761 | elseif self.element._maxargs == 1 then | ||
1762 | if self.element._minargs == 0 and self.element._mincount ~= self.element._maxcount then | ||
1763 | args = self.args | ||
1764 | else | ||
1765 | args = self.args[1] | ||
1766 | end | ||
1767 | else | ||
1768 | args = self.args | ||
1769 | end | ||
1770 | |||
1771 | self.action(self.result, self.target, args, self.overwrite) | ||
1772 | end | ||
1773 | end | ||
1774 | |||
1775 | local ParseState = class({ | ||
1776 | result = {}, | ||
1777 | options = {}, | ||
1778 | arguments = {}, | ||
1779 | argument_i = 1, | ||
1780 | element_to_mutexes = {}, | ||
1781 | mutex_to_element_state = {}, | ||
1782 | command_actions = {} | ||
1783 | }) | ||
1784 | |||
1785 | function ParseState:__call(parser, error_handler) | ||
1786 | self.parser = parser | ||
1787 | self.error_handler = error_handler | ||
1788 | self.charset = parser:_update_charset() | ||
1789 | self:switch(parser) | ||
1790 | return self | ||
1791 | end | ||
1792 | |||
1793 | function ParseState:error(fmt, ...) | ||
1794 | self.error_handler(self.parser, fmt:format(...)) | ||
1795 | end | ||
1796 | |||
1797 | function ParseState:switch(parser) | ||
1798 | self.parser = parser | ||
1799 | |||
1800 | if parser._action then | ||
1801 | table.insert(self.command_actions, {action = parser._action, name = parser._name}) | ||
1802 | end | ||
1803 | |||
1804 | for _, option in ipairs(parser._options) do | ||
1805 | option = ElementState(self, option) | ||
1806 | table.insert(self.options, option) | ||
1807 | |||
1808 | for _, alias in ipairs(option.element._aliases) do | ||
1809 | self.options[alias] = option | ||
1810 | end | ||
1811 | end | ||
1812 | |||
1813 | for _, mutex in ipairs(parser._mutexes) do | ||
1814 | for _, element in ipairs(mutex) do | ||
1815 | if not self.element_to_mutexes[element] then | ||
1816 | self.element_to_mutexes[element] = {} | ||
1817 | end | ||
1818 | |||
1819 | table.insert(self.element_to_mutexes[element], mutex) | ||
1820 | end | ||
1821 | end | ||
1822 | |||
1823 | for _, argument in ipairs(parser._arguments) do | ||
1824 | argument = ElementState(self, argument) | ||
1825 | table.insert(self.arguments, argument) | ||
1826 | argument:set_name() | ||
1827 | argument:invoke() | ||
1828 | end | ||
1829 | |||
1830 | self.handle_options = parser._handle_options | ||
1831 | self.argument = self.arguments[self.argument_i] | ||
1832 | self.commands = parser._commands | ||
1833 | |||
1834 | for _, command in ipairs(self.commands) do | ||
1835 | for _, alias in ipairs(command._aliases) do | ||
1836 | self.commands[alias] = command | ||
1837 | end | ||
1838 | end | ||
1839 | end | ||
1840 | |||
1841 | function ParseState:get_option(name) | ||
1842 | local option = self.options[name] | ||
1843 | |||
1844 | if not option then | ||
1845 | self:error("unknown option '%s'%s", name, get_tip(self.options, name)) | ||
1846 | else | ||
1847 | return option | ||
1848 | end | ||
1849 | end | ||
1850 | |||
1851 | function ParseState:get_command(name) | ||
1852 | local command = self.commands[name] | ||
1853 | |||
1854 | if not command then | ||
1855 | if #self.commands > 0 then | ||
1856 | self:error("unknown command '%s'%s", name, get_tip(self.commands, name)) | ||
1857 | else | ||
1858 | self:error("too many arguments") | ||
1859 | end | ||
1860 | else | ||
1861 | return command | ||
1862 | end | ||
1863 | end | ||
1864 | |||
1865 | function ParseState:check_mutexes(element_state) | ||
1866 | if self.element_to_mutexes[element_state.element] then | ||
1867 | for _, mutex in ipairs(self.element_to_mutexes[element_state.element]) do | ||
1868 | local used_element_state = self.mutex_to_element_state[mutex] | ||
1869 | |||
1870 | if used_element_state and used_element_state ~= element_state then | ||
1871 | self:error("%s can not be used together with %s", element_state.name, used_element_state.name) | ||
1872 | else | ||
1873 | self.mutex_to_element_state[mutex] = element_state | ||
1874 | end | ||
1875 | end | ||
1876 | end | ||
1877 | end | ||
1878 | |||
1879 | function ParseState:invoke(option, name) | ||
1880 | self:close() | ||
1881 | option:set_name(name) | ||
1882 | self:check_mutexes(option, name) | ||
1883 | |||
1884 | if option:invoke() then | ||
1885 | self.option = option | ||
1886 | end | ||
1887 | end | ||
1888 | |||
1889 | function ParseState:pass(arg) | ||
1890 | if self.option then | ||
1891 | if not self.option:pass(arg) then | ||
1892 | self.option = nil | ||
1893 | end | ||
1894 | elseif self.argument then | ||
1895 | self:check_mutexes(self.argument) | ||
1896 | |||
1897 | if not self.argument:pass(arg) then | ||
1898 | self.argument_i = self.argument_i + 1 | ||
1899 | self.argument = self.arguments[self.argument_i] | ||
1900 | end | ||
1901 | else | ||
1902 | local command = self:get_command(arg) | ||
1903 | self.result[command._target or command._name] = true | ||
1904 | |||
1905 | if self.parser._command_target then | ||
1906 | self.result[self.parser._command_target] = command._name | ||
1907 | end | ||
1908 | |||
1909 | self:switch(command) | ||
1910 | end | ||
1911 | end | ||
1912 | |||
1913 | function ParseState:close() | ||
1914 | if self.option then | ||
1915 | self.option:close() | ||
1916 | self.option = nil | ||
1917 | end | ||
1918 | end | ||
1919 | |||
1920 | function ParseState:finalize() | ||
1921 | self:close() | ||
1922 | |||
1923 | for i = self.argument_i, #self.arguments do | ||
1924 | local argument = self.arguments[i] | ||
1925 | if #argument.args == 0 and argument:default("u") then | ||
1926 | argument:complete_invocation() | ||
1927 | else | ||
1928 | argument:close() | ||
1929 | end | ||
1930 | end | ||
1931 | |||
1932 | if self.parser._require_command and #self.commands > 0 then | ||
1933 | self:error("a command is required") | ||
1934 | end | ||
1935 | |||
1936 | for _, option in ipairs(self.options) do | ||
1937 | option.name = option.name or ("option '%s'"):format(option.element._name) | ||
1938 | |||
1939 | if option.invocations == 0 then | ||
1940 | if option:default("u") then | ||
1941 | option:invoke() | ||
1942 | option:complete_invocation() | ||
1943 | option:close() | ||
1944 | end | ||
1945 | end | ||
1946 | |||
1947 | local mincount = option.element._mincount | ||
1948 | |||
1949 | if option.invocations < mincount then | ||
1950 | if option:default("a") then | ||
1951 | while option.invocations < mincount do | ||
1952 | option:invoke() | ||
1953 | option:close() | ||
1954 | end | ||
1955 | elseif option.invocations == 0 then | ||
1956 | self:error("missing %s", option.name) | ||
1957 | else | ||
1958 | self:error("%s must be used %s", option.name, bound("time", mincount, option.element._maxcount)) | ||
1959 | end | ||
1960 | end | ||
1961 | end | ||
1962 | |||
1963 | for i = #self.command_actions, 1, -1 do | ||
1964 | self.command_actions[i].action(self.result, self.command_actions[i].name) | ||
1965 | end | ||
1966 | end | ||
1967 | |||
1968 | function ParseState:parse(args) | ||
1969 | for _, arg in ipairs(args) do | ||
1970 | local plain = true | ||
1971 | |||
1972 | if self.handle_options then | ||
1973 | local first = arg:sub(1, 1) | ||
1974 | |||
1975 | if self.charset[first] then | ||
1976 | if #arg > 1 then | ||
1977 | plain = false | ||
1978 | |||
1979 | if arg:sub(2, 2) == first then | ||
1980 | if #arg == 2 then | ||
1981 | if self.options[arg] then | ||
1982 | local option = self:get_option(arg) | ||
1983 | self:invoke(option, arg) | ||
1984 | else | ||
1985 | self:close() | ||
1986 | end | ||
1987 | |||
1988 | self.handle_options = false | ||
1989 | else | ||
1990 | local equals = arg:find "=" | ||
1991 | if equals then | ||
1992 | local name = arg:sub(1, equals - 1) | ||
1993 | local option = self:get_option(name) | ||
1994 | |||
1995 | if option.element._maxargs <= 0 then | ||
1996 | self:error("option '%s' does not take arguments", name) | ||
1997 | end | ||
1998 | |||
1999 | self:invoke(option, name) | ||
2000 | self:pass(arg:sub(equals + 1)) | ||
2001 | else | ||
2002 | local option = self:get_option(arg) | ||
2003 | self:invoke(option, arg) | ||
2004 | end | ||
2005 | end | ||
2006 | else | ||
2007 | for i = 2, #arg do | ||
2008 | local name = first .. arg:sub(i, i) | ||
2009 | local option = self:get_option(name) | ||
2010 | self:invoke(option, name) | ||
2011 | |||
2012 | if i ~= #arg and option.element._maxargs > 0 then | ||
2013 | self:pass(arg:sub(i + 1)) | ||
2014 | break | ||
2015 | end | ||
2016 | end | ||
2017 | end | ||
2018 | end | ||
2019 | end | ||
2020 | end | ||
2021 | |||
2022 | if plain then | ||
2023 | self:pass(arg) | ||
2024 | end | ||
2025 | end | ||
2026 | |||
2027 | self:finalize() | ||
2028 | return self.result | ||
2029 | end | ||
2030 | |||
2031 | function Parser:error(msg) | ||
2032 | io.stderr:write(("%s\n\nError: %s\n"):format(self:get_usage(), msg)) | ||
2033 | os.exit(1) | ||
2034 | end | ||
2035 | |||
2036 | -- Compatibility with strict.lua and other checkers: | ||
2037 | local default_cmdline = rawget(_G, "arg") or {} | ||
2038 | |||
2039 | function Parser:_parse(args, error_handler) | ||
2040 | return ParseState(self, error_handler):parse(args or default_cmdline) | ||
2041 | end | ||
2042 | |||
2043 | function Parser:parse(args) | ||
2044 | return self:_parse(args, self.error) | ||
2045 | end | ||
2046 | |||
2047 | local function xpcall_error_handler(err) | ||
2048 | return tostring(err) .. "\noriginal " .. debug.traceback("", 2):sub(2) | ||
2049 | end | ||
2050 | |||
2051 | function Parser:pparse(args) | ||
2052 | local parse_error | ||
2053 | |||
2054 | local ok, result = xpcall(function() | ||
2055 | return self:_parse(args, function(_, err) | ||
2056 | parse_error = err | ||
2057 | error(err, 0) | ||
2058 | end) | ||
2059 | end, xpcall_error_handler) | ||
2060 | |||
2061 | if ok then | ||
2062 | return true, result | ||
2063 | elseif not parse_error then | ||
2064 | error(result, 0) | ||
2065 | else | ||
2066 | return false, parse_error | ||
2067 | end | ||
2068 | end | ||
2069 | |||
2070 | local argparse = {} | ||
2071 | |||
2072 | argparse.version = "0.7.0" | ||
2073 | |||
2074 | setmetatable(argparse, {__call = function(_, ...) | ||
2075 | return Parser(default_cmdline[0]):add_help(true)(...) | ||
2076 | end}) | ||
2077 | |||
2078 | return argparse | ||
diff --git a/src/luarocks/cmd.lua b/src/luarocks/cmd.lua index 1ead6c4b..f424f488 100644 --- a/src/luarocks/cmd.lua +++ b/src/luarocks/cmd.lua | |||
@@ -2,16 +2,14 @@ | |||
2 | --- Functions for command-line scripts. | 2 | --- Functions for command-line scripts. |
3 | local cmd = {} | 3 | local cmd = {} |
4 | 4 | ||
5 | local unpack = unpack or table.unpack | ||
6 | |||
7 | local loader = require("luarocks.loader") | 5 | local loader = require("luarocks.loader") |
8 | local util = require("luarocks.util") | 6 | local util = require("luarocks.util") |
9 | local path = require("luarocks.path") | 7 | local path = require("luarocks.path") |
10 | local deps = require("luarocks.deps") | ||
11 | local cfg = require("luarocks.core.cfg") | 8 | local cfg = require("luarocks.core.cfg") |
12 | local dir = require("luarocks.dir") | 9 | local dir = require("luarocks.dir") |
13 | local fun = require("luarocks.fun") | 10 | local fun = require("luarocks.fun") |
14 | local fs = require("luarocks.fs") | 11 | local fs = require("luarocks.fs") |
12 | local argparse = require("luarocks.argparse") | ||
15 | 13 | ||
16 | local hc_ok, hardcoded = pcall(require, "luarocks.core.hardcoded") | 14 | local hc_ok, hardcoded = pcall(require, "luarocks.core.hardcoded") |
17 | if not hc_ok then | 15 | if not hc_ok then |
@@ -41,11 +39,11 @@ local function check_popen() | |||
41 | end | 39 | end |
42 | end | 40 | end |
43 | 41 | ||
44 | local process_tree_flags | 42 | local process_tree_args |
45 | do | 43 | do |
46 | local function replace_tree(flags, root, tree) | 44 | local function replace_tree(args, root, tree) |
47 | root = dir.normalize(root) | 45 | root = dir.normalize(root) |
48 | flags["tree"] = root | 46 | args.tree = root |
49 | path.use_tree(tree or root) | 47 | path.use_tree(tree or root) |
50 | end | 48 | end |
51 | 49 | ||
@@ -61,44 +59,44 @@ do | |||
61 | cfg.deploy_lib_dir = cfg.deploy_lib_dir:gsub("/+$", "") | 59 | cfg.deploy_lib_dir = cfg.deploy_lib_dir:gsub("/+$", "") |
62 | end | 60 | end |
63 | 61 | ||
64 | process_tree_flags = function(flags, project_dir) | 62 | process_tree_args = function(args, project_dir) |
65 | 63 | ||
66 | if flags["global"] then | 64 | if args.global then |
67 | cfg.local_by_default = false | 65 | cfg.local_by_default = false |
68 | end | 66 | end |
69 | 67 | ||
70 | if flags["tree"] then | 68 | if args.tree then |
71 | local named = false | 69 | local named = false |
72 | for _, tree in ipairs(cfg.rocks_trees) do | 70 | for _, tree in ipairs(cfg.rocks_trees) do |
73 | if type(tree) == "table" and flags["tree"] == tree.name then | 71 | if type(tree) == "table" and args.tree == tree.name then |
74 | if not tree.root then | 72 | if not tree.root then |
75 | return nil, "Configuration error: tree '"..tree.name.."' has no 'root' field." | 73 | return nil, "Configuration error: tree '"..tree.name.."' has no 'root' field." |
76 | end | 74 | end |
77 | replace_tree(flags, tree.root, tree) | 75 | replace_tree(args, tree.root, tree) |
78 | named = true | 76 | named = true |
79 | break | 77 | break |
80 | end | 78 | end |
81 | end | 79 | end |
82 | if not named then | 80 | if not named then |
83 | local root_dir = fs.absolute_name(flags["tree"]) | 81 | local root_dir = fs.absolute_name(args.tree) |
84 | replace_tree(flags, root_dir) | 82 | replace_tree(args, root_dir) |
85 | end | 83 | end |
86 | elseif flags["local"] then | 84 | elseif args["local"] then |
87 | if not cfg.home_tree then | 85 | if not cfg.home_tree then |
88 | return nil, "The --local flag is meant for operating in a user's home directory.\n".. | 86 | return nil, "The --local flag is meant for operating in a user's home directory.\n".. |
89 | "You are running as a superuser, which is intended for system-wide operation.\n".. | 87 | "You are running as a superuser, which is intended for system-wide operation.\n".. |
90 | "To force using the superuser's home, use --tree explicitly." | 88 | "To force using the superuser's home, use --tree explicitly." |
91 | else | 89 | else |
92 | replace_tree(flags, cfg.home_tree) | 90 | replace_tree(args, cfg.home_tree) |
93 | end | 91 | end |
94 | elseif flags["project-tree"] then | 92 | elseif args.project_tree then |
95 | local tree = flags["project-tree"] | 93 | local tree = args.project_tree |
96 | table.insert(cfg.rocks_trees, 1, { name = "project", root = tree } ) | 94 | table.insert(cfg.rocks_trees, 1, { name = "project", root = tree } ) |
97 | loader.load_rocks_trees() | 95 | loader.load_rocks_trees() |
98 | path.use_tree(tree) | 96 | path.use_tree(tree) |
99 | elseif cfg.local_by_default then | 97 | elseif cfg.local_by_default then |
100 | if cfg.home_tree then | 98 | if cfg.home_tree then |
101 | replace_tree(flags, cfg.home_tree) | 99 | replace_tree(args, cfg.home_tree) |
102 | end | 100 | end |
103 | elseif project_dir then | 101 | elseif project_dir then |
104 | local project_tree = project_dir .. "/lua_modules" | 102 | local project_tree = project_dir .. "/lua_modules" |
@@ -119,26 +117,26 @@ do | |||
119 | end | 117 | end |
120 | end | 118 | end |
121 | 119 | ||
122 | local function process_server_flags(flags) | 120 | local function process_server_args(args) |
123 | if flags["server"] then | 121 | if args.server then |
124 | local protocol, pathname = dir.split_url(flags["server"]) | 122 | local protocol, pathname = dir.split_url(args.server) |
125 | table.insert(cfg.rocks_servers, 1, protocol.."://"..pathname) | 123 | table.insert(cfg.rocks_servers, 1, protocol.."://"..pathname) |
126 | end | 124 | end |
127 | 125 | ||
128 | if flags["dev"] then | 126 | if args.dev then |
129 | local append_dev = function(s) return dir.path(s, "dev") end | 127 | local append_dev = function(s) return dir.path(s, "dev") end |
130 | local dev_servers = fun.traverse(cfg.rocks_servers, append_dev) | 128 | local dev_servers = fun.traverse(cfg.rocks_servers, append_dev) |
131 | cfg.rocks_servers = fun.concat(dev_servers, cfg.rocks_servers) | 129 | cfg.rocks_servers = fun.concat(dev_servers, cfg.rocks_servers) |
132 | end | 130 | end |
133 | 131 | ||
134 | if flags["only-server"] then | 132 | if args.only_server then |
135 | if flags["dev"] then | 133 | if args.dev then |
136 | return nil, "--only-server cannot be used with --dev" | 134 | return nil, "--only-server cannot be used with --dev" |
137 | end | 135 | end |
138 | if flags["server"] then | 136 | if args.server then |
139 | return nil, "--only-server cannot be used with --server" | 137 | return nil, "--only-server cannot be used with --server" |
140 | end | 138 | end |
141 | cfg.rocks_servers = { flags["only-server"] } | 139 | cfg.rocks_servers = { args.only_server } |
142 | end | 140 | end |
143 | 141 | ||
144 | return true | 142 | return true |
@@ -174,7 +172,7 @@ end | |||
174 | 172 | ||
175 | local init_config | 173 | local init_config |
176 | do | 174 | do |
177 | local detect_config_via_flags | 175 | local detect_config_via_args |
178 | do | 176 | do |
179 | local function find_project_dir(project_tree) | 177 | local function find_project_dir(project_tree) |
180 | if project_tree then | 178 | if project_tree then |
@@ -193,7 +191,7 @@ do | |||
193 | return nil | 191 | return nil |
194 | end | 192 | end |
195 | 193 | ||
196 | local function find_default_lua_version(flags, project_dir) | 194 | local function find_default_lua_version(args, project_dir) |
197 | if hardcoded.FORCE_CONFIG then | 195 | if hardcoded.FORCE_CONFIG then |
198 | return nil | 196 | return nil |
199 | end | 197 | end |
@@ -212,7 +210,7 @@ do | |||
212 | if mod then | 210 | if mod then |
213 | local pok, ver = pcall(mod) | 211 | local pok, ver = pcall(mod) |
214 | if pok and type(ver) == "string" and ver:match("%d+.%d+") then | 212 | if pok and type(ver) == "string" and ver:match("%d+.%d+") then |
215 | if flags["verbose"] then | 213 | if args.verbose then |
216 | util.printout("Defaulting to Lua " .. ver .. " based on " .. f .. " ...") | 214 | util.printout("Defaulting to Lua " .. ver .. " based on " .. f .. " ...") |
217 | end | 215 | end |
218 | return ver | 216 | return ver |
@@ -230,13 +228,13 @@ do | |||
230 | end) | 228 | end) |
231 | end | 229 | end |
232 | 230 | ||
233 | local function detect_lua_via_flags(flags, project_dir) | 231 | local function detect_lua_via_args(args, project_dir) |
234 | local lua_version = flags["lua-version"] | 232 | local lua_version = args.lua_version |
235 | or find_default_lua_version(flags, project_dir) | 233 | or find_default_lua_version(args, project_dir) |
236 | or (project_dir and find_version_from_config(project_dir)) | 234 | or (project_dir and find_version_from_config(project_dir)) |
237 | 235 | ||
238 | if flags["lua-dir"] then | 236 | if args.lua_dir then |
239 | local detected, err = util.find_lua(flags["lua-dir"], lua_version) | 237 | local detected, err = util.find_lua(args.lua_dir, lua_version) |
240 | if not detected then | 238 | if not detected then |
241 | die(err) | 239 | die(err) |
242 | end | 240 | end |
@@ -264,14 +262,14 @@ do | |||
264 | return {} | 262 | return {} |
265 | end | 263 | end |
266 | 264 | ||
267 | detect_config_via_flags = function(flags) | 265 | detect_config_via_args = function(args) |
268 | local project_dir, given = find_project_dir(flags["project-tree"]) | 266 | local project_dir, given = find_project_dir(args.project_tree) |
269 | local detected = detect_lua_via_flags(flags, project_dir) | 267 | local detected = detect_lua_via_args(args, project_dir) |
270 | if flags["lua-version"] then | 268 | if args.lua_version then |
271 | detected.given_lua_version = flags["lua-version"] | 269 | detected.given_lua_version = args.lua_version |
272 | end | 270 | end |
273 | if flags["lua-dir"] then | 271 | if args.lua_dir then |
274 | detected.given_lua_dir = flags["lua-dir"] | 272 | detected.given_lua_dir = args.lua_dir |
275 | end | 273 | end |
276 | if given then | 274 | if given then |
277 | detected.given_project_dir = project_dir | 275 | detected.given_project_dir = project_dir |
@@ -281,8 +279,8 @@ do | |||
281 | end | 279 | end |
282 | end | 280 | end |
283 | 281 | ||
284 | init_config = function(flags) | 282 | init_config = function(args) |
285 | local detected = detect_config_via_flags(flags) | 283 | local detected = detect_config_via_args(args) |
286 | 284 | ||
287 | -- FIXME A quick hack for the experimental Windows build | 285 | -- FIXME A quick hack for the experimental Windows build |
288 | if os.getenv("LUAROCKS_CROSS_COMPILING") then | 286 | if os.getenv("LUAROCKS_CROSS_COMPILING") then |
@@ -306,6 +304,135 @@ do | |||
306 | end | 304 | end |
307 | end | 305 | end |
308 | 306 | ||
307 | local variables_help = [[ | ||
308 | Variables: | ||
309 | Variables from the "variables" table of the configuration file can be | ||
310 | overridden with VAR=VALUE assignments. | ||
311 | |||
312 | ]] | ||
313 | |||
314 | local function get_status(status) | ||
315 | return status and "ok" or "not found" | ||
316 | end | ||
317 | |||
318 | local function get_config_text(cfg) | ||
319 | local buf = "Configuration:\n Lua version: "..cfg.lua_version.."\n" | ||
320 | if cfg.luajit_version then | ||
321 | buf = buf.." LuaJIT version: "..cfg.luajit_version.."\n" | ||
322 | end | ||
323 | buf = buf.."\n Configuration files:\n" | ||
324 | local conf = cfg.config_files | ||
325 | buf = buf.." System : "..fs.absolute_name(conf.system.file).." ("..get_status(conf.system.found)..")\n" | ||
326 | if conf.user.file then | ||
327 | buf = buf.." User : "..fs.absolute_name(conf.user.file).." ("..get_status(conf.user.found)..")\n" | ||
328 | else | ||
329 | buf = buf.." User : disabled in this LuaRocks installation.\n" | ||
330 | end | ||
331 | if conf.project then | ||
332 | buf = buf.." Project : "..fs.absolute_name(conf.project.file).." ("..get_status(conf.project.found)..")\n" | ||
333 | end | ||
334 | buf = buf.."\n Rocks trees in use: \n" | ||
335 | for _, tree in ipairs(cfg.rocks_trees) do | ||
336 | if type(tree) == "string" then | ||
337 | buf = buf.." "..fs.absolute_name(tree) | ||
338 | else | ||
339 | local name = tree.name and " (\""..tree.name.."\")" or "" | ||
340 | buf = buf.." "..fs.absolute_name(tree.root)..name | ||
341 | end | ||
342 | end | ||
343 | |||
344 | return buf.."\n" | ||
345 | end | ||
346 | |||
347 | local function get_parser(description, cmd_modules) | ||
348 | local basename = dir.base_name(program) | ||
349 | local parser = argparse( | ||
350 | basename, "LuaRocks "..cfg.program_version..", the Lua package manager\n\n".. | ||
351 | program.." - "..description, variables_help.."Run '"..basename.. | ||
352 | "' without any arguments to see the configuration.") | ||
353 | :help_max_width(80) | ||
354 | :add_help_command() | ||
355 | :add_complete_command({ | ||
356 | help_max_width = 100, | ||
357 | summary = "Output a shell completion script.", | ||
358 | description = [[ | ||
359 | Output a shell completion script. | ||
360 | |||
361 | Enabling completions for Bash: | ||
362 | |||
363 | Add the following line to your ~/.bashrc: | ||
364 | source <(]]..basename..[[ completion bash) | ||
365 | or save the completion script to the local completion directory: | ||
366 | ]]..basename..[[ completion bash > ~/.local/share/bash-completion/completions/]]..basename..[[ | ||
367 | |||
368 | |||
369 | Enabling completions for Zsh: | ||
370 | |||
371 | Save the completion script to a file in your $fpath. | ||
372 | You can add a new directory to your $fpath by adding e.g. | ||
373 | fpath=(~/.zfunc $fpath) | ||
374 | to your ~/.zshrc. | ||
375 | Then run: | ||
376 | ]]..basename..[[ completion zsh > ~/.zfunc/_]]..basename..[[ | ||
377 | |||
378 | |||
379 | Enabling completion for Fish: | ||
380 | |||
381 | Add the following line to your ~/.config/fish/config.fish: | ||
382 | ]]..basename..[[ completion fish | source | ||
383 | or save the completion script to the local completion directory: | ||
384 | ]]..basename..[[ completion fish > ~/.config/fish/completions/]]..basename..[[.fish | ||
385 | ]]}) | ||
386 | :command_target("command") | ||
387 | :require_command(false) | ||
388 | |||
389 | parser:flag("--version", "Show version info and exit.") | ||
390 | :action(function() | ||
391 | util.printout(program.." "..cfg.program_version) | ||
392 | util.printout(description) | ||
393 | util.printout() | ||
394 | os.exit(cmd.errorcodes.OK) | ||
395 | end) | ||
396 | parser:flag("--dev", "Enable the sub-repositories in rocks servers for ".. | ||
397 | "rockspecs of in-development versions.") | ||
398 | parser:option("--server", "Fetch rocks/rockspecs from this server ".. | ||
399 | "(takes priority over config file).") | ||
400 | parser:option("--only-server", "Fetch rocks/rockspecs from this server only ".. | ||
401 | "(overrides any entries in the config file).") | ||
402 | :argname("<server>") | ||
403 | parser:option("--only-sources", "Restrict downloads to paths matching the given URL.") | ||
404 | :argname("<url>") | ||
405 | parser:option("--namespace", "Specify the rocks server namespace to use.") | ||
406 | parser:option("--lua-dir", "Which Lua installation to use.") | ||
407 | :argname("<prefix>") | ||
408 | parser:option("--lua-version", "Which Lua version to use.") | ||
409 | :argname("<ver>") | ||
410 | parser:option("--tree", "Which tree to operate on.") | ||
411 | parser:flag("--local", "Use the tree in the user's home directory.\n".. | ||
412 | "To enable it, see '"..program.." help path'.") | ||
413 | parser:flag("--global", "Use the system tree when `local_by_default` is `true`.") | ||
414 | parser:flag("--verbose", "Display verbose output of commands executed.") | ||
415 | parser:option("--timeout", "Timeout on network operations, in seconds.\n".. | ||
416 | "0 means no timeout (wait forever). Default is ".. | ||
417 | tostring(cfg.connection_timeout)..".") | ||
418 | :argname("<seconds>") | ||
419 | :convert(tonumber) | ||
420 | |||
421 | -- Used internally to force the use of a particular project tree | ||
422 | parser:option("--project-tree"):hidden(true) | ||
423 | -- Compatibility for old names of some options | ||
424 | parser:option("--to"):target("tree"):hidden(true) | ||
425 | parser:option("--from"):target("server"):hidden(true) | ||
426 | parser:option("--only-from"):target("only_server"):hidden(true) | ||
427 | parser:option("--only-sources-from"):target("only_sources"):hidden(true) | ||
428 | |||
429 | for _, module in util.sortedpairs(cmd_modules) do | ||
430 | module.add_to_parser(parser) | ||
431 | end | ||
432 | |||
433 | return parser | ||
434 | end | ||
435 | |||
309 | --- Main command-line processor. | 436 | --- Main command-line processor. |
310 | -- Parses input arguments and calls the appropriate driver function | 437 | -- Parses input arguments and calls the appropriate driver function |
311 | -- to execute the action requested on the command-line, forwarding | 438 | -- to execute the action requested on the command-line, forwarding |
@@ -318,7 +445,20 @@ function cmd.run_command(description, commands, external_namespace, ...) | |||
318 | 445 | ||
319 | check_popen() | 446 | check_popen() |
320 | 447 | ||
321 | local function process_arguments(...) | 448 | fs.init() |
449 | |||
450 | for _, module_name in ipairs(fs.modules(external_namespace)) do | ||
451 | if not commands[module_name] then | ||
452 | commands[module_name] = external_namespace.."."..module_name | ||
453 | end | ||
454 | end | ||
455 | |||
456 | local cmd_modules = {} | ||
457 | for name, module in pairs(commands) do | ||
458 | cmd_modules[name] = require(module) | ||
459 | end | ||
460 | |||
461 | local function process_cmdline_vars(...) | ||
322 | local args = {...} | 462 | local args = {...} |
323 | local cmdline_vars = {} | 463 | local cmdline_vars = {} |
324 | local last = #args | 464 | local last = #args |
@@ -340,69 +480,38 @@ function cmd.run_command(description, commands, external_namespace, ...) | |||
340 | end | 480 | end |
341 | end | 481 | end |
342 | end | 482 | end |
343 | local nonflags = { util.parse_flags(unpack(args)) } | ||
344 | local flags = table.remove(nonflags, 1) | ||
345 | if flags.ERROR then | ||
346 | die(flags.ERROR.." See --help.") | ||
347 | end | ||
348 | 483 | ||
349 | -- Compatibility for old names of some flags | 484 | return args, cmdline_vars |
350 | if flags["to"] then flags["tree"] = flags["to"] end | ||
351 | if flags["from"] then flags["server"] = flags["from"] end | ||
352 | if flags["nodeps"] then flags["deps-mode"] = "none" end | ||
353 | if flags["only-from"] then flags["only-server"] = flags["only-from"] end | ||
354 | if flags["only-sources-from"] then flags["only-sources"] = flags["only-sources-from"] end | ||
355 | |||
356 | return flags, nonflags, cmdline_vars | ||
357 | end | 485 | end |
358 | 486 | ||
359 | local flags, nonflags, cmdline_vars = process_arguments(...) | 487 | local args, cmdline_vars = process_cmdline_vars(...) |
488 | local parser = get_parser(description, cmd_modules) | ||
489 | args = parser:parse(args) | ||
360 | 490 | ||
361 | if flags["timeout"] then -- setting it in the config file will kick-in earlier in the process | 491 | -- Compatibility for old flag |
362 | local timeout = tonumber(flags["timeout"]) | 492 | if args.nodeps then |
363 | if timeout then | 493 | args.deps_mode = "none" |
364 | cfg.connection_timeout = timeout | ||
365 | else | ||
366 | die "Argument error: --timeout expects a numeric argument." | ||
367 | end | ||
368 | end | 494 | end |
369 | 495 | ||
370 | local command | 496 | if args.timeout then -- setting it in the config file will kick-in earlier in the process |
371 | if flags["help"] or #nonflags == 0 then | 497 | cfg.connection_timeout = args.timeout |
372 | command = "help" | ||
373 | else | ||
374 | command = table.remove(nonflags, 1) | ||
375 | end | 498 | end |
376 | command = command:gsub("-", "_") | ||
377 | 499 | ||
378 | if command == "config" then | 500 | if args.command == "config" then |
379 | if nonflags[1] == "lua_version" and nonflags[2] then | 501 | if args.key == "lua_version" and args.value then |
380 | flags["lua-version"] = nonflags[2] | 502 | args.lua_version = args.value |
381 | elseif nonflags[1] == "lua_dir" and nonflags[2] then | 503 | elseif args.key == "lua_dir" and args.value then |
382 | flags["lua-dir"] = nonflags[2] | 504 | args.lua_dir = args.value |
383 | end | 505 | end |
384 | end | 506 | end |
385 | 507 | ||
386 | if flags["deps-mode"] and not deps.check_deps_mode_flag(flags["deps-mode"]) then | ||
387 | die("Invalid entry for --deps-mode.") | ||
388 | end | ||
389 | |||
390 | ----------------------------------------------------------------------------- | 508 | ----------------------------------------------------------------------------- |
391 | local lua_found, err = init_config(flags) | 509 | local lua_found, err = init_config(args) |
392 | if err then | 510 | if err then |
393 | die(err) | 511 | die(err) |
394 | end | 512 | end |
395 | ----------------------------------------------------------------------------- | 513 | ----------------------------------------------------------------------------- |
396 | 514 | ||
397 | if flags["version"] then | ||
398 | util.printout(program.." "..cfg.program_version) | ||
399 | util.printout(description) | ||
400 | util.printout() | ||
401 | os.exit(cmd.errorcodes.OK) | ||
402 | end | ||
403 | |||
404 | fs.init() | ||
405 | |||
406 | -- if the Lua interpreter wasn't explicitly found before cfg.init, | 515 | -- if the Lua interpreter wasn't explicitly found before cfg.init, |
407 | -- try again now. | 516 | -- try again now. |
408 | if not lua_found then | 517 | if not lua_found then |
@@ -414,7 +523,7 @@ function cmd.run_command(description, commands, external_namespace, ...) | |||
414 | if not lua_found then | 523 | if not lua_found then |
415 | util.warning("Could not find a Lua " .. cfg.lua_version .. " interpreter in your PATH. " .. | 524 | util.warning("Could not find a Lua " .. cfg.lua_version .. " interpreter in your PATH. " .. |
416 | "Modules may not install with the correct configurations. " .. | 525 | "Modules may not install with the correct configurations. " .. |
417 | "You may want to specify to the path prefix to your build " .. | 526 | "You may want to specify the path prefix to your build " .. |
418 | "of Lua " .. cfg.lua_version .. " using --lua-dir") | 527 | "of Lua " .. cfg.lua_version .. " using --lua-dir") |
419 | end | 528 | end |
420 | cfg.lua_found = lua_found | 529 | cfg.lua_found = lua_found |
@@ -423,13 +532,7 @@ function cmd.run_command(description, commands, external_namespace, ...) | |||
423 | cfg.project_dir = fs.absolute_name(cfg.project_dir) | 532 | cfg.project_dir = fs.absolute_name(cfg.project_dir) |
424 | end | 533 | end |
425 | 534 | ||
426 | for _, module_name in ipairs(fs.modules(external_namespace)) do | 535 | if args.verbose then |
427 | if not commands[module_name] then | ||
428 | commands[module_name] = external_namespace.."."..module_name | ||
429 | end | ||
430 | end | ||
431 | |||
432 | if flags["verbose"] then | ||
433 | cfg.verbose = true | 536 | cfg.verbose = true |
434 | fs.verbose() | 537 | fs.verbose() |
435 | end | 538 | end |
@@ -438,24 +541,22 @@ function cmd.run_command(description, commands, external_namespace, ...) | |||
438 | die("Current directory does not exist. Please run LuaRocks from an existing directory.") | 541 | die("Current directory does not exist. Please run LuaRocks from an existing directory.") |
439 | end | 542 | end |
440 | 543 | ||
441 | ok, err = process_tree_flags(flags, cfg.project_dir) | 544 | local ok, err = process_tree_args(args, cfg.project_dir) |
442 | if not ok then | 545 | if not ok then |
443 | die(err) | 546 | die(err) |
444 | end | 547 | end |
445 | 548 | ||
446 | ok, err = process_server_flags(flags) | 549 | ok, err = process_server_args(args) |
447 | if not ok then | 550 | if not ok then |
448 | die(err) | 551 | die(err) |
449 | end | 552 | end |
450 | 553 | ||
451 | if flags["only-sources"] then | 554 | if args.only_sources then |
452 | cfg.only_sources_from = flags["only-sources"] | 555 | cfg.only_sources_from = args.only_sources |
453 | end | 556 | end |
454 | 557 | ||
455 | if command ~= "help" then | 558 | for k, v in pairs(cmdline_vars) do |
456 | for k, v in pairs(cmdline_vars) do | 559 | cfg.variables[k] = v |
457 | cfg.variables[k] = v | ||
458 | end | ||
459 | end | 560 | end |
460 | 561 | ||
461 | -- if running as superuser, use system cache dir | 562 | -- if running as superuser, use system cache dir |
@@ -463,22 +564,23 @@ function cmd.run_command(description, commands, external_namespace, ...) | |||
463 | cfg.local_cache = dir.path(fs.system_cache_dir(), "luarocks") | 564 | cfg.local_cache = dir.path(fs.system_cache_dir(), "luarocks") |
464 | end | 565 | end |
465 | 566 | ||
466 | if commands[command] then | 567 | if not args.command then |
467 | local cmd_mod = require(commands[command]) | 568 | parser:epilog(variables_help..get_config_text(cfg)) |
468 | local call_ok, ok, err, exitcode = xpcall(function() | 569 | util.printout() |
469 | if command == "help" then | 570 | util.printout(parser:get_help()) |
470 | return cmd_mod.command(description, commands, unpack(nonflags)) | 571 | util.printout() |
471 | else | 572 | os.exit(cmd.errorcodes.OK) |
472 | return cmd_mod.command(flags, unpack(nonflags)) | 573 | end |
473 | end | 574 | |
474 | end, error_handler) | 575 | local cmd_mod = cmd_modules[args.command] |
475 | if not call_ok then | 576 | local call_ok, ok, err, exitcode = xpcall(function() |
476 | die(ok, cmd.errorcodes.CRASH) | 577 | return cmd_mod.command(args) |
477 | elseif not ok then | 578 | end, error_handler) |
478 | die(err, exitcode) | 579 | |
479 | end | 580 | if not call_ok then |
480 | else | 581 | die(ok, cmd.errorcodes.CRASH) |
481 | die("Unknown command: "..command) | 582 | elseif not ok then |
583 | die(err, exitcode) | ||
482 | end | 584 | end |
483 | util.run_scheduled_functions() | 585 | util.run_scheduled_functions() |
484 | end | 586 | end |
diff --git a/src/luarocks/cmd/build.lua b/src/luarocks/cmd/build.lua index 23a94fa7..0654b4eb 100644 --- a/src/luarocks/cmd/build.lua +++ b/src/luarocks/cmd/build.lua | |||
@@ -18,40 +18,20 @@ local search = require("luarocks.search") | |||
18 | local make = require("luarocks.cmd.make") | 18 | local make = require("luarocks.cmd.make") |
19 | local cmd = require("luarocks.cmd") | 19 | local cmd = require("luarocks.cmd") |
20 | 20 | ||
21 | cmd_build.help_summary = "build/compile a rock." | 21 | function cmd_build.add_to_parser(parser) |
22 | cmd_build.help_arguments = "[<flags...>] {<rockspec>|<rock>|<name> [<version>]}" | 22 | local cmd = parser:command("build", "Build and install a rock, compiling its C parts if any.\n".. |
23 | cmd_build.help = [[ | 23 | "If no arguments are given, behaves as luarocks make.", util.see_also()) |
24 | Build and install a rock, compiling its C parts if any. | 24 | :summary("Build/compile a rock.") |
25 | Argument may be a rockspec file, a source rock file | 25 | |
26 | or the name of a rock to be fetched from a repository. | 26 | cmd:argument("rock", "A rockspec file, a source rock file, or the name of ".. |
27 | 27 | "a rock to be fetched from a repository.") | |
28 | --pack-binary-rock Do not install rock. Instead, produce a .rock file | 28 | :args("?") |
29 | with the contents of compilation in the current | 29 | cmd:argument("version", "Rock version.") |
30 | directory. | 30 | :args("?") |
31 | 31 | ||
32 | --keep Do not remove previously installed versions of the | 32 | cmd:flag("--only-deps", "Installs only the dependencies of the rock.") |
33 | rock after building a new one. This behavior can | 33 | make.cmd_options(cmd) |
34 | be made permanent by setting keep_other_versions=true | 34 | end |
35 | in the configuration file. | ||
36 | |||
37 | --branch=<name> Override the `source.branch` field in the loaded | ||
38 | rockspec. Allows to specify a different branch to | ||
39 | fetch. Particularly for "dev" rocks. | ||
40 | |||
41 | --only-deps Installs only the dependencies of the rock. | ||
42 | |||
43 | --verify Verify signature of the rockspec or src.rock being | ||
44 | built. If the rockspec or src.rock is being downloaded, | ||
45 | LuaRocks will attempt to download the signature as well. | ||
46 | Otherwise, the signature file should be already | ||
47 | available locally in the same directory. | ||
48 | You need the signer’s public key in your local | ||
49 | keyring for this option to work properly. | ||
50 | |||
51 | --sign To be used with --pack-binary-rock. Also produce | ||
52 | a signature file for the generated .rock file. | ||
53 | |||
54 | ]]..util.deps_mode_help() | ||
55 | 35 | ||
56 | --- Build and install a rock. | 36 | --- Build and install a rock. |
57 | -- @param rock_filename string: local or remote filename of a rock. | 37 | -- @param rock_filename string: local or remote filename of a rock. |
@@ -132,58 +112,54 @@ local function remove_doc_dir(name, version) | |||
132 | end | 112 | end |
133 | 113 | ||
134 | --- Driver function for "build" command. | 114 | --- Driver function for "build" command. |
135 | -- @param name string: A local or remote rockspec or rock file. | ||
136 | -- If a package name is given, forwards the request to "search" and, | 115 | -- If a package name is given, forwards the request to "search" and, |
137 | -- if returned a result, installs the matching rock. | 116 | -- if returned a result, installs the matching rock. |
138 | -- @param version string: When passing a package name, a version number may | 117 | -- When passing a package name, a version number may also be given. |
139 | -- also be given. | ||
140 | -- @return boolean or (nil, string, exitcode): True if build was successful; nil and an | 118 | -- @return boolean or (nil, string, exitcode): True if build was successful; nil and an |
141 | -- error message otherwise. exitcode is optionally returned. | 119 | -- error message otherwise. exitcode is optionally returned. |
142 | function cmd_build.command(flags, name, version) | 120 | function cmd_build.command(args) |
143 | assert(type(name) == "string" or not name) | 121 | if not args.rock then |
144 | assert(type(version) == "string" or not version) | 122 | return make.command(args) |
145 | |||
146 | if not name then | ||
147 | return make.command(flags) | ||
148 | end | 123 | end |
149 | 124 | ||
150 | name = util.adjust_name_and_namespace(name, flags) | 125 | local name = util.adjust_name_and_namespace(args.rock, args) |
151 | 126 | ||
152 | local opts = build.opts({ | 127 | local opts = build.opts({ |
153 | need_to_fetch = true, | 128 | need_to_fetch = true, |
154 | minimal_mode = false, | 129 | minimal_mode = false, |
155 | deps_mode = deps.get_deps_mode(flags), | 130 | deps_mode = deps.get_deps_mode(args), |
156 | build_only_deps = not not flags["only-deps"], | 131 | build_only_deps = not not args.only_deps, |
157 | namespace = flags["namespace"], | 132 | namespace = args.namespace, |
158 | branch = not not flags["branch"], | 133 | branch = not not args.branch, |
159 | verify = not not flags["verify"], | 134 | verify = not not args.verify, |
160 | }) | 135 | }) |
161 | 136 | ||
162 | if flags["sign"] and not flags["pack-binary-rock"] then | 137 | if args.sign and not args.pack_binary_rock then |
163 | return nil, "In the build command, --sign is meant to be used only with --pack-binary-rock" | 138 | return nil, "In the build command, --sign is meant to be used only with --pack-binary-rock" |
164 | end | 139 | end |
165 | 140 | ||
166 | if flags["pack-binary-rock"] then | 141 | if args.pack_binary_rock then |
167 | return pack.pack_binary_rock(name, version, flags["sign"], function() | 142 | return pack.pack_binary_rock(name, args.version, args.sign, function() |
168 | opts.build_only_deps = false | 143 | opts.build_only_deps = false |
169 | local status, err, errcode = do_build(name, version, opts) | 144 | local status, err, errcode = do_build(name, args.version, opts) |
170 | if status and flags["no-doc"] then | 145 | if status and args.no_doc then |
171 | remove_doc_dir(name, version) | 146 | remove_doc_dir(name, args.version) |
172 | end | 147 | end |
173 | return status, err, errcode | 148 | return status, err, errcode |
174 | end) | 149 | end) |
175 | end | 150 | end |
176 | 151 | ||
177 | local ok, err = fs.check_command_permissions(flags) | 152 | local ok, err = fs.check_command_permissions(args) |
178 | if not ok then | 153 | if not ok then |
179 | return nil, err, cmd.errorcodes.PERMISSIONDENIED | 154 | return nil, err, cmd.errorcodes.PERMISSIONDENIED |
180 | end | 155 | end |
181 | 156 | ||
182 | ok, err = do_build(name, version, opts) | 157 | ok, err = do_build(name, args.version, opts) |
183 | if not ok then return nil, err end | 158 | if not ok then return nil, err end |
159 | local version | ||
184 | name, version = ok, err | 160 | name, version = ok, err |
185 | 161 | ||
186 | if flags["no-doc"] then | 162 | if args.no_doc then |
187 | remove_doc_dir(name, version) | 163 | remove_doc_dir(name, version) |
188 | end | 164 | end |
189 | 165 | ||
@@ -191,15 +167,15 @@ function cmd_build.command(flags, name, version) | |||
191 | util.printout("Stopping after installing dependencies for " ..name.." "..version) | 167 | util.printout("Stopping after installing dependencies for " ..name.." "..version) |
192 | util.printout() | 168 | util.printout() |
193 | else | 169 | else |
194 | if (not flags["keep"]) and not cfg.keep_other_versions then | 170 | if (not args.keep) and not cfg.keep_other_versions then |
195 | local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) | 171 | local ok, err = remove.remove_other_versions(name, version, args.force, args.force_fast) |
196 | if not ok then | 172 | if not ok then |
197 | util.printerr(err) | 173 | util.printerr(err) |
198 | end | 174 | end |
199 | end | 175 | end |
200 | end | 176 | end |
201 | 177 | ||
202 | writer.check_dependencies(nil, deps.get_deps_mode(flags)) | 178 | writer.check_dependencies(nil, deps.get_deps_mode(args)) |
203 | return name, version | 179 | return name, version |
204 | end | 180 | end |
205 | 181 | ||
diff --git a/src/luarocks/cmd/config.lua b/src/luarocks/cmd/config.lua index b3cb5e07..59dafdb5 100644 --- a/src/luarocks/cmd/config.lua +++ b/src/luarocks/cmd/config.lua | |||
@@ -9,21 +9,22 @@ local deps = require("luarocks.deps") | |||
9 | local dir = require("luarocks.dir") | 9 | local dir = require("luarocks.dir") |
10 | local fs = require("luarocks.fs") | 10 | local fs = require("luarocks.fs") |
11 | 11 | ||
12 | config_cmd.help_summary = "Query information about the LuaRocks configuration." | 12 | function config_cmd.add_to_parser(parser) |
13 | config_cmd.help_arguments = "(<key> | <key> <value> --scope=<scope> | <key> --unset --scope=<scope> | )" | 13 | local cmd = parser:command("config", [[ |
14 | config_cmd.help = [[ | 14 | Query information about the LuaRocks configuration. |
15 | * When given a configuration key, it prints the value of that key | 15 | |
16 | according to the currently active configuration (taking into account | 16 | * When given a configuration key, it prints the value of that key according to |
17 | all config files and any command-line flags passed) | 17 | the currently active configuration (taking into account all config files and |
18 | any command-line flags passed) | ||
18 | 19 | ||
19 | Examples: | 20 | Examples: |
20 | luarocks config lua_interpreter | 21 | luarocks config lua_interpreter |
21 | luarocks config variables.LUA_INCDIR | 22 | luarocks config variables.LUA_INCDIR |
22 | luarocks config lua_version | 23 | luarocks config lua_version |
23 | 24 | ||
24 | * When given a configuration key and a value, | 25 | * When given a configuration key and a value, it overwrites the config file (see |
25 | it overwrites the config file (see the --scope option below to determine which) | 26 | the --scope option below to determine which) and replaces the value of the |
26 | and replaces the value of the given key with the given value. | 27 | given key with the given value. |
27 | 28 | ||
28 | * `lua_dir` is a special key as it checks for a valid Lua installation | 29 | * `lua_dir` is a special key as it checks for a valid Lua installation |
29 | (equivalent to --lua-dir) and sets several keys at once. | 30 | (equivalent to --lua-dir) and sets several keys at once. |
@@ -35,33 +36,42 @@ config_cmd.help = [[ | |||
35 | luarocks config lua_dir /usr/local | 36 | luarocks config lua_dir /usr/local |
36 | luarocks config lua_version 5.3 | 37 | luarocks config lua_version 5.3 |
37 | 38 | ||
38 | * When given a configuration key and --unset, | 39 | * When given a configuration key and --unset, it overwrites the config file (see |
39 | it overwrites the config file (see the --scope option below to determine which) | 40 | the --scope option below to determine which) and deletes that key from the |
40 | and deletes that key from the file. | 41 | file. |
41 | 42 | ||
42 | Example: luarocks config variables.OPENSSL_DIR --unset | 43 | Example: luarocks config variables.OPENSSL_DIR --unset |
43 | 44 | ||
44 | * When given no arguments, it prints the entire currently active | 45 | * When given no arguments, it prints the entire currently active configuration, |
45 | configuration, resulting from reading the config files from | 46 | resulting from reading the config files from all scopes. |
46 | all scopes. | 47 | |
47 | 48 | Example: luarocks config]], util.see_also([[ | |
48 | Example: luarocks config | 49 | https://github.com/luarocks/luarocks/wiki/Config-file-format |
49 | 50 | for detailed information on the LuaRocks config file format. | |
50 | OPTIONS | 51 | ]])) |
51 | --scope=<scope> The scope indicates which config file should be rewritten. | 52 | :summary("Query information about the LuaRocks configuration.") |
52 | Accepted values are "system", "user" or "project". | 53 | |
53 | * Using a wrapper created with `luarocks init`, | 54 | cmd:argument("key", "The configuration key.") |
54 | the default is "project". | 55 | :args("?") |
55 | * Using --local (or when `local_by_default` is `true`), | 56 | cmd:argument("value", "The configuration value.") |
56 | the default is "user". | 57 | :args("?") |
57 | * Otherwise, the default is "system". | 58 | |
58 | 59 | cmd:option("--scope", "The scope indicates which config file should be rewritten.\n".. | |
59 | --json Output as JSON | 60 | '* Using a wrapper created with `luarocks init`, the default is "project".\n'.. |
60 | ]] | 61 | '* Using --local (or when `local_by_default` is `true`), the default is "user".\n'.. |
61 | config_cmd.help_see_also = [[ | 62 | '* Otherwise, the default is "system".') |
62 | https://github.com/luarocks/luarocks/wiki/Config-file-format | 63 | :choices({"system", "user", "project"}) |
63 | for detailed information on the LuaRocks config file format. | 64 | cmd:flag("--unset", "Delete the key from the configuration file.") |
64 | ]] | 65 | cmd:flag("--json", "Output as JSON.") |
66 | |||
67 | -- Deprecated flags | ||
68 | cmd:flag("--lua-incdir"):hidden(true) | ||
69 | cmd:flag("--lua-libdir"):hidden(true) | ||
70 | cmd:flag("--lua-ver"):hidden(true) | ||
71 | cmd:flag("--system-config"):hidden(true) | ||
72 | cmd:flag("--user-config"):hidden(true) | ||
73 | cmd:flag("--rock-trees"):hidden(true) | ||
74 | end | ||
65 | 75 | ||
66 | local function config_file(conf) | 76 | local function config_file(conf) |
67 | print(dir.normalize(conf.file)) | 77 | print(dir.normalize(conf.file)) |
@@ -218,45 +228,40 @@ local function write_entries(keys, scope, do_unset) | |||
218 | end | 228 | end |
219 | end | 229 | end |
220 | 230 | ||
221 | local function check_scope(flags) | 231 | local function get_scope(args) |
222 | local scope = flags["scope"] | 232 | return args.scope |
223 | or (flags["local"] and "user") | 233 | or (args["local"] and "user") |
224 | or (flags["project-tree"] and "project") | 234 | or (args.project_tree and "project") |
225 | or (cfg.local_by_default and "user") | 235 | or (cfg.local_by_default and "user") |
226 | or "system" | 236 | or "system" |
227 | if scope ~= "system" and scope ~= "user" and scope ~= "project" then | ||
228 | return nil, "Valid values for scope are: system, user, project" | ||
229 | end | ||
230 | |||
231 | return scope | ||
232 | end | 237 | end |
233 | 238 | ||
234 | --- Driver function for "config" command. | 239 | --- Driver function for "config" command. |
235 | -- @return boolean: True if succeeded, nil on errors. | 240 | -- @return boolean: True if succeeded, nil on errors. |
236 | function config_cmd.command(flags, var, val) | 241 | function config_cmd.command(args) |
237 | deps.check_lua_incdir(cfg.variables) | 242 | deps.check_lua_incdir(cfg.variables) |
238 | deps.check_lua_libdir(cfg.variables) | 243 | deps.check_lua_libdir(cfg.variables) |
239 | 244 | ||
240 | -- deprecated flags | 245 | -- deprecated flags |
241 | if flags["lua-incdir"] then | 246 | if args.lua_incdir then |
242 | print(cfg.variables.LUA_INCDIR) | 247 | print(cfg.variables.LUA_INCDIR) |
243 | return true | 248 | return true |
244 | end | 249 | end |
245 | if flags["lua-libdir"] then | 250 | if args.lua_libdir then |
246 | print(cfg.variables.LUA_LIBDIR) | 251 | print(cfg.variables.LUA_LIBDIR) |
247 | return true | 252 | return true |
248 | end | 253 | end |
249 | if flags["lua-ver"] then | 254 | if args.lua_ver then |
250 | print(cfg.lua_version) | 255 | print(cfg.lua_version) |
251 | return true | 256 | return true |
252 | end | 257 | end |
253 | if flags["system-config"] then | 258 | if args.system_config then |
254 | return config_file(cfg.config_files.system) | 259 | return config_file(cfg.config_files.system) |
255 | end | 260 | end |
256 | if flags["user-config"] then | 261 | if args.user_config then |
257 | return config_file(cfg.config_files.user) | 262 | return config_file(cfg.config_files.user) |
258 | end | 263 | end |
259 | if flags["rock-trees"] then | 264 | if args.rock_trees then |
260 | for _, tree in ipairs(cfg.rocks_trees) do | 265 | for _, tree in ipairs(cfg.rocks_trees) do |
261 | if type(tree) == "string" then | 266 | if type(tree) == "string" then |
262 | util.printout(dir.normalize(tree)) | 267 | util.printout(dir.normalize(tree)) |
@@ -268,29 +273,22 @@ function config_cmd.command(flags, var, val) | |||
268 | return true | 273 | return true |
269 | end | 274 | end |
270 | 275 | ||
271 | if var == "lua_version" and val then | 276 | if args.key == "lua_version" and args.value then |
272 | local scope, err = check_scope(flags) | 277 | local scope = get_scope(args) |
273 | if not scope then | ||
274 | return nil, err | ||
275 | end | ||
276 | |||
277 | if scope == "project" and not cfg.config_files.project then | 278 | if scope == "project" and not cfg.config_files.project then |
278 | return nil, "Current directory is not part of a project. You may want to run `luarocks init`." | 279 | return nil, "Current directory is not part of a project. You may want to run `luarocks init`." |
279 | end | 280 | end |
280 | 281 | ||
281 | local prefix = dir.dir_name(cfg.config_files[scope].file) | 282 | local prefix = dir.dir_name(cfg.config_files[scope].file) |
282 | local ok, err = persist.save_default_lua_version(prefix, val) | 283 | local ok, err = persist.save_default_lua_version(prefix, args.value) |
283 | if not ok then | 284 | if not ok then |
284 | return nil, "could not set default Lua version: " .. err | 285 | return nil, "could not set default Lua version: " .. err |
285 | end | 286 | end |
286 | print("Lua version will default to " .. val .. " in " .. prefix) | 287 | print("Lua version will default to " .. args.value .. " in " .. prefix) |
287 | end | 288 | end |
288 | 289 | ||
289 | if var == "lua_dir" and val then | 290 | if args.key == "lua_dir" and args.value then |
290 | local scope, err = check_scope(flags) | 291 | local scope = get_scope(args) |
291 | if not scope then | ||
292 | return nil, err | ||
293 | end | ||
294 | local keys = { | 292 | local keys = { |
295 | ["variables.LUA_DIR"] = cfg.variables.LUA_DIR, | 293 | ["variables.LUA_DIR"] = cfg.variables.LUA_DIR, |
296 | ["variables.LUA_BINDIR"] = cfg.variables.LUA_BINDIR, | 294 | ["variables.LUA_BINDIR"] = cfg.variables.LUA_BINDIR, |
@@ -298,25 +296,21 @@ function config_cmd.command(flags, var, val) | |||
298 | ["variables.LUA_LIBDIR"] = cfg.variables.LUA_LIBDIR, | 296 | ["variables.LUA_LIBDIR"] = cfg.variables.LUA_LIBDIR, |
299 | ["lua_interpreter"] = cfg.lua_interpreter, | 297 | ["lua_interpreter"] = cfg.lua_interpreter, |
300 | } | 298 | } |
301 | return write_entries(keys, scope, flags["unset"]) | 299 | return write_entries(keys, scope, args.unset) |
302 | end | 300 | end |
303 | 301 | ||
304 | if var then | 302 | if args.key then |
305 | if val or flags["unset"] then | 303 | if args.value or args.unset then |
306 | local scope, err = check_scope(flags) | 304 | local scope = get_scope(args) |
307 | if not scope then | 305 | return write_entries({ [args.key] = args.value }, scope, args.unset) |
308 | return nil, err | ||
309 | end | ||
310 | |||
311 | return write_entries({ [var] = val }, scope, flags["unset"]) | ||
312 | else | 306 | else |
313 | return print_entry(var, cfg, flags["json"]) | 307 | return print_entry(args.key, cfg, args.json) |
314 | end | 308 | end |
315 | end | 309 | end |
316 | 310 | ||
317 | local cleancfg = cleanup(cfg) | 311 | local cleancfg = cleanup(cfg) |
318 | 312 | ||
319 | if flags["json"] then | 313 | if args.json then |
320 | return print_json(cleancfg) | 314 | return print_json(cleancfg) |
321 | else | 315 | else |
322 | print(persist.save_from_table_to_string(cleancfg)) | 316 | print(persist.save_from_table_to_string(cleancfg)) |
diff --git a/src/luarocks/cmd/doc.lua b/src/luarocks/cmd/doc.lua index a2472be4..4b3335d8 100644 --- a/src/luarocks/cmd/doc.lua +++ b/src/luarocks/cmd/doc.lua | |||
@@ -12,19 +12,22 @@ local fetch = require("luarocks.fetch") | |||
12 | local fs = require("luarocks.fs") | 12 | local fs = require("luarocks.fs") |
13 | local download = require("luarocks.download") | 13 | local download = require("luarocks.download") |
14 | 14 | ||
15 | doc.help_summary = "Show documentation for an installed rock." | 15 | function doc.add_to_parser(parser) |
16 | 16 | local cmd = parser:command("doc", "Show documentation for an installed rock.\n\n".. | |
17 | doc.help = [[ | 17 | "Without any flags, tries to load the documentation using a series of heuristics.\n".. |
18 | <argument> is an existing package name. | 18 | "With flags, return only the desired information.", util.see_also([[ |
19 | Without any flags, tries to load the documentation | 19 | For more information about a rock, see the 'show' command. |
20 | using a series of heuristics. | 20 | ]])) |
21 | With these flags, return only the desired information: | 21 | :summary("Show documentation for an installed rock.") |
22 | 22 | ||
23 | --home Open the home page of project. | 23 | cmd:argument("rock", "Name of the rock.") |
24 | --list List documentation files only. | 24 | cmd:argument("version", "Version of the rock.") |
25 | 25 | :args("?") | |
26 | For more information about a rock, see the 'show' command. | 26 | |
27 | ]] | 27 | cmd:flag("--home", "Open the home page of project.") |
28 | cmd:flag("--list", "List documentation files only.") | ||
29 | cmd:flag("--porcelain", "Produce machine-friendly output.") | ||
30 | end | ||
28 | 31 | ||
29 | local function show_homepage(homepage, name, version) | 32 | local function show_homepage(homepage, name, version) |
30 | if not homepage then | 33 | if not homepage then |
@@ -54,17 +57,12 @@ local function try_to_open_homepage(name, version) | |||
54 | end | 57 | end |
55 | 58 | ||
56 | --- Driver function for "doc" command. | 59 | --- Driver function for "doc" command. |
57 | -- @param name or nil: an existing package name. | ||
58 | -- @param version string or nil: a version may also be passed. | ||
59 | -- @return boolean: True if succeeded, nil on errors. | 60 | -- @return boolean: True if succeeded, nil on errors. |
60 | function doc.command(flags, name, version) | 61 | function doc.command(args) |
61 | if not name then | 62 | local name = util.adjust_name_and_namespace(args.rock, args) |
62 | return nil, "Argument missing. "..util.see_help("doc") | 63 | local version = args.version |
63 | end | ||
64 | |||
65 | name = util.adjust_name_and_namespace(name, flags) | ||
66 | local query = queries.new(name, version) | 64 | local query = queries.new(name, version) |
67 | local iname, iversion, repo = search.pick_installed_rock(query, flags["tree"]) | 65 | local iname, iversion, repo = search.pick_installed_rock(query, args.tree) |
68 | if not iname then | 66 | if not iname then |
69 | util.printout(name..(version and " "..version or "").." is not installed. Looking for it in the rocks servers...") | 67 | util.printout(name..(version and " "..version or "").." is not installed. Looking for it in the rocks servers...") |
70 | return try_to_open_homepage(name, version) | 68 | return try_to_open_homepage(name, version) |
@@ -75,7 +73,7 @@ function doc.command(flags, name, version) | |||
75 | if not rockspec then return nil,err end | 73 | if not rockspec then return nil,err end |
76 | local descript = rockspec.description or {} | 74 | local descript = rockspec.description or {} |
77 | 75 | ||
78 | if flags["home"] then | 76 | if args.home then |
79 | return show_homepage(descript.homepage, name, version) | 77 | return show_homepage(descript.homepage, name, version) |
80 | end | 78 | end |
81 | 79 | ||
@@ -91,7 +89,7 @@ function doc.command(flags, name, version) | |||
91 | end | 89 | end |
92 | end | 90 | end |
93 | if not docdir then | 91 | if not docdir then |
94 | if descript.homepage and not flags["list"] then | 92 | if descript.homepage and not args.list then |
95 | util.printout("Local documentation directory not found -- opening "..descript.homepage.." ...") | 93 | util.printout("Local documentation directory not found -- opening "..descript.homepage.." ...") |
96 | fs.browser(descript.homepage) | 94 | fs.browser(descript.homepage) |
97 | return true | 95 | return true |
@@ -105,7 +103,7 @@ function doc.command(flags, name, version) | |||
105 | local extensions = { htmlpatt, "%.md$", "%.txt$", "%.textile$", "" } | 103 | local extensions = { htmlpatt, "%.md$", "%.txt$", "%.textile$", "" } |
106 | local basenames = { "index", "readme", "manual" } | 104 | local basenames = { "index", "readme", "manual" } |
107 | 105 | ||
108 | local porcelain = flags["porcelain"] | 106 | local porcelain = args.porcelain |
109 | if #files > 0 then | 107 | if #files > 0 then |
110 | util.title("Documentation files for "..name.." "..version, porcelain) | 108 | util.title("Documentation files for "..name.." "..version, porcelain) |
111 | if porcelain then | 109 | if porcelain then |
@@ -120,7 +118,7 @@ function doc.command(flags, name, version) | |||
120 | end | 118 | end |
121 | end | 119 | end |
122 | 120 | ||
123 | if flags["list"] then | 121 | if args.list then |
124 | return true | 122 | return true |
125 | end | 123 | end |
126 | 124 | ||
diff --git a/src/luarocks/cmd/download.lua b/src/luarocks/cmd/download.lua index 50c10c0c..5032d580 100644 --- a/src/luarocks/cmd/download.lua +++ b/src/luarocks/cmd/download.lua | |||
@@ -6,42 +6,44 @@ local cmd_download = {} | |||
6 | local util = require("luarocks.util") | 6 | local util = require("luarocks.util") |
7 | local download = require("luarocks.download") | 7 | local download = require("luarocks.download") |
8 | 8 | ||
9 | cmd_download.help_summary = "Download a specific rock file from a rocks server." | 9 | function cmd_download.add_to_parser(parser) |
10 | cmd_download.help_arguments = "[--all] [--arch=<arch> | --source | --rockspec] [<name> [<version>]]" | 10 | local cmd = parser:command("download", "Download a specific rock file from a rocks server.", util.see_also()) |
11 | cmd_download.help = [[ | 11 | |
12 | --all Download all files if there are multiple matches. | 12 | cmd:argument("name", "Name of the rock.") |
13 | --source Download .src.rock if available. | 13 | :args("?") |
14 | --rockspec Download .rockspec if available. | 14 | cmd:argument("version", "Version of the rock.") |
15 | --arch=<arch> Download rock for a specific architecture. | 15 | :args("?") |
16 | ]] | 16 | |
17 | cmd:flag("--all", "Download all files if there are multiple matches.") | ||
18 | cmd:mutex( | ||
19 | cmd:flag("--source", "Download .src.rock if available."), | ||
20 | cmd:flag("--rockspec", "Download .rockspec if available."), | ||
21 | cmd:option("--arch", "Download rock for a specific architecture.")) | ||
22 | end | ||
17 | 23 | ||
18 | --- Driver function for the "download" command. | 24 | --- Driver function for the "download" command. |
19 | -- @param name string: a rock name. | ||
20 | -- @param version string or nil: if the name of a package is given, a | ||
21 | -- version may also be passed. | ||
22 | -- @return boolean or (nil, string): true if successful or nil followed | 25 | -- @return boolean or (nil, string): true if successful or nil followed |
23 | -- by an error message. | 26 | -- by an error message. |
24 | function cmd_download.command(flags, name, version) | 27 | function cmd_download.command(args) |
25 | assert(type(version) == "string" or not version) | 28 | if not args.name and not args.all then |
26 | if type(name) ~= "string" and not flags["all"] then | ||
27 | return nil, "Argument missing. "..util.see_help("download") | 29 | return nil, "Argument missing. "..util.see_help("download") |
28 | end | 30 | end |
29 | 31 | ||
30 | name = util.adjust_name_and_namespace(name, flags) | 32 | local name = util.adjust_name_and_namespace(args.name, args) |
31 | 33 | ||
32 | if not name then name, version = "", "" end | 34 | if not name then name, args.version = "", "" end |
33 | 35 | ||
34 | local arch | 36 | local arch |
35 | 37 | ||
36 | if flags["source"] then | 38 | if args.source then |
37 | arch = "src" | 39 | arch = "src" |
38 | elseif flags["rockspec"] then | 40 | elseif args.rockspec then |
39 | arch = "rockspec" | 41 | arch = "rockspec" |
40 | elseif flags["arch"] then | 42 | elseif args.arch then |
41 | arch = flags["arch"] | 43 | arch = args.arch |
42 | end | 44 | end |
43 | 45 | ||
44 | local dl, err = download.download(arch, name:lower(), version, flags["all"]) | 46 | local dl, err = download.download(arch, name:lower(), args.version, args.all) |
45 | return dl and true, err | 47 | return dl and true, err |
46 | end | 48 | end |
47 | 49 | ||
diff --git a/src/luarocks/cmd/help.lua b/src/luarocks/cmd/help.lua deleted file mode 100644 index b3d937e1..00000000 --- a/src/luarocks/cmd/help.lua +++ /dev/null | |||
@@ -1,139 +0,0 @@ | |||
1 | |||
2 | --- Module implementing the LuaRocks "help" command. | ||
3 | -- This is a generic help display module, which | ||
4 | -- uses a global table called "commands" to find commands | ||
5 | -- to show help for; each command should be represented by a | ||
6 | -- table containing "help" and "help_summary" fields. | ||
7 | local help = {} | ||
8 | |||
9 | local util = require("luarocks.util") | ||
10 | local cfg = require("luarocks.core.cfg") | ||
11 | local dir = require("luarocks.dir") | ||
12 | local fs = require("luarocks.fs") | ||
13 | |||
14 | local program = util.this_program("luarocks") | ||
15 | |||
16 | help.help_summary = "Help on commands. Type '"..program.." help <command>' for more." | ||
17 | |||
18 | help.help_arguments = "[<command>]" | ||
19 | help.help = [[ | ||
20 | <command> is the command to show help for. | ||
21 | ]] | ||
22 | |||
23 | local function print_banner() | ||
24 | util.printout("\nLuaRocks "..cfg.program_version..", the Lua package manager") | ||
25 | end | ||
26 | |||
27 | local function print_section(section) | ||
28 | util.printout("\n"..section) | ||
29 | end | ||
30 | |||
31 | local function get_status(status) | ||
32 | if status then | ||
33 | return "ok" | ||
34 | else | ||
35 | return "not found" | ||
36 | end | ||
37 | end | ||
38 | |||
39 | --- Driver function for the "help" command. | ||
40 | -- @param command string or nil: command to show help for; if not | ||
41 | -- given, help summaries for all commands are shown. | ||
42 | -- @return boolean or (nil, string): true if there were no errors | ||
43 | -- or nil and an error message if an invalid command was requested. | ||
44 | function help.command(description, commands, command) | ||
45 | assert(type(description) == "string") | ||
46 | assert(type(commands) == "table") | ||
47 | |||
48 | if not command then | ||
49 | print_banner() | ||
50 | print_section("NAME") | ||
51 | util.printout("\t"..program..[[ - ]]..description) | ||
52 | print_section("SYNOPSIS") | ||
53 | util.printout("\t"..program..[[ [<flags...>] [VAR=VALUE]... <command> [<argument>] ]]) | ||
54 | print_section("GENERAL OPTIONS") | ||
55 | util.printout([[ | ||
56 | These apply to all commands, as appropriate: | ||
57 | |||
58 | --dev Enable the sub-repositories in rocks servers | ||
59 | for rockspecs of in-development versions | ||
60 | --server=<server> Fetch rocks/rockspecs from this server | ||
61 | (takes priority over config file) | ||
62 | --only-server=<server> Fetch rocks/rockspecs from this server only | ||
63 | (overrides any entries in the config file) | ||
64 | --only-sources=<url> Restrict downloads to paths matching the | ||
65 | given URL. | ||
66 | --lua-dir=<prefix> Which Lua installation to use. | ||
67 | --lua-version=<ver> Which Lua version to use. | ||
68 | --tree=<tree> Which tree to operate on. | ||
69 | --local Use the tree in the user's home directory. | ||
70 | To enable it, see ']]..program..[[ help path'. | ||
71 | --global Use the system tree when `local_by_default` is `true`. | ||
72 | --verbose Display verbose output of commands executed. | ||
73 | --timeout=<seconds> Timeout on network operations, in seconds. | ||
74 | 0 means no timeout (wait forever). | ||
75 | Default is ]]..tostring(cfg.connection_timeout)..[[.]]) | ||
76 | print_section("VARIABLES") | ||
77 | util.printout([[ | ||
78 | Variables from the "variables" table of the configuration file | ||
79 | can be overridden with VAR=VALUE assignments.]]) | ||
80 | print_section("COMMANDS") | ||
81 | for name, modname in util.sortedpairs(commands) do | ||
82 | local cmd = require(modname) | ||
83 | util.printout("", name) | ||
84 | util.printout("\t", cmd.help_summary) | ||
85 | end | ||
86 | print_section("CONFIGURATION") | ||
87 | util.printout("\tLua version: " .. cfg.lua_version) | ||
88 | local ljv = util.get_luajit_version() | ||
89 | if ljv then | ||
90 | util.printout("\tLuaJIT version: " .. ljv) | ||
91 | end | ||
92 | util.printout() | ||
93 | util.printout("\tConfiguration files:") | ||
94 | local conf = cfg.config_files | ||
95 | util.printout("\t\tSystem : ".. fs.absolute_name(conf.system.file) .. " (" .. get_status(conf.system.found) ..")") | ||
96 | if conf.user.file then | ||
97 | util.printout("\t\tUser : ".. fs.absolute_name(conf.user.file) .. " (" .. get_status(conf.user.found) ..")") | ||
98 | else | ||
99 | util.printout("\t\tUser : disabled in this LuaRocks installation.") | ||
100 | end | ||
101 | if conf.project then | ||
102 | util.printout("\t\tProject : ".. fs.absolute_name(conf.project.file) .. " (" .. get_status(conf.project.found) ..")") | ||
103 | end | ||
104 | util.printout() | ||
105 | util.printout("\tRocks trees in use: ") | ||
106 | for _, tree in ipairs(cfg.rocks_trees) do | ||
107 | if type(tree) == "string" then | ||
108 | util.printout("\t\t"..fs.absolute_name(tree)) | ||
109 | else | ||
110 | local name = tree.name and " (\""..tree.name.."\")" or "" | ||
111 | util.printout("\t\t"..fs.absolute_name(tree.root)..name) | ||
112 | end | ||
113 | end | ||
114 | util.printout() | ||
115 | else | ||
116 | command = command:gsub("-", "_") | ||
117 | local cmd = commands[command] and require(commands[command]) | ||
118 | if cmd then | ||
119 | local arguments = cmd.help_arguments or "<argument>" | ||
120 | print_banner() | ||
121 | print_section("NAME") | ||
122 | util.printout("\t"..program.." "..command.." - "..cmd.help_summary) | ||
123 | print_section("SYNOPSIS") | ||
124 | util.printout("\t"..program.." "..command.." "..arguments) | ||
125 | print_section("DESCRIPTION") | ||
126 | util.printout("",(cmd.help:gsub("\n","\n\t"):gsub("\n\t$",""))) | ||
127 | print_section("SEE ALSO") | ||
128 | if cmd.help_see_also then | ||
129 | util.printout(cmd.help_see_also) | ||
130 | end | ||
131 | util.printout("","'"..program.." help' for general options and configuration.\n") | ||
132 | else | ||
133 | return nil, "Unknown command: "..command | ||
134 | end | ||
135 | end | ||
136 | return true | ||
137 | end | ||
138 | |||
139 | return help | ||
diff --git a/src/luarocks/cmd/init.lua b/src/luarocks/cmd/init.lua index 60aa4f82..5f269e22 100644 --- a/src/luarocks/cmd/init.lua +++ b/src/luarocks/cmd/init.lua | |||
@@ -10,27 +10,17 @@ local util = require("luarocks.util") | |||
10 | local persist = require("luarocks.persist") | 10 | local persist = require("luarocks.persist") |
11 | local write_rockspec = require("luarocks.cmd.write_rockspec") | 11 | local write_rockspec = require("luarocks.cmd.write_rockspec") |
12 | 12 | ||
13 | init.help_summary = "Initialize a directory for a Lua project using LuaRocks." | 13 | function init.add_to_parser(parser) |
14 | init.help_arguments = "[<name> [<version>]]" | 14 | local cmd = parser:command("init", "Initialize a directory for a Lua project using LuaRocks.", util.see_also()) |
15 | init.help = [[ | 15 | |
16 | <name> is the project name. | 16 | cmd:argument("name", "The project name.") |
17 | <version> is an optional project version. | 17 | :args("?") |
18 | 18 | cmd:argument("version", "An optional project version.") | |
19 | --reset Delete .luarocks/config-5.x.lua and ./lua | 19 | :args("?") |
20 | and generate new ones. | 20 | cmd:flag("--reset", "Delete .luarocks/config-5.x.lua and ./lua and generate new ones.") |
21 | 21 | ||
22 | Options for specifying rockspec data: | 22 | cmd:group("Options for specifying rockspec data", write_rockspec.cmd_options(cmd)) |
23 | 23 | end | |
24 | --license="<string>" A license string, such as "MIT/X11" or "GNU GPL v3". | ||
25 | --summary="<txt>" A short one-line description summary. | ||
26 | --detailed="<txt>" A longer description string. | ||
27 | --homepage=<url> Project homepage. | ||
28 | --lua-versions=<ver> Supported Lua versions. Accepted values are "5.1", "5.2", | ||
29 | "5.3", "5.1,5.2", "5.2,5.3", or "5.1,5.2,5.3". | ||
30 | --rockspec-format=<ver> Rockspec format version, such as "1.0" or "1.1". | ||
31 | --lib=<lib>[,<lib>] A comma-separated list of libraries that C files need to | ||
32 | link to. | ||
33 | ]] | ||
34 | 24 | ||
35 | local function write_gitignore(entries) | 25 | local function write_gitignore(entries) |
36 | local gitignore = "" | 26 | local gitignore = "" |
@@ -53,18 +43,18 @@ end | |||
53 | 43 | ||
54 | --- Driver function for "init" command. | 44 | --- Driver function for "init" command. |
55 | -- @return boolean: True if succeeded, nil on errors. | 45 | -- @return boolean: True if succeeded, nil on errors. |
56 | function init.command(flags, name, version) | 46 | function init.command(args) |
57 | 47 | ||
58 | local pwd = fs.current_dir() | 48 | local pwd = fs.current_dir() |
59 | 49 | ||
60 | if not name then | 50 | if not args.name then |
61 | name = dir.base_name(pwd) | 51 | args.name = dir.base_name(pwd) |
62 | if name == "/" then | 52 | if args.name == "/" then |
63 | return nil, "When running from the root directory, please specify the <name> argument" | 53 | return nil, "When running from the root directory, please specify the <name> argument" |
64 | end | 54 | end |
65 | end | 55 | end |
66 | 56 | ||
67 | util.title("Initializing project '" .. name .. "' for Lua " .. cfg.lua_version .. " ...") | 57 | util.title("Initializing project '" .. args.name .. "' for Lua " .. cfg.lua_version .. " ...") |
68 | 58 | ||
69 | util.printout("Checking your Lua installation ...") | 59 | util.printout("Checking your Lua installation ...") |
70 | if not cfg.lua_found then | 60 | if not cfg.lua_found then |
@@ -84,7 +74,9 @@ function init.command(flags, name, version) | |||
84 | end | 74 | end |
85 | 75 | ||
86 | if not has_rockspec then | 76 | if not has_rockspec then |
87 | local ok, err = write_rockspec.command(flags, name, version or "dev", pwd) | 77 | args.version = args.version or "dev" |
78 | args.location = pwd | ||
79 | local ok, err = write_rockspec.command(args) | ||
88 | if not ok then | 80 | if not ok then |
89 | util.printerr(err) | 81 | util.printerr(err) |
90 | end | 82 | end |
@@ -101,7 +93,7 @@ function init.command(flags, name, version) | |||
101 | fs.make_dir(".luarocks") | 93 | fs.make_dir(".luarocks") |
102 | local config_file = ".luarocks/config-" .. cfg.lua_version .. ".lua" | 94 | local config_file = ".luarocks/config-" .. cfg.lua_version .. ".lua" |
103 | 95 | ||
104 | if flags["reset"] then | 96 | if args.reset then |
105 | fs.delete(lua_wrapper) | 97 | fs.delete(lua_wrapper) |
106 | fs.delete(config_file) | 98 | fs.delete(config_file) |
107 | end | 99 | end |
diff --git a/src/luarocks/cmd/install.lua b/src/luarocks/cmd/install.lua index d1d7bf6c..4020918e 100644 --- a/src/luarocks/cmd/install.lua +++ b/src/luarocks/cmd/install.lua | |||
@@ -16,32 +16,35 @@ local cfg = require("luarocks.core.cfg") | |||
16 | local cmd = require("luarocks.cmd") | 16 | local cmd = require("luarocks.cmd") |
17 | local dir = require("luarocks.dir") | 17 | local dir = require("luarocks.dir") |
18 | 18 | ||
19 | install.help_summary = "Install a rock." | 19 | function install.add_to_parser(parser) |
20 | 20 | local cmd = parser:command("install", "Install a rock.", util.see_also()) | |
21 | install.help_arguments = "{<rock>|<name> [<version>]}" | 21 | |
22 | 22 | cmd:argument("rock", "The name of a rock to be fetched from a repository ".. | |
23 | install.help = [[ | 23 | "or a filename of a locally available rock.") |
24 | Argument may be the name of a rock to be fetched from a repository | 24 | cmd:argument("version", "Version of the rock.") |
25 | or a filename of a locally available rock. | 25 | :args("?") |
26 | 26 | ||
27 | --keep Do not remove previously installed versions of the | 27 | cmd:flag("--keep", "Do not remove previously installed versions of the ".. |
28 | rock after installing a new one. This behavior can | 28 | "rock after building a new one. This behavior can be made permanent by ".. |
29 | be made permanent by setting keep_other_versions=true | 29 | "setting keep_other_versions=true in the configuration file.") |
30 | in the configuration file. | 30 | cmd:flag("--force", "If --keep is not specified, force removal of ".. |
31 | 31 | "previously installed versions if it would break dependencies.") | |
32 | --only-deps Installs only the dependencies of the rock. | 32 | cmd:flag("--force-fast", "Like --force, but performs a forced removal ".. |
33 | 33 | "without reporting dependency issues.") | |
34 | --no-doc Installs the rock without its documentation. | 34 | cmd:flag("--only-deps", "Installs only the dependencies of the rock.") |
35 | 35 | cmd:flag("--no-doc", "Installs the rock without its documentation.") | |
36 | --verify Verify signature of the rock being installed. | 36 | cmd:flag("--verify", "Verify signature of the rockspec or src.rock being ".. |
37 | If rock is being downloaded, LuaRocks will attempt | 37 | "built. If the rockspec or src.rock is being downloaded, LuaRocks will ".. |
38 | to download the signature as well. If the rock is | 38 | "attempt to download the signature as well. Otherwise, the signature ".. |
39 | local, the signature file should be in the same | 39 | "file should be already available locally in the same directory.\n".. |
40 | directory. | 40 | "You need the signer’s public key in your local keyring for this ".. |
41 | You need the signer’s public key in your local | 41 | "option to work properly.") |
42 | keyring for this option to work properly. | 42 | util.deps_mode_option(cmd) |
43 | 43 | -- luarocks build options | |
44 | ]]..util.deps_mode_help() | 44 | parser:flag("--pack-binary-rock"):hidden(true) |
45 | parser:flag("--branch"):hidden(true) | ||
46 | parser:flag("--sign"):hidden(true) | ||
47 | end | ||
45 | 48 | ||
46 | install.opts = util.opts_table("install.opts", { | 49 | install.opts = util.opts_table("install.opts", { |
47 | namespace = "string?", | 50 | namespace = "string?", |
@@ -208,51 +211,46 @@ local function install_rock_file(filename, opts) | |||
208 | end | 211 | end |
209 | 212 | ||
210 | --- Driver function for the "install" command. | 213 | --- Driver function for the "install" command. |
211 | -- @param name string: name of a binary rock. If an URL or pathname | 214 | -- If an URL or pathname to a binary rock is given, fetches and installs it. |
212 | -- to a binary rock is given, fetches and installs it. If a rockspec or a | 215 | -- If a rockspec or a source rock is given, forwards the request to the "build" |
213 | -- source rock is given, forwards the request to the "build" command. | 216 | -- command. |
214 | -- If a package name is given, forwards the request to "search" and, | 217 | -- If a package name is given, forwards the request to "search" and, |
215 | -- if returned a result, installs the matching rock. | 218 | -- if returned a result, installs the matching rock. |
216 | -- @param version string: When passing a package name, a version number | ||
217 | -- may also be given. | ||
218 | -- @return boolean or (nil, string, exitcode): True if installation was | 219 | -- @return boolean or (nil, string, exitcode): True if installation was |
219 | -- successful, nil and an error message otherwise. exitcode is optionally returned. | 220 | -- successful, nil and an error message otherwise. exitcode is optionally returned. |
220 | function install.command(flags, name, version) | 221 | function install.command(args) |
221 | if type(name) ~= "string" then | 222 | args.rock = util.adjust_name_and_namespace(args.rock, args) |
222 | return nil, "Argument missing. "..util.see_help("install") | ||
223 | end | ||
224 | |||
225 | name = util.adjust_name_and_namespace(name, flags) | ||
226 | 223 | ||
227 | local ok, err = fs.check_command_permissions(flags) | 224 | local ok, err = fs.check_command_permissions(args) |
228 | if not ok then return nil, err, cmd.errorcodes.PERMISSIONDENIED end | 225 | if not ok then return nil, err, cmd.errorcodes.PERMISSIONDENIED end |
229 | 226 | ||
230 | if name:match("%.rockspec$") or name:match("%.src%.rock$") then | 227 | if args.rock:match("%.rockspec$") or args.rock:match("%.src%.rock$") then |
231 | local build = require("luarocks.cmd.build") | 228 | local build = require("luarocks.cmd.build") |
232 | return build.command(flags, name) | 229 | return build.command(args) |
233 | elseif name:match("%.rock$") then | 230 | elseif args.rock:match("%.rock$") then |
234 | local deps_mode = deps.get_deps_mode(flags) | 231 | local deps_mode = deps.get_deps_mode(args) |
235 | local opts = install.opts({ | 232 | local opts = install.opts({ |
236 | namespace = flags["namespace"], | 233 | namespace = args.namespace, |
237 | keep = not not flags["keep"], | 234 | keep = not not args.keep, |
238 | force = not not flags["force"], | 235 | force = not not args.force, |
239 | force_fast = not not flags["force-fast"], | 236 | force_fast = not not args.force_fast, |
240 | no_doc = not not flags["no-doc"], | 237 | no_doc = not not args.no_doc, |
241 | deps_mode = deps_mode, | 238 | deps_mode = deps_mode, |
242 | verify = not not flags["verify"], | 239 | verify = not not args.verify, |
243 | }) | 240 | }) |
244 | if flags["only-deps"] then | 241 | if args.only_deps then |
245 | return install_rock_file_deps(name, opts) | 242 | return install_rock_file_deps(args.rock, opts) |
246 | else | 243 | else |
247 | return install_rock_file(name, opts) | 244 | return install_rock_file(args.rock, opts) |
248 | end | 245 | end |
249 | else | 246 | else |
250 | local url, err = search.find_suitable_rock(queries.new(name:lower(), version), true) | 247 | local url, err = search.find_suitable_rock(queries.new(args.rock:lower(), args.version), true) |
251 | if not url then | 248 | if not url then |
252 | return nil, err | 249 | return nil, err |
253 | end | 250 | end |
254 | util.printout("Installing "..url) | 251 | util.printout("Installing "..url) |
255 | return install.command(flags, url) | 252 | args.rock = url |
253 | return install.command(args) | ||
256 | end | 254 | end |
257 | end | 255 | end |
258 | 256 | ||
diff --git a/src/luarocks/cmd/lint.lua b/src/luarocks/cmd/lint.lua index c9ea45ea..20c842ff 100644 --- a/src/luarocks/cmd/lint.lua +++ b/src/luarocks/cmd/lint.lua | |||
@@ -7,24 +7,21 @@ local util = require("luarocks.util") | |||
7 | local download = require("luarocks.download") | 7 | local download = require("luarocks.download") |
8 | local fetch = require("luarocks.fetch") | 8 | local fetch = require("luarocks.fetch") |
9 | 9 | ||
10 | lint.help_summary = "Check syntax of a rockspec." | 10 | function lint.add_to_parser(parser) |
11 | lint.help_arguments = "<rockspec>" | 11 | local cmd = parser:command("lint", "Check syntax of a rockspec.\n\n".. |
12 | lint.help = [[ | 12 | "Returns success if the text of the rockspec is syntactically correct, else failure.", |
13 | This is a utility function that checks the syntax of a rockspec. | 13 | util.see_also()) |
14 | 14 | :summary("Check syntax of a rockspec.") | |
15 | It returns success or failure if the text of a rockspec is | 15 | |
16 | syntactically correct. | 16 | cmd:argument("rockspec", "The rockspec to check.") |
17 | ]] | 17 | end |
18 | 18 | ||
19 | function lint.command(flags, input) | 19 | function lint.command(args) |
20 | if not input then | 20 | |
21 | return nil, "Argument missing. "..util.see_help("lint") | 21 | local filename = args.rockspec |
22 | end | 22 | if not filename:match(".rockspec$") then |
23 | |||
24 | local filename = input | ||
25 | if not input:match(".rockspec$") then | ||
26 | local err | 23 | local err |
27 | filename, err = download.download("rockspec", input:lower()) | 24 | filename, err = download.download("rockspec", filename:lower()) |
28 | if not filename then | 25 | if not filename then |
29 | return nil, err | 26 | return nil, err |
30 | end | 27 | end |
diff --git a/src/luarocks/cmd/list.lua b/src/luarocks/cmd/list.lua index 5e5cfac8..cac5cd8a 100644 --- a/src/luarocks/cmd/list.lua +++ b/src/luarocks/cmd/list.lua | |||
@@ -10,16 +10,18 @@ local cfg = require("luarocks.core.cfg") | |||
10 | local util = require("luarocks.util") | 10 | local util = require("luarocks.util") |
11 | local path = require("luarocks.path") | 11 | local path = require("luarocks.path") |
12 | 12 | ||
13 | list.help_summary = "List currently installed rocks." | 13 | function list.add_to_parser(parser) |
14 | list.help_arguments = "[--porcelain] <filter>" | 14 | local cmd = parser:command("list", "List currently installed rocks.", util.see_also()) |
15 | list.help = [[ | ||
16 | <filter> is a substring of a rock name to filter by. | ||
17 | 15 | ||
18 | --outdated List only rocks for which there is a | 16 | cmd:argument("filter", "A substring of a rock name to filter by.") |
19 | higher version available in the rocks server. | 17 | :args("?") |
18 | cmd:argument("version", "Rock version to filter by.") | ||
19 | :args("?") | ||
20 | 20 | ||
21 | --porcelain Produce machine-friendly output. | 21 | cmd:flag("--outdated", "List only rocks for which there is a higher ".. |
22 | ]] | 22 | "version available in the rocks server.") |
23 | cmd:flag("--porcelain", "Produce machine-friendly output.") | ||
24 | end | ||
23 | 25 | ||
24 | local function check_outdated(trees, query) | 26 | local function check_outdated(trees, query) |
25 | local results_installed = {} | 27 | local results_installed = {} |
@@ -65,20 +67,18 @@ local function list_outdated(trees, query, porcelain) | |||
65 | end | 67 | end |
66 | 68 | ||
67 | --- Driver function for "list" command. | 69 | --- Driver function for "list" command. |
68 | -- @param filter string or nil: A substring of a rock name to filter by. | ||
69 | -- @param version string or nil: a version may also be passed. | ||
70 | -- @return boolean: True if succeeded, nil on errors. | 70 | -- @return boolean: True if succeeded, nil on errors. |
71 | function list.command(flags, filter, version) | 71 | function list.command(args) |
72 | local query = queries.new(filter and filter:lower() or "", version, true) | 72 | local query = queries.new(args.filter and args.filter:lower() or "", args.version, true) |
73 | local trees = cfg.rocks_trees | 73 | local trees = cfg.rocks_trees |
74 | local title = "Rocks installed for Lua "..cfg.lua_version | 74 | local title = "Rocks installed for Lua "..cfg.lua_version |
75 | if flags["tree"] then | 75 | if args.tree then |
76 | trees = { flags["tree"] } | 76 | trees = { args.tree } |
77 | title = title .. " in " .. flags["tree"] | 77 | title = title .. " in " .. args.tree |
78 | end | 78 | end |
79 | 79 | ||
80 | if flags["outdated"] then | 80 | if args.outdated then |
81 | return list_outdated(trees, query, flags["porcelain"]) | 81 | return list_outdated(trees, query, args.porcelain) |
82 | end | 82 | end |
83 | 83 | ||
84 | local results = {} | 84 | local results = {} |
@@ -88,8 +88,8 @@ function list.command(flags, filter, version) | |||
88 | util.warning(err) | 88 | util.warning(err) |
89 | end | 89 | end |
90 | end | 90 | end |
91 | util.title(title, flags["porcelain"]) | 91 | util.title(title, args.porcelain) |
92 | search.print_result_tree(results, flags["porcelain"]) | 92 | search.print_result_tree(results, args.porcelain) |
93 | return true | 93 | return true |
94 | end | 94 | end |
95 | 95 | ||
diff --git a/src/luarocks/cmd/make.lua b/src/luarocks/cmd/make.lua index 4d813864..480ec48d 100644 --- a/src/luarocks/cmd/make.lua +++ b/src/luarocks/cmd/make.lua | |||
@@ -16,58 +16,61 @@ local deps = require("luarocks.deps") | |||
16 | local writer = require("luarocks.manif.writer") | 16 | local writer = require("luarocks.manif.writer") |
17 | local cmd = require("luarocks.cmd") | 17 | local cmd = require("luarocks.cmd") |
18 | 18 | ||
19 | make.help_summary = "Compile package in current directory using a rockspec." | 19 | function make.cmd_options(parser) |
20 | make.help_arguments = "[--pack-binary-rock] [<rockspec>]" | 20 | parser:flag("--pack-binary-rock", "Do not install rock. Instead, produce a ".. |
21 | make.help = [[ | 21 | ".rock file with the contents of compilation in the current directory.") |
22 | Builds sources in the current directory, but unlike "build", | 22 | parser:flag("--keep", "Do not remove previously installed versions of the ".. |
23 | it does not fetch sources, etc., assuming everything is | 23 | "rock after building a new one. This behavior can be made permanent by ".. |
24 | available in the current directory. If no argument is given, | 24 | "setting keep_other_versions=true in the configuration file.") |
25 | it looks for a rockspec in the current directory and in "rockspec/" | 25 | parser:flag("--force", "If --keep is not specified, force removal of ".. |
26 | and "rockspecs/" subdirectories, picking the rockspec with newest version | 26 | "previously installed versions if it would break dependencies.") |
27 | or without version name. If rockspecs for different rocks are found | 27 | parser:flag("--force-fast", "Like --force, but performs a forced removal ".. |
28 | or there are several rockspecs without version, you must specify which to use, | 28 | "without reporting dependency issues.") |
29 | parser:option("--branch", "Override the `source.branch` field in the loaded ".. | ||
30 | "rockspec. Allows to specify a different branch to fetch. Particularly ".. | ||
31 | 'for "dev" rocks.') | ||
32 | :argname("<name>") | ||
33 | parser:flag("--verify", "Verify signature of the rockspec or src.rock being ".. | ||
34 | "built. If the rockspec or src.rock is being downloaded, LuaRocks will ".. | ||
35 | "attempt to download the signature as well. Otherwise, the signature ".. | ||
36 | "file should be already available locally in the same directory.\n".. | ||
37 | "You need the signer’s public key in your local keyring for this ".. | ||
38 | "option to work properly.") | ||
39 | parser:flag("--sign", "To be used with --pack-binary-rock. Also produce a ".. | ||
40 | "signature file for the generated .rock file.") | ||
41 | util.deps_mode_option(parser) | ||
42 | end | ||
43 | |||
44 | function make.add_to_parser(parser) | ||
45 | local cmd = parser:command("make", [[ | ||
46 | Builds sources in the current directory, but unlike "build", it does not fetch | ||
47 | sources, etc., assuming everything is available in the current directory. If no | ||
48 | argument is given, it looks for a rockspec in the current directory and in | ||
49 | "rockspec/" and "rockspecs/" subdirectories, picking the rockspec with newest | ||
50 | version or without version name. If rockspecs for different rocks are found or | ||
51 | there are several rockspecs without version, you must specify which to use, | ||
29 | through the command-line. | 52 | through the command-line. |
30 | 53 | ||
31 | This command is useful as a tool for debugging rockspecs. | 54 | This command is useful as a tool for debugging rockspecs. |
32 | To install rocks, you'll normally want to use the "install" and | 55 | To install rocks, you'll normally want to use the "install" and "build" |
33 | "build" commands. See the help on those for details. | 56 | commands. See the help on those for details. |
34 | 57 | ||
35 | NB: Use `luarocks install` with the `--only-deps` flag if you want to install | 58 | NB: Use `luarocks install` with the `--only-deps` flag if you want to install |
36 | only dependencies of the rockspec (see `luarocks help install`). | 59 | only dependencies of the rockspec (see `luarocks help install`). |
60 | ]], util.see_also()) | ||
61 | :summary("Compile package in current directory using a rockspec.") | ||
37 | 62 | ||
38 | --pack-binary-rock Do not install rock. Instead, produce a .rock file | 63 | cmd:argument("rockspec", "Rockspec for the rock to build.") |
39 | with the contents of compilation in the current | 64 | :args("?") |
40 | directory. | ||
41 | |||
42 | --keep Do not remove previously installed versions of the | ||
43 | rock after installing a new one. This behavior can | ||
44 | be made permanent by setting keep_other_versions=true | ||
45 | in the configuration file. | ||
46 | |||
47 | --branch=<name> Override the `source.branch` field in the loaded | ||
48 | rockspec. Allows to specify a different branch to | ||
49 | fetch. Particularly for "dev" rocks. | ||
50 | 65 | ||
51 | --verify Verify signature of the rockspec or src.rock being | 66 | make.cmd_options(cmd) |
52 | built. If the rockspec or src.rock is being downloaded, | 67 | end |
53 | LuaRocks will attempt to download the signature as well. | ||
54 | Otherwise, the signature file should be already | ||
55 | available locally in the same directory. | ||
56 | You need the signer’s public key in your local | ||
57 | keyring for this option to work properly. | ||
58 | |||
59 | --sign To be used with --pack-binary-rock. Also produce | ||
60 | a signature file for the generated .rock file. | ||
61 | |||
62 | ]] | ||
63 | 68 | ||
64 | --- Driver function for "make" command. | 69 | --- Driver function for "make" command. |
65 | -- @param name string: A local rockspec. | ||
66 | -- @return boolean or (nil, string, exitcode): True if build was successful; nil and an | 70 | -- @return boolean or (nil, string, exitcode): True if build was successful; nil and an |
67 | -- error message otherwise. exitcode is optionally returned. | 71 | -- error message otherwise. exitcode is optionally returned. |
68 | function make.command(flags, rockspec_filename) | 72 | function make.command(args) |
69 | assert(type(rockspec_filename) == "string" or not rockspec_filename) | 73 | local rockspec_filename = args.rockspec |
70 | |||
71 | if not rockspec_filename then | 74 | if not rockspec_filename then |
72 | local err | 75 | local err |
73 | rockspec_filename, err = util.get_default_rockspec() | 76 | rockspec_filename, err = util.get_default_rockspec() |
@@ -84,39 +87,39 @@ function make.command(flags, rockspec_filename) | |||
84 | return nil, err | 87 | return nil, err |
85 | end | 88 | end |
86 | 89 | ||
87 | local name = util.adjust_name_and_namespace(rockspec.name, flags) | 90 | local name = util.adjust_name_and_namespace(rockspec.name, args) |
88 | 91 | ||
89 | local opts = build.opts({ | 92 | local opts = build.opts({ |
90 | need_to_fetch = false, | 93 | need_to_fetch = false, |
91 | minimal_mode = true, | 94 | minimal_mode = true, |
92 | deps_mode = deps.get_deps_mode(flags), | 95 | deps_mode = deps.get_deps_mode(args), |
93 | build_only_deps = false, | 96 | build_only_deps = false, |
94 | namespace = flags["namespace"], | 97 | namespace = args.namespace, |
95 | branch = not not flags["branch"], | 98 | branch = not not args.branch, |
96 | verify = not not flags["verify"], | 99 | verify = not not args.verify, |
97 | }) | 100 | }) |
98 | 101 | ||
99 | if flags["sign"] and not flags["pack-binary-rock"] then | 102 | if args.sign and not args.pack_binary_rock then |
100 | return nil, "In the make command, --sign is meant to be used only with --pack-binary-rock" | 103 | return nil, "In the make command, --sign is meant to be used only with --pack-binary-rock" |
101 | end | 104 | end |
102 | 105 | ||
103 | if flags["pack-binary-rock"] then | 106 | if args.pack_binary_rock then |
104 | return pack.pack_binary_rock(name, rockspec.version, flags["sign"], function() | 107 | return pack.pack_binary_rock(name, rockspec.version, args.sign, function() |
105 | return build.build_rockspec(rockspec, opts) | 108 | return build.build_rockspec(rockspec, opts) |
106 | end) | 109 | end) |
107 | else | 110 | else |
108 | local ok, err = fs.check_command_permissions(flags) | 111 | local ok, err = fs.check_command_permissions(args) |
109 | if not ok then return nil, err, cmd.errorcodes.PERMISSIONDENIED end | 112 | if not ok then return nil, err, cmd.errorcodes.PERMISSIONDENIED end |
110 | ok, err = build.build_rockspec(rockspec, opts) | 113 | ok, err = build.build_rockspec(rockspec, opts) |
111 | if not ok then return nil, err end | 114 | if not ok then return nil, err end |
112 | local name, version = ok, err | 115 | local name, version = ok, err |
113 | 116 | ||
114 | if (not flags["keep"]) and not cfg.keep_other_versions then | 117 | if (not args.keep) and not cfg.keep_other_versions then |
115 | local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) | 118 | local ok, err = remove.remove_other_versions(name, version, args.force, args.force_fast) |
116 | if not ok then util.printerr(err) end | 119 | if not ok then util.printerr(err) end |
117 | end | 120 | end |
118 | 121 | ||
119 | writer.check_dependencies(nil, deps.get_deps_mode(flags)) | 122 | writer.check_dependencies(nil, deps.get_deps_mode(args)) |
120 | return name, version | 123 | return name, version |
121 | end | 124 | end |
122 | end | 125 | end |
diff --git a/src/luarocks/cmd/new_version.lua b/src/luarocks/cmd/new_version.lua index 19b5fa1e..62fb08f6 100644 --- a/src/luarocks/cmd/new_version.lua +++ b/src/luarocks/cmd/new_version.lua | |||
@@ -11,35 +11,46 @@ local fs = require("luarocks.fs") | |||
11 | local dir = require("luarocks.dir") | 11 | local dir = require("luarocks.dir") |
12 | local type_rockspec = require("luarocks.type.rockspec") | 12 | local type_rockspec = require("luarocks.type.rockspec") |
13 | 13 | ||
14 | new_version.help_summary = "Auto-write a rockspec for a new version of a rock." | 14 | function new_version.add_to_parser(parser) |
15 | new_version.help_arguments = "[--tag=<tag>] [--dir=<path>] [<package>|<rockspec>] [<new_version>] [<new_url>]" | 15 | local cmd = parser:command("new_version", [[ |
16 | new_version.help = [[ | 16 | This is a utility function that writes a new rockspec, updating data from a |
17 | This is a utility function that writes a new rockspec, updating data | 17 | previous one. |
18 | from a previous one. | 18 | |
19 | 19 | If a package name is given, it downloads the latest rockspec from the default | |
20 | If a package name is given, it downloads the latest rockspec from the | 20 | server. If a rockspec is given, it uses it instead. If no argument is given, it |
21 | default server. If a rockspec is given, it uses it instead. If no argument | 21 | looks for a rockspec same way 'luarocks make' does. |
22 | is given, it looks for a rockspec same way 'luarocks make' does. | 22 | |
23 | 23 | If the version number is not given and tag is passed using --tag, it is used as | |
24 | If the version number is not given and tag is passed using --tag, | 24 | the version, with 'v' removed from beginning. Otherwise, it only increments the |
25 | it is used as the version, with 'v' removed from beginning. | 25 | revision number of the given (or downloaded) rockspec. |
26 | Otherwise, it only increments the revision number of the given | 26 | |
27 | (or downloaded) rockspec. | 27 | If a URL is given, it replaces the one from the old rockspec with the given URL. |
28 | 28 | If a URL is not given and a new version is given, it tries to guess the new URL | |
29 | If a URL is given, it replaces the one from the old rockspec with the | 29 | by replacing occurrences of the version number in the URL or tag. It also tries |
30 | given URL. If a URL is not given and a new version is given, it tries | 30 | to download the new URL to determine the new MD5 checksum. |
31 | to guess the new URL by replacing occurrences of the version number | 31 | |
32 | in the URL or tag. It also tries to download the new URL to determine | 32 | If a tag is given, it replaces the one from the old rockspec. If there is an old |
33 | the new MD5 checksum. | 33 | tag but no new one passed, it is guessed in the same way URL is. |
34 | |||
35 | If a tag is given, it replaces the one from the old rockspec. If there is | ||
36 | an old tag but no new one passed, it is guessed in the same way URL is. | ||
37 | 34 | ||
38 | If a directory is not given, it defaults to the current directory. | 35 | If a directory is not given, it defaults to the current directory. |
39 | 36 | ||
40 | WARNING: it writes the new rockspec to the given directory, | 37 | WARNING: it writes the new rockspec to the given directory, overwriting the file |
41 | overwriting the file if it already exists. | 38 | if it already exists.]], util.see_also()) |
42 | ]] | 39 | :summary("Auto-write a rockspec for a new version of a rock.") |
40 | |||
41 | parser:command("new-version"):hidden(true):action(function(args) args.command = "new_version" end) | ||
42 | |||
43 | cmd:argument("rock", "Package name or rockspec.") | ||
44 | :args("?") | ||
45 | cmd:argument("new_version", "New version of the rock.") | ||
46 | :args("?") | ||
47 | cmd:argument("new_url", "New URL of the rock.") | ||
48 | :args("?") | ||
49 | |||
50 | cmd:option("--dir", "Output directory for the new rockspec.") | ||
51 | cmd:option("--tag", "New SCM tag.") | ||
52 | end | ||
53 | |||
43 | 54 | ||
44 | local function try_replace(tbl, field, old, new) | 55 | local function try_replace(tbl, field, old, new) |
45 | if not tbl[field] then | 56 | if not tbl[field] then |
@@ -126,24 +137,23 @@ local function update_source_section(out_rs, url, tag, old_ver, new_ver) | |||
126 | return true | 137 | return true |
127 | end | 138 | end |
128 | 139 | ||
129 | function new_version.command(flags, input, version, url) | 140 | function new_version.command(args) |
130 | if not input then | 141 | if not args.rock then |
131 | local err | 142 | local err |
132 | input, err = util.get_default_rockspec() | 143 | args.rock, err = util.get_default_rockspec() |
133 | if not input then | 144 | if not args.rock then |
134 | return nil, err | 145 | return nil, err |
135 | end | 146 | end |
136 | end | 147 | end |
137 | assert(type(input) == "string") | ||
138 | 148 | ||
139 | local filename, err | 149 | local filename, err |
140 | if input:match("rockspec$") then | 150 | if args.rock:match("rockspec$") then |
141 | filename, err = fetch.fetch_url(input) | 151 | filename, err = fetch.fetch_url(args.rock) |
142 | if not filename then | 152 | if not filename then |
143 | return nil, err | 153 | return nil, err |
144 | end | 154 | end |
145 | else | 155 | else |
146 | filename, err = download.download("rockspec", input:lower()) | 156 | filename, err = download.download("rockspec", args.rock:lower()) |
147 | if not filename then | 157 | if not filename then |
148 | return nil, err | 158 | return nil, err |
149 | end | 159 | end |
@@ -157,20 +167,20 @@ function new_version.command(flags, input, version, url) | |||
157 | local old_ver, old_rev = valid_rs.version:match("(.*)%-(%d+)$") | 167 | local old_ver, old_rev = valid_rs.version:match("(.*)%-(%d+)$") |
158 | local new_ver, new_rev | 168 | local new_ver, new_rev |
159 | 169 | ||
160 | if flags.tag and not version then | 170 | if args.tag and not args.new_version then |
161 | version = flags.tag:gsub("^v", "") | 171 | args.new_version = args.tag:gsub("^v", "") |
162 | end | 172 | end |
163 | 173 | ||
164 | local out_dir | 174 | local out_dir |
165 | if flags.dir then | 175 | if args.dir then |
166 | out_dir = dir.normalize(flags.dir) | 176 | out_dir = dir.normalize(args.dir) |
167 | end | 177 | end |
168 | 178 | ||
169 | if version then | 179 | if args.new_version then |
170 | new_ver, new_rev = version:match("(.*)%-(%d+)$") | 180 | new_ver, new_rev = args.new_version:match("(.*)%-(%d+)$") |
171 | new_rev = tonumber(new_rev) | 181 | new_rev = tonumber(new_rev) |
172 | if not new_rev then | 182 | if not new_rev then |
173 | new_ver = version | 183 | new_ver = args.new_version |
174 | new_rev = 1 | 184 | new_rev = 1 |
175 | end | 185 | end |
176 | else | 186 | else |
@@ -183,7 +193,7 @@ function new_version.command(flags, input, version, url) | |||
183 | local out_name = out_rs.package:lower() | 193 | local out_name = out_rs.package:lower() |
184 | out_rs.version = new_rockver.."-"..new_rev | 194 | out_rs.version = new_rockver.."-"..new_rev |
185 | 195 | ||
186 | local ok, err = update_source_section(out_rs, url, flags.tag, old_ver, new_ver) | 196 | local ok, err = update_source_section(out_rs, args.new_url, args.tag, old_ver, new_ver) |
187 | if not ok then return nil, err end | 197 | if not ok then return nil, err end |
188 | 198 | ||
189 | if out_rs.build and out_rs.build.type == "module" then | 199 | if out_rs.build and out_rs.build.type == "module" then |
diff --git a/src/luarocks/cmd/pack.lua b/src/luarocks/cmd/pack.lua index 7781a3bd..fde8f875 100644 --- a/src/luarocks/cmd/pack.lua +++ b/src/luarocks/cmd/pack.lua | |||
@@ -8,39 +8,30 @@ local pack = require("luarocks.pack") | |||
8 | local signing = require("luarocks.signing") | 8 | local signing = require("luarocks.signing") |
9 | local queries = require("luarocks.queries") | 9 | local queries = require("luarocks.queries") |
10 | 10 | ||
11 | cmd_pack.help_summary = "Create a rock, packing sources or binaries." | 11 | function cmd_pack.add_to_parser(parser) |
12 | cmd_pack.help_arguments = "{<rockspec>|<name> [<version>]}" | 12 | local cmd = parser:command("pack", "Create a rock, packing sources or binaries.", util.see_also()) |
13 | cmd_pack.help = [[ | ||
14 | --sign Produce a signature file as well. | ||
15 | 13 | ||
16 | Argument may be a rockspec file, for creating a source rock, | 14 | cmd:argument("rock", "A rockspec file, for creating a source rock, or the ".. |
17 | or the name of an installed package, for creating a binary rock. | 15 | "name of an installed package, for creating a binary rock.") |
18 | In the latter case, the app version may be given as a second | 16 | cmd:argument("version", "A version may be given if the first argument is a rock name.") |
19 | argument. | 17 | :args("?") |
20 | ]] | 18 | |
19 | cmd:flag("--sign", "Produce a signature file as well.") | ||
20 | end | ||
21 | 21 | ||
22 | --- Driver function for the "pack" command. | 22 | --- Driver function for the "pack" command. |
23 | -- @param arg string: may be a rockspec file, for creating a source rock, | ||
24 | -- or the name of an installed package, for creating a binary rock. | ||
25 | -- @param version string or nil: if the name of a package is given, a | ||
26 | -- version may also be passed. | ||
27 | -- @return boolean or (nil, string): true if successful or nil followed | 23 | -- @return boolean or (nil, string): true if successful or nil followed |
28 | -- by an error message. | 24 | -- by an error message. |
29 | function cmd_pack.command(flags, arg, version) | 25 | function cmd_pack.command(args) |
30 | assert(type(version) == "string" or not version) | ||
31 | if type(arg) ~= "string" then | ||
32 | return nil, "Argument missing. "..util.see_help("pack") | ||
33 | end | ||
34 | |||
35 | local file, err | 26 | local file, err |
36 | if arg:match(".*%.rockspec") then | 27 | if args.rock:match(".*%.rockspec") then |
37 | file, err = pack.pack_source_rock(arg) | 28 | file, err = pack.pack_source_rock(args.rock) |
38 | else | 29 | else |
39 | local name = util.adjust_name_and_namespace(arg, flags) | 30 | local name = util.adjust_name_and_namespace(args.rock, args) |
40 | local query = queries.new(name, version) | 31 | local query = queries.new(name, args.version) |
41 | file, err = pack.pack_installed_rock(query, flags["tree"]) | 32 | file, err = pack.pack_installed_rock(query, args.tree) |
42 | end | 33 | end |
43 | return pack.report_and_sign_local_file(file, err, flags["sign"]) | 34 | return pack.report_and_sign_local_file(file, err, args.sign) |
44 | end | 35 | end |
45 | 36 | ||
46 | return cmd_pack | 37 | return cmd_pack |
diff --git a/src/luarocks/cmd/path.lua b/src/luarocks/cmd/path.lua index bb383ad9..b1da4c0b 100644 --- a/src/luarocks/cmd/path.lua +++ b/src/luarocks/cmd/path.lua | |||
@@ -7,48 +7,45 @@ local util = require("luarocks.util") | |||
7 | local cfg = require("luarocks.core.cfg") | 7 | local cfg = require("luarocks.core.cfg") |
8 | local fs = require("luarocks.fs") | 8 | local fs = require("luarocks.fs") |
9 | 9 | ||
10 | path_cmd.help_summary = "Return the currently configured package path." | 10 | function path_cmd.add_to_parser(parser) |
11 | path_cmd.help_arguments = "" | 11 | local cmd = parser:command("path", [[ |
12 | path_cmd.help = [[ | ||
13 | Returns the package path currently configured for this installation | 12 | Returns the package path currently configured for this installation |
14 | of LuaRocks, formatted as shell commands to update LUA_PATH and LUA_CPATH. | 13 | of LuaRocks, formatted as shell commands to update LUA_PATH and LUA_CPATH. |
15 | 14 | ||
16 | --no-bin Do not export the PATH variable | ||
17 | |||
18 | --append Appends the paths to the existing paths. Default is to prefix | ||
19 | the LR paths to the existing paths. | ||
20 | |||
21 | --lr-path Exports the Lua path (not formatted as shell command) | ||
22 | |||
23 | --lr-cpath Exports the Lua cpath (not formatted as shell command) | ||
24 | |||
25 | --lr-bin Exports the system path (not formatted as shell command) | ||
26 | |||
27 | |||
28 | On Unix systems, you may run: | 15 | On Unix systems, you may run: |
29 | eval `luarocks path` | 16 | eval `luarocks path` |
30 | And on Windows: | 17 | And on Windows: |
31 | luarocks path > "%temp%\_lrp.bat" && call "%temp%\_lrp.bat" && del "%temp%\_lrp.bat" | 18 | luarocks path > "%temp%\_lrp.bat" && call "%temp%\_lrp.bat" && del "%temp%\_lrp.bat"]], |
32 | ]] | 19 | util.see_also()) |
20 | :summary("Return the currently configured package path.") | ||
21 | |||
22 | cmd:flag("--no-bin", "Do not export the PATH variable.") | ||
23 | cmd:flag("--append", "Appends the paths to the existing paths. Default is ".. | ||
24 | "to prefix the LR paths to the existing paths.") | ||
25 | cmd:flag("--lr-path", "Exports the Lua path (not formatted as shell command).") | ||
26 | cmd:flag("--lr-cpath", "Exports the Lua cpath (not formatted as shell command).") | ||
27 | cmd:flag("--lr-bin", "Exports the system path (not formatted as shell command).") | ||
28 | cmd:flag("--bin"):hidden(true) | ||
29 | end | ||
33 | 30 | ||
34 | --- Driver function for "path" command. | 31 | --- Driver function for "path" command. |
35 | -- @return boolean This function always succeeds. | 32 | -- @return boolean This function always succeeds. |
36 | function path_cmd.command(flags) | 33 | function path_cmd.command(args) |
37 | local lr_path, lr_cpath, lr_bin = cfg.package_paths(flags["tree"]) | 34 | local lr_path, lr_cpath, lr_bin = cfg.package_paths(args.tree) |
38 | local path_sep = cfg.export_path_separator | 35 | local path_sep = cfg.export_path_separator |
39 | 36 | ||
40 | if flags["lr-path"] then | 37 | if args.lr_path then |
41 | util.printout(util.cleanup_path(lr_path, ';', cfg.lua_version)) | 38 | util.printout(util.cleanup_path(lr_path, ';', cfg.lua_version)) |
42 | return true | 39 | return true |
43 | elseif flags["lr-cpath"] then | 40 | elseif args.lr_cpath then |
44 | util.printout(util.cleanup_path(lr_cpath, ';', cfg.lua_version)) | 41 | util.printout(util.cleanup_path(lr_cpath, ';', cfg.lua_version)) |
45 | return true | 42 | return true |
46 | elseif flags["lr-bin"] then | 43 | elseif args.lr_bin then |
47 | util.printout(util.cleanup_path(lr_bin, path_sep)) | 44 | util.printout(util.cleanup_path(lr_bin, path_sep)) |
48 | return true | 45 | return true |
49 | end | 46 | end |
50 | 47 | ||
51 | if flags["append"] then | 48 | if args.append then |
52 | lr_path = package.path .. ";" .. lr_path | 49 | lr_path = package.path .. ";" .. lr_path |
53 | lr_cpath = package.cpath .. ";" .. lr_cpath | 50 | lr_cpath = package.cpath .. ";" .. lr_cpath |
54 | lr_bin = os.getenv("PATH") .. path_sep .. lr_bin | 51 | lr_bin = os.getenv("PATH") .. path_sep .. lr_bin |
@@ -60,10 +57,10 @@ function path_cmd.command(flags) | |||
60 | 57 | ||
61 | local lpath_var, lcpath_var = util.lua_path_variables() | 58 | local lpath_var, lcpath_var = util.lua_path_variables() |
62 | 59 | ||
63 | util.printout(fs.export_cmd(lpath_var, util.cleanup_path(lr_path, ';', cfg.lua_version, flags["append"]))) | 60 | util.printout(fs.export_cmd(lpath_var, util.cleanup_path(lr_path, ';', cfg.lua_version))) |
64 | util.printout(fs.export_cmd(lcpath_var, util.cleanup_path(lr_cpath, ';', cfg.lua_version, flags["append"]))) | 61 | util.printout(fs.export_cmd(lcpath_var, util.cleanup_path(lr_cpath, ';', cfg.lua_version))) |
65 | if not flags["no-bin"] then | 62 | if not args.no_bin then |
66 | util.printout(fs.export_cmd("PATH", util.cleanup_path(lr_bin, path_sep, nil, flags["append"]))) | 63 | util.printout(fs.export_cmd("PATH", util.cleanup_path(lr_bin, path_sep))) |
67 | end | 64 | end |
68 | return true | 65 | return true |
69 | end | 66 | end |
diff --git a/src/luarocks/cmd/purge.lua b/src/luarocks/cmd/purge.lua index 98b76a0f..b71baa7c 100644 --- a/src/luarocks/cmd/purge.lua +++ b/src/luarocks/cmd/purge.lua | |||
@@ -15,24 +15,27 @@ local remove = require("luarocks.remove") | |||
15 | local queries = require("luarocks.queries") | 15 | local queries = require("luarocks.queries") |
16 | local cmd = require("luarocks.cmd") | 16 | local cmd = require("luarocks.cmd") |
17 | 17 | ||
18 | purge.help_summary = "Remove all installed rocks from a tree." | 18 | function purge.add_to_parser(parser) |
19 | purge.help_arguments = "--tree=<tree> [--old-versions]" | 19 | local cmd = parser:command("purge", [[ |
20 | purge.help = [[ | ||
21 | This command removes rocks en masse from a given tree. | 20 | This command removes rocks en masse from a given tree. |
22 | By default, it removes all rocks from a tree. | 21 | By default, it removes all rocks from a tree. |
23 | 22 | ||
24 | The --tree argument is mandatory: luarocks purge does not | 23 | The --tree option is mandatory: luarocks purge does not assume a default tree.]], |
25 | assume a default tree. | 24 | util.see_also()) |
25 | :summary("Remove all installed rocks from a tree.") | ||
26 | 26 | ||
27 | --old-versions Keep the highest-numbered version of each | 27 | cmd:flag("--old-versions", "Keep the highest-numbered version of each ".. |
28 | rock and remove the other ones. By default | 28 | "rock and remove the other ones. By default it only removes old ".. |
29 | it only removes old versions if they are | 29 | "versions if they are not needed as dependencies. This can be ".. |
30 | not needed as dependencies. This can be | 30 | "overridden with the flag --force.") |
31 | overridden with the flag --force. | 31 | cmd:flag("--force", "If --old-versions is specified, force removal of ".. |
32 | ]] | 32 | "previously installed versions if it would break dependencies.") |
33 | cmd:flag("--force-fast", "Like --force, but performs a forced removal ".. | ||
34 | "without reporting dependency issues.") | ||
35 | end | ||
33 | 36 | ||
34 | function purge.command(flags) | 37 | function purge.command(args) |
35 | local tree = flags["tree"] | 38 | local tree = args.tree |
36 | 39 | ||
37 | if type(tree) ~= "string" then | 40 | if type(tree) ~= "string" then |
38 | return nil, "The --tree argument is mandatory. "..util.see_help("purge") | 41 | return nil, "The --tree argument is mandatory. "..util.see_help("purge") |
@@ -43,21 +46,21 @@ function purge.command(flags) | |||
43 | return nil, "Directory not found: "..tree | 46 | return nil, "Directory not found: "..tree |
44 | end | 47 | end |
45 | 48 | ||
46 | local ok, err = fs.check_command_permissions(flags) | 49 | local ok, err = fs.check_command_permissions(args) |
47 | if not ok then return nil, err, cmd.errorcodes.PERMISSIONDENIED end | 50 | if not ok then return nil, err, cmd.errorcodes.PERMISSIONDENIED end |
48 | 51 | ||
49 | search.local_manifest_search(results, path.rocks_dir(tree), queries.all()) | 52 | search.local_manifest_search(results, path.rocks_dir(tree), queries.all()) |
50 | 53 | ||
51 | local sort = function(a,b) return vers.compare_versions(b,a) end | 54 | local sort = function(a,b) return vers.compare_versions(b,a) end |
52 | if flags["old-versions"] then | 55 | if args.old_versions then |
53 | sort = vers.compare_versions | 56 | sort = vers.compare_versions |
54 | end | 57 | end |
55 | 58 | ||
56 | for package, versions in util.sortedpairs(results) do | 59 | for package, versions in util.sortedpairs(results) do |
57 | for version, _ in util.sortedpairs(versions, sort) do | 60 | for version, _ in util.sortedpairs(versions, sort) do |
58 | if flags["old-versions"] then | 61 | if args.old_versions then |
59 | util.printout("Keeping "..package.." "..version.."...") | 62 | util.printout("Keeping "..package.." "..version.."...") |
60 | local ok, err = remove.remove_other_versions(package, version, flags["force"], flags["force-fast"]) | 63 | local ok, err = remove.remove_other_versions(package, version, args.force, args.force_fast) |
61 | if not ok then | 64 | if not ok then |
62 | util.printerr(err) | 65 | util.printerr(err) |
63 | end | 66 | end |
diff --git a/src/luarocks/cmd/remove.lua b/src/luarocks/cmd/remove.lua index 5ddf7477..f29b0b7d 100644 --- a/src/luarocks/cmd/remove.lua +++ b/src/luarocks/cmd/remove.lua | |||
@@ -14,38 +14,38 @@ local writer = require("luarocks.manif.writer") | |||
14 | local queries = require("luarocks.queries") | 14 | local queries = require("luarocks.queries") |
15 | local cmd = require("luarocks.cmd") | 15 | local cmd = require("luarocks.cmd") |
16 | 16 | ||
17 | cmd_remove.help_summary = "Uninstall a rock." | 17 | function cmd_remove.add_to_parser(parser) |
18 | cmd_remove.help_arguments = "[--force|--force-fast] <name> [<version>]" | 18 | local cmd = parser:command("remove", [[ |
19 | cmd_remove.help = [[ | 19 | Uninstall a rock. |
20 | Argument is the name of a rock to be uninstalled. | 20 | |
21 | If a version is not given, try to remove all versions at once. | 21 | If a version is not given, try to remove all versions at once. |
22 | Will only perform the removal if it does not break dependencies. | 22 | Will only perform the removal if it does not break dependencies. |
23 | To override this check and force the removal, use --force. | 23 | To override this check and force the removal, use --force or --force-fast.]], |
24 | To perform a forced removal without reporting dependency issues, | 24 | util.see_also()) |
25 | use --force-fast. | 25 | :summary("Uninstall a rock.") |
26 | |||
27 | cmd:argument("rock", "Name of the rock to be uninstalled.") | ||
28 | cmd:argument("version", "Version of the rock to uninstall.") | ||
29 | :args("?") | ||
26 | 30 | ||
27 | ]]..util.deps_mode_help() | 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) | ||
34 | end | ||
28 | 35 | ||
29 | --- Driver function for the "remove" command. | 36 | --- Driver function for the "remove" command. |
30 | -- @param name string: name of a rock. If a version is given, refer to | ||
31 | -- a specific version; otherwise, try to remove all versions. | ||
32 | -- @param version string: When passing a package name, a version number | ||
33 | -- may also be given. | ||
34 | -- @return boolean or (nil, string, exitcode): True if removal was | 37 | -- @return boolean or (nil, string, exitcode): True if removal was |
35 | -- successful, nil and an error message otherwise. exitcode is optionally returned. | 38 | -- successful, nil and an error message otherwise. exitcode is optionally returned. |
36 | function cmd_remove.command(flags, name, version) | 39 | function cmd_remove.command(args) |
37 | if type(name) ~= "string" then | 40 | local name = util.adjust_name_and_namespace(args.rock, args) |
38 | return nil, "Argument missing. "..util.see_help("remove") | ||
39 | end | ||
40 | |||
41 | name = util.adjust_name_and_namespace(name, flags) | ||
42 | 41 | ||
43 | local deps_mode = flags["deps-mode"] or cfg.deps_mode | 42 | local deps_mode = args.deps_mode or cfg.deps_mode |
44 | 43 | ||
45 | local ok, err = fs.check_command_permissions(flags) | 44 | local ok, err = fs.check_command_permissions(args) |
46 | if not ok then return nil, err, cmd.errorcodes.PERMISSIONDENIED end | 45 | if not ok then return nil, err, cmd.errorcodes.PERMISSIONDENIED end |
47 | 46 | ||
48 | local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$") | 47 | local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$") |
48 | local version = args.version | ||
49 | local filename = name | 49 | local filename = name |
50 | if rock_type then | 50 | if rock_type then |
51 | name, version = path.parse_name(filename) | 51 | name, version = path.parse_name(filename) |
@@ -59,12 +59,12 @@ function cmd_remove.command(flags, name, version) | |||
59 | return nil, "Could not find rock '"..name..(version and " "..version or "").."' in "..path.rocks_tree_to_string(cfg.root_dir) | 59 | return nil, "Could not find rock '"..name..(version and " "..version or "").."' in "..path.rocks_tree_to_string(cfg.root_dir) |
60 | end | 60 | end |
61 | 61 | ||
62 | local ok, err = remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"]) | 62 | local ok, err = remove.remove_search_results(results, name, deps_mode, args.force, args.force_fast) |
63 | if not ok then | 63 | if not ok then |
64 | return nil, err | 64 | return nil, err |
65 | end | 65 | end |
66 | 66 | ||
67 | writer.check_dependencies(nil, deps.get_deps_mode(flags)) | 67 | writer.check_dependencies(nil, deps.get_deps_mode(args)) |
68 | return true | 68 | return true |
69 | end | 69 | end |
70 | 70 | ||
diff --git a/src/luarocks/cmd/search.lua b/src/luarocks/cmd/search.lua index 8f4d014e..f34cf7b9 100644 --- a/src/luarocks/cmd/search.lua +++ b/src/luarocks/cmd/search.lua | |||
@@ -9,17 +9,22 @@ local search = require("luarocks.search") | |||
9 | local queries = require("luarocks.queries") | 9 | local queries = require("luarocks.queries") |
10 | local results = require("luarocks.results") | 10 | local results = require("luarocks.results") |
11 | 11 | ||
12 | cmd_search.help_summary = "Query the LuaRocks servers." | 12 | function cmd_search.add_to_parser(parser) |
13 | cmd_search.help_arguments = "[--source] [--binary] { <name> [<version>] | --all }" | 13 | local cmd = parser:command("search", "Query the LuaRocks servers.", util.see_also()) |
14 | cmd_search.help = [[ | 14 | |
15 | --source Return only rockspecs and source rocks, | 15 | cmd:argument("name", "Name of the rock to search for.") |
16 | to be used with the "build" command. | 16 | :args("?") |
17 | --binary Return only pure Lua and binary rocks (rocks that can be used | 17 | cmd:argument("version", "Rock version to search for.") |
18 | with the "install" command without requiring a C toolchain). | 18 | :args("?") |
19 | --all List all contents of the server that are suitable to | 19 | |
20 | this platform, do not filter by name. | 20 | cmd:flag("--source", "Return only rockspecs and source rocks, to be used ".. |
21 | --porcelain Return a machine readable format. | 21 | 'with the "build" command.') |
22 | ]] | 22 | cmd:flag("--binary", "Return only pure Lua and binary rocks (rocks that ".. |
23 | 'can be used with the "install" command without requiring a C toolchain).') | ||
24 | cmd:flag("--all", "List all contents of the server that are suitable to ".. | ||
25 | "this platform, do not filter by name.") | ||
26 | cmd:flag("--porcelain", "Return a machine readable format.") | ||
27 | end | ||
23 | 28 | ||
24 | --- Splits a list of search results into two lists, one for "source" results | 29 | --- Splits a list of search results into two lists, one for "source" results |
25 | -- to be used with the "build" command, and one for "binary" results to be | 30 | -- to be used with the "build" command, and one for "binary" results to be |
@@ -45,33 +50,31 @@ local function split_source_and_binary_results(result_tree) | |||
45 | end | 50 | end |
46 | 51 | ||
47 | --- Driver function for "search" command. | 52 | --- Driver function for "search" command. |
48 | -- @param name string: A substring of a rock name to search. | ||
49 | -- @param version string or nil: a version may also be passed. | ||
50 | -- @return boolean or (nil, string): True if build was successful; nil and an | 53 | -- @return boolean or (nil, string): True if build was successful; nil and an |
51 | -- error message otherwise. | 54 | -- error message otherwise. |
52 | function cmd_search.command(flags, name, version) | 55 | function cmd_search.command(args) |
53 | 56 | ||
54 | name = util.adjust_name_and_namespace(name, flags) | 57 | local name = util.adjust_name_and_namespace(args.name, args) |
55 | 58 | ||
56 | if flags["all"] then | 59 | if args.all then |
57 | name, version = "", nil | 60 | name, args.version = "", nil |
58 | end | 61 | end |
59 | 62 | ||
60 | if type(name) ~= "string" and not flags["all"] then | 63 | if not args.name and not args.all then |
61 | return nil, "Enter name and version or use --all. "..util.see_help("search") | 64 | return nil, "Enter name and version or use --all. "..util.see_help("search") |
62 | end | 65 | end |
63 | 66 | ||
64 | local query = queries.new(name:lower(), version, true) | 67 | local query = queries.new(name:lower(), args.version, true) |
65 | local result_tree, err = search.search_repos(query) | 68 | local result_tree, err = search.search_repos(query) |
66 | local porcelain = flags["porcelain"] | 69 | local porcelain = args.porcelain |
67 | local full_name = name .. (version and " " .. version or "") | 70 | local full_name = name .. (args.version and " " .. args.version or "") |
68 | util.title(full_name .. " - Search results for Lua "..cfg.lua_version..":", porcelain, "=") | 71 | util.title(full_name .. " - Search results for Lua "..cfg.lua_version..":", porcelain, "=") |
69 | local sources, binaries = split_source_and_binary_results(result_tree) | 72 | local sources, binaries = split_source_and_binary_results(result_tree) |
70 | if next(sources) and not flags["binary"] then | 73 | if next(sources) and not args.binary then |
71 | util.title("Rockspecs and source rocks:", porcelain) | 74 | util.title("Rockspecs and source rocks:", porcelain) |
72 | search.print_result_tree(sources, porcelain) | 75 | search.print_result_tree(sources, porcelain) |
73 | end | 76 | end |
74 | if next(binaries) and not flags["source"] then | 77 | if next(binaries) and not args.source then |
75 | util.title("Binary and pure-Lua rocks:", porcelain) | 78 | util.title("Binary and pure-Lua rocks:", porcelain) |
76 | search.print_result_tree(binaries, porcelain) | 79 | search.print_result_tree(binaries, porcelain) |
77 | end | 80 | end |
diff --git a/src/luarocks/cmd/show.lua b/src/luarocks/cmd/show.lua index 37c2c55e..db7aed54 100644 --- a/src/luarocks/cmd/show.lua +++ b/src/luarocks/cmd/show.lua | |||
@@ -13,24 +13,33 @@ local fetch = require("luarocks.fetch") | |||
13 | local manif = require("luarocks.manif") | 13 | local manif = require("luarocks.manif") |
14 | local repos = require("luarocks.repos") | 14 | local repos = require("luarocks.repos") |
15 | 15 | ||
16 | show.help_summary = "Show information about an installed rock." | 16 | function show.add_to_parser(parser) |
17 | local cmd = parser:command("show", [[ | ||
18 | Show information about an installed rock. | ||
17 | 19 | ||
18 | show.help = [[ | ||
19 | <argument> is an existing package name. | ||
20 | Without any flags, show all module information. | 20 | Without any flags, show all module information. |
21 | With these flags, return only the desired information: | 21 | With flags, return only the desired information.]], util.see_also()) |
22 | :summary("Show information about an installed rock.") | ||
22 | 23 | ||
23 | --home home page of project | 24 | cmd:argument("rock", "Name of an installed rock.") |
24 | --modules all modules provided by this package as used by require() | 25 | cmd:argument("version", "Rock version.") |
25 | --deps packages this package depends on | 26 | :args("?") |
26 | --build-deps build-only dependencies for this package | ||
27 | --test-deps dependencies for testing this package | ||
28 | --rockspec the full path of the rockspec file | ||
29 | --mversion the package version | ||
30 | --rock-tree local tree where rock is installed | ||
31 | --rock-dir data directory of the installed rock | ||
32 | ]] | ||
33 | 27 | ||
28 | cmd:flag("--home", "Show home page of project.") | ||
29 | cmd:flag("--modules", "Show all modules provided by the package as used by require().") | ||
30 | cmd:flag("--deps", "Show packages the package depends on.") | ||
31 | cmd:flag("--build-deps", "Show build-only dependencies for the package.") | ||
32 | cmd:flag("--test-deps", "Show dependencies for testing the package.") | ||
33 | cmd:flag("--rockspec", "Show the full path of the rockspec file.") | ||
34 | cmd:flag("--mversion", "Show the package version.") | ||
35 | cmd:flag("--rock-tree", "Show local tree where rock is installed.") | ||
36 | cmd:flag("--rock-namespace", "Show rock namespace.") | ||
37 | cmd:flag("--rock-dir", "Show data directory of the installed rock.") | ||
38 | cmd:flag("--rock-license", "Show rock license.") | ||
39 | cmd:flag("--issues", "Show URL for project's issue tracker.") | ||
40 | cmd:flag("--labels", "List the labels of the rock.") | ||
41 | cmd:flag("--porcelain", "Produce machine-friendly output.") | ||
42 | end | ||
34 | 43 | ||
35 | local friendly_template = [[ | 44 | local friendly_template = [[ |
36 | : | 45 | : |
@@ -249,19 +258,14 @@ local function show_rock(template, namespace, name, version, rockspec, repo, min | |||
249 | end | 258 | end |
250 | 259 | ||
251 | --- Driver function for "show" command. | 260 | --- Driver function for "show" command. |
252 | -- @param name or nil: an existing package name. | ||
253 | -- @param version string or nil: a version may also be passed. | ||
254 | -- @return boolean: True if succeeded, nil on errors. | 261 | -- @return boolean: True if succeeded, nil on errors. |
255 | function show.command(flags, name, version) | 262 | function show.command(args) |
256 | if not name then | 263 | local name = util.adjust_name_and_namespace(args.rock, args) |
257 | return nil, "Argument missing. "..util.see_help("show") | 264 | local version = args.version |
258 | end | ||
259 | |||
260 | name = util.adjust_name_and_namespace(name, flags) | ||
261 | local query = queries.new(name, version) | 265 | local query = queries.new(name, version) |
262 | 266 | ||
263 | local repo, repo_url | 267 | local repo, repo_url |
264 | name, version, repo, repo_url = search.pick_installed_rock(query, flags["tree"]) | 268 | name, version, repo, repo_url = search.pick_installed_rock(query, args.tree) |
265 | if not name then | 269 | if not name then |
266 | return nil, version | 270 | return nil, version |
267 | end | 271 | end |
@@ -277,32 +281,32 @@ function show.command(flags, name, version) | |||
277 | if not manifest then return nil,err end | 281 | if not manifest then return nil,err end |
278 | local minfo = manifest.repository[name][version][1] | 282 | local minfo = manifest.repository[name][version][1] |
279 | 283 | ||
280 | if flags["rock-tree"] then util.printout(tree) | 284 | if args.rock_tree then util.printout(tree) |
281 | elseif flags["rock-namespace"] then util.printout(namespace) | 285 | elseif args.rock_namespace then util.printout(namespace) |
282 | elseif flags["rock-dir"] then util.printout(directory) | 286 | elseif args.rock_dir then util.printout(directory) |
283 | elseif flags["home"] then util.printout(descript.homepage) | 287 | elseif args.home then util.printout(descript.homepage) |
284 | elseif flags["rock-license"] then util.printout(descript.license) | 288 | elseif args.rock_license then util.printout(descript.license) |
285 | elseif flags["issues"] then util.printout(descript.issues_url) | 289 | elseif args.issues then util.printout(descript.issues_url) |
286 | elseif flags["labels"] then util.printout(descript.labels and table.concat(descript.labels, "\n")) | 290 | elseif args.labels then util.printout(descript.labels and table.concat(descript.labels, "\n")) |
287 | elseif flags["modules"] then util.printout(keys_as_string(minfo.modules, "\n")) | 291 | elseif args.modules then util.printout(keys_as_string(minfo.modules, "\n")) |
288 | elseif flags["deps"] then | 292 | elseif args.deps then |
289 | for _, dep in ipairs(rockspec.dependencies) do | 293 | for _, dep in ipairs(rockspec.dependencies) do |
290 | util.printout(tostring(dep)) | 294 | util.printout(tostring(dep)) |
291 | end | 295 | end |
292 | elseif flags["build-deps"] then | 296 | elseif args.build_deps then |
293 | for _, dep in ipairs(rockspec.build_dependencies) do | 297 | for _, dep in ipairs(rockspec.build_dependencies) do |
294 | util.printout(tostring(dep)) | 298 | util.printout(tostring(dep)) |
295 | end | 299 | end |
296 | elseif flags["test-deps"] then | 300 | elseif args.test_deps then |
297 | for _, dep in ipairs(rockspec.test_dependencies) do | 301 | for _, dep in ipairs(rockspec.test_dependencies) do |
298 | util.printout(tostring(dep)) | 302 | util.printout(tostring(dep)) |
299 | end | 303 | end |
300 | elseif flags["rockspec"] then util.printout(rockspec_file) | 304 | elseif args.rockspec then util.printout(rockspec_file) |
301 | elseif flags["mversion"] then util.printout(version) | 305 | elseif args.mversion then util.printout(version) |
302 | elseif flags["porcelain"] then | 306 | elseif args.porcelain then |
303 | show_rock(porcelain_template, namespace, name, version, rockspec, repo, minfo, flags["tree"]) | 307 | show_rock(porcelain_template, namespace, name, version, rockspec, repo, minfo, args.tree) |
304 | else | 308 | else |
305 | show_rock(friendly_template, namespace, name, version, rockspec, repo, minfo, flags["tree"]) | 309 | show_rock(friendly_template, namespace, name, version, rockspec, repo, minfo, args.tree) |
306 | end | 310 | end |
307 | return true | 311 | return true |
308 | end | 312 | end |
diff --git a/src/luarocks/cmd/test.lua b/src/luarocks/cmd/test.lua index 413a029c..7a1ffda2 100644 --- a/src/luarocks/cmd/test.lua +++ b/src/luarocks/cmd/test.lua | |||
@@ -6,42 +6,42 @@ local cmd_test = {} | |||
6 | local util = require("luarocks.util") | 6 | local util = require("luarocks.util") |
7 | local test = require("luarocks.test") | 7 | local test = require("luarocks.test") |
8 | 8 | ||
9 | cmd_test.help_summary = "Run the test suite in the current directory." | 9 | function cmd_test.add_to_parser(parser) |
10 | cmd_test.help_arguments = "[--test-type=<type>] [<rockspec>] [-- <args>]" | 10 | local cmd = parser:command("test", [[ |
11 | cmd_test.help = [[ | ||
12 | Run the test suite for the Lua project in the current directory. | 11 | Run the test suite for the Lua project in the current directory. |
13 | If the first argument is a rockspec, it will use it to determine | ||
14 | the parameters for running tests; otherwise, it will attempt to | ||
15 | detect the rockspec. | ||
16 | 12 | ||
17 | Any additional arguments are forwarded to the test suite. | 13 | If the first argument is a rockspec, it will use it to determine the parameters |
18 | To make sure that any flags passed in <args> are not interpreted | 14 | for running tests; otherwise, it will attempt to detect the rockspec. |
19 | as LuaRocks flags, use -- to separate LuaRocks arguments from | ||
20 | test suite arguments. | ||
21 | 15 | ||
22 | --test-type=<type> Specify the test suite type manually if it was not | 16 | Any additional arguments are forwarded to the test suite. |
23 | specified in the rockspec and it could not be | 17 | To make sure that test suite flags are not interpreted as LuaRocks flags, use -- |
24 | auto-detected. | 18 | to separate LuaRocks arguments from test suite arguments.]], |
19 | util.see_also()) | ||
20 | :summary("Run the test suite in the current directory.") | ||
25 | 21 | ||
26 | ]]..util.deps_mode_help() | 22 | cmd:argument("rockspec", "Project rockspec.") |
23 | :args("?") | ||
24 | cmd:argument("args", "Test suite arguments.") | ||
25 | :args("*") | ||
27 | 26 | ||
28 | function cmd_test.command(flags, argument, ...) | 27 | cmd:option("--test-type", "Specify the test suite type manually if it was ".. |
29 | assert(type(argument) == "string" or not argument) | 28 | "not specified in the rockspec and it could not be auto-detected.") |
30 | 29 | :argname("<type>") | |
31 | local arguments = { ... } | 30 | end |
32 | 31 | ||
33 | if argument and argument:match("rockspec$") then | 32 | function cmd_test.command(args) |
34 | return test.run_test_suite(argument, flags["test-type"], arguments) | 33 | if args.rockspec and args.rockspec:match("rockspec$") then |
34 | return test.run_test_suite(args.rockspec, args.test_type, args.args) | ||
35 | end | 35 | end |
36 | 36 | ||
37 | table.insert(arguments, 1, argument) | 37 | table.insert(args.args, 1, args.rockspec) |
38 | 38 | ||
39 | local rockspec, err = util.get_default_rockspec() | 39 | local rockspec, err = util.get_default_rockspec() |
40 | if not rockspec then | 40 | if not rockspec then |
41 | return nil, err | 41 | return nil, err |
42 | end | 42 | end |
43 | 43 | ||
44 | return test.run_test_suite(rockspec, flags["test-type"], arguments) | 44 | return test.run_test_suite(rockspec, args.test_type, args.args) |
45 | end | 45 | end |
46 | 46 | ||
47 | return cmd_test | 47 | return cmd_test |
diff --git a/src/luarocks/cmd/unpack.lua b/src/luarocks/cmd/unpack.lua index 99263f49..fe0535e4 100644 --- a/src/luarocks/cmd/unpack.lua +++ b/src/luarocks/cmd/unpack.lua | |||
@@ -10,15 +10,20 @@ local build = require("luarocks.build") | |||
10 | local dir = require("luarocks.dir") | 10 | local dir = require("luarocks.dir") |
11 | local search = require("luarocks.search") | 11 | local search = require("luarocks.search") |
12 | 12 | ||
13 | unpack.help_summary = "Unpack the contents of a rock." | 13 | function unpack.add_to_parser(parser) |
14 | unpack.help_arguments = "[--force] {<rock>|<name> [<version>]}" | 14 | local cmd = parser:command("unpack", [[ |
15 | unpack.help = [[ | ||
16 | Unpacks the contents of a rock in a newly created directory. | 15 | Unpacks the contents of a rock in a newly created directory. |
17 | Argument may be a rock file, or the name of a rock in a rocks server. | 16 | Argument may be a rock file, or the name of a rock in a rocks server. |
18 | In the latter case, the app version may be given as a second argument. | 17 | In 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.") | ||
19 | 20 | ||
20 | --force Unpack files even if the output directory already exists. | 21 | cmd:argument("rock", "A rock file or the name of a rock.") |
21 | ]] | 22 | cmd:argument("version", "Rock version.") |
23 | :args("?") | ||
24 | |||
25 | cmd:flag("--force", "Unpack files even if the output directory already exists.") | ||
26 | end | ||
22 | 27 | ||
23 | --- Load a rockspec file to the given directory, fetches the source | 28 | --- Load a rockspec file to the given directory, fetches the source |
24 | -- files specified in the rockspec, and unpack them inside the directory. | 29 | -- files specified in the rockspec, and unpack them inside the directory. |
@@ -141,31 +146,22 @@ local function run_unpacker(file, force) | |||
141 | end | 146 | end |
142 | 147 | ||
143 | --- Driver function for the "unpack" command. | 148 | --- Driver function for the "unpack" command. |
144 | -- @param ns_name string: may be a rock filename, for unpacking a | ||
145 | -- rock file or the name of a rock to be fetched and unpacked. | ||
146 | -- @param version string or nil: if the name of a package is given, a | ||
147 | -- version may also be passed. | ||
148 | -- @return boolean or (nil, string): true if successful or nil followed | 149 | -- @return boolean or (nil, string): true if successful or nil followed |
149 | -- by an error message. | 150 | -- by an error message. |
150 | function unpack.command(flags, ns_name, version) | 151 | function unpack.command(args) |
151 | assert(type(version) == "string" or not version) | 152 | local ns_name = util.adjust_name_and_namespace(args.rock, args) |
152 | if type(ns_name) ~= "string" then | ||
153 | return nil, "Argument missing. "..util.see_help("unpack") | ||
154 | end | ||
155 | |||
156 | ns_name = util.adjust_name_and_namespace(ns_name, flags) | ||
157 | 153 | ||
158 | local url, err | 154 | local url, err |
159 | if ns_name:match(".*%.rock") or ns_name:match(".*%.rockspec") then | 155 | if ns_name:match(".*%.rock") or ns_name:match(".*%.rockspec") then |
160 | url = ns_name | 156 | url = ns_name |
161 | else | 157 | else |
162 | url, err = search.find_src_or_rockspec(ns_name, version, true) | 158 | url, err = search.find_src_or_rockspec(ns_name, args.version, true) |
163 | if not url then | 159 | if not url then |
164 | return nil, err | 160 | return nil, err |
165 | end | 161 | end |
166 | end | 162 | end |
167 | 163 | ||
168 | return run_unpacker(url, flags["force"]) | 164 | return run_unpacker(url, args.force) |
169 | end | 165 | end |
170 | 166 | ||
171 | return unpack | 167 | return unpack |
diff --git a/src/luarocks/cmd/upload.lua b/src/luarocks/cmd/upload.lua index b052500e..6e3877ba 100644 --- a/src/luarocks/cmd/upload.lua +++ b/src/luarocks/cmd/upload.lua | |||
@@ -8,37 +8,33 @@ local pack = require("luarocks.pack") | |||
8 | local cfg = require("luarocks.core.cfg") | 8 | local cfg = require("luarocks.core.cfg") |
9 | local Api = require("luarocks.upload.api") | 9 | local Api = require("luarocks.upload.api") |
10 | 10 | ||
11 | upload.help_summary = "Upload a rockspec to the public rocks repository." | 11 | function upload.add_to_parser(parser) |
12 | upload.help_arguments = "[--skip-pack] [--api-key=<key>] [--force] <rockspec>" | 12 | local cmd = parser:command("upload", "Pack a source rock file (.src.rock extension) ".. |
13 | upload.help = [[ | 13 | "and upload it and the rockspec to the public rocks repository.", util.see_also()) |
14 | <rockspec> Pack a source rock file (.src.rock extension), | 14 | :summary("Upload a rockspec to the public rocks repository.") |
15 | upload rockspec and source rock to server. | 15 | |
16 | 16 | cmd:argument("rockspec", "Rockspec for the rock to upload.") | |
17 | --skip-pack Do not pack and send source rock. | 17 | |
18 | 18 | cmd:flag("--skip-pack", "Do not pack and send source rock.") | |
19 | --api-key=<key> Give it an API key. It will be stored for subsequent uses. | 19 | cmd:option("--api-key", "Pass an API key. It will be stored for subsequent uses.") |
20 | 20 | :argname("<key>") | |
21 | --temp-key=<key> Use the given a temporary API key in this invocation only. | 21 | cmd:option("--temp-key", "Use the given a temporary API key in this ".. |
22 | It will not be stored. | 22 | "invocation only. It will not be stored.") |
23 | 23 | :argname("<key>") | |
24 | --force Replace existing rockspec if the same revision of | 24 | cmd:flag("--force", "Replace existing rockspec if the same revision of a ".. |
25 | a module already exists. This should be used only | 25 | "module already exists. This should be used only in case of upload ".. |
26 | in case of upload mistakes: when updating a rockspec, | 26 | "mistakes: when updating a rockspec, increment the revision number ".. |
27 | increment the revision number instead. | 27 | "instead.") |
28 | 28 | cmd:flag("--sign", "Upload a signature file alongside each file as well.") | |
29 | --sign Upload a signature file alongside each file as well. | 29 | cmd:flag("--debug"):hidden(true) |
30 | ]] | 30 | end |
31 | 31 | ||
32 | local function is_dev_version(version) | 32 | local function is_dev_version(version) |
33 | return version:match("^dev") or version:match("^scm") | 33 | return version:match("^dev") or version:match("^scm") |
34 | end | 34 | end |
35 | 35 | ||
36 | function upload.command(flags, fname) | 36 | function upload.command(args) |
37 | if not fname then | 37 | local api, err = Api.new(args) |
38 | return nil, "Missing rockspec. "..util.see_help("upload") | ||
39 | end | ||
40 | |||
41 | local api, err = Api.new(flags) | ||
42 | if not api then | 38 | if not api then |
43 | return nil, err | 39 | return nil, err |
44 | end | 40 | end |
@@ -46,12 +42,12 @@ function upload.command(flags, fname) | |||
46 | api.debug = true | 42 | api.debug = true |
47 | end | 43 | end |
48 | 44 | ||
49 | local rockspec, err, errcode = fetch.load_rockspec(fname) | 45 | local rockspec, err, errcode = fetch.load_rockspec(args.rockspec) |
50 | if err then | 46 | if err then |
51 | return nil, err, errcode | 47 | return nil, err, errcode |
52 | end | 48 | end |
53 | 49 | ||
54 | util.printout("Sending " .. tostring(fname) .. " ...") | 50 | util.printout("Sending " .. tostring(args.rockspec) .. " ...") |
55 | local res, err = api:method("check_rockspec", { | 51 | local res, err = api:method("check_rockspec", { |
56 | package = rockspec.package, | 52 | package = rockspec.package, |
57 | version = rockspec.version | 53 | version = rockspec.version |
@@ -61,15 +57,15 @@ function upload.command(flags, fname) | |||
61 | if not res.module then | 57 | if not res.module then |
62 | util.printout("Will create new module (" .. tostring(rockspec.package) .. ")") | 58 | util.printout("Will create new module (" .. tostring(rockspec.package) .. ")") |
63 | end | 59 | end |
64 | if res.version and not flags["force"] then | 60 | if res.version and not args.force then |
65 | return nil, "Revision "..rockspec.version.." already exists on the server. "..util.see_help("upload") | 61 | return nil, "Revision "..rockspec.version.." already exists on the server. "..util.see_help("upload") |
66 | end | 62 | end |
67 | 63 | ||
68 | local sigfname | 64 | local sigfname |
69 | local rock_sigfname | 65 | local rock_sigfname |
70 | 66 | ||
71 | if flags["sign"] then | 67 | if args.sign then |
72 | sigfname, err = signing.sign_file(fname) | 68 | sigfname, err = signing.sign_file(args.rockspec) |
73 | if err then | 69 | if err then |
74 | return nil, "Failed signing rockspec: " .. err | 70 | return nil, "Failed signing rockspec: " .. err |
75 | end | 71 | end |
@@ -77,13 +73,13 @@ function upload.command(flags, fname) | |||
77 | end | 73 | end |
78 | 74 | ||
79 | local rock_fname | 75 | local rock_fname |
80 | if not flags["skip-pack"] and not is_dev_version(rockspec.version) then | 76 | if not args.skip_pack and not is_dev_version(rockspec.version) then |
81 | util.printout("Packing " .. tostring(rockspec.package)) | 77 | util.printout("Packing " .. tostring(rockspec.package)) |
82 | rock_fname, err = pack.pack_source_rock(fname) | 78 | rock_fname, err = pack.pack_source_rock(args.rockspec) |
83 | if not rock_fname then | 79 | if not rock_fname then |
84 | return nil, err | 80 | return nil, err |
85 | end | 81 | end |
86 | if flags["sign"] then | 82 | if args.sign then |
87 | rock_sigfname, err = signing.sign_file(rock_fname) | 83 | rock_sigfname, err = signing.sign_file(rock_fname) |
88 | if err then | 84 | if err then |
89 | return nil, "Failed signing rock: " .. err | 85 | return nil, "Failed signing rock: " .. err |
@@ -95,7 +91,7 @@ function upload.command(flags, fname) | |||
95 | local multipart = require("luarocks.upload.multipart") | 91 | local multipart = require("luarocks.upload.multipart") |
96 | 92 | ||
97 | res, err = api:method("upload", nil, { | 93 | res, err = api:method("upload", nil, { |
98 | rockspec_file = multipart.new_file(fname), | 94 | rockspec_file = multipart.new_file(args.rockspec), |
99 | rockspec_sig = sigfname and multipart.new_file(sigfname), | 95 | rockspec_sig = sigfname and multipart.new_file(sigfname), |
100 | }) | 96 | }) |
101 | if not res then return nil, err end | 97 | if not res then return nil, err end |
diff --git a/src/luarocks/cmd/which.lua b/src/luarocks/cmd/which.lua index 56db5254..9a363e85 100644 --- a/src/luarocks/cmd/which.lua +++ b/src/luarocks/cmd/which.lua | |||
@@ -8,20 +8,20 @@ local cfg = require("luarocks.core.cfg") | |||
8 | local util = require("luarocks.util") | 8 | local util = require("luarocks.util") |
9 | local fs = require("luarocks.fs") | 9 | local fs = require("luarocks.fs") |
10 | 10 | ||
11 | which_cmd.help_summary = "Tell which file corresponds to a given module name." | 11 | function which_cmd.add_to_parser(parser) |
12 | which_cmd.help_arguments = "<modname>" | 12 | local cmd = parser:command("which", 'Given a module name like "foo.bar", '.. |
13 | which_cmd.help = [[ | 13 | "output which file would be loaded to resolve that module by ".. |
14 | Given a module name like "foo.bar", output which file would be loaded to resolve | 14 | 'luarocks.loader, like "/usr/local/lua/'..cfg.lua_version..'/foo/bar.lua".', |
15 | that module by luarocks.loader, like "/usr/local/lua/]]..cfg.lua_version..[[/foo/bar.lua". | 15 | util.see_also()) |
16 | ]] | 16 | :summary("Tell which file corresponds to a given module name.") |
17 | 17 | ||
18 | --- Driver function for "lua" command. | 18 | cmd:argument("modname", "Module name.") |
19 | end | ||
20 | |||
21 | --- Driver function for "which" command. | ||
19 | -- @return boolean This function terminates the interpreter. | 22 | -- @return boolean This function terminates the interpreter. |
20 | function which_cmd.command(_, modname) | 23 | function which_cmd.command(args) |
21 | if modname == nil then | 24 | local pathname, rock_name, rock_version = loader.which(args.modname) |
22 | return nil, "Missing module name. " .. util.see_help("which") | ||
23 | end | ||
24 | local pathname, rock_name, rock_version = loader.which(modname) | ||
25 | 25 | ||
26 | if pathname then | 26 | if pathname then |
27 | util.printout(pathname) | 27 | util.printout(pathname) |
@@ -29,7 +29,7 @@ function which_cmd.command(_, modname) | |||
29 | return true | 29 | return true |
30 | end | 30 | end |
31 | 31 | ||
32 | local modpath = modname:gsub("%.", "/") | 32 | local modpath = args.modname:gsub("%.", "/") |
33 | for _, v in ipairs({"path", "cpath"}) do | 33 | for _, v in ipairs({"path", "cpath"}) do |
34 | for p in package[v]:gmatch("([^;]+)") do | 34 | for p in package[v]:gmatch("([^;]+)") do |
35 | local pathname = p:gsub("%?", modpath) | 35 | local pathname = p:gsub("%?", modpath) |
@@ -41,7 +41,7 @@ function which_cmd.command(_, modname) | |||
41 | end | 41 | end |
42 | end | 42 | end |
43 | 43 | ||
44 | return nil, "Module '" .. modname .. "' not found." | 44 | return nil, "Module '" .. args.modname .. "' not found." |
45 | end | 45 | end |
46 | 46 | ||
47 | return which_cmd | 47 | return which_cmd |
diff --git a/src/luarocks/cmd/write_rockspec.lua b/src/luarocks/cmd/write_rockspec.lua index aad91910..ee825ce4 100644 --- a/src/luarocks/cmd/write_rockspec.lua +++ b/src/luarocks/cmd/write_rockspec.lua | |||
@@ -11,36 +11,69 @@ local rockspecs = require("luarocks.rockspecs") | |||
11 | local type_rockspec = require("luarocks.type.rockspec") | 11 | local type_rockspec = require("luarocks.type.rockspec") |
12 | local util = require("luarocks.util") | 12 | local util = require("luarocks.util") |
13 | 13 | ||
14 | write_rockspec.help_summary = "Write a template for a rockspec file." | 14 | local lua_versions = { |
15 | write_rockspec.help_arguments = "[--output=<file> ...] [<name>] [<version>] [<url>|<path>]" | 15 | "5.1", |
16 | write_rockspec.help = [[ | 16 | "5.2", |
17 | "5.3", | ||
18 | "5.4", | ||
19 | "5.1,5.2", | ||
20 | "5.2,5.3", | ||
21 | "5.3,5.4", | ||
22 | "5.1,5.2,5.3", | ||
23 | "5.2,5.3,5.4", | ||
24 | "5.1,5.2,5.3,5.4" | ||
25 | } | ||
26 | |||
27 | function write_rockspec.cmd_options(parser) | ||
28 | return parser:option("--output", "Write the rockspec with the given filename.\n".. | ||
29 | "If not given, a file is written in the current directory with a ".. | ||
30 | "filename based on given name and version.") | ||
31 | :argname("<file>"), | ||
32 | parser:option("--license", 'A license string, such as "MIT/X11" or "GNU GPL v3".') | ||
33 | :argname("<string>"), | ||
34 | parser:option("--summary", "A short one-line description summary.") | ||
35 | :argname("<txt>"), | ||
36 | parser:option("--detailed", "A longer description string.") | ||
37 | :argname("<txt>"), | ||
38 | parser:option("--homepage", "Project homepage.") | ||
39 | :argname("<txt>"), | ||
40 | parser:option("--lua-versions", 'Supported Lua versions. Accepted values are: "'.. | ||
41 | table.concat(lua_versions, '", "')..'".') | ||
42 | :argname("<ver>") | ||
43 | :choices(lua_versions), | ||
44 | parser:option("--rockspec-format", 'Rockspec format version, such as "1.0" or "1.1".') | ||
45 | :argname("<ver>"), | ||
46 | parser:option("--tag", "Tag to use. Will attempt to extract version number from it."), | ||
47 | parser:option("--lib", "A comma-separated list of libraries that C files need to link to.") | ||
48 | :argname("<libs>") | ||
49 | end | ||
50 | |||
51 | function write_rockspec.add_to_parser(parser) | ||
52 | local cmd = parser:command("write_rockspec", [[ | ||
17 | This command writes an initial version of a rockspec file, | 53 | This command writes an initial version of a rockspec file, |
18 | based on a name, a version, and a location (an URL or a local path). | 54 | based on a name, a version, and a location (an URL or a local path). |
19 | If only two arguments are given, the first one is considered the name and the | 55 | If only two arguments are given, the first one is considered the name and the |
20 | second one is the location. | 56 | second one is the location. |
21 | If only one argument is given, it must be the location. | 57 | If only one argument is given, it must be the location. |
22 | If no arguments are given, current directory is used as location. | 58 | If no arguments are given, current directory is used as the location. |
23 | LuaRocks will attempt to infer name and version if not given, | 59 | LuaRocks will attempt to infer name and version if not given, |
24 | using 'dev' as a fallback default version. | 60 | using 'dev' as a fallback default version. |
25 | 61 | ||
26 | Note that the generated file is a _starting point_ for writing a | 62 | Note that the generated file is a _starting point_ for writing a |
27 | rockspec, and is not guaranteed to be complete or correct. | 63 | rockspec, and is not guaranteed to be complete or correct. ]], util.see_also()) |
28 | 64 | :summary("Write a template for a rockspec file.") | |
29 | --output=<file> Write the rockspec with the given filename. | 65 | |
30 | If not given, a file is written in the current | 66 | parser:command("write-rockspec"):hidden(true):action(function(args) args.command = "write_rockspec" end) |
31 | directory with a filename based on given name and version. | 67 | |
32 | --license="<string>" A license string, such as "MIT/X11" or "GNU GPL v3". | 68 | cmd:argument("name", "Name of the rock.") |
33 | --summary="<txt>" A short one-line description summary. | 69 | :args("?") |
34 | --detailed="<txt>" A longer description string. | 70 | cmd:argument("version", "Rock version.") |
35 | --homepage=<url> Project homepage. | 71 | :args("?") |
36 | --lua-versions=<ver> Supported Lua versions. Accepted values are: "5.1", "5.2", | 72 | cmd:argument("location", "URL or path to the rock sources.") |
37 | "5.3", "5.4", "5.1,5.2", "5.2,5.3", "5.3,5.4", "5.1,5.2,5.3", | 73 | :args("?") |
38 | "5.2,5.3,5.4", or "5.1,5.2,5.3,5.4" | 74 | |
39 | --rockspec-format=<ver> Rockspec format version, such as "1.0" or "1.1". | 75 | write_rockspec.cmd_options(cmd) |
40 | --tag=<tag> Tag to use. Will attempt to extract version number from it. | 76 | end |
41 | --lib=<lib>[,<lib>] A comma-separated list of libraries that C files need to | ||
42 | link to. | ||
43 | ]] | ||
44 | 77 | ||
45 | local function open_file(name) | 78 | local function open_file(name) |
46 | return io.open(dir.path(fs.current_dir(), name), "r") | 79 | return io.open(dir.path(fs.current_dir(), name), "r") |
@@ -229,40 +262,42 @@ local function rockspec_cleanup(rockspec) | |||
229 | end | 262 | end |
230 | end | 263 | end |
231 | 264 | ||
232 | function write_rockspec.command(flags, name, version, url_or_dir) | 265 | function write_rockspec.command(args) |
233 | 266 | ||
234 | name = util.adjust_name_and_namespace(name, flags) | 267 | local name = util.adjust_name_and_namespace(args.name, args) |
268 | local version = args.version | ||
269 | local location = args.location | ||
235 | 270 | ||
236 | if not name then | 271 | if not name then |
237 | url_or_dir = "." | 272 | location = "." |
238 | elseif not version then | 273 | elseif not version then |
239 | url_or_dir = name | 274 | location = name |
240 | name = nil | 275 | name = nil |
241 | elseif not url_or_dir then | 276 | elseif not location then |
242 | url_or_dir = version | 277 | location = version |
243 | version = nil | 278 | version = nil |
244 | end | 279 | end |
245 | 280 | ||
246 | if flags["tag"] then | 281 | if args.tag then |
247 | if not version then | 282 | if not version then |
248 | version = flags["tag"]:gsub("^v", "") | 283 | version = args.tag:gsub("^v", "") |
249 | end | 284 | end |
250 | end | 285 | end |
251 | 286 | ||
252 | local protocol, pathname = dir.split_url(url_or_dir) | 287 | local protocol, pathname = dir.split_url(location) |
253 | if protocol == "file" then | 288 | if protocol == "file" then |
254 | if pathname == "." then | 289 | if pathname == "." then |
255 | name = name or dir.base_name(fs.current_dir()) | 290 | name = name or dir.base_name(fs.current_dir()) |
256 | end | 291 | end |
257 | elseif dir.is_basic_protocol(protocol) then | 292 | elseif dir.is_basic_protocol(protocol) then |
258 | local filename = dir.base_name(url_or_dir) | 293 | local filename = dir.base_name(location) |
259 | local newname, newversion = filename:match("(.*)-([^-]+)") | 294 | local newname, newversion = filename:match("(.*)-([^-]+)") |
260 | if newname then | 295 | if newname then |
261 | name = name or newname | 296 | name = name or newname |
262 | version = version or newversion:gsub("%.[a-z]+$", ""):gsub("%.tar$", "") | 297 | version = version or newversion:gsub("%.[a-z]+$", ""):gsub("%.tar$", "") |
263 | end | 298 | end |
264 | else | 299 | else |
265 | name = name or dir.base_name(url_or_dir):gsub("%.[^.]+$", "") | 300 | name = name or dir.base_name(location):gsub("%.[^.]+$", "") |
266 | end | 301 | end |
267 | 302 | ||
268 | if not name then | 303 | if not name then |
@@ -270,27 +305,27 @@ function write_rockspec.command(flags, name, version, url_or_dir) | |||
270 | end | 305 | end |
271 | version = version or "dev" | 306 | version = version or "dev" |
272 | 307 | ||
273 | local filename = flags["output"] or dir.path(fs.current_dir(), name:lower().."-"..version.."-1.rockspec") | 308 | local filename = args.output or dir.path(fs.current_dir(), name:lower().."-"..version.."-1.rockspec") |
274 | 309 | ||
275 | local url = detect_url(url_or_dir) | 310 | local url = detect_url(location) |
276 | local homepage = detect_homepage(url, flags["homepage"]) | 311 | local homepage = detect_homepage(url, args.homepage) |
277 | 312 | ||
278 | local rockspec, err = rockspecs.from_persisted_table(filename, { | 313 | local rockspec, err = rockspecs.from_persisted_table(filename, { |
279 | rockspec_format = flags["rockspec-format"], | 314 | rockspec_format = args.rockspec_format, |
280 | package = name, | 315 | package = name, |
281 | version = version.."-1", | 316 | version = version.."-1", |
282 | source = { | 317 | source = { |
283 | url = url, | 318 | url = url, |
284 | tag = flags["tag"], | 319 | tag = args.tag, |
285 | }, | 320 | }, |
286 | description = { | 321 | description = { |
287 | summary = flags["summary"] or "*** please specify description summary ***", | 322 | summary = args.summary or "*** please specify description summary ***", |
288 | detailed = flags["detailed"] or "*** please enter a detailed description ***", | 323 | detailed = args.detailed or "*** please enter a detailed description ***", |
289 | homepage = homepage, | 324 | homepage = homepage, |
290 | license = flags["license"] or "*** please specify a license ***", | 325 | license = args.license or "*** please specify a license ***", |
291 | }, | 326 | }, |
292 | dependencies = { | 327 | dependencies = { |
293 | lua_version_dep[flags["lua-versions"]], | 328 | lua_version_dep[args.lua_versions], |
294 | }, | 329 | }, |
295 | build = {}, | 330 | build = {}, |
296 | }) | 331 | }) |
@@ -301,19 +336,19 @@ function write_rockspec.command(flags, name, version, url_or_dir) | |||
301 | util.warning("Please specify supported Lua versions with --lua-versions=<ver>. "..util.see_help("write_rockspec")) | 336 | util.warning("Please specify supported Lua versions with --lua-versions=<ver>. "..util.see_help("write_rockspec")) |
302 | end | 337 | end |
303 | 338 | ||
304 | local local_dir = url_or_dir | 339 | local local_dir = location |
305 | 340 | ||
306 | if url_or_dir:match("://") then | 341 | if location:match("://") then |
307 | rockspec.source.file = dir.base_name(url_or_dir) | 342 | rockspec.source.file = dir.base_name(location) |
308 | if not dir.is_basic_protocol(rockspec.source.protocol) then | 343 | if not dir.is_basic_protocol(rockspec.source.protocol) then |
309 | if version ~= "dev" then | 344 | if version ~= "dev" then |
310 | rockspec.source.tag = flags["tag"] or "v" .. version | 345 | rockspec.source.tag = args.tag or "v" .. version |
311 | end | 346 | end |
312 | end | 347 | end |
313 | rockspec.source.dir = nil | 348 | rockspec.source.dir = nil |
314 | local ok, base_dir, temp_dir = fetch_url(rockspec) | 349 | local ok, base_dir, temp_dir = fetch_url(rockspec) |
315 | if ok then | 350 | if ok then |
316 | if base_dir ~= dir.base_name(url_or_dir) then | 351 | if base_dir ~= dir.base_name(location) then |
317 | rockspec.source.dir = base_dir | 352 | rockspec.source.dir = base_dir |
318 | end | 353 | end |
319 | end | 354 | end |
@@ -329,10 +364,10 @@ function write_rockspec.command(flags, name, version, url_or_dir) | |||
329 | end | 364 | end |
330 | 365 | ||
331 | local libs = nil | 366 | local libs = nil |
332 | if flags["lib"] then | 367 | if args.lib then |
333 | libs = {} | 368 | libs = {} |
334 | rockspec.external_dependencies = {} | 369 | rockspec.external_dependencies = {} |
335 | for lib in flags["lib"]:gmatch("([^,]+)") do | 370 | for lib in args.lib:gmatch("([^,]+)") do |
336 | table.insert(libs, lib) | 371 | table.insert(libs, lib) |
337 | rockspec.external_dependencies[lib:upper()] = { | 372 | rockspec.external_dependencies[lib:upper()] = { |
338 | library = lib | 373 | library = lib |
@@ -343,13 +378,13 @@ function write_rockspec.command(flags, name, version, url_or_dir) | |||
343 | local ok, err = fs.change_dir(local_dir) | 378 | local ok, err = fs.change_dir(local_dir) |
344 | if not ok then return nil, "Failed reaching files from project - error entering directory "..local_dir end | 379 | if not ok then return nil, "Failed reaching files from project - error entering directory "..local_dir end |
345 | 380 | ||
346 | if not (flags["summary"] and flags["detailed"]) then | 381 | if not (args.summary and args.detailed) then |
347 | local summary, detailed = detect_description() | 382 | local summary, detailed = detect_description() |
348 | rockspec.description.summary = flags["summary"] or summary | 383 | rockspec.description.summary = args.summary or summary |
349 | rockspec.description.detailed = flags["detailed"] or detailed | 384 | rockspec.description.detailed = args.detailed or detailed |
350 | end | 385 | end |
351 | 386 | ||
352 | if not flags["license"] then | 387 | if not args.license then |
353 | local license, fulltext = check_license() | 388 | local license, fulltext = check_license() |
354 | if license then | 389 | if license then |
355 | rockspec.description.license = license | 390 | rockspec.description.license = license |
diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua index 9ee21772..cb85764e 100644 --- a/src/luarocks/deps.lua +++ b/src/luarocks/deps.lua | |||
@@ -170,12 +170,13 @@ function deps.fulfill_dependency(dep, deps_mode, name, version, rocks_provided, | |||
170 | return nil, "Could not satisfy dependency "..tostring(dep)..": "..search_err | 170 | return nil, "Could not satisfy dependency "..tostring(dep)..": "..search_err |
171 | end | 171 | end |
172 | util.printout("Installing "..url) | 172 | util.printout("Installing "..url) |
173 | local install_flags = { | 173 | local install_args = { |
174 | rock = url, | ||
174 | deps_mode = deps_mode, | 175 | deps_mode = deps_mode, |
175 | namespace = dep.namespace, | 176 | namespace = dep.namespace, |
176 | verify = verify, | 177 | verify = verify, |
177 | } | 178 | } |
178 | local ok, install_err, errcode = install.command(install_flags, url) | 179 | local ok, install_err, errcode = install.command(install_args) |
179 | if not ok then | 180 | if not ok then |
180 | return nil, "Failed installing dependency: "..url.." - "..install_err, errcode | 181 | return nil, "Failed installing dependency: "..url.." - "..install_err, errcode |
181 | end | 182 | end |
@@ -569,23 +570,8 @@ function deps.check_lua_libdir(vars) | |||
569 | return nil, "Failed finding Lua library. You may need to configure LUA_LIBDIR.", "dependency" | 570 | return nil, "Failed finding Lua library. You may need to configure LUA_LIBDIR.", "dependency" |
570 | end | 571 | end |
571 | 572 | ||
572 | local valid_deps_modes = { | 573 | function deps.get_deps_mode(args) |
573 | one = true, | 574 | return args.deps_mode or cfg.deps_mode |
574 | order = true, | ||
575 | all = true, | ||
576 | none = true, | ||
577 | } | ||
578 | |||
579 | function deps.check_deps_mode_flag(flag) | ||
580 | return valid_deps_modes[flag] | ||
581 | end | ||
582 | |||
583 | function deps.get_deps_mode(flags) | ||
584 | if flags["deps-mode"] then | ||
585 | return flags["deps-mode"] | ||
586 | else | ||
587 | return cfg.deps_mode | ||
588 | end | ||
589 | end | 575 | end |
590 | 576 | ||
591 | return deps | 577 | return deps |
diff --git a/src/luarocks/fs/lua.lua b/src/luarocks/fs/lua.lua index 56583676..6028a925 100644 --- a/src/luarocks/fs/lua.lua +++ b/src/luarocks/fs/lua.lua | |||
@@ -1092,10 +1092,10 @@ end | |||
1092 | 1092 | ||
1093 | --- Check if user has write permissions for the command. | 1093 | --- Check if user has write permissions for the command. |
1094 | -- Assumes the configuration variables under cfg have been previously set up. | 1094 | -- Assumes the configuration variables under cfg have been previously set up. |
1095 | -- @param flags table: the flags table passed to run() drivers. | 1095 | -- @param args table: the args table passed to run() drivers. |
1096 | -- @return boolean or (boolean, string): true on success, false on failure, | 1096 | -- @return boolean or (boolean, string): true on success, false on failure, |
1097 | -- plus an error message. | 1097 | -- plus an error message. |
1098 | function fs_lua.check_command_permissions(flags) | 1098 | function fs_lua.check_command_permissions(args) |
1099 | local ok = true | 1099 | local ok = true |
1100 | local err = "" | 1100 | local err = "" |
1101 | for _, directory in ipairs { cfg.rocks_dir, cfg.deploy_lua_dir, cfg.deploy_bin_dir, cfg.deploy_lua_dir } do | 1101 | for _, directory in ipairs { cfg.rocks_dir, cfg.deploy_lua_dir, cfg.deploy_bin_dir, cfg.deploy_lua_dir } do |
@@ -1124,7 +1124,7 @@ function fs_lua.check_command_permissions(flags) | |||
1124 | if ok then | 1124 | if ok then |
1125 | return true | 1125 | return true |
1126 | else | 1126 | else |
1127 | if flags["local"] or cfg.local_by_default then | 1127 | if args["local"] or cfg.local_by_default then |
1128 | err = err .. " \n-- please check your permissions." | 1128 | err = err .. " \n-- please check your permissions." |
1129 | else | 1129 | else |
1130 | err = err .. " \n-- you may want to run as a privileged user or use your local tree with --local." | 1130 | err = err .. " \n-- you may want to run as a privileged user or use your local tree with --local." |
diff --git a/src/luarocks/manif/writer.lua b/src/luarocks/manif/writer.lua index 23ba2532..d6b70a5f 100644 --- a/src/luarocks/manif/writer.lua +++ b/src/luarocks/manif/writer.lua | |||
@@ -279,7 +279,7 @@ function writer.make_rock_manifest(name, version) | |||
279 | end | 279 | end |
280 | 280 | ||
281 | -- Writes a 'rock_namespace' file in a locally installed rock directory. | 281 | -- Writes a 'rock_namespace' file in a locally installed rock directory. |
282 | -- @param name string: the rock name (may be in user/rock format) | 282 | -- @param name string: the rock name, without a namespace |
283 | -- @param version string: the rock version | 283 | -- @param version string: the rock version |
284 | -- @param namespace string?: the namespace | 284 | -- @param namespace string?: the namespace |
285 | -- @return true if successful (or unnecessary, if there is no namespace), | 285 | -- @return true if successful (or unnecessary, if there is no namespace), |
@@ -288,8 +288,6 @@ function writer.make_namespace_file(name, version, namespace) | |||
288 | assert(type(name) == "string" and not name:match("/")) | 288 | assert(type(name) == "string" and not name:match("/")) |
289 | assert(type(version) == "string") | 289 | assert(type(version) == "string") |
290 | assert(type(namespace) == "string" or not namespace) | 290 | assert(type(namespace) == "string" or not namespace) |
291 | name = util.adjust_name_and_namespace(name, { namespace = namespace }) | ||
292 | name, namespace = util.split_namespace(name) | ||
293 | if not namespace then | 291 | if not namespace then |
294 | return true | 292 | return true |
295 | end | 293 | end |
diff --git a/src/luarocks/upload/api.lua b/src/luarocks/upload/api.lua index 0e71432f..a28b517a 100644 --- a/src/luarocks/upload/api.lua +++ b/src/luarocks/upload/api.lua | |||
@@ -245,20 +245,20 @@ end | |||
245 | 245 | ||
246 | end | 246 | end |
247 | 247 | ||
248 | function api.new(flags) | 248 | function api.new(args) |
249 | local self = {} | 249 | local self = {} |
250 | setmetatable(self, { __index = Api }) | 250 | setmetatable(self, { __index = Api }) |
251 | self.config = self:load_config() or {} | 251 | self.config = self:load_config() or {} |
252 | self.config.server = flags["server"] or self.config.server or cfg.upload.server | 252 | self.config.server = args.server or self.config.server or cfg.upload.server |
253 | self.config.version = self.config.version or cfg.upload.version | 253 | self.config.version = self.config.version or cfg.upload.version |
254 | self.config.key = flags["temp-key"] or flags["api-key"] or self.config.key | 254 | self.config.key = args.temp_key or args.api_key or self.config.key |
255 | self.debug = flags["debug"] | 255 | self.debug = args.debug |
256 | if not self.config.key then | 256 | if not self.config.key then |
257 | return nil, "You need an API key to upload rocks.\n" .. | 257 | return nil, "You need an API key to upload rocks.\n" .. |
258 | "Navigate to "..self.config.server.."/settings to get a key\n" .. | 258 | "Navigate to "..self.config.server.."/settings to get a key\n" .. |
259 | "and then pass it through the --api-key=<key> flag." | 259 | "and then pass it through the --api-key=<key> flag." |
260 | end | 260 | end |
261 | if flags["api-key"] then | 261 | if args.api_key then |
262 | self:save_config() | 262 | self:save_config() |
263 | end | 263 | end |
264 | return self | 264 | return self |
diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua index 8b84df4d..d4461c92 100644 --- a/src/luarocks/util.lua +++ b/src/luarocks/util.lua | |||
@@ -77,162 +77,6 @@ function util.matchquote(s) | |||
77 | return (s:gsub("[?%-+*%[%].%%()$^]","%%%1")) | 77 | return (s:gsub("[?%-+*%[%].%%()$^]","%%%1")) |
78 | end | 78 | end |
79 | 79 | ||
80 | --- List of supported arguments. | ||
81 | -- Arguments that take no parameters are marked with the boolean true. | ||
82 | -- Arguments that take a parameter are marked with a descriptive string. | ||
83 | -- Arguments that may take an empty string are described in quotes, | ||
84 | -- (as in the value for --detailed="<text>"). | ||
85 | -- For all other string values, it means the parameter is mandatory. | ||
86 | local supported_flags = { | ||
87 | ["all"] = true, | ||
88 | ["api-key"] = "<key>", | ||
89 | ["append"] = true, | ||
90 | ["arch"] = "<arch>", | ||
91 | ["bin"] = true, | ||
92 | ["binary"] = true, | ||
93 | ["branch"] = "<branch-name>", | ||
94 | ["build-deps"] = true, | ||
95 | ["debug"] = true, | ||
96 | ["deps"] = true, | ||
97 | ["deps-mode"] = "<mode>", | ||
98 | ["detailed"] = "\"<text>\"", | ||
99 | ["dev"] = true, | ||
100 | ["dir"] = "<path>", | ||
101 | ["force"] = true, | ||
102 | ["force-fast"] = true, | ||
103 | ["from"] = "<server>", | ||
104 | ["global"] = true, | ||
105 | ["help"] = true, | ||
106 | ["home"] = true, | ||
107 | ["homepage"] = "\"<url>\"", | ||
108 | ["index"] = true, | ||
109 | ["issues"] = true, | ||
110 | ["json"] = true, | ||
111 | ["keep"] = true, | ||
112 | ["labels"] = true, | ||
113 | ["lib"] = "<library>", | ||
114 | ["license"] = "\"<text>\"", | ||
115 | ["list"] = true, | ||
116 | ["local"] = true, | ||
117 | ["local-tree"] = true, | ||
118 | ["lr-bin"] = true, | ||
119 | ["lr-cpath"] = true, | ||
120 | ["lr-path"] = true, | ||
121 | ["lua-dir"] = "<path>", | ||
122 | ["lua-version"] = "<vers>", | ||
123 | ["lua-versions"] = "<versions>", | ||
124 | ["lua-ver"] = true, | ||
125 | ["lua-incdir"] = true, | ||
126 | ["lua-libdir"] = true, | ||
127 | ["modules"] = true, | ||
128 | ["mversion"] = true, | ||
129 | ["namespace"] = "<namespace>", | ||
130 | ["no-bin"] = true, | ||
131 | ["no-doc"] = true, | ||
132 | ["no-refresh"] = true, | ||
133 | ["nodeps"] = true, | ||
134 | ["old-versions"] = true, | ||
135 | ["only-deps"] = true, | ||
136 | ["only-from"] = "<server>", | ||
137 | ["only-server"] = "<server>", | ||
138 | ["only-sources"] = "<url>", | ||
139 | ["only-sources-from"] = "<url>", | ||
140 | ["outdated"] = true, | ||
141 | ["output"] = "<file>", | ||
142 | ["pack-binary-rock"] = true, | ||
143 | ["porcelain"] = true, | ||
144 | ["project-tree"] = "<tree>", | ||
145 | ["quick"] = true, | ||
146 | ["reset"] = true, | ||
147 | ["rock-dir"] = true, | ||
148 | ["rock-license"] = true, | ||
149 | ["rock-namespace"] = true, | ||
150 | ["rock-tree"] = true, | ||
151 | ["rock-trees"] = true, | ||
152 | ["rockspec"] = true, | ||
153 | ["rockspec-format"] = "<ver>", | ||
154 | ["scope"] = "<system|user|project>", | ||
155 | ["server"] = "<server>", | ||
156 | ["sign"] = true, | ||
157 | ["skip-pack"] = true, | ||
158 | ["source"] = true, | ||
159 | ["summary"] = "\"<text>\"", | ||
160 | ["system-config"] = true, | ||
161 | ["tag"] = "<tag>", | ||
162 | ["test-type"] = "<type>", | ||
163 | ["temp-key"] = "<key>", | ||
164 | ["timeout"] = "<seconds>", | ||
165 | ["to"] = "<path>", | ||
166 | ["tree"] = "<path>", | ||
167 | ["unset"] = true, | ||
168 | ["user-config"] = true, | ||
169 | ["verbose"] = true, | ||
170 | ["verify"] = true, | ||
171 | ["version"] = true, | ||
172 | } | ||
173 | |||
174 | --- Extract flags from an arguments list. | ||
175 | -- Given string arguments, extract flag arguments into a flags set. | ||
176 | -- For example, given "foo", "--tux=beep", "--bla", "bar", "--baz", | ||
177 | -- it would return the following: | ||
178 | -- {["bla"] = true, ["tux"] = "beep", ["baz"] = true}, "foo", "bar". | ||
179 | function util.parse_flags(...) | ||
180 | local args = {...} | ||
181 | local flags = {} | ||
182 | local i = 1 | ||
183 | local out = {} | ||
184 | local state = "initial" | ||
185 | while i <= #args do | ||
186 | local flag = args[i]:match("^%-%-(.*)") | ||
187 | if state == "initial" and flag == "" then | ||
188 | state = "ignore_flags" | ||
189 | elseif state == "initial" and flag then | ||
190 | local var,val = flag:match("([a-z_%-]*)=(.*)") | ||
191 | if val then | ||
192 | local vartype = supported_flags[var] | ||
193 | if type(vartype) == "string" then | ||
194 | if val == "" and vartype:sub(1,1) ~= '"' then | ||
195 | return { ERROR = "Invalid argument: parameter to flag --"..var.."="..vartype.." cannot be empty." } | ||
196 | end | ||
197 | flags[var] = val | ||
198 | else | ||
199 | if vartype then | ||
200 | return { ERROR = "Invalid argument: flag --"..var.." does not take an parameter." } | ||
201 | else | ||
202 | return { ERROR = "Invalid argument: unknown flag --"..var.."." } | ||
203 | end | ||
204 | end | ||
205 | else | ||
206 | local var = flag | ||
207 | local vartype = supported_flags[var] | ||
208 | if type(vartype) == "string" then | ||
209 | i = i + 1 | ||
210 | local val = args[i] | ||
211 | if not val then | ||
212 | return { ERROR = "Invalid argument: flag --"..var.."="..vartype.." expects a parameter." } | ||
213 | end | ||
214 | if val:match("^%-%-.*") then | ||
215 | return { ERROR = "Invalid argument: flag --"..var.."="..vartype.." expects a parameter (if you really want to pass "..val.." as an argument to --"..var..", use --"..var.."="..val..")." } | ||
216 | else | ||
217 | if val == "" and vartype:sub(1,1) ~= '"' then | ||
218 | return { ERROR = "Invalid argument: parameter to flag --"..var.."="..vartype.." cannot be empty." } | ||
219 | end | ||
220 | flags[var] = val | ||
221 | end | ||
222 | elseif vartype == true then | ||
223 | flags[var] = true | ||
224 | else | ||
225 | return { ERROR = "Invalid argument: unknown flag --"..var.."." } | ||
226 | end | ||
227 | end | ||
228 | elseif state == "ignore_flags" or (state == "initial" and not flag) then | ||
229 | table.insert(out, args[i]) | ||
230 | end | ||
231 | i = i + 1 | ||
232 | end | ||
233 | return flags, unpack(out) | ||
234 | end | ||
235 | |||
236 | local var_format_pattern = "%$%((%a[%a%d_]+)%)" | 80 | local var_format_pattern = "%$%((%a[%a%d_]+)%)" |
237 | 81 | ||
238 | -- Check if a set of needed variables are referenced | 82 | -- Check if a set of needed variables are referenced |
@@ -371,29 +215,36 @@ function util.this_program(default) | |||
371 | return prog | 215 | return prog |
372 | end | 216 | end |
373 | 217 | ||
374 | function util.deps_mode_help(program) | 218 | function util.deps_mode_option(parser, program) |
375 | local cfg = require("luarocks.core.cfg") | 219 | local cfg = require("luarocks.core.cfg") |
376 | return [[ | 220 | |
377 | --deps-mode=<mode> How to handle dependencies. Four modes are supported: | 221 | parser:option("--deps-mode", "How to handle dependencies. Four modes are supported:\n".. |
378 | * all - use all trees from the rocks_trees list | 222 | "* all - use all trees from the rocks_trees list for finding dependencies\n".. |
379 | for finding dependencies | 223 | "* one - use only the current tree (possibly set with --tree)\n".. |
380 | * one - use only the current tree (possibly set | 224 | "* order - use trees based on order (use the current tree and all ".. |
381 | with --tree) | 225 | "trees below it on the rocks_trees list)\n".. |
382 | * order - use trees based on order (use the current | 226 | "* none - ignore dependencies altogether.\n".. |
383 | tree and all trees below it on the rocks_trees list) | 227 | "The default mode may be set with the deps_mode entry in the configuration file.\n".. |
384 | * none - ignore dependencies altogether. | 228 | 'The current default is "'..cfg.deps_mode..'".\n'.. |
385 | The default mode may be set with the deps_mode entry | 229 | "Type '"..util.this_program(program or "luarocks").."' with no ".. |
386 | in the configuration file. | 230 | "arguments to see your list of rocks trees.") |
387 | The current default is "]]..cfg.deps_mode..[[". | 231 | :argname("<mode>") |
388 | Type ']]..util.this_program(program or "luarocks")..[[' with no arguments to see | 232 | :choices({"all", "one", "order", "none"}) |
389 | your list of rocks trees. | 233 | parser:flag("--nodeps"):hidden(true) |
390 | ]] | ||
391 | end | 234 | end |
392 | 235 | ||
393 | function util.see_help(command, program) | 236 | function util.see_help(command, program) |
394 | return "See '"..util.this_program(program or "luarocks")..' help'..(command and " "..command or "").."'." | 237 | return "See '"..util.this_program(program or "luarocks")..' help'..(command and " "..command or "").."'." |
395 | end | 238 | end |
396 | 239 | ||
240 | function util.see_also(text) | ||
241 | local see_also = "See also:\n" | ||
242 | if text then | ||
243 | see_also = see_also..text.."\n" | ||
244 | end | ||
245 | return see_also.." '"..util.this_program("luarocks").." help' for general options and configuration." | ||
246 | end | ||
247 | |||
397 | function util.announce_install(rockspec) | 248 | function util.announce_install(rockspec) |
398 | local cfg = require("luarocks.core.cfg") | 249 | local cfg = require("luarocks.core.cfg") |
399 | local path = require("luarocks.path") | 250 | local path = require("luarocks.path") |
@@ -487,13 +338,13 @@ function util.LQ(s) | |||
487 | return ("%q"):format(s) | 338 | return ("%q"):format(s) |
488 | end | 339 | end |
489 | 340 | ||
490 | --- Normalize the --namespace flag and the user/rock syntax for namespaces. | 341 | --- Normalize the --namespace option and the user/rock syntax for namespaces. |
491 | -- If a namespace is given in user/rock syntax, update the --namespace flag; | 342 | -- If a namespace is given in user/rock syntax, update the --namespace option; |
492 | -- If a namespace is given in --namespace flag, update the user/rock syntax. | 343 | -- If a namespace is given in --namespace option, update the user/rock syntax. |
493 | -- In case of conflicts, the user/rock syntax takes precedence. | 344 | -- In case of conflicts, the user/rock syntax takes precedence. |
494 | function util.adjust_name_and_namespace(ns_name, flags) | 345 | function util.adjust_name_and_namespace(ns_name, args) |
495 | assert(type(ns_name) == "string" or not ns_name) | 346 | assert(type(ns_name) == "string" or not ns_name) |
496 | assert(type(flags) == "table") | 347 | assert(type(args) == "table") |
497 | 348 | ||
498 | if not ns_name then | 349 | if not ns_name then |
499 | return | 350 | return |
@@ -503,10 +354,10 @@ function util.adjust_name_and_namespace(ns_name, flags) | |||
503 | 354 | ||
504 | local name, namespace = util.split_namespace(ns_name) | 355 | local name, namespace = util.split_namespace(ns_name) |
505 | if namespace then | 356 | if namespace then |
506 | flags["namespace"] = namespace | 357 | args.namespace = namespace |
507 | end | 358 | end |
508 | if flags["namespace"] then | 359 | if args.namespace then |
509 | name = flags["namespace"] .. "/" .. name | 360 | name = args.namespace .. "/" .. name |
510 | end | 361 | end |
511 | return name:lower() | 362 | return name:lower() |
512 | end | 363 | end |