1--- Module implementing the LuaRocks "config" command. 2-- Queries information about the LuaRocks configuration. 3local config_cmd = {} 4 5local persist = require("luarocks.persist") 6local cfg = require("luarocks.core.cfg") 7local util = require("luarocks.util") 8local deps = require("luarocks.deps") 9local dir = require("luarocks.dir") 10local fs = require("luarocks.fs") 11 12function config_cmd.add_to_parser(parser) 13 local cmd = parser:command("config", [[ 14Query information about the LuaRocks configuration. 15 16* When given a configuration key, it prints the value of that key according to 17 the currently active configuration (taking into account all config files and 18 any command-line flags passed) 19 20 Examples: 21 luarocks config lua_interpreter 22 luarocks config variables.LUA_INCDIR 23 luarocks config lua_version 24 25* When given a configuration key and a value, it overwrites the config file (see 26 the --scope option below to determine which) and replaces the value of the 27 given key with the given value. 28 29 * `lua_dir` is a special key as it checks for a valid Lua installation 30 (equivalent to --lua-dir) and sets several keys at once. 31 * `lua_version` is a special key as it changes the default Lua version 32 used by LuaRocks commands (equivalent to passing --lua-version). 33 34 Examples: 35 luarocks config variables.OPENSSL_DIR /usr/local/openssl 36 luarocks config lua_dir /usr/local 37 luarocks config lua_version 5.3 38 39* When given a configuration key and --unset, it overwrites the config file (see 40 the --scope option below to determine which) and deletes that key from the 41 file. 42 43 Example: luarocks config variables.OPENSSL_DIR --unset 44 45* When given no arguments, it prints the entire currently active configuration, 46 resulting from reading the config files from all scopes. 47 48 Example: luarocks config]], util.see_also([[ 49 https://github.com/luarocks/luarocks/wiki/Config-file-format 50 for detailed information on the LuaRocks config file format. 51]])) 52 :summary("Query information about the LuaRocks configuration.") 53 54 cmd:argument("key", "The configuration key.") 55 :args("?") 56 cmd:argument("value", "The configuration value.") 57 :args("?") 58 59 cmd:option("--scope", "The scope indicates which config file should be rewritten.\n".. 60 '* Using a wrapper created with `luarocks init`, the default is "project".\n'.. 61 '* Using --local (or when `local_by_default` is `true`), the default is "user".\n'.. 62 '* Otherwise, the default is "system".') 63 :choices({"system", "user", "project"}) 64 cmd:flag("--unset", "Delete the key from the configuration file.") 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) 74end 75 76local function config_file(conf) 77 print(dir.normalize(conf.file)) 78 if conf.found then 79 return true 80 else 81 return nil, "file not found" 82 end 83end 84 85local cfg_skip = { 86 errorcodes = true, 87 flags = true, 88 platforms = true, 89 root_dir = true, 90 upload_servers = true, 91} 92 93local function should_skip(k, v) 94 return type(v) == "function" or cfg_skip[k] 95end 96 97local function cleanup(tbl) 98 local copy = {} 99 for k, v in pairs(tbl) do 100 if not should_skip(k, v) then 101 copy[k] = v 102 end 103 end 104 return copy 105end 106 107local function traverse_varstring(var, tbl, fn, missing_parent) 108 local k, r = var:match("^%[([0-9]+)%]%.(.*)$") 109 if k then 110 k = tonumber(k) 111 else 112 k, r = var:match("^([^.[]+)%.(.*)$") 113 if not k then 114 k, r = var:match("^([^[]+)(%[.*)$") 115 end 116 end 117 118 if k then 119 if not tbl[k] and missing_parent then 120 missing_parent(tbl, k) 121 end 122 123 if tbl[k] then 124 return traverse_varstring(r, tbl[k], fn, missing_parent) 125 else 126 return nil, "Unknown entry " .. k 127 end 128 end 129 130 local i = var:match("^%[([0-9]+)%]$") 131 if i then 132 var = tonumber(i) 133 end 134 135 return fn(tbl, var) 136end 137 138local function print_json(value) 139 local json_ok, json = util.require_json() 140 if not json_ok then 141 return nil, "A JSON library is required for this command. "..json 142 end 143 144 print(json.encode(value)) 145 return true 146end 147 148local function print_entry(var, tbl, is_json) 149 return traverse_varstring(var, tbl, function(t, k) 150 if not t[k] then 151 return nil, "Unknown entry " .. k 152 end 153 local val = t[k] 154 155 if not should_skip(var, val) then 156 if is_json then 157 return print_json(val) 158 elseif type(val) == "string" then 159 print(val) 160 else 161 persist.write_value(io.stdout, val) 162 end 163 end 164 return true 165 end) 166end 167 168local function infer_type(var) 169 local typ 170 traverse_varstring(var, cfg, function(t, k) 171 if t[k] ~= nil then 172 typ = type(t[k]) 173 end 174 end) 175 return typ 176end 177 178local function write_entries(keys, scope, do_unset) 179 if scope == "project" and not cfg.config_files.project then 180 return nil, "Current directory is not part of a project. You may want to run `luarocks init`." 181 end 182 183 local tbl, err = persist.load_config_file_if_basic(cfg.config_files[scope].file, cfg) 184 if not tbl then 185 return nil, err 186 end 187 188 for var, val in util.sortedpairs(keys) do 189 traverse_varstring(var, tbl, function(t, k) 190 if do_unset then 191 t[k] = nil 192 else 193 local typ = infer_type(var) 194 local v 195 if typ == "number" and tonumber(val) then 196 v = tonumber(val) 197 elseif typ == "boolean" and val == "true" then 198 v = true 199 elseif typ == "boolean" and val == "false" then 200 v = false 201 else 202 v = val 203 end 204 t[k] = v 205 keys[var] = v 206 end 207 return true 208 end, function(p, k) 209 p[k] = {} 210 end) 211 end 212 213 local ok, err = persist.save_from_table(cfg.config_files[scope].file, tbl) 214 if ok then 215 print(do_unset and "Removed" or "Wrote") 216 for var, val in util.sortedpairs(keys) do 217 if do_unset then 218 print(("\t%s"):format(var)) 219 else 220 print(("\t%s = %q"):format(var, val)) 221 end 222 end 223 print(do_unset and "from" or "to") 224 print("\t" .. cfg.config_files[scope].file) 225 return true 226 else 227 return nil, err 228 end 229end 230 231local function get_scope(args) 232 return args.scope 233 or (args["local"] and "user") 234 or (args.project_tree and "project") 235 or (cfg.local_by_default and "user") 236 or (fs.is_writable(cfg.config_files["system"].file and "system")) 237 or "user" 238end 239 240--- Driver function for "config" command. 241-- @return boolean: True if succeeded, nil on errors. 242function config_cmd.command(args) 243 deps.check_lua_incdir(cfg.variables, args.lua_version or cfg.lua_version) 244 deps.check_lua_libdir(cfg.variables, args.lua_version or cfg.lua_version) 245 246 -- deprecated flags 247 if args.lua_incdir then 248 print(cfg.variables.LUA_INCDIR) 249 return true 250 end 251 if args.lua_libdir then 252 print(cfg.variables.LUA_LIBDIR) 253 return true 254 end 255 if args.lua_ver then 256 print(cfg.lua_version) 257 return true 258 end 259 if args.system_config then 260 return config_file(cfg.config_files.system) 261 end 262 if args.user_config then 263 return config_file(cfg.config_files.user) 264 end 265 if args.rock_trees then 266 for _, tree in ipairs(cfg.rocks_trees) do 267 if type(tree) == "string" then 268 util.printout(dir.normalize(tree)) 269 else 270 local name = tree.name and "\t"..tree.name or "" 271 util.printout(dir.normalize(tree.root)..name) 272 end 273 end 274 return true 275 end 276 277 if args.key == "lua_version" and args.value then 278 local scope = get_scope(args) 279 if scope == "project" and not cfg.config_files.project then 280 return nil, "Current directory is not part of a project. You may want to run `luarocks init`." 281 end 282 283 local prefix = dir.dir_name(cfg.config_files[scope].file) 284 local ok, err = persist.save_default_lua_version(prefix, args.value) 285 if not ok then 286 return nil, "could not set default Lua version: " .. err 287 end 288 print("Lua version will default to " .. args.value .. " in " .. prefix) 289 end 290 291 if args.key == "lua_dir" and args.value then 292 local scope = get_scope(args) 293 local keys = { 294 ["variables.LUA_DIR"] = cfg.variables.LUA_DIR, 295 ["variables.LUA_BINDIR"] = cfg.variables.LUA_BINDIR, 296 ["variables.LUA_INCDIR"] = cfg.variables.LUA_INCDIR, 297 ["variables.LUA_LIBDIR"] = cfg.variables.LUA_LIBDIR, 298 ["lua_interpreter"] = cfg.lua_interpreter, 299 } 300 if args.lua_version then 301 local prefix = dir.dir_name(cfg.config_files[scope].file) 302 persist.save_default_lua_version(prefix, args.lua_version) 303 end 304 return write_entries(keys, scope, args.unset) 305 end 306 307 if args.key then 308 if args.value or args.unset then 309 local scope = get_scope(args) 310 return write_entries({ [args.key] = args.value or args.unset }, scope, args.unset) 311 else 312 return print_entry(args.key, cfg, args.json) 313 end 314 end 315 316 local cleancfg = cleanup(cfg) 317 318 if args.json then 319 return print_json(cleancfg) 320 else 321 print(persist.save_from_table_to_string(cleancfg)) 322 return true 323 end 324end 325 326return config_cmd 327