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 |
