summaryrefslogtreecommitdiff
path: root/src/luarocks/cmd.lua
diff options
context:
space:
mode:
authorMike Vink <mike@pionative.com>2025-02-03 21:29:42 +0100
committerMike Vink <mike@pionative.com>2025-02-03 21:29:42 +0100
commit5155816b7b925dec5d5feb1568b1d7ceb00938b9 (patch)
treedeca28ea15e79f6f804c3d90d2ba757881638af5 /src/luarocks/cmd.lua
fetch tarballHEADmaster
Diffstat (limited to 'src/luarocks/cmd.lua')
-rw-r--r--src/luarocks/cmd.lua781
1 files changed, 781 insertions, 0 deletions
diff --git a/src/luarocks/cmd.lua b/src/luarocks/cmd.lua
new file mode 100644
index 0000000..7e0abe5
--- /dev/null
+++ b/src/luarocks/cmd.lua
@@ -0,0 +1,781 @@
+
+--- Functions for command-line scripts.
+local cmd = {}
+
+local manif = require("luarocks.manif")
+local config = require("luarocks.config")
+local util = require("luarocks.util")
+local path = require("luarocks.path")
+local cfg = require("luarocks.core.cfg")
+local dir = require("luarocks.dir")
+local fun = require("luarocks.fun")
+local fs = require("luarocks.fs")
+local argparse = require("luarocks.vendor.argparse")
+
+local unpack = table.unpack or unpack
+local pack = table.pack or function(...) return { n = select("#", ...), ... } end
+
+local hc_ok, hardcoded = pcall(require, "luarocks.core.hardcoded")
+if not hc_ok then
+ hardcoded = {}
+end
+
+local program = util.this_program("luarocks")
+
+cmd.errorcodes = {
+ OK = 0,
+ UNSPECIFIED = 1,
+ PERMISSIONDENIED = 2,
+ CONFIGFILE = 3,
+ LOCK = 4,
+ CRASH = 99
+}
+
+local function check_popen()
+ local popen_ok, popen_result = pcall(io.popen, "")
+ if popen_ok then
+ if popen_result then
+ popen_result:close()
+ end
+ else
+ io.stderr:write("Your version of Lua does not support io.popen,\n")
+ io.stderr:write("which is required by LuaRocks. Please check your Lua installation.\n")
+ os.exit(cmd.errorcodes.UNSPECIFIED)
+ end
+end
+
+local process_tree_args
+do
+ local function replace_tree(args, root, tree)
+ root = dir.normalize(root)
+ args.tree = root
+ path.use_tree(tree or root)
+ end
+
+ local function strip_trailing_slashes()
+ if type(cfg.root_dir) == "string" then
+ cfg.root_dir = cfg.root_dir:gsub("/+$", "")
+ else
+ cfg.root_dir.root = cfg.root_dir.root:gsub("/+$", "")
+ end
+ cfg.rocks_dir = cfg.rocks_dir:gsub("/+$", "")
+ cfg.deploy_bin_dir = cfg.deploy_bin_dir:gsub("/+$", "")
+ cfg.deploy_lua_dir = cfg.deploy_lua_dir:gsub("/+$", "")
+ cfg.deploy_lib_dir = cfg.deploy_lib_dir:gsub("/+$", "")
+ end
+
+ local function set_named_tree(args, name)
+ for _, tree in ipairs(cfg.rocks_trees) do
+ if type(tree) == "table" and name == tree.name then
+ if not tree.root then
+ return nil, "Configuration error: tree '"..tree.name.."' has no 'root' field."
+ end
+ replace_tree(args, tree.root, tree)
+ return true
+ end
+ end
+ return false
+ end
+
+ process_tree_args = function(args, project_dir)
+
+ if args.global then
+ local ok, err = set_named_tree(args, "system")
+ if not ok then
+ return nil, err
+ end
+ elseif args.tree then
+ local named = set_named_tree(args, args.tree)
+ if not named then
+ local root_dir = fs.absolute_name(args.tree)
+ replace_tree(args, root_dir)
+ if (args.deps_mode or cfg.deps_mode) ~= "order" then
+ table.insert(cfg.rocks_trees, 1, { name = "arg", root = root_dir } )
+ end
+ end
+ elseif args["local"] then
+ if fs.is_superuser() then
+ return nil, "The --local flag is meant for operating in a user's home directory.\n"..
+ "You are running as a superuser, which is intended for system-wide operation.\n"..
+ "To force using the superuser's home, use --tree explicitly."
+ else
+ local ok, err = set_named_tree(args, "user")
+ if not ok then
+ return nil, err
+ end
+ end
+ elseif args.project_tree then
+ local tree = args.project_tree
+ table.insert(cfg.rocks_trees, 1, { name = "project", root = tree } )
+ manif.load_rocks_tree_manifests()
+ path.use_tree(tree)
+ elseif cfg.local_by_default then
+ local ok, err = set_named_tree(args, "user")
+ if not ok then
+ return nil, err
+ end
+ elseif project_dir then
+ local project_tree = project_dir .. "/lua_modules"
+ table.insert(cfg.rocks_trees, 1, { name = "project", root = project_tree } )
+ manif.load_rocks_tree_manifests()
+ path.use_tree(project_tree)
+ else
+ local trees = cfg.rocks_trees
+ path.use_tree(trees[#trees])
+ end
+
+ strip_trailing_slashes()
+
+ cfg.variables.ROCKS_TREE = cfg.rocks_dir
+ cfg.variables.SCRIPTS_DIR = cfg.deploy_bin_dir
+
+ return true
+ end
+end
+
+local function process_server_args(args)
+ if args.server then
+ local protocol, pathname = dir.split_url(args.server)
+ table.insert(cfg.rocks_servers, 1, protocol.."://"..pathname)
+ end
+
+ if args.dev then
+ local append_dev = function(s) return dir.path(s, "dev") end
+ local dev_servers = fun.traverse(cfg.rocks_servers, append_dev)
+ cfg.rocks_servers = fun.concat(dev_servers, cfg.rocks_servers)
+ end
+
+ if args.only_server then
+ if args.dev then
+ return nil, "--only-server cannot be used with --dev"
+ end
+ if args.server then
+ return nil, "--only-server cannot be used with --server"
+ end
+ cfg.rocks_servers = { args.only_server }
+ end
+
+ return true
+end
+
+local function error_handler(err)
+ if not debug then
+ return err
+ end
+ local mode = "Arch.: " .. (cfg and cfg.arch or "unknown")
+ if package.config:sub(1, 1) == "\\" then
+ if cfg and cfg.fs_use_modules then
+ mode = mode .. " (fs_use_modules = true)"
+ end
+ end
+ if cfg and cfg.is_binary then
+ mode = mode .. " (binary)"
+ end
+ return debug.traceback("LuaRocks "..cfg.program_version..
+ " bug (please report at https://github.com/luarocks/luarocks/issues).\n"..
+ mode.."\n"..err, 2)
+end
+
+--- Display an error message and exit.
+-- @param message string: The error message.
+-- @param exitcode number: the exitcode to use
+local function die(message, exitcode)
+ assert(type(message) == "string", "bad error, expected string, got: " .. type(message))
+ assert(exitcode == nil or type(exitcode) == "number", "bad error, expected number, got: " .. type(exitcode) .. " - " .. tostring(exitcode))
+ util.printerr("\nError: "..message)
+
+ local ok, err = xpcall(util.run_scheduled_functions, error_handler)
+ if not ok then
+ util.printerr("\nError: "..err)
+ exitcode = cmd.errorcodes.CRASH
+ end
+
+ os.exit(exitcode or cmd.errorcodes.UNSPECIFIED)
+end
+
+local function search_lua(lua_version, verbose, search_at)
+ if search_at then
+ return util.find_lua(search_at, lua_version, verbose)
+ end
+
+ local path_sep = (package.config:sub(1, 1) == "\\" and ";" or ":")
+ local all_tried = {}
+ for bindir in (os.getenv("PATH") or ""):gmatch("[^"..path_sep.."]+") do
+ local searchdir = (bindir:gsub("[\\/]+bin[\\/]?$", ""))
+ local detected, tried = util.find_lua(searchdir, lua_version)
+ if detected then
+ return detected
+ else
+ table.insert(all_tried, tried)
+ end
+ end
+ return nil, "Could not find " ..
+ (lua_version and "Lua " .. lua_version or "Lua") ..
+ " in PATH." ..
+ (verbose and " Tried:\n" .. table.concat(all_tried, "\n") or "")
+end
+
+local init_config
+do
+ local detect_config_via_args
+ do
+ local function find_project_dir(project_tree)
+ if project_tree then
+ return project_tree:gsub("[/\\][^/\\]+$", ""), true
+ else
+ local try = "."
+ for _ = 1, 10 do -- FIXME detect when root dir was hit instead
+ if util.exists(try .. "/.luarocks") and util.exists(try .. "/lua_modules") then
+ return dir.normalize(try), false
+ elseif util.exists(try .. "/.luarocks-no-project") then
+ break
+ end
+ try = try .. "/.."
+ end
+ end
+ return nil
+ end
+
+ local function find_default_lua_version(args, project_dir)
+ if hardcoded.FORCE_CONFIG then
+ return nil
+ end
+
+ local dirs = {}
+ if project_dir then
+ table.insert(dirs, dir.path(project_dir, ".luarocks"))
+ end
+ if cfg.homeconfdir then
+ table.insert(dirs, cfg.homeconfdir)
+ end
+ table.insert(dirs, cfg.sysconfdir)
+ for _, d in ipairs(dirs) do
+ local f = dir.path(d, "default-lua-version.lua")
+ local mod, err = loadfile(f, "t")
+ if mod then
+ local pok, ver = pcall(mod)
+ if pok and type(ver) == "string" and ver:match("%d+.%d+") then
+ if args.verbose then
+ util.printout("Defaulting to Lua " .. ver .. " based on " .. f .. " ...")
+ end
+ return ver
+ end
+ end
+ end
+ return nil
+ end
+
+ local function find_version_from_config(dirname)
+ return fun.find(util.lua_versions("descending"), function(v)
+ if util.exists(dir.path(dirname, ".luarocks", "config-"..v..".lua")) then
+ return v
+ end
+ end)
+ end
+
+ local function detect_lua_via_args(args, project_dir)
+ local lua_version = args.lua_version
+ or find_default_lua_version(args, project_dir)
+ or (project_dir and find_version_from_config(project_dir))
+
+ if args.lua_dir then
+ local detected, err = util.find_lua(args.lua_dir, lua_version)
+ if not detected then
+ local suggestion = (not args.lua_version)
+ and "\nYou may want to specify a different Lua version with --lua-version\n"
+ or ""
+ die(err .. suggestion)
+ end
+ return detected
+ end
+
+ if lua_version then
+ local detected = search_lua(lua_version)
+ if detected then
+ return detected
+ end
+ return {
+ lua_version = lua_version,
+ }
+ end
+
+ return {}
+ end
+
+ detect_config_via_args = function(args)
+ local project_dir, given
+ if not args.no_project then
+ project_dir, given = find_project_dir(args.project_tree)
+ end
+
+ local detected = detect_lua_via_args(args, project_dir)
+ if args.lua_version then
+ detected.given_lua_version = args.lua_version
+ end
+ if args.lua_dir then
+ detected.given_lua_dir = args.lua_dir
+ end
+ if given then
+ detected.given_project_dir = project_dir
+ end
+ detected.project_dir = project_dir
+ return detected
+ end
+ end
+
+ init_config = function(args)
+ local detected = detect_config_via_args(args)
+
+ local ok, err = cfg.init(detected, util.warning)
+ if not ok then
+ return nil, err
+ end
+
+ return (detected.lua_dir ~= nil)
+ end
+end
+
+local variables_help = [[
+Variables:
+ Variables from the "variables" table of the configuration file can be
+ overridden with VAR=VALUE assignments.
+
+]]
+
+local lua_example = package.config:sub(1, 1) == "\\"
+ and "<d:\\path\\lua.exe>"
+ or "</path/lua>"
+
+local function show_status(file, status, err)
+ return (file and file .. " " or "") .. (status and "(ok)" or ("(" .. (err or "not found") ..")"))
+end
+
+local function use_to_fix_location(key, what)
+ local buf = " ****************************************\n"
+ buf = buf .. " Use the command\n\n"
+ buf = buf .. " luarocks config " .. key .. " " .. (what or "<dir>") .. "\n\n"
+ buf = buf .. " to fix the location\n"
+ buf = buf .. " ****************************************\n"
+ return buf
+end
+
+local function get_config_text(cfg) -- luacheck: ignore 431
+ local deps = require("luarocks.deps")
+
+ local libdir_ok = deps.check_lua_libdir(cfg.variables)
+ local incdir_ok = deps.check_lua_incdir(cfg.variables)
+ local lua_ok = cfg.variables.LUA and fs.exists(cfg.variables.LUA)
+
+ local buf = "Configuration:\n"
+ buf = buf.." Lua:\n"
+ buf = buf.." Version : "..cfg.lua_version.."\n"
+ if cfg.luajit_version then
+ buf = buf.." LuaJIT : "..cfg.luajit_version.."\n"
+ end
+ buf = buf.." LUA : "..show_status(cfg.variables.LUA, lua_ok, "interpreter not found").."\n"
+ if not lua_ok then
+ buf = buf .. use_to_fix_location("variables.LUA", lua_example)
+ end
+ buf = buf.." LUA_INCDIR : "..show_status(cfg.variables.LUA_INCDIR, incdir_ok, "lua.h not found").."\n"
+ if lua_ok and not incdir_ok then
+ buf = buf .. use_to_fix_location("variables.LUA_INCDIR")
+ end
+ buf = buf.." LUA_LIBDIR : "..show_status(cfg.variables.LUA_LIBDIR, libdir_ok, "Lua library itself not found").."\n"
+ if lua_ok and not libdir_ok then
+ buf = buf .. use_to_fix_location("variables.LUA_LIBDIR")
+ end
+
+ buf = buf.."\n Configuration files:\n"
+ local conf = cfg.config_files
+ buf = buf.." System : "..show_status(fs.absolute_name(conf.system.file), conf.system.found).."\n"
+ if conf.user.file then
+ buf = buf.." User : "..show_status(fs.absolute_name(conf.user.file), conf.user.found).."\n"
+ else
+ buf = buf.." User : disabled in this LuaRocks installation.\n"
+ end
+ if conf.project then
+ buf = buf.." Project : "..show_status(fs.absolute_name(conf.project.file), conf.project.found).."\n"
+ end
+ buf = buf.."\n Rocks trees in use: \n"
+ for _, tree in ipairs(cfg.rocks_trees) do
+ if type(tree) == "string" then
+ buf = buf.." "..fs.absolute_name(tree)
+ else
+ local name = tree.name and " (\""..tree.name.."\")" or ""
+ buf = buf.." "..fs.absolute_name(tree.root)..name
+ end
+ buf = buf .. "\n"
+ end
+
+ return buf
+end
+
+local function get_parser(description, cmd_modules)
+ local basename = dir.base_name(program)
+ local parser = argparse(
+ basename, "LuaRocks "..cfg.program_version..", the Lua package manager\n\n"..
+ program.." - "..description, variables_help.."Run '"..basename..
+ "' without any arguments to see the configuration.")
+ :help_max_width(80)
+ :add_help_command()
+ :add_complete_command({
+ help_max_width = 100,
+ summary = "Output a shell completion script.",
+ description = [[
+Output a shell completion script.
+
+Enabling completions for Bash:
+
+ Add the following line to your ~/.bashrc:
+ source <(]]..basename..[[ completion bash)
+ or save the completion script to the local completion directory:
+ ]]..basename..[[ completion bash > ~/.local/share/bash-completion/completions/]]..basename..[[
+
+
+Enabling completions for Zsh:
+
+ Save the completion script to a file in your $fpath.
+ You can add a new directory to your $fpath by adding e.g.
+ fpath=(~/.zfunc $fpath)
+ to your ~/.zshrc.
+ Then run:
+ ]]..basename..[[ completion zsh > ~/.zfunc/_]]..basename..[[
+
+
+Enabling completion for Fish:
+
+ Add the following line to your ~/.config/fish/config.fish:
+ ]]..basename..[[ completion fish | source
+ or save the completion script to the local completion directory:
+ ]]..basename..[[ completion fish > ~/.config/fish/completions/]]..basename..[[.fish
+]]})
+ :command_target("command")
+ :require_command(false)
+
+ parser:flag("--version", "Show version info and exit.")
+ :action(function()
+ util.printout(program.." "..cfg.program_version)
+ util.printout(description)
+ util.printout()
+ os.exit(cmd.errorcodes.OK)
+ end)
+ parser:flag("--dev", "Enable the sub-repositories in rocks servers for "..
+ "rockspecs of in-development versions.")
+ parser:option("--server", "Fetch rocks/rockspecs from this server "..
+ "(takes priority over config file).")
+ :hidden_name("--from")
+ parser:option("--only-server", "Fetch rocks/rockspecs from this server only "..
+ "(overrides any entries in the config file).")
+ :argname("<server>")
+ :hidden_name("--only-from")
+ parser:option("--only-sources", "Restrict downloads to paths matching the given URL.")
+ :argname("<url>")
+ :hidden_name("--only-sources-from")
+ parser:option("--namespace", "Specify the rocks server namespace to use.")
+ :convert(string.lower)
+ parser:option("--lua-dir", "Which Lua installation to use.")
+ :argname("<prefix>")
+ parser:option("--lua-version", "Which Lua version to use.")
+ :argname("<ver>")
+ :convert(function(s) return (s:match("^%d+%.%d+$")) end)
+ parser:option("--tree", "Which tree to operate on.")
+ :hidden_name("--to")
+ parser:flag("--local", "Use the tree in the user's home directory.\n"..
+ "To enable it, see '"..program.." help path'.")
+ parser:flag("--global", "Use the system tree when `local_by_default` is `true`.")
+ parser:flag("--no-project", "Do not use project tree even if running from a project folder.")
+ parser:flag("--force-lock", "Attempt to overwrite the lock for commands " ..
+ "that require exclusive access, such as 'install'")
+ parser:flag("--verbose", "Display verbose output of commands executed.")
+ parser:option("--timeout", "Timeout on network operations, in seconds.\n"..
+ "0 means no timeout (wait forever). Default is "..
+ tostring(cfg.connection_timeout)..".")
+ :argname("<seconds>")
+ :convert(tonumber)
+
+ -- Used internally to force the use of a particular project tree
+ parser:option("--project-tree"):hidden(true)
+
+ for _, module in util.sortedpairs(cmd_modules) do
+ module.add_to_parser(parser)
+ end
+
+ return parser
+end
+
+local function get_first_arg()
+ if not arg then
+ return
+ end
+ local first_arg = arg[0]
+ local i = -1
+ while arg[i] do
+ first_arg = arg[i]
+ i = i -1
+ end
+ return first_arg
+end
+
+--- Main command-line processor.
+-- Parses input arguments and calls the appropriate driver function
+-- to execute the action requested on the command-line, forwarding
+-- to it any additional arguments passed by the user.
+-- @param description string: Short summary description of the program.
+-- @param commands table: contains the loaded modules representing commands.
+-- @param external_namespace string: where to look for external commands.
+-- @param ... string: Arguments given on the command-line.
+function cmd.run_command(description, commands, external_namespace, ...)
+
+ check_popen()
+
+ -- Preliminary initialization
+ cfg.init()
+
+ fs.init()
+
+ for _, module_name in ipairs(fs.modules(external_namespace)) do
+ if not commands[module_name] then
+ commands[module_name] = external_namespace.."."..module_name
+ end
+ end
+
+ local cmd_modules = {}
+ for name, module in pairs(commands) do
+ local pok, mod = pcall(require, module)
+ if pok and type(mod) == "table" then
+ local original_command = mod.command
+ if original_command then
+ if not mod.add_to_parser then
+ mod.add_to_parser = function(parser)
+ parser:command(name, mod.help, util.see_also())
+ :summary(mod.help_summary)
+ :handle_options(false)
+ :argument("input")
+ :args("*")
+ end
+
+ mod.command = function(args)
+ return original_command(args, unpack(args.input))
+ end
+ end
+ cmd_modules[name] = mod
+ else
+ util.warning("command module " .. module .. " does not implement command(), skipping")
+ end
+ else
+ util.warning("failed to load command module " .. module .. ": " .. mod)
+ end
+ end
+
+ local function process_cmdline_vars(...)
+ local args = pack(...)
+ local cmdline_vars = {}
+ local last = args.n
+ for i = 1, args.n do
+ if args[i] == "--" then
+ last = i - 1
+ break
+ end
+ end
+ for i = last, 1, -1 do
+ local arg = args[i]
+ if arg:match("^[^-][^=]*=") then
+ local var, val = arg:match("^([A-Z_][A-Z0-9_]*)=(.*)")
+ if val then
+ cmdline_vars[var] = val
+ table.remove(args, i)
+ else
+ die("Invalid assignment: "..arg)
+ end
+ end
+ end
+
+ return args, cmdline_vars
+ end
+
+ local args, cmdline_vars = process_cmdline_vars(...)
+ local parser = get_parser(description, cmd_modules)
+ args = parser:parse(args)
+
+ -- Compatibility for old flag
+ if args.nodeps then
+ args.deps_mode = "none"
+ end
+
+ if args.timeout then -- setting it in the config file will kick-in earlier in the process
+ cfg.connection_timeout = args.timeout
+ end
+
+ if args.command == "config" then
+ if args.key == "lua_version" and args.value then
+ args.lua_version = args.value
+ elseif args.key == "lua_dir" and args.value then
+ args.lua_dir = args.value
+ end
+ end
+
+ -----------------------------------------------------------------------------
+ local lua_found, err = init_config(args)
+ if err then
+ die(err)
+ end
+ -----------------------------------------------------------------------------
+
+ -- Now that the config is fully loaded, reinitialize fs using the full
+ -- feature set.
+ fs.init()
+
+ -- if the Lua interpreter wasn't explicitly found before cfg.init,
+ -- try again now.
+ local tried
+ if not lua_found then
+ local detected
+ detected, tried = search_lua(cfg.lua_version, args.verbose, cfg.variables.LUA_DIR)
+ if detected then
+ lua_found = true
+ cfg.variables.LUA = detected.lua
+ cfg.variables.LUA_DIR = detected.lua_dir
+ cfg.variables.LUA_BINDIR = detected.lua_bindir
+ if args.lua_dir then
+ cfg.variables.LUA_INCDIR = nil
+ cfg.variables.LUA_LIBDIR = nil
+ end
+ else
+ cfg.variables.LUA = nil
+ cfg.variables.LUA_DIR = nil
+ cfg.variables.LUA_BINDIR = nil
+ cfg.variables.LUA_INCDIR = nil
+ cfg.variables.LUA_LIBDIR = nil
+ end
+ end
+
+ if lua_found then
+ assert(cfg.variables.LUA)
+ else
+ -- Fallback producing _some_ Lua configuration based on the running interpreter.
+ -- Most likely won't produce correct results when running from the standalone binary,
+ -- so eventually we need to drop this and outright fail if Lua is not found
+ -- or explictly configured
+ if not cfg.variables.LUA then
+ local first_arg = get_first_arg()
+ local bin_dir = dir.dir_name(fs.absolute_name(first_arg))
+ local exe = dir.base_name(first_arg)
+ exe = exe:match("rocks") and ("lua" .. (cfg.arch:match("win") and ".exe" or "")) or exe
+ local full_path = dir.path(bin_dir, exe)
+ if util.check_lua_version(full_path, cfg.lua_version) then
+ cfg.variables.LUA = dir.path(bin_dir, exe)
+ cfg.variables.LUA_DIR = bin_dir:gsub("[/\\]bin[/\\]?$", "")
+ cfg.variables.LUA_BINDIR = bin_dir
+ cfg.variables.LUA_INCDIR = nil
+ cfg.variables.LUA_LIBDIR = nil
+ end
+ end
+ end
+
+ cfg.lua_found = lua_found
+
+ if cfg.project_dir then
+ cfg.project_dir = fs.absolute_name(cfg.project_dir)
+ end
+
+ if args.verbose then
+ cfg.verbose = true
+ print(("-"):rep(79))
+ print("Current configuration:")
+ print(("-"):rep(79))
+ print(config.to_string(cfg))
+ print(("-"):rep(79))
+ fs.verbose()
+ end
+
+ if (not fs.current_dir()) or fs.current_dir() == "" then
+ die("Current directory does not exist. Please run LuaRocks from an existing directory.")
+ end
+
+ local ok, err = process_tree_args(args, cfg.project_dir)
+ if not ok then
+ die(err)
+ end
+
+ ok, err = process_server_args(args)
+ if not ok then
+ die(err)
+ end
+
+ if args.only_sources then
+ cfg.only_sources_from = args.only_sources
+ end
+
+ for k, v in pairs(cmdline_vars) do
+ cfg.variables[k] = v
+ end
+
+ -- if running as superuser, use system cache dir
+ if fs.is_superuser() then
+ cfg.local_cache = dir.path(fs.system_cache_dir(), "luarocks")
+ end
+
+ if args.no_manifest then
+ cfg.no_manifest = true
+ end
+
+ if not args.command then
+ parser:epilog(variables_help..get_config_text(cfg))
+ util.printout()
+ util.printout(parser:get_help())
+ util.printout()
+ os.exit(cmd.errorcodes.OK)
+ end
+
+ if not cfg.variables["LUA"] and args.command ~= "config" and args.command ~= "help" then
+ local flag = (not cfg.project_tree)
+ and "--local "
+ or ""
+ if args.lua_version then
+ flag = "--lua-version=" .. args.lua_version .. " " .. flag
+ end
+ die((tried or "Lua interpreter not found.") ..
+ "\nPlease set your Lua interpreter with:\n\n" ..
+ " luarocks " .. flag.. "config variables.LUA " .. lua_example .. "\n")
+ end
+
+ local cmd_mod = cmd_modules[args.command]
+
+ local lock
+ if cmd_mod.needs_lock and cmd_mod.needs_lock(args) then
+ local ok, err = fs.check_command_permissions(args)
+ if not ok then
+ die(err, cmd.errorcodes.PERMISSIONDENIED)
+ end
+
+ lock, err = fs.lock_access(path.root_dir(cfg.root_dir), args.force_lock)
+ if not lock then
+ err = args.force_lock
+ and ("failed to force the lock" .. (err and ": " .. err or ""))
+ or (err and err ~= "File exists")
+ and err
+ or "try --force-lock to overwrite the lock"
+
+ die("command '" .. args.command .. "' " ..
+ "requires exclusive write access to " .. path.root_dir(cfg.root_dir) .. " - " ..
+ err, cmd.errorcodes.LOCK)
+ end
+ end
+
+ local call_ok, ok, err, exitcode = xpcall(function()
+ return cmd_mod.command(args)
+ end, error_handler)
+
+ if lock then
+ fs.unlock_access(lock)
+ end
+
+ if not call_ok then
+ die(ok, cmd.errorcodes.CRASH)
+ elseif not ok then
+ die(err, exitcode)
+ end
+ util.run_scheduled_functions()
+end
+
+return cmd