diff options
author | V1K1NGbg <victor@ilchev.com> | 2024-07-28 00:12:23 +0300 |
---|---|---|
committer | V1K1NGbg <victor@ilchev.com> | 2024-08-05 20:49:17 +0300 |
commit | 00cb13ccd83adad7d8816ada99f6aae4a47d849d (patch) | |
tree | ece0a640002e25628b4a7698683f328755ab22f4 | |
parent | 7101268df03250c371bec9cc25de791ae790d22f (diff) | |
download | luarocks-00cb13ccd83adad7d8816ada99f6aae4a47d849d.tar.gz luarocks-00cb13ccd83adad7d8816ada99f6aae4a47d849d.tar.bz2 luarocks-00cb13ccd83adad7d8816ada99f6aae4a47d849d.zip |
util test
-rw-r--r-- | src/luarocks/cmd/install.lua | 2 | ||||
-rw-r--r-- | src/luarocks/type_check.tl | 213 | ||||
-rw-r--r-- | src/luarocks/util.lua | 643 | ||||
-rw-r--r-- | src/luarocks/util.tl | 1 |
4 files changed, 858 insertions, 1 deletions
diff --git a/src/luarocks/cmd/install.lua b/src/luarocks/cmd/install.lua index 4f27e471..b50271f0 100644 --- a/src/luarocks/cmd/install.lua +++ b/src/luarocks/cmd/install.lua | |||
@@ -54,7 +54,7 @@ function install.add_to_parser(parser) | |||
54 | parser:flag("--sign"):hidden(true) | 54 | parser:flag("--sign"):hidden(true) |
55 | end | 55 | end |
56 | 56 | ||
57 | install.opts = util.opts_table("install.opts", { --WORK | 57 | install.opts = util.opts_table("install.opts", { --WORK util.opts_table doesn't exist. Make a record |
58 | namespace = "string?", | 58 | namespace = "string?", |
59 | keep = "boolean", | 59 | keep = "boolean", |
60 | force = "boolean", | 60 | force = "boolean", |
diff --git a/src/luarocks/type_check.tl b/src/luarocks/type_check.tl new file mode 100644 index 00000000..0e8ce60f --- /dev/null +++ b/src/luarocks/type_check.tl | |||
@@ -0,0 +1,213 @@ | |||
1 | |||
2 | local type_check = {} | ||
3 | |||
4 | local cfg = require("luarocks.core.cfg") | ||
5 | local fun = require("luarocks.fun") | ||
6 | local util = require("luarocks.util") | ||
7 | local vers = require("luarocks.core.vers") | ||
8 | -------------------------------------------------------------------------------- | ||
9 | |||
10 | -- A magic constant that is not used anywhere in a schema definition | ||
11 | -- and retains equality when the table is deep-copied. | ||
12 | type_check.MAGIC_PLATFORMS = 0xEBABEFAC | ||
13 | |||
14 | do | ||
15 | local function fill_in_version(tbl, version) | ||
16 | for _, v in pairs(tbl) do | ||
17 | if v is table then | ||
18 | if v._version == nil then | ||
19 | v._version = version | ||
20 | end | ||
21 | fill_in_version(v) | ||
22 | end | ||
23 | end | ||
24 | end | ||
25 | |||
26 | local function expand_magic_platforms(tbl) | ||
27 | for k,v in pairs(tbl) do | ||
28 | if v == type_check.MAGIC_PLATFORMS then | ||
29 | tbl[k] = { | ||
30 | _any = util.deep_copy(tbl) | ||
31 | } | ||
32 | tbl[k]._any[k] = nil | ||
33 | elseif type(v) == "table" then | ||
34 | expand_magic_platforms(v) | ||
35 | end | ||
36 | end | ||
37 | end | ||
38 | |||
39 | -- Build a table of schemas. | ||
40 | -- @param versions a table where each key is a version number as a string, | ||
41 | -- and the value is a schema specification. Schema versions are considered | ||
42 | -- incremental: version "2.0" only needs to specify what's new/changed from | ||
43 | -- version "1.0". | ||
44 | function type_check.declare_schemas(inputs) | ||
45 | local schemas = {} | ||
46 | local parent_version | ||
47 | |||
48 | local versions = fun.reverse_in(fun.sort_in(util.keys(inputs), vers.compare_versions)) | ||
49 | |||
50 | for _, version in ipairs(versions) do | ||
51 | local schema = inputs[version] | ||
52 | if parent_version ~= nil then | ||
53 | local copy = util.deep_copy(schemas[parent_version]) | ||
54 | util.deep_merge(copy, schema) | ||
55 | schema = copy | ||
56 | end | ||
57 | fill_in_version(schema, version) | ||
58 | expand_magic_platforms(schema) | ||
59 | parent_version = version | ||
60 | schemas[version] = schema | ||
61 | end | ||
62 | |||
63 | return schemas, versions | ||
64 | end | ||
65 | end | ||
66 | |||
67 | -------------------------------------------------------------------------------- | ||
68 | |||
69 | local function check_version(version, typetbl, context) | ||
70 | local typetbl_version = typetbl._version or "1.0" | ||
71 | if vers.compare_versions(typetbl_version, version) then | ||
72 | if context == "" then | ||
73 | return nil, "Invalid rockspec_format version number in rockspec? Please fix rockspec accordingly." | ||
74 | else | ||
75 | return nil, context.." is not supported in rockspec format "..version.." (requires version "..typetbl_version.."), please fix the rockspec_format field accordingly." | ||
76 | end | ||
77 | end | ||
78 | return true | ||
79 | end | ||
80 | |||
81 | --- Type check an object. | ||
82 | -- The object is compared against an archetypical value | ||
83 | -- matching the expected type -- the actual values don't matter, | ||
84 | -- only their types. Tables are type checked recursively. | ||
85 | -- @param version string: The version of the item. | ||
86 | -- @param item any: The object being checked. | ||
87 | -- @param typetbl any: The type-checking table for the object. | ||
88 | -- @param context string: A string indicating the "context" where the | ||
89 | -- error occurred (the full table path), for error messages. | ||
90 | -- @return boolean or (nil, string): true if type checking | ||
91 | -- succeeded, or nil and an error message if it failed. | ||
92 | -- @see type_check_table | ||
93 | local function type_check_item(version, item, typetbl, context) | ||
94 | assert(type(version) == "string") | ||
95 | |||
96 | if typetbl._version and typetbl._version ~= "1.0" then | ||
97 | local ok, err = check_version(version, typetbl, context) | ||
98 | if not ok then | ||
99 | return nil, err | ||
100 | end | ||
101 | end | ||
102 | |||
103 | local item_type = type(item) or "nil" | ||
104 | local expected_type = typetbl._type or "table" | ||
105 | |||
106 | if expected_type == "number" then | ||
107 | if not tonumber(item) then | ||
108 | return nil, "Type mismatch on field "..context..": expected a number" | ||
109 | end | ||
110 | elseif expected_type == "string" then | ||
111 | if item_type ~= "string" then | ||
112 | return nil, "Type mismatch on field "..context..": expected a string, got "..item_type | ||
113 | end | ||
114 | local pattern = typetbl._pattern | ||
115 | if pattern then | ||
116 | if not item:match("^"..pattern.."$") then | ||
117 | local what = typetbl._name or ("'"..pattern.."'") | ||
118 | return nil, "Type mismatch on field "..context..": invalid value '"..item.."' does not match " .. what | ||
119 | end | ||
120 | end | ||
121 | elseif expected_type == "table" then | ||
122 | if item_type ~= expected_type then | ||
123 | return nil, "Type mismatch on field "..context..": expected a table" | ||
124 | else | ||
125 | return type_check.type_check_table(version, item, typetbl, context) | ||
126 | end | ||
127 | elseif item_type ~= expected_type then | ||
128 | return nil, "Type mismatch on field "..context..": expected "..expected_type | ||
129 | end | ||
130 | return true | ||
131 | end | ||
132 | |||
133 | local function mkfield(context, field) | ||
134 | if context == "" then | ||
135 | return tostring(field) | ||
136 | elseif type(field) == "string" then | ||
137 | return context.."."..field | ||
138 | else | ||
139 | return context.."["..tostring(field).."]" | ||
140 | end | ||
141 | end | ||
142 | |||
143 | --- Type check the contents of a table. | ||
144 | -- The table's contents are compared against a reference table, | ||
145 | -- which contains the recognized fields, with archetypical values | ||
146 | -- matching the expected types -- the actual values of items in the | ||
147 | -- reference table don't matter, only their types (ie, for field x | ||
148 | -- in tbl that is correctly typed, type(tbl.x) == type(types.x)). | ||
149 | -- If the reference table contains a field called MORE, then | ||
150 | -- unknown fields in the checked table are accepted. | ||
151 | -- If it contains a field called ANY, then its type will be | ||
152 | -- used to check any unknown fields. If a field is prefixed | ||
153 | -- with MUST_, it is mandatory; its absence from the table is | ||
154 | -- a type error. | ||
155 | -- Tables are type checked recursively. | ||
156 | -- @param version string: The version of tbl. | ||
157 | -- @param tbl table: The table to be type checked. | ||
158 | -- @param typetbl table: The type-checking table, containing | ||
159 | -- values for recognized fields in the checked table. | ||
160 | -- @param context string: A string indicating the "context" where the | ||
161 | -- error occurred (such as the name of the table the item is a part of), | ||
162 | -- to be used by error messages. | ||
163 | -- @return boolean or (nil, string): true if type checking | ||
164 | -- succeeded, or nil and an error message if it failed. | ||
165 | function type_check.type_check_table(version, tbl, typetbl, context) | ||
166 | assert(type(version) == "string") | ||
167 | assert(type(tbl) == "table") | ||
168 | assert(type(typetbl) == "table") | ||
169 | |||
170 | local ok, err = check_version(version, typetbl, context) | ||
171 | if not ok then | ||
172 | return nil, err | ||
173 | end | ||
174 | |||
175 | for k, v in pairs(tbl) do | ||
176 | local t = typetbl[k] or typetbl._any | ||
177 | if t then | ||
178 | local ok, err = type_check_item(version, v, t, mkfield(context, k)) | ||
179 | if not ok then return nil, err end | ||
180 | elseif typetbl._more then | ||
181 | -- Accept unknown field | ||
182 | else | ||
183 | if not cfg.accept_unknown_fields then | ||
184 | return nil, "Unknown field "..k | ||
185 | end | ||
186 | end | ||
187 | end | ||
188 | for k, v in pairs(typetbl) do | ||
189 | if k:sub(1,1) ~= "_" and v._mandatory then | ||
190 | if not tbl[k] then | ||
191 | return nil, "Mandatory field "..mkfield(context, k).." is missing." | ||
192 | end | ||
193 | end | ||
194 | end | ||
195 | return true | ||
196 | end | ||
197 | |||
198 | function type_check.check_undeclared_globals(globals, typetbl) | ||
199 | local undeclared = {} | ||
200 | for glob, _ in pairs(globals) do | ||
201 | if not (typetbl[glob] or typetbl["MUST_"..glob]) then | ||
202 | table.insert(undeclared, glob) | ||
203 | end | ||
204 | end | ||
205 | if #undeclared == 1 then | ||
206 | return nil, "Unknown variable: "..undeclared[1] | ||
207 | elseif #undeclared > 1 then | ||
208 | return nil, "Unknown variables: "..table.concat(undeclared, ", ") | ||
209 | end | ||
210 | return true | ||
211 | end | ||
212 | |||
213 | return type_check | ||
diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua new file mode 100644 index 00000000..59aff9a7 --- /dev/null +++ b/src/luarocks/util.lua | |||
@@ -0,0 +1,643 @@ | |||
1 | local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local debug = _tl_compat and _tl_compat.debug or debug; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local os = _tl_compat and _tl_compat.os or os; local package = _tl_compat and _tl_compat.package or package; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table; local _tl_table_unpack = unpack or table.unpack | ||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | local core = require("luarocks.core.util") | ||
8 | local cfg = require("luarocks.core.cfg") | ||
9 | |||
10 | local util = {Fn = {}, Parser = {}, } | ||
11 | |||
12 | |||
13 | |||
14 | |||
15 | |||
16 | |||
17 | |||
18 | |||
19 | |||
20 | |||
21 | |||
22 | |||
23 | |||
24 | |||
25 | |||
26 | |||
27 | |||
28 | |||
29 | |||
30 | |||
31 | |||
32 | |||
33 | |||
34 | |||
35 | |||
36 | |||
37 | |||
38 | |||
39 | util.cleanup_path = core.cleanup_path | ||
40 | util.split_string = core.split_string | ||
41 | util.sortedpairs = core.sortedpairs | ||
42 | util.deep_merge = core.deep_merge | ||
43 | util.deep_merge_under = core.deep_merge_under | ||
44 | util.popen_read = core.popen_read | ||
45 | util.show_table = core.show_table | ||
46 | util.printerr = core.printerr | ||
47 | util.warning = core.warning | ||
48 | util.keys = core.keys | ||
49 | util.matchquote = core.matchquote | ||
50 | |||
51 | |||
52 | |||
53 | |||
54 | |||
55 | |||
56 | local scheduled_functions = {} | ||
57 | |||
58 | |||
59 | |||
60 | |||
61 | |||
62 | |||
63 | |||
64 | |||
65 | function util.schedule_function(f, ...) | ||
66 | local item = { fn = f, args = table.pack(...) } | ||
67 | table.insert(scheduled_functions, item) | ||
68 | return item | ||
69 | end | ||
70 | |||
71 | |||
72 | |||
73 | |||
74 | |||
75 | function util.remove_scheduled_function(item) | ||
76 | for k, v in ipairs(scheduled_functions) do | ||
77 | if v == item then | ||
78 | table.remove(scheduled_functions, k) | ||
79 | return | ||
80 | end | ||
81 | end | ||
82 | end | ||
83 | |||
84 | |||
85 | |||
86 | |||
87 | |||
88 | |||
89 | function util.run_scheduled_functions() | ||
90 | local fs = require("luarocks.fs") | ||
91 | if fs.change_dir_to_root then | ||
92 | fs.change_dir_to_root() | ||
93 | end | ||
94 | for i = #scheduled_functions, 1, -1 do | ||
95 | local item = scheduled_functions[i] | ||
96 | item.fn(_tl_table_unpack(item.args, 1, item.args.n)) | ||
97 | end | ||
98 | end | ||
99 | |||
100 | local var_format_pattern = "%$%((%a[%a%d_]+)%)" | ||
101 | |||
102 | |||
103 | |||
104 | |||
105 | |||
106 | |||
107 | |||
108 | |||
109 | |||
110 | |||
111 | |||
112 | function util.warn_if_not_used(var_defs, needed_set, msg) | ||
113 | local seen = {} | ||
114 | for _, val in pairs(var_defs) do | ||
115 | for used in val:gmatch(var_format_pattern) do | ||
116 | seen[used] = true | ||
117 | end | ||
118 | end | ||
119 | for var, _ in pairs(needed_set) do | ||
120 | if not seen[var] then | ||
121 | util.warning(msg:format(var)) | ||
122 | end | ||
123 | end | ||
124 | end | ||
125 | |||
126 | |||
127 | |||
128 | |||
129 | local function warn_failed_matches(line) | ||
130 | local any_failed = false | ||
131 | if line:match(var_format_pattern) then | ||
132 | for unmatched in line:gmatch(var_format_pattern) do | ||
133 | util.warning("unmatched variable " .. unmatched) | ||
134 | any_failed = true | ||
135 | end | ||
136 | end | ||
137 | return any_failed | ||
138 | end | ||
139 | |||
140 | |||
141 | |||
142 | |||
143 | |||
144 | |||
145 | |||
146 | |||
147 | |||
148 | function util.variable_substitutions(tbl, vars) | ||
149 | |||
150 | local updated = {} | ||
151 | for k, v in pairs(tbl) do | ||
152 | if type(v) == "string" then | ||
153 | updated[k] = string.gsub(v, var_format_pattern, vars) | ||
154 | if warn_failed_matches(updated[k]) then | ||
155 | updated[k] = updated[k]:gsub(var_format_pattern, "") | ||
156 | end | ||
157 | end | ||
158 | end | ||
159 | for k, v in pairs(updated) do | ||
160 | tbl[k] = v | ||
161 | end | ||
162 | end | ||
163 | |||
164 | function util.lua_versions(sort) | ||
165 | local versions = { "5.1", "5.2", "5.3", "5.4" } | ||
166 | local i = 0 | ||
167 | if sort == "descending" then | ||
168 | i = #versions + 1 | ||
169 | return function() | ||
170 | i = i - 1 | ||
171 | return versions[i] | ||
172 | end | ||
173 | else | ||
174 | return function() | ||
175 | i = i + 1 | ||
176 | return versions[i] | ||
177 | end | ||
178 | end | ||
179 | end | ||
180 | |||
181 | function util.lua_path_variables() | ||
182 | local lpath_var = "LUA_PATH" | ||
183 | local lcpath_var = "LUA_CPATH" | ||
184 | |||
185 | local lv = cfg.lua_version:gsub("%.", "_") | ||
186 | if lv ~= "5_1" then | ||
187 | if os.getenv("LUA_PATH_" .. lv) then | ||
188 | lpath_var = "LUA_PATH_" .. lv | ||
189 | end | ||
190 | if os.getenv("LUA_CPATH_" .. lv) then | ||
191 | lcpath_var = "LUA_CPATH_" .. lv | ||
192 | end | ||
193 | end | ||
194 | return lpath_var, lcpath_var | ||
195 | end | ||
196 | |||
197 | function util.starts_with(s, prefix) | ||
198 | return s:sub(1, #prefix) == prefix | ||
199 | end | ||
200 | |||
201 | |||
202 | function util.printout(...) | ||
203 | io.stdout:write(table.concat({ ... }, "\t")) | ||
204 | io.stdout:write("\n") | ||
205 | end | ||
206 | |||
207 | function util.title(msg, porcelain, underline) | ||
208 | if porcelain then return end | ||
209 | util.printout() | ||
210 | util.printout(msg) | ||
211 | util.printout((underline or "-"):rep(#msg)) | ||
212 | util.printout() | ||
213 | end | ||
214 | |||
215 | function util.this_program(default) | ||
216 | local i = 1 | ||
217 | local last, cur = default, default | ||
218 | while i do | ||
219 | local dbg = debug and debug.getinfo(i, "S") | ||
220 | if not dbg then break end | ||
221 | last = cur | ||
222 | cur = dbg.source | ||
223 | i = i + 1 | ||
224 | end | ||
225 | local prog = last:sub(1, 1) == "@" and last:sub(2) or last | ||
226 | |||
227 | |||
228 | local lrdir, binpath = prog:match("^(.*)/lib/luarocks/rocks%-[0-9.]*/[^/]+/[^/]+(/bin/[^/]+)$") | ||
229 | if lrdir then | ||
230 | |||
231 | return lrdir .. binpath | ||
232 | end | ||
233 | |||
234 | return prog | ||
235 | end | ||
236 | |||
237 | function util.format_rock_name(name, namespace, version) | ||
238 | return (namespace and namespace .. "/" or "") .. name .. (version and " " .. version or "") | ||
239 | end | ||
240 | |||
241 | function util.deps_mode_option(parser, program) | ||
242 | |||
243 | parser:option("--deps-mode", "How to handle dependencies. Four modes are supported:\n" .. | ||
244 | "* all - use all trees from the rocks_trees list for finding dependencies\n" .. | ||
245 | "* one - use only the current tree (possibly set with --tree)\n" .. | ||
246 | "* order - use trees based on order (use the current tree and all " .. | ||
247 | "trees below it on the rocks_trees list)\n" .. | ||
248 | "* none - ignore dependencies altogether.\n" .. | ||
249 | "The default mode may be set with the deps_mode entry in the configuration file.\n" .. | ||
250 | 'The current default is "' .. cfg.deps_mode .. '".\n' .. | ||
251 | "Type '" .. util.this_program(program or "luarocks") .. "' with no " .. | ||
252 | "arguments to see your list of rocks trees."): | ||
253 | argname("<mode>"): | ||
254 | choices({ "all", "one", "order", "none" }) | ||
255 | parser:flag("--nodeps"):hidden(true) | ||
256 | end | ||
257 | |||
258 | function util.see_help(command, program) | ||
259 | return "See '" .. util.this_program(program or "luarocks") .. ' help' .. (command and " " .. command or "") .. "'." | ||
260 | end | ||
261 | |||
262 | function util.see_also(text) | ||
263 | local see_also = "See also:\n" | ||
264 | if text then | ||
265 | see_also = see_also .. text .. "\n" | ||
266 | end | ||
267 | return see_also .. " '" .. util.this_program("luarocks") .. " help' for general options and configuration." | ||
268 | end | ||
269 | |||
270 | function util.announce_install(rockspec) | ||
271 | local path = require("luarocks.path") | ||
272 | |||
273 | local suffix = "" | ||
274 | if rockspec.description and rockspec.description.license then | ||
275 | suffix = " (license: " .. rockspec.description.license .. ")" | ||
276 | end | ||
277 | |||
278 | util.printout(rockspec.name .. " " .. rockspec.version .. " is now installed in " .. path.root_dir(cfg.root_dir) .. suffix) | ||
279 | util.printout() | ||
280 | end | ||
281 | |||
282 | |||
283 | |||
284 | |||
285 | |||
286 | |||
287 | |||
288 | local function collect_rockspecs(versions, paths, unnamed_paths, subdir) | ||
289 | local fs = require("luarocks.fs") | ||
290 | local dir = require("luarocks.dir") | ||
291 | local path = require("luarocks.path") | ||
292 | local vers = require("luarocks.core.vers") | ||
293 | if fs.is_dir(subdir) then | ||
294 | for file in fs.dir(subdir) do | ||
295 | file = dir.path(subdir, file) | ||
296 | |||
297 | if file:match("rockspec$") and fs.is_file(file) then | ||
298 | local rock, version = path.parse_name(file) | ||
299 | |||
300 | if rock then | ||
301 | if not versions[rock] or vers.compare_versions(version, versions[rock]) then | ||
302 | versions[rock] = version | ||
303 | paths[rock] = file | ||
304 | end | ||
305 | else | ||
306 | table.insert(unnamed_paths, file) | ||
307 | end | ||
308 | end | ||
309 | end | ||
310 | end | ||
311 | end | ||
312 | |||
313 | |||
314 | |||
315 | function util.get_default_rockspec() | ||
316 | |||
317 | local versions = {} | ||
318 | local paths = {} | ||
319 | local unnamed_paths = {} | ||
320 | |||
321 | collect_rockspecs(versions, paths, unnamed_paths, ".") | ||
322 | collect_rockspecs(versions, paths, unnamed_paths, "rockspec") | ||
323 | collect_rockspecs(versions, paths, unnamed_paths, "rockspecs") | ||
324 | |||
325 | if #unnamed_paths > 0 then | ||
326 | |||
327 | |||
328 | if #unnamed_paths > 1 then | ||
329 | return nil, "Please specify which rockspec file to use." | ||
330 | else | ||
331 | return unnamed_paths[1] | ||
332 | end | ||
333 | else | ||
334 | local fs = require("luarocks.fs") | ||
335 | local dir = require("luarocks.dir") | ||
336 | local basename = dir.base_name(fs.current_dir()) | ||
337 | |||
338 | if paths[basename] then | ||
339 | return paths[basename] | ||
340 | end | ||
341 | |||
342 | local rock = next(versions) | ||
343 | |||
344 | if rock then | ||
345 | |||
346 | if next(versions, rock) ~= nil then | ||
347 | return nil, "Please specify which rockspec file to use." | ||
348 | else | ||
349 | return paths[rock] | ||
350 | end | ||
351 | else | ||
352 | return nil, "Argument missing: please specify a rockspec to use on current directory." | ||
353 | end | ||
354 | end | ||
355 | end | ||
356 | |||
357 | |||
358 | |||
359 | |||
360 | function util.LQ(s) | ||
361 | return ("%q"):format(s) | ||
362 | end | ||
363 | |||
364 | |||
365 | |||
366 | |||
367 | function util.split_namespace(ns_name) | ||
368 | local p1, p2 = ns_name:match("^([^/]+)/([^/]+)$") | ||
369 | if p1 then | ||
370 | return p2, p1 | ||
371 | end | ||
372 | return ns_name | ||
373 | end | ||
374 | |||
375 | |||
376 | function util.namespaced_name_action(args, target, ns_name) | ||
377 | |||
378 | if not ns_name then | ||
379 | return | ||
380 | end | ||
381 | |||
382 | if ns_name:match("%.rockspec$") or ns_name:match("%.rock$") then | ||
383 | args[target] = ns_name | ||
384 | else | ||
385 | local name, namespace = util.split_namespace(ns_name) | ||
386 | args[target] = name:lower() | ||
387 | if namespace then | ||
388 | args.namespace = namespace:lower() | ||
389 | end | ||
390 | end | ||
391 | end | ||
392 | |||
393 | function util.deep_copy(tbl) | ||
394 | local copy = {} | ||
395 | for k, v in pairs(tbl) do | ||
396 | if type(v) == "table" then | ||
397 | copy[k] = util.deep_copy(v) | ||
398 | else | ||
399 | copy[k] = v | ||
400 | end | ||
401 | end | ||
402 | return copy | ||
403 | end | ||
404 | |||
405 | |||
406 | |||
407 | |||
408 | function util.exists(file) | ||
409 | local fd, _, code = io.open(file, "r") | ||
410 | if code == 13 then | ||
411 | |||
412 | |||
413 | return true | ||
414 | end | ||
415 | if fd then | ||
416 | fd:close() | ||
417 | return true | ||
418 | end | ||
419 | return false | ||
420 | end | ||
421 | |||
422 | function util.lua_is_wrapper(interp) | ||
423 | local fd, err = io.open(interp, "r") | ||
424 | if not fd then | ||
425 | return nil, err | ||
426 | end | ||
427 | local data | ||
428 | data, err = fd:read(1000) | ||
429 | fd:close() | ||
430 | if not data then | ||
431 | return nil, err | ||
432 | end | ||
433 | return not not data:match("LUAROCKS_SYSCONFDIR") | ||
434 | end | ||
435 | |||
436 | do | ||
437 | local function Q(pathname) | ||
438 | if pathname:match("^.:") then | ||
439 | return pathname:sub(1, 2) .. '"' .. pathname:sub(3) .. '"' | ||
440 | end | ||
441 | return '"' .. pathname .. '"' | ||
442 | end | ||
443 | |||
444 | function util.check_lua_version(lua, luaver) | ||
445 | if not util.exists(lua) then | ||
446 | return nil | ||
447 | end | ||
448 | local lv = util.popen_read(Q(lua) .. ' -e "io.write(_VERSION:sub(5))"') | ||
449 | if lv == "" then | ||
450 | return nil | ||
451 | end | ||
452 | if luaver and luaver ~= lv then | ||
453 | return nil | ||
454 | end | ||
455 | return lv | ||
456 | end | ||
457 | |||
458 | function util.get_luajit_version() | ||
459 | if cfg.cache.luajit_version_checked then | ||
460 | return cfg.cache.luajit_version | ||
461 | end | ||
462 | cfg.cache.luajit_version_checked = true | ||
463 | |||
464 | if not cfg.variables.LUA then | ||
465 | return nil | ||
466 | end | ||
467 | |||
468 | local ljv | ||
469 | if cfg.lua_version == "5.1" then | ||
470 | |||
471 | ljv = util.popen_read(Q(cfg.variables.LUA) .. ' -e "io.write(tostring(jit and jit.version:gsub([[^%S+ (%S+).*]], [[%1]])))"') | ||
472 | if ljv == "nil" then | ||
473 | ljv = nil | ||
474 | end | ||
475 | end | ||
476 | cfg.cache.luajit_version = ljv | ||
477 | return ljv | ||
478 | end | ||
479 | |||
480 | local find_lua_bindir | ||
481 | do | ||
482 | local exe_suffix = (package.config:sub(1, 1) == "\\" and ".exe" or "") | ||
483 | |||
484 | local function insert_lua_variants(names, luaver) | ||
485 | local variants = { | ||
486 | "lua" .. luaver .. exe_suffix, | ||
487 | "lua" .. luaver:gsub("%.", "") .. exe_suffix, | ||
488 | "lua-" .. luaver .. exe_suffix, | ||
489 | "lua-" .. luaver:gsub("%.", "") .. exe_suffix, | ||
490 | } | ||
491 | for _, name in ipairs(variants) do | ||
492 | table.insert(names, name) | ||
493 | end | ||
494 | end | ||
495 | |||
496 | find_lua_bindir = function(prefix, luaver, verbose) | ||
497 | local names = {} | ||
498 | if luaver then | ||
499 | insert_lua_variants(names, luaver) | ||
500 | else | ||
501 | for v in util.lua_versions("descending") do | ||
502 | insert_lua_variants(names, v) | ||
503 | end | ||
504 | end | ||
505 | if luaver == "5.1" or not luaver then | ||
506 | table.insert(names, "luajit" .. exe_suffix) | ||
507 | end | ||
508 | table.insert(names, "lua" .. exe_suffix) | ||
509 | |||
510 | local tried = {} | ||
511 | local dir_sep = package.config:sub(1, 1) | ||
512 | for _, d in ipairs({ prefix .. dir_sep .. "bin", prefix }) do | ||
513 | for _, name in ipairs(names) do | ||
514 | local lua = d .. dir_sep .. name | ||
515 | local is_wrapper, err = util.lua_is_wrapper(lua) | ||
516 | if is_wrapper == false then | ||
517 | local lv = util.check_lua_version(lua, luaver) | ||
518 | if lv then | ||
519 | return lua, d, lv | ||
520 | end | ||
521 | elseif is_wrapper == true or err == nil then | ||
522 | table.insert(tried, lua) | ||
523 | else | ||
524 | table.insert(tried, string.format("%-13s (%s)", lua, err)) | ||
525 | end | ||
526 | end | ||
527 | end | ||
528 | local interp = luaver and | ||
529 | ("Lua " .. luaver .. " interpreter") or | ||
530 | "Lua interpreter" | ||
531 | return nil, interp .. " not found at " .. prefix .. "\n" .. | ||
532 | (verbose and "Tried:\t" .. table.concat(tried, "\n\t") or "") | ||
533 | end | ||
534 | end | ||
535 | |||
536 | function util.find_lua(prefix, luaver, verbose) | ||
537 | local lua, bindir | ||
538 | lua, bindir, luaver = find_lua_bindir(prefix, luaver, verbose) | ||
539 | if not lua then | ||
540 | return nil, bindir | ||
541 | end | ||
542 | |||
543 | return { | ||
544 | lua_version = luaver, | ||
545 | lua = lua, | ||
546 | lua_dir = prefix, | ||
547 | lua_bindir = bindir, | ||
548 | } | ||
549 | end | ||
550 | end | ||
551 | |||
552 | |||
553 | |||
554 | function util.opts_table(type_name, valid_opts) --! TEMP | ||
555 | local opts_mt = {} | ||
556 | |||
557 | opts_mt.__index = opts_mt | ||
558 | |||
559 | function opts_mt.type() | ||
560 | return type_name | ||
561 | end | ||
562 | |||
563 | return function(opts) | ||
564 | for k, v in pairs(opts) do | ||
565 | local tv = type(v) | ||
566 | if not valid_opts[k] then | ||
567 | error("invalid option: "..k) | ||
568 | end | ||
569 | local vo, optional = valid_opts[k]:match("^(.-)(%??)$") | ||
570 | if not (tv == vo or (optional == "?" and tv == nil)) then | ||
571 | error("invalid type option: "..k.." - got "..tv..", expected "..vo) | ||
572 | end | ||
573 | end | ||
574 | for k, v in pairs(valid_opts) do | ||
575 | if (not v:find("?", 1, true)) and opts[k] == nil then | ||
576 | error("missing option: "..k) | ||
577 | end | ||
578 | end | ||
579 | return setmetatable(opts, opts_mt) | ||
580 | end | ||
581 | end | ||
582 | |||
583 | |||
584 | |||
585 | |||
586 | function util.get_rocks_provided(rockspec) | ||
587 | |||
588 | if not rockspec and cfg.cache.rocks_provided then | ||
589 | return cfg.cache.rocks_provided | ||
590 | end | ||
591 | |||
592 | local rocks_provided = {} | ||
593 | |||
594 | local lv = cfg.lua_version | ||
595 | |||
596 | rocks_provided["lua"] = lv .. "-1" | ||
597 | |||
598 | if lv == "5.2" then | ||
599 | rocks_provided["bit32"] = lv .. "-1" | ||
600 | end | ||
601 | |||
602 | if lv == "5.3" or lv == "5.4" then | ||
603 | rocks_provided["utf8"] = lv .. "-1" | ||
604 | end | ||
605 | |||
606 | if lv == "5.1" then | ||
607 | local ljv = util.get_luajit_version() | ||
608 | if ljv then | ||
609 | rocks_provided["luabitop"] = ljv .. "-1" | ||
610 | if (not rockspec) or rockspec:format_is_at_least("3.0") then | ||
611 | rocks_provided["luajit"] = ljv .. "-1" | ||
612 | end | ||
613 | end | ||
614 | end | ||
615 | |||
616 | if cfg.rocks_provided then | ||
617 | util.deep_merge_under(rocks_provided, cfg.rocks_provided) | ||
618 | end | ||
619 | |||
620 | if not rockspec then | ||
621 | cfg.cache.rocks_provided = rocks_provided | ||
622 | end | ||
623 | |||
624 | return rocks_provided | ||
625 | end | ||
626 | |||
627 | function util.remove_doc_dir(name, version) | ||
628 | local path = require("luarocks.path") | ||
629 | local fs = require("luarocks.fs") | ||
630 | local dir = require("luarocks.dir") | ||
631 | |||
632 | local install_dir = path.install_dir(name, version) | ||
633 | for _, f in ipairs(fs.list_dir(install_dir)) do | ||
634 | local doc_dirs = { "doc", "docs" } | ||
635 | for _, d in ipairs(doc_dirs) do | ||
636 | if f == d then | ||
637 | fs.delete(dir.path(install_dir, f)) | ||
638 | end | ||
639 | end | ||
640 | end | ||
641 | end | ||
642 | |||
643 | return util | ||
diff --git a/src/luarocks/util.tl b/src/luarocks/util.tl index aa04a3f3..72b8d995 100644 --- a/src/luarocks/util.tl +++ b/src/luarocks/util.tl | |||
@@ -548,6 +548,7 @@ do | |||
548 | } | 548 | } |
549 | end | 549 | end |
550 | end | 550 | end |
551 | |||
551 | --- Return a table of modules that are already provided by the VM, which | 552 | --- Return a table of modules that are already provided by the VM, which |
552 | -- can be specified as dependencies without having to install an actual rock. | 553 | -- can be specified as dependencies without having to install an actual rock. |
553 | -- @param rockspec (optional) a rockspec table, so that rockspec format | 554 | -- @param rockspec (optional) a rockspec table, so that rockspec format |