From e388deecaa13b7b89da202d09d328a79bbcf40dd Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Thu, 21 Mar 2019 18:47:44 -0400 Subject: cmd: refactor detection code and improve detection of project dir --- spec/init_spec.lua | 4 +- src/luarocks/cmd.lua | 198 ++++++++++++---------------------------------- src/luarocks/core/cfg.lua | 50 ++++++------ src/luarocks/util.lua | 109 +++++++++++++++++++++++++ 4 files changed, 189 insertions(+), 172 deletions(-) diff --git a/spec/init_spec.lua b/spec/init_spec.lua index 2c8dd64c..22570df2 100644 --- a/spec/init_spec.lua +++ b/spec/init_spec.lua @@ -111,7 +111,9 @@ describe("Luarocks init test #integration", function() ]], finally) write_file(tmpdir .. "/my_dependency.lua", "return {}", finally) - assert.is_true(run.luarocks_bool("build --verbose my_dependency-1.0-1.rockspec")) + print(run.luarocks("install inspect")) + + print(run.luarocks("build --verbose my_dependency-1.0-1.rockspec")) assert.truthy(lfs.attributes(myproject .. "/lua_modules/share/lua/" .. test_env.lua_version .."/my_dependency.lua")) os.remove(rockspec_filename) diff --git a/src/luarocks/cmd.lua b/src/luarocks/cmd.lua index 4397d43b..a46b0722 100644 --- a/src/luarocks/cmd.lua +++ b/src/luarocks/cmd.lua @@ -38,106 +38,6 @@ local function is_ownership_ok(directory) return false end -local function exists(file) - local fd = io.open(file, "r") - if fd then - fd:close() - return true - end - return false -end - -do - local function Q(pathname) - if pathname:match("^.:") then - return pathname:sub(1, 2) .. '"' .. pathname:sub(3) .. '"' - end - return '"' .. pathname .. '"' - end - - local function check_lua_version(lua_exe, luaver) - if not exists(lua_exe) then - return nil - end - local lv, err = util.popen_read(Q(lua_exe) .. ' -e "io.write(_VERSION:sub(5))"') - if luaver and luaver ~= lv then - return nil - end - local ljv - if lv == "5.1" then - ljv = util.popen_read(Q(lua_exe) .. ' -e "io.write(tostring(jit and jit.version:sub(8)))"') - if ljv == "nil" then - ljv = nil - end - end - return lv, ljv - 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 - if luaver == "5.1" or not luaver then - table.insert(names, "luajit" .. exe_suffix) - end - table.insert(names, "lua" .. 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) - local lv, ljv = check_lua_version(lua_exe, luaver) - if lv then - return name, d, lv, ljv - end - end - end - return nil, "Lua interpreter not found at " .. prefix .. "\n" .. - "Tried:\t" .. table.concat(tried, "\n\t") - end - end - - function cmd.find_lua(prefix, luaver) - local lua_interpreter, bindir, luajitver - lua_interpreter, bindir, luaver, luajitver = find_lua_bindir(prefix, luaver) - if not lua_interpreter then - return nil, bindir - end - - return { - lua_version = luaver, - luajit_version = luajitver, - lua_interpreter = lua_interpreter, - lua_dir = prefix, - lua_bindir = bindir, - } - end -end - local function check_popen() local popen_ok, popen_result = pcall(io.popen, "") if popen_ok then @@ -151,16 +51,17 @@ local function check_popen() end end -local function find_project_dir() - local try = "." - for _ = 1, 10 do -- FIXME detect when root dir was hit instead - if exists(try .. "/.luarocks") and exists(try .. "/lua_modules") then - return try - elseif exists(try .. "/.luarocks-no-project") then - return nil +local function check_if_config_is_present(detected, try) + local versions = detected.lua_version + and { detected.lua_version } + or util.lua_versions("descending") + return fun.find(versions, function(v) + if util.exists(dir.path(try, ".luarocks", "config-"..v..".lua")) then + detected.project_dir = try + detected.lua_version = v + return detected end - try = try .. "/.." - end + end) end local process_tree_flags @@ -261,19 +162,6 @@ local function process_server_flags(flags) return true end -local function find_lua_version_at(dirname) - local lua_version - for v in util.lua_versions("descending") do - if exists(dir.path(dirname, ".luarocks", "config-"..v..".lua")) then - lua_version = v - break - end - end - return { - lua_version = lua_version - } -end - --- Main command-line processor. -- Parses input arguments and calls the appropriate driver function -- to execute the action requested on the command-line, forwarding @@ -375,47 +263,65 @@ function cmd.run_command(description, commands, external_namespace, ...) die("Invalid entry for --deps-mode.") end - local project_dir - if flags["project-tree"] then - project_dir = flags["project-tree"]:gsub("[/\\][^/\\]+$", "") - end - - local lua_data + local detected if flags["lua-dir"] then local err - lua_data, err = cmd.find_lua(flags["lua-dir"], flags["lua-version"]) - if not lua_data then + detected, err = util.find_lua(flags["lua-dir"], flags["lua-version"]) + if not detected then die(err) end + assert(detected.lua_version) + assert(detected.lua_dir) elseif flags["lua-version"] then local path_sep = (package.config:sub(1, 1) == "\\" and ";" or ":") for bindir in os.getenv("PATH"):gmatch("[^"..path_sep.."]+") do local parentdir = bindir:gsub("[\\/][^\\/]+[\\/]?$", "") - lua_data = cmd.find_lua(dir.path(parentdir), flags["lua-version"]) - if lua_data then + detected = util.find_lua(dir.path(parentdir), flags["lua-version"]) + if detected then break end - lua_data = cmd.find_lua(bindir, flags["lua-version"]) - if lua_data then + detected = util.find_lua(bindir, flags["lua-version"]) + if detected then break end end - if not lua_data then + if not detected then util.warning("Could not find a Lua interpreter for version " .. flags["lua-version"] .. " in your PATH. " .. "Modules may not install with the correct configurations. " .. "You may want to specify to the path prefix to your build " .. "of Lua " .. flags["lua-version"] .. " using --lua-dir") - lua_data = { + detected = { lua_version = flags["lua-version"], } end - else - if not project_dir then - project_dir = find_project_dir() + end + + if flags["project-tree"] then + local project_tree = flags["project-tree"]:gsub("[/\\][^/\\]+$", "") + detected = detected or { + project_dir = project_tree + } + local d = check_if_config_is_present(detected, project_tree) + if d then + detected = d + else + detected.project_dir = nil end - if project_dir then - lua_data = find_lua_version_at(project_dir) + else + detected = detected or {} + 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 + local d = check_if_config_is_present(detected, try) + if d then + detected = d + break + end + elseif util.exists(try .. "/.luarocks-no-project") then + break + end + try = try .. "/.." end end @@ -433,16 +339,16 @@ function cmd.run_command(description, commands, external_namespace, ...) end ----------------------------------------------------------------------------- - local ok, err = cfg.init(lua_data, project_dir, util.warning) + local ok, err = cfg.init(detected, util.warning) if not ok then die(err) end ----------------------------------------------------------------------------- fs.init() - - if project_dir then - project_dir = fs.absolute_name(project_dir) + + if detected.project_dir then + detected.project_dir = fs.absolute_name(detected.project_dir) end if flags["version"] then @@ -467,7 +373,7 @@ function cmd.run_command(description, commands, external_namespace, ...) die("Current directory does not exist. Please run LuaRocks from an existing directory.") end - ok, err = process_tree_flags(flags, project_dir) + ok, err = process_tree_flags(flags, detected.project_dir) if not ok then die(err) end diff --git a/src/luarocks/core/cfg.lua b/src/luarocks/core/cfg.lua index 5cfa10c3..670f04b2 100644 --- a/src/luarocks/core/cfg.lua +++ b/src/luarocks/core/cfg.lua @@ -535,8 +535,8 @@ local cfg = {} --- Initializes the LuaRocks configuration for variables, paths -- and OS detection. --- @param lua_data table containing information pertaining the location --- of Lua in the environment. All fields below are optional: +-- @param detected table containing information detected about the +-- environment. All fields below are optional: -- * lua_version (in x.y format, e.g. "5.3") -- * luajit_version (complete, e.g. "2.1.0-beta3") -- * lua_bindir (e.g. "/usr/local/bin") @@ -544,25 +544,25 @@ local cfg = {} -- * lua_libdir(e.g. "/usr/local/lib") -- * lua_dir (e.g. "/usr/local") -- * lua_interpreter (e.g. "lua-5.3") --- @param project_dir a string with the path of the project directory --- when using per-project environments, as created with `luarocks init` +-- * project_dir (a string with the path of the project directory +-- when using per-project environments, as created with `luarocks init`) -- @param warning a logging function for warnings that takes a string -- @return true on success; nil and an error message on failure. -function cfg.init(lua_data, project_dir, warning) - lua_data = lua_data or {} +function cfg.init(detected, warning) + detected = detected or {} local hc_ok, hardcoded = pcall(require, "luarocks.core.hardcoded") if not hc_ok then hardcoded = {} 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 and 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 and 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 lua_version = detected.lua_version or hardcoded.LUA_VERSION or _VERSION:sub(5) + local luajit_version = detected.luajit_version or hardcoded.LUAJIT_VERSION or (jit and jit.version:sub(8)) + local lua_interpreter = detected.lua_interpreter or hardcoded.LUA_INTERPRETER or (arg and arg[-1] and arg[-1]:gsub(".*[\\/]", "")) or (is_windows and "lua.exe" or "lua") + local lua_bindir = detected.lua_bindir or hardcoded.LUA_BINDIR or (arg and arg[-1] and arg[-1]:gsub("[\\/][^\\/]+$", "")) + local lua_incdir = detected.lua_incdir or hardcoded.LUA_INCDIR + local lua_libdir = detected.lua_libdir or hardcoded.LUA_LIBDIR + local lua_dir = detected.lua_dir or hardcoded.LUA_DIR local init = cfg.init @@ -638,8 +638,8 @@ function cfg.init(lua_data, project_dir, warning) local name = "config-"..cfg.lua_version..".lua" sys_config_file = (cfg.sysconfdir .. "/" .. name):gsub("\\", "/") home_config_file = (cfg.homeconfdir .. "/" .. name):gsub("\\", "/") - if project_dir then - project_config_file = project_dir .. "/.luarocks/" .. name + if detected.project_dir then + project_config_file = detected.project_dir .. "/.luarocks/" .. name end end @@ -682,7 +682,7 @@ function cfg.init(lua_data, project_dir, warning) end -- finally, use the project-specific config file if any - if project_dir then + if detected.project_dir then project_config_ok, err = load_config_file(cfg, platforms, project_config_file) if err then return nil, err, "config" @@ -695,14 +695,14 @@ function cfg.init(lua_data, project_dir, warning) -- 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 or lua_bindir - cfg.variables.LUA_INCDIR = lua_data.lua_incdir or cfg.variables.LUA_INCDIR or lua_incdir - cfg.variables.LUA_LIBDIR = lua_data.lua_libdir or cfg.variables.LUA_LIBDIR or lua_libdir - cfg.variables.LUA_DIR = lua_data.lua_dir or cfg.variables.LUA_DIR or lua_dir + -- Settings detected or given via the CLI (i.e. --lua-dir) take precedence over config files: + cfg.lua_version = detected.lua_version or cfg.lua_version + cfg.luajit_version = detected.luajit_version or cfg.luajit_version + cfg.lua_interpreter = detected.lua_interpreter or cfg.lua_interpreter + cfg.variables.LUA_BINDIR = detected.lua_bindir or cfg.variables.LUA_BINDIR or lua_bindir + cfg.variables.LUA_INCDIR = detected.lua_incdir or cfg.variables.LUA_INCDIR or lua_incdir + cfg.variables.LUA_LIBDIR = detected.lua_libdir or cfg.variables.LUA_LIBDIR or lua_libdir + cfg.variables.LUA_DIR = detected.lua_dir or cfg.variables.LUA_DIR or lua_dir -- Build a default list of rocks trees if not given if cfg.rocks_trees == nil then @@ -739,7 +739,7 @@ function cfg.init(lua_data, project_dir, warning) function cfg.which_config() return { - project = project_dir and { + project = detected.project_dir and { file = project_config_file, ok = project_config_ok, }, diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua index 7d7ce921..25c521ea 100644 --- a/src/luarocks/util.lua +++ b/src/luarocks/util.lua @@ -548,4 +548,113 @@ function util.require_json() return nil, errmsg end +-- A portable version of fs.exists that can be used at early startup, +-- before the platform has been determined and luarocks.fs has been +-- initialized. +function util.exists(file) + local fd, _, code = io.open(file, "r") + if code == 13 then + -- code 13 means "Permission denied" on both Unix and Windows + -- io.open on folders always fails with code 13 on Windows + return true + end + if fd then + fd:close() + return true + end + return false +end + +do + local function Q(pathname) + if pathname:match("^.:") then + return pathname:sub(1, 2) .. '"' .. pathname:sub(3) .. '"' + end + return '"' .. pathname .. '"' + end + + function util.check_lua_version(lua_exe, luaver) + if not util.exists(lua_exe) then + return nil + end + local lv, err = util.popen_read(Q(lua_exe) .. ' -e "io.write(_VERSION:sub(5))"') + if luaver and luaver ~= lv then + return nil + end + local ljv + if lv == "5.1" then + ljv = util.popen_read(Q(lua_exe) .. ' -e "io.write(tostring(jit and jit.version:sub(8)))"') + if ljv == "nil" then + ljv = nil + end + end + return lv, ljv + end + + local find_lua_bindir + do + local exe_suffix = (package.config:sub(1, 1) == "\\" and ".exe" or "") + + local function insert_lua_variants(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_variants(names, luaver) + else + for v in util.lua_variants("descending") do + insert_lua_variants(names, v) + end + end + if luaver == "5.1" or not luaver then + table.insert(names, "luajit" .. exe_suffix) + end + table.insert(names, "lua" .. exe_suffix) + + local bindirs = { prefix .. "/bin", prefix } + local tried = {} + for _, d in ipairs(bindirs) do + for _, name in ipairs(names) do + local lua_exe = d .. "/" .. name + table.insert(tried, lua_exe) + local lv, ljv = util.check_lua_version(lua_exe, luaver) + if lv then + return name, d, lv, ljv + end + end + end + return nil, "Lua interpreter not found at " .. prefix .. "\n" .. + "Tried:\t" .. table.concat(tried, "\n\t") + end + end + + function util.find_lua(prefix, luaver) + local lua_interpreter, bindir, luajitver + lua_interpreter, bindir, luaver, luajitver = find_lua_bindir(prefix, luaver) + if not lua_interpreter then + return nil, bindir + end + + return { + lua_version = luaver, + luajit_version = luajitver, + lua_interpreter = lua_interpreter, + lua_dir = prefix, + lua_bindir = bindir, + } + end +end + return util + -- cgit v1.2.3-55-g6feb