From 4c1c170b9d344b6c069c8e6df5798f6e0bd63966 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Wed, 20 Jun 2018 10:28:49 -0300 Subject: cfg, cmd: major reorganization * `--lua-dir` flag, specifying a prefix for the Lua installation to be used. This reconfigures LuaRocks entirely, including allowing a LuaRocks which is itself running using one Lua 5.x version to manage packages for a different Lua 5.y version. The resulting configuration can be checked with `luarocks config --lua-dir=`. * requiring `luarocks.core.cfg` no longer has side-effects * configuration now needs to be initialized with `cfg.init([lua_data])`, where `lua_data` is a table with the configuration of the VM: * `lua_version` - e.g. `"5.3"` * `luajit_version` - e.g. `"2.1.0-beta3"` * `lua_interpreter` - e.g. `"lua5.3"` * `lua_bindir` - e.g. `"/usr/local/bin"` * `lua_libdir` - e.g. `"/usr/local/lib"` * `lua_incdir` - e.g. `"/usr/local/include/lua-5.3"` * `cfg.init` can be called multiple times, reconfiguring the global state. This is important since `luarocks.loader` now calls it, and the `--lua-dir` command line can override the configuration and reconfigure LuaRocks. * `site_config_*` is no more: LuaRocks is no longer dependent on a properly-written site_config file. Instead, it can *optionally* use `luarocks.core.hardcoded` for hardcoded values, or detect its configuration at runtime, based on OS detection, arg[-1] or command-line flags. * reduction of moving parts in the configuration: * `cfg.platforms` is no longer a globally-visible table; instead, `cfg` provides an API of read-only functions: `is_platform`, `each_platform`, `print_platforms`. * `cfg.*_proxy` options are no longer configured via the config files, but rather via the standard `*_proxy` environment variables. * `"windows"` is now the more general platform name of the Windows family. This is technically a breaking change but I don't expect it to cause problems with real-world rockspecs. * internal code reorganization in `luarocks.cmd` module --- src/luarocks/cmd.lua | 427 ++++++++++----- src/luarocks/cmd/help.lua | 1 + src/luarocks/core/cfg.lua | 1288 +++++++++++++++++++++++---------------------- src/luarocks/deps.lua | 18 +- src/luarocks/fs/lua.lua | 8 +- src/luarocks/loader.lua | 5 +- src/luarocks/util.lua | 1 + 7 files changed, 963 insertions(+), 785 deletions(-) (limited to 'src') diff --git a/src/luarocks/cmd.lua b/src/luarocks/cmd.lua index 51d22154..70d93670 100644 --- a/src/luarocks/cmd.lua +++ b/src/luarocks/cmd.lua @@ -4,13 +4,13 @@ local cmd = {} local unpack = unpack or table.unpack -local util = require("luarocks.util") local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") local path = require("luarocks.path") -local dir = require("luarocks.dir") local deps = require("luarocks.deps") -local fs = require("luarocks.fs") +local dir = require("luarocks.dir") local fun = require("luarocks.fun") +local fs = require("luarocks.fs") local program = util.this_program("luarocks") @@ -22,12 +22,6 @@ cmd.errorcodes = { CRASH = 99 } -local function replace_tree(flags, tree) - tree = dir.normalize(tree) - flags["tree"] = tree - path.use_tree(tree) -end - local function is_ownership_ok(directory) local me = fs.current_user() for _ = 1,3 do -- try up to grandparent @@ -40,16 +34,240 @@ local function is_ownership_ok(directory) return false end -local function find_project_dir() - local try = "." - for _ = 1, 10 do -- FIXME detect when root dir was hit instead - local abs = fs.absolute_name(try) - if fs.is_dir(abs .. "/.luarocks") and fs.is_dir(abs .. "/lua_modules") then - abs = abs:gsub("/.$", "") - return abs, abs .. "/lua_modules" +do + local function check_lua_version(lua_exe, luaver) + local luajitver + if not (luaver and luaver:match("^[0-9]")) then + luaver = util.popen_read(lua_exe .. ' -e "io.write(_VERSION:sub(5))"') + end + if luaver == "5.1" then + luajitver = util.popen_read(lua_exe .. ' -e "io.write(tostring(jit and jit.version:sub(8)))"') + if luajitver == "nil" then + luajitver = nil + end + end + return luaver, luajitver + end + + local function exists(file) + local fd = io.open(file, "r") + if fd then + fd:close() + return true + end + return false + end + + local find_lua_bindir + do + local exe_suffix = (package.config:sub(1, 1) == "\\" and ".exe" or "") + + local function insert_lua_versions(names, luaver) + local variants = { + "lua" .. luaver .. exe_suffix, + "lua" .. luaver:gsub("%.", "") .. exe_suffix, + "lua-" .. luaver .. exe_suffix, + "lua-" .. luaver:gsub("%.", "") .. exe_suffix, + } + for _, name in ipairs(variants) do + names[name] = luaver + table.insert(names, name) + end + end + + find_lua_bindir = function(prefix, luaver) + local names = {} + if luaver then + insert_lua_versions(names, luaver) + else + for v in util.lua_versions("descending") do + insert_lua_versions(names, v) + end + end + table.insert(names, "lua" .. exe_suffix) + table.insert(names, "luajit" .. exe_suffix) + + local bindirs = { prefix .. "/bin", prefix } + local tried = {} + for _, d in ipairs(bindirs) do + for _, name in ipairs(names) do + local lua_exe = dir.path(d, name) + table.insert(tried, lua_exe) + if exists(lua_exe) then + return name, d, luaver + end + end + end + return nil, "Lua interpreter not found at " .. prefix .. "\n" .. + "Tried:\t" .. table.concat(tried, "\n\t") + end + end + + local function find_lua_incdir(prefix, luaver, luajitver) + luajitver = luajitver and luajitver:gsub("%-.*", "") + local incdirs = { + prefix .. "/include", + prefix .. "/include/lua/" .. luaver, + prefix .. "/include/lua" .. luaver, + prefix, + luajitver and prefix .. "/include/luajit-" .. luajitver, + } + + for _, d in ipairs(incdirs) do + local lua_h = dir.path(d, "lua.h") + if exists(lua_h) then + return d + end + end + + -- fallback to a default, as it is not necessarily needed. + return incdirs[1] + end + + find_lua = function(prefix, luaver) + local lua_interpreter, bindir + lua_interpreter, bindir, luaver = find_lua_bindir(prefix, luaver) + if not lua_interpreter then + return nil, bindir + end + + local luajitver + local lua_exe = dir.path(bindir, lua_interpreter) + luaver, luajitver = check_lua_version(lua_exe, luaver) + + return { + lua_version = luaver, + luajit_version = luajitver, + lua_interpreter = lua_interpreter, + lua_dir = prefix, + lua_bindir = bindir, + lua_incdir = find_lua_incdir(prefix, luaver, luajitver), + lua_libdir = prefix .. "/lib", + } + end +end + +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_flags +do + local function replace_tree(flags, tree) + tree = dir.normalize(tree) + flags["tree"] = tree + path.use_tree(tree) + end + + local function find_project_dir() + local try = "." + for _ = 1, 10 do -- FIXME detect when root dir was hit instead + local abs = fs.absolute_name(try) + if fs.is_dir(abs .. "/.luarocks") and fs.is_dir(abs .. "/lua_modules") then + abs = abs:gsub("/.$", "") + return abs, abs .. "/lua_modules" + end + try = try .. "/.." + end + 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 + + process_tree_flags = function(flags) + + if cfg.local_by_default then + flags["local"] = true + end + + if flags["tree"] then + local named = false + for _, tree in ipairs(cfg.rocks_trees) do + if type(tree) == "table" and flags["tree"] == tree.name then + if not tree.root then + return nil, "Configuration error: tree '"..tree.name.."' has no 'root' field." + end + replace_tree(flags, tree.root) + named = true + break + end + end + if not named then + local root_dir = fs.absolute_name(flags["tree"]) + replace_tree(flags, root_dir) + end + elseif flags["project-tree"] then + local tree = flags["project-tree"] + table.insert(cfg.rocks_trees, 1, { name = "project", root = tree } ) + path.use_tree(tree) + elseif flags["local"] then + if not cfg.home_tree 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." + end + replace_tree(flags, cfg.home_tree) + else + local project_dir, rocks_tree = find_project_dir() + if project_dir then + table.insert(cfg.rocks_trees, 1, { name = "project", root = rocks_tree } ) + path.use_tree(rocks_tree) + else + local trees = cfg.rocks_trees + path.use_tree(trees[#trees]) + end + 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_flags(flags) + if flags["server"] then + local protocol, pathname = dir.split_url(flags["server"]) + table.insert(cfg.rocks_servers, 1, protocol.."://"..pathname) + end + + if flags["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 flags["only-server"] then + if flags["dev"] then + return nil, "--only-server cannot be used with --dev" end - try = try .. "/.." + if flags["server"] then + return nil, "--only-server cannot be used with --server" + end + cfg.rocks_servers = { flags["only-server"] } end + + return true end --- Main command-line processor. @@ -58,9 +276,13 @@ end -- to it any additional arguments passed by the user. -- Uses the global table "commands", which contains -- the loaded modules representing commands. +-- @param program_version string: The program version number as a string. +-- @param description string: Short summary description of the program. -- @param ... string: Arguments given on the command-line. function cmd.run_command(description, commands, ...) + check_popen() + local function error_handler(err) return debug.traceback("LuaRocks "..cfg.program_version.. " bug (please report at https://github.com/luarocks/luarocks/issues).\n"..err, 2) @@ -82,45 +304,39 @@ function cmd.run_command(description, commands, ...) os.exit(exitcode or cmd.errorcodes.UNSPECIFIED) end - local args = {...} - local cmdline_vars = {} - for i = #args, 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) + local function process_arguments(...) + local args = {...} + local cmdline_vars = {} + for i = #args, 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 - end - local nonflags = { util.parse_flags(unpack(args)) } - local flags = table.remove(nonflags, 1) - if flags.ERROR then - die(flags.ERROR.." See --help.") - end - - fs.init() + local nonflags = { util.parse_flags(unpack(args)) } + local flags = table.remove(nonflags, 1) + if flags.ERROR then + die(flags.ERROR.." See --help.") + end - if flags["from"] then flags["server"] = flags["from"] end - if flags["only-from"] then flags["only-server"] = flags["only-from"] end - if flags["only-sources-from"] then flags["only-sources"] = flags["only-sources-from"] end - if flags["to"] then flags["tree"] = flags["to"] end - if flags["nodeps"] then - flags["deps-mode"] = "none" - end - - cfg.flags = flags + -- Compatibility for old names of some flags + if flags["to"] then flags["tree"] = flags["to"] end + if flags["from"] then flags["server"] = flags["from"] end + if flags["nodeps"] then flags["deps-mode"] = "none" end + if flags["only-from"] then flags["only-server"] = flags["only-from"] end + if flags["only-sources-from"] then flags["only-sources"] = flags["only-sources-from"] end - local command - - if flags["verbose"] then -- setting it in the config file will kick-in earlier in the process - cfg.verbose = true - fs.verbose() + return flags, nonflags, cmdline_vars end + local flags, nonflags, cmdline_vars = process_arguments(...) + if flags["timeout"] then -- setting it in the config file will kick-in earlier in the process local timeout = tonumber(flags["timeout"]) if timeout then @@ -130,6 +346,7 @@ function cmd.run_command(description, commands, ...) end end + local command if flags["version"] then util.printout(program.." "..cfg.program_version) util.printout(description) @@ -141,101 +358,55 @@ function cmd.run_command(description, commands, ...) command = table.remove(nonflags, 1) end command = command:gsub("-", "_") - - if cfg.local_by_default then - flags["local"] = true - end if flags["deps-mode"] and not deps.check_deps_mode_flag(flags["deps-mode"]) then die("Invalid entry for --deps-mode.") end - - if flags["tree"] then - local named = false - for _, tree in ipairs(cfg.rocks_trees) do - if type(tree) == "table" and flags["tree"] == tree.name then - if not tree.root then - die("Configuration error: tree '"..tree.name.."' has no 'root' field.") - end - replace_tree(flags, tree.root) - named = true - break - end - end - if not named then - local root_dir = fs.absolute_name(flags["tree"]) - replace_tree(flags, root_dir) - end - elseif flags["project-tree"] then - local tree = flags["project-tree"] - table.insert(cfg.rocks_trees, 1, { name = "project", root = tree } ) - path.use_tree(tree) - elseif flags["local"] then - if not cfg.home_tree then - die("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.") - end - replace_tree(flags, cfg.home_tree) - else - local project_dir, rocks_tree = find_project_dir() - if project_dir then - table.insert(cfg.rocks_trees, 1, { name = "project", root = rocks_tree } ) - path.use_tree(rocks_tree) - else - local trees = cfg.rocks_trees - path.use_tree(trees[#trees]) - end + + local lua_data + if flags["lua-dir"] then + lua_data = find_lua(flags["lua-dir"], flags["lua-version"]) end - if type(cfg.root_dir) == "string" then - cfg.root_dir = cfg.root_dir:gsub("/+$", "") - else - cfg.root_dir.root = cfg.root_dir.root:gsub("/+$", "") + ----------------------------------------------------------------------------- + local ok, err = cfg.init(lua_data, util.warning) + if not ok then + die(err) 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("/+$", "") - - cfg.variables.ROCKS_TREE = cfg.rocks_dir - cfg.variables.SCRIPTS_DIR = cfg.deploy_bin_dir + ----------------------------------------------------------------------------- - if flags["server"] then - local protocol, pathname = dir.split_url(flags["server"]) - table.insert(cfg.rocks_servers, 1, protocol.."://"..pathname) + fs.init() + + if flags["verbose"] then + cfg.verbose = true + fs.verbose() end - if flags["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) + if (not fs.current_dir()) or fs.current_dir() == "" then + die("Current directory does not exist. Please run LuaRocks from an existing directory.") end - - if flags["only-server"] then - if flags["dev"] then - die("--only-server cannot be used with --dev") - end - if flags["server"] then - die("--only-server cannot be used with --server") - end - cfg.rocks_servers = { flags["only-server"] } + + local ok, err + ok, err = process_tree_flags(flags) + if not ok then + die(err) + end + + ok, err = process_server_flags(flags) + if not ok then + die(err) end if flags["only-sources"] then cfg.only_sources_from = flags["only-sources"] end - + if command ~= "help" then for k, v in pairs(cmdline_vars) do cfg.variables[k] = v end 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 - if not is_ownership_ok(cfg.local_cache) then util.warning("The directory '" .. cfg.local_cache .. "' or its parent directory ".. "is not owned by the current user and the cache has been disabled. ".. @@ -246,10 +417,16 @@ function cmd.run_command(description, commands, ...) cfg.local_cache = fs.make_temp_dir("local_cache") util.schedule_function(fs.delete, cfg.local_cache) end - + if commands[command] then local cmd_mod = require(commands[command]) - local call_ok, ok, err, exitcode = xpcall(function() return cmd_mod.command(flags, unpack(nonflags)) end, error_handler) + local call_ok, ok, err, exitcode = xpcall(function() + if command == "help" then + return cmd_mod.command(description, commands, unpack(nonflags)) + else + return cmd_mod.command(flags, unpack(nonflags)) + end + end, error_handler) if not call_ok then die(ok, cmd.errorcodes.CRASH) elseif not ok then diff --git a/src/luarocks/cmd/help.lua b/src/luarocks/cmd/help.lua index 1e1d8676..3b13acc2 100644 --- a/src/luarocks/cmd/help.lua +++ b/src/luarocks/cmd/help.lua @@ -63,6 +63,7 @@ function help.command(description, commands, command) (overrides any entries in the config file) --only-sources= Restrict downloads to paths matching the given URL. + --lua-dir= Which Lua installation to use. --tree= Which tree to operate on. --local Use the tree in the user's home directory. To enable it, see ']]..program..[[ help path'. diff --git a/src/luarocks/core/cfg.lua b/src/luarocks/core/cfg.lua index 7c29e2dc..cd3aadbc 100644 --- a/src/luarocks/core/cfg.lua +++ b/src/luarocks/core/cfg.lua @@ -10,106 +10,19 @@ -- files. Run `luarocks` with no arguments to see the locations of -- these files in your platform. -local rawset, next, table, pairs, require, io, os, setmetatable, pcall, ipairs, package, tonumber, type, assert, _VERSION = - rawset, next, table, pairs, require, io, os, setmetatable, pcall, ipairs, package, tonumber, type, assert, _VERSION - -local cfg = {} - -cfg.lua_version = _VERSION:match(" (5%.[1234])$") or "5.1" -local version_suffix = cfg.lua_version:gsub("%.", "_") - --- Load site-local global configurations -local ok, site_config = pcall(require, "luarocks.core.site_config_"..version_suffix) -if not ok then - io.stderr:write("Site-local luarocks/core/site_config_"..version_suffix..".lua file not found. Incomplete installation?\n") - site_config = {} -end +local next, table, pairs, require, os, pcall, ipairs, package, tonumber, type, assert = + next, table, pairs, require, os, pcall, ipairs, package, tonumber, type, assert local util = require("luarocks.core.util") local persist = require("luarocks.core.persist") -local require = nil --------------------------------------------------------------------------------- -cfg.program_version = "dev" -cfg.program_series = "3.0" -cfg.major_version = (cfg.program_version:match("([^.]%.[^.])")) or cfg.program_series -cfg.variables = {} -cfg.rocks_trees = {} -cfg.platforms = {} - -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(1) -- FIXME -end +-------------------------------------------------------------------------------- --- System detection: - --- A proper installation of LuaRocks will hardcode the system --- and proc values with site_config.LUAROCKS_UNAME_S and site_config.LUAROCKS_UNAME_M, --- so that this detection does not run every time. When it is --- performed, we use the Unix way to identify the system, --- even on Windows (assuming UnxUtils or Cygwin). -local system = site_config.LUAROCKS_UNAME_S or util.popen_read("uname -s") -local proc = site_config.LUAROCKS_UNAME_M or util.popen_read("uname -m") -if proc:match("i[%d]86") then - cfg.target_cpu = "x86" -elseif proc:match("amd64") or proc:match("x86_64") then - cfg.target_cpu = "x86_64" -elseif proc:match("Power Macintosh") then - cfg.target_cpu = "powerpc" - else - cfg.target_cpu = proc -end +local program_version = "dev" +local program_series = "3.0" +local major_version = (program_version:match("([^.]%.[^.])")) or program_series -if system == "FreeBSD" then - cfg.platforms.unix = true - cfg.platforms.freebsd = true - cfg.platforms.bsd = true -elseif system == "OpenBSD" then - cfg.platforms.unix = true - cfg.platforms.openbsd = true - cfg.platforms.bsd = true -elseif system == "NetBSD" then - cfg.platforms.unix = true - cfg.platforms.netbsd = true - cfg.platforms.bsd = true -elseif system == "Darwin" then - cfg.platforms.unix = true - cfg.platforms.macosx = true - cfg.platforms.bsd = true -elseif system == "Linux" then - cfg.platforms.unix = true - cfg.platforms.linux = true -elseif system == "SunOS" then - cfg.platforms.unix = true - cfg.platforms.solaris = true -elseif system and system:match("^CYGWIN") then - cfg.platforms.unix = true - cfg.platforms.cygwin = true -elseif system and system:match("^MSYS") then - cfg.platforms.unix = true - cfg.platforms.msys = true - cfg.platforms.cygwin = true -elseif system and system:match("^Windows") then - cfg.platforms.windows = true - cfg.platforms.win32 = true -elseif system and system:match("^MINGW") then - cfg.platforms.windows = true - cfg.platforms.mingw32 = true - cfg.platforms.win32 = true -elseif system == "Haiku" then - cfg.platforms.unix = true - cfg.platforms.haiku = true -else - cfg.platforms.unix = true - -- Fall back to Unix in unknown systems. -end +local is_windows = package.config:sub(1,1) == "\\" -- Set order for platform overrides. -- More general platform identifiers should be listed first, @@ -128,630 +41,719 @@ local platform_order = { "msys", "haiku", -- Windows + "windows", "win32", "mingw32", - "windows", } --- Path configuration: -local sys_config_file, home_config_file -local sys_config_file_default, home_config_file_default -local sys_config_dir, home_config_dir -local sys_config_ok, home_config_ok = false, false -local extra_luarocks_module_dir -sys_config_dir = site_config.LUAROCKS_SYSCONFDIR or site_config.LUAROCKS_PREFIX -if cfg.platforms.windows then - cfg.home = os.getenv("APPDATA") or "c:" - sys_config_dir = sys_config_dir or "c:/luarocks" - home_config_dir = cfg.home.."/luarocks" - cfg.home_tree = cfg.home.."/luarocks/" -else - cfg.home = os.getenv("HOME") or "" - sys_config_dir = sys_config_dir or "/etc/luarocks" - home_config_dir = cfg.home.."/.luarocks" - cfg.home_tree = (os.getenv("USER") ~= "root") and cfg.home.."/.luarocks/" -end +local load_config_file +do + -- Create global environment for the config files; + local function env_for_config_file(cfg, platforms) + local e + e = { + home = cfg.home, + lua_version = cfg.lua_version, + platforms = util.make_shallow_copy(platforms), + processor = cfg.target_cpu, -- remains for compat reasons + target_cpu = cfg.target_cpu, -- replaces `processor` + os_getenv = os.getenv, + variables = cfg.variables or {}, + dump_env = function() + -- debug function, calling it from a config file will show all + -- available globals to that config file + print(util.show_table(e, "global environment")) + end, + } + return e + end --- Create global environment for the config files; -local env_for_config_file = function() - local e - e = { - home = cfg.home, - lua_version = cfg.lua_version, - platforms = util.make_shallow_copy(cfg.platforms), - processor = cfg.target_cpu, -- remains for compat reasons - target_cpu = cfg.target_cpu, -- replaces `processor` - os_getenv = os.getenv, - dump_env = function() - -- debug function, calling it from a config file will show all - -- available globals to that config file - print(util.show_table(e, "global environment")) - end, - } - return e -end + -- Merge values from config files read into the `cfg` table + local function merge_overrides(cfg, overrides) + -- remove some stuff we do not want to integrate + overrides.os_getenv = nil + overrides.dump_env = nil + -- remove tables to be copied verbatim instead of deeply merged + if overrides.rocks_trees then cfg.rocks_trees = nil end + if overrides.rocks_servers then cfg.rocks_servers = nil end + -- perform actual merge + util.deep_merge(cfg, overrides) + end + + local function update_platforms(platforms, overrides) + if overrides[1] then + for k, _ in pairs(platforms) do + platforms[k] = nil + end + for _, v in ipairs(overrides) do + platforms[v] = true + end + -- set some fallback default in case the user provides an incomplete configuration. + -- LuaRocks expects a set of defaults to be available. + if not (platforms.unix or platforms.windows) then + platforms[is_windows and "windows" or "unix"] = true + end + end + end --- Merge values from config files read into the `cfg` table -local merge_overrides = function(overrides) - -- remove some stuff we do not want to integrate - overrides.os_getenv = nil - overrides.dump_env = nil - -- remove tables to be copied verbatim instead of deeply merged - if overrides.rocks_trees then cfg.rocks_trees = nil end - if overrides.rocks_servers then cfg.rocks_servers = nil end - -- perform actual merge - util.deep_merge(cfg, overrides) + -- Load config file and merge its contents into the `cfg` module table. + -- @return filepath of succesfully loaded file or nil if it failed + load_config_file = function(cfg, platforms, filepath) + local result, err, errcode = persist.load_into_table(filepath, env_for_config_file(cfg, platforms)) + if (not result) and errcode ~= "open" then + -- errcode is either "load" or "run"; bad config file, so error out + return nil, err, "config" + end + if result then + -- success in loading and running, merge contents and exit + update_platforms(platforms, result.platforms) + result.platforms = nil + merge_overrides(cfg, result) + return filepath + end + return nil -- nothing was loaded + end end --- Load config file and merge its contents into the `cfg` module table. --- @return filepath of succesfully loaded file or nil if it failed -local load_config_file = function(filepath) - local result, err, errcode = persist.load_into_table(filepath, env_for_config_file()) - if (not result) and errcode ~= "open" then - -- errcode is either "load" or "run"; bad config file, so error out - io.stderr:write(err.."\n") - os.exit(3) -- FIXME - end - if result then - -- success in loading and running, merge contents and exit - merge_overrides(result) - return filepath - end - return nil -- nothing was loaded +local function make_platforms(system) + if system then + if system == "Linux" then return { unix = true, linux = true } + elseif system == "FreeBSD" then return { unix = true, bsd = true, freebsd = true } + elseif system == "OpenBSD" then return { unix = true, bsd = true, openbsd = true } + elseif system == "NetBSD" then return { unix = true, bsd = true, netbsd = true } + elseif system == "Darwin" then return { unix = true, bsd = true, macosx = true, macos = true } + elseif system == "SunOS" then return { unix = true, solaris = true } + elseif system == "Haiku" then return { unix = true, haiku = true } + elseif system:match("^CYGWIN") then return { unix = true, cygwin = true } + elseif system:match("^MSYS") then return { unix = true, cygwin = true, msys = true } + elseif system:match("^Windows") then return { windows = true, win32 = true } + elseif system:match("^MINGW") then return { windows = true, win32 = true, mingw32 = true, mingw = true } + end + end + return { unix = true } -- fallback to Unix in unknown systems end +-------------------------------------------------------------------------------- --- Load system configuration file -do - sys_config_file_default = sys_config_dir.."/config-"..cfg.lua_version..".lua" - sys_config_file = load_config_file( - site_config.LUAROCKS_SYSCONFIG or sys_config_file_default - ) - sys_config_ok = (sys_config_file ~= nil) -end +local function make_defaults(lua_version, target_cpu, platforms, home) + + -- Configure defaults: + local defaults = { + + lua_interpreter = "lua", + local_by_default = false, + accept_unknown_fields = false, + fs_use_modules = true, + hooks_enabled = true, + deps_mode = "one", + check_certificates = false, + + lua_modules_path = "/share/lua/"..lua_version, + lib_modules_path = "/lib/lua/"..lua_version, + rocks_subdir = "/lib/luarocks/rocks-"..lua_version, + + arch = "unknown", + lib_extension = "unknown", + obj_extension = "unknown", + link_lua_explicitly = false, + + rocks_servers = { + { + "https://luarocks.org", + "https://raw.githubusercontent.com/rocks-moonscript-org/moonrocks-mirror/master/", + "http://luafr.org/moonrocks/", + "http://luarocks.logiceditor.com/rocks", + } + }, + disabled_servers = {}, --- Load user configuration file (if allowed) -if not site_config.LUAROCKS_FORCE_CONFIG then - - home_config_file_default = home_config_dir.."/config-"..cfg.lua_version..".lua" - - local config_env_var = "LUAROCKS_CONFIG_" .. version_suffix - local config_env_value = os.getenv(config_env_var) - if not config_env_value then - config_env_var = "LUAROCKS_CONFIG" - config_env_value = os.getenv(config_env_var) - end - - -- first try environment provided file, so we can explicitly warn when it is missing - if config_env_value then - home_config_file = load_config_file(config_env_value) - home_config_ok = (home_config_file ~= nil) - if not home_config_ok then - io.stderr:write("Warning: could not load configuration file `"..config_env_value.."` given in environment variable "..config_env_var.."\n") + upload = { + server = "https://luarocks.org", + tool_version = "1.0.0", + api_version = "1", + }, + + lua_extension = "lua", + connection_timeout = 30, -- 0 = no timeout + + variables = { + MAKE = "make", + CC = "cc", + LD = "ld", + AR = "ar", + RANLIB = "ranlib", + + CVS = "cvs", + GIT = "git", + SSCM = "sscm", + SVN = "svn", + HG = "hg", + + RSYNC = "rsync", + WGET = "wget", + SCP = "scp", + CURL = "curl", + + PWD = "pwd", + MKDIR = "mkdir", + RMDIR = "rmdir", + CP = "cp", + LS = "ls", + RM = "rm", + FIND = "find", + TEST = "test", + CHMOD = "chmod", + ICACLS = "icacls", + MKTEMP = "mktemp", + + ZIP = "zip", + UNZIP = "unzip -n", + GUNZIP = "gunzip", + BUNZIP2 = "bunzip2", + TAR = "tar", + + MD5SUM = "md5sum", + OPENSSL = "openssl", + MD5 = "md5", + STAT = "stat", + TOUCH = "touch", + + CMAKE = "cmake", + SEVENZ = "7z", + + RSYNCFLAGS = "--exclude=.git -Oavz", + STATPERMFLAG = "-c '%a'", + STATOWNERFLAG = "-c '%U'", + CURLNOCERTFLAG = "", + WGETNOCERTFLAG = "", + }, + + external_deps_subdirs = { + bin = "bin", + lib = "lib", + include = "include" + }, + runtime_external_deps_subdirs = { + bin = "bin", + lib = "lib", + include = "include" + }, + } + + if platforms.windows then + + defaults.arch = "win32-"..target_cpu + defaults.lib_extension = "dll" + defaults.external_lib_extension = "dll" + defaults.static_lib_extension = "lib" + defaults.obj_extension = "obj" + defaults.external_deps_dirs = { "c:/external/", "c:/windows/system32" } + + defaults.makefile = "Makefile.win" + defaults.variables.MAKE = "nmake" + defaults.variables.CC = "cl" + defaults.variables.RC = "rc" + defaults.variables.LD = "link" + defaults.variables.MT = "mt" + defaults.variables.AR = "lib" + defaults.variables.LUALIB = "lua"..lua_version..".lib" + defaults.variables.CFLAGS = "/nologo /MD /O2" + defaults.variables.LIBFLAG = "/nologo /dll" + + defaults.variables.LUA_DIR = "c:/lua" + defaults.variables.LUA_BINDIR = "c:/lua/bin" + defaults.variables.LUA_LIBDIR = "c:/lua/lib" + defaults.variables.LUA_INCDIR = "c:/lua/include" + + defaults.external_deps_patterns = { + bin = { "?.exe", "?.bat" }, + lib = { "?.lib", "?.dll", "lib?.dll" }, + include = { "?.h" } + } + defaults.runtime_external_deps_patterns = { + bin = { "?.exe", "?.bat" }, + lib = { "?.dll", "lib?.dll" }, + include = { "?.h" } + } + defaults.export_path_separator = ";" + defaults.wrapper_suffix = ".bat" + + local localappdata = os.getenv("LOCALAPPDATA") + if not localappdata then + -- for Windows versions below Vista + localappdata = os.getenv("USERPROFILE").."/Local Settings/Application Data" end + defaults.local_cache = localappdata.."/LuaRocks/Cache" + defaults.web_browser = "start" + + defaults.external_deps_subdirs.lib = { "", "lib" } + defaults.runtime_external_deps_subdirs.lib = { "", "lib" } end - -- try the alternative defaults if there was no environment specified file or it didn't work - if not home_config_ok then - home_config_file = load_config_file(home_config_file_default) - home_config_ok = (home_config_file ~= nil) + if platforms.mingw32 then + defaults.obj_extension = "o" + defaults.static_lib_extension = "a" + defaults.external_deps_dirs = { "c:/external/", "c:/mingw", "c:/windows/system32" } + defaults.cmake_generator = "MinGW Makefiles" + defaults.variables.MAKE = "mingw32-make" + defaults.variables.CC = "mingw32-gcc" + defaults.variables.RC = "windres" + defaults.variables.LD = "mingw32-gcc" + defaults.variables.AR = "ar" + defaults.variables.RANLIB = "ranlib" + defaults.variables.CFLAGS = "-O2" + defaults.variables.LIBFLAG = "-shared" + defaults.makefile = "Makefile" + defaults.external_deps_patterns = { + bin = { "?.exe", "?.bat" }, + -- mingw lookup list from http://stackoverflow.com/a/15853231/1793220 + -- ...should we keep ?.lib at the end? It's not in the above list. + lib = { "lib?.dll.a", "?.dll.a", "lib?.a", "cyg?.dll", "lib?.dll", "?.dll", "?.lib" }, + include = { "?.h" } + } + defaults.runtime_external_deps_patterns = { + bin = { "?.exe", "?.bat" }, + lib = { "cyg?.dll", "?.dll", "lib?.dll" }, + include = { "?.h" } + } end -end + if platforms.unix then + defaults.lib_extension = "so" + defaults.static_lib_extension = "a" + defaults.external_lib_extension = "so" + defaults.obj_extension = "o" + defaults.external_deps_dirs = { "/usr/local", "/usr" } + defaults.variables.CFLAGS = "-O2" + defaults.cmake_generator = "Unix Makefiles" + defaults.variables.CC = "gcc" + defaults.variables.LD = "gcc" + defaults.gcc_rpath = true + defaults.variables.LIBFLAG = "-shared" + + defaults.variables.LUA_DIR = "/usr/local" + defaults.variables.LUA_BINDIR = "/usr/local/bin" + defaults.variables.LUA_LIBDIR = "/usr/local/lib" + defaults.variables.LUA_INCDIR = "/usr/local/include" + + defaults.external_deps_patterns = { + bin = { "?" }, + lib = { "lib?.a", "lib?.so", "lib?.so.*" }, + include = { "?.h" } + } + defaults.runtime_external_deps_patterns = { + bin = { "?" }, + lib = { "lib?.so", "lib?.so.*" }, + include = { "?.h" } + } + defaults.export_path_separator = ":" + defaults.wrapper_suffix = "" + defaults.local_cache = home.."/.cache/luarocks" + if not defaults.variables.CFLAGS:match("-fPIC") then + defaults.variables.CFLAGS = defaults.variables.CFLAGS.." -fPIC" + end + defaults.web_browser = "xdg-open" + end -if not next(cfg.rocks_trees) then - if cfg.home_tree then - table.insert(cfg.rocks_trees, { name = "user", root = cfg.home_tree } ) + if platforms.cygwin then + defaults.lib_extension = "so" -- can be overridden in the config file for mingw builds + defaults.arch = "cygwin-"..target_cpu + defaults.cmake_generator = "Unix Makefiles" + defaults.variables.CC = "echo -llua | xargs gcc" + defaults.variables.LD = "echo -llua | xargs gcc" + defaults.variables.LIBFLAG = "-shared" + defaults.link_lua_explicitly = true end - if site_config.LUAROCKS_ROCKS_TREE then - table.insert(cfg.rocks_trees, { name = "system", root = site_config.LUAROCKS_ROCKS_TREE } ) + + if platforms.msys then + -- msys is basically cygwin made out of mingw, meaning the subsytem is unixish + -- enough, yet we can freely mix with native win32 + defaults.external_deps_patterns = { + bin = { "?.exe", "?.bat", "?" }, + lib = { "lib?.so", "lib?.so.*", "lib?.dll.a", "?.dll.a", + "lib?.a", "lib?.dll", "?.dll", "?.lib" }, + include = { "?.h" } + } + defaults.runtime_external_deps_patterns = { + bin = { "?.exe", "?.bat" }, + lib = { "lib?.so", "?.dll", "lib?.dll" }, + include = { "?.h" } + } end -end --- update platforms list; keyed -> array -do - -- if explicitly given by user, - if cfg.platforms[1] then - local is_windows = cfg.platforms.windows - -- Clear auto-detected values - for k, _ in pairs(cfg.platforms) do - if type(k) == "string" then - cfg.platforms[k] = nil - end - end - -- and set the ones given by the user. - for _, plat in ipairs(cfg.platforms) do - cfg.platforms[plat] = true - end - -- If no major platform family was set by the user, - if not (cfg.platforms.unix or cfg.platforms.windows) then - -- set some fallback defaults in case the user provides an incomplete configuration. - -- LuaRocks expects a set of defaults to be available. - -- This is used for setting defaults here only; the platform overrides - -- will use only the user's list. - if is_windows then - cfg.platforms.windows = true - table.insert(cfg.platforms, "windows") - else - cfg.platforms.unix = true - table.insert(cfg.platforms, "unix") - end - end - else - -- Sort detected platform defaults - local order = {} - for i, v in ipairs(platform_order) do - order[v] = i - end - local entries = {} - for k, v in pairs(cfg.platforms) do - if type(k) == "string" and v == true then - table.insert(entries, k) - end + if platforms.bsd then + defaults.variables.MAKE = "gmake" + defaults.variables.STATPERMFLAG = "-f '%OLp'" + defaults.variables.STATOWNERFLAG = "-f '%Su'" + end + + if platforms.macosx then + defaults.variables.MAKE = "make" + defaults.external_lib_extension = "dylib" + defaults.arch = "macosx-"..target_cpu + defaults.variables.LIBFLAG = "-bundle -undefined dynamic_lookup -all_load" + defaults.variables.STAT = "/usr/bin/stat" + defaults.variables.STATFLAG = "-f '%A'" + local version = util.popen_read("sw_vers -productVersion") + version = tonumber(version and version:match("^[^.]+%.([^.]+)")) or 3 + if version >= 10 then + version = 8 + elseif version >= 5 then + version = 5 + else + defaults.gcc_rpath = false end - table.sort(entries, function(a, b) return order[a] < order[b] end) - util.deep_merge(cfg.platforms, entries) + defaults.variables.CC = "env MACOSX_DEPLOYMENT_TARGET=10."..version.." gcc" + defaults.variables.LD = "env MACOSX_DEPLOYMENT_TARGET=10."..version.." gcc" + defaults.web_browser = "open" end -end --- Configure defaults: -local defaults = { - - local_by_default = false, - accept_unknown_fields = false, - fs_use_modules = true, - hooks_enabled = true, - deps_mode = "one", - check_certificates = false, - - lua_modules_path = "/share/lua/"..cfg.lua_version, - lib_modules_path = "/lib/lua/"..cfg.lua_version, - rocks_subdir = "/lib/luarocks/rocks-"..cfg.lua_version, - - arch = "unknown", - lib_extension = "unknown", - obj_extension = "unknown", - link_lua_explicitly = false, - - rocks_servers = { - { - "https://luarocks.org", - "https://raw.githubusercontent.com/rocks-moonscript-org/moonrocks-mirror/master/", - "http://luafr.org/moonrocks/", - "http://luarocks.logiceditor.com/rocks", - } - }, - disabled_servers = {}, - - upload = { - server = "https://luarocks.org", - tool_version = "1.0.0", - api_version = "1", - }, - - lua_extension = "lua", - lua_interpreter = site_config.LUA_INTERPRETER or "lua", - downloader = site_config.LUAROCKS_DOWNLOADER or "wget", - md5checker = site_config.LUAROCKS_MD5CHECKER or "md5sum", - connection_timeout = 30, -- 0 = no timeout - - variables = { - MAKE = "make", - CC = "cc", - LD = "ld", - AR = "ar", - RANLIB = "ranlib", - - CVS = "cvs", - GIT = "git", - SSCM = "sscm", - SVN = "svn", - HG = "hg", - - RSYNC = "rsync", - WGET = "wget", - SCP = "scp", - CURL = "curl", - - PWD = "pwd", - MKDIR = "mkdir", - RMDIR = "rmdir", - CP = "cp", - LS = "ls", - RM = "rm", - FIND = "find", - TEST = "test", - CHMOD = "chmod", - ICACLS = "icacls", - MKTEMP = "mktemp", - - ZIP = "zip", - UNZIP = "unzip -n", - GUNZIP = "gunzip", - BUNZIP2 = "bunzip2", - TAR = "tar", - - MD5SUM = "md5sum", - OPENSSL = "openssl", - MD5 = "md5", - STAT = "stat", - TOUCH = "touch", - - CMAKE = "cmake", - SEVENZ = "7z", - - RSYNCFLAGS = "--exclude=.git -Oavz", - STATPERMFLAG = "-c '%a'", - STATOWNERFLAG = "-c '%U'", - CURLNOCERTFLAG = "", - WGETNOCERTFLAG = "", - }, - - external_deps_subdirs = site_config.LUAROCKS_EXTERNAL_DEPS_SUBDIRS or { - bin = "bin", - lib = "lib", - include = "include" - }, - runtime_external_deps_subdirs = site_config.LUAROCKS_RUNTIME_EXTERNAL_DEPS_SUBDIRS or { - bin = "bin", - lib = "lib", - include = "include" - }, - - rocks_provided = {}, - rocks_provided_3_0 = {}, -} + if platforms.linux then + defaults.arch = "linux-"..target_cpu -if cfg.platforms.windows then - local full_prefix = (site_config.LUAROCKS_PREFIX or (os.getenv("PROGRAMFILES")..[[\LuaRocks]])) - extra_luarocks_module_dir = full_prefix.."/lua/?.lua" - - home_config_file = home_config_file and home_config_file:gsub("\\","/") - defaults.fs_use_modules = false - defaults.arch = "win32-"..cfg.target_cpu - defaults.lib_extension = "dll" - defaults.external_lib_extension = "dll" - defaults.static_lib_extension = "lib" - defaults.obj_extension = "obj" - defaults.external_deps_dirs = { "c:/external", "c:/windows/system32" } - defaults.variables.LUA_BINDIR = site_config.LUA_BINDIR and site_config.LUA_BINDIR:gsub("\\", "/") or "c:/lua"..cfg.lua_version.."/bin" - defaults.variables.LUA_INCDIR = site_config.LUA_INCDIR and site_config.LUA_INCDIR:gsub("\\", "/") or "c:/lua"..cfg.lua_version.."/include" - defaults.variables.LUA_LIBDIR = site_config.LUA_LIBDIR and site_config.LUA_LIBDIR:gsub("\\", "/") or "c:/lua"..cfg.lua_version.."/lib" - - defaults.makefile = "Makefile.win" - defaults.variables.MAKE = "nmake" - defaults.variables.CC = "cl" - defaults.variables.RC = "rc" - defaults.variables.WRAPPER = full_prefix.."\\rclauncher.c" - defaults.variables.LD = "link" - defaults.variables.MT = "mt" - defaults.variables.AR = "lib" - defaults.variables.LUALIB = "lua"..cfg.lua_version..".lib" - defaults.variables.CFLAGS = "/nologo /MD /O2" - defaults.variables.LIBFLAG = "/nologo /dll" - - local bins = { "SEVENZ", "CP", "FIND", "LS", "MD5SUM", - "MKDIR", "MV", "PWD", "RMDIR", "TEST", "UNAME", "WGET" } - for _, var in ipairs(bins) do - if defaults.variables[var] then - defaults.variables[var] = full_prefix.."\\tools\\"..defaults.variables[var] + local gcc_arch = util.popen_read("gcc -print-multiarch 2>/dev/null") + if gcc_arch and gcc_arch ~= "" then + defaults.external_deps_subdirs.lib = { "lib", "lib/" .. gcc_arch, "lib64" } + defaults.runtime_external_deps_subdirs.lib = { "lib", "lib/" .. gcc_arch, "lib64" } + else + defaults.external_deps_subdirs.lib = { "lib", "lib64" } + defaults.runtime_external_deps_subdirs.lib = { "lib", "lib64" } end end - defaults.external_deps_patterns = { - bin = { "?.exe", "?.bat" }, - lib = { "?.lib", "?.dll", "lib?.dll" }, - include = { "?.h" } - } - defaults.runtime_external_deps_patterns = { - bin = { "?.exe", "?.bat" }, - lib = { "?.dll", "lib?.dll" }, - include = { "?.h" } - } - defaults.export_path_separator = ";" - defaults.wrapper_suffix = ".bat" - - local localappdata = os.getenv("LOCALAPPDATA") - if not localappdata then - -- for Windows versions below Vista - localappdata = os.getenv("USERPROFILE").."/Local Settings/Application Data" + if platforms.freebsd then + defaults.arch = "freebsd-"..target_cpu + defaults.gcc_rpath = false + defaults.variables.CC = "cc" + defaults.variables.LD = "cc" end - defaults.local_cache = localappdata.."/LuaRocks/Cache" - defaults.web_browser = "start" - defaults.external_deps_subdirs = site_config.LUAROCKS_EXTERNAL_DEPS_SUBDIRS or { - bin = {".", "bin"}, - lib = {".", "lib"}, - include = {".", "include"}, - } - defaults.runtime_external_deps_subdirs = site_config.LUAROCKS_RUNTIME_EXTERNAL_DEPS_SUBDIRS or { - bin = {".", "bin"}, - lib = {".", "lib"}, - include = {".", "include"}, - } + if platforms.openbsd then + defaults.arch = "openbsd-"..target_cpu + end -end + if platforms.netbsd then + defaults.arch = "netbsd-"..target_cpu + end -if cfg.platforms.mingw32 then - defaults.obj_extension = "o" - defaults.static_lib_extension = "a" - defaults.cmake_generator = "MinGW Makefiles" - defaults.variables.MAKE = "mingw32-make" - defaults.variables.CC = "mingw32-gcc" - defaults.variables.RC = "windres" - defaults.variables.LD = "mingw32-gcc" - defaults.variables.AR = "ar" - defaults.variables.RANLIB = "ranlib" - defaults.variables.CFLAGS = "-O2" - defaults.variables.LIBFLAG = "-shared" - defaults.makefile = "Makefile" - defaults.external_deps_dirs = { "c:/external", "c:/mingw", "c:/windows/system32" } - defaults.external_deps_patterns = { - bin = { "?.exe", "?.bat" }, - -- mingw lookup list from http://stackoverflow.com/a/15853231/1793220 - -- ...should we keep ?.lib at the end? It's not in the above list. - lib = { "lib?.dll.a", "?.dll.a", "lib?.a", "cyg?.dll", "lib?.dll", "?.dll", "?.lib" }, - include = { "?.h" } - } - defaults.runtime_external_deps_patterns = { - bin = { "?.exe", "?.bat" }, - lib = { "cyg?.dll", "?.dll", "lib?.dll" }, - include = { "?.h" } - } + if platforms.solaris then + defaults.arch = "solaris-"..target_cpu + --defaults.platforms = {"unix", "solaris"} + defaults.variables.MAKE = "gmake" + end -end + -- Expose some more values detected by LuaRocks for use by rockspec authors. + defaults.variables.LIB_EXTENSION = defaults.lib_extension + defaults.variables.OBJ_EXTENSION = defaults.obj_extension -if cfg.platforms.unix then - defaults.lib_extension = "so" - defaults.static_lib_extension = "a" - defaults.external_lib_extension = "so" - defaults.obj_extension = "o" - defaults.external_deps_dirs = { "/usr/local", "/usr" } - defaults.variables.LUA_BINDIR = site_config.LUA_BINDIR or "/usr/local/bin" - defaults.variables.LUA_INCDIR = site_config.LUA_INCDIR or "/usr/local/include" - defaults.variables.LUA_LIBDIR = site_config.LUA_LIBDIR or "/usr/local/lib" - defaults.variables.CFLAGS = "-O2" - defaults.cmake_generator = "Unix Makefiles" - defaults.variables.CC = "gcc" - defaults.variables.LD = "gcc" - defaults.gcc_rpath = true - defaults.variables.LIBFLAG = "-shared" - defaults.external_deps_patterns = { - bin = { "?" }, - lib = { "lib?.a", "lib?.so", "lib?.so.*" }, - include = { "?.h" } - } - defaults.runtime_external_deps_patterns = { - bin = { "?" }, - lib = { "lib?.so", "lib?.so.*" }, - include = { "?.h" } - } - defaults.export_path_separator = ":" - defaults.wrapper_suffix = "" - defaults.local_cache = cfg.home.."/.cache/luarocks" - if not defaults.variables.CFLAGS:match("-fPIC") then - defaults.variables.CFLAGS = defaults.variables.CFLAGS.." -fPIC" - end - defaults.web_browser = "xdg-open" + return defaults end -if cfg.platforms.cygwin then - defaults.lib_extension = "so" -- can be overridden in the config file for mingw builds - defaults.arch = "cygwin-"..cfg.target_cpu - defaults.cmake_generator = "Unix Makefiles" - defaults.variables.CC = "echo -llua | xargs gcc" - defaults.variables.LD = "echo -llua | xargs gcc" - defaults.variables.LIBFLAG = "-shared" - defaults.link_lua_explicitly = true -end +local function make_rocks_provided(lua_version, luajit_version) + local rocks_provided = {} + local rocks_provided_3_0 = {} -if cfg.platforms.msys then - -- msys is basically cygwin made out of mingw, meaning the subsytem is unixish - -- enough, yet we can freely mix with native win32 - defaults.external_deps_patterns = { - bin = { "?.exe", "?.bat", "?" }, - lib = { "lib?.so", "lib?.so.*", "lib?.dll.a", "?.dll.a", - "lib?.a", "lib?.dll", "?.dll", "?.lib" }, - include = { "?.h" } - } - defaults.runtime_external_deps_patterns = { - bin = { "?.exe", "?.bat" }, - lib = { "lib?.so", "?.dll", "lib?.dll" }, - include = { "?.h" } - } -end + rocks_provided["lua"] = lua_version.."-1" + if lua_version == "5.2" or lua_version == "5.3" then + rocks_provided["bit32"] = lua_version.."-1" + end -if cfg.platforms.bsd then - defaults.variables.MAKE = "gmake" - defaults.variables.STATPERMFLAG = "-f '%OLp'" - defaults.variables.STATOWNERFLAG = "-f '%Su'" -end + if lua_version == "5.3" or lua_version == "5.4" then + rocks_provided["utf8"] = lua_version.."-1" + end -if cfg.platforms.macosx then - defaults.variables.MAKE = "make" - defaults.external_lib_extension = "dylib" - defaults.arch = "macosx-"..cfg.target_cpu - defaults.variables.LIBFLAG = "-bundle -undefined dynamic_lookup -all_load" - defaults.variables.STAT = "/usr/bin/stat" - defaults.variables.STATFLAG = "-f '%A'" - local version = util.popen_read("sw_vers -productVersion") - version = tonumber(version and version:match("^[^.]+%.([^.]+)")) or 3 - if version >= 10 then - version = 8 - elseif version >= 5 then - version = 5 - else - defaults.gcc_rpath = false + if luajit_version then + rocks_provided["luabitop"] = luajit_version.."-1" + rocks_provided_3_0["luajit"] = luajit_version.."-1" end - defaults.variables.CC = "env MACOSX_DEPLOYMENT_TARGET=10."..version.." gcc" - defaults.variables.LD = "env MACOSX_DEPLOYMENT_TARGET=10."..version.." gcc" - defaults.web_browser = "open" -end -if cfg.platforms.linux then - defaults.arch = "linux-"..cfg.target_cpu + return rocks_provided, rocks_provided_3_0 end -if cfg.platforms.freebsd then - defaults.arch = "freebsd-"..cfg.target_cpu - defaults.gcc_rpath = false - defaults.variables.CC = "cc" - defaults.variables.LD = "cc" -end +local function use_defaults(cfg, defaults) -if cfg.platforms.openbsd then - defaults.arch = "openbsd-"..cfg.target_cpu -end + -- Populate some arrays with values from their 'defaults' counterparts + -- if they were not already set by user. + for _, entry in ipairs({"variables", "rocks_provided"}) do + if not cfg[entry] then + cfg[entry] = {} + end + for k,v in pairs(defaults[entry]) do + if not cfg[entry][k] then + cfg[entry][k] = v + end + end + end + util.deep_merge_under(defaults.rocks_provided_3_0, cfg.rocks_provided) -if cfg.platforms.netbsd then - defaults.arch = "netbsd-"..cfg.target_cpu -end + util.deep_merge_under(cfg, defaults) -if cfg.platforms.solaris then - defaults.arch = "solaris-"..cfg.target_cpu - --defaults.platforms = {"unix", "solaris"} - defaults.variables.MAKE = "gmake" + -- FIXME get rid of this + if not cfg.check_certificates then + cfg.variables.CURLNOCERTFLAG = "-k" + cfg.variables.WGETNOCERTFLAG = "--no-check-certificate" + end end --- Expose some more values detected by LuaRocks for use by rockspec authors. -defaults.variables.LIB_EXTENSION = defaults.lib_extension -defaults.variables.OBJ_EXTENSION = defaults.obj_extension -defaults.variables.LUAROCKS_PREFIX = site_config.LUAROCKS_PREFIX -defaults.variables.LUA = site_config.LUA_DIR_SET and (defaults.variables.LUA_BINDIR.."/"..defaults.lua_interpreter) or defaults.lua_interpreter +-------------------------------------------------------------------------------- --- Add built-in modules to rocks_provided -defaults.rocks_provided["lua"] = cfg.lua_version.."-1" +local cfg = {} -if bit32 then -- Lua 5.2+ - defaults.rocks_provided["bit32"] = cfg.lua_version.."-1" -end +function cfg.init(lua_data, warning) + lua_data = lua_data or {} -if utf8 then -- Lua 5.3+ - defaults.rocks_provided["utf8"] = cfg.lua_version.."-1" -end + local hc_ok, hardcoded = pcall(require, "luarocks.core.hardcoded") + if not hc_ok then + hardcoded = {} + end -if package.loaded.jit then - -- LuaJIT - local lj_version = package.loaded.jit.version:match("LuaJIT (.*)"):gsub("%-","") - defaults.rocks_provided["luabitop"] = lj_version.."-1" - defaults.rocks_provided_3_0["luajit"] = lj_version.."-1" -end + local lua_version = lua_data.lua_version or hardcoded.LUA_VERSION or _VERSION:sub(5) + local luajit_version = lua_data.luajit_version or hardcoded.LUAJIT_VERSION or (jit and jit.version:sub(8)) + local lua_interpreter = lua_data.lua_interpreter or hardcoded.LUA_INTERPRETER or (arg[-1] and arg[-1]:gsub(".*[\\/]", "")) or (is_windows and "lua.exe" or "lua") + local lua_bindir = lua_data.lua_bindir or hardcoded.LUA_BINDIR or (arg[-1] and arg[-1]:gsub("[\\/][^\\/]+$", "")) + local lua_incdir = lua_data.lua_incdir or hardcoded.LUA_INCDIR + local lua_libdir = lua_data.lua_libdir or hardcoded.LUA_LIBDIR + local lua_dir = lua_data.lua_dir or hardcoded.LUA_DIR + + local init = cfg.init --- Use defaults: + ---------------------------------------- + -- Reset the cfg table. + ---------------------------------------- --- Populate some arrays with values from their 'defaults' counterparts --- if they were not already set by user. -for _, entry in ipairs({"variables", "rocks_provided"}) do - if not cfg[entry] then - cfg[entry] = {} - end - for k,v in pairs(defaults[entry]) do - if not cfg[entry][k] then - cfg[entry][k] = v - end + for k, _ in pairs(cfg) do + cfg[k] = nil end -end -setmetatable(defaults.rocks_provided_3_0, { __index = cfg.rocks_provided }) - --- For values not set in the config file, use values from the 'defaults' table. -local cfg_mt = { - __index = function(t, k) - local default = defaults[k] - if default then - rawset(t, k, default) - end - return default - end -} -setmetatable(cfg, cfg_mt) -if not cfg.check_certificates then - cfg.variables.CURLNOCERTFLAG = "-k" - cfg.variables.WGETNOCERTFLAG = "--no-check-certificate" -end + cfg.program_version = program_version + cfg.program_series = program_series + cfg.major_version = major_version -function cfg.make_paths_from_tree(tree) - local lua_path, lib_path, bin_path - if type(tree) == "string" then - lua_path = tree..cfg.lua_modules_path - lib_path = tree..cfg.lib_modules_path - bin_path = tree.."/bin" + cfg.lua_version = lua_version + cfg.luajit_version = luajit_version + cfg.lua_interpreter = lua_interpreter + + cfg.variables = { + LUA_DIR = lua_dir, + LUA_BINDIR = lua_bindir, + LUA_INCDIR = lua_incdir, + LUA_LIBDIR = lua_libdir, + } + + cfg.rocks_trees = {} + + cfg.init = init + + ---------------------------------------- + -- System detection. + ---------------------------------------- + + -- A proper build of LuaRocks will hardcode the system + -- and proc values with hardcoded.SYSTEM and hardcoded.PROCESSOR. + -- If that is not available, we try to identify the system. + local system = hardcoded.SYSTEM + local processor = hardcoded.PROCESSOR + if is_windows then + system = system or "Windows" + if not processor then + local pe_parser = require("luarocks.fs.win32.pe-parser") + local err + local lua_exe = lua_bindir .. "\\" .. lua_interpreter + processor, err = pe_parser.get_architecture(lua_exe) + if err then + processor = "x86" + end + end else - lua_path = tree.lua_dir or tree.root..cfg.lua_modules_path - lib_path = tree.lib_dir or tree.root..cfg.lib_modules_path - bin_path = tree.bin_dir or tree.root.."/bin" + system = system or util.popen_read("uname -s") + processor = processor or util.popen_read("uname -m") end - return lua_path, lib_path, bin_path -end -function cfg.package_paths(current) - local new_path, new_cpath, new_bin = {}, {}, {} - local function add_tree_to_paths(tree) - local lua_path, lib_path, bin_path = cfg.make_paths_from_tree(tree) - table.insert(new_path, lua_path.."/?.lua") - table.insert(new_path, lua_path.."/?/init.lua") - table.insert(new_cpath, lib_path.."/?."..cfg.lib_extension) - table.insert(new_bin, bin_path) - end - if current then - add_tree_to_paths(current) + cfg.target_cpu = processor + + local platforms = make_platforms(system) + + ---------------------------------------- + -- Platform is determined. + -- Let's load the config files. + ---------------------------------------- + + local sys_config_file + local home_config_file + do + local sysconfdir = os.getenv("LUAROCKS_SYSCONFDIR") or hardcoded.SYSCONFDIR + local sdir, hdir + if platforms.windows then + cfg.home = os.getenv("APPDATA") or "c:" + sdir = sysconfdir or (os.getenv("PROGRAMFILES") or "c:") .. "/luarocks" + hdir = cfg.home.."/luarocks" + cfg.home_tree = cfg.home.."/luarocks" + else + cfg.home = os.getenv("HOME") or "" + sdir = sysconfdir or "/etc/luarocks" + hdir = cfg.home.."/.luarocks" + cfg.home_tree = (os.getenv("USER") ~= "root") and cfg.home.."/.luarocks/" + end + sys_config_file = sdir .. "/config-"..cfg.lua_version..".lua" + home_config_file = hdir .. "/config-"..cfg.lua_version..".lua" + + sys_config_file = sys_config_file:gsub("\\", "/") + home_config_file = home_config_file:gsub("\\", "/") end - for _,tree in ipairs(cfg.rocks_trees) do - add_tree_to_paths(tree) + + -- Load system configuration file + local sys_config_ok, err = load_config_file(cfg, platforms, sys_config_file) + if err then + return nil, err, "config" end - if extra_luarocks_module_dir then - table.insert(new_path, extra_luarocks_module_dir) + + -- Load user configuration file (if allowed) + local home_config_ok + if not hardcoded.FORCE_CONFIG then + local env_var = "LUAROCKS_CONFIG_" .. lua_version:gsub("%.", "_") + local env_value = os.getenv(env_var) + if not env_value then + env_var = "LUAROCKS_CONFIG" + env_value = os.getenv(env_var) + end + -- first try environment provided file, so we can explicitly warn when it is missing + if env_value then + local env_ok, err = load_config_file(cfg, platforms, env_value) + if err then + return nil, err, "config" + elseif warning and not env_ok then + warning("Warning: could not load configuration file `"..env_value.."` given in environment variable "..env_var.."\n") + end + if env_ok then + home_config_ok = true + home_config_file = env_value + end + end + + -- try the alternative defaults if there was no environment specified file or it didn't work + if not home_config_ok then + home_config_ok = load_config_file(cfg, platforms, home_config_file) + if err then + return nil, err, "config" + end + end end - return table.concat(new_path, ";"), table.concat(new_cpath, ";"), table.concat(new_bin, cfg.export_path_separator) -end -function cfg.init_package_paths() - local lr_path, lr_cpath, lr_bin = cfg.package_paths() - package.path = util.cleanup_path(package.path .. ";" .. lr_path, ";", cfg.lua_version) - package.cpath = util.cleanup_path(package.cpath .. ";" .. lr_cpath, ";", cfg.lua_version) -end + ---------------------------------------- + -- Config files are loaded. + -- Let's finish up the cfg table. + ---------------------------------------- + + -- Settings given via lua_data (i.e. --lua-dir) take precedence over config files: + cfg.lua_version = lua_data.lua_version or cfg.lua_version + cfg.luajit_version = lua_data.luajit_version or cfg.luajit_version + cfg.lua_interpreter = lua_data.lua_interpreter or cfg.lua_interpreter + cfg.variables.LUA_BINDIR = lua_data.lua_bindir or cfg.variables.LUA_BINDIR + cfg.variables.LUA_INCDIR = lua_data.lua_incdir or cfg.variables.LUA_INCDIR + cfg.variables.LUA_LIBDIR = lua_data.lua_libdir or cfg.variables.LUA_LIBDIR + + -- Build a default list of rocks trees if not given + if not next(cfg.rocks_trees) then + if cfg.home_tree then + table.insert(cfg.rocks_trees, { name = "user", root = cfg.home_tree } ) + end + if hardcoded.PREFIX then + table.insert(cfg.rocks_trees, { name = "system", root = hardcoded.PREFIX } ) + end + end -function cfg.which_config() - local ret = { - system = { - file = sys_config_file or sys_config_file_default, - ok = sys_config_ok, - }, - user = { - file = home_config_file or home_config_file_default, - ok = home_config_ok, + local defaults = make_defaults(lua_version, processor, platforms, cfg.home) + defaults.rocks_provided, defaults.rocks_provided_3_0 = make_rocks_provided(lua_version, luajit_version) + use_defaults(cfg, defaults) + + cfg.variables.LUA = cfg.variables.LUA or cfg.variables.LUA_BINDIR .. "/" .. cfg.lua_interpreter + cfg.user_agent = "LuaRocks/"..cfg.program_version.." "..cfg.arch + + ---------------------------------------- + -- Attributes of cfg are set. + -- Let's add some methods. + ---------------------------------------- + + function cfg.which_config() + return { + system = { + file = sys_config_file, + ok = sys_config_ok, + }, + user = { + file = home_config_file, + ok = home_config_ok, + }, + nearest = home_config_ok and home_config_file or sys_config_file, } - } - ret.nearest = (ret.user.ok and ret.user.file) or ret.system.file - return ret -end + end + + do + local function make_paths_from_tree(tree) + local lua_path, lib_path, bin_path + if type(tree) == "string" then + lua_path = tree..cfg.lua_modules_path + lib_path = tree..cfg.lib_modules_path + bin_path = tree.."/bin" + else + lua_path = tree.lua_dir or tree.root..cfg.lua_modules_path + lib_path = tree.lib_dir or tree.root..cfg.lib_modules_path + bin_path = tree.bin_dir or tree.root.."/bin" + end + return lua_path, lib_path, bin_path + end -cfg.user_agent = "LuaRocks/"..cfg.program_version.." "..cfg.arch + function cfg.package_paths(current) + local new_path, new_cpath, new_bin = {}, {}, {} + local function add_tree_to_paths(tree) + local lua_path, lib_path, bin_path = make_paths_from_tree(tree) + table.insert(new_path, lua_path.."/?.lua") + table.insert(new_path, lua_path.."/?/init.lua") + table.insert(new_cpath, lib_path.."/?."..cfg.lib_extension) + table.insert(new_bin, bin_path) + end + if current then + add_tree_to_paths(current) + end + for _,tree in ipairs(cfg.rocks_trees) do + add_tree_to_paths(tree) + end + return table.concat(new_path, ";"), table.concat(new_cpath, ";"), table.concat(new_bin, cfg.export_path_separator) + end + end -cfg.http_proxy = os.getenv("http_proxy") -cfg.https_proxy = os.getenv("https_proxy") -cfg.no_proxy = os.getenv("no_proxy") + function cfg.init_package_paths() + local lr_path, lr_cpath, lr_bin = cfg.package_paths() + package.path = util.cleanup_path(package.path .. ";" .. lr_path, ";", lua_version) + package.cpath = util.cleanup_path(package.cpath .. ";" .. lr_cpath, ";", lua_version) + end ---- Check if platform was detected --- @param query string: The platform name to check. --- @return boolean: true if LuaRocks is currently running on queried platform. -function cfg.is_platform(query) - assert(type(query) == "string") + --- Check if platform was detected + -- @param name string: The platform name to check. + -- @return boolean: true if LuaRocks is currently running on queried platform. + function cfg.is_platform(name) + assert(type(name) == "string") + return platforms[name] + end - for _, platform in ipairs(cfg.platforms) do - if platform == query then - return true + function cfg.each_platform() + local i = 0 + return function() + local p + repeat + i = i + 1 + p = platform_order[i] + until (not p) or platforms[p] + return p end end -end -function cfg.each_platform() - local i = 0 - return function() - i = i + 1 - return cfg.platforms[i] + function cfg.print_platforms() + return table.concat(platforms, ", ") end + + return true end return cfg diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua index bcd6ccf5..3217a18f 100644 --- a/src/luarocks/deps.lua +++ b/src/luarocks/deps.lua @@ -187,30 +187,24 @@ end -- nil and an error message if any test failed, followed by an optional -- error code. function deps.fulfill_dependencies(rockspec, depskey, deps_mode) - if rockspec.supported_platforms then - if not deps.platforms_set then - deps.platforms_set = values_set(cfg.platforms) - end - local supported = nil + if rockspec.supported_platforms and next(rockspec.supported_platforms) then + local supported = false for _, plat in pairs(rockspec.supported_platforms) do local neg neg, plat = plat:match("^(!?)(.*)") if neg == "!" then - if deps.platforms_set[plat] then + if cfg.is_platform(plat) then return nil, "This rockspec for "..rockspec.package.." does not support "..plat.." platforms." end else - if deps.platforms_set[plat] then + if cfg.is_platform(plat) then supported = true - else - if supported == nil then - supported = false - end + break end end end if supported == false then - local plats = table.concat(cfg.platforms, ", ") + local plats = cfg.print_platforms() return nil, "This rockspec for "..rockspec.package.." does not support "..plats.." platforms." end end diff --git a/src/luarocks/fs/lua.lua b/src/luarocks/fs/lua.lua index b53ef8cc..88fc31d9 100644 --- a/src/luarocks/fs/lua.lua +++ b/src/luarocks/fs/lua.lua @@ -557,7 +557,7 @@ local redirect_protocols = { local function request(url, method, http, loop_control) local result = {} - local proxy = cfg.http_proxy + local proxy = os.getenv("http_proxy") if type(proxy) ~= "string" then proxy = nil end -- LuaSocket's http.request crashes when given URLs missing the scheme part. if proxy and not proxy:find("://") then @@ -691,7 +691,7 @@ function fs_lua.download(url, filename, cache) filename = fs.absolute_name(filename or dir.base_name(url)) -- delegate to the configured downloader so we don't have to deal with whitelists - if cfg.no_proxy then + if os.getenv("no_proxy") then return fs.use_downloader(url, filename, cache) end @@ -702,7 +702,7 @@ function fs_lua.download(url, filename, cache) ok, err = ftp_request(url, filename) elseif util.starts_with(url, "https:") then -- skip LuaSec when proxy is enabled since it is not supported - if luasec_ok and not cfg.https_proxy then + if luasec_ok and not os.getenv("https_proxy") then ok, err = http_request(url, filename, https, cache) else https_err = true @@ -919,7 +919,7 @@ function fs_lua.is_lua(filename) filename = filename:gsub([[%\]],"/") -- normalize on fw slash to prevent escaping issues local lua = fs.Q(dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)) -- get lua interpreter configured -- execute on configured interpreter, might not be the same as the interpreter LR is run on - local result = fs.execute_string(lua..[[ -e "if loadfile(']]..filename..[[') then os.exit() else os.exit(1) end"]]) + local result = fs.execute_string(lua..[[ -e "if loadfile(']]..filename..[[') then os.exit(0) else os.exit(1) end"]]) return (result == true) end diff --git a/src/luarocks/loader.lua b/src/luarocks/loader.lua index 078e4068..537adf94 100644 --- a/src/luarocks/loader.lua +++ b/src/luarocks/loader.lua @@ -16,7 +16,10 @@ local is_clean = not package.loaded["luarocks.core.cfg"] -- This loader module depends only on core modules. local cfg = require("luarocks.core.cfg") -cfg.init_package_paths() +local cfg_ok, err = cfg.init() +if cfg_ok then + cfg.init_package_paths() +end local path = require("luarocks.core.path") local manif = require("luarocks.core.manif") diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua index 5a111dbb..fd2b7b42 100644 --- a/src/luarocks/util.lua +++ b/src/luarocks/util.lua @@ -115,6 +115,7 @@ local supported_flags = { ["lr-bin"] = true, ["lr-cpath"] = true, ["lr-path"] = true, + ["lua-dir"] = "", ["lua-version"] = "", ["lua-ver"] = true, ["lua-incdir"] = true, -- cgit v1.2.3-55-g6feb