From 85b8c8cc20a58a2c769ee323aa1283ad94e2b2f4 Mon Sep 17 00:00:00 2001 From: Philipp Janda Date: Tue, 27 May 2014 01:08:19 +0200 Subject: build static libraries for builtin build type --- src/luarocks/build/builtin.lua | 29 ++++++++++++++++++++++++++++- src/luarocks/cfg.lua | 8 ++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/luarocks/build/builtin.lua b/src/luarocks/build/builtin.lua index 47aa71fc..c855f48a 100644 --- a/src/luarocks/build/builtin.lua +++ b/src/luarocks/build/builtin.lua @@ -50,7 +50,7 @@ end -- nil and an error message otherwise. function builtin.run(rockspec) assert(type(rockspec) == "table") - local compile_object, compile_library, compile_wrapper_binary --TODO EXEWRAPPER + local compile_object, compile_library, compile_static_library, compile_wrapper_binary --TODO EXEWRAPPER local build = rockspec.build local variables = rockspec.variables @@ -83,6 +83,13 @@ function builtin.run(rockspec) local ok = execute(variables.LD.." "..variables.LIBFLAG, "-o", library, unpack(extras)) return ok end + compile_static_library = function(library, objects, libraries, libdirs, name) + local ok = execute(variables.AR, "rc", library, unpack(objects)) + if ok then + ok = execute(variables.RANLIB, library) + end + return ok + end compile_wrapper_binary = function(fullname, name) --TODO EXEWRAPPER local fullbasename = fullname:gsub("%.lua$", ""):gsub("/", "\\") @@ -129,6 +136,10 @@ function builtin.run(rockspec) end return ok end + compile_static_library = function(library, objects, libraries, libdirs, name) + local ok = execute(variables.AR, "-out:"..library, unpack(objects)) + return ok + end compile_wrapper_binary = function(fullname, name) --TODO EXEWRAPPER local fullbasename = fullname:gsub("%.lua$", ""):gsub("/", "\\") @@ -170,6 +181,13 @@ function builtin.run(rockspec) end return execute(variables.LD.." "..variables.LIBFLAG, "-o", library, "-L"..variables.LUA_LIBDIR, unpack(extras)) end + compile_static_library = function(library, objects, libraries, libdirs, name) + local ok = execute(variables.AR, "rc", library, unpack(objects)) + if ok then + ok = execute(variables.RANLIB, library) + end + return ok + end compile_wrapper_binary = function(fullname, name) return true, name end --TODO EXEWRAPPER end @@ -246,6 +264,15 @@ function builtin.run(rockspec) if not ok then return nil, "Failed compiling module "..module_name end + module_name = name:match("([^.]*)$").."."..util.matchquote(cfg.static_lib_extension) + if moddir ~= "" then + module_name = dir.path(moddir, module_name) + end + built_modules[module_name] = dir.path(libdir, module_name) + ok = compile_static_library(module_name, objects, info.libraries, info.libdirs, name) + if not ok then + return nil, "Failed compiling static library "..module_name + end end end for name, dest in pairs(built_modules) do diff --git a/src/luarocks/cfg.lua b/src/luarocks/cfg.lua index 59a17754..c404c663 100644 --- a/src/luarocks/cfg.lua +++ b/src/luarocks/cfg.lua @@ -233,6 +233,8 @@ local defaults = { MAKE = "make", CC = "cc", LD = "ld", + AR = "ar", + RANLIB = "ranlib", CVS = "cvs", GIT = "git", @@ -298,6 +300,7 @@ if detected.windows then defaults.platforms = {"win32", "windows" } defaults.lib_extension = "dll" defaults.external_lib_extension = "dll" + defaults.static_lib_extension = "lib" defaults.obj_extension = "obj" defaults.external_deps_dirs = { "c:/external/" } defaults.variables.LUA_BINDIR = site_config.LUA_BINDIR and site_config.LUA_BINDIR:gsub("\\", "/") or "c:/lua"..cfg.lua_version.."/bin" @@ -311,6 +314,7 @@ if detected.windows then 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 = "/MD /O2" defaults.variables.LIBFLAG = "/dll" @@ -351,11 +355,14 @@ end if detected.mingw32 then defaults.platforms = { "win32", "mingw32", "windows" } 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 = "mingw32-gcc-ar" + defaults.variables.RANLIB = "mingw32-gcc-ranlib" defaults.variables.CFLAGS = "-O2" defaults.variables.LIBFLAG = "-shared" defaults.external_deps_patterns = { @@ -375,6 +382,7 @@ end if detected.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" } -- cgit v1.2.3-55-g6feb From 4e93093b4349377188c4ec033077d1acd10eda4e Mon Sep 17 00:00:00 2001 From: Philipp Janda Date: Tue, 27 May 2014 01:28:47 +0200 Subject: avoid the need for plugin support in mingw --- src/luarocks/cfg.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/luarocks/cfg.lua b/src/luarocks/cfg.lua index c404c663..5e891b06 100644 --- a/src/luarocks/cfg.lua +++ b/src/luarocks/cfg.lua @@ -361,8 +361,8 @@ if detected.mingw32 then defaults.variables.CC = "mingw32-gcc" defaults.variables.RC = "windres" defaults.variables.LD = "mingw32-gcc" - defaults.variables.AR = "mingw32-gcc-ar" - defaults.variables.RANLIB = "mingw32-gcc-ranlib" + defaults.variables.AR = "ar" + defaults.variables.RANLIB = "ranlib" defaults.variables.CFLAGS = "-O2" defaults.variables.LIBFLAG = "-shared" defaults.external_deps_patterns = { -- cgit v1.2.3-55-g6feb From fb93696e2b640478bbe6ba4753e6eeb7693d751d Mon Sep 17 00:00:00 2001 From: Philipp Janda Date: Tue, 27 May 2014 02:18:24 +0200 Subject: handle static libraries for multiple versions of a rock --- src/luarocks/path.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/luarocks/path.lua b/src/luarocks/path.lua index 598e51d9..519f32b0 100644 --- a/src/luarocks/path.lua +++ b/src/luarocks/path.lua @@ -251,6 +251,11 @@ function path.path_to_module(file) name = file:match("(.*)%."..cfg.lib_extension.."$") if name then name = name:gsub(dir.separator, ".") + else + name = file:match("(.*)%."..cfg.static_lib_extension.."$") + if name then + name = name:gsub(dir.separator, ".") + end end end if not name then name = file end -- cgit v1.2.3-55-g6feb From d8a72ee125aa0acfa8d43b910572edfcc61cd434 Mon Sep 17 00:00:00 2001 From: mpeterv Date: Fri, 3 Apr 2015 16:45:26 +0300 Subject: Add support for git submodules When fetching rock sources using git, run `git submodule update --init --recursive` after cloning. When using git >= 1.8.4, also pass `--depth=1` to fetch only the last commit of each submodule. --- src/luarocks/fetch/git.lua | 54 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/src/luarocks/fetch/git.lua b/src/luarocks/fetch/git.lua index e540d696..0847f735 100644 --- a/src/luarocks/fetch/git.lua +++ b/src/luarocks/fetch/git.lua @@ -7,20 +7,48 @@ local unpack = unpack or table.unpack local fs = require("luarocks.fs") local dir = require("luarocks.dir") +local deps = require("luarocks.deps") local util = require("luarocks.util") +local cached_git_version + +--- Get git version. +-- @param git_cmd string: name of git command. +-- @return table: git version as returned by luarocks.deps.parse_version. +local function git_version(git_cmd) + if not cached_git_version then + local version_line = io.popen(fs.Q(git_cmd)..' --version'):read() + local version_string = version_line:match('%d-%.%d+%.?%d*') + cached_git_version = deps.parse_version(version_string) + end + + return cached_git_version +end + +--- Check if git satisfies version requirement. +-- @param git_cmd string: name of git command. +-- @param version string: required version. +-- @return boolean: true if git matches version or is newer, false otherwise. +local function git_is_at_least(git_cmd, version) + return git_version(git_cmd) >= deps.parse_version(version) +end + --- Git >= 1.7.10 can clone a branch **or tag**, < 1.7.10 by branch only. We -- need to know this in order to build the appropriate command; if we can't -- clone by tag then we'll have to issue a subsequent command to check out the -- given tag. +-- @param git_cmd string: name of git command. -- @return boolean: Whether Git can clone by tag. local function git_can_clone_by_tag(git_cmd) - local version_string = io.popen(fs.Q(git_cmd)..' --version'):read() - local major, minor, tiny = version_string:match('(%d-)%.(%d+)%.?(%d*)') - major, minor, tiny = tonumber(major), tonumber(minor), tonumber(tiny) or 0 - local value = major > 1 or (major == 1 and (minor > 7 or (minor == 7 and tiny >= 10))) - git_can_clone_by_tag = function() return value end - return value + return git_is_at_least(git_cmd, "1.7.10") +end + +--- Git >= 1.8.4 can fetch submodules shallowly, saving bandwidth and time for +-- submodules with large history. +-- @param git_cmd string: name of git command. +-- @return boolean: Whether Git can fetch submodules shallowly. +local function git_supports_shallow_submodules(git_cmd) + return git_is_at_least(git_cmd, "1.8.4") end --- Download sources for building a rock, using git. @@ -77,12 +105,22 @@ function git.get_sources(rockspec, extract, dest_dir, depth) ok, err = fs.change_dir(module) if not ok then return nil, err end if tag_or_branch and not git_can_clone_by_tag() then - local checkout_command = {fs.Q(git_cmd), "checkout", tag_or_branch} - if not fs.execute(unpack(checkout_command)) then + if not fs.execute(fs.Q(git_cmd), "checkout", tag_or_branch) then return nil, 'Failed to check out the "' .. tag_or_branch ..'" tag or branch.' end end + command = {fs.Q(git_cmd), "submodule", "update", "--init", "--recursive"} + + if git_supports_shallow_submodules(git_cmd) then + -- Fetch only the last commit of each submodule. + table.insert(command, 5, "--depth=1") + end + + if not fs.execute(unpack(command)) then + return nil, 'Failed to fetch submodules.' + end + fs.delete(dir.path(store_dir, module, ".git")) fs.delete(dir.path(store_dir, module, ".gitignore")) fs.pop_dir() -- cgit v1.2.3-55-g6feb From 1e9edc66b768ad9e78401cf51931967430de1f7b Mon Sep 17 00:00:00 2001 From: mpeterv Date: Thu, 25 Jun 2015 14:14:16 +0300 Subject: Check that rockspec format is at least 3.0 before fetching submodules For utility, add deps.format_is_at_least(rockspec, version) function. --- src/luarocks/deps.lua | 9 +++++++++ src/luarocks/fetch/git.lua | 17 ++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua index 0e3265b5..f6c86d1c 100644 --- a/src/luarocks/deps.lua +++ b/src/luarocks/deps.lua @@ -159,6 +159,15 @@ function deps.compare_versions(a, b) return deps.parse_version(a) > deps.parse_version(b) end +--- Check if rockspec format version satisfies version requirement. +-- @param rockspec table: The rockspec table. +-- @param version string: required version. +-- @return boolean: true if rockspec format matches version or is newer, false otherwise. +function deps.format_is_at_least(rockspec, version) + local rockspec_format = rockspec.rockspec_format or "1.0" + return deps.parse_version(rockspec_format) >= deps.parse_version(version) +end + --- Consumes a constraint from a string, converting it to table format. -- For example, a string ">= 1.0, > 2.0" is converted to a table in the -- format {op = ">=", version={1,0}} and the rest, "> 2.0", is returned diff --git a/src/luarocks/fetch/git.lua b/src/luarocks/fetch/git.lua index 0847f735..ce41659b 100644 --- a/src/luarocks/fetch/git.lua +++ b/src/luarocks/fetch/git.lua @@ -110,15 +110,18 @@ function git.get_sources(rockspec, extract, dest_dir, depth) end end - command = {fs.Q(git_cmd), "submodule", "update", "--init", "--recursive"} + -- Fetching git submodules is supported only when rockspec format is >= 3.0. + if deps.format_is_at_least(rockspec, "3.0") then + command = {fs.Q(git_cmd), "submodule", "update", "--init", "--recursive"} - if git_supports_shallow_submodules(git_cmd) then - -- Fetch only the last commit of each submodule. - table.insert(command, 5, "--depth=1") - end + if git_supports_shallow_submodules(git_cmd) then + -- Fetch only the last commit of each submodule. + table.insert(command, 5, "--depth=1") + end - if not fs.execute(unpack(command)) then - return nil, 'Failed to fetch submodules.' + if not fs.execute(unpack(command)) then + return nil, 'Failed to fetch submodules.' + end end fs.delete(dir.path(store_dir, module, ".git")) -- cgit v1.2.3-55-g6feb From 0601f27c2371c9f19c4175f866f7babfcabb7277 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Mon, 29 Jun 2015 21:24:43 -0300 Subject: Bump format to 3.0 in the luarocks-3 branch. --- src/luarocks/type_check.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/luarocks/type_check.lua b/src/luarocks/type_check.lua index 65b4fc15..83091a29 100644 --- a/src/luarocks/type_check.lua +++ b/src/luarocks/type_check.lua @@ -8,7 +8,7 @@ package.loaded["luarocks.type_check"] = type_check local cfg = require("luarocks.cfg") local deps = require("luarocks.deps") -type_check.rockspec_format = "1.1" +type_check.rockspec_format = "3.0" local string_1 = { _type = "string" } local number_1 = { _type = "number" } -- cgit v1.2.3-55-g6feb From 910fbe111c5f6e7e104604346ce67dbe22d15b27 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Sun, 30 Aug 2015 14:44:41 -0300 Subject: Update LuaCov and LuaSec --- test/testing.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/testing.sh b/test/testing.sh index 133597ee..49aaa244 100755 --- a/test/testing.sh +++ b/test/testing.sh @@ -161,7 +161,7 @@ then chmod og-wx ~/.ssh/authorized_keys ssh-keyscan localhost >> ~/.ssh/known_hosts else - luadir="/Programs/Lua/Current" + luadir="/Programs/Lua/$luaversion" if [ ! -e "$luadir" ] then luadir="/usr/local" @@ -184,7 +184,7 @@ srcdir_luasocket=luasocket-3.0-rc1 version_cprint=0.1 verrev_cprint=0.1-2 -version_luacov=0.7 +version_luacov=0.8 verrev_luacov=${version_luacov}-1 version_lxsh=0.8.6 version_validate_args=1.5.4 @@ -242,7 +242,7 @@ mkdir -p "$testing_server" get "$luarocks_repo/stdlib-41.0.0-1.src.rock" get "$luarocks_repo/luarepl-0.4-1.src.rock" get "$luarocks_repo/validate-args-1.5.4-1.rockspec" - get "$luarocks_repo/luasec-0.5-2.rockspec" + get "https://raw.githubusercontent.com/brunoos/luasec/master/luasec-0.6alpha-1.rockspec" get "$luarocks_repo/luabitop-1.0.2-1.rockspec" get "$luarocks_repo/lpty-1.0.1-1.src.rock" get "$luarocks_repo/cprint-${verrev_cprint}.src.rock" -- cgit v1.2.3-55-g6feb From 761fefeb9676ec7ff91e32735346852150f6ff7e Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Mon, 21 Sep 2015 13:39:16 -0300 Subject: LuaSec moved in the repository... --- test/testing.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testing.sh b/test/testing.sh index df1d301d..7292c75d 100755 --- a/test/testing.sh +++ b/test/testing.sh @@ -242,7 +242,7 @@ mkdir -p "$testing_server" get "$luarocks_repo/stdlib-41.0.0-1.src.rock" get "$luarocks_repo/luarepl-0.4-1.src.rock" get "$luarocks_repo/validate-args-1.5.4-1.rockspec" - get "https://raw.githubusercontent.com/brunoos/luasec/master/luasec-0.6alpha-1.rockspec" + get "https://raw.githubusercontent.com/brunoos/luasec/master/luasec-0.6alpha-2.rockspec" get "$luarocks_repo/luabitop-1.0.2-1.rockspec" get "$luarocks_repo/lpty-1.0.1-1.src.rock" get "$luarocks_repo/cprint-${verrev_cprint}.src.rock" -- cgit v1.2.3-55-g6feb From 65718de85ce69ac97b9b454953ce48b29bc3a674 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Tue, 22 Sep 2015 11:28:31 -0300 Subject: Support SCM repos with names ending with ".lua". Fixes #425. --- src/luarocks/fetch.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua index e92aeddf..5ae05826 100644 --- a/src/luarocks/fetch.lua +++ b/src/luarocks/fetch.lua @@ -247,7 +247,10 @@ function fetch.load_local_rockspec(filename, quick) local base = fetch.url_to_base_dir(filebase) rockspec.source.dir = rockspec.source.dir or rockspec.source.module - or ((filebase:match("%.lua$") or filebase:match("%.c$")) and ".") + or ( (filebase:match("%.lua$") or filebase:match("%.c$")) + and (deps.format_is_at_least(rockspec, "3.0") + and (fetch.is_basic_protocol(protocol) and "." or base) + or ".") ) or base if rockspec.dependencies then for i = 1, #rockspec.dependencies do -- cgit v1.2.3-55-g6feb From 8cad664aade89612f67dd99987fa036606c453e7 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Tue, 22 Sep 2015 13:23:04 -0300 Subject: Fix merge conflict. --- src/luarocks/tools/tar.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/luarocks/tools/tar.lua b/src/luarocks/tools/tar.lua index 03f7de3c..cedcceee 100644 --- a/src/luarocks/tools/tar.lua +++ b/src/luarocks/tools/tar.lua @@ -31,9 +31,10 @@ local function octal_to_number(octal) local number = 0 for i = #octal,1,-1 do local digit = tonumber(octal:sub(i,i)) - if not digit then break end - number = number + (digit * 8^exp) - exp = exp + 1 + if digit then + number = number + (digit * 8^exp) + exp = exp + 1 + end end return number end @@ -56,10 +57,11 @@ end local function read_header_block(block) local header = {} header.name = nullterm(block:sub(1,100)) - header.mode = nullterm(block:sub(101,108)) + header.mode = nullterm(block:sub(101,108)):gsub(" ", "") header.uid = octal_to_number(nullterm(block:sub(109,116))) header.gid = octal_to_number(nullterm(block:sub(117,124))) header.size = octal_to_number(nullterm(block:sub(125,136))) +print("{"..block:sub(125,136).."}", "{"..nullterm(block:sub(125,136)).."}", "{"..octal_to_number(nullterm(block:sub(125,136))).."}", header.size) header.mtime = octal_to_number(nullterm(block:sub(137,148))) header.chksum = octal_to_number(nullterm(block:sub(149,156))) header.typeflag = get_typeflag(block:sub(157,157)) @@ -93,13 +95,14 @@ function tar.untar(filename, destdir) local long_name, long_link_name while true do local block - repeat + repeat block = tar_handle:read(blocksize) until (not block) or checksum_header(block) > 256 if not block then break end local header, err = read_header_block(block) if not header then util.printerr(err) + return nil, err end local file_data = tar_handle:read(math.ceil(header.size / blocksize) * blocksize):sub(1,header.size) @@ -143,6 +146,7 @@ function tar.untar(filename, destdir) util.printout() --]] end + tar_handle:close() return true end -- cgit v1.2.3-55-g6feb From f8179800d0eccaef960442a7e65b1e305f3a96d5 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Mon, 19 Oct 2015 18:46:21 -0200 Subject: Support "luajit" as a provided dependency in rockspec format 3.0 --- src/luarocks/cfg.lua | 6 ++++-- src/luarocks/deps.lua | 22 +++++++++++++--------- src/luarocks/fetch.lua | 5 +++++ test/testfiles/luajit-fail-1.0-1.rockspec | 22 ++++++++++++++++++++++ test/testfiles/luajit-success-1.0-1.rockspec | 23 +++++++++++++++++++++++ test/testing.sh | 8 ++++++++ 6 files changed, 75 insertions(+), 11 deletions(-) create mode 100644 test/testfiles/luajit-fail-1.0-1.rockspec create mode 100644 test/testfiles/luajit-success-1.0-1.rockspec diff --git a/src/luarocks/cfg.lua b/src/luarocks/cfg.lua index 3b7f9c37..a4f60f55 100644 --- a/src/luarocks/cfg.lua +++ b/src/luarocks/cfg.lua @@ -371,7 +371,8 @@ local defaults = { include = "include" }, - rocks_provided = {} + rocks_provided = {}, + rocks_provided_3_0 = {}, } if cfg.platforms.windows then @@ -575,8 +576,8 @@ end if package.loaded.jit then -- LuaJIT local lj_version = package.loaded.jit.version:match("LuaJIT (.*)"):gsub("%-","") - --defaults.rocks_provided["luajit"] = lj_version.."-1" defaults.rocks_provided["luabitop"] = lj_version.."-1" + defaults.rocks_provided_3_0["luajit"] = lj_version.."-1" end -- Use defaults: @@ -593,6 +594,7 @@ for _, entry in ipairs({"variables", "rocks_provided"}) do end 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 = { diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua index f6c86d1c..3f7eb4d2 100644 --- a/src/luarocks/deps.lua +++ b/src/luarocks/deps.lua @@ -327,16 +327,20 @@ end -- @param dep table: A dependency parsed in table format. -- @param blacklist table: Versions that can't be accepted. Table where keys -- are program versions and values are 'true'. +-- @param provided table: A table of auto-dependencies provided +-- by this Lua implementation for the given dependency. -- @return table or nil: A table containing fields 'name' and 'version' -- representing an installed rock which matches the given dependency, -- or nil if it could not be matched. -local function match_dep(dep, blacklist, deps_mode) +local function match_dep(dep, blacklist, deps_mode, rocks_provided) assert(type(dep) == "table") - - local versions = cfg.rocks_provided[dep.name] - if cfg.rocks_provided[dep.name] then + assert(type(rocks_provided) == "table") + + local versions + local provided = rocks_provided[dep.name] + if provided then -- provided rocks have higher priority than manifest's rocks - versions = { cfg.rocks_provided[dep.name] } + versions = { provided } else versions = manif_core.get_versions(dep.name, deps_mode) end @@ -388,9 +392,9 @@ function deps.match_deps(rockspec, blacklist, deps_mode) local matched, missing, no_upgrade = {}, {}, {} for _, dep in ipairs(rockspec.dependencies) do - local found = match_dep(dep, blacklist and blacklist[dep.name] or nil, deps_mode) + local found = match_dep(dep, blacklist and blacklist[dep.name] or nil, deps_mode, rockspec.rocks_provided) if found then - if not cfg.rocks_provided[dep.name] then + if not rockspec.rocks_provided[dep.name] then matched[dep] = found end else @@ -488,7 +492,7 @@ function deps.fulfill_dependencies(rockspec, deps_mode) for _, dep in pairs(missing) do -- Double-check in case dependency was filled during recursion. - if not match_dep(dep, nil, deps_mode) then + if not match_dep(dep, nil, deps_mode, rockspec.rocks_provided) then local rock = search.find_suitable_rock(dep) if not rock then return nil, "Could not satisfy dependency: "..deps.show_dep(dep) @@ -708,7 +712,7 @@ function deps.scan_deps(results, missing, manifest, name, version, deps_mode) end dependencies_name[version] = rockspec.dependencies else - rockspec = { dependencies = deplist } + rockspec = { dependencies = deplist, rocks_provided = {} } end local matched, failures = deps.match_deps(rockspec, nil, deps_mode) results[name] = results diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua index 5ae05826..e1cad11b 100644 --- a/src/luarocks/fetch.lua +++ b/src/luarocks/fetch.lua @@ -252,6 +252,11 @@ function fetch.load_local_rockspec(filename, quick) and (fetch.is_basic_protocol(protocol) and "." or base) or ".") ) or base + + rockspec.rocks_provided = (deps.format_is_at_least(rockspec, "3.0") + and cfg.rocks_provided_3_0 + or cfg.rocks_provided) + if rockspec.dependencies then for i = 1, #rockspec.dependencies do local parsed, err = deps.parse_dep(rockspec.dependencies[i]) diff --git a/test/testfiles/luajit-fail-1.0-1.rockspec b/test/testfiles/luajit-fail-1.0-1.rockspec new file mode 100644 index 00000000..f8204600 --- /dev/null +++ b/test/testfiles/luajit-fail-1.0-1.rockspec @@ -0,0 +1,22 @@ +package = "luajit-fail" +version = "1.0-1" +source = { + url = "https://raw.githubusercontent.com/keplerproject/luarocks/master/test/testing.lua", +} +description = { + summary = "Test luajit dependency fail", + detailed = [[ +Fail luajit dependency when running with rockspec_format < 3.0. +]], + homepage = "http://luarocks.org/", + license = "MIT/X license" +} +dependencies = { + "luajit >= 2.0" +} +build = { + type = "builtin", + modules = { + testing = "testing.lua" + } +} diff --git a/test/testfiles/luajit-success-1.0-1.rockspec b/test/testfiles/luajit-success-1.0-1.rockspec new file mode 100644 index 00000000..31c930c3 --- /dev/null +++ b/test/testfiles/luajit-success-1.0-1.rockspec @@ -0,0 +1,23 @@ +rockspec_format = "3.0" +package = "luajit-success" +version = "1.0-1" +source = { + url = "https://raw.githubusercontent.com/keplerproject/luarocks/master/test/testing.lua", +} +description = { + summary = "Test luajit dependency fail", + detailed = [[ +Use luajit dependency when running with rockspec_format >= 3.0. +]], + homepage = "http://luarocks.org/", + license = "MIT/X license" +} +dependencies = { + "luajit >= 2.0" +} +build = { + type = "builtin", + modules = { + testing = "testing.lua" + } +} diff --git a/test/testing.sh b/test/testing.sh index 7292c75d..5ae6bb35 100755 --- a/test/testing.sh +++ b/test/testing.sh @@ -224,6 +224,7 @@ luarocks_noecho="run_lua --noecho luarocks" luarocks_noecho_nocov="run_lua --noecho --nocov luarocks" luarocks_admin="run_lua luarocks-admin" luarocks_admin_nocov="run_lua --nocov luarocks-admin" +luajit_luarocks="luajit -e require('luacov.runner')('$testing_dir/luacov.config') $basedir/bin/luarocks" ################################################### @@ -524,6 +525,13 @@ test_fetch_base_dir() { $lua < Date: Fri, 23 Oct 2015 14:21:24 -0200 Subject: Support install_pass in CMake. Fixes #431. --- src/luarocks/build/cmake.lua | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/luarocks/build/cmake.lua b/src/luarocks/build/cmake.lua index 7b16fa51..34f6ada0 100644 --- a/src/luarocks/build/cmake.lua +++ b/src/luarocks/build/cmake.lua @@ -6,6 +6,7 @@ local cmake = {} local fs = require("luarocks.fs") local util = require("luarocks.util") local cfg = require("luarocks.cfg") +local deps = require("luarocks.deps") --- Driver function for the "cmake" build back-end. -- @param rockspec table: the loaded rockspec. @@ -53,13 +54,26 @@ function cmake.run(rockspec) return nil, "Failed cmake." end - if not fs.execute_string(rockspec.variables.CMAKE.." --build build.luarocks --config Release") then - return nil, "Failed building." + local do_build, do_install + if deps.format_is_at_least(rockspec, "3.0") then + do_build = (build.build_pass == nil) and true or build.build_pass + do_install = (build.install_pass == nil) and true or build.install_pass + else + do_build = true + do_install = true end - if not fs.execute_string(rockspec.variables.CMAKE.." --build build.luarocks --target install --config Release") then - return nil, "Failed installing." + if do_build then + if not fs.execute_string(rockspec.variables.CMAKE.." --build build.luarocks --config Release") then + return nil, "Failed building." + end end + if do_install then + if not fs.execute_string(rockspec.variables.CMAKE.." --build build.luarocks --target install --config Release") then + return nil, "Failed installing." + end + end + return true end -- cgit v1.2.3-55-g6feb From bc3ac2174e0404b13afee9114bd38e4f3bfb476b Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Sat, 24 Oct 2015 23:22:39 -0400 Subject: If LUAROCKS_CONFIGURE is given and is invalid, do not silently switch to a default path. Fail instead (and report that when the user checks the configuration.) --- src/luarocks/cfg.lua | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/luarocks/cfg.lua b/src/luarocks/cfg.lua index a4f60f55..ec9a9829 100644 --- a/src/luarocks/cfg.lua +++ b/src/luarocks/cfg.lua @@ -228,17 +228,26 @@ end if not site_config.LUAROCKS_FORCE_CONFIG then home_config_file_default = home_config_dir.."/config-"..cfg.lua_version..".lua" - local list = { - os.getenv("LUAROCKS_CONFIG_" .. version_suffix) or os.getenv("LUAROCKS_CONFIG"), - home_config_file_default, - home_config_dir.."/config.lua", - } - -- first entry might be a silent nil, check and remove if so - if not list[1] then table.remove(list, 1) end - - home_config_file = load_config_file(list) - home_config_ok = (home_config_file ~= nil) + 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 + if config_env_value then + home_config_ok = load_config_file({ config_env_value }) + if not home_config_ok then + io.stderr:write("Warning: could not load file "..config_env_value.." given in environment variable "..config_env_var) + end + home_config_file = config_env_var + else + home_config_file = load_config_file({ + home_config_file_default, + home_config_dir.."/config.lua", + }) + home_config_ok = (home_config_file ~= nil) + end end -- cgit v1.2.3-55-g6feb From 27c2154a3da4ad13d646f14c861f58ec923da90e Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Sat, 24 Oct 2015 23:24:38 -0400 Subject: Fixes to luajit testing. --- test/testing.sh | 68 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/test/testing.sh b/test/testing.sh index 5ae6bb35..ab56402f 100755 --- a/test/testing.sh +++ b/test/testing.sh @@ -30,7 +30,15 @@ then shift fi -luashortversion=`echo $luaversion | cut -d. -f 1-2` +is_jit=`[ "${luaversion::3}" = "jit" ] && echo 1` + +if [ "$is_jit" ] +then + luashortversion=5.1 + luajitversion=${luaversion:4} +else + luashortversion=`echo $luaversion | cut -d. -f 1-2` +fi testing_dir="$PWD" @@ -147,13 +155,27 @@ then if [ ! -e "$luadir/bin/lua" ] then mkdir -p lua - echo "Downloading lua $luaversion..." - wget "http://www.lua.org/ftp/lua-$luaversion.tar.gz" &> /dev/null - tar zxpf "lua-$luaversion.tar.gz" - cd "lua-$luaversion" - echo "Building lua $luaversion..." - make linux INSTALL_TOP="$luadir" &> /dev/null - make install INSTALL_TOP="$luadir" &> /dev/null + cd lua + if [ "$is_jit" = 1 ] + then + echo "Downloading LuaJIT $luajitversion..." + #rm -f "LuaJIT-$luajitversion.tar.gz" + wget -c "http://luajit.org/download/LuaJIT-$luajitversion.tar.gz" &> /dev/null + tar zxpf "LuaJIT-$luajitversion.tar.gz" + cd "LuaJIT-$luajitversion" + echo "Building LuaJIT $luajitversion..." + make PREFIX="$luadir" &> /dev/null + make install PREFIX="$luadir" &> /dev/null + else + echo "Downloading Lua $luaversion..." + #rm -f "lua-$luaversion.tar.gz" + wget -c "http://www.lua.org/ftp/lua-$luaversion.tar.gz" &> /dev/null + tar zxpf "lua-$luaversion.tar.gz" + cd "lua-$luaversion" + echo "Building Lua $luaversion..." + make linux INSTALL_TOP="$luadir" &> /dev/null + make install INSTALL_TOP="$luadir" &> /dev/null + fi fi popd [ -e ~/.ssh/id_rsa.pub ] || ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsa @@ -161,7 +183,13 @@ then chmod og-wx ~/.ssh/authorized_keys ssh-keyscan localhost >> ~/.ssh/known_hosts else - luadir="/Programs/Lua/$luaversion" + if [ "$is_jit" = 1 ] + then + luadir="/Programs/LuaJIT/$luajitversion" +echo HELLO $luadir + else + luadir="/Programs/Lua/$luaversion" + fi if [ ! -e "$luadir" ] then luadir="/usr/local" @@ -175,7 +203,13 @@ else platform="linux-x86_64" fi -lua="$luadir/bin/lua" +if [ "$is_jit" = 1 ] +then + lua="$luadir/bin/luajit" + luarocks_configure_extra_args="--lua-suffix=jit --with-lua-include=$luadir/include/luajit-2.0" +else + lua="$luadir/bin/lua" +fi version_luasocket=3.0rc1 verrev_luasocket=${version_luasocket}-1 @@ -196,7 +230,7 @@ verrev_abelhas=${version_abelhas}-1 luasec=luasec cd .. -./configure --with-lua="$luadir" --prefix="$testing_lrprefix" +./configure --with-lua="$luadir" --prefix="$testing_lrprefix" $luarocks_configure_extra_args make clean make src/luarocks/site_config.lua make dev @@ -525,11 +559,17 @@ test_fetch_base_dir() { $lua < Date: Sat, 24 Oct 2015 23:24:53 -0400 Subject: Test LuaJIT --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 69ec28f3..f946bb99 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,5 +9,6 @@ env: - LUA_VER=5.1.5 - LUA_VER=5.2.4 - LUA_VER=5.3.1 + - LUA_VER=jit-2.0.4 script: cd test && ./testing.sh --travis --lua $LUA_VER -- cgit v1.2.3-55-g6feb From 459d1ddf965d532df787d480236237ef8aa17bc1 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Sat, 24 Oct 2015 23:32:42 -0400 Subject: Fix test on non-LuaJIT --- test/testing.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/testing.sh b/test/testing.sh index ab56402f..abda18d4 100755 --- a/test/testing.sh +++ b/test/testing.sh @@ -30,9 +30,9 @@ then shift fi -is_jit=`[ "${luaversion::3}" = "jit" ] && echo 1` +is_jit=`[ "${luaversion::3}" = "jit" ] && echo 1 || echo 0` -if [ "$is_jit" ] +if [ "$is_jit" = 1 ] then luashortversion=5.1 luajitversion=${luaversion:4} -- cgit v1.2.3-55-g6feb From e95bfca9f1f1e417a2f554cb756cd44a7eb105cb Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Sun, 29 Nov 2015 12:51:28 -0200 Subject: Add explicit macosx_deployment_target option for Mac users. --- src/luarocks/build.lua | 30 ++++++++++++++++++++++++++++++ src/luarocks/cfg.lua | 6 +++--- src/luarocks/util.lua | 14 ++++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/luarocks/build.lua b/src/luarocks/build.lua index 68f20264..dd0b4441 100644 --- a/src/luarocks/build.lua +++ b/src/luarocks/build.lua @@ -149,6 +149,31 @@ local function install_default_docs(name, version) end end +local function check_macosx_deployment_target(rockspec) + local target = rockspec.build.macosx_deployment_target + local function minor(version) + return tonumber(version and version:match("^[^.]+%.([^.]+)")) + end + local function patch_variable(var, target) + if rockspec.variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then + rockspec.variables[var] = (rockspec.variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET="..target) + else + rockspec.variables[var] = "env MACOSX_DEPLOYMENT_TARGET="..target.." "..rockspec.variables[var] + end + end + if cfg.platforms.macosx and deps.format_is_at_least(rockspec, "3.0") and target then + local version = util.popen_read("sw_vers -productVersion") + local versionminor = minor(version) + local targetminor = minor(target) + if targetminor > versionminor then + return nil, ("This rock requires Mac OSX 10.%d, and you are running 10.%d."):format(targetminor, versionminor) + end + patch_variable("CC", target) + patch_variable("LD", target) + end + return true +end + --- Build and install a rock given a rockspec. -- @param rockspec_file string: local or remote filename of a rockspec. -- @param need_to_fetch boolean: true if sources need to be fetched, @@ -244,6 +269,11 @@ function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_m end end + ok, err = check_macosx_deployment_target(rockspec) + if not ok then + return nil, err + end + if build_spec.type ~= "none" then -- Temporary compatibility diff --git a/src/luarocks/cfg.lua b/src/luarocks/cfg.lua index 4fb1b23b..6706468c 100644 --- a/src/luarocks/cfg.lua +++ b/src/luarocks/cfg.lua @@ -76,8 +76,8 @@ end -- 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 io.popen("uname -s"):read("*l") -local proc = site_config.LUAROCKS_UNAME_M or io.popen("uname -m"):read("*l") +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 @@ -531,7 +531,7 @@ if cfg.platforms.macosx then defaults.variables.LIBFLAG = "-bundle -undefined dynamic_lookup -all_load" defaults.variables.STAT = "/usr/bin/stat" defaults.variables.STATFLAG = "-f '%A'" - local version = io.popen("sw_vers -productVersion"):read("*l") + local version = util.popen_read("sw_vers -productVersion") version = tonumber(version and version:match("^[^.]+%.([^.]+)")) or 3 if version >= 10 then version = 8 diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua index c06c8354..8c4bd974 100644 --- a/src/luarocks/util.lua +++ b/src/luarocks/util.lua @@ -9,6 +9,20 @@ local util = {} local unpack = unpack or table.unpack +--- Run a process and read a its output. +-- Equivalent to io.popen(cmd):read("*l"), except that it +-- closes the fd right away. +-- @param cmd string: The command to execute +-- @param spec string: "*l" by default, to read a single line. +-- May be used to read more, passing, for instance, "*a". +-- @return string: the output of the program. +function popen_read(cmd, spec) + local fd = io.open(cmd) + local out = fd:read(spec or "*l") + fd:close() + return out +end + local scheduled_functions = {} local debug = require("debug") -- cgit v1.2.3-55-g6feb From 9fe6923d37a9e3b35428cfa631da60fdd6a89f95 Mon Sep 17 00:00:00 2001 From: Geoff Leyland Date: Tue, 22 Dec 2015 14:47:50 +1300 Subject: Fix util.popen_read --- src/luarocks/util.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua index 8c4bd974..fc45c9f4 100644 --- a/src/luarocks/util.lua +++ b/src/luarocks/util.lua @@ -16,8 +16,8 @@ local unpack = unpack or table.unpack -- @param spec string: "*l" by default, to read a single line. -- May be used to read more, passing, for instance, "*a". -- @return string: the output of the program. -function popen_read(cmd, spec) - local fd = io.open(cmd) +function util.popen_read(cmd, spec) + local fd = io.popen(cmd) local out = fd:read(spec or "*l") fd:close() return out -- cgit v1.2.3-55-g6feb From e45f2a6bb460d0690722e862152a274ada15d300 Mon Sep 17 00:00:00 2001 From: Geoff Leyland Date: Wed, 23 Dec 2015 09:43:43 +1300 Subject: If source.dir can't be found in a downloaded package in fetch.get_sources, but we find that the package only contains one directory, then use that directory. This helps for downloads from bitbucket, where the top-level directory of a zip download of master has a commit hash in its name. --- src/luarocks/fetch.lua | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua index e1cad11b..ef478658 100644 --- a/src/luarocks/fetch.lua +++ b/src/luarocks/fetch.lua @@ -355,7 +355,32 @@ function fetch.get_sources(rockspec, extract, dest_dir) if not ok then return nil, err end fs.unpack_archive(rockspec.source.file) if not fs.exists(rockspec.source.dir) then - return nil, "Directory "..rockspec.source.dir.." not found inside archive "..rockspec.source.file, "source.dir", source_file, store_dir + + -- if rockspec.source.dir can't be found, see if we only have one + -- directory in store_dir. If that's the case, assume it's what + -- we're looking for. + -- Ideally, we'd only do this if rockspec.source.dir was not + -- defined in the rockspec, but load_local_repository already + -- set it to an inferred value if it wasn't set there, so we + -- can't tell its status in the rockspec. + -- In any case, we only do this with rockspecs newer than 3.0. + local dir_count, found_dir = 0 + + if deps.format_is_at_least(rockspec, "3.0") then + local files = fs.list_dir() + for _, f in ipairs(files) do + if fs.is_dir(f) then + dir_count = dir_count + 1 + found_dir = f + end + end + end + + if dir_count == 1 then + rockspec.source.dir = found_dir + else + return nil, "Directory "..rockspec.source.dir.." not found inside archive "..rockspec.source.file, "source.dir", source_file, store_dir + end end fs.pop_dir() end -- cgit v1.2.3-55-g6feb From 7590c40d647d130e9005f6efc10ab62f7e388e4b Mon Sep 17 00:00:00 2001 From: Geoff Leyland Date: Wed, 23 Dec 2015 21:54:34 +1300 Subject: fetch.get_sources only searches the directory for the one true dir if the rockspec's source.dir was not set --- src/luarocks/fetch.lua | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua index ef478658..039efeea 100644 --- a/src/luarocks/fetch.lua +++ b/src/luarocks/fetch.lua @@ -245,6 +245,7 @@ function fetch.load_local_rockspec(filename, quick) rockspec.local_filename = filename local filebase = rockspec.source.file or rockspec.source.url local base = fetch.url_to_base_dir(filebase) + rockspec.source.dir_set = rockspec.source.dir ~= nil rockspec.source.dir = rockspec.source.dir or rockspec.source.module or ( (filebase:match("%.lua$") or filebase:match("%.c$")) @@ -356,14 +357,11 @@ function fetch.get_sources(rockspec, extract, dest_dir) fs.unpack_archive(rockspec.source.file) if not fs.exists(rockspec.source.dir) then - -- if rockspec.source.dir can't be found, see if we only have one + -- If rockspec.source.dir can't be found, see if we only have one -- directory in store_dir. If that's the case, assume it's what -- we're looking for. - -- Ideally, we'd only do this if rockspec.source.dir was not - -- defined in the rockspec, but load_local_repository already - -- set it to an inferred value if it wasn't set there, so we - -- can't tell its status in the rockspec. - -- In any case, we only do this with rockspecs newer than 3.0. + -- We only do this if the rockspec source.dir was not set, and only + -- with rockspecs newer than 3.0. local dir_count, found_dir = 0 if deps.format_is_at_least(rockspec, "3.0") then -- cgit v1.2.3-55-g6feb From f001183bcaa5243ba078889b5fe0b7eb03c49ee9 Mon Sep 17 00:00:00 2001 From: mpeterv Date: Wed, 23 Dec 2015 19:11:39 +0300 Subject: Fetch LuaJIT from GitHub mirror on Travis --- test/testing.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/testing.sh b/test/testing.sh index 86d7579b..a198c2f2 100755 --- a/test/testing.sh +++ b/test/testing.sh @@ -160,8 +160,8 @@ then then echo "Downloading LuaJIT $luajitversion..." #rm -f "LuaJIT-$luajitversion.tar.gz" - wget -c "http://luajit.org/download/LuaJIT-$luajitversion.tar.gz" &> /dev/null - tar zxpf "LuaJIT-$luajitversion.tar.gz" + wget -c "https://github.com/LuaJIT/LuaJIT/archive/v$luajitversion.tar.gz" &> /dev/null + tar zxpf "v$luajitversion.tar.gz" cd "LuaJIT-$luajitversion" echo "Building LuaJIT $luajitversion..." make PREFIX="$luadir" &> /dev/null -- cgit v1.2.3-55-g6feb From 6164f6f5c80320a0f250bbf187071b4feb668518 Mon Sep 17 00:00:00 2001 From: Geoff Leyland Date: Thu, 24 Dec 2015 21:06:00 +1300 Subject: Actually use rockspec.source.dir_set --- src/luarocks/fetch.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua index 039efeea..d47e0fc9 100644 --- a/src/luarocks/fetch.lua +++ b/src/luarocks/fetch.lua @@ -364,7 +364,7 @@ function fetch.get_sources(rockspec, extract, dest_dir) -- with rockspecs newer than 3.0. local dir_count, found_dir = 0 - if deps.format_is_at_least(rockspec, "3.0") then + if not rockspec.source.dir_set and deps.format_is_at_least(rockspec, "3.0") then local files = fs.list_dir() for _, f in ipairs(files) do if fs.is_dir(f) then -- cgit v1.2.3-55-g6feb From a85781cfda77b4ba0d0bb399f80a2ad2babc5a9b Mon Sep 17 00:00:00 2001 From: Pierre Chapuis Date: Sat, 2 Jan 2016 17:19:06 +0100 Subject: avoid "missing dependency" errors for defaults --- src/luarocks/deps.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua index 764116a2..96f6a005 100644 --- a/src/luarocks/deps.lua +++ b/src/luarocks/deps.lua @@ -727,7 +727,10 @@ function deps.scan_deps(results, missing, manifest, name, version, deps_mode) end dependencies_name[version] = rockspec.dependencies else - rockspec = { dependencies = deplist, rocks_provided = {} } + rockspec = { + dependencies = deplist, + rocks_provided = setmetatable({}, { __index = cfg.rocks_provided_3_0 }) + } end local matched, failures = deps.match_deps(rockspec, nil, deps_mode) results[name] = results -- cgit v1.2.3-55-g6feb From d96e375eb6b2356a9e7d1d172ac2043b362a41ee Mon Sep 17 00:00:00 2001 From: mpeterv Date: Sat, 16 Apr 2016 11:45:44 +0300 Subject: Fix travis: use luasec 0.6-1 --- test/testing.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testing.sh b/test/testing.sh index 3a132a2b..92c6ab5d 100755 --- a/test/testing.sh +++ b/test/testing.sh @@ -272,7 +272,7 @@ mkdir -p "$testing_server" get "$luarocks_repo/stdlib-41.0.0-1.src.rock" get "$luarocks_repo/luarepl-0.4-1.src.rock" get "$luarocks_repo/validate-args-1.5.4-1.rockspec" - get "https://raw.githubusercontent.com/brunoos/luasec/master/luasec-0.6alpha-2.rockspec" + get "$luarocks_repo/luasec-0.6-1.rockspec" get "$luarocks_repo/luabitop-1.0.2-1.rockspec" get "$luarocks_repo/luabitop-1.0.2-1.src.rock" get "$luarocks_repo/lpty-1.0.1-1.src.rock" -- cgit v1.2.3-55-g6feb From fca4a63277c0fc8d39eb887744b294ff5fd05ad6 Mon Sep 17 00:00:00 2001 From: Peter Melnichenko Date: Mon, 13 Jun 2016 12:47:34 +0300 Subject: Remove 'run' interface for commands --- src/luarocks/add.lua | 1 - src/luarocks/admin_remove.lua | 1 - src/luarocks/build.lua | 1 - src/luarocks/config_cmd.lua | 1 - src/luarocks/doc.lua | 1 - src/luarocks/download.lua | 1 - src/luarocks/help.lua | 1 - src/luarocks/install.lua | 1 - src/luarocks/lint.lua | 1 - src/luarocks/list.lua | 1 - src/luarocks/make.lua | 1 - src/luarocks/make_manifest.lua | 1 - src/luarocks/new_version.lua | 1 - src/luarocks/pack.lua | 1 - src/luarocks/path_cmd.lua | 1 - src/luarocks/purge.lua | 1 - src/luarocks/refresh_cache.lua | 2 -- src/luarocks/remove.lua | 1 - src/luarocks/search.lua | 1 - src/luarocks/show.lua | 1 - src/luarocks/unpack.lua | 1 - src/luarocks/upload.lua | 1 - src/luarocks/util.lua | 7 ------- src/luarocks/validate.lua | 1 - src/luarocks/write_rockspec.lua | 1 - 25 files changed, 32 deletions(-) diff --git a/src/luarocks/add.lua b/src/luarocks/add.lua index f37d334d..66f7504f 100644 --- a/src/luarocks/add.lua +++ b/src/luarocks/add.lua @@ -12,7 +12,6 @@ local index = require("luarocks.index") local fs = require("luarocks.fs") local cache = require("luarocks.cache") -util.add_run_function(add) add.help_summary = "Add a rock or rockspec to a rocks server." add.help_arguments = "[--server=] [--no-refresh] {|...}" add.help = [[ diff --git a/src/luarocks/admin_remove.lua b/src/luarocks/admin_remove.lua index 621f1317..be1e7cbc 100644 --- a/src/luarocks/admin_remove.lua +++ b/src/luarocks/admin_remove.lua @@ -12,7 +12,6 @@ local index = require("luarocks.index") local fs = require("luarocks.fs") local cache = require("luarocks.cache") -util.add_run_function(admin_remove) admin_remove.help_summary = "Remove a rock or rockspec from a rocks server." admin_remove.help_arguments = "[--server=] [--no-refresh] {|...}" admin_remove.help = [[ diff --git a/src/luarocks/build.lua b/src/luarocks/build.lua index a0170f06..4a71b4c5 100644 --- a/src/luarocks/build.lua +++ b/src/luarocks/build.lua @@ -16,7 +16,6 @@ local manif = require("luarocks.manif") local remove = require("luarocks.remove") local cfg = require("luarocks.cfg") -util.add_run_function(build) build.help_summary = "Build/compile a rock." build.help_arguments = "[--pack-binary-rock] [--keep] {|| []}" build.help = [[ diff --git a/src/luarocks/config_cmd.lua b/src/luarocks/config_cmd.lua index fe3cc637..9e73d228 100644 --- a/src/luarocks/config_cmd.lua +++ b/src/luarocks/config_cmd.lua @@ -6,7 +6,6 @@ local cfg = require("luarocks.cfg") local util = require("luarocks.util") local dir = require("luarocks.dir") -util.add_run_function(config_cmd) config_cmd.help_summary = "Query information about the LuaRocks configuration." config_cmd.help_arguments = "" config_cmd.help = [[ diff --git a/src/luarocks/doc.lua b/src/luarocks/doc.lua index ec2b1110..758fd9c5 100644 --- a/src/luarocks/doc.lua +++ b/src/luarocks/doc.lua @@ -12,7 +12,6 @@ local fetch = require("luarocks.fetch") local fs = require("luarocks.fs") local download = require("luarocks.download") -util.add_run_function(doc) doc.help_summary = "Show documentation for an installed rock." doc.help = [[ diff --git a/src/luarocks/download.lua b/src/luarocks/download.lua index 18573ae4..d793cab7 100644 --- a/src/luarocks/download.lua +++ b/src/luarocks/download.lua @@ -12,7 +12,6 @@ local fs = require("luarocks.fs") local dir = require("luarocks.dir") local cfg = require("luarocks.cfg") -util.add_run_function(download) download.help_summary = "Download a specific rock file from a rocks server." download.help_arguments = "[--all] [--arch= | --source | --rockspec] [ []]" diff --git a/src/luarocks/help.lua b/src/luarocks/help.lua index 28f97702..5bac77ce 100644 --- a/src/luarocks/help.lua +++ b/src/luarocks/help.lua @@ -12,7 +12,6 @@ local dir = require("luarocks.dir") local program = util.this_program("luarocks") -util.add_run_function(help) help.help_summary = "Help on commands. Type '"..program.." help ' for more." help.help_arguments = "[]" diff --git a/src/luarocks/install.lua b/src/luarocks/install.lua index acbf584a..10d57f37 100644 --- a/src/luarocks/install.lua +++ b/src/luarocks/install.lua @@ -13,7 +13,6 @@ local manif = require("luarocks.manif") local remove = require("luarocks.remove") local cfg = require("luarocks.cfg") -util.add_run_function(install) install.help_summary = "Install a rock." install.help_arguments = "{| []}" diff --git a/src/luarocks/lint.lua b/src/luarocks/lint.lua index d5cc48d0..4c30804b 100644 --- a/src/luarocks/lint.lua +++ b/src/luarocks/lint.lua @@ -8,7 +8,6 @@ local util = require("luarocks.util") local download = require("luarocks.download") local fetch = require("luarocks.fetch") -util.add_run_function(lint) lint.help_summary = "Check syntax of a rockspec." lint.help_arguments = "" lint.help = [[ diff --git a/src/luarocks/list.lua b/src/luarocks/list.lua index c65e058f..8c9c5107 100644 --- a/src/luarocks/list.lua +++ b/src/luarocks/list.lua @@ -10,7 +10,6 @@ local cfg = require("luarocks.cfg") local util = require("luarocks.util") local path = require("luarocks.path") -util.add_run_function(list) list.help_summary = "List currently installed rocks." list.help_arguments = "[--porcelain] " list.help = [[ diff --git a/src/luarocks/make.lua b/src/luarocks/make.lua index 1464def7..476150eb 100644 --- a/src/luarocks/make.lua +++ b/src/luarocks/make.lua @@ -15,7 +15,6 @@ local pack = require("luarocks.pack") local remove = require("luarocks.remove") local deps = require("luarocks.deps") -util.add_run_function(make) make.help_summary = "Compile package in current directory using a rockspec." make.help_arguments = "[--pack-binary-rock] []" make.help = [[ diff --git a/src/luarocks/make_manifest.lua b/src/luarocks/make_manifest.lua index c39c2939..b89ba47f 100644 --- a/src/luarocks/make_manifest.lua +++ b/src/luarocks/make_manifest.lua @@ -12,7 +12,6 @@ local deps = require("luarocks.deps") local fs = require("luarocks.fs") local dir = require("luarocks.dir") -util.add_run_function(make_manifest) make_manifest.help_summary = "Compile a manifest file for a repository." make_manifest.help = [[ diff --git a/src/luarocks/new_version.lua b/src/luarocks/new_version.lua index bd73e308..eb5dea25 100644 --- a/src/luarocks/new_version.lua +++ b/src/luarocks/new_version.lua @@ -10,7 +10,6 @@ local persist = require("luarocks.persist") local fs = require("luarocks.fs") local type_check = require("luarocks.type_check") -util.add_run_function(new_version) new_version.help_summary = "Auto-write a rockspec for a new version of a rock." new_version.help_arguments = "[--tag=] [|] [] []" new_version.help = [[ diff --git a/src/luarocks/pack.lua b/src/luarocks/pack.lua index 277cf246..3a163e92 100644 --- a/src/luarocks/pack.lua +++ b/src/luarocks/pack.lua @@ -16,7 +16,6 @@ local dir = require("luarocks.dir") local manif = require("luarocks.manif") local search = require("luarocks.search") -util.add_run_function(pack) pack.help_summary = "Create a rock, packing sources or binaries." pack.help_arguments = "{| []}" pack.help = [[ diff --git a/src/luarocks/path_cmd.lua b/src/luarocks/path_cmd.lua index 15fb9ca2..c0329977 100644 --- a/src/luarocks/path_cmd.lua +++ b/src/luarocks/path_cmd.lua @@ -7,7 +7,6 @@ local util = require("luarocks.util") local deps = require("luarocks.deps") local cfg = require("luarocks.cfg") -util.add_run_function(path_cmd) path_cmd.help_summary = "Return the currently configured package path." path_cmd.help_arguments = "" path_cmd.help = [[ diff --git a/src/luarocks/purge.lua b/src/luarocks/purge.lua index 1ce46c0f..18043cc3 100644 --- a/src/luarocks/purge.lua +++ b/src/luarocks/purge.lua @@ -14,7 +14,6 @@ local manif = require("luarocks.manif") local cfg = require("luarocks.cfg") local remove = require("luarocks.remove") -util.add_run_function(purge) purge.help_summary = "Remove all installed rocks from a tree." purge.help_arguments = "--tree= [--old-versions]" purge.help = [[ diff --git a/src/luarocks/refresh_cache.lua b/src/luarocks/refresh_cache.lua index bbfd1f4d..1261044f 100644 --- a/src/luarocks/refresh_cache.lua +++ b/src/luarocks/refresh_cache.lua @@ -3,11 +3,9 @@ local refresh_cache = {} package.loaded["luarocks.refresh_cache"] = refresh_cache -local util = require("luarocks.util") local cfg = require("luarocks.cfg") local cache = require("luarocks.cache") -util.add_run_function(refresh_cache) refresh_cache.help_summary = "Refresh local cache of a remote rocks server." refresh_cache.help_arguments = "[--from=]" refresh_cache.help = [[ diff --git a/src/luarocks/remove.lua b/src/luarocks/remove.lua index d72fabaf..3f62e89e 100644 --- a/src/luarocks/remove.lua +++ b/src/luarocks/remove.lua @@ -14,7 +14,6 @@ local cfg = require("luarocks.cfg") local manif = require("luarocks.manif") local fs = require("luarocks.fs") -util.add_run_function(remove) remove.help_summary = "Uninstall a rock." remove.help_arguments = "[--force|--force-fast] []" remove.help = [[ diff --git a/src/luarocks/search.lua b/src/luarocks/search.lua index eaa321d5..c3f00a7c 100644 --- a/src/luarocks/search.lua +++ b/src/luarocks/search.lua @@ -11,7 +11,6 @@ local deps = require("luarocks.deps") local cfg = require("luarocks.cfg") local util = require("luarocks.util") -util.add_run_function(search) search.help_summary = "Query the LuaRocks servers." search.help_arguments = "[--source] [--binary] { [] | --all }" search.help = [[ diff --git a/src/luarocks/show.lua b/src/luarocks/show.lua index 01860e78..df992f5c 100644 --- a/src/luarocks/show.lua +++ b/src/luarocks/show.lua @@ -11,7 +11,6 @@ local deps = require("luarocks.deps") local fetch = require("luarocks.fetch") local manif = require("luarocks.manif") -util.add_run_function(show) show.help_summary = "Show information about an installed rock." show.help = [[ diff --git a/src/luarocks/unpack.lua b/src/luarocks/unpack.lua index 2face005..4e4d9a06 100644 --- a/src/luarocks/unpack.lua +++ b/src/luarocks/unpack.lua @@ -11,7 +11,6 @@ local build = require("luarocks.build") local dir = require("luarocks.dir") local cfg = require("luarocks.cfg") -util.add_run_function(unpack) unpack.help_summary = "Unpack the contents of a rock." unpack.help_arguments = "[--force] {| []}" unpack.help = [[ diff --git a/src/luarocks/upload.lua b/src/luarocks/upload.lua index 3adc1704..5031b1ef 100644 --- a/src/luarocks/upload.lua +++ b/src/luarocks/upload.lua @@ -7,7 +7,6 @@ local pack = require("luarocks.pack") local cfg = require("luarocks.cfg") local Api = require("luarocks.upload.api") -util.add_run_function(upload) upload.help_summary = "Upload a rockspec to the public rocks repository." upload.help_arguments = "[--skip-pack] [--api-key=] [--force] " upload.help = [[ diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua index 4aa474ff..d27710c1 100644 --- a/src/luarocks/util.lua +++ b/src/luarocks/util.lua @@ -210,13 +210,6 @@ function util.parse_flags(...) return flags, unpack(out) end --- Adds legacy 'run' function to a command module. --- @param command table: command module with 'command' function, --- the added 'run' function calls it after parseing command-line arguments. -function util.add_run_function(command) - command.run = function(...) return command.command(util.parse_flags(...)) end -end - --- Merges contents of src on top of dst's contents. -- @param dst Destination table, which will receive src's contents. -- @param src Table which provides new contents to dst. diff --git a/src/luarocks/validate.lua b/src/luarocks/validate.lua index c4570aa4..f0452bbd 100644 --- a/src/luarocks/validate.lua +++ b/src/luarocks/validate.lua @@ -11,7 +11,6 @@ local build = require("luarocks.build") local install = require("luarocks.install") local util = require("luarocks.util") -util.add_run_function(validate) validate.help_summary = "Sandboxed test of build/install of all packages in a repository." validate.help = [[ diff --git a/src/luarocks/write_rockspec.lua b/src/luarocks/write_rockspec.lua index 33edeb1b..68c00cce 100644 --- a/src/luarocks/write_rockspec.lua +++ b/src/luarocks/write_rockspec.lua @@ -11,7 +11,6 @@ local persist = require("luarocks.persist") local type_check = require("luarocks.type_check") local util = require("luarocks.util") -util.add_run_function(write_rockspec) write_rockspec.help_summary = "Write a template for a rockspec file." write_rockspec.help_arguments = "[--output= ...] [] [] [|]" write_rockspec.help = [[ -- cgit v1.2.3-55-g6feb From 26f498d8b7a24d976f814c2cd1a9e544893d276a Mon Sep 17 00:00:00 2001 From: Peter Melnichenko Date: Mon, 13 Jun 2016 13:17:19 +0300 Subject: Remove now unused function deps.deps_mode_to_flag --- src/luarocks/deps.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua index 6cf5bf9d..0d85d33e 100644 --- a/src/luarocks/deps.lua +++ b/src/luarocks/deps.lua @@ -760,8 +760,4 @@ function deps.get_deps_mode(flags) end end -function deps.deps_mode_to_flag(deps_mode) - return "--deps-mode="..deps_mode -end - return deps -- cgit v1.2.3-55-g6feb From f2beb5ee1fd2c54d59a43f4f2a8fe0f7a1701daa Mon Sep 17 00:00:00 2001 From: Hisham Date: Thu, 30 Jun 2016 01:15:01 -0300 Subject: Core modules reorganization. Isolate all modules used by luarocks.loader in a luarocks.core.* namespace. Core modules can only require other core modules, and they only use require() at the top-level chunk. In other words, after luarocks.loader is setup, package.path can be altered at will and it should not affect the ability of luarocks.loader to run, and the luarocks.loader modules should not interfere with modules loaded later. This was motivated by @robooo's GSoC work on porting the LuaRocks test suite to Lua using Busted. Busted itself runs using luarocks.loader, and we need to ensure that the modules loaded by the tests do not conflict with the ones used by luarocks.loader. A circular dependency between luarocks.manif and luarocks.search was removed by creating a separate luarocks.manif.writer module. Also, luarocks-admin commands were moved to a luarocks.admin.* namespace. --- Makefile.setup.inc | 26 +- appveyor.yml | 4 +- install.bat | 2 +- makedist | 8 +- src/bin/luarocks | 2 +- src/bin/luarocks-admin | 10 +- src/luarocks/add.lua | 121 ------ src/luarocks/admin/add.lua | 129 ++++++ src/luarocks/admin/cache.lua | 77 ++++ src/luarocks/admin/make_manifest.lua | 51 +++ src/luarocks/admin/refresh_cache.lua | 30 ++ src/luarocks/admin/remove.lua | 90 +++++ src/luarocks/admin_remove.lua | 91 ----- src/luarocks/build.lua | 9 +- src/luarocks/build/builtin.lua | 2 +- src/luarocks/build/cmake.lua | 2 +- src/luarocks/build/make.lua | 2 +- src/luarocks/cache.lua | 78 ---- src/luarocks/cfg.lua | 741 ----------------------------------- src/luarocks/command_line.lua | 2 +- src/luarocks/config_cmd.lua | 2 +- src/luarocks/core/cfg.lua | 741 +++++++++++++++++++++++++++++++++++ src/luarocks/core/deps.lua | 192 +++++++++ src/luarocks/core/dir.lua | 55 +++ src/luarocks/core/manif.lua | 107 +++++ src/luarocks/core/path.lua | 149 +++++++ src/luarocks/core/persist.lua | 81 ++++ src/luarocks/core/type_check.lua | 226 +++++++++++ src/luarocks/core/util.lua | 202 ++++++++++ src/luarocks/deps.lua | 215 +--------- src/luarocks/dir.lua | 49 +-- src/luarocks/doc.lua | 1 - src/luarocks/download.lua | 3 +- src/luarocks/fetch.lua | 3 +- src/luarocks/fs.lua | 3 +- src/luarocks/fs/lua.lua | 2 +- src/luarocks/fs/tools.lua | 2 +- src/luarocks/fs/unix.lua | 2 +- src/luarocks/fs/unix/tools.lua | 2 +- src/luarocks/fs/win32.lua | 2 +- src/luarocks/fs/win32/tools.lua | 2 +- src/luarocks/help.lua | 2 +- src/luarocks/index.lua | 1 - src/luarocks/install.lua | 9 +- src/luarocks/lint.lua | 1 - src/luarocks/list.lua | 3 +- src/luarocks/loader.lua | 31 +- src/luarocks/make.lua | 3 +- src/luarocks/make_manifest.lua | 52 --- src/luarocks/manif.lua | 367 +---------------- src/luarocks/manif/writer.lua | 360 +++++++++++++++++ src/luarocks/manif_core.lua | 107 ----- src/luarocks/pack.lua | 3 +- src/luarocks/path.lua | 142 +------ src/luarocks/path_cmd.lua | 2 +- src/luarocks/persist.lua | 75 +--- src/luarocks/purge.lua | 9 +- src/luarocks/refresh_cache.lua | 31 -- src/luarocks/remove.lua | 7 +- src/luarocks/repos.lua | 3 +- src/luarocks/search.lua | 4 +- src/luarocks/show.lua | 3 +- src/luarocks/type_check.lua | 224 +---------- src/luarocks/unpack.lua | 3 +- src/luarocks/upload.lua | 2 +- src/luarocks/upload/api.lua | 2 +- src/luarocks/util.lua | 203 +--------- src/luarocks/validate.lua | 158 -------- src/luarocks/write_rockspec.lua | 3 +- test/testing.sh | 1 - 70 files changed, 2615 insertions(+), 2714 deletions(-) delete mode 100644 src/luarocks/add.lua create mode 100644 src/luarocks/admin/add.lua create mode 100644 src/luarocks/admin/cache.lua create mode 100644 src/luarocks/admin/make_manifest.lua create mode 100644 src/luarocks/admin/refresh_cache.lua create mode 100644 src/luarocks/admin/remove.lua delete mode 100644 src/luarocks/admin_remove.lua delete mode 100644 src/luarocks/cache.lua delete mode 100644 src/luarocks/cfg.lua create mode 100644 src/luarocks/core/cfg.lua create mode 100644 src/luarocks/core/deps.lua create mode 100644 src/luarocks/core/dir.lua create mode 100644 src/luarocks/core/manif.lua create mode 100644 src/luarocks/core/path.lua create mode 100644 src/luarocks/core/persist.lua create mode 100644 src/luarocks/core/type_check.lua create mode 100644 src/luarocks/core/util.lua delete mode 100644 src/luarocks/make_manifest.lua create mode 100644 src/luarocks/manif/writer.lua delete mode 100644 src/luarocks/manif_core.lua delete mode 100644 src/luarocks/refresh_cache.lua delete mode 100644 src/luarocks/validate.lua diff --git a/Makefile.setup.inc b/Makefile.setup.inc index 092d0423..eff6f49b 100644 --- a/Makefile.setup.inc +++ b/Makefile.setup.inc @@ -5,16 +5,16 @@ BINDIR ?= $(PREFIX)/bin LUADIR ?= $(PREFIX)/share/lua/$(LUA_VERSION)/ BIN_FILES = luarocks luarocks-admin -LUAROCKS_FILES = fs/tools.lua fs/unix/tools.lua fs/unix.lua fs/win32/tools.lua fs/win32.lua \ -fs/lua.lua persist.lua list.lua require.lua repos.lua dir.lua make_manifest.lua \ -command_line.lua config_cmd.lua install.lua build/command.lua build/cmake.lua \ -build/make.lua build/builtin.lua fetch/cvs.lua fetch/git.lua fetch/sscm.lua \ -tools/patch.lua fetch/svn.lua tools/zip.lua tools/tar.lua pack.lua type_check.lua \ -make.lua remove.lua fs.lua manif.lua add.lua deps.lua build.lua search.lua \ -show.lua manif_core.lua fetch.lua unpack.lua validate.lua cfg.lua download.lua \ -help.lua util.lua index.lua cache.lua refresh_cache.lua loader.lua \ -admin_remove.lua fetch/hg.lua fetch/git_file.lua new_version.lua lint.lua \ -purge.lua path.lua path_cmd.lua write_rockspec.lua doc.lua upload.lua \ -upload/api.lua upload/multipart.lua fetch/git_http.lua fetch/hg_http.lua \ -fetch/hg_https.lua fetch/hg_ssh.lua fetch/git_https.lua fetch/git_ssh.lua - +LUAROCKS_FILES = admin/add.lua admin/cache.lua admin/make_manifest.lua admin/refresh_cache.lua \ +admin/remove.lua build.lua build/builtin.lua build/cmake.lua build/command.lua \ +build/make.lua command_line.lua config_cmd.lua core/cfg.lua core/deps.lua \ +core/dir.lua core/manif.lua core/path.lua core/persist.lua core/type_check.lua \ +core/util.lua deps.lua dir.lua doc.lua download.lua fetch.lua fetch/cvs.lua \ +fetch/git.lua fetch/git_file.lua fetch/git_http.lua fetch/git_https.lua \ +fetch/git_ssh.lua fetch/hg.lua fetch/hg_http.lua fetch/hg_https.lua fetch/hg_ssh.lua \ +fetch/sscm.lua fetch/svn.lua fs.lua fs/lua.lua fs/tools.lua fs/unix.lua fs/unix/tools.lua \ +fs/win32.lua fs/win32/tools.lua help.lua index.lua install.lua lint.lua list.lua \ +loader.lua make.lua manif.lua manif/writer.lua new_version.lua pack.lua path.lua \ +path_cmd.lua persist.lua purge.lua remove.lua repos.lua require.lua search.lua \ +show.lua tools/patch.lua tools/tar.lua tools/zip.lua type_check.lua unpack.lua \ +upload.lua upload/api.lua upload/multipart.lua util.lua write_rockspec.lua diff --git a/appveyor.yml b/appveyor.yml index d7fc7cc2..d95ea952 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,9 +1,9 @@ -version: 2.2.1.{build}-test +version: 3.0.0.{build}-test shallow_clone: true environment: - LUAROCKS_VER: 2.2.1 + LUAROCKS_VER: 3.0.0 matrix: - LUA_VER: 5.1.5 diff --git a/install.bat b/install.bat index dcd132ff..3deb3d14 100644 --- a/install.bat +++ b/install.bat @@ -6,7 +6,7 @@ local vars = {} vars.PREFIX = nil -vars.VERSION = "2.2" +vars.VERSION = "3.0" vars.SYSCONFDIR = nil vars.SYSCONFFORCE = nil vars.CONFBACKUPDIR = nil diff --git a/makedist b/makedist index fb41b981..05da40a8 100755 --- a/makedist +++ b/makedist @@ -20,9 +20,9 @@ grep -q "\"$1\"" rockspec || { exit 1 } -grep -q "program_version = \"$1\"" src/luarocks/cfg.lua || { +grep -q "program_version = \"$1\"" src/luarocks/core/cfg.lua || { echo - echo "program_version in src/luarocks/cfg.lua is incorrect. Please fix it." + echo "program_version in src/luarocks/core/cfg.lua is incorrect. Please fix it." exit 1 } @@ -41,9 +41,9 @@ grep -q "LUAROCKS_VER: $1" appveyor.yml || { # e.g. if $1 is "2.3.0", $program_series is "2.3" program_series=${1%.*} -grep -q "program_series = \"$program_series\"" src/luarocks/cfg.lua || { +grep -q "program_series = \"$program_series\"" src/luarocks/core/cfg.lua || { echo - echo "program_series in src/luarocks/cfg.lua is incorrect. Please fix it." + echo "program_series in src/luarocks/core/cfg.lua is incorrect. Please fix it." exit 1 } diff --git a/src/bin/luarocks b/src/bin/luarocks index be6c2b81..21f17da9 100755 --- a/src/bin/luarocks +++ b/src/bin/luarocks @@ -1,7 +1,7 @@ #!/usr/bin/env lua -- this should be loaded first. -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local loader = require("luarocks.loader") local command_line = require("luarocks.command_line") diff --git a/src/bin/luarocks-admin b/src/bin/luarocks-admin index 2890d1f1..660c0a70 100755 --- a/src/bin/luarocks-admin +++ b/src/bin/luarocks-admin @@ -1,7 +1,7 @@ #!/usr/bin/env lua -- this should be loaded first. -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local loader = require("luarocks.loader") local command_line = require("luarocks.command_line") @@ -10,10 +10,10 @@ program_description = "LuaRocks repository administration interface" commands = { help = "luarocks.help", - make_manifest = "luarocks.make_manifest", - add = "luarocks.add", - remove = "luarocks.admin_remove", - refresh_cache = "luarocks.refresh_cache", + make_manifest = "luarocks.admin.make_manifest", + add = "luarocks.admin.add", + remove = "luarocks.admin.remove", + refresh_cache = "luarocks.admin.refresh_cache", } command_line.run_command(...) diff --git a/src/luarocks/add.lua b/src/luarocks/add.lua deleted file mode 100644 index 66f7504f..00000000 --- a/src/luarocks/add.lua +++ /dev/null @@ -1,121 +0,0 @@ - ---- Module implementing the luarocks-admin "add" command. --- Adds a rock or rockspec to a rocks server. -local add = {} -package.loaded["luarocks.add"] = add - -local cfg = require("luarocks.cfg") -local util = require("luarocks.util") -local dir = require("luarocks.dir") -local manif = require("luarocks.manif") -local index = require("luarocks.index") -local fs = require("luarocks.fs") -local cache = require("luarocks.cache") - -add.help_summary = "Add a rock or rockspec to a rocks server." -add.help_arguments = "[--server=] [--no-refresh] {|...}" -add.help = [[ -Arguments are local files, which may be rockspecs or rocks. -The flag --server indicates which server to use. -If not given, the default server set in the upload_server variable -from the configuration file is used instead. -The flag --no-refresh indicates the local cache should not be refreshed -prior to generation of the updated manifest. -]] - -local function add_files_to_server(refresh, rockfiles, server, upload_server) - assert(type(refresh) == "boolean" or not refresh) - assert(type(rockfiles) == "table") - assert(type(server) == "string") - assert(type(upload_server) == "table" or not upload_server) - - local download_url, login_url = cache.get_server_urls(server, upload_server) - local at = fs.current_dir() - local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url - - local local_cache, protocol, server_path, user, password = refresh_fn(server, download_url, cfg.upload_user, cfg.upload_password) - if not local_cache then - return nil, protocol - end - if protocol == "file" then - return nil, "Server "..server.." is not recognized, check your configuration." - end - - if not login_url then - login_url = protocol.."://"..server_path - end - - local ok, err = fs.change_dir(at) - if not ok then return nil, err end - - local files = {} - for _, rockfile in ipairs(rockfiles) do - if fs.exists(rockfile) then - util.printout("Copying file "..rockfile.." to "..local_cache.."...") - local absolute = fs.absolute_name(rockfile) - fs.copy(absolute, local_cache, cfg.perm_read) - table.insert(files, dir.base_name(absolute)) - else - util.printerr("File "..rockfile.." not found") - end - end - if #files == 0 then - return nil, "No files found" - end - - local ok, err = fs.change_dir(local_cache) - if not ok then return nil, err end - - util.printout("Updating manifest...") - manif.make_manifest(local_cache, "one", true) - - manif.zip_manifests() - - util.printout("Updating index.html...") - index.make_index(local_cache) - - local login_info = "" - if user then login_info = " -u "..user end - if password then login_info = login_info..":"..password end - if not login_url:match("/$") then - login_url = login_url .. "/" - end - - table.insert(files, "index.html") - table.insert(files, "manifest") - for ver in util.lua_versions() do - table.insert(files, "manifest-"..ver) - table.insert(files, "manifest-"..ver..".zip") - end - - -- TODO abstract away explicit 'curl' call - - local cmd - if protocol == "rsync" then - local srv, path = server_path:match("([^/]+)(/.+)") - cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/" - elseif upload_server and upload_server.sftp then - local part1, part2 = upload_server.sftp:match("^([^/]*)/(.*)$") - cmd = cfg.variables.SCP.." "..table.concat(files, " ").." "..user.."@"..part1..":/"..part2 - else - cmd = cfg.variables.CURL.." "..login_info.." -T '{"..table.concat(files, ",").."}' "..login_url - end - - util.printout(cmd) - fs.execute(cmd) - - return true -end - -function add.command(flags, ...) - local files = {...} - if #files < 1 then - return nil, "Argument missing. "..util.see_help("add", "luarocks-admin") - end - local server, server_table = cache.get_upload_server(flags["server"]) - if not server then return nil, server_table end - return add_files_to_server(not flags["no-refresh"], files, server, server_table) -end - - -return add diff --git a/src/luarocks/admin/add.lua b/src/luarocks/admin/add.lua new file mode 100644 index 00000000..daf46c1d --- /dev/null +++ b/src/luarocks/admin/add.lua @@ -0,0 +1,129 @@ + +--- Module implementing the luarocks-admin "add" command. +-- Adds a rock or rockspec to a rocks server. +local add = {} + +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") +local writer = require("luarocks.manif.writer") +local index = require("luarocks.index") +local fs = require("luarocks.fs") +local cache = require("luarocks.admin.cache") + +add.help_summary = "Add a rock or rockspec to a rocks server." +add.help_arguments = "[--server=] [--no-refresh] {|...}" +add.help = [[ +Arguments are local files, which may be rockspecs or rocks. +The flag --server indicates which server to use. +If not given, the default server set in the upload_server variable +from the configuration file is used instead. +The flag --no-refresh indicates the local cache should not be refreshed +prior to generation of the updated manifest. +]] + +local function zip_manifests() + for ver in util.lua_versions() do + local file = "manifest-"..ver + local zip = file..".zip" + fs.delete(dir.path(fs.current_dir(), zip)) + fs.zip(zip, file) + end +end + +local function add_files_to_server(refresh, rockfiles, server, upload_server) + assert(type(refresh) == "boolean" or not refresh) + assert(type(rockfiles) == "table") + assert(type(server) == "string") + assert(type(upload_server) == "table" or not upload_server) + + local download_url, login_url = cache.get_server_urls(server, upload_server) + local at = fs.current_dir() + local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url + + local local_cache, protocol, server_path, user, password = refresh_fn(server, download_url, cfg.upload_user, cfg.upload_password) + if not local_cache then + return nil, protocol + end + if protocol == "file" then + return nil, "Server "..server.." is not recognized, check your configuration." + end + + if not login_url then + login_url = protocol.."://"..server_path + end + + local ok, err = fs.change_dir(at) + if not ok then return nil, err end + + local files = {} + for _, rockfile in ipairs(rockfiles) do + if fs.exists(rockfile) then + util.printout("Copying file "..rockfile.." to "..local_cache.."...") + local absolute = fs.absolute_name(rockfile) + fs.copy(absolute, local_cache, cfg.perm_read) + table.insert(files, dir.base_name(absolute)) + else + util.printerr("File "..rockfile.." not found") + end + end + if #files == 0 then + return nil, "No files found" + end + + local ok, err = fs.change_dir(local_cache) + if not ok then return nil, err end + + util.printout("Updating manifest...") + writer.make_manifest(local_cache, "one", true) + + zip_manifests() + + util.printout("Updating index.html...") + index.make_index(local_cache) + + local login_info = "" + if user then login_info = " -u "..user end + if password then login_info = login_info..":"..password end + if not login_url:match("/$") then + login_url = login_url .. "/" + end + + table.insert(files, "index.html") + table.insert(files, "manifest") + for ver in util.lua_versions() do + table.insert(files, "manifest-"..ver) + table.insert(files, "manifest-"..ver..".zip") + end + + -- TODO abstract away explicit 'curl' call + + local cmd + if protocol == "rsync" then + local srv, path = server_path:match("([^/]+)(/.+)") + cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/" + elseif upload_server and upload_server.sftp then + local part1, part2 = upload_server.sftp:match("^([^/]*)/(.*)$") + cmd = cfg.variables.SCP.." "..table.concat(files, " ").." "..user.."@"..part1..":/"..part2 + else + cmd = cfg.variables.CURL.." "..login_info.." -T '{"..table.concat(files, ",").."}' "..login_url + end + + util.printout(cmd) + fs.execute(cmd) + + return true +end + +function add.command(flags, ...) + local files = {...} + if #files < 1 then + return nil, "Argument missing. "..util.see_help("add", "luarocks-admin") + end + local server, server_table = cache.get_upload_server(flags["server"]) + if not server then return nil, server_table end + return add_files_to_server(not flags["no-refresh"], files, server, server_table) +end + + +return add diff --git a/src/luarocks/admin/cache.lua b/src/luarocks/admin/cache.lua new file mode 100644 index 00000000..0daa0fc0 --- /dev/null +++ b/src/luarocks/admin/cache.lua @@ -0,0 +1,77 @@ + +--- Module handling the LuaRocks local cache. +-- Adds a rock or rockspec to a rocks server. +local cache = {} + +local fs = require("luarocks.fs") +local cfg = require("luarocks.core.cfg") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +function cache.get_upload_server(server) + if not server then server = cfg.upload_server end + if not server then + return nil, "No server specified and no default configured with upload_server." + end + return server, cfg.upload_servers and cfg.upload_servers[server] +end + +function cache.get_server_urls(server, upload_server) + local download_url = server + local login_url = nil + if upload_server then + if upload_server.rsync then download_url = "rsync://"..upload_server.rsync + elseif upload_server.http then download_url = "http://"..upload_server.http + elseif upload_server.ftp then download_url = "ftp://"..upload_server.ftp + end + + if upload_server.ftp then login_url = "ftp://"..upload_server.ftp + elseif upload_server.sftp then login_url = "sftp://"..upload_server.sftp + end + end + return download_url, login_url +end + +function cache.split_server_url(server, url, user, password) + local protocol, server_path = dir.split_url(url) + if server_path:match("@") then + local credentials + credentials, server_path = server_path:match("([^@]*)@(.*)") + if credentials:match(":") then + user, password = credentials:match("([^:]*):(.*)") + else + user = credentials + end + end + local local_cache = cfg.local_cache .. "/" .. server + return local_cache, protocol, server_path, user, password +end + +function cache.refresh_local_cache(server, url, user, password) + local local_cache, protocol, server_path, user, password = cache.split_server_url(server, url, user, password) + local ok, err = fs.make_dir(local_cache) + if not ok then + return nil, "Failed creating local cache dir: "..err + end + fs.change_dir(local_cache) + if not ok then return nil, err end + util.printout("Refreshing cache "..local_cache.."...") + + -- TODO abstract away explicit 'wget' call + local ok = false + if protocol == "rsync" then + local srv, path = server_path:match("([^/]+)(/.+)") + ok = fs.execute(cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." -e ssh "..user.."@"..srv..":"..path.."/ "..local_cache.."/") + else + local login_info = "" + if user then login_info = " --user="..user end + if password then login_info = login_info .. " --password="..password end + ok = fs.execute(cfg.variables.WGET.." --no-cache -q -m -np -nd "..protocol.."://"..server_path..login_info) + end + if not ok then + return nil, "Failed downloading cache." + end + return local_cache, protocol, server_path, user, password +end + +return cache diff --git a/src/luarocks/admin/make_manifest.lua b/src/luarocks/admin/make_manifest.lua new file mode 100644 index 00000000..57851942 --- /dev/null +++ b/src/luarocks/admin/make_manifest.lua @@ -0,0 +1,51 @@ + +--- Module implementing the luarocks-admin "make_manifest" command. +-- Compile a manifest file for a repository. +local make_manifest = {} + +local writer = require("luarocks.manif.writer") +local index = require("luarocks.index") +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local deps = require("luarocks.deps") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") + +make_manifest.help_summary = "Compile a manifest file for a repository." + +make_manifest.help = [[ +, if given, is a local repository pathname. + +--local-tree If given, do not write versioned versions of the manifest file. + Use this when rebuilding the manifest of a local rocks tree. +]] + +--- Driver function for "make_manifest" command. +-- @param repo string or nil: Pathname of a local repository. If not given, +-- the default local repository configured as cfg.rocks_dir is used. +-- @return boolean or (nil, string): True if manifest was generated, +-- or nil and an error message. +function make_manifest.command(flags, repo) + assert(type(repo) == "string" or not repo) + repo = repo or cfg.rocks_dir + + util.printout("Making manifest for "..repo) + + if repo:match("/lib/luarocks") and not flags["local-tree"] then + util.warning("This looks like a local rocks tree, but you did not pass --local-tree.") + end + + local ok, err = writer.make_manifest(repo, deps.get_deps_mode(flags), not flags["local-tree"]) + if ok and not flags["local-tree"] then + util.printout("Generating index.html for "..repo) + index.make_index(repo) + end + if flags["local-tree"] then + for luaver in util.lua_versions() do + fs.delete(dir.path(repo, "manifest-"..luaver)) + end + end + return ok, err +end + +return make_manifest diff --git a/src/luarocks/admin/refresh_cache.lua b/src/luarocks/admin/refresh_cache.lua new file mode 100644 index 00000000..947dbfb0 --- /dev/null +++ b/src/luarocks/admin/refresh_cache.lua @@ -0,0 +1,30 @@ + +--- Module implementing the luarocks-admin "refresh_cache" command. +local refresh_cache = {} + +local cfg = require("luarocks.core.cfg") +local cache = require("luarocks.admin.cache") + +refresh_cache.help_summary = "Refresh local cache of a remote rocks server." +refresh_cache.help_arguments = "[--from=]" +refresh_cache.help = [[ +The flag --from indicates which server to use. +If not given, the default server set in the upload_server variable +from the configuration file is used instead. +]] + +function refresh_cache.command(flags) + local server, upload_server = cache.get_upload_server(flags["server"]) + if not server then return nil, upload_server end + local download_url = cache.get_server_urls(server, upload_server) + + local ok, err = cache.refresh_local_cache(server, download_url, cfg.upload_user, cfg.upload_password) + if not ok then + return nil, err + else + return true + end +end + + +return refresh_cache diff --git a/src/luarocks/admin/remove.lua b/src/luarocks/admin/remove.lua new file mode 100644 index 00000000..763a166f --- /dev/null +++ b/src/luarocks/admin/remove.lua @@ -0,0 +1,90 @@ + +--- Module implementing the luarocks-admin "remove" command. +-- Removes a rock or rockspec from a rocks server. +local admin_remove = {} + +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") +local writer = require("luarocks.manif.writer") +local index = require("luarocks.index") +local fs = require("luarocks.fs") +local cache = require("luarocks.admin.cache") + +admin_remove.help_summary = "Remove a rock or rockspec from a rocks server." +admin_remove.help_arguments = "[--server=] [--no-refresh] {|...}" +admin_remove.help = [[ +Arguments are local files, which may be rockspecs or rocks. +The flag --server indicates which server to use. +If not given, the default server set in the upload_server variable +from the configuration file is used instead. +The flag --no-refresh indicates the local cache should not be refreshed +prior to generation of the updated manifest. +]] + +local function remove_files_from_server(refresh, rockfiles, server, upload_server) + assert(type(refresh) == "boolean" or not refresh) + assert(type(rockfiles) == "table") + assert(type(server) == "string") + assert(type(upload_server) == "table" or not upload_server) + + local download_url, login_url = cache.get_server_urls(server, upload_server) + local at = fs.current_dir() + local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url + + local local_cache, protocol, server_path, user, password = refresh_fn(server, download_url, cfg.upload_user, cfg.upload_password) + if not local_cache then + return nil, protocol + end + if protocol ~= "rsync" then + return nil, "This command requires 'rsync', check your configuration." + end + + local ok, err = fs.change_dir(at) + if not ok then return nil, err end + + local nr_files = 0 + for _, rockfile in ipairs(rockfiles) do + local basename = dir.base_name(rockfile) + local file = dir.path(local_cache, basename) + util.printout("Removing file "..file.."...") + fs.delete(file) + if not fs.exists(file) then + nr_files = nr_files + 1 + else + util.printerr("Failed removing "..file) + end + end + if nr_files == 0 then + return nil, "No files removed." + end + + local ok, err = fs.change_dir(local_cache) + if not ok then return nil, err end + + util.printout("Updating manifest...") + writer.make_manifest(local_cache, "one", true) + util.printout("Updating index.html...") + index.make_index(local_cache) + + local srv, path = server_path:match("([^/]+)(/.+)") + local cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." --delete -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/" + + util.printout(cmd) + fs.execute(cmd) + + return true +end + +function admin_remove.command(flags, ...) + local files = {...} + if #files < 1 then + return nil, "Argument missing. "..util.see_help("remove", "luarocks-admin") + end + local server, server_table = cache.get_upload_server(flags["server"]) + if not server then return nil, server_table end + return remove_files_from_server(not flags["no-refresh"], files, server, server_table) +end + + +return admin_remove diff --git a/src/luarocks/admin_remove.lua b/src/luarocks/admin_remove.lua deleted file mode 100644 index be1e7cbc..00000000 --- a/src/luarocks/admin_remove.lua +++ /dev/null @@ -1,91 +0,0 @@ - ---- Module implementing the luarocks-admin "remove" command. --- Removes a rock or rockspec from a rocks server. -local admin_remove = {} -package.loaded["luarocks.admin_remove"] = admin_remove - -local cfg = require("luarocks.cfg") -local util = require("luarocks.util") -local dir = require("luarocks.dir") -local manif = require("luarocks.manif") -local index = require("luarocks.index") -local fs = require("luarocks.fs") -local cache = require("luarocks.cache") - -admin_remove.help_summary = "Remove a rock or rockspec from a rocks server." -admin_remove.help_arguments = "[--server=] [--no-refresh] {|...}" -admin_remove.help = [[ -Arguments are local files, which may be rockspecs or rocks. -The flag --server indicates which server to use. -If not given, the default server set in the upload_server variable -from the configuration file is used instead. -The flag --no-refresh indicates the local cache should not be refreshed -prior to generation of the updated manifest. -]] - -local function remove_files_from_server(refresh, rockfiles, server, upload_server) - assert(type(refresh) == "boolean" or not refresh) - assert(type(rockfiles) == "table") - assert(type(server) == "string") - assert(type(upload_server) == "table" or not upload_server) - - local download_url, login_url = cache.get_server_urls(server, upload_server) - local at = fs.current_dir() - local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url - - local local_cache, protocol, server_path, user, password = refresh_fn(server, download_url, cfg.upload_user, cfg.upload_password) - if not local_cache then - return nil, protocol - end - if protocol ~= "rsync" then - return nil, "This command requires 'rsync', check your configuration." - end - - local ok, err = fs.change_dir(at) - if not ok then return nil, err end - - local nr_files = 0 - for _, rockfile in ipairs(rockfiles) do - local basename = dir.base_name(rockfile) - local file = dir.path(local_cache, basename) - util.printout("Removing file "..file.."...") - fs.delete(file) - if not fs.exists(file) then - nr_files = nr_files + 1 - else - util.printerr("Failed removing "..file) - end - end - if nr_files == 0 then - return nil, "No files removed." - end - - local ok, err = fs.change_dir(local_cache) - if not ok then return nil, err end - - util.printout("Updating manifest...") - manif.make_manifest(local_cache, "one", true) - util.printout("Updating index.html...") - index.make_index(local_cache) - - local srv, path = server_path:match("([^/]+)(/.+)") - local cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." --delete -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/" - - util.printout(cmd) - fs.execute(cmd) - - return true -end - -function admin_remove.command(flags, ...) - local files = {...} - if #files < 1 then - return nil, "Argument missing. "..util.see_help("remove", "luarocks-admin") - end - local server, server_table = cache.get_upload_server(flags["server"]) - if not server then return nil, server_table end - return remove_files_from_server(not flags["no-refresh"], files, server, server_table) -end - - -return admin_remove diff --git a/src/luarocks/build.lua b/src/luarocks/build.lua index 2d29e23f..d7d8ed07 100644 --- a/src/luarocks/build.lua +++ b/src/luarocks/build.lua @@ -2,7 +2,6 @@ --- Module implementing the LuaRocks "build" command. -- Builds a rock, compiling its C parts if any. local build = {} -package.loaded["luarocks.build"] = build local pack = require("luarocks.pack") local path = require("luarocks.path") @@ -12,9 +11,9 @@ local fetch = require("luarocks.fetch") local fs = require("luarocks.fs") local dir = require("luarocks.dir") local deps = require("luarocks.deps") -local manif = require("luarocks.manif") +local writer = require("luarocks.manif.writer") local remove = require("luarocks.remove") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") build.help_summary = "Build/compile a rock." build.help_arguments = "[--pack-binary-rock] [--keep] {|| []}" @@ -346,7 +345,7 @@ function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_m fs.pop_dir() end - ok, err = manif.make_rock_manifest(name, version) + ok, err = writer.make_rock_manifest(name, version) if err then return nil, err end ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec)) @@ -360,7 +359,7 @@ function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_m ok, err = repos.run_hook(rockspec, "post_install") if err then return nil, err end - ok, err = manif.update_manifest(name, version, nil, deps_mode) + ok, err = writer.update_manifest(name, version, nil, deps_mode) if err then return nil, err end util.announce_install(rockspec) diff --git a/src/luarocks/build/builtin.lua b/src/luarocks/build/builtin.lua index 81fa8b13..d9785b0c 100644 --- a/src/luarocks/build/builtin.lua +++ b/src/luarocks/build/builtin.lua @@ -7,7 +7,7 @@ local unpack = unpack or table.unpack local fs = require("luarocks.fs") local path = require("luarocks.path") local util = require("luarocks.util") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local dir = require("luarocks.dir") --- Run a command displaying its execution on standard output. diff --git a/src/luarocks/build/cmake.lua b/src/luarocks/build/cmake.lua index c2712bb2..43702979 100644 --- a/src/luarocks/build/cmake.lua +++ b/src/luarocks/build/cmake.lua @@ -4,7 +4,7 @@ local cmake = {} local fs = require("luarocks.fs") local util = require("luarocks.util") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local deps = require("luarocks.deps") --- Driver function for the "cmake" build back-end. diff --git a/src/luarocks/build/make.lua b/src/luarocks/build/make.lua index 69e73c2e..ded015b7 100644 --- a/src/luarocks/build/make.lua +++ b/src/luarocks/build/make.lua @@ -6,7 +6,7 @@ local unpack = unpack or table.unpack local fs = require("luarocks.fs") local util = require("luarocks.util") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") --- Call "make" with given target and variables -- @param make_cmd string: the make command to be used (typically diff --git a/src/luarocks/cache.lua b/src/luarocks/cache.lua deleted file mode 100644 index 4a95f70e..00000000 --- a/src/luarocks/cache.lua +++ /dev/null @@ -1,78 +0,0 @@ - ---- Module handling the LuaRocks local cache. --- Adds a rock or rockspec to a rocks server. -local cache = {} -package.loaded["luarocks.cache"] = cache - -local fs = require("luarocks.fs") -local cfg = require("luarocks.cfg") -local dir = require("luarocks.dir") -local util = require("luarocks.util") - -function cache.get_upload_server(server) - if not server then server = cfg.upload_server end - if not server then - return nil, "No server specified and no default configured with upload_server." - end - return server, cfg.upload_servers and cfg.upload_servers[server] -end - -function cache.get_server_urls(server, upload_server) - local download_url = server - local login_url = nil - if upload_server then - if upload_server.rsync then download_url = "rsync://"..upload_server.rsync - elseif upload_server.http then download_url = "http://"..upload_server.http - elseif upload_server.ftp then download_url = "ftp://"..upload_server.ftp - end - - if upload_server.ftp then login_url = "ftp://"..upload_server.ftp - elseif upload_server.sftp then login_url = "sftp://"..upload_server.sftp - end - end - return download_url, login_url -end - -function cache.split_server_url(server, url, user, password) - local protocol, server_path = dir.split_url(url) - if server_path:match("@") then - local credentials - credentials, server_path = server_path:match("([^@]*)@(.*)") - if credentials:match(":") then - user, password = credentials:match("([^:]*):(.*)") - else - user = credentials - end - end - local local_cache = cfg.local_cache .. "/" .. server - return local_cache, protocol, server_path, user, password -end - -function cache.refresh_local_cache(server, url, user, password) - local local_cache, protocol, server_path, user, password = cache.split_server_url(server, url, user, password) - local ok, err = fs.make_dir(local_cache) - if not ok then - return nil, "Failed creating local cache dir: "..err - end - fs.change_dir(local_cache) - if not ok then return nil, err end - util.printout("Refreshing cache "..local_cache.."...") - - -- TODO abstract away explicit 'wget' call - local ok = false - if protocol == "rsync" then - local srv, path = server_path:match("([^/]+)(/.+)") - ok = fs.execute(cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." -e ssh "..user.."@"..srv..":"..path.."/ "..local_cache.."/") - else - local login_info = "" - if user then login_info = " --user="..user end - if password then login_info = login_info .. " --password="..password end - ok = fs.execute(cfg.variables.WGET.." --no-cache -q -m -np -nd "..protocol.."://"..server_path..login_info) - end - if not ok then - return nil, "Failed downloading cache." - end - return local_cache, protocol, server_path, user, password -end - -return cache diff --git a/src/luarocks/cfg.lua b/src/luarocks/cfg.lua deleted file mode 100644 index 53387276..00000000 --- a/src/luarocks/cfg.lua +++ /dev/null @@ -1,741 +0,0 @@ ---- Configuration for LuaRocks. --- Tries to load the user's configuration file and --- defines defaults for unset values. See the --- config --- file format documentation for details. --- --- End-users shouldn't edit this file. They can override any defaults --- set in this file using their system-wide $LUAROCKS_SYSCONFIG file --- (see luarocks.site_config) or their user-specific configuration file --- (~/.luarocks/config.lua on Unix or %APPDATA%/luarocks/config.lua on --- Windows). - -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 - ---module("luarocks.cfg") -local cfg = {} -package.loaded["luarocks.cfg"] = cfg - -local util = require("luarocks.util") - -cfg.lua_version = _VERSION:sub(5) -local version_suffix = cfg.lua_version:gsub("%.", "_") - --- Load site-local global configurations -local ok, site_config = pcall(require, "luarocks.site_config_"..version_suffix) -if not ok then - ok, site_config = pcall(require, "luarocks.site_config") -end -if not ok then - io.stderr:write("Site-local luarocks/site_config.lua file not found. Incomplete installation?\n") - site_config = {} -end - -cfg.program_version = "scm" -cfg.program_series = "2.2" -cfg.major_version = (cfg.program_version:match("([^.]%.[^.])")) or cfg.program_series -cfg.variables = {} -cfg.rocks_trees = {} -cfg.platforms = {} - -local persist = require("luarocks.persist") - -cfg.errorcodes = setmetatable({ - OK = 0, - UNSPECIFIED = 1, - PERMISSIONDENIED = 2, - CONFIGFILE = 3, - CRASH = 99 -},{ - __index = function(t, key) - local val = rawget(t, key) - if not val then - error("'"..tostring(key).."' is not a valid errorcode", 2) - end - return val - end -}) - - -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(cfg.errorcodes.UNSPECIFIED) -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 - -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 - --- Set order for platform overrides -local platform_order = { - -- Unixes - unix = 1, - bsd = 2, - solaris = 3, - netbsd = 4, - openbsd = 5, - freebsd = 6, - linux = 7, - macosx = 8, - cygwin = 9, - msys = 10, - haiku = 11, - -- Windows - win32 = 12, - mingw32 = 13, - windows = 14 } - --- 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 - --- 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 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) -end - --- load config file from a list until first succesful one. Info is --- added to `cfg` module table, returns filepath of succesfully loaded --- file or nil if it failed -local load_config_file = function(list) - for _, filepath in ipairs(list) do - 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(cfg.errorcodes.CONFIGFILE) - end - if result then - -- succes in loading and running, merge contents and exit - merge_overrides(result) - return filepath - end - end - return nil -- nothing was loaded -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_dir.."/config.lua", - }) - sys_config_ok = (sys_config_file ~= nil) -end - --- 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 - local list = { config_env_value } - home_config_file = load_config_file(list) - 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") - end - end - - -- try the alternative defaults if there was no environment specified file or it didn't work - if not home_config_ok then - local list = { - home_config_file_default, - home_config_dir.."/config.lua", - } - home_config_file = load_config_file(list) - home_config_ok = (home_config_file ~= nil) - end -end - - -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 site_config.LUAROCKS_ROCKS_TREE then - table.insert(cfg.rocks_trees, { name = "system", root = site_config.LUAROCKS_ROCKS_TREE } ) - end -end - - --- update platforms list; keyed -> array -do - local lst = {} -- use temp array to not confuse `pairs` in loop - for plat in pairs(cfg.platforms) do - if cfg.platforms[plat] then -- entries set to 'false' skipped - if not platform_order[plat] then - local pl = "" - for k,_ in pairs(platform_order) do pl = pl .. ", " .. k end - io.stderr:write("Bad platform given; "..tostring(plat)..". Valid entries are: "..pl:sub(3,-1) ..".\n") - os.exit(cfg.errorcodes.CONFIGFILE) - end - table.insert(lst, plat) - else - cfg.platforms[plat] = nil - end - end - -- platform overrides depent on the order, so set priorities - table.sort(lst, function(key1, key2) return platform_order[key1] < platform_order[key2] end) - util.deep_merge(cfg.platforms, lst) -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, - perm_read = "0644", - perm_exec = "0755", - - lua_modules_path = "/share/lua/"..cfg.lua_version, - lib_modules_path = "/lib/lua/"..cfg.lua_version, - rocks_subdir = site_config.LUAROCKS_ROCKS_SUBDIR or "/lib/luarocks/rocks", - - arch = "unknown", - lib_extension = "unknown", - obj_extension = "unknown", - - 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", - 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", - STATFLAG = "-c '%a'", - 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 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/" } - 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] - 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 = "SET PATH=%s" - defaults.export_path_separator = ";" - defaults.export_lua_path = "SET LUA_PATH=%s" - defaults.export_lua_cpath = "SET LUA_CPATH=%s" - 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" -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.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 - -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 = "export PATH='%s'" - defaults.export_path_separator = ":" - defaults.export_lua_path = "export LUA_PATH='%s'" - defaults.export_lua_cpath = "export LUA_CPATH='%s'" - 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" -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" -end - -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 - - -if cfg.platforms.bsd then - defaults.variables.MAKE = "gmake" - defaults.variables.STATFLAG = "-f '%OLp'" -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 - 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 -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 - -if cfg.platforms.openbsd then - defaults.arch = "openbsd-"..cfg.target_cpu -end - -if cfg.platforms.netbsd then - defaults.arch = "netbsd-"..cfg.target_cpu -end - -if cfg.platforms.solaris then - defaults.arch = "solaris-"..cfg.target_cpu - --defaults.platforms = {"unix", "solaris"} - defaults.variables.MAKE = "gmake" -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" - -if bit32 then -- Lua 5.2+ - defaults.rocks_provided["bit32"] = cfg.lua_version.."-1" -end - -if utf8 then -- Lua 5.3+ - defaults.rocks_provided["utf8"] = cfg.lua_version.."-1" -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 - --- Use defaults: - --- 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 -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 - -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" - 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 - -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) - end - for _,tree in ipairs(cfg.rocks_trees) do - add_tree_to_paths(tree) - end - if extra_luarocks_module_dir then - table.insert(new_path, extra_luarocks_module_dir) - 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.remove_path_dupes(package.path .. ";" .. lr_path, ";") - package.cpath = util.remove_path_dupes(package.cpath .. ";" .. lr_cpath, ";") -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, - } - } - ret.nearest = (ret.user.ok and ret.user.file) or ret.system.file - return ret -end - -cfg.user_agent = "LuaRocks/"..cfg.program_version.." "..cfg.arch - -cfg.http_proxy = os.getenv("http_proxy") -cfg.https_proxy = os.getenv("https_proxy") -cfg.no_proxy = os.getenv("no_proxy") - ---- 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") - - for _, platform in ipairs(cfg.platforms) do - if platform == query then - return true - end - end -end - -return cfg diff --git a/src/luarocks/command_line.lua b/src/luarocks/command_line.lua index 1a8c0fe7..936e9950 100644 --- a/src/luarocks/command_line.lua +++ b/src/luarocks/command_line.lua @@ -5,7 +5,7 @@ local command_line = {} local unpack = unpack or table.unpack local util = require("luarocks.util") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local path = require("luarocks.path") local dir = require("luarocks.dir") local deps = require("luarocks.deps") diff --git a/src/luarocks/config_cmd.lua b/src/luarocks/config_cmd.lua index 9e73d228..b68f7898 100644 --- a/src/luarocks/config_cmd.lua +++ b/src/luarocks/config_cmd.lua @@ -2,7 +2,7 @@ -- Queries information about the LuaRocks configuration. local config_cmd = {} -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local util = require("luarocks.util") local dir = require("luarocks.dir") diff --git a/src/luarocks/core/cfg.lua b/src/luarocks/core/cfg.lua new file mode 100644 index 00000000..42508b9e --- /dev/null +++ b/src/luarocks/core/cfg.lua @@ -0,0 +1,741 @@ + +--- Configuration for LuaRocks. +-- Tries to load the user's configuration file and +-- defines defaults for unset values. See the +-- config +-- file format documentation for details. +-- +-- End-users shouldn't edit this file. They can override any defaults +-- set in this file using their system-wide $LUAROCKS_SYSCONFIG file +-- (see luarocks.site_config) or their user-specific configuration file +-- (~/.luarocks/config.lua on Unix or %APPDATA%/luarocks/config.lua on +-- Windows). + +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:sub(5) +local version_suffix = cfg.lua_version:gsub("%.", "_") + +-- Load site-local global configurations +local ok, site_config = pcall(require, "luarocks.site_config_"..version_suffix) +if not ok then + ok, site_config = pcall(require, "luarocks.site_config") +end +if not ok then + io.stderr:write("Site-local luarocks/site_config.lua file not found. Incomplete installation?\n") + site_config = {} +end + +local util = require("luarocks.core.util") +local persist = require("luarocks.core.persist") +local require = nil +-------------------------------------------------------------------------------- + +cfg.program_version = "scm" +cfg.program_series = "3.0" +cfg.major_version = (cfg.program_version:match("([^.]%.[^.])")) or cfg.program_series +cfg.variables = {} +cfg.rocks_trees = {} +cfg.platforms = {} + +cfg.errorcodes = setmetatable({ + OK = 0, + UNSPECIFIED = 1, + PERMISSIONDENIED = 2, + CONFIGFILE = 3, + CRASH = 99 +},{ + __index = function(t, key) + local val = rawget(t, key) + if not val then + error("'"..tostring(key).."' is not a valid errorcode", 2) + end + return val + end +}) + + +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(cfg.errorcodes.UNSPECIFIED) +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 + +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 + +-- Set order for platform overrides +local platform_order = { + -- Unixes + unix = 1, + bsd = 2, + solaris = 3, + netbsd = 4, + openbsd = 5, + freebsd = 6, + linux = 7, + macosx = 8, + cygwin = 9, + msys = 10, + haiku = 11, + -- Windows + win32 = 12, + mingw32 = 13, + windows = 14 } + +-- 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 + +-- 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 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) +end + +-- load config file from a list until first succesful one. Info is +-- added to `cfg` module table, returns filepath of succesfully loaded +-- file or nil if it failed +local load_config_file = function(list) + for _, filepath in ipairs(list) do + 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(cfg.errorcodes.CONFIGFILE) + end + if result then + -- succes in loading and running, merge contents and exit + merge_overrides(result) + return filepath + end + end + return nil -- nothing was loaded +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_dir.."/config.lua", + }) + sys_config_ok = (sys_config_file ~= nil) +end + +-- 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 + local list = { config_env_value } + home_config_file = load_config_file(list) + 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") + end + end + + -- try the alternative defaults if there was no environment specified file or it didn't work + if not home_config_ok then + local list = { + home_config_file_default, + home_config_dir.."/config.lua", + } + home_config_file = load_config_file(list) + home_config_ok = (home_config_file ~= nil) + end +end + + +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 site_config.LUAROCKS_ROCKS_TREE then + table.insert(cfg.rocks_trees, { name = "system", root = site_config.LUAROCKS_ROCKS_TREE } ) + end +end + + +-- update platforms list; keyed -> array +do + local lst = {} -- use temp array to not confuse `pairs` in loop + for plat in pairs(cfg.platforms) do + if cfg.platforms[plat] then -- entries set to 'false' skipped + if not platform_order[plat] then + local pl = "" + for k,_ in pairs(platform_order) do pl = pl .. ", " .. k end + io.stderr:write("Bad platform given; "..tostring(plat)..". Valid entries are: "..pl:sub(3,-1) ..".\n") + os.exit(cfg.errorcodes.CONFIGFILE) + end + table.insert(lst, plat) + else + cfg.platforms[plat] = nil + end + end + -- platform overrides depent on the order, so set priorities + table.sort(lst, function(key1, key2) return platform_order[key1] < platform_order[key2] end) + util.deep_merge(cfg.platforms, lst) +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, + perm_read = "0644", + perm_exec = "0755", + + lua_modules_path = "/share/lua/"..cfg.lua_version, + lib_modules_path = "/lib/lua/"..cfg.lua_version, + rocks_subdir = site_config.LUAROCKS_ROCKS_SUBDIR or "/lib/luarocks/rocks", + + arch = "unknown", + lib_extension = "unknown", + obj_extension = "unknown", + + 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", + 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", + STATFLAG = "-c '%a'", + 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 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/" } + 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] + 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 = "SET PATH=%s" + defaults.export_path_separator = ";" + defaults.export_lua_path = "SET LUA_PATH=%s" + defaults.export_lua_cpath = "SET LUA_CPATH=%s" + 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" +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.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 + +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 = "export PATH='%s'" + defaults.export_path_separator = ":" + defaults.export_lua_path = "export LUA_PATH='%s'" + defaults.export_lua_cpath = "export LUA_CPATH='%s'" + 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" +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" +end + +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 + + +if cfg.platforms.bsd then + defaults.variables.MAKE = "gmake" + defaults.variables.STATFLAG = "-f '%OLp'" +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 + 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 +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 + +if cfg.platforms.openbsd then + defaults.arch = "openbsd-"..cfg.target_cpu +end + +if cfg.platforms.netbsd then + defaults.arch = "netbsd-"..cfg.target_cpu +end + +if cfg.platforms.solaris then + defaults.arch = "solaris-"..cfg.target_cpu + --defaults.platforms = {"unix", "solaris"} + defaults.variables.MAKE = "gmake" +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" + +if bit32 then -- Lua 5.2+ + defaults.rocks_provided["bit32"] = cfg.lua_version.."-1" +end + +if utf8 then -- Lua 5.3+ + defaults.rocks_provided["utf8"] = cfg.lua_version.."-1" +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 + +-- Use defaults: + +-- 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 +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 + +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" + 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 + +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) + end + for _,tree in ipairs(cfg.rocks_trees) do + add_tree_to_paths(tree) + end + if extra_luarocks_module_dir then + table.insert(new_path, extra_luarocks_module_dir) + 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.remove_path_dupes(package.path .. ";" .. lr_path, ";") + package.cpath = util.remove_path_dupes(package.cpath .. ";" .. lr_cpath, ";") +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, + } + } + ret.nearest = (ret.user.ok and ret.user.file) or ret.system.file + return ret +end + +cfg.user_agent = "LuaRocks/"..cfg.program_version.." "..cfg.arch + +cfg.http_proxy = os.getenv("http_proxy") +cfg.https_proxy = os.getenv("https_proxy") +cfg.no_proxy = os.getenv("no_proxy") + +--- 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") + + for _, platform in ipairs(cfg.platforms) do + if platform == query then + return true + end + end +end + +return cfg diff --git a/src/luarocks/core/deps.lua b/src/luarocks/core/deps.lua new file mode 100644 index 00000000..6d539eb8 --- /dev/null +++ b/src/luarocks/core/deps.lua @@ -0,0 +1,192 @@ + +local deps = {} + +local util = require("luarocks.core.util") +local require = nil +-------------------------------------------------------------------------------- + +local deltas = { + scm = 1100, + cvs = 1000, + rc = -1000, + pre = -10000, + beta = -100000, + alpha = -1000000 +} + +local version_mt = { + --- Equality comparison for versions. + -- All version numbers must be equal. + -- If both versions have revision numbers, they must be equal; + -- otherwise the revision number is ignored. + -- @param v1 table: version table to compare. + -- @param v2 table: version table to compare. + -- @return boolean: true if they are considered equivalent. + __eq = function(v1, v2) + if #v1 ~= #v2 then + return false + end + for i = 1, #v1 do + if v1[i] ~= v2[i] then + return false + end + end + if v1.revision and v2.revision then + return (v1.revision == v2.revision) + end + return true + end, + --- Size comparison for versions. + -- All version numbers are compared. + -- If both versions have revision numbers, they are compared; + -- otherwise the revision number is ignored. + -- @param v1 table: version table to compare. + -- @param v2 table: version table to compare. + -- @return boolean: true if v1 is considered lower than v2. + __lt = function(v1, v2) + for i = 1, math.max(#v1, #v2) do + local v1i, v2i = v1[i] or 0, v2[i] or 0 + if v1i ~= v2i then + return (v1i < v2i) + end + end + if v1.revision and v2.revision then + return (v1.revision < v2.revision) + end + return false + end +} + +local version_cache = {} +setmetatable(version_cache, { + __mode = "kv" +}) + +--- Parse a version string, converting to table format. +-- A version table contains all components of the version string +-- converted to numeric format, stored in the array part of the table. +-- If the version contains a revision, it is stored numerically +-- in the 'revision' field. The original string representation of +-- the string is preserved in the 'string' field. +-- Returned version tables use a metatable +-- allowing later comparison through relational operators. +-- @param vstring string: A version number in string format. +-- @return table or nil: A version table or nil +-- if the input string contains invalid characters. +function deps.parse_version(vstring) + if not vstring then return nil end + assert(type(vstring) == "string") + + local cached = version_cache[vstring] + if cached then + return cached + end + + local version = {} + local i = 1 + + local function add_token(number) + version[i] = version[i] and version[i] + number/100000 or number + i = i + 1 + end + + -- trim leading and trailing spaces + vstring = vstring:match("^%s*(.*)%s*$") + version.string = vstring + -- store revision separately if any + local main, revision = vstring:match("(.*)%-(%d+)$") + if revision then + vstring = main + version.revision = tonumber(revision) + end + while #vstring > 0 do + -- extract a number + local token, rest = vstring:match("^(%d+)[%.%-%_]*(.*)") + if token then + add_token(tonumber(token)) + else + -- extract a word + token, rest = vstring:match("^(%a+)[%.%-%_]*(.*)") + if not token then + util.printerr("Warning: version number '"..vstring.."' could not be parsed.") + version[i] = 0 + break + end + version[i] = deltas[token] or (token:byte() / 1000) + end + vstring = rest + end + setmetatable(version, version_mt) + version_cache[vstring] = version + return version +end + +--- Utility function to compare version numbers given as strings. +-- @param a string: one version. +-- @param b string: another version. +-- @return boolean: True if a > b. +function deps.compare_versions(a, b) + return deps.parse_version(a) > deps.parse_version(b) +end + +--- A more lenient check for equivalence between versions. +-- This returns true if the requested components of a version +-- match and ignore the ones that were not given. For example, +-- when requesting "2", then "2", "2.1", "2.3.5-9"... all match. +-- When requesting "2.1", then "2.1", "2.1.3" match, but "2.2" +-- doesn't. +-- @param version string or table: Version to be tested; may be +-- in string format or already parsed into a table. +-- @param requested string or table: Version requested; may be +-- in string format or already parsed into a table. +-- @return boolean: True if the tested version matches the requested +-- version, false otherwise. +local function partial_match(version, requested) + assert(type(version) == "string" or type(version) == "table") + assert(type(requested) == "string" or type(version) == "table") + + if type(version) ~= "table" then version = deps.parse_version(version) end + if type(requested) ~= "table" then requested = deps.parse_version(requested) end + if not version or not requested then return false end + + for i, ri in ipairs(requested) do + local vi = version[i] or 0 + if ri ~= vi then return false end + end + if requested.revision then + return requested.revision == version.revision + end + return true +end + +--- Check if a version satisfies a set of constraints. +-- @param version table: A version in table format +-- @param constraints table: An array of constraints in table format. +-- @return boolean: True if version satisfies all constraints, +-- false otherwise. +function deps.match_constraints(version, constraints) + assert(type(version) == "table") + assert(type(constraints) == "table") + local ok = true + setmetatable(version, version_mt) + for _, constr in pairs(constraints) do + if type(constr.version) == "string" then + constr.version = deps.parse_version(constr.version) + end + local constr_version, constr_op = constr.version, constr.op + setmetatable(constr_version, version_mt) + if constr_op == "==" then ok = version == constr_version + elseif constr_op == "~=" then ok = version ~= constr_version + elseif constr_op == ">" then ok = version > constr_version + elseif constr_op == "<" then ok = version < constr_version + elseif constr_op == ">=" then ok = version >= constr_version + elseif constr_op == "<=" then ok = version <= constr_version + elseif constr_op == "~>" then ok = partial_match(version, constr_version) + end + if not ok then break end + end + return ok +end + +return deps + diff --git a/src/luarocks/core/dir.lua b/src/luarocks/core/dir.lua new file mode 100644 index 00000000..240bb38a --- /dev/null +++ b/src/luarocks/core/dir.lua @@ -0,0 +1,55 @@ + +local dir = {} + +local require = nil +-------------------------------------------------------------------------------- + +dir.separator = "/" + +--- Describe a path in a cross-platform way. +-- Use this function to avoid platform-specific directory +-- separators in other modules. Removes trailing slashes from +-- each component given, to avoid repeated separators. +-- Separators inside strings are kept, to handle URLs containing +-- protocols. +-- @param ... strings representing directories +-- @return string: a string with a platform-specific representation +-- of the path. +function dir.path(...) + local t = {...} + while t[1] == "" do + table.remove(t, 1) + end + return (table.concat(t, "/"):gsub("([^:])/+", "%1/"):gsub("^/+", "/"):gsub("/*$", "")) +end + +--- Split protocol and path from an URL or local pathname. +-- URLs should be in the "protocol://path" format. +-- For local pathnames, "file" is returned as the protocol. +-- @param url string: an URL or a local pathname. +-- @return string, string: the protocol, and the pathname without the protocol. +function dir.split_url(url) + assert(type(url) == "string") + + local protocol, pathname = url:match("^([^:]*)://(.*)") + if not protocol then + protocol = "file" + pathname = url + end + return protocol, pathname +end + +--- Normalize a url or local path. +-- URLs should be in the "protocol://path" format. System independent +-- forward slashes are used, removing trailing and double slashes +-- @param url string: an URL or a local pathname. +-- @return string: Normalized result. +function dir.normalize(name) + local protocol, pathname = dir.split_url(name) + pathname = pathname:gsub("\\", "/"):gsub("(.)/*$", "%1"):gsub("//", "/") + if protocol ~= "file" then pathname = protocol .."://"..pathname end + return pathname +end + +return dir + diff --git a/src/luarocks/core/manif.lua b/src/luarocks/core/manif.lua new file mode 100644 index 00000000..f0912bfd --- /dev/null +++ b/src/luarocks/core/manif.lua @@ -0,0 +1,107 @@ + +--- Core functions for querying manifest files. +local manif = {} + +local persist = require("luarocks.core.persist") +local type_check = require("luarocks.core.type_check") +local cfg = require("luarocks.core.cfg") +local dir = require("luarocks.core.dir") +local util = require("luarocks.core.util") +local path = require("luarocks.core.path") +local require = nil +-------------------------------------------------------------------------------- + +-- Table with repository identifiers as keys and tables mapping +-- Lua versions to cached loaded manifests as values. +local manifest_cache = {} + +--- Cache a loaded manifest. +-- @param repo_url string: The repository identifier. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @param manifest table: the manifest to be cached. +function manif.cache_manifest(repo_url, lua_version, manifest) + lua_version = lua_version or cfg.lua_version + manifest_cache[repo_url] = manifest_cache[repo_url] or {} + manifest_cache[repo_url][lua_version] = manifest +end + +--- Attempt to get cached loaded manifest. +-- @param repo_url string: The repository identifier. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @return table or nil: loaded manifest or nil if cache is empty. +function manif.get_cached_manifest(repo_url, lua_version) + lua_version = lua_version or cfg.lua_version + return manifest_cache[repo_url] and manifest_cache[repo_url][lua_version] +end + +--- Back-end function that actually loads the manifest +-- and stores it in the manifest cache. +-- @param file string: The local filename of the manifest file. +-- @param repo_url string: The repository identifier. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @param quick boolean: If given, skips type checking. +-- @return table or (nil, string, string): the manifest or nil, +-- error message and error code ("open", "load", "run" or "type"). +function manif.manifest_loader(file, repo_url, lua_version, quick) + local manifest, err, errcode = persist.load_into_table(file) + if not manifest then + return nil, "Failed loading manifest for "..repo_url..": "..err, errcode + end + local globals = err + if not quick then + local ok, err = type_check.type_check_manifest(manifest, globals) + if not ok then + return nil, "Error checking manifest: "..err, "type" + end + end + + manif.cache_manifest(repo_url, lua_version, manifest) + return manifest +end + +--- Load a local manifest describing a repository. +-- All functions that use manifest tables assume they were obtained +-- through either this function or load_manifest. +-- @param repo_url string: URL or pathname for the repository. +-- @return table or (nil, string, string): A table representing the manifest, +-- or nil followed by an error message and an error code, see manifest_loader. +function manif.load_local_manifest(repo_url) + assert(type(repo_url) == "string") + + local cached_manifest = manif.get_cached_manifest(repo_url) + if cached_manifest then + return cached_manifest + end + + local pathname = dir.path(repo_url, "manifest") + return manif.manifest_loader(pathname, repo_url, nil, true) +end + +--- Get all versions of a package listed in a manifest file. +-- @param name string: a package name. +-- @param deps_mode string: "one", to use only the currently +-- configured tree; "order" to select trees based on order +-- (use the current tree and all trees below it on the list) +-- or "all", to use all trees. +-- @return table: An array of strings listing installed +-- versions of a package. +function manif.get_versions(name, deps_mode) + assert(type(name) == "string") + assert(type(deps_mode) == "string") + + local manifest = {} + path.map_trees(deps_mode, function(tree) + local loaded = manif.load_local_manifest(path.rocks_dir(tree)) + if loaded then + util.deep_merge(manifest, loaded) + end + end) + + local item = next(manifest) and manifest.repository[name] + if item then + return util.keys(item) + end + return {} +end + +return manif diff --git a/src/luarocks/core/path.lua b/src/luarocks/core/path.lua new file mode 100644 index 00000000..ffde2c68 --- /dev/null +++ b/src/luarocks/core/path.lua @@ -0,0 +1,149 @@ + +--- Core LuaRocks-specific path handling functions. +local path = {} + +local cfg = require("luarocks.core.cfg") +local dir = require("luarocks.core.dir") +local require = nil +-------------------------------------------------------------------------------- + +function path.rocks_dir(tree) + if type(tree) == "string" then + return dir.path(tree, cfg.rocks_subdir) + else + assert(type(tree) == "table") + return tree.rocks_dir or dir.path(tree.root, cfg.rocks_subdir) + end +end + +--- Produce a versioned version of a filename. +-- @param file string: filename (must start with prefix) +-- @param prefix string: Path prefix for file +-- @param name string: Rock name +-- @param version string: Rock version +-- @return string: a pathname with the same directory parts and a versioned basename. +function path.versioned_name(file, prefix, name, version) + assert(type(file) == "string") + assert(type(name) == "string") + assert(type(version) == "string") + + local rest = file:sub(#prefix+1):gsub("^/*", "") + local name_version = (name.."_"..version):gsub("%-", "_"):gsub("%.", "_") + return dir.path(prefix, name_version.."-"..rest) +end + +--- Convert a pathname to a module identifier. +-- In Unix, for example, a path "foo/bar/baz.lua" is converted to +-- "foo.bar.baz"; "bla/init.lua" returns "bla"; "foo.so" returns "foo". +-- @param file string: Pathname of module +-- @return string: The module identifier, or nil if given path is +-- not a conformant module path (the function does not check if the +-- path actually exists). +function path.path_to_module(file) + assert(type(file) == "string") + + local name = file:match("(.*)%."..cfg.lua_extension.."$") + if name then + name = name:gsub(dir.separator, ".") + local init = name:match("(.*)%.init$") + if init then + name = init + end + else + name = file:match("(.*)%."..cfg.lib_extension.."$") + if name then + name = name:gsub(dir.separator, ".") + else + name = file:match("(.*)%."..cfg.static_lib_extension.."$") + if name then + name = name:gsub(dir.separator, ".") + end + end + end + if not name then name = file end + name = name:gsub("^%.+", ""):gsub("%.+$", "") + return name +end + +function path.deploy_lua_dir(tree) + if type(tree) == "string" then + return dir.path(tree, cfg.lua_modules_path) + else + assert(type(tree) == "table") + return tree.lua_dir or dir.path(tree.root, cfg.lua_modules_path) + end +end + +function path.deploy_lib_dir(tree) + if type(tree) == "string" then + return dir.path(tree, cfg.lib_modules_path) + else + assert(type(tree) == "table") + return tree.lib_dir or dir.path(tree.root, cfg.lib_modules_path) + end +end + +local is_src_extension = { [".lua"] = true, [".tl"] = true, [".tld"] = true, [".moon"] = true } + +--- Return the pathname of the file that would be loaded for a module, indexed. +-- @param file_name string: module file name as in manifest (eg. "socket/core.so") +-- @param name string: name of the package (eg. "luasocket") +-- @param version string: version number (eg. "2.0.2-1") +-- @param tree string: repository path (eg. "/usr/local") +-- @param i number: the index, 1 if version is the current default, > 1 otherwise. +-- This is done this way for use by select_module in luarocks.loader. +-- @return string: filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so") +function path.which_i(file_name, name, version, tree, i) + local deploy_dir + local extension = file_name:match("%.[a-z]+$") + if is_src_extension[extension] then + deploy_dir = path.deploy_lua_dir(tree) + file_name = dir.path(deploy_dir, file_name) + else + deploy_dir = path.deploy_lib_dir(tree) + file_name = dir.path(deploy_dir, file_name) + end + if i > 1 then + file_name = path.versioned_name(file_name, deploy_dir, name, version) + end + return file_name +end + +function path.rocks_tree_to_string(tree) + if type(tree) == "string" then + return tree + else + assert(type(tree) == "table") + return tree.root + end +end + +--- Apply a given function to the active rocks trees based on chosen dependency mode. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for no trees (this function becomes a nop). +-- @param fn function: function to be applied, with the tree dir (string) as the first +-- argument and the remaining varargs of map_trees as the following arguments. +-- @return a table with all results of invocations of fn collected. +function path.map_trees(deps_mode, fn, ...) + local result = {} + if deps_mode == "one" then + table.insert(result, (fn(cfg.root_dir, ...)) or 0) + elseif deps_mode == "all" or deps_mode == "order" then + local use = false + if deps_mode == "all" then + use = true + end + for _, tree in ipairs(cfg.rocks_trees) do + if dir.normalize(path.rocks_tree_to_string(tree)) == dir.normalize(path.rocks_tree_to_string(cfg.root_dir)) then + use = true + end + if use then + table.insert(result, (fn(tree, ...)) or 0) + end + end + end + return result +end + +return path diff --git a/src/luarocks/core/persist.lua b/src/luarocks/core/persist.lua new file mode 100644 index 00000000..48979184 --- /dev/null +++ b/src/luarocks/core/persist.lua @@ -0,0 +1,81 @@ + +local persist = {} + +local require = nil +-------------------------------------------------------------------------------- + +--- Load and run a Lua file in an environment. +-- @param filename string: the name of the file. +-- @param env table: the environment table. +-- @return (true, any) or (nil, string, string): true and the return value +-- of the file, or nil, an error message and an error code ("open", "load" +-- or "run") in case of errors. +local function run_file(filename, env) + local fd, err = io.open(filename) + if not fd then + return nil, err, "open" + end + local str, err = fd:read("*a") + fd:close() + if not str then + return nil, err, "open" + end + str = str:gsub("^#![^\n]*\n", "") + local chunk, ran + if _VERSION == "Lua 5.1" then -- Lua 5.1 + chunk, err = loadstring(str, filename) + if chunk then + setfenv(chunk, env) + ran, err = pcall(chunk) + end + else -- Lua 5.2 + chunk, err = load(str, filename, "t", env) + if chunk then + ran, err = pcall(chunk) + end + end + if not chunk then + return nil, "Error loading file: "..err, "load" + end + if not ran then + return nil, "Error running file: "..err, "run" + end + return true, err +end + +--- Load a Lua file containing assignments, storing them in a table. +-- The global environment is not propagated to the loaded file. +-- @param filename string: the name of the file. +-- @param tbl table or nil: if given, this table is used to store +-- loaded values. +-- @return (table, table) or (nil, string, string): a table with the file's assignments +-- as fields and set of undefined globals accessed in file, +-- or nil, an error message and an error code ("open"; couldn't open the file, +-- "load"; compile-time error, or "run"; run-time error) +-- in case of errors. +function persist.load_into_table(filename, tbl) + assert(type(filename) == "string") + assert(type(tbl) == "table" or not tbl) + + local result = tbl or {} + local globals = {} + local globals_mt = { + __index = function(t, k) + globals[k] = true + end + } + local save_mt = getmetatable(result) + setmetatable(result, globals_mt) + + local ok, err, errcode = run_file(filename, result) + + setmetatable(result, save_mt) + + if not ok then + return nil, err, errcode + end + return result, globals +end + +return persist + diff --git a/src/luarocks/core/type_check.lua b/src/luarocks/core/type_check.lua new file mode 100644 index 00000000..a2e14efd --- /dev/null +++ b/src/luarocks/core/type_check.lua @@ -0,0 +1,226 @@ + +local type_check = {} + +local cfg = require("luarocks.core.cfg") +local deps = require("luarocks.core.deps") +local require = nil +-------------------------------------------------------------------------------- + +type_check.string_1 = { _type = "string" } +type_check.number_1 = { _type = "number" } +type_check.mandatory_string_1 = { _type = "string", _mandatory = true } + +local number_1 = type_check.number_1 +local string_1 = type_check.string_1 +local mandatory_string_1 = type_check.mandatory_string_1 + +local manifest_types = { + repository = { + _mandatory = true, + -- packages + _any = { + -- versions + _any = { + -- items + _any = { + arch = mandatory_string_1, + modules = { _any = string_1 }, + commands = { _any = string_1 }, + dependencies = { _any = string_1 }, + -- TODO: to be extended with more metadata. + } + } + } + }, + modules = { + _mandatory = true, + -- modules + _any = { + -- providers + _any = string_1 + } + }, + commands = { + _mandatory = true, + -- modules + _any = { + -- commands + _any = string_1 + } + }, + dependencies = { + -- each module + _any = { + -- each version + _any = { + -- each dependency + _any = { + name = string_1, + constraints = { + _any = { + no_upgrade = { _type = "boolean" }, + op = string_1, + version = { + string = string_1, + _any = number_1, + } + } + } + } + } + } + } +} + +local function check_version(version, typetbl, context) + local typetbl_version = typetbl._version or "1.0" + if deps.compare_versions(typetbl_version, version) then + if context == "" then + return nil, "Invalid rockspec_format version number in rockspec? Please fix rockspec accordingly." + else + return nil, context.." is not supported in rockspec format "..version.." (requires version "..typetbl_version.."), please fix the rockspec_format field accordingly." + end + end + return true +end + +--- Type check an object. +-- The object is compared against an archetypical value +-- matching the expected type -- the actual values don't matter, +-- only their types. Tables are type checked recursively. +-- @param version string: The version of the item. +-- @param item any: The object being checked. +-- @param typetbl any: The type-checking table for the object. +-- @param context string: A string indicating the "context" where the +-- error occurred (the full table path), for error messages. +-- @return boolean or (nil, string): true if type checking +-- succeeded, or nil and an error message if it failed. +-- @see type_check_table +local function type_check_item(version, item, typetbl, context) + assert(type(version) == "string") + + local ok, err = check_version(version, typetbl, context) + if not ok then + return nil, err + end + + local item_type = type(item) or "nil" + local expected_type = typetbl._type or "table" + + if expected_type == "number" then + if not tonumber(item) then + return nil, "Type mismatch on field "..context..": expected a number" + end + elseif expected_type == "string" then + if item_type ~= "string" then + return nil, "Type mismatch on field "..context..": expected a string, got "..item_type + end + if typetbl._pattern then + if not item:match("^"..typetbl._pattern.."$") then + return nil, "Type mismatch on field "..context..": invalid value "..item.." does not match '"..typetbl._pattern.."'" + end + end + elseif expected_type == "table" then + if item_type ~= expected_type then + return nil, "Type mismatch on field "..context..": expected a table" + else + return type_check.type_check_table(version, item, typetbl, context) + end + elseif item_type ~= expected_type then + return nil, "Type mismatch on field "..context..": expected "..expected_type + end + return true +end + +local function mkfield(context, field) + if context == "" then + return field + end + return context.."."..field +end + +--- Type check the contents of a table. +-- The table's contents are compared against a reference table, +-- which contains the recognized fields, with archetypical values +-- matching the expected types -- the actual values of items in the +-- reference table don't matter, only their types (ie, for field x +-- in tbl that is correctly typed, type(tbl.x) == type(types.x)). +-- If the reference table contains a field called MORE, then +-- unknown fields in the checked table are accepted. +-- If it contains a field called ANY, then its type will be +-- used to check any unknown fields. If a field is prefixed +-- with MUST_, it is mandatory; its absence from the table is +-- a type error. +-- Tables are type checked recursively. +-- @param version string: The version of tbl. +-- @param tbl table: The table to be type checked. +-- @param typetbl table: The type-checking table, containing +-- values for recognized fields in the checked table. +-- @param context string: A string indicating the "context" where the +-- error occurred (such as the name of the table the item is a part of), +-- to be used by error messages. +-- @return boolean or (nil, string): true if type checking +-- succeeded, or nil and an error message if it failed. +function type_check.type_check_table(version, tbl, typetbl, context) + assert(type(version) == "string") + assert(type(tbl) == "table") + assert(type(typetbl) == "table") + + local ok, err = check_version(version, typetbl, context) + if not ok then + return nil, err + end + + for k, v in pairs(tbl) do + local t = typetbl[k] or typetbl._any + if t then + local ok, err = type_check_item(version, v, t, mkfield(context, k)) + if not ok then return nil, err end + elseif typetbl._more then + -- Accept unknown field + else + if not cfg.accept_unknown_fields then + return nil, "Unknown field "..k + end + end + end + for k, v in pairs(typetbl) do + if k:sub(1,1) ~= "_" and v._mandatory then + if not tbl[k] then + return nil, "Mandatory field "..mkfield(context, k).." is missing." + end + end + end + return true +end + +function type_check.check_undeclared_globals(globals, typetbl) + local undeclared = {} + for glob, _ in pairs(globals) do + if not (typetbl[glob] or typetbl["MUST_"..glob]) then + table.insert(undeclared, glob) + end + end + if #undeclared == 1 then + return nil, "Unknown variable: "..undeclared[1] + elseif #undeclared > 1 then + return nil, "Unknown variables: "..table.concat(undeclared, ", ") + end + return true +end + +--- Type check a manifest table. +-- Verify the correctness of elements from a +-- manifest table, reporting on unknown fields and type +-- mismatches. +-- @return boolean or (nil, string): true if type checking +-- succeeded, or nil and an error message if it failed. +function type_check.type_check_manifest(manifest, globals) + assert(type(manifest) == "table") + local ok, err = type_check.check_undeclared_globals(globals, manifest_types) + if not ok then return nil, err end + return type_check.type_check_table("1.0", manifest, manifest_types, "") +end + +return type_check + diff --git a/src/luarocks/core/util.lua b/src/luarocks/core/util.lua new file mode 100644 index 00000000..4f896b17 --- /dev/null +++ b/src/luarocks/core/util.lua @@ -0,0 +1,202 @@ + +local util = {} + +local require = nil +-------------------------------------------------------------------------------- + +--- Run a process and read a its output. +-- Equivalent to io.popen(cmd):read("*l"), except that it +-- closes the fd right away. +-- @param cmd string: The command to execute +-- @param spec string: "*l" by default, to read a single line. +-- May be used to read more, passing, for instance, "*a". +-- @return string: the output of the program. +function util.popen_read(cmd, spec) + local fd = io.popen(cmd) + local out = fd:read(spec or "*l") + fd:close() + return out +end + +--- Create a new shallow copy of a table: a new table with +-- the same keys and values. Keys point to the same objects as +-- the original table (ie, does not copy recursively). +-- @param tbl table: the input table +-- @return table: a new table with the same contents. +function util.make_shallow_copy(tbl) + local copy = {} + for k,v in pairs(tbl) do + copy[k] = v + end + return copy +end + +--- +-- Formats tables with cycles recursively to any depth. +-- References to other tables are shown as values. +-- Self references are indicated. +-- The string returned is "Lua code", which can be procesed +-- (in the case in which indent is composed by spaces or "--"). +-- Userdata and function keys and values are shown as strings, +-- which logically are exactly not equivalent to the original code. +-- This routine can serve for pretty formating tables with +-- proper indentations, apart from printing them: +-- io.write(table.show(t, "t")) -- a typical use +-- Written by Julio Manuel Fernandez-Diaz, +-- Heavily based on "Saving tables with cycles", PIL2, p. 113. +-- @param t table: is the table. +-- @param name string: is the name of the table (optional) +-- @param indent string: is a first indentation (optional). +-- @return string: the pretty-printed table +function util.show_table(t, name, indent) + local cart -- a container + local autoref -- for self references + + local function is_empty_table(t) return next(t) == nil end + + local function basic_serialize (o) + local so = tostring(o) + if type(o) == "function" then + local info = debug.getinfo(o, "S") + -- info.name is nil because o is not a calling level + if info.what == "C" then + return ("%q"):format(so .. ", C function") + else + -- the information is defined through lines + return ("%q"):format(so .. ", defined in (" .. info.linedefined .. "-" .. info.lastlinedefined .. ")" .. info.source) + end + elseif type(o) == "number" then + return so + else + return ("%q"):format(so) + end + end + + local function add_to_cart (value, name, indent, saved, field) + indent = indent or "" + saved = saved or {} + field = field or name + + cart = cart .. indent .. field + + if type(value) ~= "table" then + cart = cart .. " = " .. basic_serialize(value) .. ";\n" + else + if saved[value] then + cart = cart .. " = {}; -- " .. saved[value] .. " (self reference)\n" + autoref = autoref .. name .. " = " .. saved[value] .. ";\n" + else + saved[value] = name + --if tablecount(value) == 0 then + if is_empty_table(value) then + cart = cart .. " = {};\n" + else + cart = cart .. " = {\n" + for k, v in pairs(value) do + k = basic_serialize(k) + local fname = ("%s[%s]"):format(name, k) + field = ("[%s]"):format(k) + -- three spaces between levels + add_to_cart(v, fname, indent .. " ", saved, field) + end + cart = cart .. indent .. "};\n" + end + end + end + end + + name = name or "__unnamed__" + if type(t) ~= "table" then + return name .. " = " .. basic_serialize(t) + end + cart, autoref = "", "" + add_to_cart(t, name, indent) + return cart .. autoref +end + +--- Merges contents of src on top of dst's contents. +-- @param dst Destination table, which will receive src's contents. +-- @param src Table which provides new contents to dst. +-- @see platform_overrides +function util.deep_merge(dst, src) + for k, v in pairs(src) do + if type(v) == "table" then + if not dst[k] then + dst[k] = {} + end + if type(dst[k]) == "table" then + util.deep_merge(dst[k], v) + else + dst[k] = v + end + else + dst[k] = v + end + end +end + +--- Remove repeated entries from a path-style string. +-- Example: given ("a;b;c;a;b;d", ";"), returns "a;b;c;d". +-- @param list string: A path string (from $PATH or package.path) +-- @param sep string: The separator +function util.remove_path_dupes(list, sep) + assert(type(list) == "string") + assert(type(sep) == "string") + local parts = util.split_string(list, sep) + local final, entries = {}, {} + for _, part in ipairs(parts) do + part = part:gsub("//", "/") + if not entries[part] then + table.insert(final, part) + entries[part] = true + end + end + return table.concat(final, sep) +end + +-- from http://lua-users.org/wiki/SplitJoin +-- by Philippe Lhoste +function util.split_string(str, delim, maxNb) + -- Eliminate bad cases... + if string.find(str, delim) == nil then + return { str } + end + if maxNb == nil or maxNb < 1 then + maxNb = 0 -- No limit + end + local result = {} + local pat = "(.-)" .. delim .. "()" + local nb = 0 + local lastPos + for part, pos in string.gmatch(str, pat) do + nb = nb + 1 + result[nb] = part + lastPos = pos + if nb == maxNb then break end + end + -- Handle the last field + if nb ~= maxNb then + result[nb + 1] = string.sub(str, lastPos) + end + return result +end + +--- Return an array of keys of a table. +-- @param tbl table: The input table. +-- @return table: The array of keys. +function util.keys(tbl) + local ks = {} + for k,_ in pairs(tbl) do + table.insert(ks, k) + end + return ks +end + +--- Print a line to standard error +function util.printerr(...) + io.stderr:write(table.concat({...},"\t")) + io.stderr:write("\n") +end + +return util + diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua index 0d85d33e..acbf1dd6 100644 --- a/src/luarocks/deps.lua +++ b/src/luarocks/deps.lua @@ -12,14 +12,23 @@ -- test/test_deps.lua file included with LuaRocks provides some -- insights on what these criteria are. local deps = {} -package.loaded["luarocks.deps"] = deps +setmetatable(deps, { __index = require("luarocks.core.deps") }) -local cfg = require("luarocks.cfg") -local manif_core = require("luarocks.manif_core") +local cfg = require("luarocks.core.cfg") +local manif = require("luarocks.core.manif") local path = require("luarocks.path") local dir = require("luarocks.dir") local util = require("luarocks.util") +--- Check if rockspec format version satisfies version requirement. +-- @param rockspec table: The rockspec table. +-- @param version string: required version. +-- @return boolean: true if rockspec format matches version or is newer, false otherwise. +function deps.format_is_at_least(rockspec, version) + local rockspec_format = rockspec.rockspec_format or "1.0" + return deps.parse_version(rockspec_format) >= deps.parse_version(version) +end + local operators = { ["=="] = "==", ["~="] = "~=", @@ -34,139 +43,6 @@ local operators = { ["!="] = "~=" } -local deltas = { - scm = 1100, - cvs = 1000, - rc = -1000, - pre = -10000, - beta = -100000, - alpha = -1000000 -} - -local version_mt = { - --- Equality comparison for versions. - -- All version numbers must be equal. - -- If both versions have revision numbers, they must be equal; - -- otherwise the revision number is ignored. - -- @param v1 table: version table to compare. - -- @param v2 table: version table to compare. - -- @return boolean: true if they are considered equivalent. - __eq = function(v1, v2) - if #v1 ~= #v2 then - return false - end - for i = 1, #v1 do - if v1[i] ~= v2[i] then - return false - end - end - if v1.revision and v2.revision then - return (v1.revision == v2.revision) - end - return true - end, - --- Size comparison for versions. - -- All version numbers are compared. - -- If both versions have revision numbers, they are compared; - -- otherwise the revision number is ignored. - -- @param v1 table: version table to compare. - -- @param v2 table: version table to compare. - -- @return boolean: true if v1 is considered lower than v2. - __lt = function(v1, v2) - for i = 1, math.max(#v1, #v2) do - local v1i, v2i = v1[i] or 0, v2[i] or 0 - if v1i ~= v2i then - return (v1i < v2i) - end - end - if v1.revision and v2.revision then - return (v1.revision < v2.revision) - end - return false - end -} - -local version_cache = {} -setmetatable(version_cache, { - __mode = "kv" -}) - ---- Parse a version string, converting to table format. --- A version table contains all components of the version string --- converted to numeric format, stored in the array part of the table. --- If the version contains a revision, it is stored numerically --- in the 'revision' field. The original string representation of --- the string is preserved in the 'string' field. --- Returned version tables use a metatable --- allowing later comparison through relational operators. --- @param vstring string: A version number in string format. --- @return table or nil: A version table or nil --- if the input string contains invalid characters. -function deps.parse_version(vstring) - if not vstring then return nil end - assert(type(vstring) == "string") - - local cached = version_cache[vstring] - if cached then - return cached - end - - local version = {} - local i = 1 - - local function add_token(number) - version[i] = version[i] and version[i] + number/100000 or number - i = i + 1 - end - - -- trim leading and trailing spaces - vstring = vstring:match("^%s*(.*)%s*$") - version.string = vstring - -- store revision separately if any - local main, revision = vstring:match("(.*)%-(%d+)$") - if revision then - vstring = main - version.revision = tonumber(revision) - end - while #vstring > 0 do - -- extract a number - local token, rest = vstring:match("^(%d+)[%.%-%_]*(.*)") - if token then - add_token(tonumber(token)) - else - -- extract a word - token, rest = vstring:match("^(%a+)[%.%-%_]*(.*)") - if not token then - util.printerr("Warning: version number '"..vstring.."' could not be parsed.") - version[i] = 0 - break - end - version[i] = deltas[token] or (token:byte() / 1000) - end - vstring = rest - end - setmetatable(version, version_mt) - version_cache[vstring] = version - return version -end - ---- Utility function to compare version numbers given as strings. --- @param a string: one version. --- @param b string: another version. --- @return boolean: True if a > b. -function deps.compare_versions(a, b) - return deps.parse_version(a) > deps.parse_version(b) -end - ---- Check if rockspec format version satisfies version requirement. --- @param rockspec table: The rockspec table. --- @param version string: required version. --- @return boolean: true if rockspec format matches version or is newer, false otherwise. -function deps.format_is_at_least(rockspec, version) - local rockspec_format = rockspec.rockspec_format or "1.0" - return deps.parse_version(rockspec_format) >= deps.parse_version(version) -end - --- Consumes a constraint from a string, converting it to table format. -- For example, a string ">= 1.0, > 2.0" is converted to a table in the -- format {op = ">=", version={1,0}} and the rest, "> 2.0", is returned @@ -201,7 +77,7 @@ end function deps.parse_constraints(input) assert(type(input) == "string") - local constraints, constraint, oinput = {}, nil, input + local constraints, oinput, constraint = {}, input while #input > 0 do constraint, input = parse_constraint(input) if constraint then @@ -267,65 +143,6 @@ function deps.show_dep(dep, internal) end end ---- A more lenient check for equivalence between versions. --- This returns true if the requested components of a version --- match and ignore the ones that were not given. For example, --- when requesting "2", then "2", "2.1", "2.3.5-9"... all match. --- When requesting "2.1", then "2.1", "2.1.3" match, but "2.2" --- doesn't. --- @param version string or table: Version to be tested; may be --- in string format or already parsed into a table. --- @param requested string or table: Version requested; may be --- in string format or already parsed into a table. --- @return boolean: True if the tested version matches the requested --- version, false otherwise. -local function partial_match(version, requested) - assert(type(version) == "string" or type(version) == "table") - assert(type(requested) == "string" or type(version) == "table") - - if type(version) ~= "table" then version = deps.parse_version(version) end - if type(requested) ~= "table" then requested = deps.parse_version(requested) end - if not version or not requested then return false end - - for i, ri in ipairs(requested) do - local vi = version[i] or 0 - if ri ~= vi then return false end - end - if requested.revision then - return requested.revision == version.revision - end - return true -end - ---- Check if a version satisfies a set of constraints. --- @param version table: A version in table format --- @param constraints table: An array of constraints in table format. --- @return boolean: True if version satisfies all constraints, --- false otherwise. -function deps.match_constraints(version, constraints) - assert(type(version) == "table") - assert(type(constraints) == "table") - local ok = true - setmetatable(version, version_mt) - for _, constr in pairs(constraints) do - if type(constr.version) == "string" then - constr.version = deps.parse_version(constr.version) - end - local constr_version, constr_op = constr.version, constr.op - setmetatable(constr_version, version_mt) - if constr_op == "==" then ok = version == constr_version - elseif constr_op == "~=" then ok = version ~= constr_version - elseif constr_op == ">" then ok = version > constr_version - elseif constr_op == "<" then ok = version < constr_version - elseif constr_op == ">=" then ok = version >= constr_version - elseif constr_op == "<=" then ok = version <= constr_version - elseif constr_op == "~>" then ok = partial_match(version, constr_version) - end - if not ok then break end - end - return ok -end - --- Attempt to match a dependency to an installed rock. -- @param dep table: A dependency parsed in table format. -- @param blacklist table: Versions that can't be accepted. Table where keys @@ -344,7 +161,7 @@ local function match_dep(dep, blacklist, deps_mode, rocks_provided) -- Provided rocks have higher priority than manifest's rocks. versions = { provided } else - versions = manif_core.get_versions(dep.name, deps_mode) + versions = manif.get_versions(dep.name, deps_mode) end local latest_version @@ -431,7 +248,8 @@ function deps.fulfill_dependencies(rockspec, deps_mode) end local supported = nil for _, plat in pairs(rockspec.supported_platforms) do - local neg, plat = plat:match("^(!?)(.*)") + local neg + neg, plat = plat:match("^(!?)(.*)") if neg == "!" then if deps.platforms_set[plat] then return nil, "This rockspec for "..rockspec.package.." does not support "..plat.." platforms." @@ -704,7 +522,6 @@ function deps.scan_deps(results, missing, manifest, name, version, deps_mode) local fetch = require("luarocks.fetch") - local err if results[name] then return results, missing end diff --git a/src/luarocks/dir.lua b/src/luarocks/dir.lua index f72ebd6c..ad7fb870 100644 --- a/src/luarocks/dir.lua +++ b/src/luarocks/dir.lua @@ -1,9 +1,7 @@ --- Generic utilities for handling pathnames. local dir = {} -package.loaded["luarocks.dir"] = dir - -dir.separator = "/" +setmetatable(dir, { __index = require("luarocks.core.dir") }) --- Strip the path off a path+filename. -- @param pathname string: A path+name, such as "/a/b/c" @@ -26,49 +24,4 @@ function dir.dir_name(pathname) return (pathname:gsub("/*$", ""):match("(.*)[/]+[^/]*")) or "" end ---- Describe a path in a cross-platform way. --- Use this function to avoid platform-specific directory --- separators in other modules. Removes trailing slashes from --- each component given, to avoid repeated separators. --- Separators inside strings are kept, to handle URLs containing --- protocols. --- @param ... strings representing directories --- @return string: a string with a platform-specific representation --- of the path. -function dir.path(...) - local t = {...} - while t[1] == "" do - table.remove(t, 1) - end - return (table.concat(t, "/"):gsub("([^:])/+", "%1/"):gsub("^/+", "/"):gsub("/*$", "")) -end - ---- Split protocol and path from an URL or local pathname. --- URLs should be in the "protocol://path" format. --- For local pathnames, "file" is returned as the protocol. --- @param url string: an URL or a local pathname. --- @return string, string: the protocol, and the pathname without the protocol. -function dir.split_url(url) - assert(type(url) == "string") - - local protocol, pathname = url:match("^([^:]*)://(.*)") - if not protocol then - protocol = "file" - pathname = url - end - return protocol, pathname -end - ---- Normalize a url or local path. --- URLs should be in the "protocol://path" format. System independent --- forward slashes are used, removing trailing and double slashes --- @param url string: an URL or a local pathname. --- @return string: Normalized result. -function dir.normalize(name) - local protocol, pathname = dir.split_url(name) - pathname = pathname:gsub("\\", "/"):gsub("(.)/*$", "%1"):gsub("//", "/") - if protocol ~= "file" then pathname = protocol .."://"..pathname end - return pathname -end - return dir diff --git a/src/luarocks/doc.lua b/src/luarocks/doc.lua index 758fd9c5..8378bbc2 100644 --- a/src/luarocks/doc.lua +++ b/src/luarocks/doc.lua @@ -2,7 +2,6 @@ --- Module implementing the LuaRocks "doc" command. -- Shows documentation for an installed rock. local doc = {} -package.loaded["luarocks.doc"] = doc local util = require("luarocks.util") local show = require("luarocks.show") diff --git a/src/luarocks/download.lua b/src/luarocks/download.lua index d793cab7..c81958de 100644 --- a/src/luarocks/download.lua +++ b/src/luarocks/download.lua @@ -2,7 +2,6 @@ --- Module implementing the luarocks "download" command. -- Download a rock from the repository. local download = {} -package.loaded["luarocks.download"] = download local util = require("luarocks.util") local path = require("luarocks.path") @@ -10,7 +9,7 @@ local fetch = require("luarocks.fetch") local search = require("luarocks.search") local fs = require("luarocks.fs") local dir = require("luarocks.dir") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") download.help_summary = "Download a specific rock file from a rocks server." download.help_arguments = "[--all] [--arch= | --source | --rockspec] [ []]" diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua index 2590afd4..9bf1922a 100644 --- a/src/luarocks/fetch.lua +++ b/src/luarocks/fetch.lua @@ -1,7 +1,6 @@ --- Functions related to fetching and loading local and remote files. local fetch = {} -package.loaded["luarocks.fetch"] = fetch local fs = require("luarocks.fs") local dir = require("luarocks.dir") @@ -10,7 +9,7 @@ local path = require("luarocks.path") local deps = require("luarocks.deps") local persist = require("luarocks.persist") local util = require("luarocks.util") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") function fetch.is_basic_protocol(protocol, remote) return protocol == "http" or protocol == "https" or protocol == "ftp" or (not remote and protocol == "file") diff --git a/src/luarocks/fs.lua b/src/luarocks/fs.lua index 54cc7d73..7322e552 100644 --- a/src/luarocks/fs.lua +++ b/src/luarocks/fs.lua @@ -8,9 +8,10 @@ local pairs = pairs local fs = {} +-- To avoid a loop when loading the other fs modules. package.loaded["luarocks.fs"] = fs -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local pack = table.pack or function(...) return { n = select("#", ...), ... } end local unpack = table.unpack or unpack diff --git a/src/luarocks/fs/lua.lua b/src/luarocks/fs/lua.lua index a31cbb4e..770da2b7 100644 --- a/src/luarocks/fs/lua.lua +++ b/src/luarocks/fs/lua.lua @@ -6,7 +6,7 @@ local fs_lua = {} local fs = require("luarocks.fs") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local dir = require("luarocks.dir") local util = require("luarocks.util") local path = require("luarocks.path") diff --git a/src/luarocks/fs/tools.lua b/src/luarocks/fs/tools.lua index ed51b545..d2062ed8 100644 --- a/src/luarocks/fs/tools.lua +++ b/src/luarocks/fs/tools.lua @@ -4,7 +4,7 @@ local tools = {} local fs = require("luarocks.fs") local dir = require("luarocks.dir") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local vars = cfg.variables diff --git a/src/luarocks/fs/unix.lua b/src/luarocks/fs/unix.lua index 5c6b542c..df9d256d 100644 --- a/src/luarocks/fs/unix.lua +++ b/src/luarocks/fs/unix.lua @@ -4,7 +4,7 @@ local unix = {} local fs = require("luarocks.fs") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local dir = require("luarocks.dir") local util = require("luarocks.util") diff --git a/src/luarocks/fs/unix/tools.lua b/src/luarocks/fs/unix/tools.lua index d0802725..d9dc009f 100644 --- a/src/luarocks/fs/unix/tools.lua +++ b/src/luarocks/fs/unix/tools.lua @@ -4,7 +4,7 @@ local tools = {} local fs = require("luarocks.fs") local dir = require("luarocks.dir") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local vars = cfg.variables diff --git a/src/luarocks/fs/win32.lua b/src/luarocks/fs/win32.lua index 8debaeef..c03db6a0 100644 --- a/src/luarocks/fs/win32.lua +++ b/src/luarocks/fs/win32.lua @@ -5,7 +5,7 @@ local win32 = {} local fs = require("luarocks.fs") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local dir = require("luarocks.dir") local util = require("luarocks.util") diff --git a/src/luarocks/fs/win32/tools.lua b/src/luarocks/fs/win32/tools.lua index 4adc78d1..7f6b853e 100644 --- a/src/luarocks/fs/win32/tools.lua +++ b/src/luarocks/fs/win32/tools.lua @@ -6,7 +6,7 @@ local tools = {} local fs = require("luarocks.fs") local dir = require("luarocks.dir") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local vars = cfg.variables diff --git a/src/luarocks/help.lua b/src/luarocks/help.lua index 5bac77ce..51e2cf57 100644 --- a/src/luarocks/help.lua +++ b/src/luarocks/help.lua @@ -7,7 +7,7 @@ local help = {} local util = require("luarocks.util") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local dir = require("luarocks.dir") local program = util.this_program("luarocks") diff --git a/src/luarocks/index.lua b/src/luarocks/index.lua index e1f563ef..468f5cf8 100644 --- a/src/luarocks/index.lua +++ b/src/luarocks/index.lua @@ -1,7 +1,6 @@ --- Module which builds the index.html page to be used in rocks servers. local index = {} -package.loaded["luarocks.index"] = index local util = require("luarocks.util") local fs = require("luarocks.fs") diff --git a/src/luarocks/install.lua b/src/luarocks/install.lua index 10d57f37..c4139fd0 100644 --- a/src/luarocks/install.lua +++ b/src/luarocks/install.lua @@ -1,7 +1,6 @@ --- Module implementing the LuaRocks "install" command. -- Installs binary rocks. local install = {} -package.loaded["luarocks.install"] = install local path = require("luarocks.path") local repos = require("luarocks.repos") @@ -9,9 +8,9 @@ local fetch = require("luarocks.fetch") local util = require("luarocks.util") local fs = require("luarocks.fs") local deps = require("luarocks.deps") -local manif = require("luarocks.manif") +local writer = require("luarocks.manif.writer") local remove = require("luarocks.remove") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") install.help_summary = "Install a rock." @@ -74,7 +73,7 @@ function install.install_binary_rock(rock_file, deps_mode) -- For compatibility with .rock files built with LuaRocks 1 if not fs.exists(path.rock_manifest_file(name, version)) then - ok, err = manif.make_rock_manifest(name, version) + ok, err = writer.make_rock_manifest(name, version) if err then return nil, err end end @@ -94,7 +93,7 @@ function install.install_binary_rock(rock_file, deps_mode) ok, err = repos.run_hook(rockspec, "post_install") if err then return nil, err end - ok, err = manif.update_manifest(name, version, nil, deps_mode) + ok, err = writer.update_manifest(name, version, nil, deps_mode) if err then return nil, err end util.announce_install(rockspec) diff --git a/src/luarocks/lint.lua b/src/luarocks/lint.lua index 4c30804b..742f1736 100644 --- a/src/luarocks/lint.lua +++ b/src/luarocks/lint.lua @@ -2,7 +2,6 @@ --- Module implementing the LuaRocks "lint" command. -- Utility function that checks syntax of the rockspec. local lint = {} -package.loaded["luarocks.lint"] = lint local util = require("luarocks.util") local download = require("luarocks.download") diff --git a/src/luarocks/list.lua b/src/luarocks/list.lua index 8c9c5107..45f1a26f 100644 --- a/src/luarocks/list.lua +++ b/src/luarocks/list.lua @@ -2,11 +2,10 @@ --- Module implementing the LuaRocks "list" command. -- Lists currently installed rocks. local list = {} -package.loaded["luarocks.list"] = list local search = require("luarocks.search") local deps = require("luarocks.deps") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local util = require("luarocks.util") local path = require("luarocks.path") diff --git a/src/luarocks/loader.lua b/src/luarocks/loader.lua index 26280e94..0faaebda 100644 --- a/src/luarocks/loader.lua +++ b/src/luarocks/loader.lua @@ -6,21 +6,24 @@ -- used to load previous modules, so that the loader chooses versions -- that are declared to be compatible with the ones loaded earlier. local loaders = package.loaders or package.searchers -local package, require, ipairs, table, type, next, tostring, error = - package, require, ipairs, table, type, next, tostring, error +local require, ipairs, table, type, next, tostring, error = + require, ipairs, table, type, next, tostring, error local unpack = unpack or table.unpack ---module("luarocks.loader") local loader = {} -package.loaded["luarocks.loader"] = loader -local cfg = require("luarocks.cfg") +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 path = require("luarocks.path") -local manif_core = require("luarocks.manif_core") -local deps = require("luarocks.deps") -local util = require("luarocks.util") +local path = require("luarocks.core.path") +local manif = require("luarocks.core.manif") +local deps = require("luarocks.core.deps") +local util = require("luarocks.core.util") +local require = nil +-------------------------------------------------------------------------------- loader.context = {} @@ -33,7 +36,7 @@ local function load_rocks_trees() local any_ok = false local trees = {} for _, tree in ipairs(cfg.rocks_trees) do - local manifest, err = manif_core.load_local_manifest(path.rocks_dir(tree)) + local manifest, err = manif.load_local_manifest(path.rocks_dir(tree)) if manifest then any_ok = true table.insert(trees, {tree=tree, manifest=manifest}) @@ -217,4 +220,12 @@ end table.insert(loaders, 1, loader.luarocks_loader) +if is_clean then + for modname, _ in pairs(package.loaded) do + if modname:match("^luarocks%.") then + package.loaded[modname] = nil + end + end +end + return loader diff --git a/src/luarocks/make.lua b/src/luarocks/make.lua index 476150eb..eb38bff0 100644 --- a/src/luarocks/make.lua +++ b/src/luarocks/make.lua @@ -4,12 +4,11 @@ -- it does not fetch sources, etc., assuming everything is -- available in the current directory. local make = {} -package.loaded["luarocks.make"] = make local build = require("luarocks.build") local fs = require("luarocks.fs") local util = require("luarocks.util") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local fetch = require("luarocks.fetch") local pack = require("luarocks.pack") local remove = require("luarocks.remove") diff --git a/src/luarocks/make_manifest.lua b/src/luarocks/make_manifest.lua deleted file mode 100644 index b89ba47f..00000000 --- a/src/luarocks/make_manifest.lua +++ /dev/null @@ -1,52 +0,0 @@ - ---- Module implementing the luarocks-admin "make_manifest" command. --- Compile a manifest file for a repository. -local make_manifest = {} -package.loaded["luarocks.make_manifest"] = make_manifest - -local manif = require("luarocks.manif") -local index = require("luarocks.index") -local cfg = require("luarocks.cfg") -local util = require("luarocks.util") -local deps = require("luarocks.deps") -local fs = require("luarocks.fs") -local dir = require("luarocks.dir") - -make_manifest.help_summary = "Compile a manifest file for a repository." - -make_manifest.help = [[ -, if given, is a local repository pathname. - ---local-tree If given, do not write versioned versions of the manifest file. - Use this when rebuilding the manifest of a local rocks tree. -]] - ---- Driver function for "make_manifest" command. --- @param repo string or nil: Pathname of a local repository. If not given, --- the default local repository configured as cfg.rocks_dir is used. --- @return boolean or (nil, string): True if manifest was generated, --- or nil and an error message. -function make_manifest.command(flags, repo) - assert(type(repo) == "string" or not repo) - repo = repo or cfg.rocks_dir - - util.printout("Making manifest for "..repo) - - if repo:match("/lib/luarocks") and not flags["local-tree"] then - util.warning("This looks like a local rocks tree, but you did not pass --local-tree.") - end - - local ok, err = manif.make_manifest(repo, deps.get_deps_mode(flags), not flags["local-tree"]) - if ok and not flags["local-tree"] then - util.printout("Generating index.html for "..repo) - index.make_index(repo) - end - if flags["local-tree"] then - for luaver in util.lua_versions() do - fs.delete(dir.path(repo, "manifest-"..luaver)) - end - end - return ok, err -end - -return make_manifest diff --git a/src/luarocks/manif.lua b/src/luarocks/manif.lua index e30c2a33..c4da9a8e 100644 --- a/src/luarocks/manif.lua +++ b/src/luarocks/manif.lua @@ -3,41 +3,18 @@ -- They are loaded into manifest tables, which are then used for -- performing searches, matching dependencies, etc. local manif = {} -package.loaded["luarocks.manif"] = manif +setmetatable(manif, { __index = require("luarocks.core.manif") }) -local manif_core = require("luarocks.manif_core") local persist = require("luarocks.persist") local fetch = require("luarocks.fetch") local dir = require("luarocks.dir") local fs = require("luarocks.fs") -local search = require("luarocks.search") local util = require("luarocks.util") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local path = require("luarocks.path") -local repos = require("luarocks.repos") -local deps = require("luarocks.deps") manif.rock_manifest_cache = {} ---- Commit a table to disk in given local path. --- @param where string: The directory where the table should be saved. --- @param name string: The filename. --- @param tbl table: The table to be saved. --- @return boolean or (nil, string): true if successful, or nil and a --- message in case of errors. -local function save_table(where, name, tbl) - assert(type(where) == "string") - assert(type(name) == "string") - assert(type(tbl) == "table") - - local filename = dir.path(where, name) - local ok, err = persist.save_from_table(filename..".tmp", tbl) - if ok then - ok, err = fs.replace_file(filename, filename..".tmp") - end - return ok, err -end - function manif.load_rock_manifest(name, version, root) assert(type(name) == "string") assert(type(version) == "string") @@ -53,38 +30,6 @@ function manif.load_rock_manifest(name, version, root) return rock_manifest.rock_manifest end -function manif.make_rock_manifest(name, version) - local install_dir = path.install_dir(name, version) - local rock_manifest = path.rock_manifest_file(name, version) - local tree = {} - for _, file in ipairs(fs.find(install_dir)) do - local full_path = dir.path(install_dir, file) - local walk = tree - local last - local last_name - for name in file:gmatch("[^/]+") do - local next = walk[name] - if not next then - next = {} - walk[name] = next - end - last = walk - last_name = name - walk = next - end - if fs.is_file(full_path) then - local sum, err = fs.get_md5(full_path) - if not sum then - return nil, "Failed producing checksum: "..tostring(err) - end - last[last_name] = sum - end - end - rock_manifest = { rock_manifest=tree } - manif.rock_manifest_cache[name.."/"..version] = rock_manifest - save_table(install_dir, "rock_manifest", rock_manifest ) -end - local function fetch_manifest_from(repo_url, filename) local url = dir.path(repo_url, filename) local name = repo_url:gsub("[/:]","_") @@ -112,7 +57,7 @@ function manif.load_manifest(repo_url, lua_version) assert(type(lua_version) == "string" or not lua_version) lua_version = lua_version or cfg.lua_version - local cached_manifest = manif_core.get_cached_manifest(repo_url, lua_version) + local cached_manifest = manif.get_cached_manifest(repo_url, lua_version) if cached_manifest then return cached_manifest end @@ -159,316 +104,14 @@ function manif.load_manifest(repo_url, lua_version) end pathname = nozip end - return manif_core.manifest_loader(pathname, repo_url, lua_version) -end - ---- Output a table listing items of a package. --- @param itemsfn function: a function for obtaining items of a package. --- pkg and version will be passed to it; it should return a table with --- items as keys. --- @param pkg string: package name --- @param version string: package version --- @param tbl table: the package matching table: keys should be item names --- and values arrays of strings with packages names in "name/version" format. -local function store_package_items(itemsfn, pkg, version, tbl) - assert(type(itemsfn) == "function") - assert(type(pkg) == "string") - assert(type(version) == "string") - assert(type(tbl) == "table") - - local pkg_version = pkg.."/"..version - local result = {} - - for item, path in pairs(itemsfn(pkg, version)) do - result[item] = path - if not tbl[item] then - tbl[item] = {} - end - table.insert(tbl[item], pkg_version) - end - return result -end - ---- Sort function for ordering rock identifiers in a manifest's --- modules table. Rocks are ordered alphabetically by name, and then --- by version which greater first. --- @param a string: Version to compare. --- @param b string: Version to compare. --- @return boolean: The comparison result, according to the --- rule outlined above. -local function sort_pkgs(a, b) - assert(type(a) == "string") - assert(type(b) == "string") - - local na, va = a:match("(.*)/(.*)$") - local nb, vb = b:match("(.*)/(.*)$") - - return (na == nb) and deps.compare_versions(va, vb) or na < nb -end - ---- Sort items of a package matching table by version number (higher versions first). --- @param tbl table: the package matching table: keys should be strings --- and values arrays of strings with packages names in "name/version" format. -local function sort_package_matching_table(tbl) - assert(type(tbl) == "table") - - if next(tbl) then - for item, pkgs in pairs(tbl) do - if #pkgs > 1 then - table.sort(pkgs, sort_pkgs) - -- Remove duplicates from the sorted array. - local prev = nil - local i = 1 - while pkgs[i] do - local curr = pkgs[i] - if curr == prev then - table.remove(pkgs, i) - else - prev = curr - i = i + 1 - end - end - end - end - end -end - ---- Process the dependencies of a manifest table to determine its dependency --- chains for loading modules. The manifest dependencies information is filled --- and any dependency inconsistencies or missing dependencies are reported to --- standard error. --- @param manifest table: a manifest table. --- @param deps_mode string: Dependency mode: "one" for the current default tree, --- "all" for all trees, "order" for all trees with priority >= the current default, --- "none" for no trees. -local function update_dependencies(manifest, deps_mode) - assert(type(manifest) == "table") - assert(type(deps_mode) == "string") - - for pkg, versions in pairs(manifest.repository) do - for version, repositories in pairs(versions) do - local current = pkg.." "..version - for _, repo in ipairs(repositories) do - if repo.arch == "installed" then - local missing - repo.dependencies, missing = deps.scan_deps({}, {}, manifest, pkg, version, deps_mode) - repo.dependencies[pkg] = nil - if missing then - for miss, err in pairs(missing) do - if miss == current then - util.printerr("Tree inconsistency detected: "..current.." has no rockspec. "..err) - elseif deps_mode ~= "none" then - util.printerr("Missing dependency for "..pkg.." "..version..": "..miss) - end - end - end - end - end - end - end -end - ---- Filter manifest table by Lua version, removing rockspecs whose Lua version --- does not match. --- @param manifest table: a manifest table. --- @param lua_version string or nil: filter by Lua version --- @param repodir string: directory of repository being scanned --- @param cache table: temporary rockspec cache table -local function filter_by_lua_version(manifest, lua_version, repodir, cache) - assert(type(manifest) == "table") - assert(type(repodir) == "string") - assert((not cache) or type(cache) == "table") - - cache = cache or {} - lua_version = deps.parse_version(lua_version) - for pkg, versions in pairs(manifest.repository) do - local to_remove = {} - for version, repositories in pairs(versions) do - for _, repo in ipairs(repositories) do - if repo.arch == "rockspec" then - local pathname = dir.path(repodir, pkg.."-"..version..".rockspec") - local rockspec, err = cache[pathname] - if not rockspec then - rockspec, err = fetch.load_local_rockspec(pathname, true) - end - if rockspec then - cache[pathname] = rockspec - for _, dep in ipairs(rockspec.dependencies) do - if dep.name == "lua" then - if not deps.match_constraints(lua_version, dep.constraints) then - table.insert(to_remove, version) - end - break - end - end - else - util.printerr("Error loading rockspec for "..pkg.." "..version..": "..err) - end - end - end - end - if next(to_remove) then - for _, incompat in ipairs(to_remove) do - versions[incompat] = nil - end - if not next(versions) then - manifest.repository[pkg] = nil - end - end - end -end - ---- Store search results in a manifest table. --- @param results table: The search results as returned by search.disk_search. --- @param manifest table: A manifest table (must contain repository, modules, commands tables). --- It will be altered to include the search results. --- @param dep_handler: dependency handler function --- @return boolean or (nil, string): true in case of success, or nil followed by an error message. -local function store_results(results, manifest, dep_handler) - assert(type(results) == "table") - assert(type(manifest) == "table") - assert((not dep_handler) or type(dep_handler) == "function") - - for name, versions in pairs(results) do - local pkgtable = manifest.repository[name] or {} - for version, entries in pairs(versions) do - local versiontable = {} - for _, entry in ipairs(entries) do - local entrytable = {} - entrytable.arch = entry.arch - if entry.arch == "installed" then - local rock_manifest = manif.load_rock_manifest(name, version) - if not rock_manifest then - return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?" - end - entrytable.modules = store_package_items(repos.package_modules, name, version, manifest.modules) - entrytable.commands = store_package_items(repos.package_commands, name, version, manifest.commands) - end - table.insert(versiontable, entrytable) - end - pkgtable[version] = versiontable - end - manifest.repository[name] = pkgtable - end - if dep_handler then - dep_handler(manifest) - end - sort_package_matching_table(manifest.modules) - sort_package_matching_table(manifest.commands) - return true -end - ---- Scan a LuaRocks repository and output a manifest file. --- A file called 'manifest' will be written in the root of the given --- repository directory. --- @param repo A local repository directory. --- @param deps_mode string: Dependency mode: "one" for the current default tree, --- "all" for all trees, "order" for all trees with priority >= the current default, --- "none" for the default dependency mode from the configuration. --- @param versioned boolean: if versioned versions of the manifest should be created. --- @return boolean or (nil, string): True if manifest was generated, --- or nil and an error message. -function manif.make_manifest(repo, deps_mode, remote) - assert(type(repo) == "string") - assert(type(deps_mode) == "string") - - if deps_mode == "none" then deps_mode = cfg.deps_mode end - - if not fs.is_dir(repo) then - return nil, "Cannot access repository at "..repo - end - - local query = search.make_query("") - query.exact_name = false - query.arch = "any" - local results = search.disk_search(repo, query) - local manifest = { repository = {}, modules = {}, commands = {} } - - manif_core.cache_manifest(repo, nil, manifest) - - local dep_handler = nil - if not remote then - dep_handler = function(manifest) - update_dependencies(manifest, deps_mode) - end - end - local ok, err = store_results(results, manifest, dep_handler) - if not ok then return nil, err end - - if remote then - local cache = {} - for luaver in util.lua_versions() do - local vmanifest = { repository = {}, modules = {}, commands = {} } - local dep_handler = function(manifest) - filter_by_lua_version(manifest, luaver, repo, cache) - end - local ok, err = store_results(results, vmanifest, dep_handler) - save_table(repo, "manifest-"..luaver, vmanifest) - end - end - - return save_table(repo, "manifest", manifest) -end - ---- Load a manifest file from a local repository and add to the repository --- information with regard to the given name and version. --- A file called 'manifest' will be written in the root of the given --- repository directory. --- @param name string: Name of a package from the repository. --- @param version string: Version of a package from the repository. --- @param repo string or nil: Pathname of a local repository. If not given, --- the default local repository is used. --- @param deps_mode string: Dependency mode: "one" for the current default tree, --- "all" for all trees, "order" for all trees with priority >= the current default, --- "none" for using the default dependency mode from the configuration. --- @return boolean or (nil, string): True if manifest was generated, --- or nil and an error message. -function manif.update_manifest(name, version, repo, deps_mode) - assert(type(name) == "string") - assert(type(version) == "string") - repo = path.rocks_dir(repo or cfg.root_dir) - assert(type(deps_mode) == "string") - - if deps_mode == "none" then deps_mode = cfg.deps_mode end - - local manifest, err = manif.load_manifest(repo) - if not manifest then - util.printerr("No existing manifest. Attempting to rebuild...") - local ok, err = manif.make_manifest(repo, deps_mode) - if not ok then - return nil, err - end - manifest, err = manif.load_manifest(repo) - if not manifest then - return nil, err - end - end - - local results = {[name] = {[version] = {{arch = "installed", repo = repo}}}} - - local dep_handler = function(manifest) - update_dependencies(manifest, deps_mode) - end - local ok, err = store_results(results, manifest, dep_handler) - if not ok then return nil, err end - - return save_table(repo, "manifest", manifest) -end - -function manif.zip_manifests() - for ver in util.lua_versions() do - local file = "manifest-"..ver - local zip = file..".zip" - fs.delete(dir.path(fs.current_dir(), zip)) - fs.zip(zip, file) - end + return manif.manifest_loader(pathname, repo_url, lua_version) end local function find_providers(file, root) assert(type(file) == "string") root = root or cfg.root_dir - local manifest, err = manif_core.load_local_manifest(path.rocks_dir(root)) + local manifest, err = manif.load_local_manifest(path.rocks_dir(root)) if not manifest then return nil, "untracked" end diff --git a/src/luarocks/manif/writer.lua b/src/luarocks/manif/writer.lua new file mode 100644 index 00000000..75ea3b81 --- /dev/null +++ b/src/luarocks/manif/writer.lua @@ -0,0 +1,360 @@ + +local writer = {} + +local cfg = require("luarocks.core.cfg") +local search = require("luarocks.search") +local repos = require("luarocks.repos") +local deps = require("luarocks.deps") +local fs = require("luarocks.fs") +local util = require("luarocks.util") +local dir = require("luarocks.dir") +local fetch = require("luarocks.fetch") +local path = require("luarocks.path") +local persist = require("luarocks.persist") +local manif = require("luarocks.manif") + +--- Output a table listing items of a package. +-- @param itemsfn function: a function for obtaining items of a package. +-- pkg and version will be passed to it; it should return a table with +-- items as keys. +-- @param pkg string: package name +-- @param version string: package version +-- @param tbl table: the package matching table: keys should be item names +-- and values arrays of strings with packages names in "name/version" format. +local function store_package_items(itemsfn, pkg, version, tbl) + assert(type(itemsfn) == "function") + assert(type(pkg) == "string") + assert(type(version) == "string") + assert(type(tbl) == "table") + + local pkg_version = pkg.."/"..version + local result = {} + + for item, path in pairs(itemsfn(pkg, version)) do + result[item] = path + if not tbl[item] then + tbl[item] = {} + end + table.insert(tbl[item], pkg_version) + end + return result +end + + +--- Process the dependencies of a manifest table to determine its dependency +-- chains for loading modules. The manifest dependencies information is filled +-- and any dependency inconsistencies or missing dependencies are reported to +-- standard error. +-- @param manifest table: a manifest table. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for no trees. +local function update_dependencies(manifest, deps_mode) + assert(type(manifest) == "table") + assert(type(deps_mode) == "string") + + for pkg, versions in pairs(manifest.repository) do + for version, repositories in pairs(versions) do + local current = pkg.." "..version + for _, repo in ipairs(repositories) do + if repo.arch == "installed" then + local missing + repo.dependencies, missing = deps.scan_deps({}, {}, manifest, pkg, version, deps_mode) + repo.dependencies[pkg] = nil + if missing then + for miss, err in pairs(missing) do + if miss == current then + util.printerr("Tree inconsistency detected: "..current.." has no rockspec. "..err) + elseif deps_mode ~= "none" then + util.printerr("Missing dependency for "..pkg.." "..version..": "..miss) + end + end + end + end + end + end + end +end + +--- Sort function for ordering rock identifiers in a manifest's +-- modules table. Rocks are ordered alphabetically by name, and then +-- by version which greater first. +-- @param a string: Version to compare. +-- @param b string: Version to compare. +-- @return boolean: The comparison result, according to the +-- rule outlined above. +local function sort_pkgs(a, b) + assert(type(a) == "string") + assert(type(b) == "string") + + local na, va = a:match("(.*)/(.*)$") + local nb, vb = b:match("(.*)/(.*)$") + + return (na == nb) and deps.compare_versions(va, vb) or na < nb +end + +--- Sort items of a package matching table by version number (higher versions first). +-- @param tbl table: the package matching table: keys should be strings +-- and values arrays of strings with packages names in "name/version" format. +local function sort_package_matching_table(tbl) + assert(type(tbl) == "table") + + if next(tbl) then + for item, pkgs in pairs(tbl) do + if #pkgs > 1 then + table.sort(pkgs, sort_pkgs) + -- Remove duplicates from the sorted array. + local prev = nil + local i = 1 + while pkgs[i] do + local curr = pkgs[i] + if curr == prev then + table.remove(pkgs, i) + else + prev = curr + i = i + 1 + end + end + end + end + end +end + +--- Filter manifest table by Lua version, removing rockspecs whose Lua version +-- does not match. +-- @param manifest table: a manifest table. +-- @param lua_version string or nil: filter by Lua version +-- @param repodir string: directory of repository being scanned +-- @param cache table: temporary rockspec cache table +local function filter_by_lua_version(manifest, lua_version, repodir, cache) + assert(type(manifest) == "table") + assert(type(repodir) == "string") + assert((not cache) or type(cache) == "table") + + cache = cache or {} + lua_version = deps.parse_version(lua_version) + for pkg, versions in pairs(manifest.repository) do + local to_remove = {} + for version, repositories in pairs(versions) do + for _, repo in ipairs(repositories) do + if repo.arch == "rockspec" then + local pathname = dir.path(repodir, pkg.."-"..version..".rockspec") + local rockspec, err = cache[pathname] + if not rockspec then + rockspec, err = fetch.load_local_rockspec(pathname, true) + end + if rockspec then + cache[pathname] = rockspec + for _, dep in ipairs(rockspec.dependencies) do + if dep.name == "lua" then + if not deps.match_constraints(lua_version, dep.constraints) then + table.insert(to_remove, version) + end + break + end + end + else + util.printerr("Error loading rockspec for "..pkg.." "..version..": "..err) + end + end + end + end + if next(to_remove) then + for _, incompat in ipairs(to_remove) do + versions[incompat] = nil + end + if not next(versions) then + manifest.repository[pkg] = nil + end + end + end +end + +--- Store search results in a manifest table. +-- @param results table: The search results as returned by search.disk_search. +-- @param manifest table: A manifest table (must contain repository, modules, commands tables). +-- It will be altered to include the search results. +-- @param dep_handler: dependency handler function +-- @return boolean or (nil, string): true in case of success, or nil followed by an error message. +local function store_results(results, manifest, dep_handler) + assert(type(results) == "table") + assert(type(manifest) == "table") + assert((not dep_handler) or type(dep_handler) == "function") + + for name, versions in pairs(results) do + local pkgtable = manifest.repository[name] or {} + for version, entries in pairs(versions) do + local versiontable = {} + for _, entry in ipairs(entries) do + local entrytable = {} + entrytable.arch = entry.arch + if entry.arch == "installed" then + local rock_manifest = manif.load_rock_manifest(name, version) + if not rock_manifest then + return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?" + end + entrytable.modules = store_package_items(repos.package_modules, name, version, manifest.modules) + entrytable.commands = store_package_items(repos.package_commands, name, version, manifest.commands) + end + table.insert(versiontable, entrytable) + end + pkgtable[version] = versiontable + end + manifest.repository[name] = pkgtable + end + if dep_handler then + dep_handler(manifest) + end + sort_package_matching_table(manifest.modules) + sort_package_matching_table(manifest.commands) + return true +end + +--- Commit a table to disk in given local path. +-- @param where string: The directory where the table should be saved. +-- @param name string: The filename. +-- @param tbl table: The table to be saved. +-- @return boolean or (nil, string): true if successful, or nil and a +-- message in case of errors. +local function save_table(where, name, tbl) + assert(type(where) == "string") + assert(type(name) == "string") + assert(type(tbl) == "table") + + local filename = dir.path(where, name) + local ok, err = persist.save_from_table(filename..".tmp", tbl) + if ok then + ok, err = fs.replace_file(filename, filename..".tmp") + end + return ok, err +end + +function writer.make_rock_manifest(name, version) + local install_dir = path.install_dir(name, version) + local tree = {} + for _, file in ipairs(fs.find(install_dir)) do + local full_path = dir.path(install_dir, file) + local walk = tree + local last + local last_name + for filename in file:gmatch("[^/]+") do + local next = walk[filename] + if not next then + next = {} + walk[filename] = next + end + last = walk + last_name = filename + walk = next + end + if fs.is_file(full_path) then + local sum, err = fs.get_md5(full_path) + if not sum then + return nil, "Failed producing checksum: "..tostring(err) + end + last[last_name] = sum + end + end + local rock_manifest = { rock_manifest=tree } + manif.rock_manifest_cache[name.."/"..version] = rock_manifest + save_table(install_dir, "rock_manifest", rock_manifest ) +end + +--- Scan a LuaRocks repository and output a manifest file. +-- A file called 'manifest' will be written in the root of the given +-- repository directory. +-- @param repo A local repository directory. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for the default dependency mode from the configuration. +-- @param versioned boolean: if versioned versions of the manifest should be created. +-- @return boolean or (nil, string): True if manifest was generated, +-- or nil and an error message. +function writer.make_manifest(repo, deps_mode, remote) + assert(type(repo) == "string") + assert(type(deps_mode) == "string") + + if deps_mode == "none" then deps_mode = cfg.deps_mode end + + if not fs.is_dir(repo) then + return nil, "Cannot access repository at "..repo + end + + local query = search.make_query("") + query.exact_name = false + query.arch = "any" + local results = search.disk_search(repo, query) + local manifest = { repository = {}, modules = {}, commands = {} } + + manif.cache_manifest(repo, nil, manifest) + + local dep_handler = nil + if not remote then + dep_handler = function(manifest) + update_dependencies(manifest, deps_mode) + end + end + local ok, err = store_results(results, manifest, dep_handler) + if not ok then return nil, err end + + if remote then + local cache = {} + for luaver in util.lua_versions() do + local vmanifest = { repository = {}, modules = {}, commands = {} } + local dep_handler = function(manifest) + filter_by_lua_version(manifest, luaver, repo, cache) + end + store_results(results, vmanifest, dep_handler) + save_table(repo, "manifest-"..luaver, vmanifest) + end + end + + return save_table(repo, "manifest", manifest) +end + +--- Load a manifest file from a local repository and add to the repository +-- information with regard to the given name and version. +-- A file called 'manifest' will be written in the root of the given +-- repository directory. +-- @param name string: Name of a package from the repository. +-- @param version string: Version of a package from the repository. +-- @param repo string or nil: Pathname of a local repository. If not given, +-- the default local repository is used. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for using the default dependency mode from the configuration. +-- @return boolean or (nil, string): True if manifest was generated, +-- or nil and an error message. +function writer.update_manifest(name, version, repo, deps_mode) + assert(type(name) == "string") + assert(type(version) == "string") + repo = path.rocks_dir(repo or cfg.root_dir) + assert(type(deps_mode) == "string") + + if deps_mode == "none" then deps_mode = cfg.deps_mode end + + local manifest, err = manif.load_manifest(repo) + if not manifest then + util.printerr("No existing manifest. Attempting to rebuild...") + local ok, err = writer.make_manifest(repo, deps_mode) + if not ok then + return nil, err + end + manifest, err = manif.load_manifest(repo) + if not manifest then + return nil, err + end + end + + local results = {[name] = {[version] = {{arch = "installed", repo = repo}}}} + + local dep_handler = function(manifest) + update_dependencies(manifest, deps_mode) + end + local ok, err = store_results(results, manifest, dep_handler) + if not ok then return nil, err end + + return save_table(repo, "manifest", manifest) +end + +return writer diff --git a/src/luarocks/manif_core.lua b/src/luarocks/manif_core.lua deleted file mode 100644 index 5c8928d4..00000000 --- a/src/luarocks/manif_core.lua +++ /dev/null @@ -1,107 +0,0 @@ - ---- Core functions for querying manifest files. --- This module requires no specific 'fs' functionality. -local manif_core = {} -package.loaded["luarocks.manif_core"] = manif_core - -local persist = require("luarocks.persist") -local type_check = require("luarocks.type_check") -local cfg = require("luarocks.cfg") -local dir = require("luarocks.dir") -local util = require("luarocks.util") -local path = require("luarocks.path") - --- Table with repository identifiers as keys and tables mapping --- Lua versions to cached loaded manifests as values. -local manifest_cache = {} - ---- Cache a loaded manifest. --- @param repo_url string: The repository identifier. --- @param lua_version string: Lua version in "5.x" format, defaults to installed version. --- @param manifest table: the manifest to be cached. -function manif_core.cache_manifest(repo_url, lua_version, manifest) - lua_version = lua_version or cfg.lua_version - manifest_cache[repo_url] = manifest_cache[repo_url] or {} - manifest_cache[repo_url][lua_version] = manifest -end - ---- Attempt to get cached loaded manifest. --- @param repo_url string: The repository identifier. --- @param lua_version string: Lua version in "5.x" format, defaults to installed version. --- @return table or nil: loaded manifest or nil if cache is empty. -function manif_core.get_cached_manifest(repo_url, lua_version) - lua_version = lua_version or cfg.lua_version - return manifest_cache[repo_url] and manifest_cache[repo_url][lua_version] -end - ---- Back-end function that actually loads the manifest --- and stores it in the manifest cache. --- @param file string: The local filename of the manifest file. --- @param repo_url string: The repository identifier. --- @param lua_version string: Lua version in "5.x" format, defaults to installed version. --- @param quick boolean: If given, skips type checking. --- @return table or (nil, string, string): the manifest or nil, --- error message and error code ("open", "load", "run" or "type"). -function manif_core.manifest_loader(file, repo_url, lua_version, quick) - local manifest, err, errcode = persist.load_into_table(file) - if not manifest then - return nil, "Failed loading manifest for "..repo_url..": "..err, errcode - end - local globals = err - if not quick then - local ok, err = type_check.type_check_manifest(manifest, globals) - if not ok then - return nil, "Error checking manifest: "..err, "type" - end - end - - manif_core.cache_manifest(repo_url, lua_version, manifest) - return manifest -end - ---- Load a local manifest describing a repository. --- All functions that use manifest tables assume they were obtained --- through either this function or load_manifest. --- @param repo_url string: URL or pathname for the repository. --- @return table or (nil, string, string): A table representing the manifest, --- or nil followed by an error message and an error code, see manifest_loader. -function manif_core.load_local_manifest(repo_url) - assert(type(repo_url) == "string") - - local cached_manifest = manif_core.get_cached_manifest(repo_url) - if cached_manifest then - return cached_manifest - end - - local pathname = dir.path(repo_url, "manifest") - return manif_core.manifest_loader(pathname, repo_url, nil, true) -end - ---- Get all versions of a package listed in a manifest file. --- @param name string: a package name. --- @param deps_mode string: "one", to use only the currently --- configured tree; "order" to select trees based on order --- (use the current tree and all trees below it on the list) --- or "all", to use all trees. --- @return table: An array of strings listing installed --- versions of a package. -function manif_core.get_versions(name, deps_mode) - assert(type(name) == "string") - assert(type(deps_mode) == "string") - - local manifest = {} - path.map_trees(deps_mode, function(tree) - local loaded = manif_core.load_local_manifest(path.rocks_dir(tree)) - if loaded then - util.deep_merge(manifest, loaded) - end - end) - - local item = next(manifest) and manifest.repository[name] - if item then - return util.keys(item) - end - return {} -end - -return manif_core diff --git a/src/luarocks/pack.lua b/src/luarocks/pack.lua index 3a163e92..b85653e9 100644 --- a/src/luarocks/pack.lua +++ b/src/luarocks/pack.lua @@ -2,7 +2,6 @@ --- Module implementing the LuaRocks "pack" command. -- Creates a rock, packing sources or binaries. local pack = {} -package.loaded["luarocks.pack"] = pack local unpack = unpack or table.unpack @@ -10,7 +9,7 @@ local path = require("luarocks.path") local repos = require("luarocks.repos") local fetch = require("luarocks.fetch") local fs = require("luarocks.fs") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local util = require("luarocks.util") local dir = require("luarocks.dir") local manif = require("luarocks.manif") diff --git a/src/luarocks/path.lua b/src/luarocks/path.lua index 6219d8c6..83e9c295 100644 --- a/src/luarocks/path.lua +++ b/src/luarocks/path.lua @@ -3,9 +3,10 @@ -- All paths are configured in this module, making it a single -- point where the layout of the local installation is defined in LuaRocks. local path = {} +setmetatable(path, { __index = require("luarocks.core.path") }) local dir = require("luarocks.dir") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local util = require("luarocks.util") --- Infer rockspec filename from a rock filename. @@ -17,29 +18,11 @@ function path.rockspec_name_from_rock(rock_name) return base_name:match("(.*)%.[^.]*.rock") .. ".rockspec" end -function path.rocks_dir(tree) - if type(tree) == "string" then - return dir.path(tree, cfg.rocks_subdir) - else - assert(type(tree) == "table") - return tree.rocks_dir or dir.path(tree.root, cfg.rocks_subdir) - end -end - function path.root_dir(rocks_dir) assert(type(rocks_dir) == "string") return rocks_dir:match("(.*)" .. util.matchquote(cfg.rocks_subdir) .. ".*$") end -function path.rocks_tree_to_string(tree) - if type(tree) == "string" then - return tree - else - assert(type(tree) == "table") - return tree.root - end -end - function path.deploy_bin_dir(tree) if type(tree) == "string" then return dir.path(tree, "bin") @@ -49,24 +32,6 @@ function path.deploy_bin_dir(tree) end end -function path.deploy_lua_dir(tree) - if type(tree) == "string" then - return dir.path(tree, cfg.lua_modules_path) - else - assert(type(tree) == "table") - return tree.lua_dir or dir.path(tree.root, cfg.lua_modules_path) - end -end - -function path.deploy_lib_dir(tree) - if type(tree) == "string" then - return dir.path(tree, cfg.lib_modules_path) - else - assert(type(tree) == "table") - return tree.lib_dir or dir.path(tree.root, cfg.lib_modules_path) - end -end - function path.manifest_file(tree) if type(tree) == "string" then return dir.path(tree, cfg.rocks_subdir, "manifest") @@ -229,39 +194,6 @@ function path.make_url(pathname, name, version, arch) return dir.path(pathname, filename) end ---- Convert a pathname to a module identifier. --- In Unix, for example, a path "foo/bar/baz.lua" is converted to --- "foo.bar.baz"; "bla/init.lua" returns "bla"; "foo.so" returns "foo". --- @param file string: Pathname of module --- @return string: The module identifier, or nil if given path is --- not a conformant module path (the function does not check if the --- path actually exists). -function path.path_to_module(file) - assert(type(file) == "string") - - local name = file:match("(.*)%."..cfg.lua_extension.."$") - if name then - name = name:gsub(dir.separator, ".") - local init = name:match("(.*)%.init$") - if init then - name = init - end - else - name = file:match("(.*)%."..cfg.lib_extension.."$") - if name then - name = name:gsub(dir.separator, ".") - else - name = file:match("(.*)%."..cfg.static_lib_extension.."$") - if name then - name = name:gsub(dir.separator, ".") - end - end - end - if not name then name = file end - name = name:gsub("^%.+", ""):gsub("%.+$", "") - return name -end - --- Obtain the directory name where a module should be stored. -- For example, on Unix, "foo.bar.baz" will return "foo/bar". -- @param mod string: A module name in Lua dot-separated format. @@ -291,22 +223,6 @@ function path.configure_paths(rockspec) rockspec.variables = vars end ---- Produce a versioned version of a filename. --- @param file string: filename (must start with prefix) --- @param prefix string: Path prefix for file --- @param name string: Rock name --- @param version string: Rock version --- @return string: a pathname with the same directory parts and a versioned basename. -function path.versioned_name(file, prefix, name, version) - assert(type(file) == "string") - assert(type(name) == "string") - assert(type(version) == "string") - - local rest = file:sub(#prefix+1):gsub("^/*", "") - local name_version = (name.."_"..version):gsub("%-", "_"):gsub("%.", "_") - return dir.path(prefix, name_version.."-"..rest) -end - function path.use_tree(tree) cfg.root_dir = tree cfg.rocks_dir = path.rocks_dir(tree) @@ -315,60 +231,6 @@ function path.use_tree(tree) cfg.deploy_lib_dir = path.deploy_lib_dir(tree) end ---- Apply a given function to the active rocks trees based on chosen dependency mode. --- @param deps_mode string: Dependency mode: "one" for the current default tree, --- "all" for all trees, "order" for all trees with priority >= the current default, --- "none" for no trees (this function becomes a nop). --- @param fn function: function to be applied, with the tree dir (string) as the first --- argument and the remaining varargs of map_trees as the following arguments. --- @return a table with all results of invocations of fn collected. -function path.map_trees(deps_mode, fn, ...) - local result = {} - if deps_mode == "one" then - table.insert(result, (fn(cfg.root_dir, ...)) or 0) - elseif deps_mode == "all" or deps_mode == "order" then - local use = false - if deps_mode == "all" then - use = true - end - for _, tree in ipairs(cfg.rocks_trees) do - if dir.normalize(path.rocks_tree_to_string(tree)) == dir.normalize(path.rocks_tree_to_string(cfg.root_dir)) then - use = true - end - if use then - table.insert(result, (fn(tree, ...)) or 0) - end - end - end - return result -end - -local is_src_extension = { [".lua"] = true, [".tl"] = true, [".tld"] = true, [".moon"] = true } - ---- Return the pathname of the file that would be loaded for a module, indexed. --- @param file_name string: module file name as in manifest (eg. "socket/core.so") --- @param name string: name of the package (eg. "luasocket") --- @param version string: version number (eg. "2.0.2-1") --- @param tree string: repository path (eg. "/usr/local") --- @param i number: the index, 1 if version is the current default, > 1 otherwise. --- This is done this way for use by select_module in luarocks.loader. --- @return string: filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so") -function path.which_i(file_name, name, version, tree, i) - local deploy_dir - local extension = file_name:match("%.[a-z]+$") - if is_src_extension[extension] then - deploy_dir = path.deploy_lua_dir(tree) - file_name = dir.path(deploy_dir, file_name) - else - deploy_dir = path.deploy_lib_dir(tree) - file_name = dir.path(deploy_dir, file_name) - end - if i > 1 then - file_name = path.versioned_name(file_name, deploy_dir, name, version) - end - return file_name -end - --- Return the pathname of the file that would be loaded for a module, -- returning the versioned pathname if given version is not the default version -- in the given manifest. diff --git a/src/luarocks/path_cmd.lua b/src/luarocks/path_cmd.lua index c0329977..1f5bb07f 100644 --- a/src/luarocks/path_cmd.lua +++ b/src/luarocks/path_cmd.lua @@ -5,7 +5,7 @@ local path_cmd = {} local util = require("luarocks.util") local deps = require("luarocks.deps") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") path_cmd.help_summary = "Return the currently configured package path." path_cmd.help_arguments = "" diff --git a/src/luarocks/persist.lua b/src/luarocks/persist.lua index c2adb570..16ff5065 100644 --- a/src/luarocks/persist.lua +++ b/src/luarocks/persist.lua @@ -4,83 +4,10 @@ -- Implemented separately to avoid interdependencies, -- as it is used in the bootstrapping stage of the cfg module. local persist = {} -package.loaded["luarocks.persist"] = persist +setmetatable(persist, { __index = require("luarocks.core.persist") }) local util = require("luarocks.util") ---- Load and run a Lua file in an environment. --- @param filename string: the name of the file. --- @param env table: the environment table. --- @return (true, any) or (nil, string, string): true and the return value --- of the file, or nil, an error message and an error code ("open", "load" --- or "run") in case of errors. -local function run_file(filename, env) - local fd, err = io.open(filename) - if not fd then - return nil, err, "open" - end - local str, err = fd:read("*a") - fd:close() - if not str then - return nil, err, "open" - end - str = str:gsub("^#![^\n]*\n", "") - local chunk, ran - if _VERSION == "Lua 5.1" then -- Lua 5.1 - chunk, err = loadstring(str, filename) - if chunk then - setfenv(chunk, env) - ran, err = pcall(chunk) - end - else -- Lua 5.2 - chunk, err = load(str, filename, "t", env) - if chunk then - ran, err = pcall(chunk) - end - end - if not chunk then - return nil, "Error loading file: "..err, "load" - end - if not ran then - return nil, "Error running file: "..err, "run" - end - return true, err -end - ---- Load a Lua file containing assignments, storing them in a table. --- The global environment is not propagated to the loaded file. --- @param filename string: the name of the file. --- @param tbl table or nil: if given, this table is used to store --- loaded values. --- @return (table, table) or (nil, string, string): a table with the file's assignments --- as fields and set of undefined globals accessed in file, --- or nil, an error message and an error code ("open"; couldn't open the file, --- "load"; compile-time error, or "run"; run-time error) --- in case of errors. -function persist.load_into_table(filename, tbl) - assert(type(filename) == "string") - assert(type(tbl) == "table" or not tbl) - - local result = tbl or {} - local globals = {} - local globals_mt = { - __index = function(t, k) - globals[k] = true - end - } - local save_mt = getmetatable(result) - setmetatable(result, globals_mt) - - local ok, err, errcode = run_file(filename, result) - - setmetatable(result, save_mt) - - if not ok then - return nil, err, errcode - end - return result, globals -end - local write_table --- Write a value as Lua code. diff --git a/src/luarocks/purge.lua b/src/luarocks/purge.lua index 18043cc3..7647a243 100644 --- a/src/luarocks/purge.lua +++ b/src/luarocks/purge.lua @@ -2,7 +2,6 @@ --- Module implementing the LuaRocks "purge" command. -- Remove all rocks from a given tree. local purge = {} -package.loaded["luarocks.purge"] = purge local util = require("luarocks.util") local fs = require("luarocks.fs") @@ -10,8 +9,8 @@ local path = require("luarocks.path") local search = require("luarocks.search") local deps = require("luarocks.deps") local repos = require("luarocks.repos") -local manif = require("luarocks.manif") -local cfg = require("luarocks.cfg") +local writer = require("luarocks.manif.writer") +local cfg = require("luarocks.core.cfg") local remove = require("luarocks.remove") purge.help_summary = "Remove all installed rocks from a tree." @@ -55,7 +54,7 @@ function purge.command(flags) end for package, versions in util.sortedpairs(results) do - for version, repositories in util.sortedpairs(versions, sort) do + for version, _ in util.sortedpairs(versions, sort) do if flags["old-versions"] then util.printout("Keeping "..package.." "..version.."...") local ok, err = remove.remove_other_versions(package, version, flags["force"], flags["force-fast"]) @@ -72,7 +71,7 @@ function purge.command(flags) end end end - return manif.make_manifest(cfg.rocks_dir, "one") + return writer.make_manifest(cfg.rocks_dir, "one") end return purge diff --git a/src/luarocks/refresh_cache.lua b/src/luarocks/refresh_cache.lua deleted file mode 100644 index 1261044f..00000000 --- a/src/luarocks/refresh_cache.lua +++ /dev/null @@ -1,31 +0,0 @@ - ---- Module implementing the luarocks-admin "refresh_cache" command. -local refresh_cache = {} -package.loaded["luarocks.refresh_cache"] = refresh_cache - -local cfg = require("luarocks.cfg") -local cache = require("luarocks.cache") - -refresh_cache.help_summary = "Refresh local cache of a remote rocks server." -refresh_cache.help_arguments = "[--from=]" -refresh_cache.help = [[ -The flag --from indicates which server to use. -If not given, the default server set in the upload_server variable -from the configuration file is used instead. -]] - -function refresh_cache.command(flags) - local server, upload_server = cache.get_upload_server(flags["server"]) - if not server then return nil, upload_server end - local download_url = cache.get_server_urls(server, upload_server) - - local ok, err = cache.refresh_local_cache(server, download_url, cfg.upload_user, cfg.upload_password) - if not ok then - return nil, err - else - return true - end -end - - -return refresh_cache diff --git a/src/luarocks/remove.lua b/src/luarocks/remove.lua index 3f62e89e..e2c05176 100644 --- a/src/luarocks/remove.lua +++ b/src/luarocks/remove.lua @@ -2,7 +2,6 @@ --- Module implementing the LuaRocks "remove" command. -- Uninstalls rocks. local remove = {} -package.loaded["luarocks.remove"] = remove local search = require("luarocks.search") local deps = require("luarocks.deps") @@ -10,8 +9,8 @@ local fetch = require("luarocks.fetch") local repos = require("luarocks.repos") local path = require("luarocks.path") local util = require("luarocks.util") -local cfg = require("luarocks.cfg") -local manif = require("luarocks.manif") +local cfg = require("luarocks.core.cfg") +local writer = require("luarocks.manif.writer") local fs = require("luarocks.fs") remove.help_summary = "Uninstall a rock." @@ -113,7 +112,7 @@ function remove.remove_search_results(results, name, deps_mode, force, fast) local ok, err = delete_versions(name, versions) if not ok then return nil, err end - ok, err = manif.make_manifest(cfg.rocks_dir, deps_mode) + ok, err = writer.make_manifest(cfg.rocks_dir, deps_mode) if not ok then return nil, err end util.printout("Removal successful.") diff --git a/src/luarocks/repos.lua b/src/luarocks/repos.lua index 86126a13..7acb00b9 100644 --- a/src/luarocks/repos.lua +++ b/src/luarocks/repos.lua @@ -1,11 +1,10 @@ --- Functions for managing the repository on disk. local repos = {} -package.loaded["luarocks.repos"] = repos local fs = require("luarocks.fs") local path = require("luarocks.path") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local util = require("luarocks.util") local dir = require("luarocks.dir") local manif = require("luarocks.manif") diff --git a/src/luarocks/search.lua b/src/luarocks/search.lua index c3f00a7c..7d4f3e81 100644 --- a/src/luarocks/search.lua +++ b/src/luarocks/search.lua @@ -2,13 +2,13 @@ --- Module implementing the LuaRocks "search" command. -- Queries LuaRocks servers. local search = {} -package.loaded["luarocks.search"] = search + local dir = require("luarocks.dir") local path = require("luarocks.path") local manif = require("luarocks.manif") local deps = require("luarocks.deps") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local util = require("luarocks.util") search.help_summary = "Query the LuaRocks servers." diff --git a/src/luarocks/show.lua b/src/luarocks/show.lua index df992f5c..f0bf2164 100644 --- a/src/luarocks/show.lua +++ b/src/luarocks/show.lua @@ -1,10 +1,9 @@ --- Module implementing the LuaRocks "show" command. -- Shows information about an installed rock. local show = {} -package.loaded["luarocks.show"] = show local search = require("luarocks.search") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local util = require("luarocks.util") local path = require("luarocks.path") local deps = require("luarocks.deps") diff --git a/src/luarocks/type_check.lua b/src/luarocks/type_check.lua index 287d8151..382d3f0a 100644 --- a/src/luarocks/type_check.lua +++ b/src/luarocks/type_check.lua @@ -2,16 +2,12 @@ -- Functions and definitions for doing a basic lint check on files -- loaded by LuaRocks. local type_check = {} -package.loaded["luarocks.type_check"] = type_check - -local cfg = require("luarocks.cfg") -local deps = require("luarocks.deps") +setmetatable(type_check, { __index = require("luarocks.core.type_check") }) type_check.rockspec_format = "3.0" -local string_1 = { _type = "string" } -local number_1 = { _type = "number" } -local mandatory_string_1 = { _type = "string", _mandatory = true } +local string_1 = type_check.string_1 +local mandatory_string_1 = type_check.mandatory_string_1 -- Syntax for type-checking tables: -- @@ -112,203 +108,6 @@ rockspec_types.external_dependencies.platforms._any = rockspec_types.external_de rockspec_types.source.platforms._any = rockspec_types.source rockspec_types.hooks.platforms._any = rockspec_types.hooks -local manifest_types = { - repository = { - _mandatory = true, - -- packages - _any = { - -- versions - _any = { - -- items - _any = { - arch = mandatory_string_1, - modules = { _any = string_1 }, - commands = { _any = string_1 }, - dependencies = { _any = string_1 }, - -- TODO: to be extended with more metadata. - } - } - } - }, - modules = { - _mandatory = true, - -- modules - _any = { - -- providers - _any = string_1 - } - }, - commands = { - _mandatory = true, - -- modules - _any = { - -- commands - _any = string_1 - } - }, - dependencies = { - -- each module - _any = { - -- each version - _any = { - -- each dependency - _any = { - name = string_1, - constraints = { - _any = { - no_upgrade = { _type = "boolean" }, - op = string_1, - version = { - string = string_1, - _any = number_1, - } - } - } - } - } - } - } -} - -local function check_version(version, typetbl, context) - local typetbl_version = typetbl._version or "1.0" - if deps.compare_versions(typetbl_version, version) then - if context == "" then - return nil, "Invalid rockspec_format version number in rockspec? Please fix rockspec accordingly." - else - return nil, context.." is not supported in rockspec format "..version.." (requires version "..typetbl_version.."), please fix the rockspec_format field accordingly." - end - end - return true -end - -local type_check_table - ---- Type check an object. --- The object is compared against an archetypical value --- matching the expected type -- the actual values don't matter, --- only their types. Tables are type checked recursively. --- @param version string: The version of the item. --- @param item any: The object being checked. --- @param typetbl any: The type-checking table for the object. --- @param context string: A string indicating the "context" where the --- error occurred (the full table path), for error messages. --- @return boolean or (nil, string): true if type checking --- succeeded, or nil and an error message if it failed. --- @see type_check_table -local function type_check_item(version, item, typetbl, context) - assert(type(version) == "string") - - local ok, err = check_version(version, typetbl, context) - if not ok then - return nil, err - end - - local item_type = type(item) or "nil" - local expected_type = typetbl._type or "table" - - if expected_type == "number" then - if not tonumber(item) then - return nil, "Type mismatch on field "..context..": expected a number" - end - elseif expected_type == "string" then - if item_type ~= "string" then - return nil, "Type mismatch on field "..context..": expected a string, got "..item_type - end - if typetbl._pattern then - if not item:match("^"..typetbl._pattern.."$") then - return nil, "Type mismatch on field "..context..": invalid value "..item.." does not match '"..typetbl._pattern.."'" - end - end - elseif expected_type == "table" then - if item_type ~= expected_type then - return nil, "Type mismatch on field "..context..": expected a table" - else - return type_check_table(version, item, typetbl, context) - end - elseif item_type ~= expected_type then - return nil, "Type mismatch on field "..context..": expected "..expected_type - end - return true -end - -local function mkfield(context, field) - if context == "" then - return field - end - return context.."."..field -end - ---- Type check the contents of a table. --- The table's contents are compared against a reference table, --- which contains the recognized fields, with archetypical values --- matching the expected types -- the actual values of items in the --- reference table don't matter, only their types (ie, for field x --- in tbl that is correctly typed, type(tbl.x) == type(types.x)). --- If the reference table contains a field called MORE, then --- unknown fields in the checked table are accepted. --- If it contains a field called ANY, then its type will be --- used to check any unknown fields. If a field is prefixed --- with MUST_, it is mandatory; its absence from the table is --- a type error. --- Tables are type checked recursively. --- @param version string: The version of tbl. --- @param tbl table: The table to be type checked. --- @param typetbl table: The type-checking table, containing --- values for recognized fields in the checked table. --- @param context string: A string indicating the "context" where the --- error occurred (such as the name of the table the item is a part of), --- to be used by error messages. --- @return boolean or (nil, string): true if type checking --- succeeded, or nil and an error message if it failed. -type_check_table = function(version, tbl, typetbl, context) - assert(type(version) == "string") - assert(type(tbl) == "table") - assert(type(typetbl) == "table") - - local ok, err = check_version(version, typetbl, context) - if not ok then - return nil, err - end - - for k, v in pairs(tbl) do - local t = typetbl[k] or typetbl._any - if t then - local ok, err = type_check_item(version, v, t, mkfield(context, k)) - if not ok then return nil, err end - elseif typetbl._more then - -- Accept unknown field - else - if not cfg.accept_unknown_fields then - return nil, "Unknown field "..k - end - end - end - for k, v in pairs(typetbl) do - if k:sub(1,1) ~= "_" and v._mandatory then - if not tbl[k] then - return nil, "Mandatory field "..mkfield(context, k).." is missing." - end - end - end - return true -end - -local function check_undeclared_globals(globals, typetbl) - local undeclared = {} - for glob, _ in pairs(globals) do - if not (typetbl[glob] or typetbl["MUST_"..glob]) then - table.insert(undeclared, glob) - end - end - if #undeclared == 1 then - return nil, "Unknown variable: "..undeclared[1] - elseif #undeclared > 1 then - return nil, "Unknown variables: "..table.concat(undeclared, ", ") - end - return true -end - --- Type check a rockspec table. -- Verify the correctness of elements from a -- rockspec table, reporting on unknown fields and type @@ -320,22 +119,9 @@ function type_check.type_check_rockspec(rockspec, globals) if not rockspec.rockspec_format then rockspec.rockspec_format = "1.0" end - local ok, err = check_undeclared_globals(globals, rockspec_types) - if not ok then return nil, err end - return type_check_table(rockspec.rockspec_format, rockspec, rockspec_types, "") -end - ---- Type check a manifest table. --- Verify the correctness of elements from a --- manifest table, reporting on unknown fields and type --- mismatches. --- @return boolean or (nil, string): true if type checking --- succeeded, or nil and an error message if it failed. -function type_check.type_check_manifest(manifest, globals) - assert(type(manifest) == "table") - local ok, err = check_undeclared_globals(globals, manifest_types) + local ok, err = type_check.check_undeclared_globals(globals, rockspec_types) if not ok then return nil, err end - return type_check_table("1.0", manifest, manifest_types, "") + return type_check.type_check_table(rockspec.rockspec_format, rockspec, rockspec_types, "") end return type_check diff --git a/src/luarocks/unpack.lua b/src/luarocks/unpack.lua index 4e4d9a06..22b3f49b 100644 --- a/src/luarocks/unpack.lua +++ b/src/luarocks/unpack.lua @@ -2,14 +2,13 @@ --- Module implementing the LuaRocks "unpack" command. -- Unpack the contents of a rock. local unpack = {} -package.loaded["luarocks.unpack"] = unpack local fetch = require("luarocks.fetch") local fs = require("luarocks.fs") local util = require("luarocks.util") local build = require("luarocks.build") local dir = require("luarocks.dir") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") unpack.help_summary = "Unpack the contents of a rock." unpack.help_arguments = "[--force] {| []}" diff --git a/src/luarocks/upload.lua b/src/luarocks/upload.lua index 5031b1ef..4bda4303 100644 --- a/src/luarocks/upload.lua +++ b/src/luarocks/upload.lua @@ -4,7 +4,7 @@ local upload = {} local util = require("luarocks.util") local fetch = require("luarocks.fetch") local pack = require("luarocks.pack") -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local Api = require("luarocks.upload.api") upload.help_summary = "Upload a rockspec to the public rocks repository." diff --git a/src/luarocks/upload/api.lua b/src/luarocks/upload/api.lua index 6df24569..3e28bbda 100644 --- a/src/luarocks/upload/api.lua +++ b/src/luarocks/upload/api.lua @@ -1,7 +1,7 @@ local api = {} -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local fs = require("luarocks.fs") local util = require("luarocks.util") local persist = require("luarocks.persist") diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua index d27710c1..f41b1c38 100644 --- a/src/luarocks/util.lua +++ b/src/luarocks/util.lua @@ -2,26 +2,13 @@ --- Assorted utilities for managing tables, plus a scheduler for rollback functions. -- Does not requires modules directly (only as locals -- inside specific functions) to avoid interdependencies, --- as this is used in the bootstrapping stage of luarocks.cfg. +-- as this is used in the bootstrapping stage of luarocks.core.cfg. local util = {} +setmetatable(util, { __index = require("luarocks.core.util") }) local unpack = unpack or table.unpack ---- Run a process and read a its output. --- Equivalent to io.popen(cmd):read("*l"), except that it --- closes the fd right away. --- @param cmd string: The command to execute --- @param spec string: "*l" by default, to read a single line. --- May be used to read more, passing, for instance, "*a". --- @return string: the output of the program. -function util.popen_read(cmd, spec) - local fd = io.popen(cmd) - local out = fd:read(spec or "*l") - fd:close() - return out -end - local scheduled_functions = {} local debug = require("debug") @@ -210,27 +197,6 @@ function util.parse_flags(...) return flags, unpack(out) end ---- Merges contents of src on top of dst's contents. --- @param dst Destination table, which will receive src's contents. --- @param src Table which provides new contents to dst. --- @see platform_overrides -function util.deep_merge(dst, src) - for k, v in pairs(src) do - if type(v) == "table" then - if not dst[k] then - dst[k] = {} - end - if type(dst[k]) == "table" then - util.deep_merge(dst[k], v) - else - dst[k] = v - end - else - dst[k] = v - end - end -end - --- Perform platform-specific overrides on a table. -- Overrides values of table with the contents of the appropriate -- subset of its "platforms" field. The "platforms" field should @@ -247,7 +213,7 @@ end function util.platform_overrides(tbl) assert(type(tbl) == "table" or not tbl) - local cfg = require("luarocks.cfg") + local cfg = require("luarocks.core.cfg") if not tbl then return end @@ -264,19 +230,6 @@ end local var_format_pattern = "%$%((%a[%a%d_]+)%)" ---- Create a new shallow copy of a table: a new table with --- the same keys and values. Keys point to the same objects as --- the original table (ie, does not copy recursively). --- @param tbl table: the input table --- @return table: a new table with the same contents. -function util.make_shallow_copy(tbl) - local copy = {} - for k,v in pairs(tbl) do - copy[k] = v - end - return copy -end - -- Check if a set of needed variables are referenced -- somewhere in a list of definitions, warning the user -- about any unused ones. Each key in needed_set should @@ -339,17 +292,6 @@ function util.variable_substitutions(tbl, vars) end end ---- Return an array of keys of a table. --- @param tbl table: The input table. --- @return table: The array of keys. -function util.keys(tbl) - local ks = {} - for k,_ in pairs(tbl) do - table.insert(ks, k) - end - return ks -end - local function default_sort(a, b) local ta = type(a) local tb = type(b) @@ -431,12 +373,6 @@ function util.printout(...) io.stdout:write("\n") end ---- Print a line to standard error -function util.printerr(...) - io.stderr:write(table.concat({...},"\t")) - io.stderr:write("\n") -end - --- Display a warning message. -- @param msg string: the warning message function util.warning(msg) @@ -465,7 +401,7 @@ function util.this_program(default) end function util.deps_mode_help(program) - local cfg = require("luarocks.cfg") + local cfg = require("luarocks.core.cfg") return [[ --deps-mode= How to handle dependencies. Four modes are supported: * all - use all trees from the rocks_trees list @@ -488,7 +424,7 @@ function util.see_help(command, program) end function util.announce_install(rockspec) - local cfg = require("luarocks.cfg") + local cfg = require("luarocks.core.cfg") local path = require("luarocks.path") local suffix = "" @@ -566,135 +502,6 @@ function util.get_default_rockspec() end end --- from http://lua-users.org/wiki/SplitJoin --- by PhilippeLhoste -function util.split_string(str, delim, maxNb) - -- Eliminate bad cases... - if string.find(str, delim) == nil then - return { str } - end - if maxNb == nil or maxNb < 1 then - maxNb = 0 -- No limit - end - local result = {} - local pat = "(.-)" .. delim .. "()" - local nb = 0 - local lastPos - for part, pos in string.gmatch(str, pat) do - nb = nb + 1 - result[nb] = part - lastPos = pos - if nb == maxNb then break end - end - -- Handle the last field - if nb ~= maxNb then - result[nb + 1] = string.sub(str, lastPos) - end - return result -end - ---- Remove repeated entries from a path-style string. --- Example: given ("a;b;c;a;b;d", ";"), returns "a;b;c;d". --- @param list string: A path string (from $PATH or package.path) --- @param sep string: The separator -function util.remove_path_dupes(list, sep) - assert(type(list) == "string") - assert(type(sep) == "string") - local parts = util.split_string(list, sep) - local final, entries = {}, {} - for _, part in ipairs(parts) do - part = part:gsub("//", "/") - if not entries[part] then - table.insert(final, part) - entries[part] = true - end - end - return table.concat(final, sep) -end - ---- --- Formats tables with cycles recursively to any depth. --- References to other tables are shown as values. --- Self references are indicated. --- The string returned is "Lua code", which can be procesed --- (in the case in which indent is composed by spaces or "--"). --- Userdata and function keys and values are shown as strings, --- which logically are exactly not equivalent to the original code. --- This routine can serve for pretty formating tables with --- proper indentations, apart from printing them: --- io.write(table.show(t, "t")) -- a typical use --- Written by Julio Manuel Fernandez-Diaz, --- Heavily based on "Saving tables with cycles", PIL2, p. 113. --- @param t table: is the table. --- @param name string: is the name of the table (optional) --- @param indent string: is a first indentation (optional). --- @return string: the pretty-printed table -function util.show_table(t, name, indent) - local cart -- a container - local autoref -- for self references - - local function isemptytable(t) return next(t) == nil end - - local function basicSerialize (o) - local so = tostring(o) - if type(o) == "function" then - local info = debug.getinfo(o, "S") - -- info.name is nil because o is not a calling level - if info.what == "C" then - return ("%q"):format(so .. ", C function") - else - -- the information is defined through lines - return ("%q"):format(so .. ", defined in (" .. info.linedefined .. "-" .. info.lastlinedefined .. ")" .. info.source) - end - elseif type(o) == "number" then - return so - else - return ("%q"):format(so) - end - end - - local function addtocart (value, name, indent, saved, field) - indent = indent or "" - saved = saved or {} - field = field or name - - cart = cart .. indent .. field - - if type(value) ~= "table" then - cart = cart .. " = " .. basicSerialize(value) .. ";\n" - else - if saved[value] then - cart = cart .. " = {}; -- " .. saved[value] .. " (self reference)\n" - autoref = autoref .. name .. " = " .. saved[value] .. ";\n" - else - saved[value] = name - --if tablecount(value) == 0 then - if isemptytable(value) then - cart = cart .. " = {};\n" - else - cart = cart .. " = {\n" - for k, v in pairs(value) do - k = basicSerialize(k) - local fname = ("%s[%s]"):format(name, k) - field = ("[%s]"):format(k) - -- three spaces between levels - addtocart(v, fname, indent .. " ", saved, field) - end - cart = cart .. indent .. "};\n" - end - end - end - end - - name = name or "__unnamed__" - if type(t) ~= "table" then - return name .. " = " .. basicSerialize(t) - end - cart, autoref = "", "" - addtocart(t, name, indent) - return cart .. autoref -end - function util.array_contains(tbl, value) for _, v in ipairs(tbl) do if v == value then diff --git a/src/luarocks/validate.lua b/src/luarocks/validate.lua deleted file mode 100644 index f0452bbd..00000000 --- a/src/luarocks/validate.lua +++ /dev/null @@ -1,158 +0,0 @@ - ---- Sandboxed test of build/install of all packages in a repository (unfinished and disabled). -local validate = {} -package.loaded["luarocks.validate"] = validate - -local fs = require("luarocks.fs") -local dir = require("luarocks.dir") -local path = require("luarocks.path") -local cfg = require("luarocks.cfg") -local build = require("luarocks.build") -local install = require("luarocks.install") -local util = require("luarocks.util") - -validate.help_summary = "Sandboxed test of build/install of all packages in a repository." - -validate.help = [[ -, if given, is a local repository pathname. -]] - -local function save_settings(repo) - local protocol, path = dir.split_url(repo) - table.insert(cfg.rocks_servers, 1, protocol.."://"..path) - return { - root_dir = cfg.root_dir, - rocks_dir = cfg.rocks_dir, - deploy_bin_dir = cfg.deploy_bin_dir, - deploy_lua_dir = cfg.deploy_lua_dir, - deploy_lib_dir = cfg.deploy_lib_dir, - } -end - -local function restore_settings(settings) - cfg.root_dir = settings.root_dir - cfg.rocks_dir = settings.rocks_dir - cfg.deploy_bin_dir = settings.deploy_bin_dir - cfg.deploy_lua_dir = settings.deploy_lua_dir - cfg.deploy_lib_dir = settings.deploy_lib_dir - cfg.variables.ROCKS_TREE = settings.rocks_dir - cfg.variables.SCRIPTS_DIR = settings.deploy_bin_dir - table.remove(cfg.rocks_servers, 1) -end - -local function prepare_sandbox(file) - local root_dir = fs.make_temp_dir(file):gsub("/+$", "") - cfg.root_dir = root_dir - cfg.rocks_dir = path.rocks_dir(root_dir) - cfg.deploy_bin_dir = path.deploy_bin_dir(root_dir) - cfg.variables.ROCKS_TREE = cfg.rocks_dir - cfg.variables.SCRIPTS_DIR = cfg.deploy_bin_dir - return root_dir -end - -local function validate_rockspec(file) - local ok, err, errcode = build.build_rockspec(file, true, "one") - if not ok then - util.printerr(err) - end - return ok, err, errcode -end - -local function validate_src_rock(file) - local ok, err, errcode = build.build_rock(file, false, "one") - if not ok then - util.printerr(err) - end - return ok, err, errcode -end - -local function validate_rock(file) - local ok, err, errcode = install.install_binary_rock(file, "one") - if not ok then - util.printerr(err) - end - return ok, err, errcode -end - -function validate.command(flags, repo) - repo = repo or cfg.rocks_dir - - util.printout("Verifying contents of "..repo) - - local results = { - ok = {} - } - local settings = save_settings(repo) - local sandbox - if flags["quick"] then - sandbox = prepare_sandbox("luarocks_validate") - end - if not fs.exists(repo) then - return nil, repo.." is not a local repository." - end - for file in fs.dir(repo) do for _=1,1 do - if file == "manifest" or file == "index.html" then - break -- continue for - end - local pathname = fs.absolute_name(dir.path(repo, file)) - if not flags["quick"] then - sandbox = prepare_sandbox(file) - end - local ok, err, errcode - util.printout() - util.printout("Verifying "..pathname) - if file:match("%.rockspec$") then - ok, err, errcode = validate_rockspec(pathname, "one") - elseif file:match("%.src%.rock$") then - ok, err, errcode = validate_src_rock(pathname) - elseif file:match("%.rock$") then - ok, err, errcode = validate_rock(pathname) - end - if ok then - table.insert(results.ok, {file=file} ) - else - if not errcode then - errcode = "misc" - end - if not results[errcode] then - results[errcode] = {} - end - table.insert(results[errcode], {file=file, err=err} ) - end - util.run_scheduled_functions() - if not flags["quick"] then - fs.delete(sandbox) - end - repeat until not fs.pop_dir() - end end - if flags["quick"] then - fs.delete(sandbox) - end - restore_settings(settings) - util.title("Results:") - util.printout("OK: "..tostring(#results.ok)) - for _, entry in ipairs(results.ok) do - util.printout(entry.file) - end - for errcode, errors in pairs(results) do - if errcode ~= "ok" then - util.printout() - util.printout(errcode.." errors: "..tostring(#errors)) - for _, entry in ipairs(errors) do - util.printout(entry.file, entry.err) - end - end - end - - util.title("Summary:") - local total = 0 - for errcode, errors in pairs(results) do - util.printout(errcode..": "..tostring(#errors)) - total = total + #errors - end - util.printout("Total: "..total) - return true -end - - -return validate diff --git a/src/luarocks/write_rockspec.lua b/src/luarocks/write_rockspec.lua index 68c00cce..d6075dbd 100644 --- a/src/luarocks/write_rockspec.lua +++ b/src/luarocks/write_rockspec.lua @@ -1,8 +1,7 @@ local write_rockspec = {} -package.loaded["luarocks.write_rockspec"] = write_rockspec -local cfg = require("luarocks.cfg") +local cfg = require("luarocks.core.cfg") local dir = require("luarocks.dir") local fetch = require("luarocks.fetch") local fs = require("luarocks.fs") diff --git a/test/testing.sh b/test/testing.sh index e4b5d8c2..0dd34bf1 100755 --- a/test/testing.sh +++ b/test/testing.sh @@ -577,7 +577,6 @@ fail_config_system_config() { rm -f "$testing_lrprefix/etc/luarocks/config.lua"; test_config_system_config() { mkdir -p "$testing_lrprefix/etc/luarocks"; touch "$testing_lrprefix/etc/luarocks/config.lua"; $luarocks config --system-config; err=$?; rm -f "$testing_lrprefix/etc/luarocks/config.lua"; return $err; } fail_config_system_config_invalid() { mkdir -p "$testing_lrprefix/etc/luarocks"; echo "if if if" > "$testing_lrprefix/etc/luarocks/config.lua"; $luarocks config --system-config; err=$?; rm -f "$testing_lrprefix/etc/luarocks/config.lua"; return $err; } test_config_user_config() { $luarocks config --user-config; } -fail_config_user_config() { LUAROCKS_CONFIG="/missing_file.lua" $luarocks config --user-config; } test_config_rock_trees() { $luarocks config --rock-trees; } test_config_help() { $luarocks help config; } -- cgit v1.2.3-55-g6feb From 813ad75a7f87d96bd372e7c2eca6a677009722a2 Mon Sep 17 00:00:00 2001 From: Hisham Date: Thu, 28 Jul 2016 17:24:25 -0300 Subject: Fix field name building in type_check.lua Port fix by @mpeterv in 5f069526966ae37319ba745644a43e0cf64ad62c --- src/luarocks/core/type_check.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/luarocks/core/type_check.lua b/src/luarocks/core/type_check.lua index a2e14efd..1822312b 100644 --- a/src/luarocks/core/type_check.lua +++ b/src/luarocks/core/type_check.lua @@ -134,9 +134,12 @@ end local function mkfield(context, field) if context == "" then - return field + return tostring(field) + elseif type(field) == "string" then + return context.."."..field + else + return context.."["..tostring(field).."]" end - return context.."."..field end --- Type check the contents of a table. -- cgit v1.2.3-55-g6feb From 628779b5a2357685221f62141bec22c0e84a24f1 Mon Sep 17 00:00:00 2001 From: Hisham Date: Thu, 28 Jul 2016 18:15:13 -0300 Subject: Move sortedpairs to core; used by luarocks.loader. --- src/luarocks/core/util.lua | 63 ++++++++++++++++++++++++++++++++++++++++++++++ src/luarocks/util.lua | 62 --------------------------------------------- 2 files changed, 63 insertions(+), 62 deletions(-) diff --git a/src/luarocks/core/util.lua b/src/luarocks/core/util.lua index 4f896b17..f2e409d1 100644 --- a/src/luarocks/core/util.lua +++ b/src/luarocks/core/util.lua @@ -198,5 +198,68 @@ function util.printerr(...) io.stderr:write("\n") end +--- Simple sort function used as a default for util.sortedpairs. +local function default_sort(a, b) + local ta = type(a) + local tb = type(b) + if ta == "number" and tb == "number" then + return a < b + elseif ta == "number" then + return true + elseif tb == "number" then + return false + else + return tostring(a) < tostring(b) + end +end + +--- The iterator function used internally by util.sortedpairs. +-- @param tbl table: The table to be iterated. +-- @param sort_function function or nil: An optional comparison function +-- to be used by table.sort when sorting keys. +-- @see sortedpairs +local function sortedpairs_iterator(tbl, sort_function) + local ks = util.keys(tbl) + if not sort_function or type(sort_function) == "function" then + table.sort(ks, sort_function or default_sort) + for _, k in ipairs(ks) do + coroutine.yield(k, tbl[k]) + end + else + local order = sort_function + local done = {} + for _, k in ipairs(order) do + local sub_order + if type(k) == "table" then + sub_order = k[2] + k = k[1] + end + if tbl[k] then + done[k] = true + coroutine.yield(k, tbl[k], sub_order) + end + end + table.sort(ks, default_sort) + for _, k in ipairs(ks) do + if not done[k] then + coroutine.yield(k, tbl[k]) + end + end + end +end + +--- A table iterator generator that returns elements sorted by key, +-- to be used in "for" loops. +-- @param tbl table: The table to be iterated. +-- @param sort_function function or table or nil: An optional comparison function +-- to be used by table.sort when sorting keys, or an array listing an explicit order +-- for keys. If a value itself is an array, it is taken so that the first element +-- is a string representing the field name, and the second element is a priority table +-- for that key. +-- @return function: the iterator function. +function util.sortedpairs(tbl, sort_function) + return coroutine.wrap(function() sortedpairs_iterator(tbl, sort_function) end) +end + return util diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua index f41b1c38..9760fafd 100644 --- a/src/luarocks/util.lua +++ b/src/luarocks/util.lua @@ -292,68 +292,6 @@ function util.variable_substitutions(tbl, vars) end end -local function default_sort(a, b) - local ta = type(a) - local tb = type(b) - if ta == "number" and tb == "number" then - return a < b - elseif ta == "number" then - return true - elseif tb == "number" then - return false - else - return tostring(a) < tostring(b) - end -end - --- The iterator function used internally by util.sortedpairs. --- @param tbl table: The table to be iterated. --- @param sort_function function or nil: An optional comparison function --- to be used by table.sort when sorting keys. --- @see sortedpairs -local function sortedpairs_iterator(tbl, sort_function) - local ks = util.keys(tbl) - if not sort_function or type(sort_function) == "function" then - table.sort(ks, sort_function or default_sort) - for _, k in ipairs(ks) do - coroutine.yield(k, tbl[k]) - end - else - local order = sort_function - local done = {} - for _, k in ipairs(order) do - local sub_order - if type(k) == "table" then - sub_order = k[2] - k = k[1] - end - if tbl[k] then - done[k] = true - coroutine.yield(k, tbl[k], sub_order) - end - end - table.sort(ks, default_sort) - for _, k in ipairs(ks) do - if not done[k] then - coroutine.yield(k, tbl[k]) - end - end - end -end - ---- A table iterator generator that returns elements sorted by key, --- to be used in "for" loops. --- @param tbl table: The table to be iterated. --- @param sort_function function or table or nil: An optional comparison function --- to be used by table.sort when sorting keys, or an array listing an explicit order --- for keys. If a value itself is an array, it is taken so that the first element --- is a string representing the field name, and the second element is a priority table --- for that key. --- @return function: the iterator function. -function util.sortedpairs(tbl, sort_function) - return coroutine.wrap(function() sortedpairs_iterator(tbl, sort_function) end) -end - function util.lua_versions() local versions = { "5.1", "5.2", "5.3" } local i = 0 -- cgit v1.2.3-55-g6feb From 83b0f6d2343f2631b9dc96c09c2bc4b2cfadd17f Mon Sep 17 00:00:00 2001 From: Hisham Date: Thu, 28 Jul 2016 18:15:35 -0300 Subject: Tweak tests for luarocks-3 branch. --- .travis.yml | 2 +- src/bin/luarocks | 3 ++- src/bin/luarocks-admin | 3 ++- test/test_environment.lua | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 02bdb6c9..86f7bbad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ matrix: before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; brew install python; fi - pip install hererocks - - hererocks lua_install -r^ --$LUA + - hererocks lua_install --luarocks @luarocks-3 --$LUA - export PATH=$PATH:$PWD/lua_install/bin # Add directory with all installed binaries to PATH install: diff --git a/src/bin/luarocks b/src/bin/luarocks index 21f17da9..633f9f43 100755 --- a/src/bin/luarocks +++ b/src/bin/luarocks @@ -1,4 +1,5 @@ -#!/usr/bin/env lua +#!/Programs/Lua/5.3.0/bin/lua +package.path = [[/Users/hisham/luarocks/src/?.lua;]]..package.path -- this should be loaded first. local cfg = require("luarocks.core.cfg") diff --git a/src/bin/luarocks-admin b/src/bin/luarocks-admin index 660c0a70..587afcab 100755 --- a/src/bin/luarocks-admin +++ b/src/bin/luarocks-admin @@ -1,4 +1,5 @@ -#!/usr/bin/env lua +#!/Programs/Lua/5.3.0/bin/lua +package.path = [[/Users/hisham/luarocks/src/?.lua;]]..package.path -- this should be loaded first. local cfg = require("luarocks.core.cfg") diff --git a/test/test_environment.lua b/test/test_environment.lua index 13e548f9..c26c1e87 100644 --- a/test/test_environment.lua +++ b/test/test_environment.lua @@ -361,7 +361,7 @@ local function reset_environment(testing_paths, md5sums) end local function create_paths(luaversion_full) - local cfg = require("luarocks.cfg") + local cfg = require("luarocks.core.cfg") local testing_paths = {} testing_paths.luadir = cfg.variables.LUA_BINDIR:gsub("/bin/?$", "") @@ -409,7 +409,7 @@ function test_env.setup_specs(extra_rocks) test_env.main() package.path = test_env.env_variables.LUA_PATH - test_env.platform = execute_output(test_env.testing_paths.lua .. " -e 'print(require(\"luarocks.cfg\").arch)'", false, test_env.env_variables) + test_env.platform = execute_output(test_env.testing_paths.lua .. " -e 'print(require(\"luarocks.core.cfg\").arch)'", false, test_env.env_variables) test_env.md5sums = create_md5sums(test_env.testing_paths) test_env.setup_done = true title("RUNNING TESTS") -- cgit v1.2.3-55-g6feb From dbca97cdcc15e386554b2631a0ae7aca02500abf Mon Sep 17 00:00:00 2001 From: Hisham Date: Thu, 28 Jul 2016 18:24:44 -0300 Subject: Fix download in test --- spec/unpack_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unpack_spec.lua b/spec/unpack_spec.lua index 495c514e..8db779c3 100644 --- a/spec/unpack_spec.lua +++ b/spec/unpack_spec.lua @@ -49,7 +49,7 @@ describe("LuaRocks unpack tests #blackbox #b_unpack", function() end) -- #595 luarocks unpack of a git:// rockspec fails to copy the rockspec it("LuaRocks unpack git:// rockspec", function() - assert.is_true(run.luarocks_bool("download --rockspec cprint")) + assert.is_true(run.luarocks_bool("download --rockspec luazip")) assert.is_true(run.luarocks_bool("unpack luazip-1.2.4-1.rockspec")) assert.is_truthy(lfs.attributes("luazip-1.2.4-1/luazip/luazip-1.2.4-1.rockspec")) test_env.remove_dir("luazip-1.2.4-1") -- cgit v1.2.3-55-g6feb From cf94893bbd540d2ed62642ecf0938b7954ef13c6 Mon Sep 17 00:00:00 2001 From: Hisham Date: Fri, 14 Oct 2016 17:09:38 -0700 Subject: Add labels and issues_url metadata --- src/luarocks/show.lua | 8 ++++++++ src/luarocks/type_check.lua | 5 +++++ src/luarocks/util.lua | 2 ++ 3 files changed, 15 insertions(+) diff --git a/src/luarocks/show.lua b/src/luarocks/show.lua index 66ac6448..53d8ffa5 100644 --- a/src/luarocks/show.lua +++ b/src/luarocks/show.lua @@ -94,6 +94,8 @@ function show.command(flags, name, version) if flags["rock-tree"] then util.printout(path.rocks_tree_to_string(repo)) elseif flags["rock-dir"] then util.printout(directory) elseif flags["home"] then util.printout(descript.homepage) + elseif flags["issues"] then util.printout(descript.issues_url) + elseif flags["labels"] then util.printout(descript.labels and table.concat(descript.labels, "\n")) elseif flags["modules"] then util.printout(keys_as_string(minfo.modules, "\n")) elseif flags["deps"] then util.printout(keys_as_string(minfo.dependencies)) elseif flags["rockspec"] then util.printout(rockspec_file) @@ -112,6 +114,12 @@ function show.command(flags, name, version) if descript.homepage then util.printout("Homepage: ", descript.homepage) end + if descript.issues_url then + util.printout("Issues: ", descript.issues_url) + end + if descript.labels then + util.printout("Labels: ", table.concat(descript.labels, ", ")) + end util.printout("Installed in: ", path.rocks_tree_to_string(repo)) if next(minfo.modules) then util.printout() diff --git a/src/luarocks/type_check.lua b/src/luarocks/type_check.lua index 382d3f0a..4b7e68d7 100644 --- a/src/luarocks/type_check.lua +++ b/src/luarocks/type_check.lua @@ -9,6 +9,9 @@ type_check.rockspec_format = "3.0" local string_1 = type_check.string_1 local mandatory_string_1 = type_check.mandatory_string_1 +local string_3 = { _type = "string", _version = "3.0" } +local list_of_strings_3 = { _any = string_3, _version = "3.0" } + -- Syntax for type-checking tables: -- -- A type-checking table describes typing data for a value. @@ -33,6 +36,8 @@ local rockspec_types = { homepage = string_1, license = string_1, maintainer = string_1, + labels = list_of_strings_3, + issues_url = string_3, }, dependencies = { platforms = {}, -- recursively defined below diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua index 9760fafd..b23f4fab 100644 --- a/src/luarocks/util.lua +++ b/src/luarocks/util.lua @@ -87,7 +87,9 @@ local supported_flags = { ["help"] = true, ["home"] = true, ["homepage"] = "\"\"", + ["issues"] = true, ["keep"] = true, + ["labels"] = true, ["lib"] = "", ["license"] = "\"\"", ["list"] = true, -- cgit v1.2.3-55-g6feb From 1799cf40199bc27658fe28b3e6d35744162a65da Mon Sep 17 00:00:00 2001 From: Hisham Date: Fri, 14 Oct 2016 17:12:18 -0700 Subject: Add tests for new flags --- spec/show_spec.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/show_spec.lua b/spec/show_spec.lua index b2cdc07e..b95ea818 100644 --- a/spec/show_spec.lua +++ b/spec/show_spec.lua @@ -49,6 +49,14 @@ describe("LuaRocks show tests #blackbox #b_show", function() it("LuaRocks show rock directory of luacov", function() local output = run.luarocks("show --rock-dir luacov") end) + + it("LuaRocks show issues URL of luacov", function() + local output = run.luarocks("show --issues luacov") + end) + + it("LuaRocks show labels of luacov", function() + local output = run.luarocks("show --labels luacov") + end) end) it("LuaRocks show old version of luacov", function() -- cgit v1.2.3-55-g6feb From e7b09524fbd9f2880621063dd4670d9f92cb3a4b Mon Sep 17 00:00:00 2001 From: Hisham Date: Sat, 15 Oct 2016 10:12:27 -0700 Subject: Add missing file in test repo --- spec/unpack_spec.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/unpack_spec.lua b/spec/unpack_spec.lua index a0fc370d..d629e225 100644 --- a/spec/unpack_spec.lua +++ b/spec/unpack_spec.lua @@ -6,7 +6,8 @@ test_env.unload_luarocks() local extra_rocks = { "/cprint-0.1-2.src.rock", - "/cprint-0.1-2.rockspec" + "/cprint-0.1-2.rockspec", + "/luazip-1.2.4-1.rockspec" } describe("LuaRocks unpack tests #blackbox #b_unpack", function() -- cgit v1.2.3-55-g6feb From a47f54bb139aa3b932d807e89a0691dfdfaa9723 Mon Sep 17 00:00:00 2001 From: Hisham Date: Sat, 15 Oct 2016 10:16:01 -0700 Subject: Oops, my dev environment leaked in a commit, sorry. --- src/bin/luarocks | 3 +-- src/bin/luarocks-admin | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/bin/luarocks b/src/bin/luarocks index 633f9f43..21f17da9 100755 --- a/src/bin/luarocks +++ b/src/bin/luarocks @@ -1,5 +1,4 @@ -#!/Programs/Lua/5.3.0/bin/lua -package.path = [[/Users/hisham/luarocks/src/?.lua;]]..package.path +#!/usr/bin/env lua -- this should be loaded first. local cfg = require("luarocks.core.cfg") diff --git a/src/bin/luarocks-admin b/src/bin/luarocks-admin index 587afcab..660c0a70 100755 --- a/src/bin/luarocks-admin +++ b/src/bin/luarocks-admin @@ -1,5 +1,4 @@ -#!/Programs/Lua/5.3.0/bin/lua -package.path = [[/Users/hisham/luarocks/src/?.lua;]]..package.path +#!/usr/bin/env lua -- this should be loaded first. local cfg = require("luarocks.core.cfg") -- cgit v1.2.3-55-g6feb From 67767955a9f175a4db5c3c0a08ca52203bb6d9c1 Mon Sep 17 00:00:00 2001 From: Hisham Date: Wed, 19 Oct 2016 00:53:07 -0400 Subject: Use luarocks-3 branch on Appveyor --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 32dc3e14..a7cf1be6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -38,7 +38,7 @@ init: before_build: - set PATH=C:\Python27\Scripts;%PATH% # Add directory containing 'pip' to PATH - pip install hererocks - - hererocks env --%LUA% -rlatest --target=%COMPILER% + - hererocks env --%LUA% --luarocks @luarocks-3 --target=%COMPILER% - call env\bin\activate build_script: -- cgit v1.2.3-55-g6feb From 4958f45fc46ce9bf22a1487413c8290ae27b9082 Mon Sep 17 00:00:00 2001 From: Hisham Date: Wed, 19 Oct 2016 12:42:25 -0400 Subject: Avoid circular dependency. --- src/luarocks/repos.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/luarocks/repos.lua b/src/luarocks/repos.lua index a5698b8f..f1d58da0 100644 --- a/src/luarocks/repos.lua +++ b/src/luarocks/repos.lua @@ -8,7 +8,6 @@ local cfg = require("luarocks.core.cfg") local util = require("luarocks.util") local dir = require("luarocks.dir") local manif = require("luarocks.manif") -local writer = require("luarocks.manif.writer") local deps = require("luarocks.deps") --- Get all installed versions of a package. @@ -332,6 +331,7 @@ function repos.delete_version(name, version, deps_mode, quick) return true end + local writer = require("luarocks.manif.writer") return writer.make_manifest(cfg.rocks_dir, deps_mode) end -- cgit v1.2.3-55-g6feb From 66a4d40893199fbee5eed5abd3c91aad0a3cce2b Mon Sep 17 00:00:00 2001 From: Hisham Date: Wed, 19 Oct 2016 12:43:41 -0400 Subject: Refactor error message and add extra checks. --- src/luarocks/manif.lua | 4 +++- src/luarocks/manif/writer.lua | 6 ++---- src/luarocks/pack.lua | 6 ++---- src/luarocks/repos.lua | 17 +++++++++-------- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/luarocks/manif.lua b/src/luarocks/manif.lua index 680b5cfe..bd6fd6ee 100644 --- a/src/luarocks/manif.lua +++ b/src/luarocks/manif.lua @@ -25,7 +25,9 @@ function manif.load_rock_manifest(name, version, root) end local pathname = path.rock_manifest_file(name, version, root) local rock_manifest = persist.load_into_table(pathname) - if not rock_manifest then return nil end + if not rock_manifest then + return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks tree?" + end manif.rock_manifest_cache[name_version] = rock_manifest return rock_manifest.rock_manifest end diff --git a/src/luarocks/manif/writer.lua b/src/luarocks/manif/writer.lua index 75ea3b81..1eb5ee7c 100644 --- a/src/luarocks/manif/writer.lua +++ b/src/luarocks/manif/writer.lua @@ -189,10 +189,8 @@ local function store_results(results, manifest, dep_handler) local entrytable = {} entrytable.arch = entry.arch if entry.arch == "installed" then - local rock_manifest = manif.load_rock_manifest(name, version) - if not rock_manifest then - return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?" - end + local rock_manifest, err = manif.load_rock_manifest(name, version) + if not rock_manifest then return nil, err end entrytable.modules = store_package_items(repos.package_modules, name, version, manifest.modules) entrytable.commands = store_package_items(repos.package_commands, name, version, manifest.commands) end diff --git a/src/luarocks/pack.lua b/src/luarocks/pack.lua index 989d49ee..49c68e85 100644 --- a/src/luarocks/pack.lua +++ b/src/luarocks/pack.lua @@ -102,10 +102,8 @@ local function do_pack_binary_rock(name, version, tree) return nil, "'"..name.." "..version.."' does not seem to be an installed rock." end - local rock_manifest = manif.load_rock_manifest(name, version, root) - if not rock_manifest then - return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?" - end + local rock_manifest, err = manif.load_rock_manifest(name, version, root) + if not rock_manifest then return nil, err end local name_version = name .. "-" .. version local rock_file = fs.absolute_name(name_version .. "."..cfg.arch..".rock") diff --git a/src/luarocks/repos.lua b/src/luarocks/repos.lua index f1d58da0..0ed9068c 100644 --- a/src/luarocks/repos.lua +++ b/src/luarocks/repos.lua @@ -78,6 +78,7 @@ function repos.package_modules(package, version) local result = {} local rock_manifest = manif.load_rock_manifest(package, version) + if not rock_manifest then return result end store_package_data(result, package, rock_manifest.lib) store_package_data(result, package, rock_manifest.lua) return result @@ -97,6 +98,7 @@ function repos.package_commands(package, version) local result = {} local rock_manifest = manif.load_rock_manifest(package, version) + if not rock_manifest then return result end store_package_data(result, package, rock_manifest.bin) return result end @@ -112,7 +114,7 @@ function repos.has_binaries(name, version) assert(type(version) == "string") local rock_manifest = manif.load_rock_manifest(name, version) - if rock_manifest.bin then + if rock_manifest and rock_manifest.bin then for name, md5 in pairs(rock_manifest.bin) do -- TODO verify that it is the same file. If it isn't, find the actual command. if fs.is_actual_binary(dir.path(cfg.deploy_bin_dir, name)) then @@ -196,7 +198,7 @@ function repos.deploy_files(name, version, wrap_bin_scripts) local function deploy_file_tree(file_tree, path_fn, deploy_dir, move_fn) local source_dir = path_fn(name, version) - return recurse_rock_manifest_tree(file_tree, + return recurse_rock_manifest_tree(file_tree, function(parent_path, parent_module, file) local source = dir.path(source_dir, parent_path, file) local target = dir.path(deploy_dir, parent_path, file) @@ -226,7 +228,8 @@ function repos.deploy_files(name, version, wrap_bin_scripts) ) end - local rock_manifest = manif.load_rock_manifest(name, version) + local rock_manifest, err = manif.load_rock_manifest(name, version) + if not rock_manifest then return nil, err end local ok, err = true if rock_manifest.bin then @@ -305,12 +308,10 @@ function repos.delete_version(name, version, deps_mode, quick) ) end - local rock_manifest = manif.load_rock_manifest(name, version) - if not rock_manifest then - return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?" - end + local rock_manifest, err = manif.load_rock_manifest(name, version) + if not rock_manifest then return nil, err end - local ok, err = true + local ok = true if rock_manifest.bin then ok, err = delete_deployed_file_tree(rock_manifest.bin, cfg.deploy_bin_dir, cfg.wrapper_suffix) end -- cgit v1.2.3-55-g6feb From e1197dc9e029cb32b91c99c8cc26794ab4ab3cc1 Mon Sep 17 00:00:00 2001 From: Hisham Date: Wed, 19 Oct 2016 13:43:16 -0400 Subject: Add some useful rules to .gitignore --- .gitignore | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.gitignore b/.gitignore index f74b9549..97698111 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,13 @@ /src/luarocks/site_config.lua /test/testing_* /test/luacov.* +# Stuff that pops up during development but shouldn't be in the repo (helps clean up `git status`) +/*.rockspec +/*.rock +/*.tar.gz +/*.zip +/src/*.* +/prefix-* +/dev_* +/gh-pages +/wiki -- cgit v1.2.3-55-g6feb From 02dd4e1301ea11d60be2e36d66392d367c8e2c5b Mon Sep 17 00:00:00 2001 From: Hisham Date: Wed, 19 Oct 2016 15:18:37 -0400 Subject: Add format-check function in rockspec table. This avoids sprinkling the luarocks.deps dependency everywhere. --- src/luarocks/build.lua | 4 ++-- src/luarocks/build/cmake.lua | 3 +-- src/luarocks/fetch.lua | 7 ++++--- src/luarocks/fetch/git.lua | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/luarocks/build.lua b/src/luarocks/build.lua index 1d2925f9..790efef8 100644 --- a/src/luarocks/build.lua +++ b/src/luarocks/build.lua @@ -161,7 +161,7 @@ local function check_macosx_deployment_target(rockspec) rockspec.variables[var] = "env MACOSX_DEPLOYMENT_TARGET="..target.." "..rockspec.variables[var] end end - if cfg.platforms.macosx and deps.format_is_at_least(rockspec, "3.0") and target then + if cfg.platforms.macosx and rockspec:format_is_at_least("3.0") and target then local version = util.popen_read("sw_vers -productVersion") local versionminor = minor(version) local targetminor = minor(target) @@ -431,7 +431,7 @@ function build.command(flags, name, version) if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end ok, err = do_build(name, version, deps.get_deps_mode(flags), flags["only-deps"]) if not ok then return nil, err end - local name, version = ok, err + name, version = ok, err if flags["only-deps"] then return name, version end diff --git a/src/luarocks/build/cmake.lua b/src/luarocks/build/cmake.lua index 43702979..8ee6b6b2 100644 --- a/src/luarocks/build/cmake.lua +++ b/src/luarocks/build/cmake.lua @@ -5,7 +5,6 @@ local cmake = {} local fs = require("luarocks.fs") local util = require("luarocks.util") local cfg = require("luarocks.core.cfg") -local deps = require("luarocks.deps") --- Driver function for the "cmake" build back-end. -- @param rockspec table: the loaded rockspec. @@ -54,7 +53,7 @@ function cmake.run(rockspec) end local do_build, do_install - if deps.format_is_at_least(rockspec, "3.0") then + if rockspec:format_is_at_least("3.0") then do_build = (build.build_pass == nil) and true or build.build_pass do_install = (build.install_pass == nil) and true or build.install_pass else diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua index 9bf1922a..37db78f4 100644 --- a/src/luarocks/fetch.lua +++ b/src/luarocks/fetch.lua @@ -208,6 +208,7 @@ function fetch.load_local_rockspec(filename, quick) return nil, "Rockspec format "..rockspec.rockspec_format.." is not supported, please upgrade LuaRocks." end end + rockspec.format_is_at_least = deps.format_is_at_least util.platform_overrides(rockspec.build) util.platform_overrides(rockspec.dependencies) @@ -247,12 +248,12 @@ function fetch.load_local_rockspec(filename, quick) rockspec.source.dir = rockspec.source.dir or rockspec.source.module or ( (filebase:match("%.lua$") or filebase:match("%.c$")) - and (deps.format_is_at_least(rockspec, "3.0") + and (rockspec:format_is_at_least("3.0") and (fetch.is_basic_protocol(protocol) and "." or base) or ".") ) or base - rockspec.rocks_provided = (deps.format_is_at_least(rockspec, "3.0") + rockspec.rocks_provided = (rockspec:format_is_at_least("3.0") and cfg.rocks_provided_3_0 or cfg.rocks_provided) @@ -363,7 +364,7 @@ function fetch.get_sources(rockspec, extract, dest_dir) -- with rockspecs newer than 3.0. local dir_count, found_dir = 0 - if not rockspec.source.dir_set and deps.format_is_at_least(rockspec, "3.0") then + if not rockspec.source.dir_set and rockspec:format_is_at_least("3.0") then local files = fs.list_dir() for _, f in ipairs(files) do if fs.is_dir(f) then diff --git a/src/luarocks/fetch/git.lua b/src/luarocks/fetch/git.lua index eaba7ffd..72da4974 100644 --- a/src/luarocks/fetch/git.lua +++ b/src/luarocks/fetch/git.lua @@ -110,7 +110,7 @@ function git.get_sources(rockspec, extract, dest_dir, depth) end -- Fetching git submodules is supported only when rockspec format is >= 3.0. - if deps.format_is_at_least(rockspec, "3.0") then + if rockspec:format_is_at_least("3.0") then command = {fs.Q(git_cmd), "submodule", "update", "--init", "--recursive"} if git_supports_shallow_submodules(git_cmd) then -- cgit v1.2.3-55-g6feb From 8db1399ab7e9e9f0d211f81a038af18dfd5eadff Mon Sep 17 00:00:00 2001 From: Hisham Date: Wed, 19 Oct 2016 15:22:50 -0400 Subject: Merge in Windows-compatible quoting. --- test/test_environment.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_environment.lua b/test/test_environment.lua index f813707a..b174020b 100644 --- a/test/test_environment.lua +++ b/test/test_environment.lua @@ -513,8 +513,8 @@ function test_env.setup_specs(extra_rocks) test_env.main() package.path = test_env.env_variables.LUA_PATH - test_env.platform = execute_output(test_env.testing_paths.lua .. " -e 'print(require(\"luarocks.core.cfg\").arch)'", false, test_env.env_variables) - test_env.lib_extension = execute_output(test_env.testing_paths.lua .. " -e 'print(require(\"luarocks.core.cfg\").lib_extension)'", false, test_env.env_variables) + test_env.platform = execute_output(test_env.testing_paths.lua .. " -e \"print(require('luarocks.cfg').arch)\"", false, test_env.env_variables) + test_env.lib_extension = execute_output(test_env.testing_paths.lua .. " -e \"print(require('luarocks.cfg').lib_extension)\"", false, test_env.env_variables) test_env.md5sums = create_md5sums(test_env.testing_paths) test_env.setup_done = true title("RUNNING TESTS") -- cgit v1.2.3-55-g6feb From c5f11204bf774c845fd3b6205e736fcbeaf680c9 Mon Sep 17 00:00:00 2001 From: Hisham Date: Wed, 19 Oct 2016 16:12:46 -0400 Subject: Oops, messed with the module location. --- test/test_environment.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_environment.lua b/test/test_environment.lua index b174020b..93f10b83 100644 --- a/test/test_environment.lua +++ b/test/test_environment.lua @@ -513,8 +513,8 @@ function test_env.setup_specs(extra_rocks) test_env.main() package.path = test_env.env_variables.LUA_PATH - test_env.platform = execute_output(test_env.testing_paths.lua .. " -e \"print(require('luarocks.cfg').arch)\"", false, test_env.env_variables) - test_env.lib_extension = execute_output(test_env.testing_paths.lua .. " -e \"print(require('luarocks.cfg').lib_extension)\"", false, test_env.env_variables) + test_env.platform = execute_output(test_env.testing_paths.lua .. " -e \"print(require('luarocks.core.cfg').arch)\"", false, test_env.env_variables) + test_env.lib_extension = execute_output(test_env.testing_paths.lua .. " -e \"print(require('luarocks.core.cfg').lib_extension)\"", false, test_env.env_variables) test_env.md5sums = create_md5sums(test_env.testing_paths) test_env.setup_done = true title("RUNNING TESTS") -- cgit v1.2.3-55-g6feb From b526f07e99af98ba42786e88c52636e52059b398 Mon Sep 17 00:00:00 2001 From: Hisham Date: Thu, 20 Oct 2016 03:04:28 -0400 Subject: Make sure brand-new rockspec table has the utility function. --- src/luarocks/write_rockspec.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/luarocks/write_rockspec.lua b/src/luarocks/write_rockspec.lua index d6075dbd..be563eaa 100644 --- a/src/luarocks/write_rockspec.lua +++ b/src/luarocks/write_rockspec.lua @@ -9,6 +9,7 @@ local path = require("luarocks.path") local persist = require("luarocks.persist") local type_check = require("luarocks.type_check") local util = require("luarocks.util") +local deps = require("luarocks.deps") write_rockspec.help_summary = "Write a template for a rockspec file." write_rockspec.help_arguments = "[--output= ...] [] [] [|]" @@ -221,6 +222,7 @@ local function rockspec_cleanup(rockspec) rockspec.source.protocol = nil rockspec.variables = nil rockspec.name = nil + rockspec.format_is_at_least = nil end function write_rockspec.command(flags, name, version, url_or_dir) @@ -283,6 +285,7 @@ function write_rockspec.command(flags, name, version, url_or_dir) } path.configure_paths(rockspec) rockspec.source.protocol = protocol + rockspec.format_is_at_least = deps.format_is_at_least configure_lua_version(rockspec, flags["lua-version"]) -- cgit v1.2.3-55-g6feb From 518e3b4f49f95a345d5f93600c85ce61b0f9fc41 Mon Sep 17 00:00:00 2001 From: Hisham Date: Tue, 25 Oct 2016 16:43:18 -0200 Subject: Check rockspec_version before running type check. --- src/luarocks/fetch.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua index 37db78f4..d03de1c6 100644 --- a/src/luarocks/fetch.lua +++ b/src/luarocks/fetch.lua @@ -196,6 +196,12 @@ function fetch.load_local_rockspec(filename, quick) end local globals = err + if rockspec.rockspec_format then + if deps.compare_versions(rockspec.rockspec_format, type_check.rockspec_format) then + return nil, "Rockspec format "..rockspec.rockspec_format.." is not supported, please upgrade LuaRocks." + end + end + if not quick then local ok, err = type_check.type_check_rockspec(rockspec, globals) if not ok then @@ -203,11 +209,6 @@ function fetch.load_local_rockspec(filename, quick) end end - if rockspec.rockspec_format then - if deps.compare_versions(rockspec.rockspec_format, type_check.rockspec_format) then - return nil, "Rockspec format "..rockspec.rockspec_format.." is not supported, please upgrade LuaRocks." - end - end rockspec.format_is_at_least = deps.format_is_at_least util.platform_overrides(rockspec.build) -- cgit v1.2.3-55-g6feb From 17f71ed0e37122d24d8df53e276bbb190c12fb69 Mon Sep 17 00:00:00 2001 From: Hisham Date: Fri, 28 Oct 2016 09:53:27 -0200 Subject: Fix merge. --- src/luarocks/path_cmd.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/src/luarocks/path_cmd.lua b/src/luarocks/path_cmd.lua index 627d97cf..516a0c47 100644 --- a/src/luarocks/path_cmd.lua +++ b/src/luarocks/path_cmd.lua @@ -5,7 +5,6 @@ local path_cmd = {} local util = require("luarocks.util") local cfg = require("luarocks.core.cfg") -local cfg = require("luarocks.cfg") path_cmd.help_summary = "Return the currently configured package path." path_cmd.help_arguments = "" -- cgit v1.2.3-55-g6feb From 75902ff48a5bdc2cd00b492deb9f8aaa1d35bd3d Mon Sep 17 00:00:00 2001 From: Hisham Date: Fri, 28 Oct 2016 20:15:15 -0200 Subject: Duplicate files to preserve git-blame --- src/luarocks/base/build.lua | 443 ++++++++++++++++++++++++++++++++ src/luarocks/base/config_cmd.lua | 71 ++++++ src/luarocks/base/doc.lua | 155 +++++++++++ src/luarocks/base/download.lua | 107 ++++++++ src/luarocks/base/help.lua | 117 +++++++++ src/luarocks/base/install.lua | 183 +++++++++++++ src/luarocks/base/lint.lua | 53 ++++ src/luarocks/base/list.lua | 95 +++++++ src/luarocks/base/make.lua | 86 +++++++ src/luarocks/base/new_version.lua | 199 +++++++++++++++ src/luarocks/base/pack.lua | 193 ++++++++++++++ src/luarocks/base/path_cmd.lua | 68 +++++ src/luarocks/base/purge.lua | 77 ++++++ src/luarocks/base/remove.lua | 165 ++++++++++++ src/luarocks/base/search.lua | 482 +++++++++++++++++++++++++++++++++++ src/luarocks/base/show.lua | 158 ++++++++++++ src/luarocks/base/unpack.lua | 164 ++++++++++++ src/luarocks/base/upload.lua | 94 +++++++ src/luarocks/base/write_rockspec.lua | 376 +++++++++++++++++++++++++++ src/luarocks/build.lua | 443 -------------------------------- src/luarocks/cmd/build.lua | 443 ++++++++++++++++++++++++++++++++ src/luarocks/cmd/config_cmd.lua | 71 ++++++ src/luarocks/cmd/doc.lua | 155 +++++++++++ src/luarocks/cmd/download.lua | 107 ++++++++ src/luarocks/cmd/help.lua | 117 +++++++++ src/luarocks/cmd/install.lua | 183 +++++++++++++ src/luarocks/cmd/lint.lua | 53 ++++ src/luarocks/cmd/list.lua | 95 +++++++ src/luarocks/cmd/make.lua | 86 +++++++ src/luarocks/cmd/new_version.lua | 199 +++++++++++++++ src/luarocks/cmd/pack.lua | 193 ++++++++++++++ src/luarocks/cmd/path_cmd.lua | 68 +++++ src/luarocks/cmd/purge.lua | 77 ++++++ src/luarocks/cmd/remove.lua | 165 ++++++++++++ src/luarocks/cmd/search.lua | 482 +++++++++++++++++++++++++++++++++++ src/luarocks/cmd/show.lua | 158 ++++++++++++ src/luarocks/cmd/unpack.lua | 164 ++++++++++++ src/luarocks/cmd/upload.lua | 94 +++++++ src/luarocks/cmd/write_rockspec.lua | 376 +++++++++++++++++++++++++++ src/luarocks/config_cmd.lua | 71 ------ src/luarocks/doc.lua | 155 ----------- src/luarocks/download.lua | 107 -------- src/luarocks/help.lua | 117 --------- src/luarocks/install.lua | 183 ------------- src/luarocks/lint.lua | 53 ---- src/luarocks/list.lua | 95 ------- src/luarocks/make.lua | 86 ------- src/luarocks/new_version.lua | 199 --------------- src/luarocks/pack.lua | 193 -------------- src/luarocks/path_cmd.lua | 68 ----- src/luarocks/purge.lua | 77 ------ src/luarocks/remove.lua | 165 ------------ src/luarocks/search.lua | 482 ----------------------------------- src/luarocks/show.lua | 158 ------------ src/luarocks/unpack.lua | 164 ------------ src/luarocks/upload.lua | 94 ------- src/luarocks/write_rockspec.lua | 376 --------------------------- 57 files changed, 6572 insertions(+), 3286 deletions(-) create mode 100644 src/luarocks/base/build.lua create mode 100644 src/luarocks/base/config_cmd.lua create mode 100644 src/luarocks/base/doc.lua create mode 100644 src/luarocks/base/download.lua create mode 100644 src/luarocks/base/help.lua create mode 100644 src/luarocks/base/install.lua create mode 100644 src/luarocks/base/lint.lua create mode 100644 src/luarocks/base/list.lua create mode 100644 src/luarocks/base/make.lua create mode 100644 src/luarocks/base/new_version.lua create mode 100644 src/luarocks/base/pack.lua create mode 100644 src/luarocks/base/path_cmd.lua create mode 100644 src/luarocks/base/purge.lua create mode 100644 src/luarocks/base/remove.lua create mode 100644 src/luarocks/base/search.lua create mode 100644 src/luarocks/base/show.lua create mode 100644 src/luarocks/base/unpack.lua create mode 100644 src/luarocks/base/upload.lua create mode 100644 src/luarocks/base/write_rockspec.lua delete mode 100644 src/luarocks/build.lua create mode 100644 src/luarocks/cmd/build.lua create mode 100644 src/luarocks/cmd/config_cmd.lua create mode 100644 src/luarocks/cmd/doc.lua create mode 100644 src/luarocks/cmd/download.lua create mode 100644 src/luarocks/cmd/help.lua create mode 100644 src/luarocks/cmd/install.lua create mode 100644 src/luarocks/cmd/lint.lua create mode 100644 src/luarocks/cmd/list.lua create mode 100644 src/luarocks/cmd/make.lua create mode 100644 src/luarocks/cmd/new_version.lua create mode 100644 src/luarocks/cmd/pack.lua create mode 100644 src/luarocks/cmd/path_cmd.lua create mode 100644 src/luarocks/cmd/purge.lua create mode 100644 src/luarocks/cmd/remove.lua create mode 100644 src/luarocks/cmd/search.lua create mode 100644 src/luarocks/cmd/show.lua create mode 100644 src/luarocks/cmd/unpack.lua create mode 100644 src/luarocks/cmd/upload.lua create mode 100644 src/luarocks/cmd/write_rockspec.lua delete mode 100644 src/luarocks/config_cmd.lua delete mode 100644 src/luarocks/doc.lua delete mode 100644 src/luarocks/download.lua delete mode 100644 src/luarocks/help.lua delete mode 100644 src/luarocks/install.lua delete mode 100644 src/luarocks/lint.lua delete mode 100644 src/luarocks/list.lua delete mode 100644 src/luarocks/make.lua delete mode 100644 src/luarocks/new_version.lua delete mode 100644 src/luarocks/pack.lua delete mode 100644 src/luarocks/path_cmd.lua delete mode 100644 src/luarocks/purge.lua delete mode 100644 src/luarocks/remove.lua delete mode 100644 src/luarocks/search.lua delete mode 100644 src/luarocks/show.lua delete mode 100644 src/luarocks/unpack.lua delete mode 100644 src/luarocks/upload.lua delete mode 100644 src/luarocks/write_rockspec.lua diff --git a/src/luarocks/base/build.lua b/src/luarocks/base/build.lua new file mode 100644 index 00000000..f3b054d2 --- /dev/null +++ b/src/luarocks/base/build.lua @@ -0,0 +1,443 @@ + +--- Module implementing the LuaRocks "build" command. +-- Builds a rock, compiling its C parts if any. +local build = {} + +local pack = require("luarocks.pack") +local path = require("luarocks.path") +local util = require("luarocks.util") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local deps = require("luarocks.deps") +local writer = require("luarocks.manif.writer") +local remove = require("luarocks.remove") +local cfg = require("luarocks.core.cfg") + +build.help_summary = "Build/compile a rock." +build.help_arguments = "[--pack-binary-rock] [--keep] {|| []}" +build.help = [[ +Build and install a rock, compiling its C parts if any. +Argument may be a rockspec file, a source rock file +or the name of a rock to be fetched from a repository. + +--pack-binary-rock Do not install rock. Instead, produce a .rock file + with the contents of compilation in the current + directory. + +--keep Do not remove previously installed versions of the + rock after building a new one. This behavior can + be made permanent by setting keep_other_versions=true + in the configuration file. + +--branch= Override the `source.branch` field in the loaded + rockspec. Allows to specify a different branch to + fetch. Particularly for SCM rocks. + +--only-deps Installs only the dependencies of the rock. + +]]..util.deps_mode_help() + +--- Install files to a given location. +-- Takes a table where the array part is a list of filenames to be copied. +-- In the hash part, other keys, if is_module_path is set, are identifiers +-- in Lua module format, to indicate which subdirectory the file should be +-- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo") +-- will copy src/bar.lua to boo/foo. +-- @param files table or nil: A table containing a list of files to copy in +-- the format described above. If nil is passed, this function is a no-op. +-- Directories should be delimited by forward slashes as in internet URLs. +-- @param location string: The base directory files should be copied to. +-- @param is_module_path boolean: True if string keys in files should be +-- interpreted as dotted module paths. +-- @param perms string: Permissions of the newly created files installed. +-- Directories are always created with the default permissions. +-- @return boolean or (nil, string): True if succeeded or +-- nil and an error message. +local function install_files(files, location, is_module_path, perms) + assert(type(files) == "table" or not files) + assert(type(location) == "string") + if files then + for k, file in pairs(files) do + local dest = location + local filename = dir.base_name(file) + if type(k) == "string" then + local modname = k + if is_module_path then + dest = dir.path(location, path.module_to_path(modname)) + local ok, err = fs.make_dir(dest) + if not ok then return nil, err end + if filename:match("%.lua$") then + local basename = modname:match("([^.]+)$") + filename = basename..".lua" + end + else + dest = dir.path(location, dir.dir_name(modname)) + local ok, err = fs.make_dir(dest) + if not ok then return nil, err end + filename = dir.base_name(modname) + end + else + local ok, err = fs.make_dir(dest) + if not ok then return nil, err end + end + local ok = fs.copy(dir.path(file), dir.path(dest, filename), perms) + if not ok then + return nil, "Failed copying "..file + end + end + end + return true +end + +--- Write to the current directory the contents of a table, +-- where each key is a file name and its value is the file content. +-- @param files table: The table of files to be written. +local function extract_from_rockspec(files) + for name, content in pairs(files) do + local fd = io.open(dir.path(fs.current_dir(), name), "w+") + fd:write(content) + fd:close() + end +end + +--- Applies patches inlined in the build.patches section +-- and extracts files inlined in the build.extra_files section +-- of a rockspec. +-- @param rockspec table: A rockspec table. +-- @return boolean or (nil, string): True if succeeded or +-- nil and an error message. +function build.apply_patches(rockspec) + assert(type(rockspec) == "table") + + local build_spec = rockspec.build + if build_spec.extra_files then + extract_from_rockspec(build_spec.extra_files) + end + if build_spec.patches then + extract_from_rockspec(build_spec.patches) + for patch, patchdata in util.sortedpairs(build_spec.patches) do + util.printout("Applying patch "..patch.."...") + local ok, err = fs.apply_patch(tostring(patch), patchdata) + if not ok then + return nil, "Failed applying patch "..patch + end + end + end + return true +end + +local function install_default_docs(name, version) + local patterns = { "readme", "license", "copying", ".*%.md" } + local dest = dir.path(path.install_dir(name, version), "doc") + local has_dir = false + for file in fs.dir() do + for _, pattern in ipairs(patterns) do + if file:lower():match("^"..pattern) then + if not has_dir then + fs.make_dir(dest) + has_dir = true + end + fs.copy(file, dest, cfg.perm_read) + break + end + end + end +end + +local function check_macosx_deployment_target(rockspec) + local target = rockspec.build.macosx_deployment_target + local function minor(version) + return tonumber(version and version:match("^[^.]+%.([^.]+)")) + end + local function patch_variable(var, target) + if rockspec.variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then + rockspec.variables[var] = (rockspec.variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET="..target) + else + rockspec.variables[var] = "env MACOSX_DEPLOYMENT_TARGET="..target.." "..rockspec.variables[var] + end + end + if cfg.platforms.macosx and rockspec:format_is_at_least("3.0") and target then + local version = util.popen_read("sw_vers -productVersion") + local versionminor = minor(version) + local targetminor = minor(target) + if targetminor > versionminor then + return nil, ("This rock requires Mac OSX 10.%d, and you are running 10.%d."):format(targetminor, versionminor) + end + patch_variable("CC", target) + patch_variable("LD", target) + end + return true +end + +--- Build and install a rock given a rockspec. +-- @param rockspec_file string: local or remote filename of a rockspec. +-- @param need_to_fetch boolean: true if sources need to be fetched, +-- false if the rockspec was obtained from inside a source rock. +-- @param minimal_mode boolean: true if there's no need to fetch, +-- unpack or change dir (this is used by "luarocks make"). Implies +-- need_to_fetch = false. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for no trees. +-- @param build_only_deps boolean: true to build the listed dependencies only. +-- @return (string, string) or (nil, string, [string]): Name and version of +-- installed rock if succeeded or nil and an error message followed by an error code. +function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode, build_only_deps) + assert(type(rockspec_file) == "string") + assert(type(need_to_fetch) == "boolean") + + local rockspec, err, errcode = fetch.load_rockspec(rockspec_file) + if err then + return nil, err, errcode + elseif not rockspec.build then + return nil, "Rockspec error: build table not specified" + elseif not rockspec.build.type then + return nil, "Rockspec error: build type not specified" + end + + local ok + if not build_only_deps then + ok, err, errcode = deps.check_external_deps(rockspec, "build") + if err then + return nil, err, errcode + end + end + + if deps_mode == "none" then + util.printerr("Warning: skipping dependency checks.") + else + local ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) + if err then + return nil, err, errcode + end + end + + local name, version = rockspec.name, rockspec.version + if build_only_deps then + util.printout("Stopping after installing dependencies for " ..name.." "..version) + util.printout() + return name, version + end + + if repos.is_installed(name, version) then + repos.delete_version(name, version, deps_mode) + end + + if not minimal_mode then + local source_dir + if need_to_fetch then + ok, source_dir, errcode = fetch.fetch_sources(rockspec, true) + if not ok then + return nil, source_dir, errcode + end + local ok, err = fs.change_dir(source_dir) + if not ok then return nil, err end + elseif rockspec.source.file then + local ok, err = fs.unpack_archive(rockspec.source.file) + if not ok then + return nil, err + end + end + fs.change_dir(rockspec.source.dir) + end + + local dirs = { + lua = { name = path.lua_dir(name, version), is_module_path = true, perms = cfg.perm_read }, + lib = { name = path.lib_dir(name, version), is_module_path = true, perms = cfg.perm_exec }, + conf = { name = path.conf_dir(name, version), is_module_path = false, perms = cfg.perm_read }, + bin = { name = path.bin_dir(name, version), is_module_path = false, perms = cfg.perm_exec }, + } + + for _, d in pairs(dirs) do + local ok, err = fs.make_dir(d.name) + if not ok then return nil, err end + end + local rollback = util.schedule_function(function() + fs.delete(path.install_dir(name, version)) + fs.remove_dir_if_empty(path.versions_dir(name)) + end) + + local build_spec = rockspec.build + + if not minimal_mode then + ok, err = build.apply_patches(rockspec) + if err then + return nil, err + end + end + + ok, err = check_macosx_deployment_target(rockspec) + if not ok then + return nil, err + end + + if build_spec.type ~= "none" then + + -- Temporary compatibility + if build_spec.type == "module" then + util.printout("Do not use 'module' as a build type. Use 'builtin' instead.") + build_spec.type = "builtin" + end + + if cfg.accepted_build_types and util.array_contains(cfg.accepted_build_types, build_spec.type) then + return nil, "This rockspec uses the '"..build_spec.type.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration." + end + + local build_type + ok, build_type = pcall(require, "luarocks.build." .. build_spec.type) + if not ok or not type(build_type) == "table" then + return nil, "Failed initializing build back-end for build type '"..build_spec.type.."': "..build_type + end + + ok, err = build_type.run(rockspec) + if not ok then + return nil, "Build error: " .. err + end + end + + if build_spec.install then + for id, install_dir in pairs(dirs) do + ok, err = install_files(build_spec.install[id], install_dir.name, install_dir.is_module_path, install_dir.perms) + if not ok then + return nil, err + end + end + end + + local copy_directories = build_spec.copy_directories + local copying_default = false + if not copy_directories then + copy_directories = {"doc"} + copying_default = true + end + + local any_docs = false + for _, copy_dir in pairs(copy_directories) do + if fs.is_dir(copy_dir) then + local dest = dir.path(path.install_dir(name, version), copy_dir) + fs.make_dir(dest) + fs.copy_contents(copy_dir, dest) + any_docs = true + else + if not copying_default then + return nil, "Directory '"..copy_dir.."' not found" + end + end + end + + if not any_docs then + install_default_docs(name, version) + end + + for _, d in pairs(dirs) do + fs.remove_dir_if_empty(d.name) + end + + fs.pop_dir() + + fs.copy(rockspec.local_filename, path.rockspec_file(name, version), cfg.perm_read) + if need_to_fetch then + fs.pop_dir() + end + + ok, err = writer.make_rock_manifest(name, version) + if err then return nil, err end + + ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) + if err then return nil, err end + + util.remove_scheduled_function(rollback) + rollback = util.schedule_function(function() + repos.delete_version(name, version, deps_mode) + end) + + ok, err = repos.run_hook(rockspec, "post_install") + if err then return nil, err end + + util.announce_install(rockspec) + util.remove_scheduled_function(rollback) + return name, version +end + +--- Build and install a rock. +-- @param rock_file string: local or remote filename of a rock. +-- @param need_to_fetch boolean: true if sources need to be fetched, +-- false if the rockspec was obtained from inside a source rock. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @param build_only_deps boolean: true to build the listed dependencies only. +-- @return boolean or (nil, string, [string]): True if build was successful, +-- or false and an error message and an optional error code. +function build.build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps) + assert(type(rock_file) == "string") + assert(type(need_to_fetch) == "boolean") + + local ok, err, errcode + local unpack_dir + unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file) + if not unpack_dir then + return nil, err, errcode + end + local rockspec_file = path.rockspec_name_from_rock(rock_file) + ok, err = fs.change_dir(unpack_dir) + if not ok then return nil, err end + ok, err, errcode = build.build_rockspec(rockspec_file, need_to_fetch, false, deps_mode, build_only_deps) + fs.pop_dir() + return ok, err, errcode +end + +local function do_build(name, version, deps_mode, build_only_deps) + if name:match("%.rockspec$") then + return build.build_rockspec(name, true, false, deps_mode, build_only_deps) + elseif name:match("%.src%.rock$") then + return build.build_rock(name, false, deps_mode, build_only_deps) + elseif name:match("%.all%.rock$") then + local install = require("luarocks.install") + local install_fun = build_only_deps and install.install_binary_rock_deps or install.install_binary_rock + return install_fun(name, deps_mode) + elseif name:match("%.rock$") then + return build.build_rock(name, true, deps_mode, build_only_deps) + elseif not name:match(dir.separator) then + local search = require("luarocks.search") + return search.act_on_src_or_rockspec(do_build, name:lower(), version, nil, deps_mode, build_only_deps) + end + return nil, "Don't know what to do with "..name +end + +--- Driver function for "build" command. +-- @param name string: A local or remote rockspec or rock file. +-- If a package name is given, forwards the request to "search" and, +-- if returned a result, installs the matching rock. +-- @param version string: When passing a package name, a version number may +-- also be given. +-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an +-- error message otherwise. exitcode is optionally returned. +function build.command(flags, name, version) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("build") + end + assert(type(version) == "string" or not version) + + if flags["pack-binary-rock"] then + return pack.pack_binary_rock(name, version, do_build, name, version, deps.get_deps_mode(flags)) + else + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + ok, err = do_build(name, version, deps.get_deps_mode(flags), flags["only-deps"]) + if not ok then return nil, err end + name, version = ok, err + if flags["only-deps"] then + return name, version + end + if (not flags["keep"]) and not cfg.keep_other_versions then + local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) + if not ok then util.printerr(err) end + end + return name, version + end +end + +return build diff --git a/src/luarocks/base/config_cmd.lua b/src/luarocks/base/config_cmd.lua new file mode 100644 index 00000000..b68f7898 --- /dev/null +++ b/src/luarocks/base/config_cmd.lua @@ -0,0 +1,71 @@ +--- Module implementing the LuaRocks "config" command. +-- Queries information about the LuaRocks configuration. +local config_cmd = {} + +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") + +config_cmd.help_summary = "Query information about the LuaRocks configuration." +config_cmd.help_arguments = "" +config_cmd.help = [[ +--lua-incdir Path to Lua header files. + +--lua-libdir Path to Lua library files. + +--lua-ver Lua version (in major.minor format). e.g. 5.1 + +--system-config Location of the system config file. + +--user-config Location of the user config file. + +--rock-trees Rocks trees in use. First the user tree, then the system tree. +]] + +local function config_file(conf) + print(dir.normalize(conf.file)) + if conf.ok then + return true + else + return nil, "file not found" + end +end + +--- Driver function for "config" command. +-- @return boolean: True if succeeded, nil on errors. +function config_cmd.command(flags) + if flags["lua-incdir"] then + print(cfg.variables.LUA_INCDIR) + return true + end + if flags["lua-libdir"] then + print(cfg.variables.LUA_LIBDIR) + return true + end + if flags["lua-ver"] then + print(cfg.lua_version) + return true + end + local conf = cfg.which_config() + if flags["system-config"] then + return config_file(conf.system) + end + if flags["user-config"] then + return config_file(conf.user) + end + if flags["rock-trees"] then + for _, tree in ipairs(cfg.rocks_trees) do + if type(tree) == "string" then + util.printout(dir.normalize(tree)) + else + local name = tree.name and "\t"..tree.name or "" + util.printout(dir.normalize(tree.root)..name) + end + end + return true + end + + return nil, "Please provide a flag for querying configuration values. "..util.see_help("config") +end + +return config_cmd diff --git a/src/luarocks/base/doc.lua b/src/luarocks/base/doc.lua new file mode 100644 index 00000000..5d521276 --- /dev/null +++ b/src/luarocks/base/doc.lua @@ -0,0 +1,155 @@ + +--- Module implementing the LuaRocks "doc" command. +-- Shows documentation for an installed rock. +local doc = {} + +local util = require("luarocks.util") +local search = require("luarocks.search") +local path = require("luarocks.path") +local dir = require("luarocks.dir") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local download = require("luarocks.download") + +doc.help_summary = "Show documentation for an installed rock." + +doc.help = [[ + is an existing package name. +Without any flags, tries to load the documentation +using a series of heuristics. +With these flags, return only the desired information: + +--home Open the home page of project. +--list List documentation files only. + +For more information about a rock, see the 'show' command. +]] + +local function show_homepage(homepage, name, version) + if not homepage then + return nil, "No 'homepage' field in rockspec for "..name.." "..version + end + util.printout("Opening "..homepage.." ...") + fs.browser(homepage) + return true +end + +local function try_to_open_homepage(name, version) + local temp_dir, err = fs.make_temp_dir("doc-"..name.."-"..(version or "")) + if not temp_dir then + return nil, "Failed creating temporary directory: "..err + end + util.schedule_function(fs.delete, temp_dir) + local ok, err = fs.change_dir(temp_dir) + if not ok then return nil, err end + local filename, err = download.download("rockspec", name, version) + if not filename then return nil, err end + local rockspec, err = fetch.load_local_rockspec(filename) + if not rockspec then return nil, err end + fs.pop_dir() + local descript = rockspec.description or {} + if not descript.homepage then return nil, "No homepage defined for "..name end + return show_homepage(descript.homepage, name, version) +end + +--- Driver function for "doc" command. +-- @param name or nil: an existing package name. +-- @param version string or nil: a version may also be passed. +-- @return boolean: True if succeeded, nil on errors. +function doc.command(flags, name, version) + if not name then + return nil, "Argument missing. "..util.see_help("doc") + end + + name = name:lower() + + local iname, iversion, repo = search.pick_installed_rock(name, version, flags["tree"]) + if not iname then + util.printout(name..(version and " "..version or "").." is not installed. Looking for it in the rocks servers...") + return try_to_open_homepage(name, version) + end + name, version = iname, iversion + + local rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version, repo)) + if not rockspec then return nil,err end + local descript = rockspec.description or {} + + if flags["home"] then + return show_homepage(descript.homepage, name, version) + end + + local directory = path.install_dir(name,version,repo) + + local docdir + local directories = { "doc", "docs" } + for _, d in ipairs(directories) do + local dirname = dir.path(directory, d) + if fs.is_dir(dirname) then + docdir = dirname + break + end + end + if not docdir then + if descript.homepage and not flags["list"] then + util.printout("Local documentation directory not found -- opening "..descript.homepage.." ...") + fs.browser(descript.homepage) + return true + end + return nil, "Documentation directory not found for "..name.." "..version + end + + docdir = dir.normalize(docdir):gsub("/+", "/") + local files = fs.find(docdir) + local htmlpatt = "%.html?$" + local extensions = { htmlpatt, "%.md$", "%.txt$", "%.textile$", "" } + local basenames = { "index", "readme", "manual" } + + local porcelain = flags["porcelain"] + if #files > 0 then + util.title("Documentation files for "..name.." "..version, porcelain) + if porcelain then + for _, file in ipairs(files) do + util.printout(docdir.."/"..file) + end + else + util.printout(docdir.."/") + for _, file in ipairs(files) do + util.printout("\t"..file) + end + end + end + + if flags["list"] then + return true + end + + for _, extension in ipairs(extensions) do + for _, basename in ipairs(basenames) do + local filename = basename..extension + local found + for _, file in ipairs(files) do + if file:lower():match(filename) and ((not found) or #file < #found) then + found = file + end + end + if found then + local pathname = dir.path(docdir, found) + util.printout() + util.printout("Opening "..pathname.." ...") + util.printout() + local ok = fs.browser(pathname) + if not ok and not pathname:match(htmlpatt) then + local fd = io.open(pathname, "r") + util.printout(fd:read("*a")) + fd:close() + end + return true + end + end + end + + return true +end + + +return doc diff --git a/src/luarocks/base/download.lua b/src/luarocks/base/download.lua new file mode 100644 index 00000000..557d1b65 --- /dev/null +++ b/src/luarocks/base/download.lua @@ -0,0 +1,107 @@ + +--- Module implementing the luarocks "download" command. +-- Download a rock from the repository. +local download = {} + +local util = require("luarocks.util") +local path = require("luarocks.path") +local fetch = require("luarocks.fetch") +local search = require("luarocks.search") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local cfg = require("luarocks.core.cfg") + +download.help_summary = "Download a specific rock file from a rocks server." +download.help_arguments = "[--all] [--arch= | --source | --rockspec] [ []]" + +download.help = [[ +--all Download all files if there are multiple matches. +--source Download .src.rock if available. +--rockspec Download .rockspec if available. +--arch= Download rock for a specific architecture. +]] + +local function get_file(filename) + local protocol, pathname = dir.split_url(filename) + if protocol == "file" then + local ok, err = fs.copy(pathname, fs.current_dir(), cfg.perm_read) + if ok then + return pathname + else + return nil, err + end + else + return fetch.fetch_url(filename) + end +end + +function download.download(arch, name, version, all) + local query = search.make_query(name, version) + if arch then query.arch = arch end + local search_err + + if all then + if name == "" then query.exact_name = false end + local results = search.search_repos(query) + local has_result = false + local all_ok = true + local any_err = "" + for name, result in pairs(results) do + for version, items in pairs(result) do + for _, item in ipairs(items) do + -- Ignore provided rocks. + if item.arch ~= "installed" then + has_result = true + local filename = path.make_url(item.repo, name, version, item.arch) + local ok, err = get_file(filename) + if not ok then + all_ok = false + any_err = any_err .. "\n" .. err + end + end + end + end + end + + if has_result then + return all_ok, any_err + end + else + local url + url, search_err = search.find_suitable_rock(query) + if url then + return get_file(url) + end + end + return nil, "Could not find a result named "..name..(version and " "..version or "").. + (search_err and ": "..search_err or ".") +end + +--- Driver function for the "download" command. +-- @param name string: a rock name. +-- @param version string or nil: if the name of a package is given, a +-- version may also be passed. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +function download.command(flags, name, version) + assert(type(version) == "string" or not version) + if type(name) ~= "string" and not flags["all"] then + return nil, "Argument missing. "..util.see_help("download") + end + if not name then name, version = "", "" end + + local arch + + if flags["source"] then + arch = "src" + elseif flags["rockspec"] then + arch = "rockspec" + elseif flags["arch"] then + arch = flags["arch"] + end + + local dl, err = download.download(arch, name:lower(), version, flags["all"]) + return dl and true, err +end + +return download diff --git a/src/luarocks/base/help.lua b/src/luarocks/base/help.lua new file mode 100644 index 00000000..d27c3a50 --- /dev/null +++ b/src/luarocks/base/help.lua @@ -0,0 +1,117 @@ + +--- Module implementing the LuaRocks "help" command. +-- This is a generic help display module, which +-- uses a global table called "commands" to find commands +-- to show help for; each command should be represented by a +-- table containing "help" and "help_summary" fields. +local help = {} + +local util = require("luarocks.util") +local cfg = require("luarocks.core.cfg") +local dir = require("luarocks.dir") + +local program = util.this_program("luarocks") + +help.help_summary = "Help on commands. Type '"..program.." help ' for more." + +help.help_arguments = "[]" +help.help = [[ + is the command to show help for. +]] + +local function print_banner() + util.printout("\nLuaRocks "..cfg.program_version..", a module deployment system for Lua") +end + +local function print_section(section) + util.printout("\n"..section) +end + +local function get_status(status) + if status then + return "ok" + else + return "not found" + end +end + +--- Driver function for the "help" command. +-- @param command string or nil: command to show help for; if not +-- given, help summaries for all commands are shown. +-- @return boolean or (nil, string): true if there were no errors +-- or nil and an error message if an invalid command was requested. +function help.command(flags, command) + if not command then + local conf = cfg.which_config() + print_banner() + print_section("NAME") + util.printout("\t"..program..[[ - ]]..program_description) + print_section("SYNOPSIS") + util.printout("\t"..program..[[ [--from= | --only-from=] [--to=] [VAR=VALUE]... [] ]]) + print_section("GENERAL OPTIONS") + util.printout([[ + These apply to all commands, as appropriate: + + --server= Fetch rocks/rockspecs from this server + (takes priority over config file) + --only-server= Fetch rocks/rockspecs from this server only + (overrides any entries in the config file) + --only-sources= Restrict downloads to paths matching the + given URL. + --tree= Which tree to operate on. + --local Use the tree in the user's home directory. + To enable it, see ']]..program..[[ help path'. + --verbose Display verbose output of commands executed. + --timeout= Timeout on network operations, in seconds. + 0 means no timeout (wait forever). + Default is ]]..tostring(cfg.connection_timeout)..[[.]]) + print_section("VARIABLES") + util.printout([[ + Variables from the "variables" table of the configuration file + can be overriden with VAR=VALUE assignments.]]) + print_section("COMMANDS") + for name, command in util.sortedpairs(commands) do + local cmd = require(command) + util.printout("", name) + util.printout("\t", cmd.help_summary) + end + print_section("CONFIGURATION") + util.printout("\tLua version: " .. cfg.lua_version) + util.printout("\tConfiguration files:") + util.printout("\t\tSystem: ".. dir.normalize(conf.system.file) .. " (" .. get_status(conf.system.ok) ..")") + if conf.user.file then + util.printout("\t\tUser : ".. dir.normalize(conf.user.file) .. " (" .. get_status(conf.user.ok) ..")\n") + else + util.printout("\t\tUser : disabled in this LuaRocks installation.\n") + end + util.printout("\tRocks trees in use: ") + for _, tree in ipairs(cfg.rocks_trees) do + if type(tree) == "string" then + util.printout("\t\t"..dir.normalize(tree)) + else + local name = tree.name and " (\""..tree.name.."\")" or "" + util.printout("\t\t"..dir.normalize(tree.root)..name) + end + end + else + command = command:gsub("-", "_") + local cmd = commands[command] and require(commands[command]) + if cmd then + local arguments = cmd.help_arguments or "" + print_banner() + print_section("NAME") + util.printout("\t"..program.." "..command.." - "..cmd.help_summary) + print_section("SYNOPSIS") + util.printout("\t"..program.." "..command.." "..arguments) + print_section("DESCRIPTION") + util.printout("",(cmd.help:gsub("\n","\n\t"):gsub("\n\t$",""))) + print_section("SEE ALSO") + util.printout("","'"..program.." help' for general options and configuration.\n") + else + return nil, "Unknown command: "..command + end + end + return true +end + +return help diff --git a/src/luarocks/base/install.lua b/src/luarocks/base/install.lua new file mode 100644 index 00000000..c9b085f5 --- /dev/null +++ b/src/luarocks/base/install.lua @@ -0,0 +1,183 @@ +--- Module implementing the LuaRocks "install" command. +-- Installs binary rocks. +local install = {} + +local path = require("luarocks.path") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local util = require("luarocks.util") +local fs = require("luarocks.fs") +local deps = require("luarocks.deps") +local writer = require("luarocks.manif.writer") +local remove = require("luarocks.remove") +local cfg = require("luarocks.core.cfg") + +install.help_summary = "Install a rock." + +install.help_arguments = "{| []}" + +install.help = [[ +Argument may be the name of a rock to be fetched from a repository +or a filename of a locally available rock. + +--keep Do not remove previously installed versions of the + rock after installing a new one. This behavior can + be made permanent by setting keep_other_versions=true + in the configuration file. + +--only-deps Installs only the dependencies of the rock. +]]..util.deps_mode_help() + + +--- Install a binary rock. +-- @param rock_file string: local or remote filename of a rock. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @return (string, string) or (nil, string, [string]): Name and version of +-- installed rock if succeeded or nil and an error message followed by an error code. +function install.install_binary_rock(rock_file, deps_mode) + assert(type(rock_file) == "string") + + local name, version, arch = path.parse_name(rock_file) + if not name then + return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." + end + + if arch ~= "all" and arch ~= cfg.arch then + return nil, "Incompatible architecture "..arch, "arch" + end + if repos.is_installed(name, version) then + repos.delete_version(name, version, deps_mode) + end + + local rollback = util.schedule_function(function() + fs.delete(path.install_dir(name, version)) + fs.remove_dir_if_empty(path.versions_dir(name)) + end) + + local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) + if not ok then return nil, err, errcode end + + local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version)) + if err then + return nil, "Failed loading rockspec for installed package: "..err, errcode + end + + if deps_mode == "none" then + util.printerr("Warning: skipping dependency checks.") + else + ok, err, errcode = deps.check_external_deps(rockspec, "install") + if err then return nil, err, errcode end + end + + -- For compatibility with .rock files built with LuaRocks 1 + if not fs.exists(path.rock_manifest_file(name, version)) then + ok, err = writer.make_rock_manifest(name, version) + if err then return nil, err end + end + + if deps_mode ~= "none" then + ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) + if err then return nil, err, errcode end + end + + ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) + if err then return nil, err end + + util.remove_scheduled_function(rollback) + rollback = util.schedule_function(function() + repos.delete_version(name, version, deps_mode) + end) + + ok, err = repos.run_hook(rockspec, "post_install") + if err then return nil, err end + + util.announce_install(rockspec) + util.remove_scheduled_function(rollback) + return name, version +end + +--- Installs the dependencies of a binary rock. +-- @param rock_file string: local or remote filename of a rock. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @return (string, string) or (nil, string, [string]): Name and version of +-- the rock whose dependencies were installed if succeeded or nil and an error message +-- followed by an error code. +function install.install_binary_rock_deps(rock_file, deps_mode) + assert(type(rock_file) == "string") + + local name, version, arch = path.parse_name(rock_file) + if not name then + return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." + end + + if arch ~= "all" and arch ~= cfg.arch then + return nil, "Incompatible architecture "..arch, "arch" + end + + local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) + if not ok then return nil, err, errcode end + + local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version)) + if err then + return nil, "Failed loading rockspec for installed package: "..err, errcode + end + + ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) + if err then return nil, err, errcode end + + util.printout() + util.printout("Successfully installed dependencies for " ..name.." "..version) + + return name, version +end + +--- Driver function for the "install" command. +-- @param name string: name of a binary rock. If an URL or pathname +-- to a binary rock is given, fetches and installs it. If a rockspec or a +-- source rock is given, forwards the request to the "build" command. +-- If a package name is given, forwards the request to "search" and, +-- if returned a result, installs the matching rock. +-- @param version string: When passing a package name, a version number +-- may also be given. +-- @return boolean or (nil, string, exitcode): True if installation was +-- successful, nil and an error message otherwise. exitcode is optionally returned. +function install.command(flags, name, version) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("install") + end + + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + + if name:match("%.rockspec$") or name:match("%.src%.rock$") then + local build = require("luarocks.build") + return build.command(flags, name) + elseif name:match("%.rock$") then + if flags["only-deps"] then + ok, err = install.install_binary_rock_deps(name, deps.get_deps_mode(flags)) + else + ok, err = install.install_binary_rock(name, deps.get_deps_mode(flags)) + end + if not ok then return nil, err end + name, version = ok, err + if (not flags["only-deps"]) and (not flags["keep"]) and not cfg.keep_other_versions then + local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) + if not ok then util.printerr(err) end + end + return name, version + else + local search = require("luarocks.search") + local url, err = search.find_suitable_rock(search.make_query(name:lower(), version)) + if not url then + return nil, err + end + util.printout("Installing "..url) + return install.command(flags, url) + end +end + +return install diff --git a/src/luarocks/base/lint.lua b/src/luarocks/base/lint.lua new file mode 100644 index 00000000..c9ea45ea --- /dev/null +++ b/src/luarocks/base/lint.lua @@ -0,0 +1,53 @@ + +--- Module implementing the LuaRocks "lint" command. +-- Utility function that checks syntax of the rockspec. +local lint = {} + +local util = require("luarocks.util") +local download = require("luarocks.download") +local fetch = require("luarocks.fetch") + +lint.help_summary = "Check syntax of a rockspec." +lint.help_arguments = "" +lint.help = [[ +This is a utility function that checks the syntax of a rockspec. + +It returns success or failure if the text of a rockspec is +syntactically correct. +]] + +function lint.command(flags, input) + if not input then + return nil, "Argument missing. "..util.see_help("lint") + end + + local filename = input + if not input:match(".rockspec$") then + local err + filename, err = download.download("rockspec", input:lower()) + if not filename then + return nil, err + end + end + + local rs, err = fetch.load_local_rockspec(filename) + if not rs then + return nil, "Failed loading rockspec: "..err + end + + local ok = true + + -- This should have been done in the type checker, + -- but it would break compatibility of other commands. + -- Making 'lint' alone be stricter shouldn't be a problem, + -- because extra-strict checks is what lint-type commands + -- are all about. + if not rs.description.license then + util.printerr("Rockspec has no license field.") + ok = false + end + + return ok, ok or filename.." failed consistency checks." +end + +return lint diff --git a/src/luarocks/base/list.lua b/src/luarocks/base/list.lua new file mode 100644 index 00000000..45f1a26f --- /dev/null +++ b/src/luarocks/base/list.lua @@ -0,0 +1,95 @@ + +--- Module implementing the LuaRocks "list" command. +-- Lists currently installed rocks. +local list = {} + +local search = require("luarocks.search") +local deps = require("luarocks.deps") +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local path = require("luarocks.path") + +list.help_summary = "List currently installed rocks." +list.help_arguments = "[--porcelain] " +list.help = [[ + is a substring of a rock name to filter by. + +--outdated List only rocks for which there is a + higher version available in the rocks server. + +--porcelain Produce machine-friendly output. +]] + +local function check_outdated(trees, query) + local results_installed = {} + for _, tree in ipairs(trees) do + search.manifest_search(results_installed, path.rocks_dir(tree), query) + end + local outdated = {} + for name, versions in util.sortedpairs(results_installed) do + versions = util.keys(versions) + table.sort(versions, deps.compare_versions) + local latest_installed = versions[1] + + local query_available = search.make_query(name:lower()) + query.exact_name = true + local results_available, err = search.search_repos(query_available) + + if results_available[name] then + local available_versions = util.keys(results_available[name]) + table.sort(available_versions, deps.compare_versions) + local latest_available = available_versions[1] + local latest_available_repo = results_available[name][latest_available][1].repo + + if deps.compare_versions(latest_available, latest_installed) then + table.insert(outdated, { name = name, installed = latest_installed, available = latest_available, repo = latest_available_repo }) + end + end + end + return outdated +end + +local function list_outdated(trees, query, porcelain) + util.title("Outdated rocks:", porcelain) + local outdated = check_outdated(trees, query) + for _, item in ipairs(outdated) do + if porcelain then + util.printout(item.name, item.installed, item.available, item.repo) + else + util.printout(item.name) + util.printout(" "..item.installed.." < "..item.available.." at "..item.repo) + util.printout() + end + end + return true +end + +--- Driver function for "list" command. +-- @param filter string or nil: A substring of a rock name to filter by. +-- @param version string or nil: a version may also be passed. +-- @return boolean: True if succeeded, nil on errors. +function list.command(flags, filter, version) + local query = search.make_query(filter and filter:lower() or "", version) + query.exact_name = false + local trees = cfg.rocks_trees + if flags["tree"] then + trees = { flags["tree"] } + end + + if flags["outdated"] then + return list_outdated(trees, query, flags["porcelain"]) + end + + local results = {} + for _, tree in ipairs(trees) do + local ok, err, errcode = search.manifest_search(results, path.rocks_dir(tree), query) + if not ok and errcode ~= "open" then + util.warning(err) + end + end + util.title("Installed rocks:", flags["porcelain"]) + search.print_results(results, flags["porcelain"]) + return true +end + +return list diff --git a/src/luarocks/base/make.lua b/src/luarocks/base/make.lua new file mode 100644 index 00000000..eb38bff0 --- /dev/null +++ b/src/luarocks/base/make.lua @@ -0,0 +1,86 @@ + +--- Module implementing the LuaRocks "make" command. +-- Builds sources in the current directory, but unlike "build", +-- it does not fetch sources, etc., assuming everything is +-- available in the current directory. +local make = {} + +local build = require("luarocks.build") +local fs = require("luarocks.fs") +local util = require("luarocks.util") +local cfg = require("luarocks.core.cfg") +local fetch = require("luarocks.fetch") +local pack = require("luarocks.pack") +local remove = require("luarocks.remove") +local deps = require("luarocks.deps") + +make.help_summary = "Compile package in current directory using a rockspec." +make.help_arguments = "[--pack-binary-rock] []" +make.help = [[ +Builds sources in the current directory, but unlike "build", +it does not fetch sources, etc., assuming everything is +available in the current directory. If no argument is given, +it looks for a rockspec in the current directory and in "rockspec/" +and "rockspecs/" subdirectories, picking the rockspec with newest version +or without version name. If rockspecs for different rocks are found +or there are several rockspecs without version, you must specify which to use, +through the command-line. + +This command is useful as a tool for debugging rockspecs. +To install rocks, you'll normally want to use the "install" and +"build" commands. See the help on those for details. + +--pack-binary-rock Do not install rock. Instead, produce a .rock file + with the contents of compilation in the current + directory. + +--keep Do not remove previously installed versions of the + rock after installing a new one. This behavior can + be made permanent by setting keep_other_versions=true + in the configuration file. + +--branch= Override the `source.branch` field in the loaded + rockspec. Allows to specify a different branch to + fetch. Particularly for SCM rocks. + +]] + +--- Driver function for "make" command. +-- @param name string: A local rockspec. +-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an +-- error message otherwise. exitcode is optionally returned. +function make.command(flags, rockspec) + assert(type(rockspec) == "string" or not rockspec) + + if not rockspec then + local err + rockspec, err = util.get_default_rockspec() + if not rockspec then + return nil, err + end + end + if not rockspec:match("rockspec$") then + return nil, "Invalid argument: 'make' takes a rockspec as a parameter. "..util.see_help("make") + end + + if flags["pack-binary-rock"] then + local rspec, err, errcode = fetch.load_rockspec(rockspec) + if not rspec then + return nil, err + end + return pack.pack_binary_rock(rspec.name, rspec.version, build.build_rockspec, rockspec, false, true, deps.get_deps_mode(flags)) + else + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + ok, err = build.build_rockspec(rockspec, false, true, deps.get_deps_mode(flags)) + if not ok then return nil, err end + local name, version = ok, err + if (not flags["keep"]) and not cfg.keep_other_versions then + local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) + if not ok then util.printerr(err) end + end + return name, version + end +end + +return make diff --git a/src/luarocks/base/new_version.lua b/src/luarocks/base/new_version.lua new file mode 100644 index 00000000..b13dbb97 --- /dev/null +++ b/src/luarocks/base/new_version.lua @@ -0,0 +1,199 @@ + +--- Module implementing the LuaRocks "new_version" command. +-- Utility function that writes a new rockspec, updating data from a previous one. +local new_version = {} + +local util = require("luarocks.util") +local download = require("luarocks.download") +local fetch = require("luarocks.fetch") +local persist = require("luarocks.persist") +local fs = require("luarocks.fs") +local type_check = require("luarocks.type_check") + +new_version.help_summary = "Auto-write a rockspec for a new version of a rock." +new_version.help_arguments = "[--tag=] [|] [] []" +new_version.help = [[ +This is a utility function that writes a new rockspec, updating data +from a previous one. + +If a package name is given, it downloads the latest rockspec from the +default server. If a rockspec is given, it uses it instead. If no argument +is given, it looks for a rockspec same way 'luarocks make' does. + +If the version number is not given and tag is passed using --tag, +it is used as the version, with 'v' removed from beginning. +Otherwise, it only increments the revision number of the given +(or downloaded) rockspec. + +If a URL is given, it replaces the one from the old rockspec with the +given URL. If a URL is not given and a new version is given, it tries +to guess the new URL by replacing occurrences of the version number +in the URL or tag. It also tries to download the new URL to determine +the new MD5 checksum. + +If a tag is given, it replaces the one from the old rockspec. If there is +an old tag but no new one passed, it is guessed in the same way URL is. + +WARNING: it writes the new rockspec to the current directory, +overwriting the file if it already exists. +]] + +local function try_replace(tbl, field, old, new) + if not tbl[field] then + return false + end + local old_field = tbl[field] + local new_field = tbl[field]:gsub(old, new) + if new_field ~= old_field then + util.printout("Guessing new '"..field.."' field as "..new_field) + tbl[field] = new_field + return true + end + return false +end + +-- Try to download source file using URL from a rockspec. +-- If it specified MD5, update it. +-- @return (true, false) if MD5 was not specified or it stayed same, +-- (true, true) if MD5 changed, (nil, string) on error. +local function check_url_and_update_md5(out_rs) + local file, temp_dir = fetch.fetch_url_at_temp_dir(out_rs.source.url, "luarocks-new-version-"..out_rs.package) + if not file then + util.printerr("Warning: invalid URL - "..temp_dir) + return true, false + end + + local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, out_rs.source.url, out_rs.source.dir) + if not inferred_dir then + return nil, found_dir + end + + if found_dir and found_dir ~= inferred_dir then + out_rs.source.dir = found_dir + end + + if file then + if out_rs.source.md5 then + util.printout("File successfully downloaded. Updating MD5 checksum...") + local new_md5, err = fs.get_md5(file) + if not new_md5 then + return nil, err + end + local old_md5 = out_rs.source.md5 + out_rs.source.md5 = new_md5 + return true, new_md5 ~= old_md5 + else + util.printout("File successfully downloaded.") + return true, false + end + end +end + +local function update_source_section(out_rs, url, tag, old_ver, new_ver) + if tag then + out_rs.source.tag = tag + end + if url then + out_rs.source.url = url + return check_url_and_update_md5(out_rs) + end + if new_ver == old_ver then + return true + end + if out_rs.source.dir then + try_replace(out_rs.source, "dir", old_ver, new_ver) + end + if out_rs.source.file then + try_replace(out_rs.source, "file", old_ver, new_ver) + end + if try_replace(out_rs.source, "url", old_ver, new_ver) then + return check_url_and_update_md5(out_rs) + end + if tag or try_replace(out_rs.source, "tag", old_ver, new_ver) then + return true + end + -- Couldn't replace anything significant, use the old URL. + local ok, md5_changed = check_url_and_update_md5(out_rs) + if not ok then + return nil, md5_changed + end + if md5_changed then + util.printerr("Warning: URL is the same, but MD5 has changed. Old rockspec is broken.") + end + return true +end + +function new_version.command(flags, input, version, url) + if not input then + local err + input, err = util.get_default_rockspec() + if not input then + return nil, err + end + end + assert(type(input) == "string") + + local filename, err + if input:match("rockspec$") then + filename, err = fetch.fetch_url(input) + if not filename then + return nil, err + end + else + filename, err = download.download("rockspec", input:lower()) + if not filename then + return nil, err + end + end + + local valid_rs, err = fetch.load_rockspec(filename) + if not valid_rs then + return nil, err + end + + local old_ver, old_rev = valid_rs.version:match("(.*)%-(%d+)$") + local new_ver, new_rev + + if flags.tag and not version then + version = flags.tag:gsub("^v", "") + end + + if version then + new_ver, new_rev = version:match("(.*)%-(%d+)$") + new_rev = tonumber(new_rev) + if not new_rev then + new_ver = version + new_rev = 1 + end + else + new_ver = old_ver + new_rev = tonumber(old_rev) + 1 + end + local new_rockver = new_ver:gsub("-", "") + + local out_rs, err = persist.load_into_table(filename) + local out_name = out_rs.package:lower() + out_rs.version = new_rockver.."-"..new_rev + + local ok, err = update_source_section(out_rs, url, flags.tag, old_ver, new_ver) + if not ok then return nil, err end + + if out_rs.build and out_rs.build.type == "module" then + out_rs.build.type = "builtin" + end + + local out_filename = out_name.."-"..new_rockver.."-"..new_rev..".rockspec" + + persist.save_from_table(out_filename, out_rs, type_check.rockspec_order) + + util.printout("Wrote "..out_filename) + + local valid_out_rs, err = fetch.load_local_rockspec(out_filename) + if not valid_out_rs then + return nil, "Failed loading generated rockspec: "..err + end + + return true +end + +return new_version diff --git a/src/luarocks/base/pack.lua b/src/luarocks/base/pack.lua new file mode 100644 index 00000000..655cbf37 --- /dev/null +++ b/src/luarocks/base/pack.lua @@ -0,0 +1,193 @@ + +--- Module implementing the LuaRocks "pack" command. +-- Creates a rock, packing sources or binaries. +local pack = {} + +local unpack = unpack or table.unpack + +local path = require("luarocks.path") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") +local manif = require("luarocks.manif") +local search = require("luarocks.search") + +pack.help_summary = "Create a rock, packing sources or binaries." +pack.help_arguments = "{| []}" +pack.help = [[ +Argument may be a rockspec file, for creating a source rock, +or the name of an installed package, for creating a binary rock. +In the latter case, the app version may be given as a second +argument. +]] + +--- Create a source rock. +-- Packages a rockspec and its required source files in a rock +-- file with the .src.rock extension, which can later be built and +-- installed with the "build" command. +-- @param rockspec_file string: An URL or pathname for a rockspec file. +-- @return string or (nil, string): The filename of the resulting +-- .src.rock file; or nil and an error message. +function pack.pack_source_rock(rockspec_file) + assert(type(rockspec_file) == "string") + + local rockspec, err = fetch.load_rockspec(rockspec_file) + if err then + return nil, "Error loading rockspec: "..err + end + rockspec_file = rockspec.local_filename + + local name_version = rockspec.name .. "-" .. rockspec.version + local rock_file = fs.absolute_name(name_version .. ".src.rock") + + local source_file, source_dir = fetch.fetch_sources(rockspec, false) + if not source_file then + return nil, source_dir + end + local ok, err = fs.change_dir(source_dir) + if not ok then return nil, err end + + fs.delete(rock_file) + fs.copy(rockspec_file, source_dir, cfg.perm_read) + if not fs.zip(rock_file, dir.base_name(rockspec_file), dir.base_name(source_file)) then + return nil, "Failed packing "..rock_file + end + fs.pop_dir() + + return rock_file +end + +local function copy_back_files(name, version, file_tree, deploy_dir, pack_dir, perms) + local ok, err = fs.make_dir(pack_dir) + if not ok then return nil, err end + for file, sub in pairs(file_tree) do + local source = dir.path(deploy_dir, file) + local target = dir.path(pack_dir, file) + if type(sub) == "table" then + local ok, err = copy_back_files(name, version, sub, source, target) + if not ok then return nil, err end + else + local versioned = path.versioned_name(source, deploy_dir, name, version) + if fs.exists(versioned) then + fs.copy(versioned, target, perms) + else + fs.copy(source, target, perms) + end + end + end + return true +end + +-- @param name string: Name of package to pack. +-- @param version string or nil: A version number may also be passed. +-- @param tree string or nil: An optional tree to pick the package from. +-- @return string or (nil, string): The filename of the resulting +-- .src.rock file; or nil and an error message. +local function do_pack_binary_rock(name, version, tree) + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local repo, repo_url + name, version, repo, repo_url = search.pick_installed_rock(name, version, tree) + if not name then + return nil, version + end + + local root = path.root_dir(repo_url) + local prefix = path.install_dir(name, version, root) + if not fs.exists(prefix) then + return nil, "'"..name.." "..version.."' does not seem to be an installed rock." + end + + local rock_manifest, err = manif.load_rock_manifest(name, version, root) + if not rock_manifest then return nil, err end + + local name_version = name .. "-" .. version + local rock_file = fs.absolute_name(name_version .. "."..cfg.arch..".rock") + + local temp_dir = fs.make_temp_dir("pack") + fs.copy_contents(prefix, temp_dir) + + local is_binary = false + if rock_manifest.lib then + local ok, err = copy_back_files(name, version, rock_manifest.lib, path.deploy_lib_dir(root), dir.path(temp_dir, "lib"), cfg.perm_exec) + if not ok then return nil, "Failed copying back files: " .. err end + is_binary = true + end + if rock_manifest.lua then + local ok, err = copy_back_files(name, version, rock_manifest.lua, path.deploy_lua_dir(root), dir.path(temp_dir, "lua"), cfg.perm_read) + if not ok then return nil, "Failed copying back files: " .. err end + end + + local ok, err = fs.change_dir(temp_dir) + if not ok then return nil, err end + if not is_binary and not repos.has_binaries(name, version) then + rock_file = rock_file:gsub("%."..cfg.arch:gsub("%-","%%-").."%.", ".all.") + end + fs.delete(rock_file) + if not fs.zip(rock_file, unpack(fs.list_dir())) then + return nil, "Failed packing "..rock_file + end + fs.pop_dir() + fs.delete(temp_dir) + return rock_file +end + +function pack.pack_binary_rock(name, version, cmd, ...) + + -- The --pack-binary-rock option for "luarocks build" basically performs + -- "luarocks build" on a temporary tree and then "luarocks pack". The + -- alternative would require refactoring parts of luarocks.build and + -- luarocks.pack, which would save a few file operations: the idea would be + -- to shave off the final deploy steps from the build phase and the initial + -- collect steps from the pack phase. + + local temp_dir, err = fs.make_temp_dir("luarocks-build-pack-"..dir.base_name(name)) + if not temp_dir then + return nil, "Failed creating temporary directory: "..err + end + util.schedule_function(fs.delete, temp_dir) + + path.use_tree(temp_dir) + local ok, err = cmd(...) + if not ok then + return nil, err + end + local rname, rversion = path.parse_name(name) + if not rname then + rname, rversion = name, version + end + return do_pack_binary_rock(rname, rversion, temp_dir) +end + +--- Driver function for the "pack" command. +-- @param arg string: may be a rockspec file, for creating a source rock, +-- or the name of an installed package, for creating a binary rock. +-- @param version string or nil: if the name of a package is given, a +-- version may also be passed. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +function pack.command(flags, arg, version) + assert(type(version) == "string" or not version) + if type(arg) ~= "string" then + return nil, "Argument missing. "..util.see_help("pack") + end + + local file, err + if arg:match(".*%.rockspec") then + file, err = pack.pack_source_rock(arg) + else + file, err = do_pack_binary_rock(arg:lower(), version, flags["tree"]) + end + if err then + return nil, err + else + util.printout("Packed: "..file) + return true + end +end + +return pack diff --git a/src/luarocks/base/path_cmd.lua b/src/luarocks/base/path_cmd.lua new file mode 100644 index 00000000..516a0c47 --- /dev/null +++ b/src/luarocks/base/path_cmd.lua @@ -0,0 +1,68 @@ + +--- @module luarocks.path_cmd +-- Driver for the `luarocks path` command. +local path_cmd = {} + +local util = require("luarocks.util") +local cfg = require("luarocks.core.cfg") + +path_cmd.help_summary = "Return the currently configured package path." +path_cmd.help_arguments = "" +path_cmd.help = [[ +Returns the package path currently configured for this installation +of LuaRocks, formatted as shell commands to update LUA_PATH and LUA_CPATH. + +--bin Adds the system path to the output + +--append Appends the paths to the existing paths. Default is to prefix + the LR paths to the existing paths. + +--lr-path Exports the Lua path (not formatted as shell command) + +--lr-cpath Exports the Lua cpath (not formatted as shell command) + +--lr-bin Exports the system path (not formatted as shell command) + + +On Unix systems, you may run: + eval `luarocks path` +And on Windows: + luarocks path > "%temp%\_lrp.bat" && call "%temp%\_lrp.bat" && del "%temp%\_lrp.bat" +]] + +--- Driver function for "path" command. +-- @return boolean This function always succeeds. +function path_cmd.command(flags) + local lr_path, lr_cpath, lr_bin = cfg.package_paths(flags["tree"]) + local path_sep = cfg.export_path_separator + + if flags["lr-path"] then + util.printout(util.remove_path_dupes(lr_path, ';')) + return true + elseif flags["lr-cpath"] then + util.printout(util.remove_path_dupes(lr_cpath, ';')) + return true + elseif flags["lr-bin"] then + util.printout(util.remove_path_dupes(lr_bin, path_sep)) + return true + end + + if flags["append"] then + lr_path = package.path .. ";" .. lr_path + lr_cpath = package.cpath .. ";" .. lr_cpath + lr_bin = os.getenv("PATH") .. path_sep .. lr_bin + else + lr_path = lr_path.. ";" .. package.path + lr_cpath = lr_cpath .. ";" .. package.cpath + lr_bin = lr_bin .. path_sep .. os.getenv("PATH") + end + + util.printout(cfg.export_lua_path:format(util.remove_path_dupes(lr_path, ';'))) + util.printout(cfg.export_lua_cpath:format(util.remove_path_dupes(lr_cpath, ';'))) + if flags["bin"] then + util.printout(cfg.export_path:format(util.remove_path_dupes(lr_bin, path_sep))) + end + return true +end + +return path_cmd diff --git a/src/luarocks/base/purge.lua b/src/luarocks/base/purge.lua new file mode 100644 index 00000000..50f290c8 --- /dev/null +++ b/src/luarocks/base/purge.lua @@ -0,0 +1,77 @@ + +--- Module implementing the LuaRocks "purge" command. +-- Remove all rocks from a given tree. +local purge = {} + +local util = require("luarocks.util") +local fs = require("luarocks.fs") +local path = require("luarocks.path") +local search = require("luarocks.search") +local deps = require("luarocks.deps") +local repos = require("luarocks.repos") +local writer = require("luarocks.manif.writer") +local cfg = require("luarocks.core.cfg") +local remove = require("luarocks.remove") + +purge.help_summary = "Remove all installed rocks from a tree." +purge.help_arguments = "--tree= [--old-versions]" +purge.help = [[ +This command removes rocks en masse from a given tree. +By default, it removes all rocks from a tree. + +The --tree argument is mandatory: luarocks purge does not +assume a default tree. + +--old-versions Keep the highest-numbered version of each + rock and remove the other ones. By default + it only removes old versions if they are + not needed as dependencies. This can be + overridden with the flag --force. +]] + +function purge.command(flags) + local tree = flags["tree"] + + if type(tree) ~= "string" then + return nil, "The --tree argument is mandatory. "..util.see_help("purge") + end + + local results = {} + local query = search.make_query("") + query.exact_name = false + if not fs.is_dir(tree) then + return nil, "Directory not found: "..tree + end + + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + + search.manifest_search(results, path.rocks_dir(tree), query) + + local sort = function(a,b) return deps.compare_versions(b,a) end + if flags["old-versions"] then + sort = deps.compare_versions + end + + for package, versions in util.sortedpairs(results) do + for version, _ in util.sortedpairs(versions, sort) do + if flags["old-versions"] then + util.printout("Keeping "..package.." "..version.."...") + local ok, err = remove.remove_other_versions(package, version, flags["force"], flags["force-fast"]) + if not ok then + util.printerr(err) + end + break + else + util.printout("Removing "..package.." "..version.."...") + local ok, err = repos.delete_version(package, version, "none", true) + if not ok then + util.printerr(err) + end + end + end + end + return writer.make_manifest(cfg.rocks_dir, "one") +end + +return purge diff --git a/src/luarocks/base/remove.lua b/src/luarocks/base/remove.lua new file mode 100644 index 00000000..e7f37604 --- /dev/null +++ b/src/luarocks/base/remove.lua @@ -0,0 +1,165 @@ + +--- Module implementing the LuaRocks "remove" command. +-- Uninstalls rocks. +local remove = {} + +local search = require("luarocks.search") +local deps = require("luarocks.deps") +local fetch = require("luarocks.fetch") +local repos = require("luarocks.repos") +local path = require("luarocks.path") +local util = require("luarocks.util") +local cfg = require("luarocks.core.cfg") +local fs = require("luarocks.fs") + +remove.help_summary = "Uninstall a rock." +remove.help_arguments = "[--force|--force-fast] []" +remove.help = [[ +Argument is the name of a rock to be uninstalled. +If a version is not given, try to remove all versions at once. +Will only perform the removal if it does not break dependencies. +To override this check and force the removal, use --force. +To perform a forced removal without reporting dependency issues, +use --force-fast. + +]]..util.deps_mode_help() + +--- Obtain a list of packages that depend on the given set of packages +-- (where all packages of the set are versions of one program). +-- @param name string: the name of a program +-- @param versions array of string: the versions to be deleted. +-- @return array of string: an empty table if no packages depend on any +-- of the given list, or an array of strings in "name/version" format. +local function check_dependents(name, versions, deps_mode) + local dependents = {} + local blacklist = {} + blacklist[name] = {} + for version, _ in pairs(versions) do + blacklist[name][version] = true + end + local local_rocks = {} + local query_all = search.make_query("") + query_all.exact_name = false + search.manifest_search(local_rocks, cfg.rocks_dir, query_all) + local_rocks[name] = nil + for rock_name, rock_versions in pairs(local_rocks) do + for rock_version, _ in pairs(rock_versions) do + local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version)) + if rockspec then + local _, missing = deps.match_deps(rockspec, blacklist, deps_mode) + if missing[name] then + table.insert(dependents, { name = rock_name, version = rock_version }) + end + end + end + end + return dependents +end + +--- Delete given versions of a program. +-- @param name string: the name of a program +-- @param versions array of string: the versions to be deleted. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @return boolean or (nil, string): true on success or nil and an error message. +local function delete_versions(name, versions, deps_mode) + + for version, _ in pairs(versions) do + util.printout("Removing "..name.." "..version.."...") + local ok, err = repos.delete_version(name, version, deps_mode) + if not ok then return nil, err end + end + + return true +end + +function remove.remove_search_results(results, name, deps_mode, force, fast) + local versions = results[name] + + local version = next(versions) + local second = next(versions, version) + + local dependents = {} + if not fast then + util.printout("Checking stability of dependencies in the absence of") + util.printout(name.." "..table.concat(util.keys(versions), ", ").."...") + util.printout() + dependents = check_dependents(name, versions, deps_mode) + end + + if #dependents > 0 then + if force or fast then + util.printerr("The following packages may be broken by this forced removal:") + for _, dependent in ipairs(dependents) do + util.printerr(dependent.name.." "..dependent.version) + end + util.printerr() + else + if not second then + util.printerr("Will not remove "..name.." "..version..".") + util.printerr("Removing it would break dependencies for: ") + else + util.printerr("Will not remove installed versions of "..name..".") + util.printerr("Removing them would break dependencies for: ") + end + for _, dependent in ipairs(dependents) do + util.printerr(dependent.name.." "..dependent.version) + end + util.printerr() + util.printerr("Use --force to force removal (warning: this may break modules).") + return nil, "Failed removing." + end + end + + local ok, err = delete_versions(name, versions, deps_mode) + if not ok then return nil, err end + + util.printout("Removal successful.") + return true +end + +function remove.remove_other_versions(name, version, force, fast) + local results = {} + search.manifest_search(results, cfg.rocks_dir, { name = name, exact_name = true, constraints = {{ op = "~=", version = version}} }) + if results[name] then + return remove.remove_search_results(results, name, cfg.deps_mode, force, fast) + end + return true +end + +--- Driver function for the "remove" command. +-- @param name string: name of a rock. If a version is given, refer to +-- a specific version; otherwise, try to remove all versions. +-- @param version string: When passing a package name, a version number +-- may also be given. +-- @return boolean or (nil, string, exitcode): True if removal was +-- successful, nil and an error message otherwise. exitcode is optionally returned. +function remove.command(flags, name, version) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("remove") + end + + local deps_mode = flags["deps-mode"] or cfg.deps_mode + + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + + local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$") + local filename = name + if rock_type then + name, version = path.parse_name(filename) + if not name then return nil, "Invalid "..rock_type.." filename: "..filename end + end + + local results = {} + name = name:lower() + search.manifest_search(results, cfg.rocks_dir, search.make_query(name, version)) + if not results[name] then + return nil, "Could not find rock '"..name..(version and " "..version or "").."' in "..path.rocks_tree_to_string(cfg.root_dir) + end + + return remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"]) +end + +return remove diff --git a/src/luarocks/base/search.lua b/src/luarocks/base/search.lua new file mode 100644 index 00000000..44eff694 --- /dev/null +++ b/src/luarocks/base/search.lua @@ -0,0 +1,482 @@ + +--- Module implementing the LuaRocks "search" command. +-- Queries LuaRocks servers. +local search = {} + + +local dir = require("luarocks.dir") +local path = require("luarocks.path") +local manif = require("luarocks.manif") +local deps = require("luarocks.deps") +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") + +search.help_summary = "Query the LuaRocks servers." +search.help_arguments = "[--source] [--binary] { [] | --all }" +search.help = [[ +--source Return only rockspecs and source rocks, + to be used with the "build" command. +--binary Return only pure Lua and binary rocks (rocks that can be used + with the "install" command without requiring a C toolchain). +--all List all contents of the server that are suitable to + this platform, do not filter by name. +]] + +--- Convert the arch field of a query table to table format. +-- @param query table: A query table. +local function query_arch_as_table(query) + local format = type(query.arch) + if format == "table" then + return + elseif format == "nil" then + local accept = {} + accept["src"] = true + accept["all"] = true + accept["rockspec"] = true + accept["installed"] = true + accept[cfg.arch] = true + query.arch = accept + elseif format == "string" then + local accept = {} + for a in query.arch:gmatch("[%w_-]+") do + accept[a] = true + end + query.arch = accept + end +end + +--- Store a search result (a rock or rockspec) in the results table. +-- @param results table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- @param name string: Package name. +-- @param version string: Package version. +-- @param arch string: Architecture of rock ("all", "src" or platform +-- identifier), "rockspec" or "installed" +-- @param repo string: Pathname of a local repository of URL of +-- rocks server. +local function store_result(results, name, version, arch, repo) + assert(type(results) == "table") + assert(type(name) == "string") + assert(type(version) == "string") + assert(type(arch) == "string") + assert(type(repo) == "string") + + if not results[name] then results[name] = {} end + if not results[name][version] then results[name][version] = {} end + table.insert(results[name][version], { + arch = arch, + repo = repo + }) +end + +--- Test the name field of a query. +-- If query has a boolean field exact_name set to false, +-- then substring match is performed; otherwise, exact string +-- comparison is done. +-- @param query table: A query in dependency table format. +-- @param name string: A package name. +-- @return boolean: True if names match, false otherwise. +local function match_name(query, name) + assert(type(query) == "table") + assert(type(name) == "string") + if query.exact_name == false then + return name:find(query.name, 0, true) and true or false + else + return name == query.name + end +end + +--- Store a match in a results table if version matches query. +-- Name, version, arch and repository path are stored in a given +-- table, optionally checking if version and arch (if given) match +-- a query. +-- @param results table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- @param repo string: URL or pathname of the repository. +-- @param name string: The name of the package being tested. +-- @param version string: The version of the package being tested. +-- @param arch string: The arch of the package being tested. +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- If the arch field is omitted, the local architecture (cfg.arch) +-- is used. The special value "any" is also recognized, returning all +-- matches regardless of architecture. +local function store_if_match(results, repo, name, version, arch, query) + if match_name(query, name) then + if query.arch[arch] or query.arch["any"] then + if deps.match_constraints(deps.parse_version(version), query.constraints) then + store_result(results, name, version, arch, repo) + end + end + end +end + +--- Perform search on a local repository. +-- @param repo string: The pathname of the local repository. +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- If the arch field is omitted, the local architecture (cfg.arch) +-- is used. The special value "any" is also recognized, returning all +-- matches regardless of architecture. +-- @param results table or nil: If given, this table will store the +-- results; if not given, a new table will be created. +-- @return table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- If a table was given in the "results" parameter, that is the result value. +function search.disk_search(repo, query, results) + assert(type(repo) == "string") + assert(type(query) == "table") + assert(type(results) == "table" or not results) + + local fs = require("luarocks.fs") + + if not results then + results = {} + end + query_arch_as_table(query) + + for name in fs.dir(repo) do + local pathname = dir.path(repo, name) + local rname, rversion, rarch = path.parse_name(name) + + if rname and (pathname:match(".rockspec$") or pathname:match(".rock$")) then + store_if_match(results, repo, rname, rversion, rarch, query) + elseif fs.is_dir(pathname) then + for version in fs.dir(pathname) do + if version:match("-%d+$") then + store_if_match(results, repo, name, version, "installed", query) + end + end + end + end + return results +end + +--- Perform search on a rocks server or tree. +-- @param results table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- @param repo string: The URL of a rocks server or +-- the pathname of a rocks tree (as returned by path.rocks_dir()). +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- If the arch field is omitted, the local architecture (cfg.arch) +-- is used. The special value "any" is also recognized, returning all +-- matches regardless of architecture. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @return true or, in case of errors, nil, an error message and an optional error code. +function search.manifest_search(results, repo, query, lua_version) + assert(type(results) == "table") + assert(type(repo) == "string") + assert(type(query) == "table") + + query_arch_as_table(query) + local manifest, err, errcode = manif.load_manifest(repo, lua_version) + if not manifest then + return nil, err, errcode + end + for name, versions in pairs(manifest.repository) do + for version, items in pairs(versions) do + for _, item in ipairs(items) do + store_if_match(results, repo, name, version, item.arch, query) + end + end + end + return true +end + +--- Search on all configured rocks servers. +-- @param query table: A dependency query. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @return table: A table where keys are package names +-- and values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +function search.search_repos(query, lua_version) + assert(type(query) == "table") + + local results = {} + for _, repo in ipairs(cfg.rocks_servers) do + if not cfg.disabled_servers[repo] then + if type(repo) == "string" then + repo = { repo } + end + for _, mirror in ipairs(repo) do + local protocol, pathname = dir.split_url(mirror) + if protocol == "file" then + mirror = pathname + end + local ok, err, errcode = search.manifest_search(results, mirror, query, lua_version) + if errcode == "network" then + cfg.disabled_servers[repo] = true + end + if ok then + break + else + util.warning("Failed searching manifest: "..err) + end + end + end + end + -- search through rocks in cfg.rocks_provided + local provided_repo = "provided by VM or rocks_provided" + for name, versions in pairs(cfg.rocks_provided) do + store_if_match(results, provided_repo, name, versions, "installed", query) + end + return results +end + +--- Prepare a query in dependency table format. +-- @param name string: The query name. +-- @param version string or nil: +-- @return table: A query in table format +function search.make_query(name, version) + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local query = { + name = name, + constraints = {} + } + if version then + table.insert(query.constraints, { op = "==", version = deps.parse_version(version)}) + end + return query +end + +--- Get the URL for the latest in a set of versions. +-- @param name string: The package name to be used in the URL. +-- @param versions table: An array of version informations, as stored +-- in search results tables. +-- @return string or nil: the URL for the latest version if one could +-- be picked, or nil. +local function pick_latest_version(name, versions) + assert(type(name) == "string") + assert(type(versions) == "table") + + local vtables = {} + for v, _ in pairs(versions) do + table.insert(vtables, deps.parse_version(v)) + end + table.sort(vtables) + local version = vtables[#vtables].string + local items = versions[version] + if items then + local pick = 1 + for i, item in ipairs(items) do + if (item.arch == 'src' and items[pick].arch == 'rockspec') + or (item.arch ~= 'src' and item.arch ~= 'rockspec') then + pick = i + end + end + return path.make_url(items[pick].repo, name, version, items[pick].arch) + end + return nil +end + +-- Find out which other Lua versions provide rock versions matching a query, +-- @param query table: A dependency query matching a single rock. +-- @return table: array of Lua versions supported, in "5.x" format. +local function supported_lua_versions(query) + local results = {} + + for lua_version in util.lua_versions() do + if lua_version ~= cfg.lua_version then + if search.search_repos(query, lua_version)[query.name] then + table.insert(results, lua_version) + end + end + end + + return results +end + +--- Attempt to get a single URL for a given search for a rock. +-- @param query table: A dependency query matching a single rock. +-- @return string or (nil, string): URL for latest matching version +-- of the rock if it was found, or nil followed by an error message. +function search.find_suitable_rock(query) + assert(type(query) == "table") + + local results = search.search_repos(query) + local first_rock = next(results) + if not first_rock then + if cfg.rocks_provided[query.name] == nil then + -- Check if constraints are satisfiable with other Lua versions. + local lua_versions = supported_lua_versions(query) + + if #lua_versions ~= 0 then + -- Build a nice message in "only Lua 5.x and 5.y but not 5.z." format + for i, lua_version in ipairs(lua_versions) do + lua_versions[i] = "Lua "..lua_version + end + + local versions_message = "only "..table.concat(lua_versions, " and ").. + " but not Lua "..cfg.lua_version.."." + + if #query.constraints == 0 then + return nil, query.name.." supports "..versions_message + elseif #query.constraints == 1 and query.constraints[1].op == "==" then + return nil, query.name.." "..query.constraints[1].version.string.." supports "..versions_message + else + return nil, "Matching "..query.name.." versions support "..versions_message + end + end + end + + return nil, "No results matching query were found." + elseif next(results, first_rock) then + -- Shouldn't happen as query must match only one package. + return nil, "Several rocks matched query." + elseif cfg.rocks_provided[query.name] ~= nil then + -- Do not install versions listed in cfg.rocks_provided. + return nil, "Rock "..query.name.." "..cfg.rocks_provided[query.name].. + " was found but it is provided by VM or 'rocks_provided' in the config file." + else + return pick_latest_version(query.name, results[first_rock]) + end +end + +--- Print a list of rocks/rockspecs on standard output. +-- @param results table: A table where keys are package names and versions +-- are tables matching version strings to an array of rocks servers. +-- @param porcelain boolean or nil: A flag to force machine-friendly output. +function search.print_results(results, porcelain) + assert(type(results) == "table") + assert(type(porcelain) == "boolean" or not porcelain) + + for package, versions in util.sortedpairs(results) do + if not porcelain then + util.printout(package) + end + for version, repos in util.sortedpairs(versions, deps.compare_versions) do + for _, repo in ipairs(repos) do + repo.repo = dir.normalize(repo.repo) + if porcelain then + util.printout(package, version, repo.arch, repo.repo) + else + util.printout(" "..version.." ("..repo.arch..") - "..repo.repo) + end + end + end + if not porcelain then + util.printout() + end + end +end + +--- Splits a list of search results into two lists, one for "source" results +-- to be used with the "build" command, and one for "binary" results to be +-- used with the "install" command. +-- @param results table: A search results table. +-- @return (table, table): Two tables, one for source and one for binary +-- results. +local function split_source_and_binary_results(results) + local sources, binaries = {}, {} + for name, versions in pairs(results) do + for version, repositories in pairs(versions) do + for _, repo in ipairs(repositories) do + local where = sources + if repo.arch == "all" or repo.arch == cfg.arch then + where = binaries + end + store_result(where, name, version, repo.arch, repo.repo) + end + end + end + return sources, binaries +end + +--- Given a name and optionally a version, try to find in the rocks +-- servers a single .src.rock or .rockspec file that satisfies +-- the request, and run the given function on it; or display to the +-- user possibilities if it couldn't narrow down a single match. +-- @param action function: A function that takes a .src.rock or +-- .rockspec URL as a parameter. +-- @param name string: A rock name +-- @param version string or nil: A version number may also be given. +-- @return The result of the action function, or nil and an error message. +function search.act_on_src_or_rockspec(action, name, version, ...) + assert(type(action) == "function") + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local query = search.make_query(name, version) + query.arch = "src|rockspec" + local url, err = search.find_suitable_rock(query) + if not url then + return nil, "Could not find a result named "..name..(version and " "..version or "")..": "..err + end + return action(url, ...) +end + +function search.pick_installed_rock(name, version, given_tree) + local results = {} + local query = search.make_query(name, version) + query.exact_name = true + local tree_map = {} + local trees = cfg.rocks_trees + if given_tree then + trees = { given_tree } + end + for _, tree in ipairs(trees) do + local rocks_dir = path.rocks_dir(tree) + tree_map[rocks_dir] = tree + search.manifest_search(results, rocks_dir, query) + end + + if not next(results) then -- + return nil,"cannot find package "..name.." "..(version or "").."\nUse 'list' to find installed rocks." + end + + version = nil + local repo_url + local package, versions = util.sortedpairs(results)() + --question: what do we do about multiple versions? This should + --give us the latest version on the last repo (which is usually the global one) + for vs, repositories in util.sortedpairs(versions, deps.compare_versions) do + if not version then version = vs end + for _, rp in ipairs(repositories) do repo_url = rp.repo end + end + + local repo = tree_map[repo_url] + return name, version, repo, repo_url +end + +--- Driver function for "search" command. +-- @param name string: A substring of a rock name to search. +-- @param version string or nil: a version may also be passed. +-- @return boolean or (nil, string): True if build was successful; nil and an +-- error message otherwise. +function search.command(flags, name, version) + if flags["all"] then + name, version = "", nil + end + + if type(name) ~= "string" and not flags["all"] then + return nil, "Enter name and version or use --all. "..util.see_help("search") + end + + local query = search.make_query(name:lower(), version) + query.exact_name = false + local results, err = search.search_repos(query) + local porcelain = flags["porcelain"] + util.title("Search results:", porcelain, "=") + local sources, binaries = split_source_and_binary_results(results) + if next(sources) and not flags["binary"] then + util.title("Rockspecs and source rocks:", porcelain) + search.print_results(sources, porcelain) + end + if next(binaries) and not flags["source"] then + util.title("Binary and pure-Lua rocks:", porcelain) + search.print_results(binaries, porcelain) + end + return true +end + +return search diff --git a/src/luarocks/base/show.lua b/src/luarocks/base/show.lua new file mode 100644 index 00000000..1ff81e08 --- /dev/null +++ b/src/luarocks/base/show.lua @@ -0,0 +1,158 @@ +--- Module implementing the LuaRocks "show" command. +-- Shows information about an installed rock. +local show = {} + +local search = require("luarocks.search") +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local path = require("luarocks.path") +local deps = require("luarocks.deps") +local fetch = require("luarocks.fetch") +local manif = require("luarocks.manif") + +show.help_summary = "Show information about an installed rock." + +show.help = [[ + is an existing package name. +Without any flags, show all module information. +With these flags, return only the desired information: + +--home home page of project +--modules all modules provided by this package as used by require() +--deps packages this package depends on +--rockspec the full path of the rockspec file +--mversion the package version +--rock-tree local tree where rock is installed +--rock-dir data directory of the installed rock +]] + +local function keys_as_string(t, sep) + local keys = util.keys(t) + table.sort(keys) + return table.concat(keys, sep or " ") +end + +local function word_wrap(line) + local width = tonumber(os.getenv("COLUMNS")) or 80 + if width > 80 then width = 80 end + if #line > width then + local brk = width + while brk > 0 and line:sub(brk, brk) ~= " " do + brk = brk - 1 + end + if brk > 0 then + return line:sub(1, brk-1) .. "\n" .. word_wrap(line:sub(brk+1)) + end + end + return line +end + +local function format_text(text) + text = text:gsub("^%s*",""):gsub("%s$", ""):gsub("\n[ \t]+","\n"):gsub("([^\n])\n([^\n])","%1 %2") + local paragraphs = util.split_string(text, "\n\n") + for n, line in ipairs(paragraphs) do + paragraphs[n] = word_wrap(line) + end + return (table.concat(paragraphs, "\n\n"):gsub("%s$", "")) +end + +local function installed_rock_label(name, tree) + local installed, version + if cfg.rocks_provided[name] then + installed, version = true, cfg.rocks_provided[name] + else + installed, version = search.pick_installed_rock(name, nil, tree) + end + return installed and "(using "..version..")" or "(missing)" +end + +--- Driver function for "show" command. +-- @param name or nil: an existing package name. +-- @param version string or nil: a version may also be passed. +-- @return boolean: True if succeeded, nil on errors. +function show.command(flags, name, version) + if not name then + return nil, "Argument missing. "..util.see_help("show") + end + + local repo, repo_url + name, version, repo, repo_url = search.pick_installed_rock(name:lower(), version, flags["tree"]) + if not name then + return nil, version + end + + local directory = path.install_dir(name,version,repo) + local rockspec_file = path.rockspec_file(name, version, repo) + local rockspec, err = fetch.load_local_rockspec(rockspec_file) + if not rockspec then return nil,err end + + local descript = rockspec.description or {} + local manifest, err = manif.load_manifest(repo_url) + if not manifest then return nil,err end + local minfo = manifest.repository[name][version][1] + + if flags["rock-tree"] then util.printout(path.rocks_tree_to_string(repo)) + elseif flags["rock-dir"] then util.printout(directory) + elseif flags["home"] then util.printout(descript.homepage) + elseif flags["issues"] then util.printout(descript.issues_url) + elseif flags["labels"] then util.printout(descript.labels and table.concat(descript.labels, "\n")) + elseif flags["modules"] then util.printout(keys_as_string(minfo.modules, "\n")) + elseif flags["deps"] then util.printout(keys_as_string(minfo.dependencies)) + elseif flags["rockspec"] then util.printout(rockspec_file) + elseif flags["mversion"] then util.printout(version) + else + util.printout() + util.printout(rockspec.package.." "..rockspec.version.." - "..(descript.summary or "")) + util.printout() + if descript.detailed then + util.printout(format_text(descript.detailed)) + util.printout() + end + if descript.license then + util.printout("License: ", descript.license) + end + if descript.homepage then + util.printout("Homepage: ", descript.homepage) + end + if descript.issues_url then + util.printout("Issues: ", descript.issues_url) + end + if descript.labels then + util.printout("Labels: ", table.concat(descript.labels, ", ")) + end + util.printout("Installed in: ", path.rocks_tree_to_string(repo)) + if next(minfo.modules) then + util.printout() + util.printout("Modules:") + for mod, filename in util.sortedpairs(minfo.modules) do + util.printout("\t"..mod.." ("..path.which(mod, filename, name, version, repo, manifest)..")") + end + end + local direct_deps = {} + if #rockspec.dependencies > 0 then + util.printout() + util.printout("Depends on:") + for _, dep in ipairs(rockspec.dependencies) do + direct_deps[dep.name] = true + util.printout("\t"..deps.show_dep(dep).." "..installed_rock_label(dep.name, flags["tree"])) + end + end + local has_indirect_deps + for dep_name in util.sortedpairs(minfo.dependencies) do + if not direct_deps[dep_name] then + if not has_indirect_deps then + util.printout() + util.printout("Indirectly pulling:") + has_indirect_deps = true + end + + util.printout("\t"..dep_name.." "..installed_rock_label(dep_name, flags["tree"])) + end + end + util.printout() + end + return true +end + + +return show diff --git a/src/luarocks/base/unpack.lua b/src/luarocks/base/unpack.lua new file mode 100644 index 00000000..c50701b0 --- /dev/null +++ b/src/luarocks/base/unpack.lua @@ -0,0 +1,164 @@ + +--- Module implementing the LuaRocks "unpack" command. +-- Unpack the contents of a rock. +local unpack = {} + +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local util = require("luarocks.util") +local build = require("luarocks.build") +local dir = require("luarocks.dir") +local cfg = require("luarocks.core.cfg") + +unpack.help_summary = "Unpack the contents of a rock." +unpack.help_arguments = "[--force] {| []}" +unpack.help = [[ +Unpacks the contents of a rock in a newly created directory. +Argument may be a rock file, or the name of a rock in a rocks server. +In the latter case, the app version may be given as a second argument. + +--force Unpack files even if the output directory already exists. +]] + +--- Load a rockspec file to the given directory, fetches the source +-- files specified in the rockspec, and unpack them inside the directory. +-- @param rockspec_file string: The URL for a rockspec file. +-- @param dir_name string: The directory where to store and unpack files. +-- @return table or (nil, string): the loaded rockspec table or +-- nil and an error message. +local function unpack_rockspec(rockspec_file, dir_name) + assert(type(rockspec_file) == "string") + assert(type(dir_name) == "string") + + local rockspec, err = fetch.load_rockspec(rockspec_file) + if not rockspec then + return nil, "Failed loading rockspec "..rockspec_file..": "..err + end + local ok, err = fs.change_dir(dir_name) + if not ok then return nil, err end + local ok, sources_dir = fetch.fetch_sources(rockspec, true, ".") + if not ok then + return nil, sources_dir + end + ok, err = fs.change_dir(sources_dir) + if not ok then return nil, err end + ok, err = build.apply_patches(rockspec) + fs.pop_dir() + if not ok then return nil, err end + return rockspec +end + +--- Load a .rock file to the given directory and unpack it inside it. +-- @param rock_file string: The URL for a .rock file. +-- @param dir_name string: The directory where to unpack. +-- @param kind string: the kind of rock file, as in the second-level +-- extension in the rock filename (eg. "src", "all", "linux-x86") +-- @return table or (nil, string): the loaded rockspec table or +-- nil and an error message. +local function unpack_rock(rock_file, dir_name, kind) + assert(type(rock_file) == "string") + assert(type(dir_name) == "string") + + local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, dir_name) + if not ok then + return nil, "Failed unzipping rock "..rock_file, errcode + end + ok, err = fs.change_dir(dir_name) + if not ok then return nil, err end + local rockspec_file = dir_name..".rockspec" + local rockspec, err = fetch.load_rockspec(rockspec_file) + if not rockspec then + return nil, "Failed loading rockspec "..rockspec_file..": "..err + end + if kind == "src" then + if rockspec.source.file then + local ok, err = fs.unpack_archive(rockspec.source.file) + if not ok then + return nil, err + end + ok, err = fs.change_dir(rockspec.source.dir) + if not ok then return nil, err end + ok, err = build.apply_patches(rockspec) + fs.pop_dir() + if not ok then return nil, err end + end + end + return rockspec +end + +--- Create a directory and perform the necessary actions so that +-- the sources for the rock and its rockspec are unpacked inside it, +-- laid out properly so that the 'make' command is able to build the module. +-- @param file string: A rockspec or .rock URL. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +local function run_unpacker(file, force) + assert(type(file) == "string") + + local base_name = dir.base_name(file) + local dir_name, kind, extension = base_name:match("(.*)%.([^.]+)%.(rock)$") + if not extension then + dir_name, extension = base_name:match("(.*)%.(rockspec)$") + kind = "rockspec" + end + if not extension then + return nil, file.." does not seem to be a valid filename." + end + + local exists = fs.exists(dir_name) + if exists and not force then + return nil, "Directory "..dir_name.." already exists." + end + if not exists then + local ok, err = fs.make_dir(dir_name) + if not ok then return nil, err end + end + local rollback = util.schedule_function(fs.delete, fs.absolute_name(dir_name)) + + local rockspec, err + if extension == "rock" then + rockspec, err = unpack_rock(file, dir_name, kind) + elseif extension == "rockspec" then + rockspec, err = unpack_rockspec(file, dir_name) + end + if not rockspec then + return nil, err + end + if kind == "src" or kind == "rockspec" then + if rockspec.source.dir ~= "." then + local ok = fs.copy(rockspec.local_filename, rockspec.source.dir, cfg.perm_read) + if not ok then + return nil, "Failed copying unpacked rockspec into unpacked source directory." + end + end + util.printout() + util.printout("Done. You may now enter directory ") + util.printout(dir.path(dir_name, rockspec.source.dir)) + util.printout("and type 'luarocks make' to build.") + end + util.remove_scheduled_function(rollback) + return true +end + +--- Driver function for the "unpack" command. +-- @param name string: may be a rock filename, for unpacking a +-- rock file or the name of a rock to be fetched and unpacked. +-- @param version string or nil: if the name of a package is given, a +-- version may also be passed. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +function unpack.command(flags, name, version) + assert(type(version) == "string" or not version) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("unpack") + end + + if name:match(".*%.rock") or name:match(".*%.rockspec") then + return run_unpacker(name, flags["force"]) + else + local search = require("luarocks.search") + return search.act_on_src_or_rockspec(run_unpacker, name:lower(), version) + end +end + +return unpack diff --git a/src/luarocks/base/upload.lua b/src/luarocks/base/upload.lua new file mode 100644 index 00000000..baee47ab --- /dev/null +++ b/src/luarocks/base/upload.lua @@ -0,0 +1,94 @@ + +local upload = {} + +local util = require("luarocks.util") +local fetch = require("luarocks.fetch") +local pack = require("luarocks.pack") +local cfg = require("luarocks.core.cfg") +local Api = require("luarocks.upload.api") + +upload.help_summary = "Upload a rockspec to the public rocks repository." +upload.help_arguments = "[--skip-pack] [--api-key=] [--force] " +upload.help = [[ + Pack a source rock file (.src.rock extension), + upload rockspec and source rock to server. +--skip-pack Do not pack and send source rock. +--api-key= Give it an API key. It will be stored for subsequent uses. +--force Replace existing rockspec if the same revision of + a module already exists. This should be used only + in case of upload mistakes: when updating a rockspec, + increment the revision number instead. +]] + +function upload.command(flags, fname) + if not fname then + return nil, "Missing rockspec. "..util.see_help("upload") + end + + local api, err = Api.new(flags) + if not api then + return nil, err + end + if cfg.verbose then + api.debug = true + end + + local rockspec, err, errcode = fetch.load_rockspec(fname) + if err then + return nil, err, errcode + end + + util.printout("Sending " .. tostring(fname) .. " ...") + local res, err = api:method("check_rockspec", { + package = rockspec.package, + version = rockspec.version + }) + if not res then return nil, err end + + if not res.module then + util.printout("Will create new module (" .. tostring(rockspec.package) .. ")") + end + if res.version and not flags["force"] then + return nil, "Revision "..rockspec.version.." already exists on the server. "..util.see_help("upload") + end + + local rock_fname + if not flags["skip-pack"] and not rockspec.version:match("^scm") then + util.printout("Packing " .. tostring(rockspec.package)) + rock_fname, err = pack.pack_source_rock(fname) + if not rock_fname then + return nil, err + end + end + + local multipart = require("luarocks.upload.multipart") + + res, err = api:method("upload", nil, { + rockspec_file = multipart.new_file(fname) + }) + if not res then return nil, err end + + if res.is_new and #res.manifests == 0 then + util.printerr("Warning: module not added to root manifest due to name taken.") + end + + local module_url = res.module_url + + if rock_fname then + if (not res.version) or (not res.version.id) then + return nil, "Invalid response from server." + end + util.printout(("Sending " .. tostring(rock_fname) .. " ...")) + res, err = api:method("upload_rock/" .. ("%d"):format(res.version.id), nil, { + rock_file = multipart.new_file(rock_fname) + }) + if not res then return nil, err end + end + + util.printout() + util.printout("Done: " .. tostring(module_url)) + util.printout() + return true +end + +return upload diff --git a/src/luarocks/base/write_rockspec.lua b/src/luarocks/base/write_rockspec.lua new file mode 100644 index 00000000..be563eaa --- /dev/null +++ b/src/luarocks/base/write_rockspec.lua @@ -0,0 +1,376 @@ + +local write_rockspec = {} + +local cfg = require("luarocks.core.cfg") +local dir = require("luarocks.dir") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local path = require("luarocks.path") +local persist = require("luarocks.persist") +local type_check = require("luarocks.type_check") +local util = require("luarocks.util") +local deps = require("luarocks.deps") + +write_rockspec.help_summary = "Write a template for a rockspec file." +write_rockspec.help_arguments = "[--output= ...] [] [] [|]" +write_rockspec.help = [[ +This command writes an initial version of a rockspec file, +based on a name, a version, and a location (an URL or a local path). +If only two arguments are given, the first one is considered the name and the +second one is the location. +If only one argument is given, it must be the location. +If no arguments are given, current directory is used as location. +LuaRocks will attempt to infer name and version if not given, +using 'scm' as default version. + +Note that the generated file is a _starting point_ for writing a +rockspec, and is not guaranteed to be complete or correct. + +--output= Write the rockspec with the given filename. + If not given, a file is written in the current + directory with a filename based on given name and version. +--license="" A license string, such as "MIT/X11" or "GNU GPL v3". +--summary="" A short one-line description summary. +--detailed="" A longer description string. +--homepage= Project homepage. +--lua-version= Supported Lua versions. Accepted values are "5.1", "5.2", + "5.3", "5.1,5.2", "5.2,5.3", or "5.1,5.2,5.3". +--rockspec-format= Rockspec format version, such as "1.0" or "1.1". +--tag= Tag to use. Will attempt to extract version number from it. +--lib=[,] A comma-separated list of libraries that C files need to + link to. +]] + +local function open_file(name) + return io.open(dir.path(fs.current_dir(), name), "r") +end + +local function get_url(rockspec) + local file, temp_dir, err_code, err_file, err_temp_dir = fetch.fetch_sources(rockspec, false) + if err_code == "source.dir" then + file, temp_dir = err_file, err_temp_dir + elseif not file then + util.warning("Could not fetch sources - "..temp_dir) + return false + end + util.printout("File successfully downloaded. Making checksum and checking base dir...") + if fetch.is_basic_protocol(rockspec.source.protocol) then + rockspec.source.md5 = fs.get_md5(file) + end + local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, rockspec.source.url) + return true, found_dir or inferred_dir, temp_dir +end + +local function configure_lua_version(rockspec, luaver) + if luaver == "5.1" then + table.insert(rockspec.dependencies, "lua ~> 5.1") + elseif luaver == "5.2" then + table.insert(rockspec.dependencies, "lua ~> 5.2") + elseif luaver == "5.3" then + table.insert(rockspec.dependencies, "lua ~> 5.3") + elseif luaver == "5.1,5.2" then + table.insert(rockspec.dependencies, "lua >= 5.1, < 5.3") + elseif luaver == "5.2,5.3" then + table.insert(rockspec.dependencies, "lua >= 5.2, < 5.4") + elseif luaver == "5.1,5.2,5.3" then + table.insert(rockspec.dependencies, "lua >= 5.1, < 5.4") + else + util.warning("Please specify supported Lua version with --lua-version=. "..util.see_help("write_rockspec")) + end +end + +local function detect_description() + local fd = open_file("README.md") or open_file("README") + if not fd then return end + local data = fd:read("*a") + fd:close() + local paragraph = data:match("\n\n([^%[].-)\n\n") + if not paragraph then paragraph = data:match("\n\n(.*)") end + local summary, detailed + if paragraph then + detailed = paragraph + + if #paragraph < 80 then + summary = paragraph:gsub("\n", "") + else + summary = paragraph:gsub("\n", " "):match("([^.]*%.) ") + end + end + return summary, detailed +end + +local function detect_mit_license(data) + local strip_copyright = (data:gsub("Copyright [^\n]*\n", "")) + local sum = 0 + for i = 1, #strip_copyright do + local num = string.byte(strip_copyright:sub(i,i)) + if num > 32 and num <= 128 then + sum = sum + num + end + end + return sum == 78656 +end + +local simple_scm_protocols = { + git = true, ["git+http"] = true, ["git+https"] = true, + hg = true, ["hg+http"] = true, ["hg+https"] = true +} + +local function detect_url_from_command(program, args, directory) + local command = fs.Q(cfg.variables[program:upper()]).. " "..args + local pipe = io.popen(fs.command_at(directory, fs.quiet_stderr(command))) + if not pipe then return nil end + local url = pipe:read("*a"):match("^([^\r\n]+)") + pipe:close() + if not url then return nil end + if not util.starts_with(url, program.."://") then + url = program.."+"..url + end + + if simple_scm_protocols[dir.split_url(url)] then + return url + end +end + +local function detect_scm_url(directory) + return detect_url_from_command("git", "config --get remote.origin.url", directory) or + detect_url_from_command("hg", "paths default", directory) +end + +local function show_license(rockspec) + local fd = open_file("COPYING") or open_file("LICENSE") or open_file("MIT-LICENSE.txt") + if not fd then return nil end + local data = fd:read("*a") + fd:close() + local is_mit = detect_mit_license(data) + util.title("License for "..rockspec.package..":") + util.printout(data) + util.printout() + return is_mit +end + +local function get_cmod_name(file) + local fd = open_file(file) + if not fd then return nil end + local data = fd:read("*a") + fd:close() + return (data:match("int%s+luaopen_([a-zA-Z0-9_]+)")) +end + +local luamod_blacklist = { + test = true, + tests = true, +} + +local function fill_as_builtin(rockspec, libs) + rockspec.build.type = "builtin" + rockspec.build.modules = {} + local prefix = "" + + for _, parent in ipairs({"src", "lua"}) do + if fs.is_dir(parent) then + fs.change_dir(parent) + prefix = parent.."/" + break + end + end + + local incdirs, libdirs + if libs then + incdirs, libdirs = {}, {} + for _, lib in ipairs(libs) do + local upper = lib:upper() + incdirs[#incdirs+1] = "$("..upper.."_INCDIR)" + libdirs[#libdirs+1] = "$("..upper.."_LIBDIR)" + end + end + + for _, file in ipairs(fs.find()) do + local luamod = file:match("(.*)%.lua$") + if luamod and not luamod_blacklist[luamod] then + rockspec.build.modules[path.path_to_module(file)] = prefix..file + else + local cmod = file:match("(.*)%.c$") + if cmod then + local modname = get_cmod_name(file) or path.path_to_module(file:gsub("%.c$", ".lua")) + rockspec.build.modules[modname] = { + sources = prefix..file, + libraries = libs, + incdirs = incdirs, + libdirs = libdirs, + } + end + end + end + + for _, directory in ipairs({ "doc", "docs", "samples", "tests" }) do + if fs.is_dir(directory) then + if not rockspec.build.copy_directories then + rockspec.build.copy_directories = {} + end + table.insert(rockspec.build.copy_directories, directory) + end + end + + if prefix ~= "" then + fs.pop_dir() + end +end + +local function rockspec_cleanup(rockspec) + rockspec.source.file = nil + rockspec.source.protocol = nil + rockspec.variables = nil + rockspec.name = nil + rockspec.format_is_at_least = nil +end + +function write_rockspec.command(flags, name, version, url_or_dir) + if not name then + url_or_dir = "." + elseif not version then + url_or_dir = name + name = nil + elseif not url_or_dir then + url_or_dir = version + version = nil + end + + if flags["tag"] then + if not version then + version = flags["tag"]:gsub("^v", "") + end + end + + local protocol, pathname = dir.split_url(url_or_dir) + if protocol == "file" then + if pathname == "." then + name = name or dir.base_name(fs.current_dir()) + end + elseif fetch.is_basic_protocol(protocol) then + local filename = dir.base_name(url_or_dir) + local newname, newversion = filename:match("(.*)-([^-]+)") + if newname then + name = name or newname + version = version or newversion:gsub("%.[a-z]+$", ""):gsub("%.tar$", "") + end + else + name = name or dir.base_name(url_or_dir):gsub("%.[^.]+$", "") + end + + if not name then + return nil, "Could not infer rock name. "..util.see_help("write_rockspec") + end + version = version or "scm" + + local filename = flags["output"] or dir.path(fs.current_dir(), name:lower().."-"..version.."-1.rockspec") + + local rockspec = { + rockspec_format = flags["rockspec-format"], + package = name, + name = name:lower(), + version = version.."-1", + source = { + url = "*** please add URL for source tarball, zip or repository here ***", + tag = flags["tag"], + }, + description = { + summary = flags["summary"] or "*** please specify description summary ***", + detailed = flags["detailed"] or "*** please enter a detailed description ***", + homepage = flags["homepage"] or "*** please enter a project homepage ***", + license = flags["license"] or "*** please specify a license ***", + }, + dependencies = {}, + build = {}, + } + path.configure_paths(rockspec) + rockspec.source.protocol = protocol + rockspec.format_is_at_least = deps.format_is_at_least + + configure_lua_version(rockspec, flags["lua-version"]) + + local local_dir = url_or_dir + + if url_or_dir:match("://") then + rockspec.source.url = url_or_dir + rockspec.source.file = dir.base_name(url_or_dir) + rockspec.source.dir = "dummy" + if not fetch.is_basic_protocol(rockspec.source.protocol) then + if version ~= "scm" then + rockspec.source.tag = flags["tag"] or "v" .. version + end + end + rockspec.source.dir = nil + local ok, base_dir, temp_dir = get_url(rockspec) + if ok then + if base_dir ~= dir.base_name(url_or_dir) then + rockspec.source.dir = base_dir + end + end + if base_dir then + local_dir = dir.path(temp_dir, base_dir) + else + local_dir = nil + end + else + rockspec.source.url = detect_scm_url(local_dir) or rockspec.source.url + end + + if not local_dir then + local_dir = "." + end + + if not flags["homepage"] then + local url_protocol, url_path = dir.split_url(rockspec.source.url) + + if simple_scm_protocols[url_protocol] then + for _, domain in ipairs({"github.com", "bitbucket.org", "gitlab.com"}) do + if util.starts_with(url_path, domain) then + rockspec.description.homepage = "https://"..url_path:gsub("%.git$", "") + break + end + end + end + end + + local libs = nil + if flags["lib"] then + libs = {} + rockspec.external_dependencies = {} + for lib in flags["lib"]:gmatch("([^,]+)") do + table.insert(libs, lib) + rockspec.external_dependencies[lib:upper()] = { + library = lib + } + end + end + + local ok, err = fs.change_dir(local_dir) + if not ok then return nil, "Failed reaching files from project - error entering directory "..local_dir end + + if (not flags["summary"]) or (not flags["detailed"]) then + local summary, detailed = detect_description() + rockspec.description.summary = flags["summary"] or summary + rockspec.description.detailed = flags["detailed"] or detailed + end + + local is_mit = show_license(rockspec) + + if is_mit and not flags["license"] then + rockspec.description.license = "MIT" + end + + fill_as_builtin(rockspec, libs) + + rockspec_cleanup(rockspec) + + persist.save_from_table(filename, rockspec, type_check.rockspec_order) + + util.printout() + util.printout("Wrote template at "..filename.." -- you should now edit and finish it.") + util.printout() + + return true +end + +return write_rockspec diff --git a/src/luarocks/build.lua b/src/luarocks/build.lua deleted file mode 100644 index f3b054d2..00000000 --- a/src/luarocks/build.lua +++ /dev/null @@ -1,443 +0,0 @@ - ---- Module implementing the LuaRocks "build" command. --- Builds a rock, compiling its C parts if any. -local build = {} - -local pack = require("luarocks.pack") -local path = require("luarocks.path") -local util = require("luarocks.util") -local repos = require("luarocks.repos") -local fetch = require("luarocks.fetch") -local fs = require("luarocks.fs") -local dir = require("luarocks.dir") -local deps = require("luarocks.deps") -local writer = require("luarocks.manif.writer") -local remove = require("luarocks.remove") -local cfg = require("luarocks.core.cfg") - -build.help_summary = "Build/compile a rock." -build.help_arguments = "[--pack-binary-rock] [--keep] {|| []}" -build.help = [[ -Build and install a rock, compiling its C parts if any. -Argument may be a rockspec file, a source rock file -or the name of a rock to be fetched from a repository. - ---pack-binary-rock Do not install rock. Instead, produce a .rock file - with the contents of compilation in the current - directory. - ---keep Do not remove previously installed versions of the - rock after building a new one. This behavior can - be made permanent by setting keep_other_versions=true - in the configuration file. - ---branch= Override the `source.branch` field in the loaded - rockspec. Allows to specify a different branch to - fetch. Particularly for SCM rocks. - ---only-deps Installs only the dependencies of the rock. - -]]..util.deps_mode_help() - ---- Install files to a given location. --- Takes a table where the array part is a list of filenames to be copied. --- In the hash part, other keys, if is_module_path is set, are identifiers --- in Lua module format, to indicate which subdirectory the file should be --- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo") --- will copy src/bar.lua to boo/foo. --- @param files table or nil: A table containing a list of files to copy in --- the format described above. If nil is passed, this function is a no-op. --- Directories should be delimited by forward slashes as in internet URLs. --- @param location string: The base directory files should be copied to. --- @param is_module_path boolean: True if string keys in files should be --- interpreted as dotted module paths. --- @param perms string: Permissions of the newly created files installed. --- Directories are always created with the default permissions. --- @return boolean or (nil, string): True if succeeded or --- nil and an error message. -local function install_files(files, location, is_module_path, perms) - assert(type(files) == "table" or not files) - assert(type(location) == "string") - if files then - for k, file in pairs(files) do - local dest = location - local filename = dir.base_name(file) - if type(k) == "string" then - local modname = k - if is_module_path then - dest = dir.path(location, path.module_to_path(modname)) - local ok, err = fs.make_dir(dest) - if not ok then return nil, err end - if filename:match("%.lua$") then - local basename = modname:match("([^.]+)$") - filename = basename..".lua" - end - else - dest = dir.path(location, dir.dir_name(modname)) - local ok, err = fs.make_dir(dest) - if not ok then return nil, err end - filename = dir.base_name(modname) - end - else - local ok, err = fs.make_dir(dest) - if not ok then return nil, err end - end - local ok = fs.copy(dir.path(file), dir.path(dest, filename), perms) - if not ok then - return nil, "Failed copying "..file - end - end - end - return true -end - ---- Write to the current directory the contents of a table, --- where each key is a file name and its value is the file content. --- @param files table: The table of files to be written. -local function extract_from_rockspec(files) - for name, content in pairs(files) do - local fd = io.open(dir.path(fs.current_dir(), name), "w+") - fd:write(content) - fd:close() - end -end - ---- Applies patches inlined in the build.patches section --- and extracts files inlined in the build.extra_files section --- of a rockspec. --- @param rockspec table: A rockspec table. --- @return boolean or (nil, string): True if succeeded or --- nil and an error message. -function build.apply_patches(rockspec) - assert(type(rockspec) == "table") - - local build_spec = rockspec.build - if build_spec.extra_files then - extract_from_rockspec(build_spec.extra_files) - end - if build_spec.patches then - extract_from_rockspec(build_spec.patches) - for patch, patchdata in util.sortedpairs(build_spec.patches) do - util.printout("Applying patch "..patch.."...") - local ok, err = fs.apply_patch(tostring(patch), patchdata) - if not ok then - return nil, "Failed applying patch "..patch - end - end - end - return true -end - -local function install_default_docs(name, version) - local patterns = { "readme", "license", "copying", ".*%.md" } - local dest = dir.path(path.install_dir(name, version), "doc") - local has_dir = false - for file in fs.dir() do - for _, pattern in ipairs(patterns) do - if file:lower():match("^"..pattern) then - if not has_dir then - fs.make_dir(dest) - has_dir = true - end - fs.copy(file, dest, cfg.perm_read) - break - end - end - end -end - -local function check_macosx_deployment_target(rockspec) - local target = rockspec.build.macosx_deployment_target - local function minor(version) - return tonumber(version and version:match("^[^.]+%.([^.]+)")) - end - local function patch_variable(var, target) - if rockspec.variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then - rockspec.variables[var] = (rockspec.variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET="..target) - else - rockspec.variables[var] = "env MACOSX_DEPLOYMENT_TARGET="..target.." "..rockspec.variables[var] - end - end - if cfg.platforms.macosx and rockspec:format_is_at_least("3.0") and target then - local version = util.popen_read("sw_vers -productVersion") - local versionminor = minor(version) - local targetminor = minor(target) - if targetminor > versionminor then - return nil, ("This rock requires Mac OSX 10.%d, and you are running 10.%d."):format(targetminor, versionminor) - end - patch_variable("CC", target) - patch_variable("LD", target) - end - return true -end - ---- Build and install a rock given a rockspec. --- @param rockspec_file string: local or remote filename of a rockspec. --- @param need_to_fetch boolean: true if sources need to be fetched, --- false if the rockspec was obtained from inside a source rock. --- @param minimal_mode boolean: true if there's no need to fetch, --- unpack or change dir (this is used by "luarocks make"). Implies --- need_to_fetch = false. --- @param deps_mode string: Dependency mode: "one" for the current default tree, --- "all" for all trees, "order" for all trees with priority >= the current default, --- "none" for no trees. --- @param build_only_deps boolean: true to build the listed dependencies only. --- @return (string, string) or (nil, string, [string]): Name and version of --- installed rock if succeeded or nil and an error message followed by an error code. -function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode, build_only_deps) - assert(type(rockspec_file) == "string") - assert(type(need_to_fetch) == "boolean") - - local rockspec, err, errcode = fetch.load_rockspec(rockspec_file) - if err then - return nil, err, errcode - elseif not rockspec.build then - return nil, "Rockspec error: build table not specified" - elseif not rockspec.build.type then - return nil, "Rockspec error: build type not specified" - end - - local ok - if not build_only_deps then - ok, err, errcode = deps.check_external_deps(rockspec, "build") - if err then - return nil, err, errcode - end - end - - if deps_mode == "none" then - util.printerr("Warning: skipping dependency checks.") - else - local ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) - if err then - return nil, err, errcode - end - end - - local name, version = rockspec.name, rockspec.version - if build_only_deps then - util.printout("Stopping after installing dependencies for " ..name.." "..version) - util.printout() - return name, version - end - - if repos.is_installed(name, version) then - repos.delete_version(name, version, deps_mode) - end - - if not minimal_mode then - local source_dir - if need_to_fetch then - ok, source_dir, errcode = fetch.fetch_sources(rockspec, true) - if not ok then - return nil, source_dir, errcode - end - local ok, err = fs.change_dir(source_dir) - if not ok then return nil, err end - elseif rockspec.source.file then - local ok, err = fs.unpack_archive(rockspec.source.file) - if not ok then - return nil, err - end - end - fs.change_dir(rockspec.source.dir) - end - - local dirs = { - lua = { name = path.lua_dir(name, version), is_module_path = true, perms = cfg.perm_read }, - lib = { name = path.lib_dir(name, version), is_module_path = true, perms = cfg.perm_exec }, - conf = { name = path.conf_dir(name, version), is_module_path = false, perms = cfg.perm_read }, - bin = { name = path.bin_dir(name, version), is_module_path = false, perms = cfg.perm_exec }, - } - - for _, d in pairs(dirs) do - local ok, err = fs.make_dir(d.name) - if not ok then return nil, err end - end - local rollback = util.schedule_function(function() - fs.delete(path.install_dir(name, version)) - fs.remove_dir_if_empty(path.versions_dir(name)) - end) - - local build_spec = rockspec.build - - if not minimal_mode then - ok, err = build.apply_patches(rockspec) - if err then - return nil, err - end - end - - ok, err = check_macosx_deployment_target(rockspec) - if not ok then - return nil, err - end - - if build_spec.type ~= "none" then - - -- Temporary compatibility - if build_spec.type == "module" then - util.printout("Do not use 'module' as a build type. Use 'builtin' instead.") - build_spec.type = "builtin" - end - - if cfg.accepted_build_types and util.array_contains(cfg.accepted_build_types, build_spec.type) then - return nil, "This rockspec uses the '"..build_spec.type.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration." - end - - local build_type - ok, build_type = pcall(require, "luarocks.build." .. build_spec.type) - if not ok or not type(build_type) == "table" then - return nil, "Failed initializing build back-end for build type '"..build_spec.type.."': "..build_type - end - - ok, err = build_type.run(rockspec) - if not ok then - return nil, "Build error: " .. err - end - end - - if build_spec.install then - for id, install_dir in pairs(dirs) do - ok, err = install_files(build_spec.install[id], install_dir.name, install_dir.is_module_path, install_dir.perms) - if not ok then - return nil, err - end - end - end - - local copy_directories = build_spec.copy_directories - local copying_default = false - if not copy_directories then - copy_directories = {"doc"} - copying_default = true - end - - local any_docs = false - for _, copy_dir in pairs(copy_directories) do - if fs.is_dir(copy_dir) then - local dest = dir.path(path.install_dir(name, version), copy_dir) - fs.make_dir(dest) - fs.copy_contents(copy_dir, dest) - any_docs = true - else - if not copying_default then - return nil, "Directory '"..copy_dir.."' not found" - end - end - end - - if not any_docs then - install_default_docs(name, version) - end - - for _, d in pairs(dirs) do - fs.remove_dir_if_empty(d.name) - end - - fs.pop_dir() - - fs.copy(rockspec.local_filename, path.rockspec_file(name, version), cfg.perm_read) - if need_to_fetch then - fs.pop_dir() - end - - ok, err = writer.make_rock_manifest(name, version) - if err then return nil, err end - - ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) - if err then return nil, err end - - util.remove_scheduled_function(rollback) - rollback = util.schedule_function(function() - repos.delete_version(name, version, deps_mode) - end) - - ok, err = repos.run_hook(rockspec, "post_install") - if err then return nil, err end - - util.announce_install(rockspec) - util.remove_scheduled_function(rollback) - return name, version -end - ---- Build and install a rock. --- @param rock_file string: local or remote filename of a rock. --- @param need_to_fetch boolean: true if sources need to be fetched, --- false if the rockspec was obtained from inside a source rock. --- @param deps_mode: string: Which trees to check dependencies for: --- "one" for the current default tree, "all" for all trees, --- "order" for all trees with priority >= the current default, "none" for no trees. --- @param build_only_deps boolean: true to build the listed dependencies only. --- @return boolean or (nil, string, [string]): True if build was successful, --- or false and an error message and an optional error code. -function build.build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps) - assert(type(rock_file) == "string") - assert(type(need_to_fetch) == "boolean") - - local ok, err, errcode - local unpack_dir - unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file) - if not unpack_dir then - return nil, err, errcode - end - local rockspec_file = path.rockspec_name_from_rock(rock_file) - ok, err = fs.change_dir(unpack_dir) - if not ok then return nil, err end - ok, err, errcode = build.build_rockspec(rockspec_file, need_to_fetch, false, deps_mode, build_only_deps) - fs.pop_dir() - return ok, err, errcode -end - -local function do_build(name, version, deps_mode, build_only_deps) - if name:match("%.rockspec$") then - return build.build_rockspec(name, true, false, deps_mode, build_only_deps) - elseif name:match("%.src%.rock$") then - return build.build_rock(name, false, deps_mode, build_only_deps) - elseif name:match("%.all%.rock$") then - local install = require("luarocks.install") - local install_fun = build_only_deps and install.install_binary_rock_deps or install.install_binary_rock - return install_fun(name, deps_mode) - elseif name:match("%.rock$") then - return build.build_rock(name, true, deps_mode, build_only_deps) - elseif not name:match(dir.separator) then - local search = require("luarocks.search") - return search.act_on_src_or_rockspec(do_build, name:lower(), version, nil, deps_mode, build_only_deps) - end - return nil, "Don't know what to do with "..name -end - ---- Driver function for "build" command. --- @param name string: A local or remote rockspec or rock file. --- If a package name is given, forwards the request to "search" and, --- if returned a result, installs the matching rock. --- @param version string: When passing a package name, a version number may --- also be given. --- @return boolean or (nil, string, exitcode): True if build was successful; nil and an --- error message otherwise. exitcode is optionally returned. -function build.command(flags, name, version) - if type(name) ~= "string" then - return nil, "Argument missing. "..util.see_help("build") - end - assert(type(version) == "string" or not version) - - if flags["pack-binary-rock"] then - return pack.pack_binary_rock(name, version, do_build, name, version, deps.get_deps_mode(flags)) - else - local ok, err = fs.check_command_permissions(flags) - if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end - ok, err = do_build(name, version, deps.get_deps_mode(flags), flags["only-deps"]) - if not ok then return nil, err end - name, version = ok, err - if flags["only-deps"] then - return name, version - end - if (not flags["keep"]) and not cfg.keep_other_versions then - local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) - if not ok then util.printerr(err) end - end - return name, version - end -end - -return build diff --git a/src/luarocks/cmd/build.lua b/src/luarocks/cmd/build.lua new file mode 100644 index 00000000..f3b054d2 --- /dev/null +++ b/src/luarocks/cmd/build.lua @@ -0,0 +1,443 @@ + +--- Module implementing the LuaRocks "build" command. +-- Builds a rock, compiling its C parts if any. +local build = {} + +local pack = require("luarocks.pack") +local path = require("luarocks.path") +local util = require("luarocks.util") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local deps = require("luarocks.deps") +local writer = require("luarocks.manif.writer") +local remove = require("luarocks.remove") +local cfg = require("luarocks.core.cfg") + +build.help_summary = "Build/compile a rock." +build.help_arguments = "[--pack-binary-rock] [--keep] {|| []}" +build.help = [[ +Build and install a rock, compiling its C parts if any. +Argument may be a rockspec file, a source rock file +or the name of a rock to be fetched from a repository. + +--pack-binary-rock Do not install rock. Instead, produce a .rock file + with the contents of compilation in the current + directory. + +--keep Do not remove previously installed versions of the + rock after building a new one. This behavior can + be made permanent by setting keep_other_versions=true + in the configuration file. + +--branch= Override the `source.branch` field in the loaded + rockspec. Allows to specify a different branch to + fetch. Particularly for SCM rocks. + +--only-deps Installs only the dependencies of the rock. + +]]..util.deps_mode_help() + +--- Install files to a given location. +-- Takes a table where the array part is a list of filenames to be copied. +-- In the hash part, other keys, if is_module_path is set, are identifiers +-- in Lua module format, to indicate which subdirectory the file should be +-- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo") +-- will copy src/bar.lua to boo/foo. +-- @param files table or nil: A table containing a list of files to copy in +-- the format described above. If nil is passed, this function is a no-op. +-- Directories should be delimited by forward slashes as in internet URLs. +-- @param location string: The base directory files should be copied to. +-- @param is_module_path boolean: True if string keys in files should be +-- interpreted as dotted module paths. +-- @param perms string: Permissions of the newly created files installed. +-- Directories are always created with the default permissions. +-- @return boolean or (nil, string): True if succeeded or +-- nil and an error message. +local function install_files(files, location, is_module_path, perms) + assert(type(files) == "table" or not files) + assert(type(location) == "string") + if files then + for k, file in pairs(files) do + local dest = location + local filename = dir.base_name(file) + if type(k) == "string" then + local modname = k + if is_module_path then + dest = dir.path(location, path.module_to_path(modname)) + local ok, err = fs.make_dir(dest) + if not ok then return nil, err end + if filename:match("%.lua$") then + local basename = modname:match("([^.]+)$") + filename = basename..".lua" + end + else + dest = dir.path(location, dir.dir_name(modname)) + local ok, err = fs.make_dir(dest) + if not ok then return nil, err end + filename = dir.base_name(modname) + end + else + local ok, err = fs.make_dir(dest) + if not ok then return nil, err end + end + local ok = fs.copy(dir.path(file), dir.path(dest, filename), perms) + if not ok then + return nil, "Failed copying "..file + end + end + end + return true +end + +--- Write to the current directory the contents of a table, +-- where each key is a file name and its value is the file content. +-- @param files table: The table of files to be written. +local function extract_from_rockspec(files) + for name, content in pairs(files) do + local fd = io.open(dir.path(fs.current_dir(), name), "w+") + fd:write(content) + fd:close() + end +end + +--- Applies patches inlined in the build.patches section +-- and extracts files inlined in the build.extra_files section +-- of a rockspec. +-- @param rockspec table: A rockspec table. +-- @return boolean or (nil, string): True if succeeded or +-- nil and an error message. +function build.apply_patches(rockspec) + assert(type(rockspec) == "table") + + local build_spec = rockspec.build + if build_spec.extra_files then + extract_from_rockspec(build_spec.extra_files) + end + if build_spec.patches then + extract_from_rockspec(build_spec.patches) + for patch, patchdata in util.sortedpairs(build_spec.patches) do + util.printout("Applying patch "..patch.."...") + local ok, err = fs.apply_patch(tostring(patch), patchdata) + if not ok then + return nil, "Failed applying patch "..patch + end + end + end + return true +end + +local function install_default_docs(name, version) + local patterns = { "readme", "license", "copying", ".*%.md" } + local dest = dir.path(path.install_dir(name, version), "doc") + local has_dir = false + for file in fs.dir() do + for _, pattern in ipairs(patterns) do + if file:lower():match("^"..pattern) then + if not has_dir then + fs.make_dir(dest) + has_dir = true + end + fs.copy(file, dest, cfg.perm_read) + break + end + end + end +end + +local function check_macosx_deployment_target(rockspec) + local target = rockspec.build.macosx_deployment_target + local function minor(version) + return tonumber(version and version:match("^[^.]+%.([^.]+)")) + end + local function patch_variable(var, target) + if rockspec.variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then + rockspec.variables[var] = (rockspec.variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET="..target) + else + rockspec.variables[var] = "env MACOSX_DEPLOYMENT_TARGET="..target.." "..rockspec.variables[var] + end + end + if cfg.platforms.macosx and rockspec:format_is_at_least("3.0") and target then + local version = util.popen_read("sw_vers -productVersion") + local versionminor = minor(version) + local targetminor = minor(target) + if targetminor > versionminor then + return nil, ("This rock requires Mac OSX 10.%d, and you are running 10.%d."):format(targetminor, versionminor) + end + patch_variable("CC", target) + patch_variable("LD", target) + end + return true +end + +--- Build and install a rock given a rockspec. +-- @param rockspec_file string: local or remote filename of a rockspec. +-- @param need_to_fetch boolean: true if sources need to be fetched, +-- false if the rockspec was obtained from inside a source rock. +-- @param minimal_mode boolean: true if there's no need to fetch, +-- unpack or change dir (this is used by "luarocks make"). Implies +-- need_to_fetch = false. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for no trees. +-- @param build_only_deps boolean: true to build the listed dependencies only. +-- @return (string, string) or (nil, string, [string]): Name and version of +-- installed rock if succeeded or nil and an error message followed by an error code. +function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode, build_only_deps) + assert(type(rockspec_file) == "string") + assert(type(need_to_fetch) == "boolean") + + local rockspec, err, errcode = fetch.load_rockspec(rockspec_file) + if err then + return nil, err, errcode + elseif not rockspec.build then + return nil, "Rockspec error: build table not specified" + elseif not rockspec.build.type then + return nil, "Rockspec error: build type not specified" + end + + local ok + if not build_only_deps then + ok, err, errcode = deps.check_external_deps(rockspec, "build") + if err then + return nil, err, errcode + end + end + + if deps_mode == "none" then + util.printerr("Warning: skipping dependency checks.") + else + local ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) + if err then + return nil, err, errcode + end + end + + local name, version = rockspec.name, rockspec.version + if build_only_deps then + util.printout("Stopping after installing dependencies for " ..name.." "..version) + util.printout() + return name, version + end + + if repos.is_installed(name, version) then + repos.delete_version(name, version, deps_mode) + end + + if not minimal_mode then + local source_dir + if need_to_fetch then + ok, source_dir, errcode = fetch.fetch_sources(rockspec, true) + if not ok then + return nil, source_dir, errcode + end + local ok, err = fs.change_dir(source_dir) + if not ok then return nil, err end + elseif rockspec.source.file then + local ok, err = fs.unpack_archive(rockspec.source.file) + if not ok then + return nil, err + end + end + fs.change_dir(rockspec.source.dir) + end + + local dirs = { + lua = { name = path.lua_dir(name, version), is_module_path = true, perms = cfg.perm_read }, + lib = { name = path.lib_dir(name, version), is_module_path = true, perms = cfg.perm_exec }, + conf = { name = path.conf_dir(name, version), is_module_path = false, perms = cfg.perm_read }, + bin = { name = path.bin_dir(name, version), is_module_path = false, perms = cfg.perm_exec }, + } + + for _, d in pairs(dirs) do + local ok, err = fs.make_dir(d.name) + if not ok then return nil, err end + end + local rollback = util.schedule_function(function() + fs.delete(path.install_dir(name, version)) + fs.remove_dir_if_empty(path.versions_dir(name)) + end) + + local build_spec = rockspec.build + + if not minimal_mode then + ok, err = build.apply_patches(rockspec) + if err then + return nil, err + end + end + + ok, err = check_macosx_deployment_target(rockspec) + if not ok then + return nil, err + end + + if build_spec.type ~= "none" then + + -- Temporary compatibility + if build_spec.type == "module" then + util.printout("Do not use 'module' as a build type. Use 'builtin' instead.") + build_spec.type = "builtin" + end + + if cfg.accepted_build_types and util.array_contains(cfg.accepted_build_types, build_spec.type) then + return nil, "This rockspec uses the '"..build_spec.type.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration." + end + + local build_type + ok, build_type = pcall(require, "luarocks.build." .. build_spec.type) + if not ok or not type(build_type) == "table" then + return nil, "Failed initializing build back-end for build type '"..build_spec.type.."': "..build_type + end + + ok, err = build_type.run(rockspec) + if not ok then + return nil, "Build error: " .. err + end + end + + if build_spec.install then + for id, install_dir in pairs(dirs) do + ok, err = install_files(build_spec.install[id], install_dir.name, install_dir.is_module_path, install_dir.perms) + if not ok then + return nil, err + end + end + end + + local copy_directories = build_spec.copy_directories + local copying_default = false + if not copy_directories then + copy_directories = {"doc"} + copying_default = true + end + + local any_docs = false + for _, copy_dir in pairs(copy_directories) do + if fs.is_dir(copy_dir) then + local dest = dir.path(path.install_dir(name, version), copy_dir) + fs.make_dir(dest) + fs.copy_contents(copy_dir, dest) + any_docs = true + else + if not copying_default then + return nil, "Directory '"..copy_dir.."' not found" + end + end + end + + if not any_docs then + install_default_docs(name, version) + end + + for _, d in pairs(dirs) do + fs.remove_dir_if_empty(d.name) + end + + fs.pop_dir() + + fs.copy(rockspec.local_filename, path.rockspec_file(name, version), cfg.perm_read) + if need_to_fetch then + fs.pop_dir() + end + + ok, err = writer.make_rock_manifest(name, version) + if err then return nil, err end + + ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) + if err then return nil, err end + + util.remove_scheduled_function(rollback) + rollback = util.schedule_function(function() + repos.delete_version(name, version, deps_mode) + end) + + ok, err = repos.run_hook(rockspec, "post_install") + if err then return nil, err end + + util.announce_install(rockspec) + util.remove_scheduled_function(rollback) + return name, version +end + +--- Build and install a rock. +-- @param rock_file string: local or remote filename of a rock. +-- @param need_to_fetch boolean: true if sources need to be fetched, +-- false if the rockspec was obtained from inside a source rock. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @param build_only_deps boolean: true to build the listed dependencies only. +-- @return boolean or (nil, string, [string]): True if build was successful, +-- or false and an error message and an optional error code. +function build.build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps) + assert(type(rock_file) == "string") + assert(type(need_to_fetch) == "boolean") + + local ok, err, errcode + local unpack_dir + unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file) + if not unpack_dir then + return nil, err, errcode + end + local rockspec_file = path.rockspec_name_from_rock(rock_file) + ok, err = fs.change_dir(unpack_dir) + if not ok then return nil, err end + ok, err, errcode = build.build_rockspec(rockspec_file, need_to_fetch, false, deps_mode, build_only_deps) + fs.pop_dir() + return ok, err, errcode +end + +local function do_build(name, version, deps_mode, build_only_deps) + if name:match("%.rockspec$") then + return build.build_rockspec(name, true, false, deps_mode, build_only_deps) + elseif name:match("%.src%.rock$") then + return build.build_rock(name, false, deps_mode, build_only_deps) + elseif name:match("%.all%.rock$") then + local install = require("luarocks.install") + local install_fun = build_only_deps and install.install_binary_rock_deps or install.install_binary_rock + return install_fun(name, deps_mode) + elseif name:match("%.rock$") then + return build.build_rock(name, true, deps_mode, build_only_deps) + elseif not name:match(dir.separator) then + local search = require("luarocks.search") + return search.act_on_src_or_rockspec(do_build, name:lower(), version, nil, deps_mode, build_only_deps) + end + return nil, "Don't know what to do with "..name +end + +--- Driver function for "build" command. +-- @param name string: A local or remote rockspec or rock file. +-- If a package name is given, forwards the request to "search" and, +-- if returned a result, installs the matching rock. +-- @param version string: When passing a package name, a version number may +-- also be given. +-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an +-- error message otherwise. exitcode is optionally returned. +function build.command(flags, name, version) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("build") + end + assert(type(version) == "string" or not version) + + if flags["pack-binary-rock"] then + return pack.pack_binary_rock(name, version, do_build, name, version, deps.get_deps_mode(flags)) + else + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + ok, err = do_build(name, version, deps.get_deps_mode(flags), flags["only-deps"]) + if not ok then return nil, err end + name, version = ok, err + if flags["only-deps"] then + return name, version + end + if (not flags["keep"]) and not cfg.keep_other_versions then + local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) + if not ok then util.printerr(err) end + end + return name, version + end +end + +return build diff --git a/src/luarocks/cmd/config_cmd.lua b/src/luarocks/cmd/config_cmd.lua new file mode 100644 index 00000000..b68f7898 --- /dev/null +++ b/src/luarocks/cmd/config_cmd.lua @@ -0,0 +1,71 @@ +--- Module implementing the LuaRocks "config" command. +-- Queries information about the LuaRocks configuration. +local config_cmd = {} + +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") + +config_cmd.help_summary = "Query information about the LuaRocks configuration." +config_cmd.help_arguments = "" +config_cmd.help = [[ +--lua-incdir Path to Lua header files. + +--lua-libdir Path to Lua library files. + +--lua-ver Lua version (in major.minor format). e.g. 5.1 + +--system-config Location of the system config file. + +--user-config Location of the user config file. + +--rock-trees Rocks trees in use. First the user tree, then the system tree. +]] + +local function config_file(conf) + print(dir.normalize(conf.file)) + if conf.ok then + return true + else + return nil, "file not found" + end +end + +--- Driver function for "config" command. +-- @return boolean: True if succeeded, nil on errors. +function config_cmd.command(flags) + if flags["lua-incdir"] then + print(cfg.variables.LUA_INCDIR) + return true + end + if flags["lua-libdir"] then + print(cfg.variables.LUA_LIBDIR) + return true + end + if flags["lua-ver"] then + print(cfg.lua_version) + return true + end + local conf = cfg.which_config() + if flags["system-config"] then + return config_file(conf.system) + end + if flags["user-config"] then + return config_file(conf.user) + end + if flags["rock-trees"] then + for _, tree in ipairs(cfg.rocks_trees) do + if type(tree) == "string" then + util.printout(dir.normalize(tree)) + else + local name = tree.name and "\t"..tree.name or "" + util.printout(dir.normalize(tree.root)..name) + end + end + return true + end + + return nil, "Please provide a flag for querying configuration values. "..util.see_help("config") +end + +return config_cmd diff --git a/src/luarocks/cmd/doc.lua b/src/luarocks/cmd/doc.lua new file mode 100644 index 00000000..5d521276 --- /dev/null +++ b/src/luarocks/cmd/doc.lua @@ -0,0 +1,155 @@ + +--- Module implementing the LuaRocks "doc" command. +-- Shows documentation for an installed rock. +local doc = {} + +local util = require("luarocks.util") +local search = require("luarocks.search") +local path = require("luarocks.path") +local dir = require("luarocks.dir") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local download = require("luarocks.download") + +doc.help_summary = "Show documentation for an installed rock." + +doc.help = [[ + is an existing package name. +Without any flags, tries to load the documentation +using a series of heuristics. +With these flags, return only the desired information: + +--home Open the home page of project. +--list List documentation files only. + +For more information about a rock, see the 'show' command. +]] + +local function show_homepage(homepage, name, version) + if not homepage then + return nil, "No 'homepage' field in rockspec for "..name.." "..version + end + util.printout("Opening "..homepage.." ...") + fs.browser(homepage) + return true +end + +local function try_to_open_homepage(name, version) + local temp_dir, err = fs.make_temp_dir("doc-"..name.."-"..(version or "")) + if not temp_dir then + return nil, "Failed creating temporary directory: "..err + end + util.schedule_function(fs.delete, temp_dir) + local ok, err = fs.change_dir(temp_dir) + if not ok then return nil, err end + local filename, err = download.download("rockspec", name, version) + if not filename then return nil, err end + local rockspec, err = fetch.load_local_rockspec(filename) + if not rockspec then return nil, err end + fs.pop_dir() + local descript = rockspec.description or {} + if not descript.homepage then return nil, "No homepage defined for "..name end + return show_homepage(descript.homepage, name, version) +end + +--- Driver function for "doc" command. +-- @param name or nil: an existing package name. +-- @param version string or nil: a version may also be passed. +-- @return boolean: True if succeeded, nil on errors. +function doc.command(flags, name, version) + if not name then + return nil, "Argument missing. "..util.see_help("doc") + end + + name = name:lower() + + local iname, iversion, repo = search.pick_installed_rock(name, version, flags["tree"]) + if not iname then + util.printout(name..(version and " "..version or "").." is not installed. Looking for it in the rocks servers...") + return try_to_open_homepage(name, version) + end + name, version = iname, iversion + + local rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version, repo)) + if not rockspec then return nil,err end + local descript = rockspec.description or {} + + if flags["home"] then + return show_homepage(descript.homepage, name, version) + end + + local directory = path.install_dir(name,version,repo) + + local docdir + local directories = { "doc", "docs" } + for _, d in ipairs(directories) do + local dirname = dir.path(directory, d) + if fs.is_dir(dirname) then + docdir = dirname + break + end + end + if not docdir then + if descript.homepage and not flags["list"] then + util.printout("Local documentation directory not found -- opening "..descript.homepage.." ...") + fs.browser(descript.homepage) + return true + end + return nil, "Documentation directory not found for "..name.." "..version + end + + docdir = dir.normalize(docdir):gsub("/+", "/") + local files = fs.find(docdir) + local htmlpatt = "%.html?$" + local extensions = { htmlpatt, "%.md$", "%.txt$", "%.textile$", "" } + local basenames = { "index", "readme", "manual" } + + local porcelain = flags["porcelain"] + if #files > 0 then + util.title("Documentation files for "..name.." "..version, porcelain) + if porcelain then + for _, file in ipairs(files) do + util.printout(docdir.."/"..file) + end + else + util.printout(docdir.."/") + for _, file in ipairs(files) do + util.printout("\t"..file) + end + end + end + + if flags["list"] then + return true + end + + for _, extension in ipairs(extensions) do + for _, basename in ipairs(basenames) do + local filename = basename..extension + local found + for _, file in ipairs(files) do + if file:lower():match(filename) and ((not found) or #file < #found) then + found = file + end + end + if found then + local pathname = dir.path(docdir, found) + util.printout() + util.printout("Opening "..pathname.." ...") + util.printout() + local ok = fs.browser(pathname) + if not ok and not pathname:match(htmlpatt) then + local fd = io.open(pathname, "r") + util.printout(fd:read("*a")) + fd:close() + end + return true + end + end + end + + return true +end + + +return doc diff --git a/src/luarocks/cmd/download.lua b/src/luarocks/cmd/download.lua new file mode 100644 index 00000000..557d1b65 --- /dev/null +++ b/src/luarocks/cmd/download.lua @@ -0,0 +1,107 @@ + +--- Module implementing the luarocks "download" command. +-- Download a rock from the repository. +local download = {} + +local util = require("luarocks.util") +local path = require("luarocks.path") +local fetch = require("luarocks.fetch") +local search = require("luarocks.search") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local cfg = require("luarocks.core.cfg") + +download.help_summary = "Download a specific rock file from a rocks server." +download.help_arguments = "[--all] [--arch= | --source | --rockspec] [ []]" + +download.help = [[ +--all Download all files if there are multiple matches. +--source Download .src.rock if available. +--rockspec Download .rockspec if available. +--arch= Download rock for a specific architecture. +]] + +local function get_file(filename) + local protocol, pathname = dir.split_url(filename) + if protocol == "file" then + local ok, err = fs.copy(pathname, fs.current_dir(), cfg.perm_read) + if ok then + return pathname + else + return nil, err + end + else + return fetch.fetch_url(filename) + end +end + +function download.download(arch, name, version, all) + local query = search.make_query(name, version) + if arch then query.arch = arch end + local search_err + + if all then + if name == "" then query.exact_name = false end + local results = search.search_repos(query) + local has_result = false + local all_ok = true + local any_err = "" + for name, result in pairs(results) do + for version, items in pairs(result) do + for _, item in ipairs(items) do + -- Ignore provided rocks. + if item.arch ~= "installed" then + has_result = true + local filename = path.make_url(item.repo, name, version, item.arch) + local ok, err = get_file(filename) + if not ok then + all_ok = false + any_err = any_err .. "\n" .. err + end + end + end + end + end + + if has_result then + return all_ok, any_err + end + else + local url + url, search_err = search.find_suitable_rock(query) + if url then + return get_file(url) + end + end + return nil, "Could not find a result named "..name..(version and " "..version or "").. + (search_err and ": "..search_err or ".") +end + +--- Driver function for the "download" command. +-- @param name string: a rock name. +-- @param version string or nil: if the name of a package is given, a +-- version may also be passed. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +function download.command(flags, name, version) + assert(type(version) == "string" or not version) + if type(name) ~= "string" and not flags["all"] then + return nil, "Argument missing. "..util.see_help("download") + end + if not name then name, version = "", "" end + + local arch + + if flags["source"] then + arch = "src" + elseif flags["rockspec"] then + arch = "rockspec" + elseif flags["arch"] then + arch = flags["arch"] + end + + local dl, err = download.download(arch, name:lower(), version, flags["all"]) + return dl and true, err +end + +return download diff --git a/src/luarocks/cmd/help.lua b/src/luarocks/cmd/help.lua new file mode 100644 index 00000000..d27c3a50 --- /dev/null +++ b/src/luarocks/cmd/help.lua @@ -0,0 +1,117 @@ + +--- Module implementing the LuaRocks "help" command. +-- This is a generic help display module, which +-- uses a global table called "commands" to find commands +-- to show help for; each command should be represented by a +-- table containing "help" and "help_summary" fields. +local help = {} + +local util = require("luarocks.util") +local cfg = require("luarocks.core.cfg") +local dir = require("luarocks.dir") + +local program = util.this_program("luarocks") + +help.help_summary = "Help on commands. Type '"..program.." help ' for more." + +help.help_arguments = "[]" +help.help = [[ + is the command to show help for. +]] + +local function print_banner() + util.printout("\nLuaRocks "..cfg.program_version..", a module deployment system for Lua") +end + +local function print_section(section) + util.printout("\n"..section) +end + +local function get_status(status) + if status then + return "ok" + else + return "not found" + end +end + +--- Driver function for the "help" command. +-- @param command string or nil: command to show help for; if not +-- given, help summaries for all commands are shown. +-- @return boolean or (nil, string): true if there were no errors +-- or nil and an error message if an invalid command was requested. +function help.command(flags, command) + if not command then + local conf = cfg.which_config() + print_banner() + print_section("NAME") + util.printout("\t"..program..[[ - ]]..program_description) + print_section("SYNOPSIS") + util.printout("\t"..program..[[ [--from= | --only-from=] [--to=] [VAR=VALUE]... [] ]]) + print_section("GENERAL OPTIONS") + util.printout([[ + These apply to all commands, as appropriate: + + --server= Fetch rocks/rockspecs from this server + (takes priority over config file) + --only-server= Fetch rocks/rockspecs from this server only + (overrides any entries in the config file) + --only-sources= Restrict downloads to paths matching the + given URL. + --tree= Which tree to operate on. + --local Use the tree in the user's home directory. + To enable it, see ']]..program..[[ help path'. + --verbose Display verbose output of commands executed. + --timeout= Timeout on network operations, in seconds. + 0 means no timeout (wait forever). + Default is ]]..tostring(cfg.connection_timeout)..[[.]]) + print_section("VARIABLES") + util.printout([[ + Variables from the "variables" table of the configuration file + can be overriden with VAR=VALUE assignments.]]) + print_section("COMMANDS") + for name, command in util.sortedpairs(commands) do + local cmd = require(command) + util.printout("", name) + util.printout("\t", cmd.help_summary) + end + print_section("CONFIGURATION") + util.printout("\tLua version: " .. cfg.lua_version) + util.printout("\tConfiguration files:") + util.printout("\t\tSystem: ".. dir.normalize(conf.system.file) .. " (" .. get_status(conf.system.ok) ..")") + if conf.user.file then + util.printout("\t\tUser : ".. dir.normalize(conf.user.file) .. " (" .. get_status(conf.user.ok) ..")\n") + else + util.printout("\t\tUser : disabled in this LuaRocks installation.\n") + end + util.printout("\tRocks trees in use: ") + for _, tree in ipairs(cfg.rocks_trees) do + if type(tree) == "string" then + util.printout("\t\t"..dir.normalize(tree)) + else + local name = tree.name and " (\""..tree.name.."\")" or "" + util.printout("\t\t"..dir.normalize(tree.root)..name) + end + end + else + command = command:gsub("-", "_") + local cmd = commands[command] and require(commands[command]) + if cmd then + local arguments = cmd.help_arguments or "" + print_banner() + print_section("NAME") + util.printout("\t"..program.." "..command.." - "..cmd.help_summary) + print_section("SYNOPSIS") + util.printout("\t"..program.." "..command.." "..arguments) + print_section("DESCRIPTION") + util.printout("",(cmd.help:gsub("\n","\n\t"):gsub("\n\t$",""))) + print_section("SEE ALSO") + util.printout("","'"..program.." help' for general options and configuration.\n") + else + return nil, "Unknown command: "..command + end + end + return true +end + +return help diff --git a/src/luarocks/cmd/install.lua b/src/luarocks/cmd/install.lua new file mode 100644 index 00000000..c9b085f5 --- /dev/null +++ b/src/luarocks/cmd/install.lua @@ -0,0 +1,183 @@ +--- Module implementing the LuaRocks "install" command. +-- Installs binary rocks. +local install = {} + +local path = require("luarocks.path") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local util = require("luarocks.util") +local fs = require("luarocks.fs") +local deps = require("luarocks.deps") +local writer = require("luarocks.manif.writer") +local remove = require("luarocks.remove") +local cfg = require("luarocks.core.cfg") + +install.help_summary = "Install a rock." + +install.help_arguments = "{| []}" + +install.help = [[ +Argument may be the name of a rock to be fetched from a repository +or a filename of a locally available rock. + +--keep Do not remove previously installed versions of the + rock after installing a new one. This behavior can + be made permanent by setting keep_other_versions=true + in the configuration file. + +--only-deps Installs only the dependencies of the rock. +]]..util.deps_mode_help() + + +--- Install a binary rock. +-- @param rock_file string: local or remote filename of a rock. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @return (string, string) or (nil, string, [string]): Name and version of +-- installed rock if succeeded or nil and an error message followed by an error code. +function install.install_binary_rock(rock_file, deps_mode) + assert(type(rock_file) == "string") + + local name, version, arch = path.parse_name(rock_file) + if not name then + return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." + end + + if arch ~= "all" and arch ~= cfg.arch then + return nil, "Incompatible architecture "..arch, "arch" + end + if repos.is_installed(name, version) then + repos.delete_version(name, version, deps_mode) + end + + local rollback = util.schedule_function(function() + fs.delete(path.install_dir(name, version)) + fs.remove_dir_if_empty(path.versions_dir(name)) + end) + + local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) + if not ok then return nil, err, errcode end + + local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version)) + if err then + return nil, "Failed loading rockspec for installed package: "..err, errcode + end + + if deps_mode == "none" then + util.printerr("Warning: skipping dependency checks.") + else + ok, err, errcode = deps.check_external_deps(rockspec, "install") + if err then return nil, err, errcode end + end + + -- For compatibility with .rock files built with LuaRocks 1 + if not fs.exists(path.rock_manifest_file(name, version)) then + ok, err = writer.make_rock_manifest(name, version) + if err then return nil, err end + end + + if deps_mode ~= "none" then + ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) + if err then return nil, err, errcode end + end + + ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) + if err then return nil, err end + + util.remove_scheduled_function(rollback) + rollback = util.schedule_function(function() + repos.delete_version(name, version, deps_mode) + end) + + ok, err = repos.run_hook(rockspec, "post_install") + if err then return nil, err end + + util.announce_install(rockspec) + util.remove_scheduled_function(rollback) + return name, version +end + +--- Installs the dependencies of a binary rock. +-- @param rock_file string: local or remote filename of a rock. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @return (string, string) or (nil, string, [string]): Name and version of +-- the rock whose dependencies were installed if succeeded or nil and an error message +-- followed by an error code. +function install.install_binary_rock_deps(rock_file, deps_mode) + assert(type(rock_file) == "string") + + local name, version, arch = path.parse_name(rock_file) + if not name then + return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." + end + + if arch ~= "all" and arch ~= cfg.arch then + return nil, "Incompatible architecture "..arch, "arch" + end + + local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) + if not ok then return nil, err, errcode end + + local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version)) + if err then + return nil, "Failed loading rockspec for installed package: "..err, errcode + end + + ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) + if err then return nil, err, errcode end + + util.printout() + util.printout("Successfully installed dependencies for " ..name.." "..version) + + return name, version +end + +--- Driver function for the "install" command. +-- @param name string: name of a binary rock. If an URL or pathname +-- to a binary rock is given, fetches and installs it. If a rockspec or a +-- source rock is given, forwards the request to the "build" command. +-- If a package name is given, forwards the request to "search" and, +-- if returned a result, installs the matching rock. +-- @param version string: When passing a package name, a version number +-- may also be given. +-- @return boolean or (nil, string, exitcode): True if installation was +-- successful, nil and an error message otherwise. exitcode is optionally returned. +function install.command(flags, name, version) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("install") + end + + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + + if name:match("%.rockspec$") or name:match("%.src%.rock$") then + local build = require("luarocks.build") + return build.command(flags, name) + elseif name:match("%.rock$") then + if flags["only-deps"] then + ok, err = install.install_binary_rock_deps(name, deps.get_deps_mode(flags)) + else + ok, err = install.install_binary_rock(name, deps.get_deps_mode(flags)) + end + if not ok then return nil, err end + name, version = ok, err + if (not flags["only-deps"]) and (not flags["keep"]) and not cfg.keep_other_versions then + local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) + if not ok then util.printerr(err) end + end + return name, version + else + local search = require("luarocks.search") + local url, err = search.find_suitable_rock(search.make_query(name:lower(), version)) + if not url then + return nil, err + end + util.printout("Installing "..url) + return install.command(flags, url) + end +end + +return install diff --git a/src/luarocks/cmd/lint.lua b/src/luarocks/cmd/lint.lua new file mode 100644 index 00000000..c9ea45ea --- /dev/null +++ b/src/luarocks/cmd/lint.lua @@ -0,0 +1,53 @@ + +--- Module implementing the LuaRocks "lint" command. +-- Utility function that checks syntax of the rockspec. +local lint = {} + +local util = require("luarocks.util") +local download = require("luarocks.download") +local fetch = require("luarocks.fetch") + +lint.help_summary = "Check syntax of a rockspec." +lint.help_arguments = "" +lint.help = [[ +This is a utility function that checks the syntax of a rockspec. + +It returns success or failure if the text of a rockspec is +syntactically correct. +]] + +function lint.command(flags, input) + if not input then + return nil, "Argument missing. "..util.see_help("lint") + end + + local filename = input + if not input:match(".rockspec$") then + local err + filename, err = download.download("rockspec", input:lower()) + if not filename then + return nil, err + end + end + + local rs, err = fetch.load_local_rockspec(filename) + if not rs then + return nil, "Failed loading rockspec: "..err + end + + local ok = true + + -- This should have been done in the type checker, + -- but it would break compatibility of other commands. + -- Making 'lint' alone be stricter shouldn't be a problem, + -- because extra-strict checks is what lint-type commands + -- are all about. + if not rs.description.license then + util.printerr("Rockspec has no license field.") + ok = false + end + + return ok, ok or filename.." failed consistency checks." +end + +return lint diff --git a/src/luarocks/cmd/list.lua b/src/luarocks/cmd/list.lua new file mode 100644 index 00000000..45f1a26f --- /dev/null +++ b/src/luarocks/cmd/list.lua @@ -0,0 +1,95 @@ + +--- Module implementing the LuaRocks "list" command. +-- Lists currently installed rocks. +local list = {} + +local search = require("luarocks.search") +local deps = require("luarocks.deps") +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local path = require("luarocks.path") + +list.help_summary = "List currently installed rocks." +list.help_arguments = "[--porcelain] " +list.help = [[ + is a substring of a rock name to filter by. + +--outdated List only rocks for which there is a + higher version available in the rocks server. + +--porcelain Produce machine-friendly output. +]] + +local function check_outdated(trees, query) + local results_installed = {} + for _, tree in ipairs(trees) do + search.manifest_search(results_installed, path.rocks_dir(tree), query) + end + local outdated = {} + for name, versions in util.sortedpairs(results_installed) do + versions = util.keys(versions) + table.sort(versions, deps.compare_versions) + local latest_installed = versions[1] + + local query_available = search.make_query(name:lower()) + query.exact_name = true + local results_available, err = search.search_repos(query_available) + + if results_available[name] then + local available_versions = util.keys(results_available[name]) + table.sort(available_versions, deps.compare_versions) + local latest_available = available_versions[1] + local latest_available_repo = results_available[name][latest_available][1].repo + + if deps.compare_versions(latest_available, latest_installed) then + table.insert(outdated, { name = name, installed = latest_installed, available = latest_available, repo = latest_available_repo }) + end + end + end + return outdated +end + +local function list_outdated(trees, query, porcelain) + util.title("Outdated rocks:", porcelain) + local outdated = check_outdated(trees, query) + for _, item in ipairs(outdated) do + if porcelain then + util.printout(item.name, item.installed, item.available, item.repo) + else + util.printout(item.name) + util.printout(" "..item.installed.." < "..item.available.." at "..item.repo) + util.printout() + end + end + return true +end + +--- Driver function for "list" command. +-- @param filter string or nil: A substring of a rock name to filter by. +-- @param version string or nil: a version may also be passed. +-- @return boolean: True if succeeded, nil on errors. +function list.command(flags, filter, version) + local query = search.make_query(filter and filter:lower() or "", version) + query.exact_name = false + local trees = cfg.rocks_trees + if flags["tree"] then + trees = { flags["tree"] } + end + + if flags["outdated"] then + return list_outdated(trees, query, flags["porcelain"]) + end + + local results = {} + for _, tree in ipairs(trees) do + local ok, err, errcode = search.manifest_search(results, path.rocks_dir(tree), query) + if not ok and errcode ~= "open" then + util.warning(err) + end + end + util.title("Installed rocks:", flags["porcelain"]) + search.print_results(results, flags["porcelain"]) + return true +end + +return list diff --git a/src/luarocks/cmd/make.lua b/src/luarocks/cmd/make.lua new file mode 100644 index 00000000..eb38bff0 --- /dev/null +++ b/src/luarocks/cmd/make.lua @@ -0,0 +1,86 @@ + +--- Module implementing the LuaRocks "make" command. +-- Builds sources in the current directory, but unlike "build", +-- it does not fetch sources, etc., assuming everything is +-- available in the current directory. +local make = {} + +local build = require("luarocks.build") +local fs = require("luarocks.fs") +local util = require("luarocks.util") +local cfg = require("luarocks.core.cfg") +local fetch = require("luarocks.fetch") +local pack = require("luarocks.pack") +local remove = require("luarocks.remove") +local deps = require("luarocks.deps") + +make.help_summary = "Compile package in current directory using a rockspec." +make.help_arguments = "[--pack-binary-rock] []" +make.help = [[ +Builds sources in the current directory, but unlike "build", +it does not fetch sources, etc., assuming everything is +available in the current directory. If no argument is given, +it looks for a rockspec in the current directory and in "rockspec/" +and "rockspecs/" subdirectories, picking the rockspec with newest version +or without version name. If rockspecs for different rocks are found +or there are several rockspecs without version, you must specify which to use, +through the command-line. + +This command is useful as a tool for debugging rockspecs. +To install rocks, you'll normally want to use the "install" and +"build" commands. See the help on those for details. + +--pack-binary-rock Do not install rock. Instead, produce a .rock file + with the contents of compilation in the current + directory. + +--keep Do not remove previously installed versions of the + rock after installing a new one. This behavior can + be made permanent by setting keep_other_versions=true + in the configuration file. + +--branch= Override the `source.branch` field in the loaded + rockspec. Allows to specify a different branch to + fetch. Particularly for SCM rocks. + +]] + +--- Driver function for "make" command. +-- @param name string: A local rockspec. +-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an +-- error message otherwise. exitcode is optionally returned. +function make.command(flags, rockspec) + assert(type(rockspec) == "string" or not rockspec) + + if not rockspec then + local err + rockspec, err = util.get_default_rockspec() + if not rockspec then + return nil, err + end + end + if not rockspec:match("rockspec$") then + return nil, "Invalid argument: 'make' takes a rockspec as a parameter. "..util.see_help("make") + end + + if flags["pack-binary-rock"] then + local rspec, err, errcode = fetch.load_rockspec(rockspec) + if not rspec then + return nil, err + end + return pack.pack_binary_rock(rspec.name, rspec.version, build.build_rockspec, rockspec, false, true, deps.get_deps_mode(flags)) + else + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + ok, err = build.build_rockspec(rockspec, false, true, deps.get_deps_mode(flags)) + if not ok then return nil, err end + local name, version = ok, err + if (not flags["keep"]) and not cfg.keep_other_versions then + local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) + if not ok then util.printerr(err) end + end + return name, version + end +end + +return make diff --git a/src/luarocks/cmd/new_version.lua b/src/luarocks/cmd/new_version.lua new file mode 100644 index 00000000..b13dbb97 --- /dev/null +++ b/src/luarocks/cmd/new_version.lua @@ -0,0 +1,199 @@ + +--- Module implementing the LuaRocks "new_version" command. +-- Utility function that writes a new rockspec, updating data from a previous one. +local new_version = {} + +local util = require("luarocks.util") +local download = require("luarocks.download") +local fetch = require("luarocks.fetch") +local persist = require("luarocks.persist") +local fs = require("luarocks.fs") +local type_check = require("luarocks.type_check") + +new_version.help_summary = "Auto-write a rockspec for a new version of a rock." +new_version.help_arguments = "[--tag=] [|] [] []" +new_version.help = [[ +This is a utility function that writes a new rockspec, updating data +from a previous one. + +If a package name is given, it downloads the latest rockspec from the +default server. If a rockspec is given, it uses it instead. If no argument +is given, it looks for a rockspec same way 'luarocks make' does. + +If the version number is not given and tag is passed using --tag, +it is used as the version, with 'v' removed from beginning. +Otherwise, it only increments the revision number of the given +(or downloaded) rockspec. + +If a URL is given, it replaces the one from the old rockspec with the +given URL. If a URL is not given and a new version is given, it tries +to guess the new URL by replacing occurrences of the version number +in the URL or tag. It also tries to download the new URL to determine +the new MD5 checksum. + +If a tag is given, it replaces the one from the old rockspec. If there is +an old tag but no new one passed, it is guessed in the same way URL is. + +WARNING: it writes the new rockspec to the current directory, +overwriting the file if it already exists. +]] + +local function try_replace(tbl, field, old, new) + if not tbl[field] then + return false + end + local old_field = tbl[field] + local new_field = tbl[field]:gsub(old, new) + if new_field ~= old_field then + util.printout("Guessing new '"..field.."' field as "..new_field) + tbl[field] = new_field + return true + end + return false +end + +-- Try to download source file using URL from a rockspec. +-- If it specified MD5, update it. +-- @return (true, false) if MD5 was not specified or it stayed same, +-- (true, true) if MD5 changed, (nil, string) on error. +local function check_url_and_update_md5(out_rs) + local file, temp_dir = fetch.fetch_url_at_temp_dir(out_rs.source.url, "luarocks-new-version-"..out_rs.package) + if not file then + util.printerr("Warning: invalid URL - "..temp_dir) + return true, false + end + + local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, out_rs.source.url, out_rs.source.dir) + if not inferred_dir then + return nil, found_dir + end + + if found_dir and found_dir ~= inferred_dir then + out_rs.source.dir = found_dir + end + + if file then + if out_rs.source.md5 then + util.printout("File successfully downloaded. Updating MD5 checksum...") + local new_md5, err = fs.get_md5(file) + if not new_md5 then + return nil, err + end + local old_md5 = out_rs.source.md5 + out_rs.source.md5 = new_md5 + return true, new_md5 ~= old_md5 + else + util.printout("File successfully downloaded.") + return true, false + end + end +end + +local function update_source_section(out_rs, url, tag, old_ver, new_ver) + if tag then + out_rs.source.tag = tag + end + if url then + out_rs.source.url = url + return check_url_and_update_md5(out_rs) + end + if new_ver == old_ver then + return true + end + if out_rs.source.dir then + try_replace(out_rs.source, "dir", old_ver, new_ver) + end + if out_rs.source.file then + try_replace(out_rs.source, "file", old_ver, new_ver) + end + if try_replace(out_rs.source, "url", old_ver, new_ver) then + return check_url_and_update_md5(out_rs) + end + if tag or try_replace(out_rs.source, "tag", old_ver, new_ver) then + return true + end + -- Couldn't replace anything significant, use the old URL. + local ok, md5_changed = check_url_and_update_md5(out_rs) + if not ok then + return nil, md5_changed + end + if md5_changed then + util.printerr("Warning: URL is the same, but MD5 has changed. Old rockspec is broken.") + end + return true +end + +function new_version.command(flags, input, version, url) + if not input then + local err + input, err = util.get_default_rockspec() + if not input then + return nil, err + end + end + assert(type(input) == "string") + + local filename, err + if input:match("rockspec$") then + filename, err = fetch.fetch_url(input) + if not filename then + return nil, err + end + else + filename, err = download.download("rockspec", input:lower()) + if not filename then + return nil, err + end + end + + local valid_rs, err = fetch.load_rockspec(filename) + if not valid_rs then + return nil, err + end + + local old_ver, old_rev = valid_rs.version:match("(.*)%-(%d+)$") + local new_ver, new_rev + + if flags.tag and not version then + version = flags.tag:gsub("^v", "") + end + + if version then + new_ver, new_rev = version:match("(.*)%-(%d+)$") + new_rev = tonumber(new_rev) + if not new_rev then + new_ver = version + new_rev = 1 + end + else + new_ver = old_ver + new_rev = tonumber(old_rev) + 1 + end + local new_rockver = new_ver:gsub("-", "") + + local out_rs, err = persist.load_into_table(filename) + local out_name = out_rs.package:lower() + out_rs.version = new_rockver.."-"..new_rev + + local ok, err = update_source_section(out_rs, url, flags.tag, old_ver, new_ver) + if not ok then return nil, err end + + if out_rs.build and out_rs.build.type == "module" then + out_rs.build.type = "builtin" + end + + local out_filename = out_name.."-"..new_rockver.."-"..new_rev..".rockspec" + + persist.save_from_table(out_filename, out_rs, type_check.rockspec_order) + + util.printout("Wrote "..out_filename) + + local valid_out_rs, err = fetch.load_local_rockspec(out_filename) + if not valid_out_rs then + return nil, "Failed loading generated rockspec: "..err + end + + return true +end + +return new_version diff --git a/src/luarocks/cmd/pack.lua b/src/luarocks/cmd/pack.lua new file mode 100644 index 00000000..655cbf37 --- /dev/null +++ b/src/luarocks/cmd/pack.lua @@ -0,0 +1,193 @@ + +--- Module implementing the LuaRocks "pack" command. +-- Creates a rock, packing sources or binaries. +local pack = {} + +local unpack = unpack or table.unpack + +local path = require("luarocks.path") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") +local manif = require("luarocks.manif") +local search = require("luarocks.search") + +pack.help_summary = "Create a rock, packing sources or binaries." +pack.help_arguments = "{| []}" +pack.help = [[ +Argument may be a rockspec file, for creating a source rock, +or the name of an installed package, for creating a binary rock. +In the latter case, the app version may be given as a second +argument. +]] + +--- Create a source rock. +-- Packages a rockspec and its required source files in a rock +-- file with the .src.rock extension, which can later be built and +-- installed with the "build" command. +-- @param rockspec_file string: An URL or pathname for a rockspec file. +-- @return string or (nil, string): The filename of the resulting +-- .src.rock file; or nil and an error message. +function pack.pack_source_rock(rockspec_file) + assert(type(rockspec_file) == "string") + + local rockspec, err = fetch.load_rockspec(rockspec_file) + if err then + return nil, "Error loading rockspec: "..err + end + rockspec_file = rockspec.local_filename + + local name_version = rockspec.name .. "-" .. rockspec.version + local rock_file = fs.absolute_name(name_version .. ".src.rock") + + local source_file, source_dir = fetch.fetch_sources(rockspec, false) + if not source_file then + return nil, source_dir + end + local ok, err = fs.change_dir(source_dir) + if not ok then return nil, err end + + fs.delete(rock_file) + fs.copy(rockspec_file, source_dir, cfg.perm_read) + if not fs.zip(rock_file, dir.base_name(rockspec_file), dir.base_name(source_file)) then + return nil, "Failed packing "..rock_file + end + fs.pop_dir() + + return rock_file +end + +local function copy_back_files(name, version, file_tree, deploy_dir, pack_dir, perms) + local ok, err = fs.make_dir(pack_dir) + if not ok then return nil, err end + for file, sub in pairs(file_tree) do + local source = dir.path(deploy_dir, file) + local target = dir.path(pack_dir, file) + if type(sub) == "table" then + local ok, err = copy_back_files(name, version, sub, source, target) + if not ok then return nil, err end + else + local versioned = path.versioned_name(source, deploy_dir, name, version) + if fs.exists(versioned) then + fs.copy(versioned, target, perms) + else + fs.copy(source, target, perms) + end + end + end + return true +end + +-- @param name string: Name of package to pack. +-- @param version string or nil: A version number may also be passed. +-- @param tree string or nil: An optional tree to pick the package from. +-- @return string or (nil, string): The filename of the resulting +-- .src.rock file; or nil and an error message. +local function do_pack_binary_rock(name, version, tree) + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local repo, repo_url + name, version, repo, repo_url = search.pick_installed_rock(name, version, tree) + if not name then + return nil, version + end + + local root = path.root_dir(repo_url) + local prefix = path.install_dir(name, version, root) + if not fs.exists(prefix) then + return nil, "'"..name.." "..version.."' does not seem to be an installed rock." + end + + local rock_manifest, err = manif.load_rock_manifest(name, version, root) + if not rock_manifest then return nil, err end + + local name_version = name .. "-" .. version + local rock_file = fs.absolute_name(name_version .. "."..cfg.arch..".rock") + + local temp_dir = fs.make_temp_dir("pack") + fs.copy_contents(prefix, temp_dir) + + local is_binary = false + if rock_manifest.lib then + local ok, err = copy_back_files(name, version, rock_manifest.lib, path.deploy_lib_dir(root), dir.path(temp_dir, "lib"), cfg.perm_exec) + if not ok then return nil, "Failed copying back files: " .. err end + is_binary = true + end + if rock_manifest.lua then + local ok, err = copy_back_files(name, version, rock_manifest.lua, path.deploy_lua_dir(root), dir.path(temp_dir, "lua"), cfg.perm_read) + if not ok then return nil, "Failed copying back files: " .. err end + end + + local ok, err = fs.change_dir(temp_dir) + if not ok then return nil, err end + if not is_binary and not repos.has_binaries(name, version) then + rock_file = rock_file:gsub("%."..cfg.arch:gsub("%-","%%-").."%.", ".all.") + end + fs.delete(rock_file) + if not fs.zip(rock_file, unpack(fs.list_dir())) then + return nil, "Failed packing "..rock_file + end + fs.pop_dir() + fs.delete(temp_dir) + return rock_file +end + +function pack.pack_binary_rock(name, version, cmd, ...) + + -- The --pack-binary-rock option for "luarocks build" basically performs + -- "luarocks build" on a temporary tree and then "luarocks pack". The + -- alternative would require refactoring parts of luarocks.build and + -- luarocks.pack, which would save a few file operations: the idea would be + -- to shave off the final deploy steps from the build phase and the initial + -- collect steps from the pack phase. + + local temp_dir, err = fs.make_temp_dir("luarocks-build-pack-"..dir.base_name(name)) + if not temp_dir then + return nil, "Failed creating temporary directory: "..err + end + util.schedule_function(fs.delete, temp_dir) + + path.use_tree(temp_dir) + local ok, err = cmd(...) + if not ok then + return nil, err + end + local rname, rversion = path.parse_name(name) + if not rname then + rname, rversion = name, version + end + return do_pack_binary_rock(rname, rversion, temp_dir) +end + +--- Driver function for the "pack" command. +-- @param arg string: may be a rockspec file, for creating a source rock, +-- or the name of an installed package, for creating a binary rock. +-- @param version string or nil: if the name of a package is given, a +-- version may also be passed. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +function pack.command(flags, arg, version) + assert(type(version) == "string" or not version) + if type(arg) ~= "string" then + return nil, "Argument missing. "..util.see_help("pack") + end + + local file, err + if arg:match(".*%.rockspec") then + file, err = pack.pack_source_rock(arg) + else + file, err = do_pack_binary_rock(arg:lower(), version, flags["tree"]) + end + if err then + return nil, err + else + util.printout("Packed: "..file) + return true + end +end + +return pack diff --git a/src/luarocks/cmd/path_cmd.lua b/src/luarocks/cmd/path_cmd.lua new file mode 100644 index 00000000..516a0c47 --- /dev/null +++ b/src/luarocks/cmd/path_cmd.lua @@ -0,0 +1,68 @@ + +--- @module luarocks.path_cmd +-- Driver for the `luarocks path` command. +local path_cmd = {} + +local util = require("luarocks.util") +local cfg = require("luarocks.core.cfg") + +path_cmd.help_summary = "Return the currently configured package path." +path_cmd.help_arguments = "" +path_cmd.help = [[ +Returns the package path currently configured for this installation +of LuaRocks, formatted as shell commands to update LUA_PATH and LUA_CPATH. + +--bin Adds the system path to the output + +--append Appends the paths to the existing paths. Default is to prefix + the LR paths to the existing paths. + +--lr-path Exports the Lua path (not formatted as shell command) + +--lr-cpath Exports the Lua cpath (not formatted as shell command) + +--lr-bin Exports the system path (not formatted as shell command) + + +On Unix systems, you may run: + eval `luarocks path` +And on Windows: + luarocks path > "%temp%\_lrp.bat" && call "%temp%\_lrp.bat" && del "%temp%\_lrp.bat" +]] + +--- Driver function for "path" command. +-- @return boolean This function always succeeds. +function path_cmd.command(flags) + local lr_path, lr_cpath, lr_bin = cfg.package_paths(flags["tree"]) + local path_sep = cfg.export_path_separator + + if flags["lr-path"] then + util.printout(util.remove_path_dupes(lr_path, ';')) + return true + elseif flags["lr-cpath"] then + util.printout(util.remove_path_dupes(lr_cpath, ';')) + return true + elseif flags["lr-bin"] then + util.printout(util.remove_path_dupes(lr_bin, path_sep)) + return true + end + + if flags["append"] then + lr_path = package.path .. ";" .. lr_path + lr_cpath = package.cpath .. ";" .. lr_cpath + lr_bin = os.getenv("PATH") .. path_sep .. lr_bin + else + lr_path = lr_path.. ";" .. package.path + lr_cpath = lr_cpath .. ";" .. package.cpath + lr_bin = lr_bin .. path_sep .. os.getenv("PATH") + end + + util.printout(cfg.export_lua_path:format(util.remove_path_dupes(lr_path, ';'))) + util.printout(cfg.export_lua_cpath:format(util.remove_path_dupes(lr_cpath, ';'))) + if flags["bin"] then + util.printout(cfg.export_path:format(util.remove_path_dupes(lr_bin, path_sep))) + end + return true +end + +return path_cmd diff --git a/src/luarocks/cmd/purge.lua b/src/luarocks/cmd/purge.lua new file mode 100644 index 00000000..50f290c8 --- /dev/null +++ b/src/luarocks/cmd/purge.lua @@ -0,0 +1,77 @@ + +--- Module implementing the LuaRocks "purge" command. +-- Remove all rocks from a given tree. +local purge = {} + +local util = require("luarocks.util") +local fs = require("luarocks.fs") +local path = require("luarocks.path") +local search = require("luarocks.search") +local deps = require("luarocks.deps") +local repos = require("luarocks.repos") +local writer = require("luarocks.manif.writer") +local cfg = require("luarocks.core.cfg") +local remove = require("luarocks.remove") + +purge.help_summary = "Remove all installed rocks from a tree." +purge.help_arguments = "--tree= [--old-versions]" +purge.help = [[ +This command removes rocks en masse from a given tree. +By default, it removes all rocks from a tree. + +The --tree argument is mandatory: luarocks purge does not +assume a default tree. + +--old-versions Keep the highest-numbered version of each + rock and remove the other ones. By default + it only removes old versions if they are + not needed as dependencies. This can be + overridden with the flag --force. +]] + +function purge.command(flags) + local tree = flags["tree"] + + if type(tree) ~= "string" then + return nil, "The --tree argument is mandatory. "..util.see_help("purge") + end + + local results = {} + local query = search.make_query("") + query.exact_name = false + if not fs.is_dir(tree) then + return nil, "Directory not found: "..tree + end + + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + + search.manifest_search(results, path.rocks_dir(tree), query) + + local sort = function(a,b) return deps.compare_versions(b,a) end + if flags["old-versions"] then + sort = deps.compare_versions + end + + for package, versions in util.sortedpairs(results) do + for version, _ in util.sortedpairs(versions, sort) do + if flags["old-versions"] then + util.printout("Keeping "..package.." "..version.."...") + local ok, err = remove.remove_other_versions(package, version, flags["force"], flags["force-fast"]) + if not ok then + util.printerr(err) + end + break + else + util.printout("Removing "..package.." "..version.."...") + local ok, err = repos.delete_version(package, version, "none", true) + if not ok then + util.printerr(err) + end + end + end + end + return writer.make_manifest(cfg.rocks_dir, "one") +end + +return purge diff --git a/src/luarocks/cmd/remove.lua b/src/luarocks/cmd/remove.lua new file mode 100644 index 00000000..e7f37604 --- /dev/null +++ b/src/luarocks/cmd/remove.lua @@ -0,0 +1,165 @@ + +--- Module implementing the LuaRocks "remove" command. +-- Uninstalls rocks. +local remove = {} + +local search = require("luarocks.search") +local deps = require("luarocks.deps") +local fetch = require("luarocks.fetch") +local repos = require("luarocks.repos") +local path = require("luarocks.path") +local util = require("luarocks.util") +local cfg = require("luarocks.core.cfg") +local fs = require("luarocks.fs") + +remove.help_summary = "Uninstall a rock." +remove.help_arguments = "[--force|--force-fast] []" +remove.help = [[ +Argument is the name of a rock to be uninstalled. +If a version is not given, try to remove all versions at once. +Will only perform the removal if it does not break dependencies. +To override this check and force the removal, use --force. +To perform a forced removal without reporting dependency issues, +use --force-fast. + +]]..util.deps_mode_help() + +--- Obtain a list of packages that depend on the given set of packages +-- (where all packages of the set are versions of one program). +-- @param name string: the name of a program +-- @param versions array of string: the versions to be deleted. +-- @return array of string: an empty table if no packages depend on any +-- of the given list, or an array of strings in "name/version" format. +local function check_dependents(name, versions, deps_mode) + local dependents = {} + local blacklist = {} + blacklist[name] = {} + for version, _ in pairs(versions) do + blacklist[name][version] = true + end + local local_rocks = {} + local query_all = search.make_query("") + query_all.exact_name = false + search.manifest_search(local_rocks, cfg.rocks_dir, query_all) + local_rocks[name] = nil + for rock_name, rock_versions in pairs(local_rocks) do + for rock_version, _ in pairs(rock_versions) do + local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version)) + if rockspec then + local _, missing = deps.match_deps(rockspec, blacklist, deps_mode) + if missing[name] then + table.insert(dependents, { name = rock_name, version = rock_version }) + end + end + end + end + return dependents +end + +--- Delete given versions of a program. +-- @param name string: the name of a program +-- @param versions array of string: the versions to be deleted. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @return boolean or (nil, string): true on success or nil and an error message. +local function delete_versions(name, versions, deps_mode) + + for version, _ in pairs(versions) do + util.printout("Removing "..name.." "..version.."...") + local ok, err = repos.delete_version(name, version, deps_mode) + if not ok then return nil, err end + end + + return true +end + +function remove.remove_search_results(results, name, deps_mode, force, fast) + local versions = results[name] + + local version = next(versions) + local second = next(versions, version) + + local dependents = {} + if not fast then + util.printout("Checking stability of dependencies in the absence of") + util.printout(name.." "..table.concat(util.keys(versions), ", ").."...") + util.printout() + dependents = check_dependents(name, versions, deps_mode) + end + + if #dependents > 0 then + if force or fast then + util.printerr("The following packages may be broken by this forced removal:") + for _, dependent in ipairs(dependents) do + util.printerr(dependent.name.." "..dependent.version) + end + util.printerr() + else + if not second then + util.printerr("Will not remove "..name.." "..version..".") + util.printerr("Removing it would break dependencies for: ") + else + util.printerr("Will not remove installed versions of "..name..".") + util.printerr("Removing them would break dependencies for: ") + end + for _, dependent in ipairs(dependents) do + util.printerr(dependent.name.." "..dependent.version) + end + util.printerr() + util.printerr("Use --force to force removal (warning: this may break modules).") + return nil, "Failed removing." + end + end + + local ok, err = delete_versions(name, versions, deps_mode) + if not ok then return nil, err end + + util.printout("Removal successful.") + return true +end + +function remove.remove_other_versions(name, version, force, fast) + local results = {} + search.manifest_search(results, cfg.rocks_dir, { name = name, exact_name = true, constraints = {{ op = "~=", version = version}} }) + if results[name] then + return remove.remove_search_results(results, name, cfg.deps_mode, force, fast) + end + return true +end + +--- Driver function for the "remove" command. +-- @param name string: name of a rock. If a version is given, refer to +-- a specific version; otherwise, try to remove all versions. +-- @param version string: When passing a package name, a version number +-- may also be given. +-- @return boolean or (nil, string, exitcode): True if removal was +-- successful, nil and an error message otherwise. exitcode is optionally returned. +function remove.command(flags, name, version) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("remove") + end + + local deps_mode = flags["deps-mode"] or cfg.deps_mode + + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + + local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$") + local filename = name + if rock_type then + name, version = path.parse_name(filename) + if not name then return nil, "Invalid "..rock_type.." filename: "..filename end + end + + local results = {} + name = name:lower() + search.manifest_search(results, cfg.rocks_dir, search.make_query(name, version)) + if not results[name] then + return nil, "Could not find rock '"..name..(version and " "..version or "").."' in "..path.rocks_tree_to_string(cfg.root_dir) + end + + return remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"]) +end + +return remove diff --git a/src/luarocks/cmd/search.lua b/src/luarocks/cmd/search.lua new file mode 100644 index 00000000..44eff694 --- /dev/null +++ b/src/luarocks/cmd/search.lua @@ -0,0 +1,482 @@ + +--- Module implementing the LuaRocks "search" command. +-- Queries LuaRocks servers. +local search = {} + + +local dir = require("luarocks.dir") +local path = require("luarocks.path") +local manif = require("luarocks.manif") +local deps = require("luarocks.deps") +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") + +search.help_summary = "Query the LuaRocks servers." +search.help_arguments = "[--source] [--binary] { [] | --all }" +search.help = [[ +--source Return only rockspecs and source rocks, + to be used with the "build" command. +--binary Return only pure Lua and binary rocks (rocks that can be used + with the "install" command without requiring a C toolchain). +--all List all contents of the server that are suitable to + this platform, do not filter by name. +]] + +--- Convert the arch field of a query table to table format. +-- @param query table: A query table. +local function query_arch_as_table(query) + local format = type(query.arch) + if format == "table" then + return + elseif format == "nil" then + local accept = {} + accept["src"] = true + accept["all"] = true + accept["rockspec"] = true + accept["installed"] = true + accept[cfg.arch] = true + query.arch = accept + elseif format == "string" then + local accept = {} + for a in query.arch:gmatch("[%w_-]+") do + accept[a] = true + end + query.arch = accept + end +end + +--- Store a search result (a rock or rockspec) in the results table. +-- @param results table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- @param name string: Package name. +-- @param version string: Package version. +-- @param arch string: Architecture of rock ("all", "src" or platform +-- identifier), "rockspec" or "installed" +-- @param repo string: Pathname of a local repository of URL of +-- rocks server. +local function store_result(results, name, version, arch, repo) + assert(type(results) == "table") + assert(type(name) == "string") + assert(type(version) == "string") + assert(type(arch) == "string") + assert(type(repo) == "string") + + if not results[name] then results[name] = {} end + if not results[name][version] then results[name][version] = {} end + table.insert(results[name][version], { + arch = arch, + repo = repo + }) +end + +--- Test the name field of a query. +-- If query has a boolean field exact_name set to false, +-- then substring match is performed; otherwise, exact string +-- comparison is done. +-- @param query table: A query in dependency table format. +-- @param name string: A package name. +-- @return boolean: True if names match, false otherwise. +local function match_name(query, name) + assert(type(query) == "table") + assert(type(name) == "string") + if query.exact_name == false then + return name:find(query.name, 0, true) and true or false + else + return name == query.name + end +end + +--- Store a match in a results table if version matches query. +-- Name, version, arch and repository path are stored in a given +-- table, optionally checking if version and arch (if given) match +-- a query. +-- @param results table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- @param repo string: URL or pathname of the repository. +-- @param name string: The name of the package being tested. +-- @param version string: The version of the package being tested. +-- @param arch string: The arch of the package being tested. +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- If the arch field is omitted, the local architecture (cfg.arch) +-- is used. The special value "any" is also recognized, returning all +-- matches regardless of architecture. +local function store_if_match(results, repo, name, version, arch, query) + if match_name(query, name) then + if query.arch[arch] or query.arch["any"] then + if deps.match_constraints(deps.parse_version(version), query.constraints) then + store_result(results, name, version, arch, repo) + end + end + end +end + +--- Perform search on a local repository. +-- @param repo string: The pathname of the local repository. +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- If the arch field is omitted, the local architecture (cfg.arch) +-- is used. The special value "any" is also recognized, returning all +-- matches regardless of architecture. +-- @param results table or nil: If given, this table will store the +-- results; if not given, a new table will be created. +-- @return table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- If a table was given in the "results" parameter, that is the result value. +function search.disk_search(repo, query, results) + assert(type(repo) == "string") + assert(type(query) == "table") + assert(type(results) == "table" or not results) + + local fs = require("luarocks.fs") + + if not results then + results = {} + end + query_arch_as_table(query) + + for name in fs.dir(repo) do + local pathname = dir.path(repo, name) + local rname, rversion, rarch = path.parse_name(name) + + if rname and (pathname:match(".rockspec$") or pathname:match(".rock$")) then + store_if_match(results, repo, rname, rversion, rarch, query) + elseif fs.is_dir(pathname) then + for version in fs.dir(pathname) do + if version:match("-%d+$") then + store_if_match(results, repo, name, version, "installed", query) + end + end + end + end + return results +end + +--- Perform search on a rocks server or tree. +-- @param results table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- @param repo string: The URL of a rocks server or +-- the pathname of a rocks tree (as returned by path.rocks_dir()). +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- If the arch field is omitted, the local architecture (cfg.arch) +-- is used. The special value "any" is also recognized, returning all +-- matches regardless of architecture. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @return true or, in case of errors, nil, an error message and an optional error code. +function search.manifest_search(results, repo, query, lua_version) + assert(type(results) == "table") + assert(type(repo) == "string") + assert(type(query) == "table") + + query_arch_as_table(query) + local manifest, err, errcode = manif.load_manifest(repo, lua_version) + if not manifest then + return nil, err, errcode + end + for name, versions in pairs(manifest.repository) do + for version, items in pairs(versions) do + for _, item in ipairs(items) do + store_if_match(results, repo, name, version, item.arch, query) + end + end + end + return true +end + +--- Search on all configured rocks servers. +-- @param query table: A dependency query. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @return table: A table where keys are package names +-- and values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +function search.search_repos(query, lua_version) + assert(type(query) == "table") + + local results = {} + for _, repo in ipairs(cfg.rocks_servers) do + if not cfg.disabled_servers[repo] then + if type(repo) == "string" then + repo = { repo } + end + for _, mirror in ipairs(repo) do + local protocol, pathname = dir.split_url(mirror) + if protocol == "file" then + mirror = pathname + end + local ok, err, errcode = search.manifest_search(results, mirror, query, lua_version) + if errcode == "network" then + cfg.disabled_servers[repo] = true + end + if ok then + break + else + util.warning("Failed searching manifest: "..err) + end + end + end + end + -- search through rocks in cfg.rocks_provided + local provided_repo = "provided by VM or rocks_provided" + for name, versions in pairs(cfg.rocks_provided) do + store_if_match(results, provided_repo, name, versions, "installed", query) + end + return results +end + +--- Prepare a query in dependency table format. +-- @param name string: The query name. +-- @param version string or nil: +-- @return table: A query in table format +function search.make_query(name, version) + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local query = { + name = name, + constraints = {} + } + if version then + table.insert(query.constraints, { op = "==", version = deps.parse_version(version)}) + end + return query +end + +--- Get the URL for the latest in a set of versions. +-- @param name string: The package name to be used in the URL. +-- @param versions table: An array of version informations, as stored +-- in search results tables. +-- @return string or nil: the URL for the latest version if one could +-- be picked, or nil. +local function pick_latest_version(name, versions) + assert(type(name) == "string") + assert(type(versions) == "table") + + local vtables = {} + for v, _ in pairs(versions) do + table.insert(vtables, deps.parse_version(v)) + end + table.sort(vtables) + local version = vtables[#vtables].string + local items = versions[version] + if items then + local pick = 1 + for i, item in ipairs(items) do + if (item.arch == 'src' and items[pick].arch == 'rockspec') + or (item.arch ~= 'src' and item.arch ~= 'rockspec') then + pick = i + end + end + return path.make_url(items[pick].repo, name, version, items[pick].arch) + end + return nil +end + +-- Find out which other Lua versions provide rock versions matching a query, +-- @param query table: A dependency query matching a single rock. +-- @return table: array of Lua versions supported, in "5.x" format. +local function supported_lua_versions(query) + local results = {} + + for lua_version in util.lua_versions() do + if lua_version ~= cfg.lua_version then + if search.search_repos(query, lua_version)[query.name] then + table.insert(results, lua_version) + end + end + end + + return results +end + +--- Attempt to get a single URL for a given search for a rock. +-- @param query table: A dependency query matching a single rock. +-- @return string or (nil, string): URL for latest matching version +-- of the rock if it was found, or nil followed by an error message. +function search.find_suitable_rock(query) + assert(type(query) == "table") + + local results = search.search_repos(query) + local first_rock = next(results) + if not first_rock then + if cfg.rocks_provided[query.name] == nil then + -- Check if constraints are satisfiable with other Lua versions. + local lua_versions = supported_lua_versions(query) + + if #lua_versions ~= 0 then + -- Build a nice message in "only Lua 5.x and 5.y but not 5.z." format + for i, lua_version in ipairs(lua_versions) do + lua_versions[i] = "Lua "..lua_version + end + + local versions_message = "only "..table.concat(lua_versions, " and ").. + " but not Lua "..cfg.lua_version.."." + + if #query.constraints == 0 then + return nil, query.name.." supports "..versions_message + elseif #query.constraints == 1 and query.constraints[1].op == "==" then + return nil, query.name.." "..query.constraints[1].version.string.." supports "..versions_message + else + return nil, "Matching "..query.name.." versions support "..versions_message + end + end + end + + return nil, "No results matching query were found." + elseif next(results, first_rock) then + -- Shouldn't happen as query must match only one package. + return nil, "Several rocks matched query." + elseif cfg.rocks_provided[query.name] ~= nil then + -- Do not install versions listed in cfg.rocks_provided. + return nil, "Rock "..query.name.." "..cfg.rocks_provided[query.name].. + " was found but it is provided by VM or 'rocks_provided' in the config file." + else + return pick_latest_version(query.name, results[first_rock]) + end +end + +--- Print a list of rocks/rockspecs on standard output. +-- @param results table: A table where keys are package names and versions +-- are tables matching version strings to an array of rocks servers. +-- @param porcelain boolean or nil: A flag to force machine-friendly output. +function search.print_results(results, porcelain) + assert(type(results) == "table") + assert(type(porcelain) == "boolean" or not porcelain) + + for package, versions in util.sortedpairs(results) do + if not porcelain then + util.printout(package) + end + for version, repos in util.sortedpairs(versions, deps.compare_versions) do + for _, repo in ipairs(repos) do + repo.repo = dir.normalize(repo.repo) + if porcelain then + util.printout(package, version, repo.arch, repo.repo) + else + util.printout(" "..version.." ("..repo.arch..") - "..repo.repo) + end + end + end + if not porcelain then + util.printout() + end + end +end + +--- Splits a list of search results into two lists, one for "source" results +-- to be used with the "build" command, and one for "binary" results to be +-- used with the "install" command. +-- @param results table: A search results table. +-- @return (table, table): Two tables, one for source and one for binary +-- results. +local function split_source_and_binary_results(results) + local sources, binaries = {}, {} + for name, versions in pairs(results) do + for version, repositories in pairs(versions) do + for _, repo in ipairs(repositories) do + local where = sources + if repo.arch == "all" or repo.arch == cfg.arch then + where = binaries + end + store_result(where, name, version, repo.arch, repo.repo) + end + end + end + return sources, binaries +end + +--- Given a name and optionally a version, try to find in the rocks +-- servers a single .src.rock or .rockspec file that satisfies +-- the request, and run the given function on it; or display to the +-- user possibilities if it couldn't narrow down a single match. +-- @param action function: A function that takes a .src.rock or +-- .rockspec URL as a parameter. +-- @param name string: A rock name +-- @param version string or nil: A version number may also be given. +-- @return The result of the action function, or nil and an error message. +function search.act_on_src_or_rockspec(action, name, version, ...) + assert(type(action) == "function") + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local query = search.make_query(name, version) + query.arch = "src|rockspec" + local url, err = search.find_suitable_rock(query) + if not url then + return nil, "Could not find a result named "..name..(version and " "..version or "")..": "..err + end + return action(url, ...) +end + +function search.pick_installed_rock(name, version, given_tree) + local results = {} + local query = search.make_query(name, version) + query.exact_name = true + local tree_map = {} + local trees = cfg.rocks_trees + if given_tree then + trees = { given_tree } + end + for _, tree in ipairs(trees) do + local rocks_dir = path.rocks_dir(tree) + tree_map[rocks_dir] = tree + search.manifest_search(results, rocks_dir, query) + end + + if not next(results) then -- + return nil,"cannot find package "..name.." "..(version or "").."\nUse 'list' to find installed rocks." + end + + version = nil + local repo_url + local package, versions = util.sortedpairs(results)() + --question: what do we do about multiple versions? This should + --give us the latest version on the last repo (which is usually the global one) + for vs, repositories in util.sortedpairs(versions, deps.compare_versions) do + if not version then version = vs end + for _, rp in ipairs(repositories) do repo_url = rp.repo end + end + + local repo = tree_map[repo_url] + return name, version, repo, repo_url +end + +--- Driver function for "search" command. +-- @param name string: A substring of a rock name to search. +-- @param version string or nil: a version may also be passed. +-- @return boolean or (nil, string): True if build was successful; nil and an +-- error message otherwise. +function search.command(flags, name, version) + if flags["all"] then + name, version = "", nil + end + + if type(name) ~= "string" and not flags["all"] then + return nil, "Enter name and version or use --all. "..util.see_help("search") + end + + local query = search.make_query(name:lower(), version) + query.exact_name = false + local results, err = search.search_repos(query) + local porcelain = flags["porcelain"] + util.title("Search results:", porcelain, "=") + local sources, binaries = split_source_and_binary_results(results) + if next(sources) and not flags["binary"] then + util.title("Rockspecs and source rocks:", porcelain) + search.print_results(sources, porcelain) + end + if next(binaries) and not flags["source"] then + util.title("Binary and pure-Lua rocks:", porcelain) + search.print_results(binaries, porcelain) + end + return true +end + +return search diff --git a/src/luarocks/cmd/show.lua b/src/luarocks/cmd/show.lua new file mode 100644 index 00000000..1ff81e08 --- /dev/null +++ b/src/luarocks/cmd/show.lua @@ -0,0 +1,158 @@ +--- Module implementing the LuaRocks "show" command. +-- Shows information about an installed rock. +local show = {} + +local search = require("luarocks.search") +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local path = require("luarocks.path") +local deps = require("luarocks.deps") +local fetch = require("luarocks.fetch") +local manif = require("luarocks.manif") + +show.help_summary = "Show information about an installed rock." + +show.help = [[ + is an existing package name. +Without any flags, show all module information. +With these flags, return only the desired information: + +--home home page of project +--modules all modules provided by this package as used by require() +--deps packages this package depends on +--rockspec the full path of the rockspec file +--mversion the package version +--rock-tree local tree where rock is installed +--rock-dir data directory of the installed rock +]] + +local function keys_as_string(t, sep) + local keys = util.keys(t) + table.sort(keys) + return table.concat(keys, sep or " ") +end + +local function word_wrap(line) + local width = tonumber(os.getenv("COLUMNS")) or 80 + if width > 80 then width = 80 end + if #line > width then + local brk = width + while brk > 0 and line:sub(brk, brk) ~= " " do + brk = brk - 1 + end + if brk > 0 then + return line:sub(1, brk-1) .. "\n" .. word_wrap(line:sub(brk+1)) + end + end + return line +end + +local function format_text(text) + text = text:gsub("^%s*",""):gsub("%s$", ""):gsub("\n[ \t]+","\n"):gsub("([^\n])\n([^\n])","%1 %2") + local paragraphs = util.split_string(text, "\n\n") + for n, line in ipairs(paragraphs) do + paragraphs[n] = word_wrap(line) + end + return (table.concat(paragraphs, "\n\n"):gsub("%s$", "")) +end + +local function installed_rock_label(name, tree) + local installed, version + if cfg.rocks_provided[name] then + installed, version = true, cfg.rocks_provided[name] + else + installed, version = search.pick_installed_rock(name, nil, tree) + end + return installed and "(using "..version..")" or "(missing)" +end + +--- Driver function for "show" command. +-- @param name or nil: an existing package name. +-- @param version string or nil: a version may also be passed. +-- @return boolean: True if succeeded, nil on errors. +function show.command(flags, name, version) + if not name then + return nil, "Argument missing. "..util.see_help("show") + end + + local repo, repo_url + name, version, repo, repo_url = search.pick_installed_rock(name:lower(), version, flags["tree"]) + if not name then + return nil, version + end + + local directory = path.install_dir(name,version,repo) + local rockspec_file = path.rockspec_file(name, version, repo) + local rockspec, err = fetch.load_local_rockspec(rockspec_file) + if not rockspec then return nil,err end + + local descript = rockspec.description or {} + local manifest, err = manif.load_manifest(repo_url) + if not manifest then return nil,err end + local minfo = manifest.repository[name][version][1] + + if flags["rock-tree"] then util.printout(path.rocks_tree_to_string(repo)) + elseif flags["rock-dir"] then util.printout(directory) + elseif flags["home"] then util.printout(descript.homepage) + elseif flags["issues"] then util.printout(descript.issues_url) + elseif flags["labels"] then util.printout(descript.labels and table.concat(descript.labels, "\n")) + elseif flags["modules"] then util.printout(keys_as_string(minfo.modules, "\n")) + elseif flags["deps"] then util.printout(keys_as_string(minfo.dependencies)) + elseif flags["rockspec"] then util.printout(rockspec_file) + elseif flags["mversion"] then util.printout(version) + else + util.printout() + util.printout(rockspec.package.." "..rockspec.version.." - "..(descript.summary or "")) + util.printout() + if descript.detailed then + util.printout(format_text(descript.detailed)) + util.printout() + end + if descript.license then + util.printout("License: ", descript.license) + end + if descript.homepage then + util.printout("Homepage: ", descript.homepage) + end + if descript.issues_url then + util.printout("Issues: ", descript.issues_url) + end + if descript.labels then + util.printout("Labels: ", table.concat(descript.labels, ", ")) + end + util.printout("Installed in: ", path.rocks_tree_to_string(repo)) + if next(minfo.modules) then + util.printout() + util.printout("Modules:") + for mod, filename in util.sortedpairs(minfo.modules) do + util.printout("\t"..mod.." ("..path.which(mod, filename, name, version, repo, manifest)..")") + end + end + local direct_deps = {} + if #rockspec.dependencies > 0 then + util.printout() + util.printout("Depends on:") + for _, dep in ipairs(rockspec.dependencies) do + direct_deps[dep.name] = true + util.printout("\t"..deps.show_dep(dep).." "..installed_rock_label(dep.name, flags["tree"])) + end + end + local has_indirect_deps + for dep_name in util.sortedpairs(minfo.dependencies) do + if not direct_deps[dep_name] then + if not has_indirect_deps then + util.printout() + util.printout("Indirectly pulling:") + has_indirect_deps = true + end + + util.printout("\t"..dep_name.." "..installed_rock_label(dep_name, flags["tree"])) + end + end + util.printout() + end + return true +end + + +return show diff --git a/src/luarocks/cmd/unpack.lua b/src/luarocks/cmd/unpack.lua new file mode 100644 index 00000000..c50701b0 --- /dev/null +++ b/src/luarocks/cmd/unpack.lua @@ -0,0 +1,164 @@ + +--- Module implementing the LuaRocks "unpack" command. +-- Unpack the contents of a rock. +local unpack = {} + +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local util = require("luarocks.util") +local build = require("luarocks.build") +local dir = require("luarocks.dir") +local cfg = require("luarocks.core.cfg") + +unpack.help_summary = "Unpack the contents of a rock." +unpack.help_arguments = "[--force] {| []}" +unpack.help = [[ +Unpacks the contents of a rock in a newly created directory. +Argument may be a rock file, or the name of a rock in a rocks server. +In the latter case, the app version may be given as a second argument. + +--force Unpack files even if the output directory already exists. +]] + +--- Load a rockspec file to the given directory, fetches the source +-- files specified in the rockspec, and unpack them inside the directory. +-- @param rockspec_file string: The URL for a rockspec file. +-- @param dir_name string: The directory where to store and unpack files. +-- @return table or (nil, string): the loaded rockspec table or +-- nil and an error message. +local function unpack_rockspec(rockspec_file, dir_name) + assert(type(rockspec_file) == "string") + assert(type(dir_name) == "string") + + local rockspec, err = fetch.load_rockspec(rockspec_file) + if not rockspec then + return nil, "Failed loading rockspec "..rockspec_file..": "..err + end + local ok, err = fs.change_dir(dir_name) + if not ok then return nil, err end + local ok, sources_dir = fetch.fetch_sources(rockspec, true, ".") + if not ok then + return nil, sources_dir + end + ok, err = fs.change_dir(sources_dir) + if not ok then return nil, err end + ok, err = build.apply_patches(rockspec) + fs.pop_dir() + if not ok then return nil, err end + return rockspec +end + +--- Load a .rock file to the given directory and unpack it inside it. +-- @param rock_file string: The URL for a .rock file. +-- @param dir_name string: The directory where to unpack. +-- @param kind string: the kind of rock file, as in the second-level +-- extension in the rock filename (eg. "src", "all", "linux-x86") +-- @return table or (nil, string): the loaded rockspec table or +-- nil and an error message. +local function unpack_rock(rock_file, dir_name, kind) + assert(type(rock_file) == "string") + assert(type(dir_name) == "string") + + local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, dir_name) + if not ok then + return nil, "Failed unzipping rock "..rock_file, errcode + end + ok, err = fs.change_dir(dir_name) + if not ok then return nil, err end + local rockspec_file = dir_name..".rockspec" + local rockspec, err = fetch.load_rockspec(rockspec_file) + if not rockspec then + return nil, "Failed loading rockspec "..rockspec_file..": "..err + end + if kind == "src" then + if rockspec.source.file then + local ok, err = fs.unpack_archive(rockspec.source.file) + if not ok then + return nil, err + end + ok, err = fs.change_dir(rockspec.source.dir) + if not ok then return nil, err end + ok, err = build.apply_patches(rockspec) + fs.pop_dir() + if not ok then return nil, err end + end + end + return rockspec +end + +--- Create a directory and perform the necessary actions so that +-- the sources for the rock and its rockspec are unpacked inside it, +-- laid out properly so that the 'make' command is able to build the module. +-- @param file string: A rockspec or .rock URL. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +local function run_unpacker(file, force) + assert(type(file) == "string") + + local base_name = dir.base_name(file) + local dir_name, kind, extension = base_name:match("(.*)%.([^.]+)%.(rock)$") + if not extension then + dir_name, extension = base_name:match("(.*)%.(rockspec)$") + kind = "rockspec" + end + if not extension then + return nil, file.." does not seem to be a valid filename." + end + + local exists = fs.exists(dir_name) + if exists and not force then + return nil, "Directory "..dir_name.." already exists." + end + if not exists then + local ok, err = fs.make_dir(dir_name) + if not ok then return nil, err end + end + local rollback = util.schedule_function(fs.delete, fs.absolute_name(dir_name)) + + local rockspec, err + if extension == "rock" then + rockspec, err = unpack_rock(file, dir_name, kind) + elseif extension == "rockspec" then + rockspec, err = unpack_rockspec(file, dir_name) + end + if not rockspec then + return nil, err + end + if kind == "src" or kind == "rockspec" then + if rockspec.source.dir ~= "." then + local ok = fs.copy(rockspec.local_filename, rockspec.source.dir, cfg.perm_read) + if not ok then + return nil, "Failed copying unpacked rockspec into unpacked source directory." + end + end + util.printout() + util.printout("Done. You may now enter directory ") + util.printout(dir.path(dir_name, rockspec.source.dir)) + util.printout("and type 'luarocks make' to build.") + end + util.remove_scheduled_function(rollback) + return true +end + +--- Driver function for the "unpack" command. +-- @param name string: may be a rock filename, for unpacking a +-- rock file or the name of a rock to be fetched and unpacked. +-- @param version string or nil: if the name of a package is given, a +-- version may also be passed. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +function unpack.command(flags, name, version) + assert(type(version) == "string" or not version) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("unpack") + end + + if name:match(".*%.rock") or name:match(".*%.rockspec") then + return run_unpacker(name, flags["force"]) + else + local search = require("luarocks.search") + return search.act_on_src_or_rockspec(run_unpacker, name:lower(), version) + end +end + +return unpack diff --git a/src/luarocks/cmd/upload.lua b/src/luarocks/cmd/upload.lua new file mode 100644 index 00000000..baee47ab --- /dev/null +++ b/src/luarocks/cmd/upload.lua @@ -0,0 +1,94 @@ + +local upload = {} + +local util = require("luarocks.util") +local fetch = require("luarocks.fetch") +local pack = require("luarocks.pack") +local cfg = require("luarocks.core.cfg") +local Api = require("luarocks.upload.api") + +upload.help_summary = "Upload a rockspec to the public rocks repository." +upload.help_arguments = "[--skip-pack] [--api-key=] [--force] " +upload.help = [[ + Pack a source rock file (.src.rock extension), + upload rockspec and source rock to server. +--skip-pack Do not pack and send source rock. +--api-key= Give it an API key. It will be stored for subsequent uses. +--force Replace existing rockspec if the same revision of + a module already exists. This should be used only + in case of upload mistakes: when updating a rockspec, + increment the revision number instead. +]] + +function upload.command(flags, fname) + if not fname then + return nil, "Missing rockspec. "..util.see_help("upload") + end + + local api, err = Api.new(flags) + if not api then + return nil, err + end + if cfg.verbose then + api.debug = true + end + + local rockspec, err, errcode = fetch.load_rockspec(fname) + if err then + return nil, err, errcode + end + + util.printout("Sending " .. tostring(fname) .. " ...") + local res, err = api:method("check_rockspec", { + package = rockspec.package, + version = rockspec.version + }) + if not res then return nil, err end + + if not res.module then + util.printout("Will create new module (" .. tostring(rockspec.package) .. ")") + end + if res.version and not flags["force"] then + return nil, "Revision "..rockspec.version.." already exists on the server. "..util.see_help("upload") + end + + local rock_fname + if not flags["skip-pack"] and not rockspec.version:match("^scm") then + util.printout("Packing " .. tostring(rockspec.package)) + rock_fname, err = pack.pack_source_rock(fname) + if not rock_fname then + return nil, err + end + end + + local multipart = require("luarocks.upload.multipart") + + res, err = api:method("upload", nil, { + rockspec_file = multipart.new_file(fname) + }) + if not res then return nil, err end + + if res.is_new and #res.manifests == 0 then + util.printerr("Warning: module not added to root manifest due to name taken.") + end + + local module_url = res.module_url + + if rock_fname then + if (not res.version) or (not res.version.id) then + return nil, "Invalid response from server." + end + util.printout(("Sending " .. tostring(rock_fname) .. " ...")) + res, err = api:method("upload_rock/" .. ("%d"):format(res.version.id), nil, { + rock_file = multipart.new_file(rock_fname) + }) + if not res then return nil, err end + end + + util.printout() + util.printout("Done: " .. tostring(module_url)) + util.printout() + return true +end + +return upload diff --git a/src/luarocks/cmd/write_rockspec.lua b/src/luarocks/cmd/write_rockspec.lua new file mode 100644 index 00000000..be563eaa --- /dev/null +++ b/src/luarocks/cmd/write_rockspec.lua @@ -0,0 +1,376 @@ + +local write_rockspec = {} + +local cfg = require("luarocks.core.cfg") +local dir = require("luarocks.dir") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local path = require("luarocks.path") +local persist = require("luarocks.persist") +local type_check = require("luarocks.type_check") +local util = require("luarocks.util") +local deps = require("luarocks.deps") + +write_rockspec.help_summary = "Write a template for a rockspec file." +write_rockspec.help_arguments = "[--output= ...] [] [] [|]" +write_rockspec.help = [[ +This command writes an initial version of a rockspec file, +based on a name, a version, and a location (an URL or a local path). +If only two arguments are given, the first one is considered the name and the +second one is the location. +If only one argument is given, it must be the location. +If no arguments are given, current directory is used as location. +LuaRocks will attempt to infer name and version if not given, +using 'scm' as default version. + +Note that the generated file is a _starting point_ for writing a +rockspec, and is not guaranteed to be complete or correct. + +--output= Write the rockspec with the given filename. + If not given, a file is written in the current + directory with a filename based on given name and version. +--license="" A license string, such as "MIT/X11" or "GNU GPL v3". +--summary="" A short one-line description summary. +--detailed="" A longer description string. +--homepage= Project homepage. +--lua-version= Supported Lua versions. Accepted values are "5.1", "5.2", + "5.3", "5.1,5.2", "5.2,5.3", or "5.1,5.2,5.3". +--rockspec-format= Rockspec format version, such as "1.0" or "1.1". +--tag= Tag to use. Will attempt to extract version number from it. +--lib=[,] A comma-separated list of libraries that C files need to + link to. +]] + +local function open_file(name) + return io.open(dir.path(fs.current_dir(), name), "r") +end + +local function get_url(rockspec) + local file, temp_dir, err_code, err_file, err_temp_dir = fetch.fetch_sources(rockspec, false) + if err_code == "source.dir" then + file, temp_dir = err_file, err_temp_dir + elseif not file then + util.warning("Could not fetch sources - "..temp_dir) + return false + end + util.printout("File successfully downloaded. Making checksum and checking base dir...") + if fetch.is_basic_protocol(rockspec.source.protocol) then + rockspec.source.md5 = fs.get_md5(file) + end + local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, rockspec.source.url) + return true, found_dir or inferred_dir, temp_dir +end + +local function configure_lua_version(rockspec, luaver) + if luaver == "5.1" then + table.insert(rockspec.dependencies, "lua ~> 5.1") + elseif luaver == "5.2" then + table.insert(rockspec.dependencies, "lua ~> 5.2") + elseif luaver == "5.3" then + table.insert(rockspec.dependencies, "lua ~> 5.3") + elseif luaver == "5.1,5.2" then + table.insert(rockspec.dependencies, "lua >= 5.1, < 5.3") + elseif luaver == "5.2,5.3" then + table.insert(rockspec.dependencies, "lua >= 5.2, < 5.4") + elseif luaver == "5.1,5.2,5.3" then + table.insert(rockspec.dependencies, "lua >= 5.1, < 5.4") + else + util.warning("Please specify supported Lua version with --lua-version=. "..util.see_help("write_rockspec")) + end +end + +local function detect_description() + local fd = open_file("README.md") or open_file("README") + if not fd then return end + local data = fd:read("*a") + fd:close() + local paragraph = data:match("\n\n([^%[].-)\n\n") + if not paragraph then paragraph = data:match("\n\n(.*)") end + local summary, detailed + if paragraph then + detailed = paragraph + + if #paragraph < 80 then + summary = paragraph:gsub("\n", "") + else + summary = paragraph:gsub("\n", " "):match("([^.]*%.) ") + end + end + return summary, detailed +end + +local function detect_mit_license(data) + local strip_copyright = (data:gsub("Copyright [^\n]*\n", "")) + local sum = 0 + for i = 1, #strip_copyright do + local num = string.byte(strip_copyright:sub(i,i)) + if num > 32 and num <= 128 then + sum = sum + num + end + end + return sum == 78656 +end + +local simple_scm_protocols = { + git = true, ["git+http"] = true, ["git+https"] = true, + hg = true, ["hg+http"] = true, ["hg+https"] = true +} + +local function detect_url_from_command(program, args, directory) + local command = fs.Q(cfg.variables[program:upper()]).. " "..args + local pipe = io.popen(fs.command_at(directory, fs.quiet_stderr(command))) + if not pipe then return nil end + local url = pipe:read("*a"):match("^([^\r\n]+)") + pipe:close() + if not url then return nil end + if not util.starts_with(url, program.."://") then + url = program.."+"..url + end + + if simple_scm_protocols[dir.split_url(url)] then + return url + end +end + +local function detect_scm_url(directory) + return detect_url_from_command("git", "config --get remote.origin.url", directory) or + detect_url_from_command("hg", "paths default", directory) +end + +local function show_license(rockspec) + local fd = open_file("COPYING") or open_file("LICENSE") or open_file("MIT-LICENSE.txt") + if not fd then return nil end + local data = fd:read("*a") + fd:close() + local is_mit = detect_mit_license(data) + util.title("License for "..rockspec.package..":") + util.printout(data) + util.printout() + return is_mit +end + +local function get_cmod_name(file) + local fd = open_file(file) + if not fd then return nil end + local data = fd:read("*a") + fd:close() + return (data:match("int%s+luaopen_([a-zA-Z0-9_]+)")) +end + +local luamod_blacklist = { + test = true, + tests = true, +} + +local function fill_as_builtin(rockspec, libs) + rockspec.build.type = "builtin" + rockspec.build.modules = {} + local prefix = "" + + for _, parent in ipairs({"src", "lua"}) do + if fs.is_dir(parent) then + fs.change_dir(parent) + prefix = parent.."/" + break + end + end + + local incdirs, libdirs + if libs then + incdirs, libdirs = {}, {} + for _, lib in ipairs(libs) do + local upper = lib:upper() + incdirs[#incdirs+1] = "$("..upper.."_INCDIR)" + libdirs[#libdirs+1] = "$("..upper.."_LIBDIR)" + end + end + + for _, file in ipairs(fs.find()) do + local luamod = file:match("(.*)%.lua$") + if luamod and not luamod_blacklist[luamod] then + rockspec.build.modules[path.path_to_module(file)] = prefix..file + else + local cmod = file:match("(.*)%.c$") + if cmod then + local modname = get_cmod_name(file) or path.path_to_module(file:gsub("%.c$", ".lua")) + rockspec.build.modules[modname] = { + sources = prefix..file, + libraries = libs, + incdirs = incdirs, + libdirs = libdirs, + } + end + end + end + + for _, directory in ipairs({ "doc", "docs", "samples", "tests" }) do + if fs.is_dir(directory) then + if not rockspec.build.copy_directories then + rockspec.build.copy_directories = {} + end + table.insert(rockspec.build.copy_directories, directory) + end + end + + if prefix ~= "" then + fs.pop_dir() + end +end + +local function rockspec_cleanup(rockspec) + rockspec.source.file = nil + rockspec.source.protocol = nil + rockspec.variables = nil + rockspec.name = nil + rockspec.format_is_at_least = nil +end + +function write_rockspec.command(flags, name, version, url_or_dir) + if not name then + url_or_dir = "." + elseif not version then + url_or_dir = name + name = nil + elseif not url_or_dir then + url_or_dir = version + version = nil + end + + if flags["tag"] then + if not version then + version = flags["tag"]:gsub("^v", "") + end + end + + local protocol, pathname = dir.split_url(url_or_dir) + if protocol == "file" then + if pathname == "." then + name = name or dir.base_name(fs.current_dir()) + end + elseif fetch.is_basic_protocol(protocol) then + local filename = dir.base_name(url_or_dir) + local newname, newversion = filename:match("(.*)-([^-]+)") + if newname then + name = name or newname + version = version or newversion:gsub("%.[a-z]+$", ""):gsub("%.tar$", "") + end + else + name = name or dir.base_name(url_or_dir):gsub("%.[^.]+$", "") + end + + if not name then + return nil, "Could not infer rock name. "..util.see_help("write_rockspec") + end + version = version or "scm" + + local filename = flags["output"] or dir.path(fs.current_dir(), name:lower().."-"..version.."-1.rockspec") + + local rockspec = { + rockspec_format = flags["rockspec-format"], + package = name, + name = name:lower(), + version = version.."-1", + source = { + url = "*** please add URL for source tarball, zip or repository here ***", + tag = flags["tag"], + }, + description = { + summary = flags["summary"] or "*** please specify description summary ***", + detailed = flags["detailed"] or "*** please enter a detailed description ***", + homepage = flags["homepage"] or "*** please enter a project homepage ***", + license = flags["license"] or "*** please specify a license ***", + }, + dependencies = {}, + build = {}, + } + path.configure_paths(rockspec) + rockspec.source.protocol = protocol + rockspec.format_is_at_least = deps.format_is_at_least + + configure_lua_version(rockspec, flags["lua-version"]) + + local local_dir = url_or_dir + + if url_or_dir:match("://") then + rockspec.source.url = url_or_dir + rockspec.source.file = dir.base_name(url_or_dir) + rockspec.source.dir = "dummy" + if not fetch.is_basic_protocol(rockspec.source.protocol) then + if version ~= "scm" then + rockspec.source.tag = flags["tag"] or "v" .. version + end + end + rockspec.source.dir = nil + local ok, base_dir, temp_dir = get_url(rockspec) + if ok then + if base_dir ~= dir.base_name(url_or_dir) then + rockspec.source.dir = base_dir + end + end + if base_dir then + local_dir = dir.path(temp_dir, base_dir) + else + local_dir = nil + end + else + rockspec.source.url = detect_scm_url(local_dir) or rockspec.source.url + end + + if not local_dir then + local_dir = "." + end + + if not flags["homepage"] then + local url_protocol, url_path = dir.split_url(rockspec.source.url) + + if simple_scm_protocols[url_protocol] then + for _, domain in ipairs({"github.com", "bitbucket.org", "gitlab.com"}) do + if util.starts_with(url_path, domain) then + rockspec.description.homepage = "https://"..url_path:gsub("%.git$", "") + break + end + end + end + end + + local libs = nil + if flags["lib"] then + libs = {} + rockspec.external_dependencies = {} + for lib in flags["lib"]:gmatch("([^,]+)") do + table.insert(libs, lib) + rockspec.external_dependencies[lib:upper()] = { + library = lib + } + end + end + + local ok, err = fs.change_dir(local_dir) + if not ok then return nil, "Failed reaching files from project - error entering directory "..local_dir end + + if (not flags["summary"]) or (not flags["detailed"]) then + local summary, detailed = detect_description() + rockspec.description.summary = flags["summary"] or summary + rockspec.description.detailed = flags["detailed"] or detailed + end + + local is_mit = show_license(rockspec) + + if is_mit and not flags["license"] then + rockspec.description.license = "MIT" + end + + fill_as_builtin(rockspec, libs) + + rockspec_cleanup(rockspec) + + persist.save_from_table(filename, rockspec, type_check.rockspec_order) + + util.printout() + util.printout("Wrote template at "..filename.." -- you should now edit and finish it.") + util.printout() + + return true +end + +return write_rockspec diff --git a/src/luarocks/config_cmd.lua b/src/luarocks/config_cmd.lua deleted file mode 100644 index b68f7898..00000000 --- a/src/luarocks/config_cmd.lua +++ /dev/null @@ -1,71 +0,0 @@ ---- Module implementing the LuaRocks "config" command. --- Queries information about the LuaRocks configuration. -local config_cmd = {} - -local cfg = require("luarocks.core.cfg") -local util = require("luarocks.util") -local dir = require("luarocks.dir") - -config_cmd.help_summary = "Query information about the LuaRocks configuration." -config_cmd.help_arguments = "" -config_cmd.help = [[ ---lua-incdir Path to Lua header files. - ---lua-libdir Path to Lua library files. - ---lua-ver Lua version (in major.minor format). e.g. 5.1 - ---system-config Location of the system config file. - ---user-config Location of the user config file. - ---rock-trees Rocks trees in use. First the user tree, then the system tree. -]] - -local function config_file(conf) - print(dir.normalize(conf.file)) - if conf.ok then - return true - else - return nil, "file not found" - end -end - ---- Driver function for "config" command. --- @return boolean: True if succeeded, nil on errors. -function config_cmd.command(flags) - if flags["lua-incdir"] then - print(cfg.variables.LUA_INCDIR) - return true - end - if flags["lua-libdir"] then - print(cfg.variables.LUA_LIBDIR) - return true - end - if flags["lua-ver"] then - print(cfg.lua_version) - return true - end - local conf = cfg.which_config() - if flags["system-config"] then - return config_file(conf.system) - end - if flags["user-config"] then - return config_file(conf.user) - end - if flags["rock-trees"] then - for _, tree in ipairs(cfg.rocks_trees) do - if type(tree) == "string" then - util.printout(dir.normalize(tree)) - else - local name = tree.name and "\t"..tree.name or "" - util.printout(dir.normalize(tree.root)..name) - end - end - return true - end - - return nil, "Please provide a flag for querying configuration values. "..util.see_help("config") -end - -return config_cmd diff --git a/src/luarocks/doc.lua b/src/luarocks/doc.lua deleted file mode 100644 index 5d521276..00000000 --- a/src/luarocks/doc.lua +++ /dev/null @@ -1,155 +0,0 @@ - ---- Module implementing the LuaRocks "doc" command. --- Shows documentation for an installed rock. -local doc = {} - -local util = require("luarocks.util") -local search = require("luarocks.search") -local path = require("luarocks.path") -local dir = require("luarocks.dir") -local fetch = require("luarocks.fetch") -local fs = require("luarocks.fs") -local download = require("luarocks.download") - -doc.help_summary = "Show documentation for an installed rock." - -doc.help = [[ - is an existing package name. -Without any flags, tries to load the documentation -using a series of heuristics. -With these flags, return only the desired information: - ---home Open the home page of project. ---list List documentation files only. - -For more information about a rock, see the 'show' command. -]] - -local function show_homepage(homepage, name, version) - if not homepage then - return nil, "No 'homepage' field in rockspec for "..name.." "..version - end - util.printout("Opening "..homepage.." ...") - fs.browser(homepage) - return true -end - -local function try_to_open_homepage(name, version) - local temp_dir, err = fs.make_temp_dir("doc-"..name.."-"..(version or "")) - if not temp_dir then - return nil, "Failed creating temporary directory: "..err - end - util.schedule_function(fs.delete, temp_dir) - local ok, err = fs.change_dir(temp_dir) - if not ok then return nil, err end - local filename, err = download.download("rockspec", name, version) - if not filename then return nil, err end - local rockspec, err = fetch.load_local_rockspec(filename) - if not rockspec then return nil, err end - fs.pop_dir() - local descript = rockspec.description or {} - if not descript.homepage then return nil, "No homepage defined for "..name end - return show_homepage(descript.homepage, name, version) -end - ---- Driver function for "doc" command. --- @param name or nil: an existing package name. --- @param version string or nil: a version may also be passed. --- @return boolean: True if succeeded, nil on errors. -function doc.command(flags, name, version) - if not name then - return nil, "Argument missing. "..util.see_help("doc") - end - - name = name:lower() - - local iname, iversion, repo = search.pick_installed_rock(name, version, flags["tree"]) - if not iname then - util.printout(name..(version and " "..version or "").." is not installed. Looking for it in the rocks servers...") - return try_to_open_homepage(name, version) - end - name, version = iname, iversion - - local rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version, repo)) - if not rockspec then return nil,err end - local descript = rockspec.description or {} - - if flags["home"] then - return show_homepage(descript.homepage, name, version) - end - - local directory = path.install_dir(name,version,repo) - - local docdir - local directories = { "doc", "docs" } - for _, d in ipairs(directories) do - local dirname = dir.path(directory, d) - if fs.is_dir(dirname) then - docdir = dirname - break - end - end - if not docdir then - if descript.homepage and not flags["list"] then - util.printout("Local documentation directory not found -- opening "..descript.homepage.." ...") - fs.browser(descript.homepage) - return true - end - return nil, "Documentation directory not found for "..name.." "..version - end - - docdir = dir.normalize(docdir):gsub("/+", "/") - local files = fs.find(docdir) - local htmlpatt = "%.html?$" - local extensions = { htmlpatt, "%.md$", "%.txt$", "%.textile$", "" } - local basenames = { "index", "readme", "manual" } - - local porcelain = flags["porcelain"] - if #files > 0 then - util.title("Documentation files for "..name.." "..version, porcelain) - if porcelain then - for _, file in ipairs(files) do - util.printout(docdir.."/"..file) - end - else - util.printout(docdir.."/") - for _, file in ipairs(files) do - util.printout("\t"..file) - end - end - end - - if flags["list"] then - return true - end - - for _, extension in ipairs(extensions) do - for _, basename in ipairs(basenames) do - local filename = basename..extension - local found - for _, file in ipairs(files) do - if file:lower():match(filename) and ((not found) or #file < #found) then - found = file - end - end - if found then - local pathname = dir.path(docdir, found) - util.printout() - util.printout("Opening "..pathname.." ...") - util.printout() - local ok = fs.browser(pathname) - if not ok and not pathname:match(htmlpatt) then - local fd = io.open(pathname, "r") - util.printout(fd:read("*a")) - fd:close() - end - return true - end - end - end - - return true -end - - -return doc diff --git a/src/luarocks/download.lua b/src/luarocks/download.lua deleted file mode 100644 index 557d1b65..00000000 --- a/src/luarocks/download.lua +++ /dev/null @@ -1,107 +0,0 @@ - ---- Module implementing the luarocks "download" command. --- Download a rock from the repository. -local download = {} - -local util = require("luarocks.util") -local path = require("luarocks.path") -local fetch = require("luarocks.fetch") -local search = require("luarocks.search") -local fs = require("luarocks.fs") -local dir = require("luarocks.dir") -local cfg = require("luarocks.core.cfg") - -download.help_summary = "Download a specific rock file from a rocks server." -download.help_arguments = "[--all] [--arch= | --source | --rockspec] [ []]" - -download.help = [[ ---all Download all files if there are multiple matches. ---source Download .src.rock if available. ---rockspec Download .rockspec if available. ---arch= Download rock for a specific architecture. -]] - -local function get_file(filename) - local protocol, pathname = dir.split_url(filename) - if protocol == "file" then - local ok, err = fs.copy(pathname, fs.current_dir(), cfg.perm_read) - if ok then - return pathname - else - return nil, err - end - else - return fetch.fetch_url(filename) - end -end - -function download.download(arch, name, version, all) - local query = search.make_query(name, version) - if arch then query.arch = arch end - local search_err - - if all then - if name == "" then query.exact_name = false end - local results = search.search_repos(query) - local has_result = false - local all_ok = true - local any_err = "" - for name, result in pairs(results) do - for version, items in pairs(result) do - for _, item in ipairs(items) do - -- Ignore provided rocks. - if item.arch ~= "installed" then - has_result = true - local filename = path.make_url(item.repo, name, version, item.arch) - local ok, err = get_file(filename) - if not ok then - all_ok = false - any_err = any_err .. "\n" .. err - end - end - end - end - end - - if has_result then - return all_ok, any_err - end - else - local url - url, search_err = search.find_suitable_rock(query) - if url then - return get_file(url) - end - end - return nil, "Could not find a result named "..name..(version and " "..version or "").. - (search_err and ": "..search_err or ".") -end - ---- Driver function for the "download" command. --- @param name string: a rock name. --- @param version string or nil: if the name of a package is given, a --- version may also be passed. --- @return boolean or (nil, string): true if successful or nil followed --- by an error message. -function download.command(flags, name, version) - assert(type(version) == "string" or not version) - if type(name) ~= "string" and not flags["all"] then - return nil, "Argument missing. "..util.see_help("download") - end - if not name then name, version = "", "" end - - local arch - - if flags["source"] then - arch = "src" - elseif flags["rockspec"] then - arch = "rockspec" - elseif flags["arch"] then - arch = flags["arch"] - end - - local dl, err = download.download(arch, name:lower(), version, flags["all"]) - return dl and true, err -end - -return download diff --git a/src/luarocks/help.lua b/src/luarocks/help.lua deleted file mode 100644 index d27c3a50..00000000 --- a/src/luarocks/help.lua +++ /dev/null @@ -1,117 +0,0 @@ - ---- Module implementing the LuaRocks "help" command. --- This is a generic help display module, which --- uses a global table called "commands" to find commands --- to show help for; each command should be represented by a --- table containing "help" and "help_summary" fields. -local help = {} - -local util = require("luarocks.util") -local cfg = require("luarocks.core.cfg") -local dir = require("luarocks.dir") - -local program = util.this_program("luarocks") - -help.help_summary = "Help on commands. Type '"..program.." help ' for more." - -help.help_arguments = "[]" -help.help = [[ - is the command to show help for. -]] - -local function print_banner() - util.printout("\nLuaRocks "..cfg.program_version..", a module deployment system for Lua") -end - -local function print_section(section) - util.printout("\n"..section) -end - -local function get_status(status) - if status then - return "ok" - else - return "not found" - end -end - ---- Driver function for the "help" command. --- @param command string or nil: command to show help for; if not --- given, help summaries for all commands are shown. --- @return boolean or (nil, string): true if there were no errors --- or nil and an error message if an invalid command was requested. -function help.command(flags, command) - if not command then - local conf = cfg.which_config() - print_banner() - print_section("NAME") - util.printout("\t"..program..[[ - ]]..program_description) - print_section("SYNOPSIS") - util.printout("\t"..program..[[ [--from= | --only-from=] [--to=] [VAR=VALUE]... [] ]]) - print_section("GENERAL OPTIONS") - util.printout([[ - These apply to all commands, as appropriate: - - --server= Fetch rocks/rockspecs from this server - (takes priority over config file) - --only-server= Fetch rocks/rockspecs from this server only - (overrides any entries in the config file) - --only-sources= Restrict downloads to paths matching the - given URL. - --tree= Which tree to operate on. - --local Use the tree in the user's home directory. - To enable it, see ']]..program..[[ help path'. - --verbose Display verbose output of commands executed. - --timeout= Timeout on network operations, in seconds. - 0 means no timeout (wait forever). - Default is ]]..tostring(cfg.connection_timeout)..[[.]]) - print_section("VARIABLES") - util.printout([[ - Variables from the "variables" table of the configuration file - can be overriden with VAR=VALUE assignments.]]) - print_section("COMMANDS") - for name, command in util.sortedpairs(commands) do - local cmd = require(command) - util.printout("", name) - util.printout("\t", cmd.help_summary) - end - print_section("CONFIGURATION") - util.printout("\tLua version: " .. cfg.lua_version) - util.printout("\tConfiguration files:") - util.printout("\t\tSystem: ".. dir.normalize(conf.system.file) .. " (" .. get_status(conf.system.ok) ..")") - if conf.user.file then - util.printout("\t\tUser : ".. dir.normalize(conf.user.file) .. " (" .. get_status(conf.user.ok) ..")\n") - else - util.printout("\t\tUser : disabled in this LuaRocks installation.\n") - end - util.printout("\tRocks trees in use: ") - for _, tree in ipairs(cfg.rocks_trees) do - if type(tree) == "string" then - util.printout("\t\t"..dir.normalize(tree)) - else - local name = tree.name and " (\""..tree.name.."\")" or "" - util.printout("\t\t"..dir.normalize(tree.root)..name) - end - end - else - command = command:gsub("-", "_") - local cmd = commands[command] and require(commands[command]) - if cmd then - local arguments = cmd.help_arguments or "" - print_banner() - print_section("NAME") - util.printout("\t"..program.." "..command.." - "..cmd.help_summary) - print_section("SYNOPSIS") - util.printout("\t"..program.." "..command.." "..arguments) - print_section("DESCRIPTION") - util.printout("",(cmd.help:gsub("\n","\n\t"):gsub("\n\t$",""))) - print_section("SEE ALSO") - util.printout("","'"..program.." help' for general options and configuration.\n") - else - return nil, "Unknown command: "..command - end - end - return true -end - -return help diff --git a/src/luarocks/install.lua b/src/luarocks/install.lua deleted file mode 100644 index c9b085f5..00000000 --- a/src/luarocks/install.lua +++ /dev/null @@ -1,183 +0,0 @@ ---- Module implementing the LuaRocks "install" command. --- Installs binary rocks. -local install = {} - -local path = require("luarocks.path") -local repos = require("luarocks.repos") -local fetch = require("luarocks.fetch") -local util = require("luarocks.util") -local fs = require("luarocks.fs") -local deps = require("luarocks.deps") -local writer = require("luarocks.manif.writer") -local remove = require("luarocks.remove") -local cfg = require("luarocks.core.cfg") - -install.help_summary = "Install a rock." - -install.help_arguments = "{| []}" - -install.help = [[ -Argument may be the name of a rock to be fetched from a repository -or a filename of a locally available rock. - ---keep Do not remove previously installed versions of the - rock after installing a new one. This behavior can - be made permanent by setting keep_other_versions=true - in the configuration file. - ---only-deps Installs only the dependencies of the rock. -]]..util.deps_mode_help() - - ---- Install a binary rock. --- @param rock_file string: local or remote filename of a rock. --- @param deps_mode: string: Which trees to check dependencies for: --- "one" for the current default tree, "all" for all trees, --- "order" for all trees with priority >= the current default, "none" for no trees. --- @return (string, string) or (nil, string, [string]): Name and version of --- installed rock if succeeded or nil and an error message followed by an error code. -function install.install_binary_rock(rock_file, deps_mode) - assert(type(rock_file) == "string") - - local name, version, arch = path.parse_name(rock_file) - if not name then - return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." - end - - if arch ~= "all" and arch ~= cfg.arch then - return nil, "Incompatible architecture "..arch, "arch" - end - if repos.is_installed(name, version) then - repos.delete_version(name, version, deps_mode) - end - - local rollback = util.schedule_function(function() - fs.delete(path.install_dir(name, version)) - fs.remove_dir_if_empty(path.versions_dir(name)) - end) - - local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) - if not ok then return nil, err, errcode end - - local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version)) - if err then - return nil, "Failed loading rockspec for installed package: "..err, errcode - end - - if deps_mode == "none" then - util.printerr("Warning: skipping dependency checks.") - else - ok, err, errcode = deps.check_external_deps(rockspec, "install") - if err then return nil, err, errcode end - end - - -- For compatibility with .rock files built with LuaRocks 1 - if not fs.exists(path.rock_manifest_file(name, version)) then - ok, err = writer.make_rock_manifest(name, version) - if err then return nil, err end - end - - if deps_mode ~= "none" then - ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) - if err then return nil, err, errcode end - end - - ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) - if err then return nil, err end - - util.remove_scheduled_function(rollback) - rollback = util.schedule_function(function() - repos.delete_version(name, version, deps_mode) - end) - - ok, err = repos.run_hook(rockspec, "post_install") - if err then return nil, err end - - util.announce_install(rockspec) - util.remove_scheduled_function(rollback) - return name, version -end - ---- Installs the dependencies of a binary rock. --- @param rock_file string: local or remote filename of a rock. --- @param deps_mode: string: Which trees to check dependencies for: --- "one" for the current default tree, "all" for all trees, --- "order" for all trees with priority >= the current default, "none" for no trees. --- @return (string, string) or (nil, string, [string]): Name and version of --- the rock whose dependencies were installed if succeeded or nil and an error message --- followed by an error code. -function install.install_binary_rock_deps(rock_file, deps_mode) - assert(type(rock_file) == "string") - - local name, version, arch = path.parse_name(rock_file) - if not name then - return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." - end - - if arch ~= "all" and arch ~= cfg.arch then - return nil, "Incompatible architecture "..arch, "arch" - end - - local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) - if not ok then return nil, err, errcode end - - local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version)) - if err then - return nil, "Failed loading rockspec for installed package: "..err, errcode - end - - ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) - if err then return nil, err, errcode end - - util.printout() - util.printout("Successfully installed dependencies for " ..name.." "..version) - - return name, version -end - ---- Driver function for the "install" command. --- @param name string: name of a binary rock. If an URL or pathname --- to a binary rock is given, fetches and installs it. If a rockspec or a --- source rock is given, forwards the request to the "build" command. --- If a package name is given, forwards the request to "search" and, --- if returned a result, installs the matching rock. --- @param version string: When passing a package name, a version number --- may also be given. --- @return boolean or (nil, string, exitcode): True if installation was --- successful, nil and an error message otherwise. exitcode is optionally returned. -function install.command(flags, name, version) - if type(name) ~= "string" then - return nil, "Argument missing. "..util.see_help("install") - end - - local ok, err = fs.check_command_permissions(flags) - if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end - - if name:match("%.rockspec$") or name:match("%.src%.rock$") then - local build = require("luarocks.build") - return build.command(flags, name) - elseif name:match("%.rock$") then - if flags["only-deps"] then - ok, err = install.install_binary_rock_deps(name, deps.get_deps_mode(flags)) - else - ok, err = install.install_binary_rock(name, deps.get_deps_mode(flags)) - end - if not ok then return nil, err end - name, version = ok, err - if (not flags["only-deps"]) and (not flags["keep"]) and not cfg.keep_other_versions then - local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) - if not ok then util.printerr(err) end - end - return name, version - else - local search = require("luarocks.search") - local url, err = search.find_suitable_rock(search.make_query(name:lower(), version)) - if not url then - return nil, err - end - util.printout("Installing "..url) - return install.command(flags, url) - end -end - -return install diff --git a/src/luarocks/lint.lua b/src/luarocks/lint.lua deleted file mode 100644 index c9ea45ea..00000000 --- a/src/luarocks/lint.lua +++ /dev/null @@ -1,53 +0,0 @@ - ---- Module implementing the LuaRocks "lint" command. --- Utility function that checks syntax of the rockspec. -local lint = {} - -local util = require("luarocks.util") -local download = require("luarocks.download") -local fetch = require("luarocks.fetch") - -lint.help_summary = "Check syntax of a rockspec." -lint.help_arguments = "" -lint.help = [[ -This is a utility function that checks the syntax of a rockspec. - -It returns success or failure if the text of a rockspec is -syntactically correct. -]] - -function lint.command(flags, input) - if not input then - return nil, "Argument missing. "..util.see_help("lint") - end - - local filename = input - if not input:match(".rockspec$") then - local err - filename, err = download.download("rockspec", input:lower()) - if not filename then - return nil, err - end - end - - local rs, err = fetch.load_local_rockspec(filename) - if not rs then - return nil, "Failed loading rockspec: "..err - end - - local ok = true - - -- This should have been done in the type checker, - -- but it would break compatibility of other commands. - -- Making 'lint' alone be stricter shouldn't be a problem, - -- because extra-strict checks is what lint-type commands - -- are all about. - if not rs.description.license then - util.printerr("Rockspec has no license field.") - ok = false - end - - return ok, ok or filename.." failed consistency checks." -end - -return lint diff --git a/src/luarocks/list.lua b/src/luarocks/list.lua deleted file mode 100644 index 45f1a26f..00000000 --- a/src/luarocks/list.lua +++ /dev/null @@ -1,95 +0,0 @@ - ---- Module implementing the LuaRocks "list" command. --- Lists currently installed rocks. -local list = {} - -local search = require("luarocks.search") -local deps = require("luarocks.deps") -local cfg = require("luarocks.core.cfg") -local util = require("luarocks.util") -local path = require("luarocks.path") - -list.help_summary = "List currently installed rocks." -list.help_arguments = "[--porcelain] " -list.help = [[ - is a substring of a rock name to filter by. - ---outdated List only rocks for which there is a - higher version available in the rocks server. - ---porcelain Produce machine-friendly output. -]] - -local function check_outdated(trees, query) - local results_installed = {} - for _, tree in ipairs(trees) do - search.manifest_search(results_installed, path.rocks_dir(tree), query) - end - local outdated = {} - for name, versions in util.sortedpairs(results_installed) do - versions = util.keys(versions) - table.sort(versions, deps.compare_versions) - local latest_installed = versions[1] - - local query_available = search.make_query(name:lower()) - query.exact_name = true - local results_available, err = search.search_repos(query_available) - - if results_available[name] then - local available_versions = util.keys(results_available[name]) - table.sort(available_versions, deps.compare_versions) - local latest_available = available_versions[1] - local latest_available_repo = results_available[name][latest_available][1].repo - - if deps.compare_versions(latest_available, latest_installed) then - table.insert(outdated, { name = name, installed = latest_installed, available = latest_available, repo = latest_available_repo }) - end - end - end - return outdated -end - -local function list_outdated(trees, query, porcelain) - util.title("Outdated rocks:", porcelain) - local outdated = check_outdated(trees, query) - for _, item in ipairs(outdated) do - if porcelain then - util.printout(item.name, item.installed, item.available, item.repo) - else - util.printout(item.name) - util.printout(" "..item.installed.." < "..item.available.." at "..item.repo) - util.printout() - end - end - return true -end - ---- Driver function for "list" command. --- @param filter string or nil: A substring of a rock name to filter by. --- @param version string or nil: a version may also be passed. --- @return boolean: True if succeeded, nil on errors. -function list.command(flags, filter, version) - local query = search.make_query(filter and filter:lower() or "", version) - query.exact_name = false - local trees = cfg.rocks_trees - if flags["tree"] then - trees = { flags["tree"] } - end - - if flags["outdated"] then - return list_outdated(trees, query, flags["porcelain"]) - end - - local results = {} - for _, tree in ipairs(trees) do - local ok, err, errcode = search.manifest_search(results, path.rocks_dir(tree), query) - if not ok and errcode ~= "open" then - util.warning(err) - end - end - util.title("Installed rocks:", flags["porcelain"]) - search.print_results(results, flags["porcelain"]) - return true -end - -return list diff --git a/src/luarocks/make.lua b/src/luarocks/make.lua deleted file mode 100644 index eb38bff0..00000000 --- a/src/luarocks/make.lua +++ /dev/null @@ -1,86 +0,0 @@ - ---- Module implementing the LuaRocks "make" command. --- Builds sources in the current directory, but unlike "build", --- it does not fetch sources, etc., assuming everything is --- available in the current directory. -local make = {} - -local build = require("luarocks.build") -local fs = require("luarocks.fs") -local util = require("luarocks.util") -local cfg = require("luarocks.core.cfg") -local fetch = require("luarocks.fetch") -local pack = require("luarocks.pack") -local remove = require("luarocks.remove") -local deps = require("luarocks.deps") - -make.help_summary = "Compile package in current directory using a rockspec." -make.help_arguments = "[--pack-binary-rock] []" -make.help = [[ -Builds sources in the current directory, but unlike "build", -it does not fetch sources, etc., assuming everything is -available in the current directory. If no argument is given, -it looks for a rockspec in the current directory and in "rockspec/" -and "rockspecs/" subdirectories, picking the rockspec with newest version -or without version name. If rockspecs for different rocks are found -or there are several rockspecs without version, you must specify which to use, -through the command-line. - -This command is useful as a tool for debugging rockspecs. -To install rocks, you'll normally want to use the "install" and -"build" commands. See the help on those for details. - ---pack-binary-rock Do not install rock. Instead, produce a .rock file - with the contents of compilation in the current - directory. - ---keep Do not remove previously installed versions of the - rock after installing a new one. This behavior can - be made permanent by setting keep_other_versions=true - in the configuration file. - ---branch= Override the `source.branch` field in the loaded - rockspec. Allows to specify a different branch to - fetch. Particularly for SCM rocks. - -]] - ---- Driver function for "make" command. --- @param name string: A local rockspec. --- @return boolean or (nil, string, exitcode): True if build was successful; nil and an --- error message otherwise. exitcode is optionally returned. -function make.command(flags, rockspec) - assert(type(rockspec) == "string" or not rockspec) - - if not rockspec then - local err - rockspec, err = util.get_default_rockspec() - if not rockspec then - return nil, err - end - end - if not rockspec:match("rockspec$") then - return nil, "Invalid argument: 'make' takes a rockspec as a parameter. "..util.see_help("make") - end - - if flags["pack-binary-rock"] then - local rspec, err, errcode = fetch.load_rockspec(rockspec) - if not rspec then - return nil, err - end - return pack.pack_binary_rock(rspec.name, rspec.version, build.build_rockspec, rockspec, false, true, deps.get_deps_mode(flags)) - else - local ok, err = fs.check_command_permissions(flags) - if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end - ok, err = build.build_rockspec(rockspec, false, true, deps.get_deps_mode(flags)) - if not ok then return nil, err end - local name, version = ok, err - if (not flags["keep"]) and not cfg.keep_other_versions then - local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) - if not ok then util.printerr(err) end - end - return name, version - end -end - -return make diff --git a/src/luarocks/new_version.lua b/src/luarocks/new_version.lua deleted file mode 100644 index b13dbb97..00000000 --- a/src/luarocks/new_version.lua +++ /dev/null @@ -1,199 +0,0 @@ - ---- Module implementing the LuaRocks "new_version" command. --- Utility function that writes a new rockspec, updating data from a previous one. -local new_version = {} - -local util = require("luarocks.util") -local download = require("luarocks.download") -local fetch = require("luarocks.fetch") -local persist = require("luarocks.persist") -local fs = require("luarocks.fs") -local type_check = require("luarocks.type_check") - -new_version.help_summary = "Auto-write a rockspec for a new version of a rock." -new_version.help_arguments = "[--tag=] [|] [] []" -new_version.help = [[ -This is a utility function that writes a new rockspec, updating data -from a previous one. - -If a package name is given, it downloads the latest rockspec from the -default server. If a rockspec is given, it uses it instead. If no argument -is given, it looks for a rockspec same way 'luarocks make' does. - -If the version number is not given and tag is passed using --tag, -it is used as the version, with 'v' removed from beginning. -Otherwise, it only increments the revision number of the given -(or downloaded) rockspec. - -If a URL is given, it replaces the one from the old rockspec with the -given URL. If a URL is not given and a new version is given, it tries -to guess the new URL by replacing occurrences of the version number -in the URL or tag. It also tries to download the new URL to determine -the new MD5 checksum. - -If a tag is given, it replaces the one from the old rockspec. If there is -an old tag but no new one passed, it is guessed in the same way URL is. - -WARNING: it writes the new rockspec to the current directory, -overwriting the file if it already exists. -]] - -local function try_replace(tbl, field, old, new) - if not tbl[field] then - return false - end - local old_field = tbl[field] - local new_field = tbl[field]:gsub(old, new) - if new_field ~= old_field then - util.printout("Guessing new '"..field.."' field as "..new_field) - tbl[field] = new_field - return true - end - return false -end - --- Try to download source file using URL from a rockspec. --- If it specified MD5, update it. --- @return (true, false) if MD5 was not specified or it stayed same, --- (true, true) if MD5 changed, (nil, string) on error. -local function check_url_and_update_md5(out_rs) - local file, temp_dir = fetch.fetch_url_at_temp_dir(out_rs.source.url, "luarocks-new-version-"..out_rs.package) - if not file then - util.printerr("Warning: invalid URL - "..temp_dir) - return true, false - end - - local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, out_rs.source.url, out_rs.source.dir) - if not inferred_dir then - return nil, found_dir - end - - if found_dir and found_dir ~= inferred_dir then - out_rs.source.dir = found_dir - end - - if file then - if out_rs.source.md5 then - util.printout("File successfully downloaded. Updating MD5 checksum...") - local new_md5, err = fs.get_md5(file) - if not new_md5 then - return nil, err - end - local old_md5 = out_rs.source.md5 - out_rs.source.md5 = new_md5 - return true, new_md5 ~= old_md5 - else - util.printout("File successfully downloaded.") - return true, false - end - end -end - -local function update_source_section(out_rs, url, tag, old_ver, new_ver) - if tag then - out_rs.source.tag = tag - end - if url then - out_rs.source.url = url - return check_url_and_update_md5(out_rs) - end - if new_ver == old_ver then - return true - end - if out_rs.source.dir then - try_replace(out_rs.source, "dir", old_ver, new_ver) - end - if out_rs.source.file then - try_replace(out_rs.source, "file", old_ver, new_ver) - end - if try_replace(out_rs.source, "url", old_ver, new_ver) then - return check_url_and_update_md5(out_rs) - end - if tag or try_replace(out_rs.source, "tag", old_ver, new_ver) then - return true - end - -- Couldn't replace anything significant, use the old URL. - local ok, md5_changed = check_url_and_update_md5(out_rs) - if not ok then - return nil, md5_changed - end - if md5_changed then - util.printerr("Warning: URL is the same, but MD5 has changed. Old rockspec is broken.") - end - return true -end - -function new_version.command(flags, input, version, url) - if not input then - local err - input, err = util.get_default_rockspec() - if not input then - return nil, err - end - end - assert(type(input) == "string") - - local filename, err - if input:match("rockspec$") then - filename, err = fetch.fetch_url(input) - if not filename then - return nil, err - end - else - filename, err = download.download("rockspec", input:lower()) - if not filename then - return nil, err - end - end - - local valid_rs, err = fetch.load_rockspec(filename) - if not valid_rs then - return nil, err - end - - local old_ver, old_rev = valid_rs.version:match("(.*)%-(%d+)$") - local new_ver, new_rev - - if flags.tag and not version then - version = flags.tag:gsub("^v", "") - end - - if version then - new_ver, new_rev = version:match("(.*)%-(%d+)$") - new_rev = tonumber(new_rev) - if not new_rev then - new_ver = version - new_rev = 1 - end - else - new_ver = old_ver - new_rev = tonumber(old_rev) + 1 - end - local new_rockver = new_ver:gsub("-", "") - - local out_rs, err = persist.load_into_table(filename) - local out_name = out_rs.package:lower() - out_rs.version = new_rockver.."-"..new_rev - - local ok, err = update_source_section(out_rs, url, flags.tag, old_ver, new_ver) - if not ok then return nil, err end - - if out_rs.build and out_rs.build.type == "module" then - out_rs.build.type = "builtin" - end - - local out_filename = out_name.."-"..new_rockver.."-"..new_rev..".rockspec" - - persist.save_from_table(out_filename, out_rs, type_check.rockspec_order) - - util.printout("Wrote "..out_filename) - - local valid_out_rs, err = fetch.load_local_rockspec(out_filename) - if not valid_out_rs then - return nil, "Failed loading generated rockspec: "..err - end - - return true -end - -return new_version diff --git a/src/luarocks/pack.lua b/src/luarocks/pack.lua deleted file mode 100644 index 655cbf37..00000000 --- a/src/luarocks/pack.lua +++ /dev/null @@ -1,193 +0,0 @@ - ---- Module implementing the LuaRocks "pack" command. --- Creates a rock, packing sources or binaries. -local pack = {} - -local unpack = unpack or table.unpack - -local path = require("luarocks.path") -local repos = require("luarocks.repos") -local fetch = require("luarocks.fetch") -local fs = require("luarocks.fs") -local cfg = require("luarocks.core.cfg") -local util = require("luarocks.util") -local dir = require("luarocks.dir") -local manif = require("luarocks.manif") -local search = require("luarocks.search") - -pack.help_summary = "Create a rock, packing sources or binaries." -pack.help_arguments = "{| []}" -pack.help = [[ -Argument may be a rockspec file, for creating a source rock, -or the name of an installed package, for creating a binary rock. -In the latter case, the app version may be given as a second -argument. -]] - ---- Create a source rock. --- Packages a rockspec and its required source files in a rock --- file with the .src.rock extension, which can later be built and --- installed with the "build" command. --- @param rockspec_file string: An URL or pathname for a rockspec file. --- @return string or (nil, string): The filename of the resulting --- .src.rock file; or nil and an error message. -function pack.pack_source_rock(rockspec_file) - assert(type(rockspec_file) == "string") - - local rockspec, err = fetch.load_rockspec(rockspec_file) - if err then - return nil, "Error loading rockspec: "..err - end - rockspec_file = rockspec.local_filename - - local name_version = rockspec.name .. "-" .. rockspec.version - local rock_file = fs.absolute_name(name_version .. ".src.rock") - - local source_file, source_dir = fetch.fetch_sources(rockspec, false) - if not source_file then - return nil, source_dir - end - local ok, err = fs.change_dir(source_dir) - if not ok then return nil, err end - - fs.delete(rock_file) - fs.copy(rockspec_file, source_dir, cfg.perm_read) - if not fs.zip(rock_file, dir.base_name(rockspec_file), dir.base_name(source_file)) then - return nil, "Failed packing "..rock_file - end - fs.pop_dir() - - return rock_file -end - -local function copy_back_files(name, version, file_tree, deploy_dir, pack_dir, perms) - local ok, err = fs.make_dir(pack_dir) - if not ok then return nil, err end - for file, sub in pairs(file_tree) do - local source = dir.path(deploy_dir, file) - local target = dir.path(pack_dir, file) - if type(sub) == "table" then - local ok, err = copy_back_files(name, version, sub, source, target) - if not ok then return nil, err end - else - local versioned = path.versioned_name(source, deploy_dir, name, version) - if fs.exists(versioned) then - fs.copy(versioned, target, perms) - else - fs.copy(source, target, perms) - end - end - end - return true -end - --- @param name string: Name of package to pack. --- @param version string or nil: A version number may also be passed. --- @param tree string or nil: An optional tree to pick the package from. --- @return string or (nil, string): The filename of the resulting --- .src.rock file; or nil and an error message. -local function do_pack_binary_rock(name, version, tree) - assert(type(name) == "string") - assert(type(version) == "string" or not version) - - local repo, repo_url - name, version, repo, repo_url = search.pick_installed_rock(name, version, tree) - if not name then - return nil, version - end - - local root = path.root_dir(repo_url) - local prefix = path.install_dir(name, version, root) - if not fs.exists(prefix) then - return nil, "'"..name.." "..version.."' does not seem to be an installed rock." - end - - local rock_manifest, err = manif.load_rock_manifest(name, version, root) - if not rock_manifest then return nil, err end - - local name_version = name .. "-" .. version - local rock_file = fs.absolute_name(name_version .. "."..cfg.arch..".rock") - - local temp_dir = fs.make_temp_dir("pack") - fs.copy_contents(prefix, temp_dir) - - local is_binary = false - if rock_manifest.lib then - local ok, err = copy_back_files(name, version, rock_manifest.lib, path.deploy_lib_dir(root), dir.path(temp_dir, "lib"), cfg.perm_exec) - if not ok then return nil, "Failed copying back files: " .. err end - is_binary = true - end - if rock_manifest.lua then - local ok, err = copy_back_files(name, version, rock_manifest.lua, path.deploy_lua_dir(root), dir.path(temp_dir, "lua"), cfg.perm_read) - if not ok then return nil, "Failed copying back files: " .. err end - end - - local ok, err = fs.change_dir(temp_dir) - if not ok then return nil, err end - if not is_binary and not repos.has_binaries(name, version) then - rock_file = rock_file:gsub("%."..cfg.arch:gsub("%-","%%-").."%.", ".all.") - end - fs.delete(rock_file) - if not fs.zip(rock_file, unpack(fs.list_dir())) then - return nil, "Failed packing "..rock_file - end - fs.pop_dir() - fs.delete(temp_dir) - return rock_file -end - -function pack.pack_binary_rock(name, version, cmd, ...) - - -- The --pack-binary-rock option for "luarocks build" basically performs - -- "luarocks build" on a temporary tree and then "luarocks pack". The - -- alternative would require refactoring parts of luarocks.build and - -- luarocks.pack, which would save a few file operations: the idea would be - -- to shave off the final deploy steps from the build phase and the initial - -- collect steps from the pack phase. - - local temp_dir, err = fs.make_temp_dir("luarocks-build-pack-"..dir.base_name(name)) - if not temp_dir then - return nil, "Failed creating temporary directory: "..err - end - util.schedule_function(fs.delete, temp_dir) - - path.use_tree(temp_dir) - local ok, err = cmd(...) - if not ok then - return nil, err - end - local rname, rversion = path.parse_name(name) - if not rname then - rname, rversion = name, version - end - return do_pack_binary_rock(rname, rversion, temp_dir) -end - ---- Driver function for the "pack" command. --- @param arg string: may be a rockspec file, for creating a source rock, --- or the name of an installed package, for creating a binary rock. --- @param version string or nil: if the name of a package is given, a --- version may also be passed. --- @return boolean or (nil, string): true if successful or nil followed --- by an error message. -function pack.command(flags, arg, version) - assert(type(version) == "string" or not version) - if type(arg) ~= "string" then - return nil, "Argument missing. "..util.see_help("pack") - end - - local file, err - if arg:match(".*%.rockspec") then - file, err = pack.pack_source_rock(arg) - else - file, err = do_pack_binary_rock(arg:lower(), version, flags["tree"]) - end - if err then - return nil, err - else - util.printout("Packed: "..file) - return true - end -end - -return pack diff --git a/src/luarocks/path_cmd.lua b/src/luarocks/path_cmd.lua deleted file mode 100644 index 516a0c47..00000000 --- a/src/luarocks/path_cmd.lua +++ /dev/null @@ -1,68 +0,0 @@ - ---- @module luarocks.path_cmd --- Driver for the `luarocks path` command. -local path_cmd = {} - -local util = require("luarocks.util") -local cfg = require("luarocks.core.cfg") - -path_cmd.help_summary = "Return the currently configured package path." -path_cmd.help_arguments = "" -path_cmd.help = [[ -Returns the package path currently configured for this installation -of LuaRocks, formatted as shell commands to update LUA_PATH and LUA_CPATH. - ---bin Adds the system path to the output - ---append Appends the paths to the existing paths. Default is to prefix - the LR paths to the existing paths. - ---lr-path Exports the Lua path (not formatted as shell command) - ---lr-cpath Exports the Lua cpath (not formatted as shell command) - ---lr-bin Exports the system path (not formatted as shell command) - - -On Unix systems, you may run: - eval `luarocks path` -And on Windows: - luarocks path > "%temp%\_lrp.bat" && call "%temp%\_lrp.bat" && del "%temp%\_lrp.bat" -]] - ---- Driver function for "path" command. --- @return boolean This function always succeeds. -function path_cmd.command(flags) - local lr_path, lr_cpath, lr_bin = cfg.package_paths(flags["tree"]) - local path_sep = cfg.export_path_separator - - if flags["lr-path"] then - util.printout(util.remove_path_dupes(lr_path, ';')) - return true - elseif flags["lr-cpath"] then - util.printout(util.remove_path_dupes(lr_cpath, ';')) - return true - elseif flags["lr-bin"] then - util.printout(util.remove_path_dupes(lr_bin, path_sep)) - return true - end - - if flags["append"] then - lr_path = package.path .. ";" .. lr_path - lr_cpath = package.cpath .. ";" .. lr_cpath - lr_bin = os.getenv("PATH") .. path_sep .. lr_bin - else - lr_path = lr_path.. ";" .. package.path - lr_cpath = lr_cpath .. ";" .. package.cpath - lr_bin = lr_bin .. path_sep .. os.getenv("PATH") - end - - util.printout(cfg.export_lua_path:format(util.remove_path_dupes(lr_path, ';'))) - util.printout(cfg.export_lua_cpath:format(util.remove_path_dupes(lr_cpath, ';'))) - if flags["bin"] then - util.printout(cfg.export_path:format(util.remove_path_dupes(lr_bin, path_sep))) - end - return true -end - -return path_cmd diff --git a/src/luarocks/purge.lua b/src/luarocks/purge.lua deleted file mode 100644 index 50f290c8..00000000 --- a/src/luarocks/purge.lua +++ /dev/null @@ -1,77 +0,0 @@ - ---- Module implementing the LuaRocks "purge" command. --- Remove all rocks from a given tree. -local purge = {} - -local util = require("luarocks.util") -local fs = require("luarocks.fs") -local path = require("luarocks.path") -local search = require("luarocks.search") -local deps = require("luarocks.deps") -local repos = require("luarocks.repos") -local writer = require("luarocks.manif.writer") -local cfg = require("luarocks.core.cfg") -local remove = require("luarocks.remove") - -purge.help_summary = "Remove all installed rocks from a tree." -purge.help_arguments = "--tree= [--old-versions]" -purge.help = [[ -This command removes rocks en masse from a given tree. -By default, it removes all rocks from a tree. - -The --tree argument is mandatory: luarocks purge does not -assume a default tree. - ---old-versions Keep the highest-numbered version of each - rock and remove the other ones. By default - it only removes old versions if they are - not needed as dependencies. This can be - overridden with the flag --force. -]] - -function purge.command(flags) - local tree = flags["tree"] - - if type(tree) ~= "string" then - return nil, "The --tree argument is mandatory. "..util.see_help("purge") - end - - local results = {} - local query = search.make_query("") - query.exact_name = false - if not fs.is_dir(tree) then - return nil, "Directory not found: "..tree - end - - local ok, err = fs.check_command_permissions(flags) - if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end - - search.manifest_search(results, path.rocks_dir(tree), query) - - local sort = function(a,b) return deps.compare_versions(b,a) end - if flags["old-versions"] then - sort = deps.compare_versions - end - - for package, versions in util.sortedpairs(results) do - for version, _ in util.sortedpairs(versions, sort) do - if flags["old-versions"] then - util.printout("Keeping "..package.." "..version.."...") - local ok, err = remove.remove_other_versions(package, version, flags["force"], flags["force-fast"]) - if not ok then - util.printerr(err) - end - break - else - util.printout("Removing "..package.." "..version.."...") - local ok, err = repos.delete_version(package, version, "none", true) - if not ok then - util.printerr(err) - end - end - end - end - return writer.make_manifest(cfg.rocks_dir, "one") -end - -return purge diff --git a/src/luarocks/remove.lua b/src/luarocks/remove.lua deleted file mode 100644 index e7f37604..00000000 --- a/src/luarocks/remove.lua +++ /dev/null @@ -1,165 +0,0 @@ - ---- Module implementing the LuaRocks "remove" command. --- Uninstalls rocks. -local remove = {} - -local search = require("luarocks.search") -local deps = require("luarocks.deps") -local fetch = require("luarocks.fetch") -local repos = require("luarocks.repos") -local path = require("luarocks.path") -local util = require("luarocks.util") -local cfg = require("luarocks.core.cfg") -local fs = require("luarocks.fs") - -remove.help_summary = "Uninstall a rock." -remove.help_arguments = "[--force|--force-fast] []" -remove.help = [[ -Argument is the name of a rock to be uninstalled. -If a version is not given, try to remove all versions at once. -Will only perform the removal if it does not break dependencies. -To override this check and force the removal, use --force. -To perform a forced removal without reporting dependency issues, -use --force-fast. - -]]..util.deps_mode_help() - ---- Obtain a list of packages that depend on the given set of packages --- (where all packages of the set are versions of one program). --- @param name string: the name of a program --- @param versions array of string: the versions to be deleted. --- @return array of string: an empty table if no packages depend on any --- of the given list, or an array of strings in "name/version" format. -local function check_dependents(name, versions, deps_mode) - local dependents = {} - local blacklist = {} - blacklist[name] = {} - for version, _ in pairs(versions) do - blacklist[name][version] = true - end - local local_rocks = {} - local query_all = search.make_query("") - query_all.exact_name = false - search.manifest_search(local_rocks, cfg.rocks_dir, query_all) - local_rocks[name] = nil - for rock_name, rock_versions in pairs(local_rocks) do - for rock_version, _ in pairs(rock_versions) do - local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version)) - if rockspec then - local _, missing = deps.match_deps(rockspec, blacklist, deps_mode) - if missing[name] then - table.insert(dependents, { name = rock_name, version = rock_version }) - end - end - end - end - return dependents -end - ---- Delete given versions of a program. --- @param name string: the name of a program --- @param versions array of string: the versions to be deleted. --- @param deps_mode: string: Which trees to check dependencies for: --- "one" for the current default tree, "all" for all trees, --- "order" for all trees with priority >= the current default, "none" for no trees. --- @return boolean or (nil, string): true on success or nil and an error message. -local function delete_versions(name, versions, deps_mode) - - for version, _ in pairs(versions) do - util.printout("Removing "..name.." "..version.."...") - local ok, err = repos.delete_version(name, version, deps_mode) - if not ok then return nil, err end - end - - return true -end - -function remove.remove_search_results(results, name, deps_mode, force, fast) - local versions = results[name] - - local version = next(versions) - local second = next(versions, version) - - local dependents = {} - if not fast then - util.printout("Checking stability of dependencies in the absence of") - util.printout(name.." "..table.concat(util.keys(versions), ", ").."...") - util.printout() - dependents = check_dependents(name, versions, deps_mode) - end - - if #dependents > 0 then - if force or fast then - util.printerr("The following packages may be broken by this forced removal:") - for _, dependent in ipairs(dependents) do - util.printerr(dependent.name.." "..dependent.version) - end - util.printerr() - else - if not second then - util.printerr("Will not remove "..name.." "..version..".") - util.printerr("Removing it would break dependencies for: ") - else - util.printerr("Will not remove installed versions of "..name..".") - util.printerr("Removing them would break dependencies for: ") - end - for _, dependent in ipairs(dependents) do - util.printerr(dependent.name.." "..dependent.version) - end - util.printerr() - util.printerr("Use --force to force removal (warning: this may break modules).") - return nil, "Failed removing." - end - end - - local ok, err = delete_versions(name, versions, deps_mode) - if not ok then return nil, err end - - util.printout("Removal successful.") - return true -end - -function remove.remove_other_versions(name, version, force, fast) - local results = {} - search.manifest_search(results, cfg.rocks_dir, { name = name, exact_name = true, constraints = {{ op = "~=", version = version}} }) - if results[name] then - return remove.remove_search_results(results, name, cfg.deps_mode, force, fast) - end - return true -end - ---- Driver function for the "remove" command. --- @param name string: name of a rock. If a version is given, refer to --- a specific version; otherwise, try to remove all versions. --- @param version string: When passing a package name, a version number --- may also be given. --- @return boolean or (nil, string, exitcode): True if removal was --- successful, nil and an error message otherwise. exitcode is optionally returned. -function remove.command(flags, name, version) - if type(name) ~= "string" then - return nil, "Argument missing. "..util.see_help("remove") - end - - local deps_mode = flags["deps-mode"] or cfg.deps_mode - - local ok, err = fs.check_command_permissions(flags) - if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end - - local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$") - local filename = name - if rock_type then - name, version = path.parse_name(filename) - if not name then return nil, "Invalid "..rock_type.." filename: "..filename end - end - - local results = {} - name = name:lower() - search.manifest_search(results, cfg.rocks_dir, search.make_query(name, version)) - if not results[name] then - return nil, "Could not find rock '"..name..(version and " "..version or "").."' in "..path.rocks_tree_to_string(cfg.root_dir) - end - - return remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"]) -end - -return remove diff --git a/src/luarocks/search.lua b/src/luarocks/search.lua deleted file mode 100644 index 44eff694..00000000 --- a/src/luarocks/search.lua +++ /dev/null @@ -1,482 +0,0 @@ - ---- Module implementing the LuaRocks "search" command. --- Queries LuaRocks servers. -local search = {} - - -local dir = require("luarocks.dir") -local path = require("luarocks.path") -local manif = require("luarocks.manif") -local deps = require("luarocks.deps") -local cfg = require("luarocks.core.cfg") -local util = require("luarocks.util") - -search.help_summary = "Query the LuaRocks servers." -search.help_arguments = "[--source] [--binary] { [] | --all }" -search.help = [[ ---source Return only rockspecs and source rocks, - to be used with the "build" command. ---binary Return only pure Lua and binary rocks (rocks that can be used - with the "install" command without requiring a C toolchain). ---all List all contents of the server that are suitable to - this platform, do not filter by name. -]] - ---- Convert the arch field of a query table to table format. --- @param query table: A query table. -local function query_arch_as_table(query) - local format = type(query.arch) - if format == "table" then - return - elseif format == "nil" then - local accept = {} - accept["src"] = true - accept["all"] = true - accept["rockspec"] = true - accept["installed"] = true - accept[cfg.arch] = true - query.arch = accept - elseif format == "string" then - local accept = {} - for a in query.arch:gmatch("[%w_-]+") do - accept[a] = true - end - query.arch = accept - end -end - ---- Store a search result (a rock or rockspec) in the results table. --- @param results table: The results table, where keys are package names and --- values are tables matching version strings to arrays of --- tables with fields "arch" and "repo". --- @param name string: Package name. --- @param version string: Package version. --- @param arch string: Architecture of rock ("all", "src" or platform --- identifier), "rockspec" or "installed" --- @param repo string: Pathname of a local repository of URL of --- rocks server. -local function store_result(results, name, version, arch, repo) - assert(type(results) == "table") - assert(type(name) == "string") - assert(type(version) == "string") - assert(type(arch) == "string") - assert(type(repo) == "string") - - if not results[name] then results[name] = {} end - if not results[name][version] then results[name][version] = {} end - table.insert(results[name][version], { - arch = arch, - repo = repo - }) -end - ---- Test the name field of a query. --- If query has a boolean field exact_name set to false, --- then substring match is performed; otherwise, exact string --- comparison is done. --- @param query table: A query in dependency table format. --- @param name string: A package name. --- @return boolean: True if names match, false otherwise. -local function match_name(query, name) - assert(type(query) == "table") - assert(type(name) == "string") - if query.exact_name == false then - return name:find(query.name, 0, true) and true or false - else - return name == query.name - end -end - ---- Store a match in a results table if version matches query. --- Name, version, arch and repository path are stored in a given --- table, optionally checking if version and arch (if given) match --- a query. --- @param results table: The results table, where keys are package names and --- values are tables matching version strings to arrays of --- tables with fields "arch" and "repo". --- @param repo string: URL or pathname of the repository. --- @param name string: The name of the package being tested. --- @param version string: The version of the package being tested. --- @param arch string: The arch of the package being tested. --- @param query table: A table describing the query in dependency --- format (for example, {name = "filesystem", exact_name = false, --- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). --- If the arch field is omitted, the local architecture (cfg.arch) --- is used. The special value "any" is also recognized, returning all --- matches regardless of architecture. -local function store_if_match(results, repo, name, version, arch, query) - if match_name(query, name) then - if query.arch[arch] or query.arch["any"] then - if deps.match_constraints(deps.parse_version(version), query.constraints) then - store_result(results, name, version, arch, repo) - end - end - end -end - ---- Perform search on a local repository. --- @param repo string: The pathname of the local repository. --- @param query table: A table describing the query in dependency --- format (for example, {name = "filesystem", exact_name = false, --- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). --- If the arch field is omitted, the local architecture (cfg.arch) --- is used. The special value "any" is also recognized, returning all --- matches regardless of architecture. --- @param results table or nil: If given, this table will store the --- results; if not given, a new table will be created. --- @return table: The results table, where keys are package names and --- values are tables matching version strings to arrays of --- tables with fields "arch" and "repo". --- If a table was given in the "results" parameter, that is the result value. -function search.disk_search(repo, query, results) - assert(type(repo) == "string") - assert(type(query) == "table") - assert(type(results) == "table" or not results) - - local fs = require("luarocks.fs") - - if not results then - results = {} - end - query_arch_as_table(query) - - for name in fs.dir(repo) do - local pathname = dir.path(repo, name) - local rname, rversion, rarch = path.parse_name(name) - - if rname and (pathname:match(".rockspec$") or pathname:match(".rock$")) then - store_if_match(results, repo, rname, rversion, rarch, query) - elseif fs.is_dir(pathname) then - for version in fs.dir(pathname) do - if version:match("-%d+$") then - store_if_match(results, repo, name, version, "installed", query) - end - end - end - end - return results -end - ---- Perform search on a rocks server or tree. --- @param results table: The results table, where keys are package names and --- values are tables matching version strings to arrays of --- tables with fields "arch" and "repo". --- @param repo string: The URL of a rocks server or --- the pathname of a rocks tree (as returned by path.rocks_dir()). --- @param query table: A table describing the query in dependency --- format (for example, {name = "filesystem", exact_name = false, --- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). --- If the arch field is omitted, the local architecture (cfg.arch) --- is used. The special value "any" is also recognized, returning all --- matches regardless of architecture. --- @param lua_version string: Lua version in "5.x" format, defaults to installed version. --- @return true or, in case of errors, nil, an error message and an optional error code. -function search.manifest_search(results, repo, query, lua_version) - assert(type(results) == "table") - assert(type(repo) == "string") - assert(type(query) == "table") - - query_arch_as_table(query) - local manifest, err, errcode = manif.load_manifest(repo, lua_version) - if not manifest then - return nil, err, errcode - end - for name, versions in pairs(manifest.repository) do - for version, items in pairs(versions) do - for _, item in ipairs(items) do - store_if_match(results, repo, name, version, item.arch, query) - end - end - end - return true -end - ---- Search on all configured rocks servers. --- @param query table: A dependency query. --- @param lua_version string: Lua version in "5.x" format, defaults to installed version. --- @return table: A table where keys are package names --- and values are tables matching version strings to arrays of --- tables with fields "arch" and "repo". -function search.search_repos(query, lua_version) - assert(type(query) == "table") - - local results = {} - for _, repo in ipairs(cfg.rocks_servers) do - if not cfg.disabled_servers[repo] then - if type(repo) == "string" then - repo = { repo } - end - for _, mirror in ipairs(repo) do - local protocol, pathname = dir.split_url(mirror) - if protocol == "file" then - mirror = pathname - end - local ok, err, errcode = search.manifest_search(results, mirror, query, lua_version) - if errcode == "network" then - cfg.disabled_servers[repo] = true - end - if ok then - break - else - util.warning("Failed searching manifest: "..err) - end - end - end - end - -- search through rocks in cfg.rocks_provided - local provided_repo = "provided by VM or rocks_provided" - for name, versions in pairs(cfg.rocks_provided) do - store_if_match(results, provided_repo, name, versions, "installed", query) - end - return results -end - ---- Prepare a query in dependency table format. --- @param name string: The query name. --- @param version string or nil: --- @return table: A query in table format -function search.make_query(name, version) - assert(type(name) == "string") - assert(type(version) == "string" or not version) - - local query = { - name = name, - constraints = {} - } - if version then - table.insert(query.constraints, { op = "==", version = deps.parse_version(version)}) - end - return query -end - ---- Get the URL for the latest in a set of versions. --- @param name string: The package name to be used in the URL. --- @param versions table: An array of version informations, as stored --- in search results tables. --- @return string or nil: the URL for the latest version if one could --- be picked, or nil. -local function pick_latest_version(name, versions) - assert(type(name) == "string") - assert(type(versions) == "table") - - local vtables = {} - for v, _ in pairs(versions) do - table.insert(vtables, deps.parse_version(v)) - end - table.sort(vtables) - local version = vtables[#vtables].string - local items = versions[version] - if items then - local pick = 1 - for i, item in ipairs(items) do - if (item.arch == 'src' and items[pick].arch == 'rockspec') - or (item.arch ~= 'src' and item.arch ~= 'rockspec') then - pick = i - end - end - return path.make_url(items[pick].repo, name, version, items[pick].arch) - end - return nil -end - --- Find out which other Lua versions provide rock versions matching a query, --- @param query table: A dependency query matching a single rock. --- @return table: array of Lua versions supported, in "5.x" format. -local function supported_lua_versions(query) - local results = {} - - for lua_version in util.lua_versions() do - if lua_version ~= cfg.lua_version then - if search.search_repos(query, lua_version)[query.name] then - table.insert(results, lua_version) - end - end - end - - return results -end - ---- Attempt to get a single URL for a given search for a rock. --- @param query table: A dependency query matching a single rock. --- @return string or (nil, string): URL for latest matching version --- of the rock if it was found, or nil followed by an error message. -function search.find_suitable_rock(query) - assert(type(query) == "table") - - local results = search.search_repos(query) - local first_rock = next(results) - if not first_rock then - if cfg.rocks_provided[query.name] == nil then - -- Check if constraints are satisfiable with other Lua versions. - local lua_versions = supported_lua_versions(query) - - if #lua_versions ~= 0 then - -- Build a nice message in "only Lua 5.x and 5.y but not 5.z." format - for i, lua_version in ipairs(lua_versions) do - lua_versions[i] = "Lua "..lua_version - end - - local versions_message = "only "..table.concat(lua_versions, " and ").. - " but not Lua "..cfg.lua_version.."." - - if #query.constraints == 0 then - return nil, query.name.." supports "..versions_message - elseif #query.constraints == 1 and query.constraints[1].op == "==" then - return nil, query.name.." "..query.constraints[1].version.string.." supports "..versions_message - else - return nil, "Matching "..query.name.." versions support "..versions_message - end - end - end - - return nil, "No results matching query were found." - elseif next(results, first_rock) then - -- Shouldn't happen as query must match only one package. - return nil, "Several rocks matched query." - elseif cfg.rocks_provided[query.name] ~= nil then - -- Do not install versions listed in cfg.rocks_provided. - return nil, "Rock "..query.name.." "..cfg.rocks_provided[query.name].. - " was found but it is provided by VM or 'rocks_provided' in the config file." - else - return pick_latest_version(query.name, results[first_rock]) - end -end - ---- Print a list of rocks/rockspecs on standard output. --- @param results table: A table where keys are package names and versions --- are tables matching version strings to an array of rocks servers. --- @param porcelain boolean or nil: A flag to force machine-friendly output. -function search.print_results(results, porcelain) - assert(type(results) == "table") - assert(type(porcelain) == "boolean" or not porcelain) - - for package, versions in util.sortedpairs(results) do - if not porcelain then - util.printout(package) - end - for version, repos in util.sortedpairs(versions, deps.compare_versions) do - for _, repo in ipairs(repos) do - repo.repo = dir.normalize(repo.repo) - if porcelain then - util.printout(package, version, repo.arch, repo.repo) - else - util.printout(" "..version.." ("..repo.arch..") - "..repo.repo) - end - end - end - if not porcelain then - util.printout() - end - end -end - ---- Splits a list of search results into two lists, one for "source" results --- to be used with the "build" command, and one for "binary" results to be --- used with the "install" command. --- @param results table: A search results table. --- @return (table, table): Two tables, one for source and one for binary --- results. -local function split_source_and_binary_results(results) - local sources, binaries = {}, {} - for name, versions in pairs(results) do - for version, repositories in pairs(versions) do - for _, repo in ipairs(repositories) do - local where = sources - if repo.arch == "all" or repo.arch == cfg.arch then - where = binaries - end - store_result(where, name, version, repo.arch, repo.repo) - end - end - end - return sources, binaries -end - ---- Given a name and optionally a version, try to find in the rocks --- servers a single .src.rock or .rockspec file that satisfies --- the request, and run the given function on it; or display to the --- user possibilities if it couldn't narrow down a single match. --- @param action function: A function that takes a .src.rock or --- .rockspec URL as a parameter. --- @param name string: A rock name --- @param version string or nil: A version number may also be given. --- @return The result of the action function, or nil and an error message. -function search.act_on_src_or_rockspec(action, name, version, ...) - assert(type(action) == "function") - assert(type(name) == "string") - assert(type(version) == "string" or not version) - - local query = search.make_query(name, version) - query.arch = "src|rockspec" - local url, err = search.find_suitable_rock(query) - if not url then - return nil, "Could not find a result named "..name..(version and " "..version or "")..": "..err - end - return action(url, ...) -end - -function search.pick_installed_rock(name, version, given_tree) - local results = {} - local query = search.make_query(name, version) - query.exact_name = true - local tree_map = {} - local trees = cfg.rocks_trees - if given_tree then - trees = { given_tree } - end - for _, tree in ipairs(trees) do - local rocks_dir = path.rocks_dir(tree) - tree_map[rocks_dir] = tree - search.manifest_search(results, rocks_dir, query) - end - - if not next(results) then -- - return nil,"cannot find package "..name.." "..(version or "").."\nUse 'list' to find installed rocks." - end - - version = nil - local repo_url - local package, versions = util.sortedpairs(results)() - --question: what do we do about multiple versions? This should - --give us the latest version on the last repo (which is usually the global one) - for vs, repositories in util.sortedpairs(versions, deps.compare_versions) do - if not version then version = vs end - for _, rp in ipairs(repositories) do repo_url = rp.repo end - end - - local repo = tree_map[repo_url] - return name, version, repo, repo_url -end - ---- Driver function for "search" command. --- @param name string: A substring of a rock name to search. --- @param version string or nil: a version may also be passed. --- @return boolean or (nil, string): True if build was successful; nil and an --- error message otherwise. -function search.command(flags, name, version) - if flags["all"] then - name, version = "", nil - end - - if type(name) ~= "string" and not flags["all"] then - return nil, "Enter name and version or use --all. "..util.see_help("search") - end - - local query = search.make_query(name:lower(), version) - query.exact_name = false - local results, err = search.search_repos(query) - local porcelain = flags["porcelain"] - util.title("Search results:", porcelain, "=") - local sources, binaries = split_source_and_binary_results(results) - if next(sources) and not flags["binary"] then - util.title("Rockspecs and source rocks:", porcelain) - search.print_results(sources, porcelain) - end - if next(binaries) and not flags["source"] then - util.title("Binary and pure-Lua rocks:", porcelain) - search.print_results(binaries, porcelain) - end - return true -end - -return search diff --git a/src/luarocks/show.lua b/src/luarocks/show.lua deleted file mode 100644 index 1ff81e08..00000000 --- a/src/luarocks/show.lua +++ /dev/null @@ -1,158 +0,0 @@ ---- Module implementing the LuaRocks "show" command. --- Shows information about an installed rock. -local show = {} - -local search = require("luarocks.search") -local cfg = require("luarocks.core.cfg") -local util = require("luarocks.util") -local path = require("luarocks.path") -local deps = require("luarocks.deps") -local fetch = require("luarocks.fetch") -local manif = require("luarocks.manif") - -show.help_summary = "Show information about an installed rock." - -show.help = [[ - is an existing package name. -Without any flags, show all module information. -With these flags, return only the desired information: - ---home home page of project ---modules all modules provided by this package as used by require() ---deps packages this package depends on ---rockspec the full path of the rockspec file ---mversion the package version ---rock-tree local tree where rock is installed ---rock-dir data directory of the installed rock -]] - -local function keys_as_string(t, sep) - local keys = util.keys(t) - table.sort(keys) - return table.concat(keys, sep or " ") -end - -local function word_wrap(line) - local width = tonumber(os.getenv("COLUMNS")) or 80 - if width > 80 then width = 80 end - if #line > width then - local brk = width - while brk > 0 and line:sub(brk, brk) ~= " " do - brk = brk - 1 - end - if brk > 0 then - return line:sub(1, brk-1) .. "\n" .. word_wrap(line:sub(brk+1)) - end - end - return line -end - -local function format_text(text) - text = text:gsub("^%s*",""):gsub("%s$", ""):gsub("\n[ \t]+","\n"):gsub("([^\n])\n([^\n])","%1 %2") - local paragraphs = util.split_string(text, "\n\n") - for n, line in ipairs(paragraphs) do - paragraphs[n] = word_wrap(line) - end - return (table.concat(paragraphs, "\n\n"):gsub("%s$", "")) -end - -local function installed_rock_label(name, tree) - local installed, version - if cfg.rocks_provided[name] then - installed, version = true, cfg.rocks_provided[name] - else - installed, version = search.pick_installed_rock(name, nil, tree) - end - return installed and "(using "..version..")" or "(missing)" -end - ---- Driver function for "show" command. --- @param name or nil: an existing package name. --- @param version string or nil: a version may also be passed. --- @return boolean: True if succeeded, nil on errors. -function show.command(flags, name, version) - if not name then - return nil, "Argument missing. "..util.see_help("show") - end - - local repo, repo_url - name, version, repo, repo_url = search.pick_installed_rock(name:lower(), version, flags["tree"]) - if not name then - return nil, version - end - - local directory = path.install_dir(name,version,repo) - local rockspec_file = path.rockspec_file(name, version, repo) - local rockspec, err = fetch.load_local_rockspec(rockspec_file) - if not rockspec then return nil,err end - - local descript = rockspec.description or {} - local manifest, err = manif.load_manifest(repo_url) - if not manifest then return nil,err end - local minfo = manifest.repository[name][version][1] - - if flags["rock-tree"] then util.printout(path.rocks_tree_to_string(repo)) - elseif flags["rock-dir"] then util.printout(directory) - elseif flags["home"] then util.printout(descript.homepage) - elseif flags["issues"] then util.printout(descript.issues_url) - elseif flags["labels"] then util.printout(descript.labels and table.concat(descript.labels, "\n")) - elseif flags["modules"] then util.printout(keys_as_string(minfo.modules, "\n")) - elseif flags["deps"] then util.printout(keys_as_string(minfo.dependencies)) - elseif flags["rockspec"] then util.printout(rockspec_file) - elseif flags["mversion"] then util.printout(version) - else - util.printout() - util.printout(rockspec.package.." "..rockspec.version.." - "..(descript.summary or "")) - util.printout() - if descript.detailed then - util.printout(format_text(descript.detailed)) - util.printout() - end - if descript.license then - util.printout("License: ", descript.license) - end - if descript.homepage then - util.printout("Homepage: ", descript.homepage) - end - if descript.issues_url then - util.printout("Issues: ", descript.issues_url) - end - if descript.labels then - util.printout("Labels: ", table.concat(descript.labels, ", ")) - end - util.printout("Installed in: ", path.rocks_tree_to_string(repo)) - if next(minfo.modules) then - util.printout() - util.printout("Modules:") - for mod, filename in util.sortedpairs(minfo.modules) do - util.printout("\t"..mod.." ("..path.which(mod, filename, name, version, repo, manifest)..")") - end - end - local direct_deps = {} - if #rockspec.dependencies > 0 then - util.printout() - util.printout("Depends on:") - for _, dep in ipairs(rockspec.dependencies) do - direct_deps[dep.name] = true - util.printout("\t"..deps.show_dep(dep).." "..installed_rock_label(dep.name, flags["tree"])) - end - end - local has_indirect_deps - for dep_name in util.sortedpairs(minfo.dependencies) do - if not direct_deps[dep_name] then - if not has_indirect_deps then - util.printout() - util.printout("Indirectly pulling:") - has_indirect_deps = true - end - - util.printout("\t"..dep_name.." "..installed_rock_label(dep_name, flags["tree"])) - end - end - util.printout() - end - return true -end - - -return show diff --git a/src/luarocks/unpack.lua b/src/luarocks/unpack.lua deleted file mode 100644 index c50701b0..00000000 --- a/src/luarocks/unpack.lua +++ /dev/null @@ -1,164 +0,0 @@ - ---- Module implementing the LuaRocks "unpack" command. --- Unpack the contents of a rock. -local unpack = {} - -local fetch = require("luarocks.fetch") -local fs = require("luarocks.fs") -local util = require("luarocks.util") -local build = require("luarocks.build") -local dir = require("luarocks.dir") -local cfg = require("luarocks.core.cfg") - -unpack.help_summary = "Unpack the contents of a rock." -unpack.help_arguments = "[--force] {| []}" -unpack.help = [[ -Unpacks the contents of a rock in a newly created directory. -Argument may be a rock file, or the name of a rock in a rocks server. -In the latter case, the app version may be given as a second argument. - ---force Unpack files even if the output directory already exists. -]] - ---- Load a rockspec file to the given directory, fetches the source --- files specified in the rockspec, and unpack them inside the directory. --- @param rockspec_file string: The URL for a rockspec file. --- @param dir_name string: The directory where to store and unpack files. --- @return table or (nil, string): the loaded rockspec table or --- nil and an error message. -local function unpack_rockspec(rockspec_file, dir_name) - assert(type(rockspec_file) == "string") - assert(type(dir_name) == "string") - - local rockspec, err = fetch.load_rockspec(rockspec_file) - if not rockspec then - return nil, "Failed loading rockspec "..rockspec_file..": "..err - end - local ok, err = fs.change_dir(dir_name) - if not ok then return nil, err end - local ok, sources_dir = fetch.fetch_sources(rockspec, true, ".") - if not ok then - return nil, sources_dir - end - ok, err = fs.change_dir(sources_dir) - if not ok then return nil, err end - ok, err = build.apply_patches(rockspec) - fs.pop_dir() - if not ok then return nil, err end - return rockspec -end - ---- Load a .rock file to the given directory and unpack it inside it. --- @param rock_file string: The URL for a .rock file. --- @param dir_name string: The directory where to unpack. --- @param kind string: the kind of rock file, as in the second-level --- extension in the rock filename (eg. "src", "all", "linux-x86") --- @return table or (nil, string): the loaded rockspec table or --- nil and an error message. -local function unpack_rock(rock_file, dir_name, kind) - assert(type(rock_file) == "string") - assert(type(dir_name) == "string") - - local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, dir_name) - if not ok then - return nil, "Failed unzipping rock "..rock_file, errcode - end - ok, err = fs.change_dir(dir_name) - if not ok then return nil, err end - local rockspec_file = dir_name..".rockspec" - local rockspec, err = fetch.load_rockspec(rockspec_file) - if not rockspec then - return nil, "Failed loading rockspec "..rockspec_file..": "..err - end - if kind == "src" then - if rockspec.source.file then - local ok, err = fs.unpack_archive(rockspec.source.file) - if not ok then - return nil, err - end - ok, err = fs.change_dir(rockspec.source.dir) - if not ok then return nil, err end - ok, err = build.apply_patches(rockspec) - fs.pop_dir() - if not ok then return nil, err end - end - end - return rockspec -end - ---- Create a directory and perform the necessary actions so that --- the sources for the rock and its rockspec are unpacked inside it, --- laid out properly so that the 'make' command is able to build the module. --- @param file string: A rockspec or .rock URL. --- @return boolean or (nil, string): true if successful or nil followed --- by an error message. -local function run_unpacker(file, force) - assert(type(file) == "string") - - local base_name = dir.base_name(file) - local dir_name, kind, extension = base_name:match("(.*)%.([^.]+)%.(rock)$") - if not extension then - dir_name, extension = base_name:match("(.*)%.(rockspec)$") - kind = "rockspec" - end - if not extension then - return nil, file.." does not seem to be a valid filename." - end - - local exists = fs.exists(dir_name) - if exists and not force then - return nil, "Directory "..dir_name.." already exists." - end - if not exists then - local ok, err = fs.make_dir(dir_name) - if not ok then return nil, err end - end - local rollback = util.schedule_function(fs.delete, fs.absolute_name(dir_name)) - - local rockspec, err - if extension == "rock" then - rockspec, err = unpack_rock(file, dir_name, kind) - elseif extension == "rockspec" then - rockspec, err = unpack_rockspec(file, dir_name) - end - if not rockspec then - return nil, err - end - if kind == "src" or kind == "rockspec" then - if rockspec.source.dir ~= "." then - local ok = fs.copy(rockspec.local_filename, rockspec.source.dir, cfg.perm_read) - if not ok then - return nil, "Failed copying unpacked rockspec into unpacked source directory." - end - end - util.printout() - util.printout("Done. You may now enter directory ") - util.printout(dir.path(dir_name, rockspec.source.dir)) - util.printout("and type 'luarocks make' to build.") - end - util.remove_scheduled_function(rollback) - return true -end - ---- Driver function for the "unpack" command. --- @param name string: may be a rock filename, for unpacking a --- rock file or the name of a rock to be fetched and unpacked. --- @param version string or nil: if the name of a package is given, a --- version may also be passed. --- @return boolean or (nil, string): true if successful or nil followed --- by an error message. -function unpack.command(flags, name, version) - assert(type(version) == "string" or not version) - if type(name) ~= "string" then - return nil, "Argument missing. "..util.see_help("unpack") - end - - if name:match(".*%.rock") or name:match(".*%.rockspec") then - return run_unpacker(name, flags["force"]) - else - local search = require("luarocks.search") - return search.act_on_src_or_rockspec(run_unpacker, name:lower(), version) - end -end - -return unpack diff --git a/src/luarocks/upload.lua b/src/luarocks/upload.lua deleted file mode 100644 index baee47ab..00000000 --- a/src/luarocks/upload.lua +++ /dev/null @@ -1,94 +0,0 @@ - -local upload = {} - -local util = require("luarocks.util") -local fetch = require("luarocks.fetch") -local pack = require("luarocks.pack") -local cfg = require("luarocks.core.cfg") -local Api = require("luarocks.upload.api") - -upload.help_summary = "Upload a rockspec to the public rocks repository." -upload.help_arguments = "[--skip-pack] [--api-key=] [--force] " -upload.help = [[ - Pack a source rock file (.src.rock extension), - upload rockspec and source rock to server. ---skip-pack Do not pack and send source rock. ---api-key= Give it an API key. It will be stored for subsequent uses. ---force Replace existing rockspec if the same revision of - a module already exists. This should be used only - in case of upload mistakes: when updating a rockspec, - increment the revision number instead. -]] - -function upload.command(flags, fname) - if not fname then - return nil, "Missing rockspec. "..util.see_help("upload") - end - - local api, err = Api.new(flags) - if not api then - return nil, err - end - if cfg.verbose then - api.debug = true - end - - local rockspec, err, errcode = fetch.load_rockspec(fname) - if err then - return nil, err, errcode - end - - util.printout("Sending " .. tostring(fname) .. " ...") - local res, err = api:method("check_rockspec", { - package = rockspec.package, - version = rockspec.version - }) - if not res then return nil, err end - - if not res.module then - util.printout("Will create new module (" .. tostring(rockspec.package) .. ")") - end - if res.version and not flags["force"] then - return nil, "Revision "..rockspec.version.." already exists on the server. "..util.see_help("upload") - end - - local rock_fname - if not flags["skip-pack"] and not rockspec.version:match("^scm") then - util.printout("Packing " .. tostring(rockspec.package)) - rock_fname, err = pack.pack_source_rock(fname) - if not rock_fname then - return nil, err - end - end - - local multipart = require("luarocks.upload.multipart") - - res, err = api:method("upload", nil, { - rockspec_file = multipart.new_file(fname) - }) - if not res then return nil, err end - - if res.is_new and #res.manifests == 0 then - util.printerr("Warning: module not added to root manifest due to name taken.") - end - - local module_url = res.module_url - - if rock_fname then - if (not res.version) or (not res.version.id) then - return nil, "Invalid response from server." - end - util.printout(("Sending " .. tostring(rock_fname) .. " ...")) - res, err = api:method("upload_rock/" .. ("%d"):format(res.version.id), nil, { - rock_file = multipart.new_file(rock_fname) - }) - if not res then return nil, err end - end - - util.printout() - util.printout("Done: " .. tostring(module_url)) - util.printout() - return true -end - -return upload diff --git a/src/luarocks/write_rockspec.lua b/src/luarocks/write_rockspec.lua deleted file mode 100644 index be563eaa..00000000 --- a/src/luarocks/write_rockspec.lua +++ /dev/null @@ -1,376 +0,0 @@ - -local write_rockspec = {} - -local cfg = require("luarocks.core.cfg") -local dir = require("luarocks.dir") -local fetch = require("luarocks.fetch") -local fs = require("luarocks.fs") -local path = require("luarocks.path") -local persist = require("luarocks.persist") -local type_check = require("luarocks.type_check") -local util = require("luarocks.util") -local deps = require("luarocks.deps") - -write_rockspec.help_summary = "Write a template for a rockspec file." -write_rockspec.help_arguments = "[--output= ...] [] [] [|]" -write_rockspec.help = [[ -This command writes an initial version of a rockspec file, -based on a name, a version, and a location (an URL or a local path). -If only two arguments are given, the first one is considered the name and the -second one is the location. -If only one argument is given, it must be the location. -If no arguments are given, current directory is used as location. -LuaRocks will attempt to infer name and version if not given, -using 'scm' as default version. - -Note that the generated file is a _starting point_ for writing a -rockspec, and is not guaranteed to be complete or correct. - ---output= Write the rockspec with the given filename. - If not given, a file is written in the current - directory with a filename based on given name and version. ---license="" A license string, such as "MIT/X11" or "GNU GPL v3". ---summary="" A short one-line description summary. ---detailed="" A longer description string. ---homepage= Project homepage. ---lua-version= Supported Lua versions. Accepted values are "5.1", "5.2", - "5.3", "5.1,5.2", "5.2,5.3", or "5.1,5.2,5.3". ---rockspec-format= Rockspec format version, such as "1.0" or "1.1". ---tag= Tag to use. Will attempt to extract version number from it. ---lib=[,] A comma-separated list of libraries that C files need to - link to. -]] - -local function open_file(name) - return io.open(dir.path(fs.current_dir(), name), "r") -end - -local function get_url(rockspec) - local file, temp_dir, err_code, err_file, err_temp_dir = fetch.fetch_sources(rockspec, false) - if err_code == "source.dir" then - file, temp_dir = err_file, err_temp_dir - elseif not file then - util.warning("Could not fetch sources - "..temp_dir) - return false - end - util.printout("File successfully downloaded. Making checksum and checking base dir...") - if fetch.is_basic_protocol(rockspec.source.protocol) then - rockspec.source.md5 = fs.get_md5(file) - end - local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, rockspec.source.url) - return true, found_dir or inferred_dir, temp_dir -end - -local function configure_lua_version(rockspec, luaver) - if luaver == "5.1" then - table.insert(rockspec.dependencies, "lua ~> 5.1") - elseif luaver == "5.2" then - table.insert(rockspec.dependencies, "lua ~> 5.2") - elseif luaver == "5.3" then - table.insert(rockspec.dependencies, "lua ~> 5.3") - elseif luaver == "5.1,5.2" then - table.insert(rockspec.dependencies, "lua >= 5.1, < 5.3") - elseif luaver == "5.2,5.3" then - table.insert(rockspec.dependencies, "lua >= 5.2, < 5.4") - elseif luaver == "5.1,5.2,5.3" then - table.insert(rockspec.dependencies, "lua >= 5.1, < 5.4") - else - util.warning("Please specify supported Lua version with --lua-version=. "..util.see_help("write_rockspec")) - end -end - -local function detect_description() - local fd = open_file("README.md") or open_file("README") - if not fd then return end - local data = fd:read("*a") - fd:close() - local paragraph = data:match("\n\n([^%[].-)\n\n") - if not paragraph then paragraph = data:match("\n\n(.*)") end - local summary, detailed - if paragraph then - detailed = paragraph - - if #paragraph < 80 then - summary = paragraph:gsub("\n", "") - else - summary = paragraph:gsub("\n", " "):match("([^.]*%.) ") - end - end - return summary, detailed -end - -local function detect_mit_license(data) - local strip_copyright = (data:gsub("Copyright [^\n]*\n", "")) - local sum = 0 - for i = 1, #strip_copyright do - local num = string.byte(strip_copyright:sub(i,i)) - if num > 32 and num <= 128 then - sum = sum + num - end - end - return sum == 78656 -end - -local simple_scm_protocols = { - git = true, ["git+http"] = true, ["git+https"] = true, - hg = true, ["hg+http"] = true, ["hg+https"] = true -} - -local function detect_url_from_command(program, args, directory) - local command = fs.Q(cfg.variables[program:upper()]).. " "..args - local pipe = io.popen(fs.command_at(directory, fs.quiet_stderr(command))) - if not pipe then return nil end - local url = pipe:read("*a"):match("^([^\r\n]+)") - pipe:close() - if not url then return nil end - if not util.starts_with(url, program.."://") then - url = program.."+"..url - end - - if simple_scm_protocols[dir.split_url(url)] then - return url - end -end - -local function detect_scm_url(directory) - return detect_url_from_command("git", "config --get remote.origin.url", directory) or - detect_url_from_command("hg", "paths default", directory) -end - -local function show_license(rockspec) - local fd = open_file("COPYING") or open_file("LICENSE") or open_file("MIT-LICENSE.txt") - if not fd then return nil end - local data = fd:read("*a") - fd:close() - local is_mit = detect_mit_license(data) - util.title("License for "..rockspec.package..":") - util.printout(data) - util.printout() - return is_mit -end - -local function get_cmod_name(file) - local fd = open_file(file) - if not fd then return nil end - local data = fd:read("*a") - fd:close() - return (data:match("int%s+luaopen_([a-zA-Z0-9_]+)")) -end - -local luamod_blacklist = { - test = true, - tests = true, -} - -local function fill_as_builtin(rockspec, libs) - rockspec.build.type = "builtin" - rockspec.build.modules = {} - local prefix = "" - - for _, parent in ipairs({"src", "lua"}) do - if fs.is_dir(parent) then - fs.change_dir(parent) - prefix = parent.."/" - break - end - end - - local incdirs, libdirs - if libs then - incdirs, libdirs = {}, {} - for _, lib in ipairs(libs) do - local upper = lib:upper() - incdirs[#incdirs+1] = "$("..upper.."_INCDIR)" - libdirs[#libdirs+1] = "$("..upper.."_LIBDIR)" - end - end - - for _, file in ipairs(fs.find()) do - local luamod = file:match("(.*)%.lua$") - if luamod and not luamod_blacklist[luamod] then - rockspec.build.modules[path.path_to_module(file)] = prefix..file - else - local cmod = file:match("(.*)%.c$") - if cmod then - local modname = get_cmod_name(file) or path.path_to_module(file:gsub("%.c$", ".lua")) - rockspec.build.modules[modname] = { - sources = prefix..file, - libraries = libs, - incdirs = incdirs, - libdirs = libdirs, - } - end - end - end - - for _, directory in ipairs({ "doc", "docs", "samples", "tests" }) do - if fs.is_dir(directory) then - if not rockspec.build.copy_directories then - rockspec.build.copy_directories = {} - end - table.insert(rockspec.build.copy_directories, directory) - end - end - - if prefix ~= "" then - fs.pop_dir() - end -end - -local function rockspec_cleanup(rockspec) - rockspec.source.file = nil - rockspec.source.protocol = nil - rockspec.variables = nil - rockspec.name = nil - rockspec.format_is_at_least = nil -end - -function write_rockspec.command(flags, name, version, url_or_dir) - if not name then - url_or_dir = "." - elseif not version then - url_or_dir = name - name = nil - elseif not url_or_dir then - url_or_dir = version - version = nil - end - - if flags["tag"] then - if not version then - version = flags["tag"]:gsub("^v", "") - end - end - - local protocol, pathname = dir.split_url(url_or_dir) - if protocol == "file" then - if pathname == "." then - name = name or dir.base_name(fs.current_dir()) - end - elseif fetch.is_basic_protocol(protocol) then - local filename = dir.base_name(url_or_dir) - local newname, newversion = filename:match("(.*)-([^-]+)") - if newname then - name = name or newname - version = version or newversion:gsub("%.[a-z]+$", ""):gsub("%.tar$", "") - end - else - name = name or dir.base_name(url_or_dir):gsub("%.[^.]+$", "") - end - - if not name then - return nil, "Could not infer rock name. "..util.see_help("write_rockspec") - end - version = version or "scm" - - local filename = flags["output"] or dir.path(fs.current_dir(), name:lower().."-"..version.."-1.rockspec") - - local rockspec = { - rockspec_format = flags["rockspec-format"], - package = name, - name = name:lower(), - version = version.."-1", - source = { - url = "*** please add URL for source tarball, zip or repository here ***", - tag = flags["tag"], - }, - description = { - summary = flags["summary"] or "*** please specify description summary ***", - detailed = flags["detailed"] or "*** please enter a detailed description ***", - homepage = flags["homepage"] or "*** please enter a project homepage ***", - license = flags["license"] or "*** please specify a license ***", - }, - dependencies = {}, - build = {}, - } - path.configure_paths(rockspec) - rockspec.source.protocol = protocol - rockspec.format_is_at_least = deps.format_is_at_least - - configure_lua_version(rockspec, flags["lua-version"]) - - local local_dir = url_or_dir - - if url_or_dir:match("://") then - rockspec.source.url = url_or_dir - rockspec.source.file = dir.base_name(url_or_dir) - rockspec.source.dir = "dummy" - if not fetch.is_basic_protocol(rockspec.source.protocol) then - if version ~= "scm" then - rockspec.source.tag = flags["tag"] or "v" .. version - end - end - rockspec.source.dir = nil - local ok, base_dir, temp_dir = get_url(rockspec) - if ok then - if base_dir ~= dir.base_name(url_or_dir) then - rockspec.source.dir = base_dir - end - end - if base_dir then - local_dir = dir.path(temp_dir, base_dir) - else - local_dir = nil - end - else - rockspec.source.url = detect_scm_url(local_dir) or rockspec.source.url - end - - if not local_dir then - local_dir = "." - end - - if not flags["homepage"] then - local url_protocol, url_path = dir.split_url(rockspec.source.url) - - if simple_scm_protocols[url_protocol] then - for _, domain in ipairs({"github.com", "bitbucket.org", "gitlab.com"}) do - if util.starts_with(url_path, domain) then - rockspec.description.homepage = "https://"..url_path:gsub("%.git$", "") - break - end - end - end - end - - local libs = nil - if flags["lib"] then - libs = {} - rockspec.external_dependencies = {} - for lib in flags["lib"]:gmatch("([^,]+)") do - table.insert(libs, lib) - rockspec.external_dependencies[lib:upper()] = { - library = lib - } - end - end - - local ok, err = fs.change_dir(local_dir) - if not ok then return nil, "Failed reaching files from project - error entering directory "..local_dir end - - if (not flags["summary"]) or (not flags["detailed"]) then - local summary, detailed = detect_description() - rockspec.description.summary = flags["summary"] or summary - rockspec.description.detailed = flags["detailed"] or detailed - end - - local is_mit = show_license(rockspec) - - if is_mit and not flags["license"] then - rockspec.description.license = "MIT" - end - - fill_as_builtin(rockspec, libs) - - rockspec_cleanup(rockspec) - - persist.save_from_table(filename, rockspec, type_check.rockspec_order) - - util.printout() - util.printout("Wrote template at "..filename.." -- you should now edit and finish it.") - util.printout() - - return true -end - -return write_rockspec -- cgit v1.2.3-55-g6feb From 91b7c126a9dcbd9e8f2b37cf07f8e064db2bebb3 Mon Sep 17 00:00:00 2001 From: Hisham Date: Fri, 28 Oct 2016 20:45:38 -0200 Subject: Move files that will live in base dir back. --- src/luarocks/base/build.lua | 443 -------------------------------- src/luarocks/base/config_cmd.lua | 71 ------ src/luarocks/base/doc.lua | 155 ----------- src/luarocks/base/download.lua | 107 -------- src/luarocks/base/help.lua | 117 --------- src/luarocks/base/install.lua | 183 ------------- src/luarocks/base/lint.lua | 53 ---- src/luarocks/base/list.lua | 95 ------- src/luarocks/base/make.lua | 86 ------- src/luarocks/base/new_version.lua | 199 --------------- src/luarocks/base/pack.lua | 193 -------------- src/luarocks/base/path_cmd.lua | 68 ----- src/luarocks/base/purge.lua | 77 ------ src/luarocks/base/remove.lua | 165 ------------ src/luarocks/base/search.lua | 482 ----------------------------------- src/luarocks/base/show.lua | 158 ------------ src/luarocks/base/unpack.lua | 164 ------------ src/luarocks/base/upload.lua | 94 ------- src/luarocks/base/write_rockspec.lua | 376 --------------------------- src/luarocks/build.lua | 443 ++++++++++++++++++++++++++++++++ src/luarocks/download.lua | 107 ++++++++ src/luarocks/pack.lua | 193 ++++++++++++++ src/luarocks/remove.lua | 165 ++++++++++++ src/luarocks/search.lua | 482 +++++++++++++++++++++++++++++++++++ 24 files changed, 1390 insertions(+), 3286 deletions(-) delete mode 100644 src/luarocks/base/build.lua delete mode 100644 src/luarocks/base/config_cmd.lua delete mode 100644 src/luarocks/base/doc.lua delete mode 100644 src/luarocks/base/download.lua delete mode 100644 src/luarocks/base/help.lua delete mode 100644 src/luarocks/base/install.lua delete mode 100644 src/luarocks/base/lint.lua delete mode 100644 src/luarocks/base/list.lua delete mode 100644 src/luarocks/base/make.lua delete mode 100644 src/luarocks/base/new_version.lua delete mode 100644 src/luarocks/base/pack.lua delete mode 100644 src/luarocks/base/path_cmd.lua delete mode 100644 src/luarocks/base/purge.lua delete mode 100644 src/luarocks/base/remove.lua delete mode 100644 src/luarocks/base/search.lua delete mode 100644 src/luarocks/base/show.lua delete mode 100644 src/luarocks/base/unpack.lua delete mode 100644 src/luarocks/base/upload.lua delete mode 100644 src/luarocks/base/write_rockspec.lua create mode 100644 src/luarocks/build.lua create mode 100644 src/luarocks/download.lua create mode 100644 src/luarocks/pack.lua create mode 100644 src/luarocks/remove.lua create mode 100644 src/luarocks/search.lua diff --git a/src/luarocks/base/build.lua b/src/luarocks/base/build.lua deleted file mode 100644 index f3b054d2..00000000 --- a/src/luarocks/base/build.lua +++ /dev/null @@ -1,443 +0,0 @@ - ---- Module implementing the LuaRocks "build" command. --- Builds a rock, compiling its C parts if any. -local build = {} - -local pack = require("luarocks.pack") -local path = require("luarocks.path") -local util = require("luarocks.util") -local repos = require("luarocks.repos") -local fetch = require("luarocks.fetch") -local fs = require("luarocks.fs") -local dir = require("luarocks.dir") -local deps = require("luarocks.deps") -local writer = require("luarocks.manif.writer") -local remove = require("luarocks.remove") -local cfg = require("luarocks.core.cfg") - -build.help_summary = "Build/compile a rock." -build.help_arguments = "[--pack-binary-rock] [--keep] {|| []}" -build.help = [[ -Build and install a rock, compiling its C parts if any. -Argument may be a rockspec file, a source rock file -or the name of a rock to be fetched from a repository. - ---pack-binary-rock Do not install rock. Instead, produce a .rock file - with the contents of compilation in the current - directory. - ---keep Do not remove previously installed versions of the - rock after building a new one. This behavior can - be made permanent by setting keep_other_versions=true - in the configuration file. - ---branch= Override the `source.branch` field in the loaded - rockspec. Allows to specify a different branch to - fetch. Particularly for SCM rocks. - ---only-deps Installs only the dependencies of the rock. - -]]..util.deps_mode_help() - ---- Install files to a given location. --- Takes a table where the array part is a list of filenames to be copied. --- In the hash part, other keys, if is_module_path is set, are identifiers --- in Lua module format, to indicate which subdirectory the file should be --- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo") --- will copy src/bar.lua to boo/foo. --- @param files table or nil: A table containing a list of files to copy in --- the format described above. If nil is passed, this function is a no-op. --- Directories should be delimited by forward slashes as in internet URLs. --- @param location string: The base directory files should be copied to. --- @param is_module_path boolean: True if string keys in files should be --- interpreted as dotted module paths. --- @param perms string: Permissions of the newly created files installed. --- Directories are always created with the default permissions. --- @return boolean or (nil, string): True if succeeded or --- nil and an error message. -local function install_files(files, location, is_module_path, perms) - assert(type(files) == "table" or not files) - assert(type(location) == "string") - if files then - for k, file in pairs(files) do - local dest = location - local filename = dir.base_name(file) - if type(k) == "string" then - local modname = k - if is_module_path then - dest = dir.path(location, path.module_to_path(modname)) - local ok, err = fs.make_dir(dest) - if not ok then return nil, err end - if filename:match("%.lua$") then - local basename = modname:match("([^.]+)$") - filename = basename..".lua" - end - else - dest = dir.path(location, dir.dir_name(modname)) - local ok, err = fs.make_dir(dest) - if not ok then return nil, err end - filename = dir.base_name(modname) - end - else - local ok, err = fs.make_dir(dest) - if not ok then return nil, err end - end - local ok = fs.copy(dir.path(file), dir.path(dest, filename), perms) - if not ok then - return nil, "Failed copying "..file - end - end - end - return true -end - ---- Write to the current directory the contents of a table, --- where each key is a file name and its value is the file content. --- @param files table: The table of files to be written. -local function extract_from_rockspec(files) - for name, content in pairs(files) do - local fd = io.open(dir.path(fs.current_dir(), name), "w+") - fd:write(content) - fd:close() - end -end - ---- Applies patches inlined in the build.patches section --- and extracts files inlined in the build.extra_files section --- of a rockspec. --- @param rockspec table: A rockspec table. --- @return boolean or (nil, string): True if succeeded or --- nil and an error message. -function build.apply_patches(rockspec) - assert(type(rockspec) == "table") - - local build_spec = rockspec.build - if build_spec.extra_files then - extract_from_rockspec(build_spec.extra_files) - end - if build_spec.patches then - extract_from_rockspec(build_spec.patches) - for patch, patchdata in util.sortedpairs(build_spec.patches) do - util.printout("Applying patch "..patch.."...") - local ok, err = fs.apply_patch(tostring(patch), patchdata) - if not ok then - return nil, "Failed applying patch "..patch - end - end - end - return true -end - -local function install_default_docs(name, version) - local patterns = { "readme", "license", "copying", ".*%.md" } - local dest = dir.path(path.install_dir(name, version), "doc") - local has_dir = false - for file in fs.dir() do - for _, pattern in ipairs(patterns) do - if file:lower():match("^"..pattern) then - if not has_dir then - fs.make_dir(dest) - has_dir = true - end - fs.copy(file, dest, cfg.perm_read) - break - end - end - end -end - -local function check_macosx_deployment_target(rockspec) - local target = rockspec.build.macosx_deployment_target - local function minor(version) - return tonumber(version and version:match("^[^.]+%.([^.]+)")) - end - local function patch_variable(var, target) - if rockspec.variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then - rockspec.variables[var] = (rockspec.variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET="..target) - else - rockspec.variables[var] = "env MACOSX_DEPLOYMENT_TARGET="..target.." "..rockspec.variables[var] - end - end - if cfg.platforms.macosx and rockspec:format_is_at_least("3.0") and target then - local version = util.popen_read("sw_vers -productVersion") - local versionminor = minor(version) - local targetminor = minor(target) - if targetminor > versionminor then - return nil, ("This rock requires Mac OSX 10.%d, and you are running 10.%d."):format(targetminor, versionminor) - end - patch_variable("CC", target) - patch_variable("LD", target) - end - return true -end - ---- Build and install a rock given a rockspec. --- @param rockspec_file string: local or remote filename of a rockspec. --- @param need_to_fetch boolean: true if sources need to be fetched, --- false if the rockspec was obtained from inside a source rock. --- @param minimal_mode boolean: true if there's no need to fetch, --- unpack or change dir (this is used by "luarocks make"). Implies --- need_to_fetch = false. --- @param deps_mode string: Dependency mode: "one" for the current default tree, --- "all" for all trees, "order" for all trees with priority >= the current default, --- "none" for no trees. --- @param build_only_deps boolean: true to build the listed dependencies only. --- @return (string, string) or (nil, string, [string]): Name and version of --- installed rock if succeeded or nil and an error message followed by an error code. -function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode, build_only_deps) - assert(type(rockspec_file) == "string") - assert(type(need_to_fetch) == "boolean") - - local rockspec, err, errcode = fetch.load_rockspec(rockspec_file) - if err then - return nil, err, errcode - elseif not rockspec.build then - return nil, "Rockspec error: build table not specified" - elseif not rockspec.build.type then - return nil, "Rockspec error: build type not specified" - end - - local ok - if not build_only_deps then - ok, err, errcode = deps.check_external_deps(rockspec, "build") - if err then - return nil, err, errcode - end - end - - if deps_mode == "none" then - util.printerr("Warning: skipping dependency checks.") - else - local ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) - if err then - return nil, err, errcode - end - end - - local name, version = rockspec.name, rockspec.version - if build_only_deps then - util.printout("Stopping after installing dependencies for " ..name.." "..version) - util.printout() - return name, version - end - - if repos.is_installed(name, version) then - repos.delete_version(name, version, deps_mode) - end - - if not minimal_mode then - local source_dir - if need_to_fetch then - ok, source_dir, errcode = fetch.fetch_sources(rockspec, true) - if not ok then - return nil, source_dir, errcode - end - local ok, err = fs.change_dir(source_dir) - if not ok then return nil, err end - elseif rockspec.source.file then - local ok, err = fs.unpack_archive(rockspec.source.file) - if not ok then - return nil, err - end - end - fs.change_dir(rockspec.source.dir) - end - - local dirs = { - lua = { name = path.lua_dir(name, version), is_module_path = true, perms = cfg.perm_read }, - lib = { name = path.lib_dir(name, version), is_module_path = true, perms = cfg.perm_exec }, - conf = { name = path.conf_dir(name, version), is_module_path = false, perms = cfg.perm_read }, - bin = { name = path.bin_dir(name, version), is_module_path = false, perms = cfg.perm_exec }, - } - - for _, d in pairs(dirs) do - local ok, err = fs.make_dir(d.name) - if not ok then return nil, err end - end - local rollback = util.schedule_function(function() - fs.delete(path.install_dir(name, version)) - fs.remove_dir_if_empty(path.versions_dir(name)) - end) - - local build_spec = rockspec.build - - if not minimal_mode then - ok, err = build.apply_patches(rockspec) - if err then - return nil, err - end - end - - ok, err = check_macosx_deployment_target(rockspec) - if not ok then - return nil, err - end - - if build_spec.type ~= "none" then - - -- Temporary compatibility - if build_spec.type == "module" then - util.printout("Do not use 'module' as a build type. Use 'builtin' instead.") - build_spec.type = "builtin" - end - - if cfg.accepted_build_types and util.array_contains(cfg.accepted_build_types, build_spec.type) then - return nil, "This rockspec uses the '"..build_spec.type.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration." - end - - local build_type - ok, build_type = pcall(require, "luarocks.build." .. build_spec.type) - if not ok or not type(build_type) == "table" then - return nil, "Failed initializing build back-end for build type '"..build_spec.type.."': "..build_type - end - - ok, err = build_type.run(rockspec) - if not ok then - return nil, "Build error: " .. err - end - end - - if build_spec.install then - for id, install_dir in pairs(dirs) do - ok, err = install_files(build_spec.install[id], install_dir.name, install_dir.is_module_path, install_dir.perms) - if not ok then - return nil, err - end - end - end - - local copy_directories = build_spec.copy_directories - local copying_default = false - if not copy_directories then - copy_directories = {"doc"} - copying_default = true - end - - local any_docs = false - for _, copy_dir in pairs(copy_directories) do - if fs.is_dir(copy_dir) then - local dest = dir.path(path.install_dir(name, version), copy_dir) - fs.make_dir(dest) - fs.copy_contents(copy_dir, dest) - any_docs = true - else - if not copying_default then - return nil, "Directory '"..copy_dir.."' not found" - end - end - end - - if not any_docs then - install_default_docs(name, version) - end - - for _, d in pairs(dirs) do - fs.remove_dir_if_empty(d.name) - end - - fs.pop_dir() - - fs.copy(rockspec.local_filename, path.rockspec_file(name, version), cfg.perm_read) - if need_to_fetch then - fs.pop_dir() - end - - ok, err = writer.make_rock_manifest(name, version) - if err then return nil, err end - - ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) - if err then return nil, err end - - util.remove_scheduled_function(rollback) - rollback = util.schedule_function(function() - repos.delete_version(name, version, deps_mode) - end) - - ok, err = repos.run_hook(rockspec, "post_install") - if err then return nil, err end - - util.announce_install(rockspec) - util.remove_scheduled_function(rollback) - return name, version -end - ---- Build and install a rock. --- @param rock_file string: local or remote filename of a rock. --- @param need_to_fetch boolean: true if sources need to be fetched, --- false if the rockspec was obtained from inside a source rock. --- @param deps_mode: string: Which trees to check dependencies for: --- "one" for the current default tree, "all" for all trees, --- "order" for all trees with priority >= the current default, "none" for no trees. --- @param build_only_deps boolean: true to build the listed dependencies only. --- @return boolean or (nil, string, [string]): True if build was successful, --- or false and an error message and an optional error code. -function build.build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps) - assert(type(rock_file) == "string") - assert(type(need_to_fetch) == "boolean") - - local ok, err, errcode - local unpack_dir - unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file) - if not unpack_dir then - return nil, err, errcode - end - local rockspec_file = path.rockspec_name_from_rock(rock_file) - ok, err = fs.change_dir(unpack_dir) - if not ok then return nil, err end - ok, err, errcode = build.build_rockspec(rockspec_file, need_to_fetch, false, deps_mode, build_only_deps) - fs.pop_dir() - return ok, err, errcode -end - -local function do_build(name, version, deps_mode, build_only_deps) - if name:match("%.rockspec$") then - return build.build_rockspec(name, true, false, deps_mode, build_only_deps) - elseif name:match("%.src%.rock$") then - return build.build_rock(name, false, deps_mode, build_only_deps) - elseif name:match("%.all%.rock$") then - local install = require("luarocks.install") - local install_fun = build_only_deps and install.install_binary_rock_deps or install.install_binary_rock - return install_fun(name, deps_mode) - elseif name:match("%.rock$") then - return build.build_rock(name, true, deps_mode, build_only_deps) - elseif not name:match(dir.separator) then - local search = require("luarocks.search") - return search.act_on_src_or_rockspec(do_build, name:lower(), version, nil, deps_mode, build_only_deps) - end - return nil, "Don't know what to do with "..name -end - ---- Driver function for "build" command. --- @param name string: A local or remote rockspec or rock file. --- If a package name is given, forwards the request to "search" and, --- if returned a result, installs the matching rock. --- @param version string: When passing a package name, a version number may --- also be given. --- @return boolean or (nil, string, exitcode): True if build was successful; nil and an --- error message otherwise. exitcode is optionally returned. -function build.command(flags, name, version) - if type(name) ~= "string" then - return nil, "Argument missing. "..util.see_help("build") - end - assert(type(version) == "string" or not version) - - if flags["pack-binary-rock"] then - return pack.pack_binary_rock(name, version, do_build, name, version, deps.get_deps_mode(flags)) - else - local ok, err = fs.check_command_permissions(flags) - if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end - ok, err = do_build(name, version, deps.get_deps_mode(flags), flags["only-deps"]) - if not ok then return nil, err end - name, version = ok, err - if flags["only-deps"] then - return name, version - end - if (not flags["keep"]) and not cfg.keep_other_versions then - local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) - if not ok then util.printerr(err) end - end - return name, version - end -end - -return build diff --git a/src/luarocks/base/config_cmd.lua b/src/luarocks/base/config_cmd.lua deleted file mode 100644 index b68f7898..00000000 --- a/src/luarocks/base/config_cmd.lua +++ /dev/null @@ -1,71 +0,0 @@ ---- Module implementing the LuaRocks "config" command. --- Queries information about the LuaRocks configuration. -local config_cmd = {} - -local cfg = require("luarocks.core.cfg") -local util = require("luarocks.util") -local dir = require("luarocks.dir") - -config_cmd.help_summary = "Query information about the LuaRocks configuration." -config_cmd.help_arguments = "" -config_cmd.help = [[ ---lua-incdir Path to Lua header files. - ---lua-libdir Path to Lua library files. - ---lua-ver Lua version (in major.minor format). e.g. 5.1 - ---system-config Location of the system config file. - ---user-config Location of the user config file. - ---rock-trees Rocks trees in use. First the user tree, then the system tree. -]] - -local function config_file(conf) - print(dir.normalize(conf.file)) - if conf.ok then - return true - else - return nil, "file not found" - end -end - ---- Driver function for "config" command. --- @return boolean: True if succeeded, nil on errors. -function config_cmd.command(flags) - if flags["lua-incdir"] then - print(cfg.variables.LUA_INCDIR) - return true - end - if flags["lua-libdir"] then - print(cfg.variables.LUA_LIBDIR) - return true - end - if flags["lua-ver"] then - print(cfg.lua_version) - return true - end - local conf = cfg.which_config() - if flags["system-config"] then - return config_file(conf.system) - end - if flags["user-config"] then - return config_file(conf.user) - end - if flags["rock-trees"] then - for _, tree in ipairs(cfg.rocks_trees) do - if type(tree) == "string" then - util.printout(dir.normalize(tree)) - else - local name = tree.name and "\t"..tree.name or "" - util.printout(dir.normalize(tree.root)..name) - end - end - return true - end - - return nil, "Please provide a flag for querying configuration values. "..util.see_help("config") -end - -return config_cmd diff --git a/src/luarocks/base/doc.lua b/src/luarocks/base/doc.lua deleted file mode 100644 index 5d521276..00000000 --- a/src/luarocks/base/doc.lua +++ /dev/null @@ -1,155 +0,0 @@ - ---- Module implementing the LuaRocks "doc" command. --- Shows documentation for an installed rock. -local doc = {} - -local util = require("luarocks.util") -local search = require("luarocks.search") -local path = require("luarocks.path") -local dir = require("luarocks.dir") -local fetch = require("luarocks.fetch") -local fs = require("luarocks.fs") -local download = require("luarocks.download") - -doc.help_summary = "Show documentation for an installed rock." - -doc.help = [[ - is an existing package name. -Without any flags, tries to load the documentation -using a series of heuristics. -With these flags, return only the desired information: - ---home Open the home page of project. ---list List documentation files only. - -For more information about a rock, see the 'show' command. -]] - -local function show_homepage(homepage, name, version) - if not homepage then - return nil, "No 'homepage' field in rockspec for "..name.." "..version - end - util.printout("Opening "..homepage.." ...") - fs.browser(homepage) - return true -end - -local function try_to_open_homepage(name, version) - local temp_dir, err = fs.make_temp_dir("doc-"..name.."-"..(version or "")) - if not temp_dir then - return nil, "Failed creating temporary directory: "..err - end - util.schedule_function(fs.delete, temp_dir) - local ok, err = fs.change_dir(temp_dir) - if not ok then return nil, err end - local filename, err = download.download("rockspec", name, version) - if not filename then return nil, err end - local rockspec, err = fetch.load_local_rockspec(filename) - if not rockspec then return nil, err end - fs.pop_dir() - local descript = rockspec.description or {} - if not descript.homepage then return nil, "No homepage defined for "..name end - return show_homepage(descript.homepage, name, version) -end - ---- Driver function for "doc" command. --- @param name or nil: an existing package name. --- @param version string or nil: a version may also be passed. --- @return boolean: True if succeeded, nil on errors. -function doc.command(flags, name, version) - if not name then - return nil, "Argument missing. "..util.see_help("doc") - end - - name = name:lower() - - local iname, iversion, repo = search.pick_installed_rock(name, version, flags["tree"]) - if not iname then - util.printout(name..(version and " "..version or "").." is not installed. Looking for it in the rocks servers...") - return try_to_open_homepage(name, version) - end - name, version = iname, iversion - - local rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version, repo)) - if not rockspec then return nil,err end - local descript = rockspec.description or {} - - if flags["home"] then - return show_homepage(descript.homepage, name, version) - end - - local directory = path.install_dir(name,version,repo) - - local docdir - local directories = { "doc", "docs" } - for _, d in ipairs(directories) do - local dirname = dir.path(directory, d) - if fs.is_dir(dirname) then - docdir = dirname - break - end - end - if not docdir then - if descript.homepage and not flags["list"] then - util.printout("Local documentation directory not found -- opening "..descript.homepage.." ...") - fs.browser(descript.homepage) - return true - end - return nil, "Documentation directory not found for "..name.." "..version - end - - docdir = dir.normalize(docdir):gsub("/+", "/") - local files = fs.find(docdir) - local htmlpatt = "%.html?$" - local extensions = { htmlpatt, "%.md$", "%.txt$", "%.textile$", "" } - local basenames = { "index", "readme", "manual" } - - local porcelain = flags["porcelain"] - if #files > 0 then - util.title("Documentation files for "..name.." "..version, porcelain) - if porcelain then - for _, file in ipairs(files) do - util.printout(docdir.."/"..file) - end - else - util.printout(docdir.."/") - for _, file in ipairs(files) do - util.printout("\t"..file) - end - end - end - - if flags["list"] then - return true - end - - for _, extension in ipairs(extensions) do - for _, basename in ipairs(basenames) do - local filename = basename..extension - local found - for _, file in ipairs(files) do - if file:lower():match(filename) and ((not found) or #file < #found) then - found = file - end - end - if found then - local pathname = dir.path(docdir, found) - util.printout() - util.printout("Opening "..pathname.." ...") - util.printout() - local ok = fs.browser(pathname) - if not ok and not pathname:match(htmlpatt) then - local fd = io.open(pathname, "r") - util.printout(fd:read("*a")) - fd:close() - end - return true - end - end - end - - return true -end - - -return doc diff --git a/src/luarocks/base/download.lua b/src/luarocks/base/download.lua deleted file mode 100644 index 557d1b65..00000000 --- a/src/luarocks/base/download.lua +++ /dev/null @@ -1,107 +0,0 @@ - ---- Module implementing the luarocks "download" command. --- Download a rock from the repository. -local download = {} - -local util = require("luarocks.util") -local path = require("luarocks.path") -local fetch = require("luarocks.fetch") -local search = require("luarocks.search") -local fs = require("luarocks.fs") -local dir = require("luarocks.dir") -local cfg = require("luarocks.core.cfg") - -download.help_summary = "Download a specific rock file from a rocks server." -download.help_arguments = "[--all] [--arch= | --source | --rockspec] [ []]" - -download.help = [[ ---all Download all files if there are multiple matches. ---source Download .src.rock if available. ---rockspec Download .rockspec if available. ---arch= Download rock for a specific architecture. -]] - -local function get_file(filename) - local protocol, pathname = dir.split_url(filename) - if protocol == "file" then - local ok, err = fs.copy(pathname, fs.current_dir(), cfg.perm_read) - if ok then - return pathname - else - return nil, err - end - else - return fetch.fetch_url(filename) - end -end - -function download.download(arch, name, version, all) - local query = search.make_query(name, version) - if arch then query.arch = arch end - local search_err - - if all then - if name == "" then query.exact_name = false end - local results = search.search_repos(query) - local has_result = false - local all_ok = true - local any_err = "" - for name, result in pairs(results) do - for version, items in pairs(result) do - for _, item in ipairs(items) do - -- Ignore provided rocks. - if item.arch ~= "installed" then - has_result = true - local filename = path.make_url(item.repo, name, version, item.arch) - local ok, err = get_file(filename) - if not ok then - all_ok = false - any_err = any_err .. "\n" .. err - end - end - end - end - end - - if has_result then - return all_ok, any_err - end - else - local url - url, search_err = search.find_suitable_rock(query) - if url then - return get_file(url) - end - end - return nil, "Could not find a result named "..name..(version and " "..version or "").. - (search_err and ": "..search_err or ".") -end - ---- Driver function for the "download" command. --- @param name string: a rock name. --- @param version string or nil: if the name of a package is given, a --- version may also be passed. --- @return boolean or (nil, string): true if successful or nil followed --- by an error message. -function download.command(flags, name, version) - assert(type(version) == "string" or not version) - if type(name) ~= "string" and not flags["all"] then - return nil, "Argument missing. "..util.see_help("download") - end - if not name then name, version = "", "" end - - local arch - - if flags["source"] then - arch = "src" - elseif flags["rockspec"] then - arch = "rockspec" - elseif flags["arch"] then - arch = flags["arch"] - end - - local dl, err = download.download(arch, name:lower(), version, flags["all"]) - return dl and true, err -end - -return download diff --git a/src/luarocks/base/help.lua b/src/luarocks/base/help.lua deleted file mode 100644 index d27c3a50..00000000 --- a/src/luarocks/base/help.lua +++ /dev/null @@ -1,117 +0,0 @@ - ---- Module implementing the LuaRocks "help" command. --- This is a generic help display module, which --- uses a global table called "commands" to find commands --- to show help for; each command should be represented by a --- table containing "help" and "help_summary" fields. -local help = {} - -local util = require("luarocks.util") -local cfg = require("luarocks.core.cfg") -local dir = require("luarocks.dir") - -local program = util.this_program("luarocks") - -help.help_summary = "Help on commands. Type '"..program.." help ' for more." - -help.help_arguments = "[]" -help.help = [[ - is the command to show help for. -]] - -local function print_banner() - util.printout("\nLuaRocks "..cfg.program_version..", a module deployment system for Lua") -end - -local function print_section(section) - util.printout("\n"..section) -end - -local function get_status(status) - if status then - return "ok" - else - return "not found" - end -end - ---- Driver function for the "help" command. --- @param command string or nil: command to show help for; if not --- given, help summaries for all commands are shown. --- @return boolean or (nil, string): true if there were no errors --- or nil and an error message if an invalid command was requested. -function help.command(flags, command) - if not command then - local conf = cfg.which_config() - print_banner() - print_section("NAME") - util.printout("\t"..program..[[ - ]]..program_description) - print_section("SYNOPSIS") - util.printout("\t"..program..[[ [--from= | --only-from=] [--to=] [VAR=VALUE]... [] ]]) - print_section("GENERAL OPTIONS") - util.printout([[ - These apply to all commands, as appropriate: - - --server= Fetch rocks/rockspecs from this server - (takes priority over config file) - --only-server= Fetch rocks/rockspecs from this server only - (overrides any entries in the config file) - --only-sources= Restrict downloads to paths matching the - given URL. - --tree= Which tree to operate on. - --local Use the tree in the user's home directory. - To enable it, see ']]..program..[[ help path'. - --verbose Display verbose output of commands executed. - --timeout= Timeout on network operations, in seconds. - 0 means no timeout (wait forever). - Default is ]]..tostring(cfg.connection_timeout)..[[.]]) - print_section("VARIABLES") - util.printout([[ - Variables from the "variables" table of the configuration file - can be overriden with VAR=VALUE assignments.]]) - print_section("COMMANDS") - for name, command in util.sortedpairs(commands) do - local cmd = require(command) - util.printout("", name) - util.printout("\t", cmd.help_summary) - end - print_section("CONFIGURATION") - util.printout("\tLua version: " .. cfg.lua_version) - util.printout("\tConfiguration files:") - util.printout("\t\tSystem: ".. dir.normalize(conf.system.file) .. " (" .. get_status(conf.system.ok) ..")") - if conf.user.file then - util.printout("\t\tUser : ".. dir.normalize(conf.user.file) .. " (" .. get_status(conf.user.ok) ..")\n") - else - util.printout("\t\tUser : disabled in this LuaRocks installation.\n") - end - util.printout("\tRocks trees in use: ") - for _, tree in ipairs(cfg.rocks_trees) do - if type(tree) == "string" then - util.printout("\t\t"..dir.normalize(tree)) - else - local name = tree.name and " (\""..tree.name.."\")" or "" - util.printout("\t\t"..dir.normalize(tree.root)..name) - end - end - else - command = command:gsub("-", "_") - local cmd = commands[command] and require(commands[command]) - if cmd then - local arguments = cmd.help_arguments or "" - print_banner() - print_section("NAME") - util.printout("\t"..program.." "..command.." - "..cmd.help_summary) - print_section("SYNOPSIS") - util.printout("\t"..program.." "..command.." "..arguments) - print_section("DESCRIPTION") - util.printout("",(cmd.help:gsub("\n","\n\t"):gsub("\n\t$",""))) - print_section("SEE ALSO") - util.printout("","'"..program.." help' for general options and configuration.\n") - else - return nil, "Unknown command: "..command - end - end - return true -end - -return help diff --git a/src/luarocks/base/install.lua b/src/luarocks/base/install.lua deleted file mode 100644 index c9b085f5..00000000 --- a/src/luarocks/base/install.lua +++ /dev/null @@ -1,183 +0,0 @@ ---- Module implementing the LuaRocks "install" command. --- Installs binary rocks. -local install = {} - -local path = require("luarocks.path") -local repos = require("luarocks.repos") -local fetch = require("luarocks.fetch") -local util = require("luarocks.util") -local fs = require("luarocks.fs") -local deps = require("luarocks.deps") -local writer = require("luarocks.manif.writer") -local remove = require("luarocks.remove") -local cfg = require("luarocks.core.cfg") - -install.help_summary = "Install a rock." - -install.help_arguments = "{| []}" - -install.help = [[ -Argument may be the name of a rock to be fetched from a repository -or a filename of a locally available rock. - ---keep Do not remove previously installed versions of the - rock after installing a new one. This behavior can - be made permanent by setting keep_other_versions=true - in the configuration file. - ---only-deps Installs only the dependencies of the rock. -]]..util.deps_mode_help() - - ---- Install a binary rock. --- @param rock_file string: local or remote filename of a rock. --- @param deps_mode: string: Which trees to check dependencies for: --- "one" for the current default tree, "all" for all trees, --- "order" for all trees with priority >= the current default, "none" for no trees. --- @return (string, string) or (nil, string, [string]): Name and version of --- installed rock if succeeded or nil and an error message followed by an error code. -function install.install_binary_rock(rock_file, deps_mode) - assert(type(rock_file) == "string") - - local name, version, arch = path.parse_name(rock_file) - if not name then - return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." - end - - if arch ~= "all" and arch ~= cfg.arch then - return nil, "Incompatible architecture "..arch, "arch" - end - if repos.is_installed(name, version) then - repos.delete_version(name, version, deps_mode) - end - - local rollback = util.schedule_function(function() - fs.delete(path.install_dir(name, version)) - fs.remove_dir_if_empty(path.versions_dir(name)) - end) - - local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) - if not ok then return nil, err, errcode end - - local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version)) - if err then - return nil, "Failed loading rockspec for installed package: "..err, errcode - end - - if deps_mode == "none" then - util.printerr("Warning: skipping dependency checks.") - else - ok, err, errcode = deps.check_external_deps(rockspec, "install") - if err then return nil, err, errcode end - end - - -- For compatibility with .rock files built with LuaRocks 1 - if not fs.exists(path.rock_manifest_file(name, version)) then - ok, err = writer.make_rock_manifest(name, version) - if err then return nil, err end - end - - if deps_mode ~= "none" then - ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) - if err then return nil, err, errcode end - end - - ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) - if err then return nil, err end - - util.remove_scheduled_function(rollback) - rollback = util.schedule_function(function() - repos.delete_version(name, version, deps_mode) - end) - - ok, err = repos.run_hook(rockspec, "post_install") - if err then return nil, err end - - util.announce_install(rockspec) - util.remove_scheduled_function(rollback) - return name, version -end - ---- Installs the dependencies of a binary rock. --- @param rock_file string: local or remote filename of a rock. --- @param deps_mode: string: Which trees to check dependencies for: --- "one" for the current default tree, "all" for all trees, --- "order" for all trees with priority >= the current default, "none" for no trees. --- @return (string, string) or (nil, string, [string]): Name and version of --- the rock whose dependencies were installed if succeeded or nil and an error message --- followed by an error code. -function install.install_binary_rock_deps(rock_file, deps_mode) - assert(type(rock_file) == "string") - - local name, version, arch = path.parse_name(rock_file) - if not name then - return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." - end - - if arch ~= "all" and arch ~= cfg.arch then - return nil, "Incompatible architecture "..arch, "arch" - end - - local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) - if not ok then return nil, err, errcode end - - local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version)) - if err then - return nil, "Failed loading rockspec for installed package: "..err, errcode - end - - ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) - if err then return nil, err, errcode end - - util.printout() - util.printout("Successfully installed dependencies for " ..name.." "..version) - - return name, version -end - ---- Driver function for the "install" command. --- @param name string: name of a binary rock. If an URL or pathname --- to a binary rock is given, fetches and installs it. If a rockspec or a --- source rock is given, forwards the request to the "build" command. --- If a package name is given, forwards the request to "search" and, --- if returned a result, installs the matching rock. --- @param version string: When passing a package name, a version number --- may also be given. --- @return boolean or (nil, string, exitcode): True if installation was --- successful, nil and an error message otherwise. exitcode is optionally returned. -function install.command(flags, name, version) - if type(name) ~= "string" then - return nil, "Argument missing. "..util.see_help("install") - end - - local ok, err = fs.check_command_permissions(flags) - if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end - - if name:match("%.rockspec$") or name:match("%.src%.rock$") then - local build = require("luarocks.build") - return build.command(flags, name) - elseif name:match("%.rock$") then - if flags["only-deps"] then - ok, err = install.install_binary_rock_deps(name, deps.get_deps_mode(flags)) - else - ok, err = install.install_binary_rock(name, deps.get_deps_mode(flags)) - end - if not ok then return nil, err end - name, version = ok, err - if (not flags["only-deps"]) and (not flags["keep"]) and not cfg.keep_other_versions then - local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) - if not ok then util.printerr(err) end - end - return name, version - else - local search = require("luarocks.search") - local url, err = search.find_suitable_rock(search.make_query(name:lower(), version)) - if not url then - return nil, err - end - util.printout("Installing "..url) - return install.command(flags, url) - end -end - -return install diff --git a/src/luarocks/base/lint.lua b/src/luarocks/base/lint.lua deleted file mode 100644 index c9ea45ea..00000000 --- a/src/luarocks/base/lint.lua +++ /dev/null @@ -1,53 +0,0 @@ - ---- Module implementing the LuaRocks "lint" command. --- Utility function that checks syntax of the rockspec. -local lint = {} - -local util = require("luarocks.util") -local download = require("luarocks.download") -local fetch = require("luarocks.fetch") - -lint.help_summary = "Check syntax of a rockspec." -lint.help_arguments = "" -lint.help = [[ -This is a utility function that checks the syntax of a rockspec. - -It returns success or failure if the text of a rockspec is -syntactically correct. -]] - -function lint.command(flags, input) - if not input then - return nil, "Argument missing. "..util.see_help("lint") - end - - local filename = input - if not input:match(".rockspec$") then - local err - filename, err = download.download("rockspec", input:lower()) - if not filename then - return nil, err - end - end - - local rs, err = fetch.load_local_rockspec(filename) - if not rs then - return nil, "Failed loading rockspec: "..err - end - - local ok = true - - -- This should have been done in the type checker, - -- but it would break compatibility of other commands. - -- Making 'lint' alone be stricter shouldn't be a problem, - -- because extra-strict checks is what lint-type commands - -- are all about. - if not rs.description.license then - util.printerr("Rockspec has no license field.") - ok = false - end - - return ok, ok or filename.." failed consistency checks." -end - -return lint diff --git a/src/luarocks/base/list.lua b/src/luarocks/base/list.lua deleted file mode 100644 index 45f1a26f..00000000 --- a/src/luarocks/base/list.lua +++ /dev/null @@ -1,95 +0,0 @@ - ---- Module implementing the LuaRocks "list" command. --- Lists currently installed rocks. -local list = {} - -local search = require("luarocks.search") -local deps = require("luarocks.deps") -local cfg = require("luarocks.core.cfg") -local util = require("luarocks.util") -local path = require("luarocks.path") - -list.help_summary = "List currently installed rocks." -list.help_arguments = "[--porcelain] " -list.help = [[ - is a substring of a rock name to filter by. - ---outdated List only rocks for which there is a - higher version available in the rocks server. - ---porcelain Produce machine-friendly output. -]] - -local function check_outdated(trees, query) - local results_installed = {} - for _, tree in ipairs(trees) do - search.manifest_search(results_installed, path.rocks_dir(tree), query) - end - local outdated = {} - for name, versions in util.sortedpairs(results_installed) do - versions = util.keys(versions) - table.sort(versions, deps.compare_versions) - local latest_installed = versions[1] - - local query_available = search.make_query(name:lower()) - query.exact_name = true - local results_available, err = search.search_repos(query_available) - - if results_available[name] then - local available_versions = util.keys(results_available[name]) - table.sort(available_versions, deps.compare_versions) - local latest_available = available_versions[1] - local latest_available_repo = results_available[name][latest_available][1].repo - - if deps.compare_versions(latest_available, latest_installed) then - table.insert(outdated, { name = name, installed = latest_installed, available = latest_available, repo = latest_available_repo }) - end - end - end - return outdated -end - -local function list_outdated(trees, query, porcelain) - util.title("Outdated rocks:", porcelain) - local outdated = check_outdated(trees, query) - for _, item in ipairs(outdated) do - if porcelain then - util.printout(item.name, item.installed, item.available, item.repo) - else - util.printout(item.name) - util.printout(" "..item.installed.." < "..item.available.." at "..item.repo) - util.printout() - end - end - return true -end - ---- Driver function for "list" command. --- @param filter string or nil: A substring of a rock name to filter by. --- @param version string or nil: a version may also be passed. --- @return boolean: True if succeeded, nil on errors. -function list.command(flags, filter, version) - local query = search.make_query(filter and filter:lower() or "", version) - query.exact_name = false - local trees = cfg.rocks_trees - if flags["tree"] then - trees = { flags["tree"] } - end - - if flags["outdated"] then - return list_outdated(trees, query, flags["porcelain"]) - end - - local results = {} - for _, tree in ipairs(trees) do - local ok, err, errcode = search.manifest_search(results, path.rocks_dir(tree), query) - if not ok and errcode ~= "open" then - util.warning(err) - end - end - util.title("Installed rocks:", flags["porcelain"]) - search.print_results(results, flags["porcelain"]) - return true -end - -return list diff --git a/src/luarocks/base/make.lua b/src/luarocks/base/make.lua deleted file mode 100644 index eb38bff0..00000000 --- a/src/luarocks/base/make.lua +++ /dev/null @@ -1,86 +0,0 @@ - ---- Module implementing the LuaRocks "make" command. --- Builds sources in the current directory, but unlike "build", --- it does not fetch sources, etc., assuming everything is --- available in the current directory. -local make = {} - -local build = require("luarocks.build") -local fs = require("luarocks.fs") -local util = require("luarocks.util") -local cfg = require("luarocks.core.cfg") -local fetch = require("luarocks.fetch") -local pack = require("luarocks.pack") -local remove = require("luarocks.remove") -local deps = require("luarocks.deps") - -make.help_summary = "Compile package in current directory using a rockspec." -make.help_arguments = "[--pack-binary-rock] []" -make.help = [[ -Builds sources in the current directory, but unlike "build", -it does not fetch sources, etc., assuming everything is -available in the current directory. If no argument is given, -it looks for a rockspec in the current directory and in "rockspec/" -and "rockspecs/" subdirectories, picking the rockspec with newest version -or without version name. If rockspecs for different rocks are found -or there are several rockspecs without version, you must specify which to use, -through the command-line. - -This command is useful as a tool for debugging rockspecs. -To install rocks, you'll normally want to use the "install" and -"build" commands. See the help on those for details. - ---pack-binary-rock Do not install rock. Instead, produce a .rock file - with the contents of compilation in the current - directory. - ---keep Do not remove previously installed versions of the - rock after installing a new one. This behavior can - be made permanent by setting keep_other_versions=true - in the configuration file. - ---branch= Override the `source.branch` field in the loaded - rockspec. Allows to specify a different branch to - fetch. Particularly for SCM rocks. - -]] - ---- Driver function for "make" command. --- @param name string: A local rockspec. --- @return boolean or (nil, string, exitcode): True if build was successful; nil and an --- error message otherwise. exitcode is optionally returned. -function make.command(flags, rockspec) - assert(type(rockspec) == "string" or not rockspec) - - if not rockspec then - local err - rockspec, err = util.get_default_rockspec() - if not rockspec then - return nil, err - end - end - if not rockspec:match("rockspec$") then - return nil, "Invalid argument: 'make' takes a rockspec as a parameter. "..util.see_help("make") - end - - if flags["pack-binary-rock"] then - local rspec, err, errcode = fetch.load_rockspec(rockspec) - if not rspec then - return nil, err - end - return pack.pack_binary_rock(rspec.name, rspec.version, build.build_rockspec, rockspec, false, true, deps.get_deps_mode(flags)) - else - local ok, err = fs.check_command_permissions(flags) - if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end - ok, err = build.build_rockspec(rockspec, false, true, deps.get_deps_mode(flags)) - if not ok then return nil, err end - local name, version = ok, err - if (not flags["keep"]) and not cfg.keep_other_versions then - local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) - if not ok then util.printerr(err) end - end - return name, version - end -end - -return make diff --git a/src/luarocks/base/new_version.lua b/src/luarocks/base/new_version.lua deleted file mode 100644 index b13dbb97..00000000 --- a/src/luarocks/base/new_version.lua +++ /dev/null @@ -1,199 +0,0 @@ - ---- Module implementing the LuaRocks "new_version" command. --- Utility function that writes a new rockspec, updating data from a previous one. -local new_version = {} - -local util = require("luarocks.util") -local download = require("luarocks.download") -local fetch = require("luarocks.fetch") -local persist = require("luarocks.persist") -local fs = require("luarocks.fs") -local type_check = require("luarocks.type_check") - -new_version.help_summary = "Auto-write a rockspec for a new version of a rock." -new_version.help_arguments = "[--tag=] [|] [] []" -new_version.help = [[ -This is a utility function that writes a new rockspec, updating data -from a previous one. - -If a package name is given, it downloads the latest rockspec from the -default server. If a rockspec is given, it uses it instead. If no argument -is given, it looks for a rockspec same way 'luarocks make' does. - -If the version number is not given and tag is passed using --tag, -it is used as the version, with 'v' removed from beginning. -Otherwise, it only increments the revision number of the given -(or downloaded) rockspec. - -If a URL is given, it replaces the one from the old rockspec with the -given URL. If a URL is not given and a new version is given, it tries -to guess the new URL by replacing occurrences of the version number -in the URL or tag. It also tries to download the new URL to determine -the new MD5 checksum. - -If a tag is given, it replaces the one from the old rockspec. If there is -an old tag but no new one passed, it is guessed in the same way URL is. - -WARNING: it writes the new rockspec to the current directory, -overwriting the file if it already exists. -]] - -local function try_replace(tbl, field, old, new) - if not tbl[field] then - return false - end - local old_field = tbl[field] - local new_field = tbl[field]:gsub(old, new) - if new_field ~= old_field then - util.printout("Guessing new '"..field.."' field as "..new_field) - tbl[field] = new_field - return true - end - return false -end - --- Try to download source file using URL from a rockspec. --- If it specified MD5, update it. --- @return (true, false) if MD5 was not specified or it stayed same, --- (true, true) if MD5 changed, (nil, string) on error. -local function check_url_and_update_md5(out_rs) - local file, temp_dir = fetch.fetch_url_at_temp_dir(out_rs.source.url, "luarocks-new-version-"..out_rs.package) - if not file then - util.printerr("Warning: invalid URL - "..temp_dir) - return true, false - end - - local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, out_rs.source.url, out_rs.source.dir) - if not inferred_dir then - return nil, found_dir - end - - if found_dir and found_dir ~= inferred_dir then - out_rs.source.dir = found_dir - end - - if file then - if out_rs.source.md5 then - util.printout("File successfully downloaded. Updating MD5 checksum...") - local new_md5, err = fs.get_md5(file) - if not new_md5 then - return nil, err - end - local old_md5 = out_rs.source.md5 - out_rs.source.md5 = new_md5 - return true, new_md5 ~= old_md5 - else - util.printout("File successfully downloaded.") - return true, false - end - end -end - -local function update_source_section(out_rs, url, tag, old_ver, new_ver) - if tag then - out_rs.source.tag = tag - end - if url then - out_rs.source.url = url - return check_url_and_update_md5(out_rs) - end - if new_ver == old_ver then - return true - end - if out_rs.source.dir then - try_replace(out_rs.source, "dir", old_ver, new_ver) - end - if out_rs.source.file then - try_replace(out_rs.source, "file", old_ver, new_ver) - end - if try_replace(out_rs.source, "url", old_ver, new_ver) then - return check_url_and_update_md5(out_rs) - end - if tag or try_replace(out_rs.source, "tag", old_ver, new_ver) then - return true - end - -- Couldn't replace anything significant, use the old URL. - local ok, md5_changed = check_url_and_update_md5(out_rs) - if not ok then - return nil, md5_changed - end - if md5_changed then - util.printerr("Warning: URL is the same, but MD5 has changed. Old rockspec is broken.") - end - return true -end - -function new_version.command(flags, input, version, url) - if not input then - local err - input, err = util.get_default_rockspec() - if not input then - return nil, err - end - end - assert(type(input) == "string") - - local filename, err - if input:match("rockspec$") then - filename, err = fetch.fetch_url(input) - if not filename then - return nil, err - end - else - filename, err = download.download("rockspec", input:lower()) - if not filename then - return nil, err - end - end - - local valid_rs, err = fetch.load_rockspec(filename) - if not valid_rs then - return nil, err - end - - local old_ver, old_rev = valid_rs.version:match("(.*)%-(%d+)$") - local new_ver, new_rev - - if flags.tag and not version then - version = flags.tag:gsub("^v", "") - end - - if version then - new_ver, new_rev = version:match("(.*)%-(%d+)$") - new_rev = tonumber(new_rev) - if not new_rev then - new_ver = version - new_rev = 1 - end - else - new_ver = old_ver - new_rev = tonumber(old_rev) + 1 - end - local new_rockver = new_ver:gsub("-", "") - - local out_rs, err = persist.load_into_table(filename) - local out_name = out_rs.package:lower() - out_rs.version = new_rockver.."-"..new_rev - - local ok, err = update_source_section(out_rs, url, flags.tag, old_ver, new_ver) - if not ok then return nil, err end - - if out_rs.build and out_rs.build.type == "module" then - out_rs.build.type = "builtin" - end - - local out_filename = out_name.."-"..new_rockver.."-"..new_rev..".rockspec" - - persist.save_from_table(out_filename, out_rs, type_check.rockspec_order) - - util.printout("Wrote "..out_filename) - - local valid_out_rs, err = fetch.load_local_rockspec(out_filename) - if not valid_out_rs then - return nil, "Failed loading generated rockspec: "..err - end - - return true -end - -return new_version diff --git a/src/luarocks/base/pack.lua b/src/luarocks/base/pack.lua deleted file mode 100644 index 655cbf37..00000000 --- a/src/luarocks/base/pack.lua +++ /dev/null @@ -1,193 +0,0 @@ - ---- Module implementing the LuaRocks "pack" command. --- Creates a rock, packing sources or binaries. -local pack = {} - -local unpack = unpack or table.unpack - -local path = require("luarocks.path") -local repos = require("luarocks.repos") -local fetch = require("luarocks.fetch") -local fs = require("luarocks.fs") -local cfg = require("luarocks.core.cfg") -local util = require("luarocks.util") -local dir = require("luarocks.dir") -local manif = require("luarocks.manif") -local search = require("luarocks.search") - -pack.help_summary = "Create a rock, packing sources or binaries." -pack.help_arguments = "{| []}" -pack.help = [[ -Argument may be a rockspec file, for creating a source rock, -or the name of an installed package, for creating a binary rock. -In the latter case, the app version may be given as a second -argument. -]] - ---- Create a source rock. --- Packages a rockspec and its required source files in a rock --- file with the .src.rock extension, which can later be built and --- installed with the "build" command. --- @param rockspec_file string: An URL or pathname for a rockspec file. --- @return string or (nil, string): The filename of the resulting --- .src.rock file; or nil and an error message. -function pack.pack_source_rock(rockspec_file) - assert(type(rockspec_file) == "string") - - local rockspec, err = fetch.load_rockspec(rockspec_file) - if err then - return nil, "Error loading rockspec: "..err - end - rockspec_file = rockspec.local_filename - - local name_version = rockspec.name .. "-" .. rockspec.version - local rock_file = fs.absolute_name(name_version .. ".src.rock") - - local source_file, source_dir = fetch.fetch_sources(rockspec, false) - if not source_file then - return nil, source_dir - end - local ok, err = fs.change_dir(source_dir) - if not ok then return nil, err end - - fs.delete(rock_file) - fs.copy(rockspec_file, source_dir, cfg.perm_read) - if not fs.zip(rock_file, dir.base_name(rockspec_file), dir.base_name(source_file)) then - return nil, "Failed packing "..rock_file - end - fs.pop_dir() - - return rock_file -end - -local function copy_back_files(name, version, file_tree, deploy_dir, pack_dir, perms) - local ok, err = fs.make_dir(pack_dir) - if not ok then return nil, err end - for file, sub in pairs(file_tree) do - local source = dir.path(deploy_dir, file) - local target = dir.path(pack_dir, file) - if type(sub) == "table" then - local ok, err = copy_back_files(name, version, sub, source, target) - if not ok then return nil, err end - else - local versioned = path.versioned_name(source, deploy_dir, name, version) - if fs.exists(versioned) then - fs.copy(versioned, target, perms) - else - fs.copy(source, target, perms) - end - end - end - return true -end - --- @param name string: Name of package to pack. --- @param version string or nil: A version number may also be passed. --- @param tree string or nil: An optional tree to pick the package from. --- @return string or (nil, string): The filename of the resulting --- .src.rock file; or nil and an error message. -local function do_pack_binary_rock(name, version, tree) - assert(type(name) == "string") - assert(type(version) == "string" or not version) - - local repo, repo_url - name, version, repo, repo_url = search.pick_installed_rock(name, version, tree) - if not name then - return nil, version - end - - local root = path.root_dir(repo_url) - local prefix = path.install_dir(name, version, root) - if not fs.exists(prefix) then - return nil, "'"..name.." "..version.."' does not seem to be an installed rock." - end - - local rock_manifest, err = manif.load_rock_manifest(name, version, root) - if not rock_manifest then return nil, err end - - local name_version = name .. "-" .. version - local rock_file = fs.absolute_name(name_version .. "."..cfg.arch..".rock") - - local temp_dir = fs.make_temp_dir("pack") - fs.copy_contents(prefix, temp_dir) - - local is_binary = false - if rock_manifest.lib then - local ok, err = copy_back_files(name, version, rock_manifest.lib, path.deploy_lib_dir(root), dir.path(temp_dir, "lib"), cfg.perm_exec) - if not ok then return nil, "Failed copying back files: " .. err end - is_binary = true - end - if rock_manifest.lua then - local ok, err = copy_back_files(name, version, rock_manifest.lua, path.deploy_lua_dir(root), dir.path(temp_dir, "lua"), cfg.perm_read) - if not ok then return nil, "Failed copying back files: " .. err end - end - - local ok, err = fs.change_dir(temp_dir) - if not ok then return nil, err end - if not is_binary and not repos.has_binaries(name, version) then - rock_file = rock_file:gsub("%."..cfg.arch:gsub("%-","%%-").."%.", ".all.") - end - fs.delete(rock_file) - if not fs.zip(rock_file, unpack(fs.list_dir())) then - return nil, "Failed packing "..rock_file - end - fs.pop_dir() - fs.delete(temp_dir) - return rock_file -end - -function pack.pack_binary_rock(name, version, cmd, ...) - - -- The --pack-binary-rock option for "luarocks build" basically performs - -- "luarocks build" on a temporary tree and then "luarocks pack". The - -- alternative would require refactoring parts of luarocks.build and - -- luarocks.pack, which would save a few file operations: the idea would be - -- to shave off the final deploy steps from the build phase and the initial - -- collect steps from the pack phase. - - local temp_dir, err = fs.make_temp_dir("luarocks-build-pack-"..dir.base_name(name)) - if not temp_dir then - return nil, "Failed creating temporary directory: "..err - end - util.schedule_function(fs.delete, temp_dir) - - path.use_tree(temp_dir) - local ok, err = cmd(...) - if not ok then - return nil, err - end - local rname, rversion = path.parse_name(name) - if not rname then - rname, rversion = name, version - end - return do_pack_binary_rock(rname, rversion, temp_dir) -end - ---- Driver function for the "pack" command. --- @param arg string: may be a rockspec file, for creating a source rock, --- or the name of an installed package, for creating a binary rock. --- @param version string or nil: if the name of a package is given, a --- version may also be passed. --- @return boolean or (nil, string): true if successful or nil followed --- by an error message. -function pack.command(flags, arg, version) - assert(type(version) == "string" or not version) - if type(arg) ~= "string" then - return nil, "Argument missing. "..util.see_help("pack") - end - - local file, err - if arg:match(".*%.rockspec") then - file, err = pack.pack_source_rock(arg) - else - file, err = do_pack_binary_rock(arg:lower(), version, flags["tree"]) - end - if err then - return nil, err - else - util.printout("Packed: "..file) - return true - end -end - -return pack diff --git a/src/luarocks/base/path_cmd.lua b/src/luarocks/base/path_cmd.lua deleted file mode 100644 index 516a0c47..00000000 --- a/src/luarocks/base/path_cmd.lua +++ /dev/null @@ -1,68 +0,0 @@ - ---- @module luarocks.path_cmd --- Driver for the `luarocks path` command. -local path_cmd = {} - -local util = require("luarocks.util") -local cfg = require("luarocks.core.cfg") - -path_cmd.help_summary = "Return the currently configured package path." -path_cmd.help_arguments = "" -path_cmd.help = [[ -Returns the package path currently configured for this installation -of LuaRocks, formatted as shell commands to update LUA_PATH and LUA_CPATH. - ---bin Adds the system path to the output - ---append Appends the paths to the existing paths. Default is to prefix - the LR paths to the existing paths. - ---lr-path Exports the Lua path (not formatted as shell command) - ---lr-cpath Exports the Lua cpath (not formatted as shell command) - ---lr-bin Exports the system path (not formatted as shell command) - - -On Unix systems, you may run: - eval `luarocks path` -And on Windows: - luarocks path > "%temp%\_lrp.bat" && call "%temp%\_lrp.bat" && del "%temp%\_lrp.bat" -]] - ---- Driver function for "path" command. --- @return boolean This function always succeeds. -function path_cmd.command(flags) - local lr_path, lr_cpath, lr_bin = cfg.package_paths(flags["tree"]) - local path_sep = cfg.export_path_separator - - if flags["lr-path"] then - util.printout(util.remove_path_dupes(lr_path, ';')) - return true - elseif flags["lr-cpath"] then - util.printout(util.remove_path_dupes(lr_cpath, ';')) - return true - elseif flags["lr-bin"] then - util.printout(util.remove_path_dupes(lr_bin, path_sep)) - return true - end - - if flags["append"] then - lr_path = package.path .. ";" .. lr_path - lr_cpath = package.cpath .. ";" .. lr_cpath - lr_bin = os.getenv("PATH") .. path_sep .. lr_bin - else - lr_path = lr_path.. ";" .. package.path - lr_cpath = lr_cpath .. ";" .. package.cpath - lr_bin = lr_bin .. path_sep .. os.getenv("PATH") - end - - util.printout(cfg.export_lua_path:format(util.remove_path_dupes(lr_path, ';'))) - util.printout(cfg.export_lua_cpath:format(util.remove_path_dupes(lr_cpath, ';'))) - if flags["bin"] then - util.printout(cfg.export_path:format(util.remove_path_dupes(lr_bin, path_sep))) - end - return true -end - -return path_cmd diff --git a/src/luarocks/base/purge.lua b/src/luarocks/base/purge.lua deleted file mode 100644 index 50f290c8..00000000 --- a/src/luarocks/base/purge.lua +++ /dev/null @@ -1,77 +0,0 @@ - ---- Module implementing the LuaRocks "purge" command. --- Remove all rocks from a given tree. -local purge = {} - -local util = require("luarocks.util") -local fs = require("luarocks.fs") -local path = require("luarocks.path") -local search = require("luarocks.search") -local deps = require("luarocks.deps") -local repos = require("luarocks.repos") -local writer = require("luarocks.manif.writer") -local cfg = require("luarocks.core.cfg") -local remove = require("luarocks.remove") - -purge.help_summary = "Remove all installed rocks from a tree." -purge.help_arguments = "--tree= [--old-versions]" -purge.help = [[ -This command removes rocks en masse from a given tree. -By default, it removes all rocks from a tree. - -The --tree argument is mandatory: luarocks purge does not -assume a default tree. - ---old-versions Keep the highest-numbered version of each - rock and remove the other ones. By default - it only removes old versions if they are - not needed as dependencies. This can be - overridden with the flag --force. -]] - -function purge.command(flags) - local tree = flags["tree"] - - if type(tree) ~= "string" then - return nil, "The --tree argument is mandatory. "..util.see_help("purge") - end - - local results = {} - local query = search.make_query("") - query.exact_name = false - if not fs.is_dir(tree) then - return nil, "Directory not found: "..tree - end - - local ok, err = fs.check_command_permissions(flags) - if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end - - search.manifest_search(results, path.rocks_dir(tree), query) - - local sort = function(a,b) return deps.compare_versions(b,a) end - if flags["old-versions"] then - sort = deps.compare_versions - end - - for package, versions in util.sortedpairs(results) do - for version, _ in util.sortedpairs(versions, sort) do - if flags["old-versions"] then - util.printout("Keeping "..package.." "..version.."...") - local ok, err = remove.remove_other_versions(package, version, flags["force"], flags["force-fast"]) - if not ok then - util.printerr(err) - end - break - else - util.printout("Removing "..package.." "..version.."...") - local ok, err = repos.delete_version(package, version, "none", true) - if not ok then - util.printerr(err) - end - end - end - end - return writer.make_manifest(cfg.rocks_dir, "one") -end - -return purge diff --git a/src/luarocks/base/remove.lua b/src/luarocks/base/remove.lua deleted file mode 100644 index e7f37604..00000000 --- a/src/luarocks/base/remove.lua +++ /dev/null @@ -1,165 +0,0 @@ - ---- Module implementing the LuaRocks "remove" command. --- Uninstalls rocks. -local remove = {} - -local search = require("luarocks.search") -local deps = require("luarocks.deps") -local fetch = require("luarocks.fetch") -local repos = require("luarocks.repos") -local path = require("luarocks.path") -local util = require("luarocks.util") -local cfg = require("luarocks.core.cfg") -local fs = require("luarocks.fs") - -remove.help_summary = "Uninstall a rock." -remove.help_arguments = "[--force|--force-fast] []" -remove.help = [[ -Argument is the name of a rock to be uninstalled. -If a version is not given, try to remove all versions at once. -Will only perform the removal if it does not break dependencies. -To override this check and force the removal, use --force. -To perform a forced removal without reporting dependency issues, -use --force-fast. - -]]..util.deps_mode_help() - ---- Obtain a list of packages that depend on the given set of packages --- (where all packages of the set are versions of one program). --- @param name string: the name of a program --- @param versions array of string: the versions to be deleted. --- @return array of string: an empty table if no packages depend on any --- of the given list, or an array of strings in "name/version" format. -local function check_dependents(name, versions, deps_mode) - local dependents = {} - local blacklist = {} - blacklist[name] = {} - for version, _ in pairs(versions) do - blacklist[name][version] = true - end - local local_rocks = {} - local query_all = search.make_query("") - query_all.exact_name = false - search.manifest_search(local_rocks, cfg.rocks_dir, query_all) - local_rocks[name] = nil - for rock_name, rock_versions in pairs(local_rocks) do - for rock_version, _ in pairs(rock_versions) do - local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version)) - if rockspec then - local _, missing = deps.match_deps(rockspec, blacklist, deps_mode) - if missing[name] then - table.insert(dependents, { name = rock_name, version = rock_version }) - end - end - end - end - return dependents -end - ---- Delete given versions of a program. --- @param name string: the name of a program --- @param versions array of string: the versions to be deleted. --- @param deps_mode: string: Which trees to check dependencies for: --- "one" for the current default tree, "all" for all trees, --- "order" for all trees with priority >= the current default, "none" for no trees. --- @return boolean or (nil, string): true on success or nil and an error message. -local function delete_versions(name, versions, deps_mode) - - for version, _ in pairs(versions) do - util.printout("Removing "..name.." "..version.."...") - local ok, err = repos.delete_version(name, version, deps_mode) - if not ok then return nil, err end - end - - return true -end - -function remove.remove_search_results(results, name, deps_mode, force, fast) - local versions = results[name] - - local version = next(versions) - local second = next(versions, version) - - local dependents = {} - if not fast then - util.printout("Checking stability of dependencies in the absence of") - util.printout(name.." "..table.concat(util.keys(versions), ", ").."...") - util.printout() - dependents = check_dependents(name, versions, deps_mode) - end - - if #dependents > 0 then - if force or fast then - util.printerr("The following packages may be broken by this forced removal:") - for _, dependent in ipairs(dependents) do - util.printerr(dependent.name.." "..dependent.version) - end - util.printerr() - else - if not second then - util.printerr("Will not remove "..name.." "..version..".") - util.printerr("Removing it would break dependencies for: ") - else - util.printerr("Will not remove installed versions of "..name..".") - util.printerr("Removing them would break dependencies for: ") - end - for _, dependent in ipairs(dependents) do - util.printerr(dependent.name.." "..dependent.version) - end - util.printerr() - util.printerr("Use --force to force removal (warning: this may break modules).") - return nil, "Failed removing." - end - end - - local ok, err = delete_versions(name, versions, deps_mode) - if not ok then return nil, err end - - util.printout("Removal successful.") - return true -end - -function remove.remove_other_versions(name, version, force, fast) - local results = {} - search.manifest_search(results, cfg.rocks_dir, { name = name, exact_name = true, constraints = {{ op = "~=", version = version}} }) - if results[name] then - return remove.remove_search_results(results, name, cfg.deps_mode, force, fast) - end - return true -end - ---- Driver function for the "remove" command. --- @param name string: name of a rock. If a version is given, refer to --- a specific version; otherwise, try to remove all versions. --- @param version string: When passing a package name, a version number --- may also be given. --- @return boolean or (nil, string, exitcode): True if removal was --- successful, nil and an error message otherwise. exitcode is optionally returned. -function remove.command(flags, name, version) - if type(name) ~= "string" then - return nil, "Argument missing. "..util.see_help("remove") - end - - local deps_mode = flags["deps-mode"] or cfg.deps_mode - - local ok, err = fs.check_command_permissions(flags) - if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end - - local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$") - local filename = name - if rock_type then - name, version = path.parse_name(filename) - if not name then return nil, "Invalid "..rock_type.." filename: "..filename end - end - - local results = {} - name = name:lower() - search.manifest_search(results, cfg.rocks_dir, search.make_query(name, version)) - if not results[name] then - return nil, "Could not find rock '"..name..(version and " "..version or "").."' in "..path.rocks_tree_to_string(cfg.root_dir) - end - - return remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"]) -end - -return remove diff --git a/src/luarocks/base/search.lua b/src/luarocks/base/search.lua deleted file mode 100644 index 44eff694..00000000 --- a/src/luarocks/base/search.lua +++ /dev/null @@ -1,482 +0,0 @@ - ---- Module implementing the LuaRocks "search" command. --- Queries LuaRocks servers. -local search = {} - - -local dir = require("luarocks.dir") -local path = require("luarocks.path") -local manif = require("luarocks.manif") -local deps = require("luarocks.deps") -local cfg = require("luarocks.core.cfg") -local util = require("luarocks.util") - -search.help_summary = "Query the LuaRocks servers." -search.help_arguments = "[--source] [--binary] { [] | --all }" -search.help = [[ ---source Return only rockspecs and source rocks, - to be used with the "build" command. ---binary Return only pure Lua and binary rocks (rocks that can be used - with the "install" command without requiring a C toolchain). ---all List all contents of the server that are suitable to - this platform, do not filter by name. -]] - ---- Convert the arch field of a query table to table format. --- @param query table: A query table. -local function query_arch_as_table(query) - local format = type(query.arch) - if format == "table" then - return - elseif format == "nil" then - local accept = {} - accept["src"] = true - accept["all"] = true - accept["rockspec"] = true - accept["installed"] = true - accept[cfg.arch] = true - query.arch = accept - elseif format == "string" then - local accept = {} - for a in query.arch:gmatch("[%w_-]+") do - accept[a] = true - end - query.arch = accept - end -end - ---- Store a search result (a rock or rockspec) in the results table. --- @param results table: The results table, where keys are package names and --- values are tables matching version strings to arrays of --- tables with fields "arch" and "repo". --- @param name string: Package name. --- @param version string: Package version. --- @param arch string: Architecture of rock ("all", "src" or platform --- identifier), "rockspec" or "installed" --- @param repo string: Pathname of a local repository of URL of --- rocks server. -local function store_result(results, name, version, arch, repo) - assert(type(results) == "table") - assert(type(name) == "string") - assert(type(version) == "string") - assert(type(arch) == "string") - assert(type(repo) == "string") - - if not results[name] then results[name] = {} end - if not results[name][version] then results[name][version] = {} end - table.insert(results[name][version], { - arch = arch, - repo = repo - }) -end - ---- Test the name field of a query. --- If query has a boolean field exact_name set to false, --- then substring match is performed; otherwise, exact string --- comparison is done. --- @param query table: A query in dependency table format. --- @param name string: A package name. --- @return boolean: True if names match, false otherwise. -local function match_name(query, name) - assert(type(query) == "table") - assert(type(name) == "string") - if query.exact_name == false then - return name:find(query.name, 0, true) and true or false - else - return name == query.name - end -end - ---- Store a match in a results table if version matches query. --- Name, version, arch and repository path are stored in a given --- table, optionally checking if version and arch (if given) match --- a query. --- @param results table: The results table, where keys are package names and --- values are tables matching version strings to arrays of --- tables with fields "arch" and "repo". --- @param repo string: URL or pathname of the repository. --- @param name string: The name of the package being tested. --- @param version string: The version of the package being tested. --- @param arch string: The arch of the package being tested. --- @param query table: A table describing the query in dependency --- format (for example, {name = "filesystem", exact_name = false, --- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). --- If the arch field is omitted, the local architecture (cfg.arch) --- is used. The special value "any" is also recognized, returning all --- matches regardless of architecture. -local function store_if_match(results, repo, name, version, arch, query) - if match_name(query, name) then - if query.arch[arch] or query.arch["any"] then - if deps.match_constraints(deps.parse_version(version), query.constraints) then - store_result(results, name, version, arch, repo) - end - end - end -end - ---- Perform search on a local repository. --- @param repo string: The pathname of the local repository. --- @param query table: A table describing the query in dependency --- format (for example, {name = "filesystem", exact_name = false, --- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). --- If the arch field is omitted, the local architecture (cfg.arch) --- is used. The special value "any" is also recognized, returning all --- matches regardless of architecture. --- @param results table or nil: If given, this table will store the --- results; if not given, a new table will be created. --- @return table: The results table, where keys are package names and --- values are tables matching version strings to arrays of --- tables with fields "arch" and "repo". --- If a table was given in the "results" parameter, that is the result value. -function search.disk_search(repo, query, results) - assert(type(repo) == "string") - assert(type(query) == "table") - assert(type(results) == "table" or not results) - - local fs = require("luarocks.fs") - - if not results then - results = {} - end - query_arch_as_table(query) - - for name in fs.dir(repo) do - local pathname = dir.path(repo, name) - local rname, rversion, rarch = path.parse_name(name) - - if rname and (pathname:match(".rockspec$") or pathname:match(".rock$")) then - store_if_match(results, repo, rname, rversion, rarch, query) - elseif fs.is_dir(pathname) then - for version in fs.dir(pathname) do - if version:match("-%d+$") then - store_if_match(results, repo, name, version, "installed", query) - end - end - end - end - return results -end - ---- Perform search on a rocks server or tree. --- @param results table: The results table, where keys are package names and --- values are tables matching version strings to arrays of --- tables with fields "arch" and "repo". --- @param repo string: The URL of a rocks server or --- the pathname of a rocks tree (as returned by path.rocks_dir()). --- @param query table: A table describing the query in dependency --- format (for example, {name = "filesystem", exact_name = false, --- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). --- If the arch field is omitted, the local architecture (cfg.arch) --- is used. The special value "any" is also recognized, returning all --- matches regardless of architecture. --- @param lua_version string: Lua version in "5.x" format, defaults to installed version. --- @return true or, in case of errors, nil, an error message and an optional error code. -function search.manifest_search(results, repo, query, lua_version) - assert(type(results) == "table") - assert(type(repo) == "string") - assert(type(query) == "table") - - query_arch_as_table(query) - local manifest, err, errcode = manif.load_manifest(repo, lua_version) - if not manifest then - return nil, err, errcode - end - for name, versions in pairs(manifest.repository) do - for version, items in pairs(versions) do - for _, item in ipairs(items) do - store_if_match(results, repo, name, version, item.arch, query) - end - end - end - return true -end - ---- Search on all configured rocks servers. --- @param query table: A dependency query. --- @param lua_version string: Lua version in "5.x" format, defaults to installed version. --- @return table: A table where keys are package names --- and values are tables matching version strings to arrays of --- tables with fields "arch" and "repo". -function search.search_repos(query, lua_version) - assert(type(query) == "table") - - local results = {} - for _, repo in ipairs(cfg.rocks_servers) do - if not cfg.disabled_servers[repo] then - if type(repo) == "string" then - repo = { repo } - end - for _, mirror in ipairs(repo) do - local protocol, pathname = dir.split_url(mirror) - if protocol == "file" then - mirror = pathname - end - local ok, err, errcode = search.manifest_search(results, mirror, query, lua_version) - if errcode == "network" then - cfg.disabled_servers[repo] = true - end - if ok then - break - else - util.warning("Failed searching manifest: "..err) - end - end - end - end - -- search through rocks in cfg.rocks_provided - local provided_repo = "provided by VM or rocks_provided" - for name, versions in pairs(cfg.rocks_provided) do - store_if_match(results, provided_repo, name, versions, "installed", query) - end - return results -end - ---- Prepare a query in dependency table format. --- @param name string: The query name. --- @param version string or nil: --- @return table: A query in table format -function search.make_query(name, version) - assert(type(name) == "string") - assert(type(version) == "string" or not version) - - local query = { - name = name, - constraints = {} - } - if version then - table.insert(query.constraints, { op = "==", version = deps.parse_version(version)}) - end - return query -end - ---- Get the URL for the latest in a set of versions. --- @param name string: The package name to be used in the URL. --- @param versions table: An array of version informations, as stored --- in search results tables. --- @return string or nil: the URL for the latest version if one could --- be picked, or nil. -local function pick_latest_version(name, versions) - assert(type(name) == "string") - assert(type(versions) == "table") - - local vtables = {} - for v, _ in pairs(versions) do - table.insert(vtables, deps.parse_version(v)) - end - table.sort(vtables) - local version = vtables[#vtables].string - local items = versions[version] - if items then - local pick = 1 - for i, item in ipairs(items) do - if (item.arch == 'src' and items[pick].arch == 'rockspec') - or (item.arch ~= 'src' and item.arch ~= 'rockspec') then - pick = i - end - end - return path.make_url(items[pick].repo, name, version, items[pick].arch) - end - return nil -end - --- Find out which other Lua versions provide rock versions matching a query, --- @param query table: A dependency query matching a single rock. --- @return table: array of Lua versions supported, in "5.x" format. -local function supported_lua_versions(query) - local results = {} - - for lua_version in util.lua_versions() do - if lua_version ~= cfg.lua_version then - if search.search_repos(query, lua_version)[query.name] then - table.insert(results, lua_version) - end - end - end - - return results -end - ---- Attempt to get a single URL for a given search for a rock. --- @param query table: A dependency query matching a single rock. --- @return string or (nil, string): URL for latest matching version --- of the rock if it was found, or nil followed by an error message. -function search.find_suitable_rock(query) - assert(type(query) == "table") - - local results = search.search_repos(query) - local first_rock = next(results) - if not first_rock then - if cfg.rocks_provided[query.name] == nil then - -- Check if constraints are satisfiable with other Lua versions. - local lua_versions = supported_lua_versions(query) - - if #lua_versions ~= 0 then - -- Build a nice message in "only Lua 5.x and 5.y but not 5.z." format - for i, lua_version in ipairs(lua_versions) do - lua_versions[i] = "Lua "..lua_version - end - - local versions_message = "only "..table.concat(lua_versions, " and ").. - " but not Lua "..cfg.lua_version.."." - - if #query.constraints == 0 then - return nil, query.name.." supports "..versions_message - elseif #query.constraints == 1 and query.constraints[1].op == "==" then - return nil, query.name.." "..query.constraints[1].version.string.." supports "..versions_message - else - return nil, "Matching "..query.name.." versions support "..versions_message - end - end - end - - return nil, "No results matching query were found." - elseif next(results, first_rock) then - -- Shouldn't happen as query must match only one package. - return nil, "Several rocks matched query." - elseif cfg.rocks_provided[query.name] ~= nil then - -- Do not install versions listed in cfg.rocks_provided. - return nil, "Rock "..query.name.." "..cfg.rocks_provided[query.name].. - " was found but it is provided by VM or 'rocks_provided' in the config file." - else - return pick_latest_version(query.name, results[first_rock]) - end -end - ---- Print a list of rocks/rockspecs on standard output. --- @param results table: A table where keys are package names and versions --- are tables matching version strings to an array of rocks servers. --- @param porcelain boolean or nil: A flag to force machine-friendly output. -function search.print_results(results, porcelain) - assert(type(results) == "table") - assert(type(porcelain) == "boolean" or not porcelain) - - for package, versions in util.sortedpairs(results) do - if not porcelain then - util.printout(package) - end - for version, repos in util.sortedpairs(versions, deps.compare_versions) do - for _, repo in ipairs(repos) do - repo.repo = dir.normalize(repo.repo) - if porcelain then - util.printout(package, version, repo.arch, repo.repo) - else - util.printout(" "..version.." ("..repo.arch..") - "..repo.repo) - end - end - end - if not porcelain then - util.printout() - end - end -end - ---- Splits a list of search results into two lists, one for "source" results --- to be used with the "build" command, and one for "binary" results to be --- used with the "install" command. --- @param results table: A search results table. --- @return (table, table): Two tables, one for source and one for binary --- results. -local function split_source_and_binary_results(results) - local sources, binaries = {}, {} - for name, versions in pairs(results) do - for version, repositories in pairs(versions) do - for _, repo in ipairs(repositories) do - local where = sources - if repo.arch == "all" or repo.arch == cfg.arch then - where = binaries - end - store_result(where, name, version, repo.arch, repo.repo) - end - end - end - return sources, binaries -end - ---- Given a name and optionally a version, try to find in the rocks --- servers a single .src.rock or .rockspec file that satisfies --- the request, and run the given function on it; or display to the --- user possibilities if it couldn't narrow down a single match. --- @param action function: A function that takes a .src.rock or --- .rockspec URL as a parameter. --- @param name string: A rock name --- @param version string or nil: A version number may also be given. --- @return The result of the action function, or nil and an error message. -function search.act_on_src_or_rockspec(action, name, version, ...) - assert(type(action) == "function") - assert(type(name) == "string") - assert(type(version) == "string" or not version) - - local query = search.make_query(name, version) - query.arch = "src|rockspec" - local url, err = search.find_suitable_rock(query) - if not url then - return nil, "Could not find a result named "..name..(version and " "..version or "")..": "..err - end - return action(url, ...) -end - -function search.pick_installed_rock(name, version, given_tree) - local results = {} - local query = search.make_query(name, version) - query.exact_name = true - local tree_map = {} - local trees = cfg.rocks_trees - if given_tree then - trees = { given_tree } - end - for _, tree in ipairs(trees) do - local rocks_dir = path.rocks_dir(tree) - tree_map[rocks_dir] = tree - search.manifest_search(results, rocks_dir, query) - end - - if not next(results) then -- - return nil,"cannot find package "..name.." "..(version or "").."\nUse 'list' to find installed rocks." - end - - version = nil - local repo_url - local package, versions = util.sortedpairs(results)() - --question: what do we do about multiple versions? This should - --give us the latest version on the last repo (which is usually the global one) - for vs, repositories in util.sortedpairs(versions, deps.compare_versions) do - if not version then version = vs end - for _, rp in ipairs(repositories) do repo_url = rp.repo end - end - - local repo = tree_map[repo_url] - return name, version, repo, repo_url -end - ---- Driver function for "search" command. --- @param name string: A substring of a rock name to search. --- @param version string or nil: a version may also be passed. --- @return boolean or (nil, string): True if build was successful; nil and an --- error message otherwise. -function search.command(flags, name, version) - if flags["all"] then - name, version = "", nil - end - - if type(name) ~= "string" and not flags["all"] then - return nil, "Enter name and version or use --all. "..util.see_help("search") - end - - local query = search.make_query(name:lower(), version) - query.exact_name = false - local results, err = search.search_repos(query) - local porcelain = flags["porcelain"] - util.title("Search results:", porcelain, "=") - local sources, binaries = split_source_and_binary_results(results) - if next(sources) and not flags["binary"] then - util.title("Rockspecs and source rocks:", porcelain) - search.print_results(sources, porcelain) - end - if next(binaries) and not flags["source"] then - util.title("Binary and pure-Lua rocks:", porcelain) - search.print_results(binaries, porcelain) - end - return true -end - -return search diff --git a/src/luarocks/base/show.lua b/src/luarocks/base/show.lua deleted file mode 100644 index 1ff81e08..00000000 --- a/src/luarocks/base/show.lua +++ /dev/null @@ -1,158 +0,0 @@ ---- Module implementing the LuaRocks "show" command. --- Shows information about an installed rock. -local show = {} - -local search = require("luarocks.search") -local cfg = require("luarocks.core.cfg") -local util = require("luarocks.util") -local path = require("luarocks.path") -local deps = require("luarocks.deps") -local fetch = require("luarocks.fetch") -local manif = require("luarocks.manif") - -show.help_summary = "Show information about an installed rock." - -show.help = [[ - is an existing package name. -Without any flags, show all module information. -With these flags, return only the desired information: - ---home home page of project ---modules all modules provided by this package as used by require() ---deps packages this package depends on ---rockspec the full path of the rockspec file ---mversion the package version ---rock-tree local tree where rock is installed ---rock-dir data directory of the installed rock -]] - -local function keys_as_string(t, sep) - local keys = util.keys(t) - table.sort(keys) - return table.concat(keys, sep or " ") -end - -local function word_wrap(line) - local width = tonumber(os.getenv("COLUMNS")) or 80 - if width > 80 then width = 80 end - if #line > width then - local brk = width - while brk > 0 and line:sub(brk, brk) ~= " " do - brk = brk - 1 - end - if brk > 0 then - return line:sub(1, brk-1) .. "\n" .. word_wrap(line:sub(brk+1)) - end - end - return line -end - -local function format_text(text) - text = text:gsub("^%s*",""):gsub("%s$", ""):gsub("\n[ \t]+","\n"):gsub("([^\n])\n([^\n])","%1 %2") - local paragraphs = util.split_string(text, "\n\n") - for n, line in ipairs(paragraphs) do - paragraphs[n] = word_wrap(line) - end - return (table.concat(paragraphs, "\n\n"):gsub("%s$", "")) -end - -local function installed_rock_label(name, tree) - local installed, version - if cfg.rocks_provided[name] then - installed, version = true, cfg.rocks_provided[name] - else - installed, version = search.pick_installed_rock(name, nil, tree) - end - return installed and "(using "..version..")" or "(missing)" -end - ---- Driver function for "show" command. --- @param name or nil: an existing package name. --- @param version string or nil: a version may also be passed. --- @return boolean: True if succeeded, nil on errors. -function show.command(flags, name, version) - if not name then - return nil, "Argument missing. "..util.see_help("show") - end - - local repo, repo_url - name, version, repo, repo_url = search.pick_installed_rock(name:lower(), version, flags["tree"]) - if not name then - return nil, version - end - - local directory = path.install_dir(name,version,repo) - local rockspec_file = path.rockspec_file(name, version, repo) - local rockspec, err = fetch.load_local_rockspec(rockspec_file) - if not rockspec then return nil,err end - - local descript = rockspec.description or {} - local manifest, err = manif.load_manifest(repo_url) - if not manifest then return nil,err end - local minfo = manifest.repository[name][version][1] - - if flags["rock-tree"] then util.printout(path.rocks_tree_to_string(repo)) - elseif flags["rock-dir"] then util.printout(directory) - elseif flags["home"] then util.printout(descript.homepage) - elseif flags["issues"] then util.printout(descript.issues_url) - elseif flags["labels"] then util.printout(descript.labels and table.concat(descript.labels, "\n")) - elseif flags["modules"] then util.printout(keys_as_string(minfo.modules, "\n")) - elseif flags["deps"] then util.printout(keys_as_string(minfo.dependencies)) - elseif flags["rockspec"] then util.printout(rockspec_file) - elseif flags["mversion"] then util.printout(version) - else - util.printout() - util.printout(rockspec.package.." "..rockspec.version.." - "..(descript.summary or "")) - util.printout() - if descript.detailed then - util.printout(format_text(descript.detailed)) - util.printout() - end - if descript.license then - util.printout("License: ", descript.license) - end - if descript.homepage then - util.printout("Homepage: ", descript.homepage) - end - if descript.issues_url then - util.printout("Issues: ", descript.issues_url) - end - if descript.labels then - util.printout("Labels: ", table.concat(descript.labels, ", ")) - end - util.printout("Installed in: ", path.rocks_tree_to_string(repo)) - if next(minfo.modules) then - util.printout() - util.printout("Modules:") - for mod, filename in util.sortedpairs(minfo.modules) do - util.printout("\t"..mod.." ("..path.which(mod, filename, name, version, repo, manifest)..")") - end - end - local direct_deps = {} - if #rockspec.dependencies > 0 then - util.printout() - util.printout("Depends on:") - for _, dep in ipairs(rockspec.dependencies) do - direct_deps[dep.name] = true - util.printout("\t"..deps.show_dep(dep).." "..installed_rock_label(dep.name, flags["tree"])) - end - end - local has_indirect_deps - for dep_name in util.sortedpairs(minfo.dependencies) do - if not direct_deps[dep_name] then - if not has_indirect_deps then - util.printout() - util.printout("Indirectly pulling:") - has_indirect_deps = true - end - - util.printout("\t"..dep_name.." "..installed_rock_label(dep_name, flags["tree"])) - end - end - util.printout() - end - return true -end - - -return show diff --git a/src/luarocks/base/unpack.lua b/src/luarocks/base/unpack.lua deleted file mode 100644 index c50701b0..00000000 --- a/src/luarocks/base/unpack.lua +++ /dev/null @@ -1,164 +0,0 @@ - ---- Module implementing the LuaRocks "unpack" command. --- Unpack the contents of a rock. -local unpack = {} - -local fetch = require("luarocks.fetch") -local fs = require("luarocks.fs") -local util = require("luarocks.util") -local build = require("luarocks.build") -local dir = require("luarocks.dir") -local cfg = require("luarocks.core.cfg") - -unpack.help_summary = "Unpack the contents of a rock." -unpack.help_arguments = "[--force] {| []}" -unpack.help = [[ -Unpacks the contents of a rock in a newly created directory. -Argument may be a rock file, or the name of a rock in a rocks server. -In the latter case, the app version may be given as a second argument. - ---force Unpack files even if the output directory already exists. -]] - ---- Load a rockspec file to the given directory, fetches the source --- files specified in the rockspec, and unpack them inside the directory. --- @param rockspec_file string: The URL for a rockspec file. --- @param dir_name string: The directory where to store and unpack files. --- @return table or (nil, string): the loaded rockspec table or --- nil and an error message. -local function unpack_rockspec(rockspec_file, dir_name) - assert(type(rockspec_file) == "string") - assert(type(dir_name) == "string") - - local rockspec, err = fetch.load_rockspec(rockspec_file) - if not rockspec then - return nil, "Failed loading rockspec "..rockspec_file..": "..err - end - local ok, err = fs.change_dir(dir_name) - if not ok then return nil, err end - local ok, sources_dir = fetch.fetch_sources(rockspec, true, ".") - if not ok then - return nil, sources_dir - end - ok, err = fs.change_dir(sources_dir) - if not ok then return nil, err end - ok, err = build.apply_patches(rockspec) - fs.pop_dir() - if not ok then return nil, err end - return rockspec -end - ---- Load a .rock file to the given directory and unpack it inside it. --- @param rock_file string: The URL for a .rock file. --- @param dir_name string: The directory where to unpack. --- @param kind string: the kind of rock file, as in the second-level --- extension in the rock filename (eg. "src", "all", "linux-x86") --- @return table or (nil, string): the loaded rockspec table or --- nil and an error message. -local function unpack_rock(rock_file, dir_name, kind) - assert(type(rock_file) == "string") - assert(type(dir_name) == "string") - - local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, dir_name) - if not ok then - return nil, "Failed unzipping rock "..rock_file, errcode - end - ok, err = fs.change_dir(dir_name) - if not ok then return nil, err end - local rockspec_file = dir_name..".rockspec" - local rockspec, err = fetch.load_rockspec(rockspec_file) - if not rockspec then - return nil, "Failed loading rockspec "..rockspec_file..": "..err - end - if kind == "src" then - if rockspec.source.file then - local ok, err = fs.unpack_archive(rockspec.source.file) - if not ok then - return nil, err - end - ok, err = fs.change_dir(rockspec.source.dir) - if not ok then return nil, err end - ok, err = build.apply_patches(rockspec) - fs.pop_dir() - if not ok then return nil, err end - end - end - return rockspec -end - ---- Create a directory and perform the necessary actions so that --- the sources for the rock and its rockspec are unpacked inside it, --- laid out properly so that the 'make' command is able to build the module. --- @param file string: A rockspec or .rock URL. --- @return boolean or (nil, string): true if successful or nil followed --- by an error message. -local function run_unpacker(file, force) - assert(type(file) == "string") - - local base_name = dir.base_name(file) - local dir_name, kind, extension = base_name:match("(.*)%.([^.]+)%.(rock)$") - if not extension then - dir_name, extension = base_name:match("(.*)%.(rockspec)$") - kind = "rockspec" - end - if not extension then - return nil, file.." does not seem to be a valid filename." - end - - local exists = fs.exists(dir_name) - if exists and not force then - return nil, "Directory "..dir_name.." already exists." - end - if not exists then - local ok, err = fs.make_dir(dir_name) - if not ok then return nil, err end - end - local rollback = util.schedule_function(fs.delete, fs.absolute_name(dir_name)) - - local rockspec, err - if extension == "rock" then - rockspec, err = unpack_rock(file, dir_name, kind) - elseif extension == "rockspec" then - rockspec, err = unpack_rockspec(file, dir_name) - end - if not rockspec then - return nil, err - end - if kind == "src" or kind == "rockspec" then - if rockspec.source.dir ~= "." then - local ok = fs.copy(rockspec.local_filename, rockspec.source.dir, cfg.perm_read) - if not ok then - return nil, "Failed copying unpacked rockspec into unpacked source directory." - end - end - util.printout() - util.printout("Done. You may now enter directory ") - util.printout(dir.path(dir_name, rockspec.source.dir)) - util.printout("and type 'luarocks make' to build.") - end - util.remove_scheduled_function(rollback) - return true -end - ---- Driver function for the "unpack" command. --- @param name string: may be a rock filename, for unpacking a --- rock file or the name of a rock to be fetched and unpacked. --- @param version string or nil: if the name of a package is given, a --- version may also be passed. --- @return boolean or (nil, string): true if successful or nil followed --- by an error message. -function unpack.command(flags, name, version) - assert(type(version) == "string" or not version) - if type(name) ~= "string" then - return nil, "Argument missing. "..util.see_help("unpack") - end - - if name:match(".*%.rock") or name:match(".*%.rockspec") then - return run_unpacker(name, flags["force"]) - else - local search = require("luarocks.search") - return search.act_on_src_or_rockspec(run_unpacker, name:lower(), version) - end -end - -return unpack diff --git a/src/luarocks/base/upload.lua b/src/luarocks/base/upload.lua deleted file mode 100644 index baee47ab..00000000 --- a/src/luarocks/base/upload.lua +++ /dev/null @@ -1,94 +0,0 @@ - -local upload = {} - -local util = require("luarocks.util") -local fetch = require("luarocks.fetch") -local pack = require("luarocks.pack") -local cfg = require("luarocks.core.cfg") -local Api = require("luarocks.upload.api") - -upload.help_summary = "Upload a rockspec to the public rocks repository." -upload.help_arguments = "[--skip-pack] [--api-key=] [--force] " -upload.help = [[ - Pack a source rock file (.src.rock extension), - upload rockspec and source rock to server. ---skip-pack Do not pack and send source rock. ---api-key= Give it an API key. It will be stored for subsequent uses. ---force Replace existing rockspec if the same revision of - a module already exists. This should be used only - in case of upload mistakes: when updating a rockspec, - increment the revision number instead. -]] - -function upload.command(flags, fname) - if not fname then - return nil, "Missing rockspec. "..util.see_help("upload") - end - - local api, err = Api.new(flags) - if not api then - return nil, err - end - if cfg.verbose then - api.debug = true - end - - local rockspec, err, errcode = fetch.load_rockspec(fname) - if err then - return nil, err, errcode - end - - util.printout("Sending " .. tostring(fname) .. " ...") - local res, err = api:method("check_rockspec", { - package = rockspec.package, - version = rockspec.version - }) - if not res then return nil, err end - - if not res.module then - util.printout("Will create new module (" .. tostring(rockspec.package) .. ")") - end - if res.version and not flags["force"] then - return nil, "Revision "..rockspec.version.." already exists on the server. "..util.see_help("upload") - end - - local rock_fname - if not flags["skip-pack"] and not rockspec.version:match("^scm") then - util.printout("Packing " .. tostring(rockspec.package)) - rock_fname, err = pack.pack_source_rock(fname) - if not rock_fname then - return nil, err - end - end - - local multipart = require("luarocks.upload.multipart") - - res, err = api:method("upload", nil, { - rockspec_file = multipart.new_file(fname) - }) - if not res then return nil, err end - - if res.is_new and #res.manifests == 0 then - util.printerr("Warning: module not added to root manifest due to name taken.") - end - - local module_url = res.module_url - - if rock_fname then - if (not res.version) or (not res.version.id) then - return nil, "Invalid response from server." - end - util.printout(("Sending " .. tostring(rock_fname) .. " ...")) - res, err = api:method("upload_rock/" .. ("%d"):format(res.version.id), nil, { - rock_file = multipart.new_file(rock_fname) - }) - if not res then return nil, err end - end - - util.printout() - util.printout("Done: " .. tostring(module_url)) - util.printout() - return true -end - -return upload diff --git a/src/luarocks/base/write_rockspec.lua b/src/luarocks/base/write_rockspec.lua deleted file mode 100644 index be563eaa..00000000 --- a/src/luarocks/base/write_rockspec.lua +++ /dev/null @@ -1,376 +0,0 @@ - -local write_rockspec = {} - -local cfg = require("luarocks.core.cfg") -local dir = require("luarocks.dir") -local fetch = require("luarocks.fetch") -local fs = require("luarocks.fs") -local path = require("luarocks.path") -local persist = require("luarocks.persist") -local type_check = require("luarocks.type_check") -local util = require("luarocks.util") -local deps = require("luarocks.deps") - -write_rockspec.help_summary = "Write a template for a rockspec file." -write_rockspec.help_arguments = "[--output= ...] [] [] [|]" -write_rockspec.help = [[ -This command writes an initial version of a rockspec file, -based on a name, a version, and a location (an URL or a local path). -If only two arguments are given, the first one is considered the name and the -second one is the location. -If only one argument is given, it must be the location. -If no arguments are given, current directory is used as location. -LuaRocks will attempt to infer name and version if not given, -using 'scm' as default version. - -Note that the generated file is a _starting point_ for writing a -rockspec, and is not guaranteed to be complete or correct. - ---output= Write the rockspec with the given filename. - If not given, a file is written in the current - directory with a filename based on given name and version. ---license="" A license string, such as "MIT/X11" or "GNU GPL v3". ---summary="" A short one-line description summary. ---detailed="" A longer description string. ---homepage= Project homepage. ---lua-version= Supported Lua versions. Accepted values are "5.1", "5.2", - "5.3", "5.1,5.2", "5.2,5.3", or "5.1,5.2,5.3". ---rockspec-format= Rockspec format version, such as "1.0" or "1.1". ---tag= Tag to use. Will attempt to extract version number from it. ---lib=[,] A comma-separated list of libraries that C files need to - link to. -]] - -local function open_file(name) - return io.open(dir.path(fs.current_dir(), name), "r") -end - -local function get_url(rockspec) - local file, temp_dir, err_code, err_file, err_temp_dir = fetch.fetch_sources(rockspec, false) - if err_code == "source.dir" then - file, temp_dir = err_file, err_temp_dir - elseif not file then - util.warning("Could not fetch sources - "..temp_dir) - return false - end - util.printout("File successfully downloaded. Making checksum and checking base dir...") - if fetch.is_basic_protocol(rockspec.source.protocol) then - rockspec.source.md5 = fs.get_md5(file) - end - local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, rockspec.source.url) - return true, found_dir or inferred_dir, temp_dir -end - -local function configure_lua_version(rockspec, luaver) - if luaver == "5.1" then - table.insert(rockspec.dependencies, "lua ~> 5.1") - elseif luaver == "5.2" then - table.insert(rockspec.dependencies, "lua ~> 5.2") - elseif luaver == "5.3" then - table.insert(rockspec.dependencies, "lua ~> 5.3") - elseif luaver == "5.1,5.2" then - table.insert(rockspec.dependencies, "lua >= 5.1, < 5.3") - elseif luaver == "5.2,5.3" then - table.insert(rockspec.dependencies, "lua >= 5.2, < 5.4") - elseif luaver == "5.1,5.2,5.3" then - table.insert(rockspec.dependencies, "lua >= 5.1, < 5.4") - else - util.warning("Please specify supported Lua version with --lua-version=. "..util.see_help("write_rockspec")) - end -end - -local function detect_description() - local fd = open_file("README.md") or open_file("README") - if not fd then return end - local data = fd:read("*a") - fd:close() - local paragraph = data:match("\n\n([^%[].-)\n\n") - if not paragraph then paragraph = data:match("\n\n(.*)") end - local summary, detailed - if paragraph then - detailed = paragraph - - if #paragraph < 80 then - summary = paragraph:gsub("\n", "") - else - summary = paragraph:gsub("\n", " "):match("([^.]*%.) ") - end - end - return summary, detailed -end - -local function detect_mit_license(data) - local strip_copyright = (data:gsub("Copyright [^\n]*\n", "")) - local sum = 0 - for i = 1, #strip_copyright do - local num = string.byte(strip_copyright:sub(i,i)) - if num > 32 and num <= 128 then - sum = sum + num - end - end - return sum == 78656 -end - -local simple_scm_protocols = { - git = true, ["git+http"] = true, ["git+https"] = true, - hg = true, ["hg+http"] = true, ["hg+https"] = true -} - -local function detect_url_from_command(program, args, directory) - local command = fs.Q(cfg.variables[program:upper()]).. " "..args - local pipe = io.popen(fs.command_at(directory, fs.quiet_stderr(command))) - if not pipe then return nil end - local url = pipe:read("*a"):match("^([^\r\n]+)") - pipe:close() - if not url then return nil end - if not util.starts_with(url, program.."://") then - url = program.."+"..url - end - - if simple_scm_protocols[dir.split_url(url)] then - return url - end -end - -local function detect_scm_url(directory) - return detect_url_from_command("git", "config --get remote.origin.url", directory) or - detect_url_from_command("hg", "paths default", directory) -end - -local function show_license(rockspec) - local fd = open_file("COPYING") or open_file("LICENSE") or open_file("MIT-LICENSE.txt") - if not fd then return nil end - local data = fd:read("*a") - fd:close() - local is_mit = detect_mit_license(data) - util.title("License for "..rockspec.package..":") - util.printout(data) - util.printout() - return is_mit -end - -local function get_cmod_name(file) - local fd = open_file(file) - if not fd then return nil end - local data = fd:read("*a") - fd:close() - return (data:match("int%s+luaopen_([a-zA-Z0-9_]+)")) -end - -local luamod_blacklist = { - test = true, - tests = true, -} - -local function fill_as_builtin(rockspec, libs) - rockspec.build.type = "builtin" - rockspec.build.modules = {} - local prefix = "" - - for _, parent in ipairs({"src", "lua"}) do - if fs.is_dir(parent) then - fs.change_dir(parent) - prefix = parent.."/" - break - end - end - - local incdirs, libdirs - if libs then - incdirs, libdirs = {}, {} - for _, lib in ipairs(libs) do - local upper = lib:upper() - incdirs[#incdirs+1] = "$("..upper.."_INCDIR)" - libdirs[#libdirs+1] = "$("..upper.."_LIBDIR)" - end - end - - for _, file in ipairs(fs.find()) do - local luamod = file:match("(.*)%.lua$") - if luamod and not luamod_blacklist[luamod] then - rockspec.build.modules[path.path_to_module(file)] = prefix..file - else - local cmod = file:match("(.*)%.c$") - if cmod then - local modname = get_cmod_name(file) or path.path_to_module(file:gsub("%.c$", ".lua")) - rockspec.build.modules[modname] = { - sources = prefix..file, - libraries = libs, - incdirs = incdirs, - libdirs = libdirs, - } - end - end - end - - for _, directory in ipairs({ "doc", "docs", "samples", "tests" }) do - if fs.is_dir(directory) then - if not rockspec.build.copy_directories then - rockspec.build.copy_directories = {} - end - table.insert(rockspec.build.copy_directories, directory) - end - end - - if prefix ~= "" then - fs.pop_dir() - end -end - -local function rockspec_cleanup(rockspec) - rockspec.source.file = nil - rockspec.source.protocol = nil - rockspec.variables = nil - rockspec.name = nil - rockspec.format_is_at_least = nil -end - -function write_rockspec.command(flags, name, version, url_or_dir) - if not name then - url_or_dir = "." - elseif not version then - url_or_dir = name - name = nil - elseif not url_or_dir then - url_or_dir = version - version = nil - end - - if flags["tag"] then - if not version then - version = flags["tag"]:gsub("^v", "") - end - end - - local protocol, pathname = dir.split_url(url_or_dir) - if protocol == "file" then - if pathname == "." then - name = name or dir.base_name(fs.current_dir()) - end - elseif fetch.is_basic_protocol(protocol) then - local filename = dir.base_name(url_or_dir) - local newname, newversion = filename:match("(.*)-([^-]+)") - if newname then - name = name or newname - version = version or newversion:gsub("%.[a-z]+$", ""):gsub("%.tar$", "") - end - else - name = name or dir.base_name(url_or_dir):gsub("%.[^.]+$", "") - end - - if not name then - return nil, "Could not infer rock name. "..util.see_help("write_rockspec") - end - version = version or "scm" - - local filename = flags["output"] or dir.path(fs.current_dir(), name:lower().."-"..version.."-1.rockspec") - - local rockspec = { - rockspec_format = flags["rockspec-format"], - package = name, - name = name:lower(), - version = version.."-1", - source = { - url = "*** please add URL for source tarball, zip or repository here ***", - tag = flags["tag"], - }, - description = { - summary = flags["summary"] or "*** please specify description summary ***", - detailed = flags["detailed"] or "*** please enter a detailed description ***", - homepage = flags["homepage"] or "*** please enter a project homepage ***", - license = flags["license"] or "*** please specify a license ***", - }, - dependencies = {}, - build = {}, - } - path.configure_paths(rockspec) - rockspec.source.protocol = protocol - rockspec.format_is_at_least = deps.format_is_at_least - - configure_lua_version(rockspec, flags["lua-version"]) - - local local_dir = url_or_dir - - if url_or_dir:match("://") then - rockspec.source.url = url_or_dir - rockspec.source.file = dir.base_name(url_or_dir) - rockspec.source.dir = "dummy" - if not fetch.is_basic_protocol(rockspec.source.protocol) then - if version ~= "scm" then - rockspec.source.tag = flags["tag"] or "v" .. version - end - end - rockspec.source.dir = nil - local ok, base_dir, temp_dir = get_url(rockspec) - if ok then - if base_dir ~= dir.base_name(url_or_dir) then - rockspec.source.dir = base_dir - end - end - if base_dir then - local_dir = dir.path(temp_dir, base_dir) - else - local_dir = nil - end - else - rockspec.source.url = detect_scm_url(local_dir) or rockspec.source.url - end - - if not local_dir then - local_dir = "." - end - - if not flags["homepage"] then - local url_protocol, url_path = dir.split_url(rockspec.source.url) - - if simple_scm_protocols[url_protocol] then - for _, domain in ipairs({"github.com", "bitbucket.org", "gitlab.com"}) do - if util.starts_with(url_path, domain) then - rockspec.description.homepage = "https://"..url_path:gsub("%.git$", "") - break - end - end - end - end - - local libs = nil - if flags["lib"] then - libs = {} - rockspec.external_dependencies = {} - for lib in flags["lib"]:gmatch("([^,]+)") do - table.insert(libs, lib) - rockspec.external_dependencies[lib:upper()] = { - library = lib - } - end - end - - local ok, err = fs.change_dir(local_dir) - if not ok then return nil, "Failed reaching files from project - error entering directory "..local_dir end - - if (not flags["summary"]) or (not flags["detailed"]) then - local summary, detailed = detect_description() - rockspec.description.summary = flags["summary"] or summary - rockspec.description.detailed = flags["detailed"] or detailed - end - - local is_mit = show_license(rockspec) - - if is_mit and not flags["license"] then - rockspec.description.license = "MIT" - end - - fill_as_builtin(rockspec, libs) - - rockspec_cleanup(rockspec) - - persist.save_from_table(filename, rockspec, type_check.rockspec_order) - - util.printout() - util.printout("Wrote template at "..filename.." -- you should now edit and finish it.") - util.printout() - - return true -end - -return write_rockspec diff --git a/src/luarocks/build.lua b/src/luarocks/build.lua new file mode 100644 index 00000000..f3b054d2 --- /dev/null +++ b/src/luarocks/build.lua @@ -0,0 +1,443 @@ + +--- Module implementing the LuaRocks "build" command. +-- Builds a rock, compiling its C parts if any. +local build = {} + +local pack = require("luarocks.pack") +local path = require("luarocks.path") +local util = require("luarocks.util") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local deps = require("luarocks.deps") +local writer = require("luarocks.manif.writer") +local remove = require("luarocks.remove") +local cfg = require("luarocks.core.cfg") + +build.help_summary = "Build/compile a rock." +build.help_arguments = "[--pack-binary-rock] [--keep] {|| []}" +build.help = [[ +Build and install a rock, compiling its C parts if any. +Argument may be a rockspec file, a source rock file +or the name of a rock to be fetched from a repository. + +--pack-binary-rock Do not install rock. Instead, produce a .rock file + with the contents of compilation in the current + directory. + +--keep Do not remove previously installed versions of the + rock after building a new one. This behavior can + be made permanent by setting keep_other_versions=true + in the configuration file. + +--branch= Override the `source.branch` field in the loaded + rockspec. Allows to specify a different branch to + fetch. Particularly for SCM rocks. + +--only-deps Installs only the dependencies of the rock. + +]]..util.deps_mode_help() + +--- Install files to a given location. +-- Takes a table where the array part is a list of filenames to be copied. +-- In the hash part, other keys, if is_module_path is set, are identifiers +-- in Lua module format, to indicate which subdirectory the file should be +-- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo") +-- will copy src/bar.lua to boo/foo. +-- @param files table or nil: A table containing a list of files to copy in +-- the format described above. If nil is passed, this function is a no-op. +-- Directories should be delimited by forward slashes as in internet URLs. +-- @param location string: The base directory files should be copied to. +-- @param is_module_path boolean: True if string keys in files should be +-- interpreted as dotted module paths. +-- @param perms string: Permissions of the newly created files installed. +-- Directories are always created with the default permissions. +-- @return boolean or (nil, string): True if succeeded or +-- nil and an error message. +local function install_files(files, location, is_module_path, perms) + assert(type(files) == "table" or not files) + assert(type(location) == "string") + if files then + for k, file in pairs(files) do + local dest = location + local filename = dir.base_name(file) + if type(k) == "string" then + local modname = k + if is_module_path then + dest = dir.path(location, path.module_to_path(modname)) + local ok, err = fs.make_dir(dest) + if not ok then return nil, err end + if filename:match("%.lua$") then + local basename = modname:match("([^.]+)$") + filename = basename..".lua" + end + else + dest = dir.path(location, dir.dir_name(modname)) + local ok, err = fs.make_dir(dest) + if not ok then return nil, err end + filename = dir.base_name(modname) + end + else + local ok, err = fs.make_dir(dest) + if not ok then return nil, err end + end + local ok = fs.copy(dir.path(file), dir.path(dest, filename), perms) + if not ok then + return nil, "Failed copying "..file + end + end + end + return true +end + +--- Write to the current directory the contents of a table, +-- where each key is a file name and its value is the file content. +-- @param files table: The table of files to be written. +local function extract_from_rockspec(files) + for name, content in pairs(files) do + local fd = io.open(dir.path(fs.current_dir(), name), "w+") + fd:write(content) + fd:close() + end +end + +--- Applies patches inlined in the build.patches section +-- and extracts files inlined in the build.extra_files section +-- of a rockspec. +-- @param rockspec table: A rockspec table. +-- @return boolean or (nil, string): True if succeeded or +-- nil and an error message. +function build.apply_patches(rockspec) + assert(type(rockspec) == "table") + + local build_spec = rockspec.build + if build_spec.extra_files then + extract_from_rockspec(build_spec.extra_files) + end + if build_spec.patches then + extract_from_rockspec(build_spec.patches) + for patch, patchdata in util.sortedpairs(build_spec.patches) do + util.printout("Applying patch "..patch.."...") + local ok, err = fs.apply_patch(tostring(patch), patchdata) + if not ok then + return nil, "Failed applying patch "..patch + end + end + end + return true +end + +local function install_default_docs(name, version) + local patterns = { "readme", "license", "copying", ".*%.md" } + local dest = dir.path(path.install_dir(name, version), "doc") + local has_dir = false + for file in fs.dir() do + for _, pattern in ipairs(patterns) do + if file:lower():match("^"..pattern) then + if not has_dir then + fs.make_dir(dest) + has_dir = true + end + fs.copy(file, dest, cfg.perm_read) + break + end + end + end +end + +local function check_macosx_deployment_target(rockspec) + local target = rockspec.build.macosx_deployment_target + local function minor(version) + return tonumber(version and version:match("^[^.]+%.([^.]+)")) + end + local function patch_variable(var, target) + if rockspec.variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then + rockspec.variables[var] = (rockspec.variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET="..target) + else + rockspec.variables[var] = "env MACOSX_DEPLOYMENT_TARGET="..target.." "..rockspec.variables[var] + end + end + if cfg.platforms.macosx and rockspec:format_is_at_least("3.0") and target then + local version = util.popen_read("sw_vers -productVersion") + local versionminor = minor(version) + local targetminor = minor(target) + if targetminor > versionminor then + return nil, ("This rock requires Mac OSX 10.%d, and you are running 10.%d."):format(targetminor, versionminor) + end + patch_variable("CC", target) + patch_variable("LD", target) + end + return true +end + +--- Build and install a rock given a rockspec. +-- @param rockspec_file string: local or remote filename of a rockspec. +-- @param need_to_fetch boolean: true if sources need to be fetched, +-- false if the rockspec was obtained from inside a source rock. +-- @param minimal_mode boolean: true if there's no need to fetch, +-- unpack or change dir (this is used by "luarocks make"). Implies +-- need_to_fetch = false. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for no trees. +-- @param build_only_deps boolean: true to build the listed dependencies only. +-- @return (string, string) or (nil, string, [string]): Name and version of +-- installed rock if succeeded or nil and an error message followed by an error code. +function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode, build_only_deps) + assert(type(rockspec_file) == "string") + assert(type(need_to_fetch) == "boolean") + + local rockspec, err, errcode = fetch.load_rockspec(rockspec_file) + if err then + return nil, err, errcode + elseif not rockspec.build then + return nil, "Rockspec error: build table not specified" + elseif not rockspec.build.type then + return nil, "Rockspec error: build type not specified" + end + + local ok + if not build_only_deps then + ok, err, errcode = deps.check_external_deps(rockspec, "build") + if err then + return nil, err, errcode + end + end + + if deps_mode == "none" then + util.printerr("Warning: skipping dependency checks.") + else + local ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) + if err then + return nil, err, errcode + end + end + + local name, version = rockspec.name, rockspec.version + if build_only_deps then + util.printout("Stopping after installing dependencies for " ..name.." "..version) + util.printout() + return name, version + end + + if repos.is_installed(name, version) then + repos.delete_version(name, version, deps_mode) + end + + if not minimal_mode then + local source_dir + if need_to_fetch then + ok, source_dir, errcode = fetch.fetch_sources(rockspec, true) + if not ok then + return nil, source_dir, errcode + end + local ok, err = fs.change_dir(source_dir) + if not ok then return nil, err end + elseif rockspec.source.file then + local ok, err = fs.unpack_archive(rockspec.source.file) + if not ok then + return nil, err + end + end + fs.change_dir(rockspec.source.dir) + end + + local dirs = { + lua = { name = path.lua_dir(name, version), is_module_path = true, perms = cfg.perm_read }, + lib = { name = path.lib_dir(name, version), is_module_path = true, perms = cfg.perm_exec }, + conf = { name = path.conf_dir(name, version), is_module_path = false, perms = cfg.perm_read }, + bin = { name = path.bin_dir(name, version), is_module_path = false, perms = cfg.perm_exec }, + } + + for _, d in pairs(dirs) do + local ok, err = fs.make_dir(d.name) + if not ok then return nil, err end + end + local rollback = util.schedule_function(function() + fs.delete(path.install_dir(name, version)) + fs.remove_dir_if_empty(path.versions_dir(name)) + end) + + local build_spec = rockspec.build + + if not minimal_mode then + ok, err = build.apply_patches(rockspec) + if err then + return nil, err + end + end + + ok, err = check_macosx_deployment_target(rockspec) + if not ok then + return nil, err + end + + if build_spec.type ~= "none" then + + -- Temporary compatibility + if build_spec.type == "module" then + util.printout("Do not use 'module' as a build type. Use 'builtin' instead.") + build_spec.type = "builtin" + end + + if cfg.accepted_build_types and util.array_contains(cfg.accepted_build_types, build_spec.type) then + return nil, "This rockspec uses the '"..build_spec.type.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration." + end + + local build_type + ok, build_type = pcall(require, "luarocks.build." .. build_spec.type) + if not ok or not type(build_type) == "table" then + return nil, "Failed initializing build back-end for build type '"..build_spec.type.."': "..build_type + end + + ok, err = build_type.run(rockspec) + if not ok then + return nil, "Build error: " .. err + end + end + + if build_spec.install then + for id, install_dir in pairs(dirs) do + ok, err = install_files(build_spec.install[id], install_dir.name, install_dir.is_module_path, install_dir.perms) + if not ok then + return nil, err + end + end + end + + local copy_directories = build_spec.copy_directories + local copying_default = false + if not copy_directories then + copy_directories = {"doc"} + copying_default = true + end + + local any_docs = false + for _, copy_dir in pairs(copy_directories) do + if fs.is_dir(copy_dir) then + local dest = dir.path(path.install_dir(name, version), copy_dir) + fs.make_dir(dest) + fs.copy_contents(copy_dir, dest) + any_docs = true + else + if not copying_default then + return nil, "Directory '"..copy_dir.."' not found" + end + end + end + + if not any_docs then + install_default_docs(name, version) + end + + for _, d in pairs(dirs) do + fs.remove_dir_if_empty(d.name) + end + + fs.pop_dir() + + fs.copy(rockspec.local_filename, path.rockspec_file(name, version), cfg.perm_read) + if need_to_fetch then + fs.pop_dir() + end + + ok, err = writer.make_rock_manifest(name, version) + if err then return nil, err end + + ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) + if err then return nil, err end + + util.remove_scheduled_function(rollback) + rollback = util.schedule_function(function() + repos.delete_version(name, version, deps_mode) + end) + + ok, err = repos.run_hook(rockspec, "post_install") + if err then return nil, err end + + util.announce_install(rockspec) + util.remove_scheduled_function(rollback) + return name, version +end + +--- Build and install a rock. +-- @param rock_file string: local or remote filename of a rock. +-- @param need_to_fetch boolean: true if sources need to be fetched, +-- false if the rockspec was obtained from inside a source rock. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @param build_only_deps boolean: true to build the listed dependencies only. +-- @return boolean or (nil, string, [string]): True if build was successful, +-- or false and an error message and an optional error code. +function build.build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps) + assert(type(rock_file) == "string") + assert(type(need_to_fetch) == "boolean") + + local ok, err, errcode + local unpack_dir + unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file) + if not unpack_dir then + return nil, err, errcode + end + local rockspec_file = path.rockspec_name_from_rock(rock_file) + ok, err = fs.change_dir(unpack_dir) + if not ok then return nil, err end + ok, err, errcode = build.build_rockspec(rockspec_file, need_to_fetch, false, deps_mode, build_only_deps) + fs.pop_dir() + return ok, err, errcode +end + +local function do_build(name, version, deps_mode, build_only_deps) + if name:match("%.rockspec$") then + return build.build_rockspec(name, true, false, deps_mode, build_only_deps) + elseif name:match("%.src%.rock$") then + return build.build_rock(name, false, deps_mode, build_only_deps) + elseif name:match("%.all%.rock$") then + local install = require("luarocks.install") + local install_fun = build_only_deps and install.install_binary_rock_deps or install.install_binary_rock + return install_fun(name, deps_mode) + elseif name:match("%.rock$") then + return build.build_rock(name, true, deps_mode, build_only_deps) + elseif not name:match(dir.separator) then + local search = require("luarocks.search") + return search.act_on_src_or_rockspec(do_build, name:lower(), version, nil, deps_mode, build_only_deps) + end + return nil, "Don't know what to do with "..name +end + +--- Driver function for "build" command. +-- @param name string: A local or remote rockspec or rock file. +-- If a package name is given, forwards the request to "search" and, +-- if returned a result, installs the matching rock. +-- @param version string: When passing a package name, a version number may +-- also be given. +-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an +-- error message otherwise. exitcode is optionally returned. +function build.command(flags, name, version) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("build") + end + assert(type(version) == "string" or not version) + + if flags["pack-binary-rock"] then + return pack.pack_binary_rock(name, version, do_build, name, version, deps.get_deps_mode(flags)) + else + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + ok, err = do_build(name, version, deps.get_deps_mode(flags), flags["only-deps"]) + if not ok then return nil, err end + name, version = ok, err + if flags["only-deps"] then + return name, version + end + if (not flags["keep"]) and not cfg.keep_other_versions then + local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) + if not ok then util.printerr(err) end + end + return name, version + end +end + +return build diff --git a/src/luarocks/download.lua b/src/luarocks/download.lua new file mode 100644 index 00000000..557d1b65 --- /dev/null +++ b/src/luarocks/download.lua @@ -0,0 +1,107 @@ + +--- Module implementing the luarocks "download" command. +-- Download a rock from the repository. +local download = {} + +local util = require("luarocks.util") +local path = require("luarocks.path") +local fetch = require("luarocks.fetch") +local search = require("luarocks.search") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local cfg = require("luarocks.core.cfg") + +download.help_summary = "Download a specific rock file from a rocks server." +download.help_arguments = "[--all] [--arch= | --source | --rockspec] [ []]" + +download.help = [[ +--all Download all files if there are multiple matches. +--source Download .src.rock if available. +--rockspec Download .rockspec if available. +--arch= Download rock for a specific architecture. +]] + +local function get_file(filename) + local protocol, pathname = dir.split_url(filename) + if protocol == "file" then + local ok, err = fs.copy(pathname, fs.current_dir(), cfg.perm_read) + if ok then + return pathname + else + return nil, err + end + else + return fetch.fetch_url(filename) + end +end + +function download.download(arch, name, version, all) + local query = search.make_query(name, version) + if arch then query.arch = arch end + local search_err + + if all then + if name == "" then query.exact_name = false end + local results = search.search_repos(query) + local has_result = false + local all_ok = true + local any_err = "" + for name, result in pairs(results) do + for version, items in pairs(result) do + for _, item in ipairs(items) do + -- Ignore provided rocks. + if item.arch ~= "installed" then + has_result = true + local filename = path.make_url(item.repo, name, version, item.arch) + local ok, err = get_file(filename) + if not ok then + all_ok = false + any_err = any_err .. "\n" .. err + end + end + end + end + end + + if has_result then + return all_ok, any_err + end + else + local url + url, search_err = search.find_suitable_rock(query) + if url then + return get_file(url) + end + end + return nil, "Could not find a result named "..name..(version and " "..version or "").. + (search_err and ": "..search_err or ".") +end + +--- Driver function for the "download" command. +-- @param name string: a rock name. +-- @param version string or nil: if the name of a package is given, a +-- version may also be passed. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +function download.command(flags, name, version) + assert(type(version) == "string" or not version) + if type(name) ~= "string" and not flags["all"] then + return nil, "Argument missing. "..util.see_help("download") + end + if not name then name, version = "", "" end + + local arch + + if flags["source"] then + arch = "src" + elseif flags["rockspec"] then + arch = "rockspec" + elseif flags["arch"] then + arch = flags["arch"] + end + + local dl, err = download.download(arch, name:lower(), version, flags["all"]) + return dl and true, err +end + +return download diff --git a/src/luarocks/pack.lua b/src/luarocks/pack.lua new file mode 100644 index 00000000..655cbf37 --- /dev/null +++ b/src/luarocks/pack.lua @@ -0,0 +1,193 @@ + +--- Module implementing the LuaRocks "pack" command. +-- Creates a rock, packing sources or binaries. +local pack = {} + +local unpack = unpack or table.unpack + +local path = require("luarocks.path") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") +local manif = require("luarocks.manif") +local search = require("luarocks.search") + +pack.help_summary = "Create a rock, packing sources or binaries." +pack.help_arguments = "{| []}" +pack.help = [[ +Argument may be a rockspec file, for creating a source rock, +or the name of an installed package, for creating a binary rock. +In the latter case, the app version may be given as a second +argument. +]] + +--- Create a source rock. +-- Packages a rockspec and its required source files in a rock +-- file with the .src.rock extension, which can later be built and +-- installed with the "build" command. +-- @param rockspec_file string: An URL or pathname for a rockspec file. +-- @return string or (nil, string): The filename of the resulting +-- .src.rock file; or nil and an error message. +function pack.pack_source_rock(rockspec_file) + assert(type(rockspec_file) == "string") + + local rockspec, err = fetch.load_rockspec(rockspec_file) + if err then + return nil, "Error loading rockspec: "..err + end + rockspec_file = rockspec.local_filename + + local name_version = rockspec.name .. "-" .. rockspec.version + local rock_file = fs.absolute_name(name_version .. ".src.rock") + + local source_file, source_dir = fetch.fetch_sources(rockspec, false) + if not source_file then + return nil, source_dir + end + local ok, err = fs.change_dir(source_dir) + if not ok then return nil, err end + + fs.delete(rock_file) + fs.copy(rockspec_file, source_dir, cfg.perm_read) + if not fs.zip(rock_file, dir.base_name(rockspec_file), dir.base_name(source_file)) then + return nil, "Failed packing "..rock_file + end + fs.pop_dir() + + return rock_file +end + +local function copy_back_files(name, version, file_tree, deploy_dir, pack_dir, perms) + local ok, err = fs.make_dir(pack_dir) + if not ok then return nil, err end + for file, sub in pairs(file_tree) do + local source = dir.path(deploy_dir, file) + local target = dir.path(pack_dir, file) + if type(sub) == "table" then + local ok, err = copy_back_files(name, version, sub, source, target) + if not ok then return nil, err end + else + local versioned = path.versioned_name(source, deploy_dir, name, version) + if fs.exists(versioned) then + fs.copy(versioned, target, perms) + else + fs.copy(source, target, perms) + end + end + end + return true +end + +-- @param name string: Name of package to pack. +-- @param version string or nil: A version number may also be passed. +-- @param tree string or nil: An optional tree to pick the package from. +-- @return string or (nil, string): The filename of the resulting +-- .src.rock file; or nil and an error message. +local function do_pack_binary_rock(name, version, tree) + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local repo, repo_url + name, version, repo, repo_url = search.pick_installed_rock(name, version, tree) + if not name then + return nil, version + end + + local root = path.root_dir(repo_url) + local prefix = path.install_dir(name, version, root) + if not fs.exists(prefix) then + return nil, "'"..name.." "..version.."' does not seem to be an installed rock." + end + + local rock_manifest, err = manif.load_rock_manifest(name, version, root) + if not rock_manifest then return nil, err end + + local name_version = name .. "-" .. version + local rock_file = fs.absolute_name(name_version .. "."..cfg.arch..".rock") + + local temp_dir = fs.make_temp_dir("pack") + fs.copy_contents(prefix, temp_dir) + + local is_binary = false + if rock_manifest.lib then + local ok, err = copy_back_files(name, version, rock_manifest.lib, path.deploy_lib_dir(root), dir.path(temp_dir, "lib"), cfg.perm_exec) + if not ok then return nil, "Failed copying back files: " .. err end + is_binary = true + end + if rock_manifest.lua then + local ok, err = copy_back_files(name, version, rock_manifest.lua, path.deploy_lua_dir(root), dir.path(temp_dir, "lua"), cfg.perm_read) + if not ok then return nil, "Failed copying back files: " .. err end + end + + local ok, err = fs.change_dir(temp_dir) + if not ok then return nil, err end + if not is_binary and not repos.has_binaries(name, version) then + rock_file = rock_file:gsub("%."..cfg.arch:gsub("%-","%%-").."%.", ".all.") + end + fs.delete(rock_file) + if not fs.zip(rock_file, unpack(fs.list_dir())) then + return nil, "Failed packing "..rock_file + end + fs.pop_dir() + fs.delete(temp_dir) + return rock_file +end + +function pack.pack_binary_rock(name, version, cmd, ...) + + -- The --pack-binary-rock option for "luarocks build" basically performs + -- "luarocks build" on a temporary tree and then "luarocks pack". The + -- alternative would require refactoring parts of luarocks.build and + -- luarocks.pack, which would save a few file operations: the idea would be + -- to shave off the final deploy steps from the build phase and the initial + -- collect steps from the pack phase. + + local temp_dir, err = fs.make_temp_dir("luarocks-build-pack-"..dir.base_name(name)) + if not temp_dir then + return nil, "Failed creating temporary directory: "..err + end + util.schedule_function(fs.delete, temp_dir) + + path.use_tree(temp_dir) + local ok, err = cmd(...) + if not ok then + return nil, err + end + local rname, rversion = path.parse_name(name) + if not rname then + rname, rversion = name, version + end + return do_pack_binary_rock(rname, rversion, temp_dir) +end + +--- Driver function for the "pack" command. +-- @param arg string: may be a rockspec file, for creating a source rock, +-- or the name of an installed package, for creating a binary rock. +-- @param version string or nil: if the name of a package is given, a +-- version may also be passed. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +function pack.command(flags, arg, version) + assert(type(version) == "string" or not version) + if type(arg) ~= "string" then + return nil, "Argument missing. "..util.see_help("pack") + end + + local file, err + if arg:match(".*%.rockspec") then + file, err = pack.pack_source_rock(arg) + else + file, err = do_pack_binary_rock(arg:lower(), version, flags["tree"]) + end + if err then + return nil, err + else + util.printout("Packed: "..file) + return true + end +end + +return pack diff --git a/src/luarocks/remove.lua b/src/luarocks/remove.lua new file mode 100644 index 00000000..e7f37604 --- /dev/null +++ b/src/luarocks/remove.lua @@ -0,0 +1,165 @@ + +--- Module implementing the LuaRocks "remove" command. +-- Uninstalls rocks. +local remove = {} + +local search = require("luarocks.search") +local deps = require("luarocks.deps") +local fetch = require("luarocks.fetch") +local repos = require("luarocks.repos") +local path = require("luarocks.path") +local util = require("luarocks.util") +local cfg = require("luarocks.core.cfg") +local fs = require("luarocks.fs") + +remove.help_summary = "Uninstall a rock." +remove.help_arguments = "[--force|--force-fast] []" +remove.help = [[ +Argument is the name of a rock to be uninstalled. +If a version is not given, try to remove all versions at once. +Will only perform the removal if it does not break dependencies. +To override this check and force the removal, use --force. +To perform a forced removal without reporting dependency issues, +use --force-fast. + +]]..util.deps_mode_help() + +--- Obtain a list of packages that depend on the given set of packages +-- (where all packages of the set are versions of one program). +-- @param name string: the name of a program +-- @param versions array of string: the versions to be deleted. +-- @return array of string: an empty table if no packages depend on any +-- of the given list, or an array of strings in "name/version" format. +local function check_dependents(name, versions, deps_mode) + local dependents = {} + local blacklist = {} + blacklist[name] = {} + for version, _ in pairs(versions) do + blacklist[name][version] = true + end + local local_rocks = {} + local query_all = search.make_query("") + query_all.exact_name = false + search.manifest_search(local_rocks, cfg.rocks_dir, query_all) + local_rocks[name] = nil + for rock_name, rock_versions in pairs(local_rocks) do + for rock_version, _ in pairs(rock_versions) do + local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version)) + if rockspec then + local _, missing = deps.match_deps(rockspec, blacklist, deps_mode) + if missing[name] then + table.insert(dependents, { name = rock_name, version = rock_version }) + end + end + end + end + return dependents +end + +--- Delete given versions of a program. +-- @param name string: the name of a program +-- @param versions array of string: the versions to be deleted. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @return boolean or (nil, string): true on success or nil and an error message. +local function delete_versions(name, versions, deps_mode) + + for version, _ in pairs(versions) do + util.printout("Removing "..name.." "..version.."...") + local ok, err = repos.delete_version(name, version, deps_mode) + if not ok then return nil, err end + end + + return true +end + +function remove.remove_search_results(results, name, deps_mode, force, fast) + local versions = results[name] + + local version = next(versions) + local second = next(versions, version) + + local dependents = {} + if not fast then + util.printout("Checking stability of dependencies in the absence of") + util.printout(name.." "..table.concat(util.keys(versions), ", ").."...") + util.printout() + dependents = check_dependents(name, versions, deps_mode) + end + + if #dependents > 0 then + if force or fast then + util.printerr("The following packages may be broken by this forced removal:") + for _, dependent in ipairs(dependents) do + util.printerr(dependent.name.." "..dependent.version) + end + util.printerr() + else + if not second then + util.printerr("Will not remove "..name.." "..version..".") + util.printerr("Removing it would break dependencies for: ") + else + util.printerr("Will not remove installed versions of "..name..".") + util.printerr("Removing them would break dependencies for: ") + end + for _, dependent in ipairs(dependents) do + util.printerr(dependent.name.." "..dependent.version) + end + util.printerr() + util.printerr("Use --force to force removal (warning: this may break modules).") + return nil, "Failed removing." + end + end + + local ok, err = delete_versions(name, versions, deps_mode) + if not ok then return nil, err end + + util.printout("Removal successful.") + return true +end + +function remove.remove_other_versions(name, version, force, fast) + local results = {} + search.manifest_search(results, cfg.rocks_dir, { name = name, exact_name = true, constraints = {{ op = "~=", version = version}} }) + if results[name] then + return remove.remove_search_results(results, name, cfg.deps_mode, force, fast) + end + return true +end + +--- Driver function for the "remove" command. +-- @param name string: name of a rock. If a version is given, refer to +-- a specific version; otherwise, try to remove all versions. +-- @param version string: When passing a package name, a version number +-- may also be given. +-- @return boolean or (nil, string, exitcode): True if removal was +-- successful, nil and an error message otherwise. exitcode is optionally returned. +function remove.command(flags, name, version) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("remove") + end + + local deps_mode = flags["deps-mode"] or cfg.deps_mode + + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + + local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$") + local filename = name + if rock_type then + name, version = path.parse_name(filename) + if not name then return nil, "Invalid "..rock_type.." filename: "..filename end + end + + local results = {} + name = name:lower() + search.manifest_search(results, cfg.rocks_dir, search.make_query(name, version)) + if not results[name] then + return nil, "Could not find rock '"..name..(version and " "..version or "").."' in "..path.rocks_tree_to_string(cfg.root_dir) + end + + return remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"]) +end + +return remove diff --git a/src/luarocks/search.lua b/src/luarocks/search.lua new file mode 100644 index 00000000..44eff694 --- /dev/null +++ b/src/luarocks/search.lua @@ -0,0 +1,482 @@ + +--- Module implementing the LuaRocks "search" command. +-- Queries LuaRocks servers. +local search = {} + + +local dir = require("luarocks.dir") +local path = require("luarocks.path") +local manif = require("luarocks.manif") +local deps = require("luarocks.deps") +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") + +search.help_summary = "Query the LuaRocks servers." +search.help_arguments = "[--source] [--binary] { [] | --all }" +search.help = [[ +--source Return only rockspecs and source rocks, + to be used with the "build" command. +--binary Return only pure Lua and binary rocks (rocks that can be used + with the "install" command without requiring a C toolchain). +--all List all contents of the server that are suitable to + this platform, do not filter by name. +]] + +--- Convert the arch field of a query table to table format. +-- @param query table: A query table. +local function query_arch_as_table(query) + local format = type(query.arch) + if format == "table" then + return + elseif format == "nil" then + local accept = {} + accept["src"] = true + accept["all"] = true + accept["rockspec"] = true + accept["installed"] = true + accept[cfg.arch] = true + query.arch = accept + elseif format == "string" then + local accept = {} + for a in query.arch:gmatch("[%w_-]+") do + accept[a] = true + end + query.arch = accept + end +end + +--- Store a search result (a rock or rockspec) in the results table. +-- @param results table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- @param name string: Package name. +-- @param version string: Package version. +-- @param arch string: Architecture of rock ("all", "src" or platform +-- identifier), "rockspec" or "installed" +-- @param repo string: Pathname of a local repository of URL of +-- rocks server. +local function store_result(results, name, version, arch, repo) + assert(type(results) == "table") + assert(type(name) == "string") + assert(type(version) == "string") + assert(type(arch) == "string") + assert(type(repo) == "string") + + if not results[name] then results[name] = {} end + if not results[name][version] then results[name][version] = {} end + table.insert(results[name][version], { + arch = arch, + repo = repo + }) +end + +--- Test the name field of a query. +-- If query has a boolean field exact_name set to false, +-- then substring match is performed; otherwise, exact string +-- comparison is done. +-- @param query table: A query in dependency table format. +-- @param name string: A package name. +-- @return boolean: True if names match, false otherwise. +local function match_name(query, name) + assert(type(query) == "table") + assert(type(name) == "string") + if query.exact_name == false then + return name:find(query.name, 0, true) and true or false + else + return name == query.name + end +end + +--- Store a match in a results table if version matches query. +-- Name, version, arch and repository path are stored in a given +-- table, optionally checking if version and arch (if given) match +-- a query. +-- @param results table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- @param repo string: URL or pathname of the repository. +-- @param name string: The name of the package being tested. +-- @param version string: The version of the package being tested. +-- @param arch string: The arch of the package being tested. +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- If the arch field is omitted, the local architecture (cfg.arch) +-- is used. The special value "any" is also recognized, returning all +-- matches regardless of architecture. +local function store_if_match(results, repo, name, version, arch, query) + if match_name(query, name) then + if query.arch[arch] or query.arch["any"] then + if deps.match_constraints(deps.parse_version(version), query.constraints) then + store_result(results, name, version, arch, repo) + end + end + end +end + +--- Perform search on a local repository. +-- @param repo string: The pathname of the local repository. +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- If the arch field is omitted, the local architecture (cfg.arch) +-- is used. The special value "any" is also recognized, returning all +-- matches regardless of architecture. +-- @param results table or nil: If given, this table will store the +-- results; if not given, a new table will be created. +-- @return table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- If a table was given in the "results" parameter, that is the result value. +function search.disk_search(repo, query, results) + assert(type(repo) == "string") + assert(type(query) == "table") + assert(type(results) == "table" or not results) + + local fs = require("luarocks.fs") + + if not results then + results = {} + end + query_arch_as_table(query) + + for name in fs.dir(repo) do + local pathname = dir.path(repo, name) + local rname, rversion, rarch = path.parse_name(name) + + if rname and (pathname:match(".rockspec$") or pathname:match(".rock$")) then + store_if_match(results, repo, rname, rversion, rarch, query) + elseif fs.is_dir(pathname) then + for version in fs.dir(pathname) do + if version:match("-%d+$") then + store_if_match(results, repo, name, version, "installed", query) + end + end + end + end + return results +end + +--- Perform search on a rocks server or tree. +-- @param results table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- @param repo string: The URL of a rocks server or +-- the pathname of a rocks tree (as returned by path.rocks_dir()). +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- If the arch field is omitted, the local architecture (cfg.arch) +-- is used. The special value "any" is also recognized, returning all +-- matches regardless of architecture. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @return true or, in case of errors, nil, an error message and an optional error code. +function search.manifest_search(results, repo, query, lua_version) + assert(type(results) == "table") + assert(type(repo) == "string") + assert(type(query) == "table") + + query_arch_as_table(query) + local manifest, err, errcode = manif.load_manifest(repo, lua_version) + if not manifest then + return nil, err, errcode + end + for name, versions in pairs(manifest.repository) do + for version, items in pairs(versions) do + for _, item in ipairs(items) do + store_if_match(results, repo, name, version, item.arch, query) + end + end + end + return true +end + +--- Search on all configured rocks servers. +-- @param query table: A dependency query. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @return table: A table where keys are package names +-- and values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +function search.search_repos(query, lua_version) + assert(type(query) == "table") + + local results = {} + for _, repo in ipairs(cfg.rocks_servers) do + if not cfg.disabled_servers[repo] then + if type(repo) == "string" then + repo = { repo } + end + for _, mirror in ipairs(repo) do + local protocol, pathname = dir.split_url(mirror) + if protocol == "file" then + mirror = pathname + end + local ok, err, errcode = search.manifest_search(results, mirror, query, lua_version) + if errcode == "network" then + cfg.disabled_servers[repo] = true + end + if ok then + break + else + util.warning("Failed searching manifest: "..err) + end + end + end + end + -- search through rocks in cfg.rocks_provided + local provided_repo = "provided by VM or rocks_provided" + for name, versions in pairs(cfg.rocks_provided) do + store_if_match(results, provided_repo, name, versions, "installed", query) + end + return results +end + +--- Prepare a query in dependency table format. +-- @param name string: The query name. +-- @param version string or nil: +-- @return table: A query in table format +function search.make_query(name, version) + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local query = { + name = name, + constraints = {} + } + if version then + table.insert(query.constraints, { op = "==", version = deps.parse_version(version)}) + end + return query +end + +--- Get the URL for the latest in a set of versions. +-- @param name string: The package name to be used in the URL. +-- @param versions table: An array of version informations, as stored +-- in search results tables. +-- @return string or nil: the URL for the latest version if one could +-- be picked, or nil. +local function pick_latest_version(name, versions) + assert(type(name) == "string") + assert(type(versions) == "table") + + local vtables = {} + for v, _ in pairs(versions) do + table.insert(vtables, deps.parse_version(v)) + end + table.sort(vtables) + local version = vtables[#vtables].string + local items = versions[version] + if items then + local pick = 1 + for i, item in ipairs(items) do + if (item.arch == 'src' and items[pick].arch == 'rockspec') + or (item.arch ~= 'src' and item.arch ~= 'rockspec') then + pick = i + end + end + return path.make_url(items[pick].repo, name, version, items[pick].arch) + end + return nil +end + +-- Find out which other Lua versions provide rock versions matching a query, +-- @param query table: A dependency query matching a single rock. +-- @return table: array of Lua versions supported, in "5.x" format. +local function supported_lua_versions(query) + local results = {} + + for lua_version in util.lua_versions() do + if lua_version ~= cfg.lua_version then + if search.search_repos(query, lua_version)[query.name] then + table.insert(results, lua_version) + end + end + end + + return results +end + +--- Attempt to get a single URL for a given search for a rock. +-- @param query table: A dependency query matching a single rock. +-- @return string or (nil, string): URL for latest matching version +-- of the rock if it was found, or nil followed by an error message. +function search.find_suitable_rock(query) + assert(type(query) == "table") + + local results = search.search_repos(query) + local first_rock = next(results) + if not first_rock then + if cfg.rocks_provided[query.name] == nil then + -- Check if constraints are satisfiable with other Lua versions. + local lua_versions = supported_lua_versions(query) + + if #lua_versions ~= 0 then + -- Build a nice message in "only Lua 5.x and 5.y but not 5.z." format + for i, lua_version in ipairs(lua_versions) do + lua_versions[i] = "Lua "..lua_version + end + + local versions_message = "only "..table.concat(lua_versions, " and ").. + " but not Lua "..cfg.lua_version.."." + + if #query.constraints == 0 then + return nil, query.name.." supports "..versions_message + elseif #query.constraints == 1 and query.constraints[1].op == "==" then + return nil, query.name.." "..query.constraints[1].version.string.." supports "..versions_message + else + return nil, "Matching "..query.name.." versions support "..versions_message + end + end + end + + return nil, "No results matching query were found." + elseif next(results, first_rock) then + -- Shouldn't happen as query must match only one package. + return nil, "Several rocks matched query." + elseif cfg.rocks_provided[query.name] ~= nil then + -- Do not install versions listed in cfg.rocks_provided. + return nil, "Rock "..query.name.." "..cfg.rocks_provided[query.name].. + " was found but it is provided by VM or 'rocks_provided' in the config file." + else + return pick_latest_version(query.name, results[first_rock]) + end +end + +--- Print a list of rocks/rockspecs on standard output. +-- @param results table: A table where keys are package names and versions +-- are tables matching version strings to an array of rocks servers. +-- @param porcelain boolean or nil: A flag to force machine-friendly output. +function search.print_results(results, porcelain) + assert(type(results) == "table") + assert(type(porcelain) == "boolean" or not porcelain) + + for package, versions in util.sortedpairs(results) do + if not porcelain then + util.printout(package) + end + for version, repos in util.sortedpairs(versions, deps.compare_versions) do + for _, repo in ipairs(repos) do + repo.repo = dir.normalize(repo.repo) + if porcelain then + util.printout(package, version, repo.arch, repo.repo) + else + util.printout(" "..version.." ("..repo.arch..") - "..repo.repo) + end + end + end + if not porcelain then + util.printout() + end + end +end + +--- Splits a list of search results into two lists, one for "source" results +-- to be used with the "build" command, and one for "binary" results to be +-- used with the "install" command. +-- @param results table: A search results table. +-- @return (table, table): Two tables, one for source and one for binary +-- results. +local function split_source_and_binary_results(results) + local sources, binaries = {}, {} + for name, versions in pairs(results) do + for version, repositories in pairs(versions) do + for _, repo in ipairs(repositories) do + local where = sources + if repo.arch == "all" or repo.arch == cfg.arch then + where = binaries + end + store_result(where, name, version, repo.arch, repo.repo) + end + end + end + return sources, binaries +end + +--- Given a name and optionally a version, try to find in the rocks +-- servers a single .src.rock or .rockspec file that satisfies +-- the request, and run the given function on it; or display to the +-- user possibilities if it couldn't narrow down a single match. +-- @param action function: A function that takes a .src.rock or +-- .rockspec URL as a parameter. +-- @param name string: A rock name +-- @param version string or nil: A version number may also be given. +-- @return The result of the action function, or nil and an error message. +function search.act_on_src_or_rockspec(action, name, version, ...) + assert(type(action) == "function") + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local query = search.make_query(name, version) + query.arch = "src|rockspec" + local url, err = search.find_suitable_rock(query) + if not url then + return nil, "Could not find a result named "..name..(version and " "..version or "")..": "..err + end + return action(url, ...) +end + +function search.pick_installed_rock(name, version, given_tree) + local results = {} + local query = search.make_query(name, version) + query.exact_name = true + local tree_map = {} + local trees = cfg.rocks_trees + if given_tree then + trees = { given_tree } + end + for _, tree in ipairs(trees) do + local rocks_dir = path.rocks_dir(tree) + tree_map[rocks_dir] = tree + search.manifest_search(results, rocks_dir, query) + end + + if not next(results) then -- + return nil,"cannot find package "..name.." "..(version or "").."\nUse 'list' to find installed rocks." + end + + version = nil + local repo_url + local package, versions = util.sortedpairs(results)() + --question: what do we do about multiple versions? This should + --give us the latest version on the last repo (which is usually the global one) + for vs, repositories in util.sortedpairs(versions, deps.compare_versions) do + if not version then version = vs end + for _, rp in ipairs(repositories) do repo_url = rp.repo end + end + + local repo = tree_map[repo_url] + return name, version, repo, repo_url +end + +--- Driver function for "search" command. +-- @param name string: A substring of a rock name to search. +-- @param version string or nil: a version may also be passed. +-- @return boolean or (nil, string): True if build was successful; nil and an +-- error message otherwise. +function search.command(flags, name, version) + if flags["all"] then + name, version = "", nil + end + + if type(name) ~= "string" and not flags["all"] then + return nil, "Enter name and version or use --all. "..util.see_help("search") + end + + local query = search.make_query(name:lower(), version) + query.exact_name = false + local results, err = search.search_repos(query) + local porcelain = flags["porcelain"] + util.title("Search results:", porcelain, "=") + local sources, binaries = split_source_and_binary_results(results) + if next(sources) and not flags["binary"] then + util.title("Rockspecs and source rocks:", porcelain) + search.print_results(sources, porcelain) + end + if next(binaries) and not flags["source"] then + util.title("Binary and pure-Lua rocks:", porcelain) + search.print_results(binaries, porcelain) + end + return true +end + +return search -- cgit v1.2.3-55-g6feb From 5bd20308b881c63dd886b71395dbc9baaab25dfd Mon Sep 17 00:00:00 2001 From: Hisham Date: Fri, 28 Oct 2016 20:48:00 -0200 Subject: Keep only functions shared among commands --- src/luarocks/build.lua | 111 +--------------------------------------------- src/luarocks/download.lua | 45 +------------------ src/luarocks/pack.lua | 43 ++---------------- src/luarocks/remove.lua | 50 --------------------- src/luarocks/search.lua | 72 +----------------------------- 5 files changed, 9 insertions(+), 312 deletions(-) diff --git a/src/luarocks/build.lua b/src/luarocks/build.lua index f3b054d2..ceaa20dd 100644 --- a/src/luarocks/build.lua +++ b/src/luarocks/build.lua @@ -1,43 +1,15 @@ ---- Module implementing the LuaRocks "build" command. --- Builds a rock, compiling its C parts if any. local build = {} -local pack = require("luarocks.pack") local path = require("luarocks.path") local util = require("luarocks.util") -local repos = require("luarocks.repos") local fetch = require("luarocks.fetch") local fs = require("luarocks.fs") local dir = require("luarocks.dir") local deps = require("luarocks.deps") -local writer = require("luarocks.manif.writer") -local remove = require("luarocks.remove") local cfg = require("luarocks.core.cfg") - -build.help_summary = "Build/compile a rock." -build.help_arguments = "[--pack-binary-rock] [--keep] {|| []}" -build.help = [[ -Build and install a rock, compiling its C parts if any. -Argument may be a rockspec file, a source rock file -or the name of a rock to be fetched from a repository. - ---pack-binary-rock Do not install rock. Instead, produce a .rock file - with the contents of compilation in the current - directory. - ---keep Do not remove previously installed versions of the - rock after building a new one. This behavior can - be made permanent by setting keep_other_versions=true - in the configuration file. - ---branch= Override the `source.branch` field in the loaded - rockspec. Allows to specify a different branch to - fetch. Particularly for SCM rocks. - ---only-deps Installs only the dependencies of the rock. - -]]..util.deps_mode_help() +local repos = require("luarocks.repos") +local writer = require("luarocks.manif.writer") --- Install files to a given location. -- Takes a table where the array part is a list of filenames to be copied. @@ -361,83 +333,4 @@ function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_m return name, version end ---- Build and install a rock. --- @param rock_file string: local or remote filename of a rock. --- @param need_to_fetch boolean: true if sources need to be fetched, --- false if the rockspec was obtained from inside a source rock. --- @param deps_mode: string: Which trees to check dependencies for: --- "one" for the current default tree, "all" for all trees, --- "order" for all trees with priority >= the current default, "none" for no trees. --- @param build_only_deps boolean: true to build the listed dependencies only. --- @return boolean or (nil, string, [string]): True if build was successful, --- or false and an error message and an optional error code. -function build.build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps) - assert(type(rock_file) == "string") - assert(type(need_to_fetch) == "boolean") - - local ok, err, errcode - local unpack_dir - unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file) - if not unpack_dir then - return nil, err, errcode - end - local rockspec_file = path.rockspec_name_from_rock(rock_file) - ok, err = fs.change_dir(unpack_dir) - if not ok then return nil, err end - ok, err, errcode = build.build_rockspec(rockspec_file, need_to_fetch, false, deps_mode, build_only_deps) - fs.pop_dir() - return ok, err, errcode -end - -local function do_build(name, version, deps_mode, build_only_deps) - if name:match("%.rockspec$") then - return build.build_rockspec(name, true, false, deps_mode, build_only_deps) - elseif name:match("%.src%.rock$") then - return build.build_rock(name, false, deps_mode, build_only_deps) - elseif name:match("%.all%.rock$") then - local install = require("luarocks.install") - local install_fun = build_only_deps and install.install_binary_rock_deps or install.install_binary_rock - return install_fun(name, deps_mode) - elseif name:match("%.rock$") then - return build.build_rock(name, true, deps_mode, build_only_deps) - elseif not name:match(dir.separator) then - local search = require("luarocks.search") - return search.act_on_src_or_rockspec(do_build, name:lower(), version, nil, deps_mode, build_only_deps) - end - return nil, "Don't know what to do with "..name -end - ---- Driver function for "build" command. --- @param name string: A local or remote rockspec or rock file. --- If a package name is given, forwards the request to "search" and, --- if returned a result, installs the matching rock. --- @param version string: When passing a package name, a version number may --- also be given. --- @return boolean or (nil, string, exitcode): True if build was successful; nil and an --- error message otherwise. exitcode is optionally returned. -function build.command(flags, name, version) - if type(name) ~= "string" then - return nil, "Argument missing. "..util.see_help("build") - end - assert(type(version) == "string" or not version) - - if flags["pack-binary-rock"] then - return pack.pack_binary_rock(name, version, do_build, name, version, deps.get_deps_mode(flags)) - else - local ok, err = fs.check_command_permissions(flags) - if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end - ok, err = do_build(name, version, deps.get_deps_mode(flags), flags["only-deps"]) - if not ok then return nil, err end - name, version = ok, err - if flags["only-deps"] then - return name, version - end - if (not flags["keep"]) and not cfg.keep_other_versions then - local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) - if not ok then util.printerr(err) end - end - return name, version - end -end - return build diff --git a/src/luarocks/download.lua b/src/luarocks/download.lua index 557d1b65..c0f2678f 100644 --- a/src/luarocks/download.lua +++ b/src/luarocks/download.lua @@ -1,9 +1,5 @@ - ---- Module implementing the luarocks "download" command. --- Download a rock from the repository. local download = {} -local util = require("luarocks.util") local path = require("luarocks.path") local fetch = require("luarocks.fetch") local search = require("luarocks.search") @@ -11,16 +7,6 @@ local fs = require("luarocks.fs") local dir = require("luarocks.dir") local cfg = require("luarocks.core.cfg") -download.help_summary = "Download a specific rock file from a rocks server." -download.help_arguments = "[--all] [--arch= | --source | --rockspec] [ []]" - -download.help = [[ ---all Download all files if there are multiple matches. ---source Download .src.rock if available. ---rockspec Download .rockspec if available. ---arch= Download rock for a specific architecture. -]] - local function get_file(filename) local protocol, pathname = dir.split_url(filename) if protocol == "file" then @@ -46,8 +32,8 @@ function download.download(arch, name, version, all) local has_result = false local all_ok = true local any_err = "" - for name, result in pairs(results) do - for version, items in pairs(result) do + for _, result in pairs(results) do + for _, items in pairs(result) do for _, item in ipairs(items) do -- Ignore provided rocks. if item.arch ~= "installed" then @@ -77,31 +63,4 @@ function download.download(arch, name, version, all) (search_err and ": "..search_err or ".") end ---- Driver function for the "download" command. --- @param name string: a rock name. --- @param version string or nil: if the name of a package is given, a --- version may also be passed. --- @return boolean or (nil, string): true if successful or nil followed --- by an error message. -function download.command(flags, name, version) - assert(type(version) == "string" or not version) - if type(name) ~= "string" and not flags["all"] then - return nil, "Argument missing. "..util.see_help("download") - end - if not name then name, version = "", "" end - - local arch - - if flags["source"] then - arch = "src" - elseif flags["rockspec"] then - arch = "rockspec" - elseif flags["arch"] then - arch = flags["arch"] - end - - local dl, err = download.download(arch, name:lower(), version, flags["all"]) - return dl and true, err -end - return download diff --git a/src/luarocks/pack.lua b/src/luarocks/pack.lua index 655cbf37..b721a209 100644 --- a/src/luarocks/pack.lua +++ b/src/luarocks/pack.lua @@ -1,6 +1,5 @@ ---- Module implementing the LuaRocks "pack" command. --- Creates a rock, packing sources or binaries. +-- Create rock files, packing sources or binaries. local pack = {} local unpack = unpack or table.unpack @@ -15,15 +14,6 @@ local dir = require("luarocks.dir") local manif = require("luarocks.manif") local search = require("luarocks.search") -pack.help_summary = "Create a rock, packing sources or binaries." -pack.help_arguments = "{| []}" -pack.help = [[ -Argument may be a rockspec file, for creating a source rock, -or the name of an installed package, for creating a binary rock. -In the latter case, the app version may be given as a second -argument. -]] - --- Create a source rock. -- Packages a rockspec and its required source files in a rock -- file with the .src.rock extension, which can later be built and @@ -86,7 +76,7 @@ end -- @param tree string or nil: An optional tree to pick the package from. -- @return string or (nil, string): The filename of the resulting -- .src.rock file; or nil and an error message. -local function do_pack_binary_rock(name, version, tree) +function pack.pack_installed_rock(name, version, tree) assert(type(name) == "string") assert(type(version) == "string" or not version) @@ -160,34 +150,7 @@ function pack.pack_binary_rock(name, version, cmd, ...) if not rname then rname, rversion = name, version end - return do_pack_binary_rock(rname, rversion, temp_dir) -end - ---- Driver function for the "pack" command. --- @param arg string: may be a rockspec file, for creating a source rock, --- or the name of an installed package, for creating a binary rock. --- @param version string or nil: if the name of a package is given, a --- version may also be passed. --- @return boolean or (nil, string): true if successful or nil followed --- by an error message. -function pack.command(flags, arg, version) - assert(type(version) == "string" or not version) - if type(arg) ~= "string" then - return nil, "Argument missing. "..util.see_help("pack") - end - - local file, err - if arg:match(".*%.rockspec") then - file, err = pack.pack_source_rock(arg) - else - file, err = do_pack_binary_rock(arg:lower(), version, flags["tree"]) - end - if err then - return nil, err - else - util.printout("Packed: "..file) - return true - end + return pack.pack_installed_rock(rname, rversion, temp_dir) end return pack diff --git a/src/luarocks/remove.lua b/src/luarocks/remove.lua index e7f37604..6cc8334f 100644 --- a/src/luarocks/remove.lua +++ b/src/luarocks/remove.lua @@ -1,6 +1,3 @@ - ---- Module implementing the LuaRocks "remove" command. --- Uninstalls rocks. local remove = {} local search = require("luarocks.search") @@ -10,19 +7,6 @@ local repos = require("luarocks.repos") local path = require("luarocks.path") local util = require("luarocks.util") local cfg = require("luarocks.core.cfg") -local fs = require("luarocks.fs") - -remove.help_summary = "Uninstall a rock." -remove.help_arguments = "[--force|--force-fast] []" -remove.help = [[ -Argument is the name of a rock to be uninstalled. -If a version is not given, try to remove all versions at once. -Will only perform the removal if it does not break dependencies. -To override this check and force the removal, use --force. -To perform a forced removal without reporting dependency issues, -use --force-fast. - -]]..util.deps_mode_help() --- Obtain a list of packages that depend on the given set of packages -- (where all packages of the set are versions of one program). @@ -128,38 +112,4 @@ function remove.remove_other_versions(name, version, force, fast) return true end ---- Driver function for the "remove" command. --- @param name string: name of a rock. If a version is given, refer to --- a specific version; otherwise, try to remove all versions. --- @param version string: When passing a package name, a version number --- may also be given. --- @return boolean or (nil, string, exitcode): True if removal was --- successful, nil and an error message otherwise. exitcode is optionally returned. -function remove.command(flags, name, version) - if type(name) ~= "string" then - return nil, "Argument missing. "..util.see_help("remove") - end - - local deps_mode = flags["deps-mode"] or cfg.deps_mode - - local ok, err = fs.check_command_permissions(flags) - if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end - - local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$") - local filename = name - if rock_type then - name, version = path.parse_name(filename) - if not name then return nil, "Invalid "..rock_type.." filename: "..filename end - end - - local results = {} - name = name:lower() - search.manifest_search(results, cfg.rocks_dir, search.make_query(name, version)) - if not results[name] then - return nil, "Could not find rock '"..name..(version and " "..version or "").."' in "..path.rocks_tree_to_string(cfg.root_dir) - end - - return remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"]) -end - return remove diff --git a/src/luarocks/search.lua b/src/luarocks/search.lua index 44eff694..c59f9534 100644 --- a/src/luarocks/search.lua +++ b/src/luarocks/search.lua @@ -1,9 +1,5 @@ - ---- Module implementing the LuaRocks "search" command. --- Queries LuaRocks servers. local search = {} - local dir = require("luarocks.dir") local path = require("luarocks.path") local manif = require("luarocks.manif") @@ -11,17 +7,6 @@ local deps = require("luarocks.deps") local cfg = require("luarocks.core.cfg") local util = require("luarocks.util") -search.help_summary = "Query the LuaRocks servers." -search.help_arguments = "[--source] [--binary] { [] | --all }" -search.help = [[ ---source Return only rockspecs and source rocks, - to be used with the "build" command. ---binary Return only pure Lua and binary rocks (rocks that can be used - with the "install" command without requiring a C toolchain). ---all List all contents of the server that are suitable to - this platform, do not filter by name. -]] - --- Convert the arch field of a query table to table format. -- @param query table: A query table. local function query_arch_as_table(query) @@ -55,7 +40,7 @@ end -- identifier), "rockspec" or "installed" -- @param repo string: Pathname of a local repository of URL of -- rocks server. -local function store_result(results, name, version, arch, repo) +function search.store_result(results, name, version, arch, repo) assert(type(results) == "table") assert(type(name) == "string") assert(type(version) == "string") @@ -108,7 +93,7 @@ local function store_if_match(results, repo, name, version, arch, query) if match_name(query, name) then if query.arch[arch] or query.arch["any"] then if deps.match_constraints(deps.parse_version(version), query.constraints) then - store_result(results, name, version, arch, repo) + search.store_result(results, name, version, arch, repo) end end end @@ -370,28 +355,6 @@ function search.print_results(results, porcelain) end end ---- Splits a list of search results into two lists, one for "source" results --- to be used with the "build" command, and one for "binary" results to be --- used with the "install" command. --- @param results table: A search results table. --- @return (table, table): Two tables, one for source and one for binary --- results. -local function split_source_and_binary_results(results) - local sources, binaries = {}, {} - for name, versions in pairs(results) do - for version, repositories in pairs(versions) do - for _, repo in ipairs(repositories) do - local where = sources - if repo.arch == "all" or repo.arch == cfg.arch then - where = binaries - end - store_result(where, name, version, repo.arch, repo.repo) - end - end - end - return sources, binaries -end - --- Given a name and optionally a version, try to find in the rocks -- servers a single .src.rock or .rockspec file that satisfies -- the request, and run the given function on it; or display to the @@ -448,35 +411,4 @@ function search.pick_installed_rock(name, version, given_tree) return name, version, repo, repo_url end ---- Driver function for "search" command. --- @param name string: A substring of a rock name to search. --- @param version string or nil: a version may also be passed. --- @return boolean or (nil, string): True if build was successful; nil and an --- error message otherwise. -function search.command(flags, name, version) - if flags["all"] then - name, version = "", nil - end - - if type(name) ~= "string" and not flags["all"] then - return nil, "Enter name and version or use --all. "..util.see_help("search") - end - - local query = search.make_query(name:lower(), version) - query.exact_name = false - local results, err = search.search_repos(query) - local porcelain = flags["porcelain"] - util.title("Search results:", porcelain, "=") - local sources, binaries = split_source_and_binary_results(results) - if next(sources) and not flags["binary"] then - util.title("Rockspecs and source rocks:", porcelain) - search.print_results(sources, porcelain) - end - if next(binaries) and not flags["source"] then - util.title("Binary and pure-Lua rocks:", porcelain) - search.print_results(binaries, porcelain) - end - return true -end - return search -- cgit v1.2.3-55-g6feb From 5c140a5da73d304653621a0ac40f363d234928ed Mon Sep 17 00:00:00 2001 From: Hisham Date: Fri, 28 Oct 2016 20:59:42 -0200 Subject: Keep only command driver functions in luarocks.cmd --- src/luarocks/cmd/build.lua | 347 ++--------------------------------- src/luarocks/cmd/download.lua | 76 +------- src/luarocks/cmd/pack.lua | 164 +---------------- src/luarocks/cmd/remove.lua | 124 +------------ src/luarocks/cmd/search.lua | 411 +----------------------------------------- 5 files changed, 36 insertions(+), 1086 deletions(-) diff --git a/src/luarocks/cmd/build.lua b/src/luarocks/cmd/build.lua index f3b054d2..d050e6a3 100644 --- a/src/luarocks/cmd/build.lua +++ b/src/luarocks/cmd/build.lua @@ -1,23 +1,22 @@ --- Module implementing the LuaRocks "build" command. -- Builds a rock, compiling its C parts if any. -local build = {} +local cmd_build = {} local pack = require("luarocks.pack") local path = require("luarocks.path") local util = require("luarocks.util") -local repos = require("luarocks.repos") local fetch = require("luarocks.fetch") local fs = require("luarocks.fs") local dir = require("luarocks.dir") local deps = require("luarocks.deps") -local writer = require("luarocks.manif.writer") local remove = require("luarocks.remove") local cfg = require("luarocks.core.cfg") +local build = require("luarocks.build") -build.help_summary = "Build/compile a rock." -build.help_arguments = "[--pack-binary-rock] [--keep] {|| []}" -build.help = [[ +cmd_build.help_summary = "build/compile a rock." +cmd_build.help_arguments = "[--pack-binary-rock] [--keep] {|| []}" +cmd_build.help = [[ Build and install a rock, compiling its C parts if any. Argument may be a rockspec file, a source rock file or the name of a rock to be fetched from a repository. @@ -39,328 +38,6 @@ or the name of a rock to be fetched from a repository. ]]..util.deps_mode_help() ---- Install files to a given location. --- Takes a table where the array part is a list of filenames to be copied. --- In the hash part, other keys, if is_module_path is set, are identifiers --- in Lua module format, to indicate which subdirectory the file should be --- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo") --- will copy src/bar.lua to boo/foo. --- @param files table or nil: A table containing a list of files to copy in --- the format described above. If nil is passed, this function is a no-op. --- Directories should be delimited by forward slashes as in internet URLs. --- @param location string: The base directory files should be copied to. --- @param is_module_path boolean: True if string keys in files should be --- interpreted as dotted module paths. --- @param perms string: Permissions of the newly created files installed. --- Directories are always created with the default permissions. --- @return boolean or (nil, string): True if succeeded or --- nil and an error message. -local function install_files(files, location, is_module_path, perms) - assert(type(files) == "table" or not files) - assert(type(location) == "string") - if files then - for k, file in pairs(files) do - local dest = location - local filename = dir.base_name(file) - if type(k) == "string" then - local modname = k - if is_module_path then - dest = dir.path(location, path.module_to_path(modname)) - local ok, err = fs.make_dir(dest) - if not ok then return nil, err end - if filename:match("%.lua$") then - local basename = modname:match("([^.]+)$") - filename = basename..".lua" - end - else - dest = dir.path(location, dir.dir_name(modname)) - local ok, err = fs.make_dir(dest) - if not ok then return nil, err end - filename = dir.base_name(modname) - end - else - local ok, err = fs.make_dir(dest) - if not ok then return nil, err end - end - local ok = fs.copy(dir.path(file), dir.path(dest, filename), perms) - if not ok then - return nil, "Failed copying "..file - end - end - end - return true -end - ---- Write to the current directory the contents of a table, --- where each key is a file name and its value is the file content. --- @param files table: The table of files to be written. -local function extract_from_rockspec(files) - for name, content in pairs(files) do - local fd = io.open(dir.path(fs.current_dir(), name), "w+") - fd:write(content) - fd:close() - end -end - ---- Applies patches inlined in the build.patches section --- and extracts files inlined in the build.extra_files section --- of a rockspec. --- @param rockspec table: A rockspec table. --- @return boolean or (nil, string): True if succeeded or --- nil and an error message. -function build.apply_patches(rockspec) - assert(type(rockspec) == "table") - - local build_spec = rockspec.build - if build_spec.extra_files then - extract_from_rockspec(build_spec.extra_files) - end - if build_spec.patches then - extract_from_rockspec(build_spec.patches) - for patch, patchdata in util.sortedpairs(build_spec.patches) do - util.printout("Applying patch "..patch.."...") - local ok, err = fs.apply_patch(tostring(patch), patchdata) - if not ok then - return nil, "Failed applying patch "..patch - end - end - end - return true -end - -local function install_default_docs(name, version) - local patterns = { "readme", "license", "copying", ".*%.md" } - local dest = dir.path(path.install_dir(name, version), "doc") - local has_dir = false - for file in fs.dir() do - for _, pattern in ipairs(patterns) do - if file:lower():match("^"..pattern) then - if not has_dir then - fs.make_dir(dest) - has_dir = true - end - fs.copy(file, dest, cfg.perm_read) - break - end - end - end -end - -local function check_macosx_deployment_target(rockspec) - local target = rockspec.build.macosx_deployment_target - local function minor(version) - return tonumber(version and version:match("^[^.]+%.([^.]+)")) - end - local function patch_variable(var, target) - if rockspec.variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then - rockspec.variables[var] = (rockspec.variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET="..target) - else - rockspec.variables[var] = "env MACOSX_DEPLOYMENT_TARGET="..target.." "..rockspec.variables[var] - end - end - if cfg.platforms.macosx and rockspec:format_is_at_least("3.0") and target then - local version = util.popen_read("sw_vers -productVersion") - local versionminor = minor(version) - local targetminor = minor(target) - if targetminor > versionminor then - return nil, ("This rock requires Mac OSX 10.%d, and you are running 10.%d."):format(targetminor, versionminor) - end - patch_variable("CC", target) - patch_variable("LD", target) - end - return true -end - ---- Build and install a rock given a rockspec. --- @param rockspec_file string: local or remote filename of a rockspec. --- @param need_to_fetch boolean: true if sources need to be fetched, --- false if the rockspec was obtained from inside a source rock. --- @param minimal_mode boolean: true if there's no need to fetch, --- unpack or change dir (this is used by "luarocks make"). Implies --- need_to_fetch = false. --- @param deps_mode string: Dependency mode: "one" for the current default tree, --- "all" for all trees, "order" for all trees with priority >= the current default, --- "none" for no trees. --- @param build_only_deps boolean: true to build the listed dependencies only. --- @return (string, string) or (nil, string, [string]): Name and version of --- installed rock if succeeded or nil and an error message followed by an error code. -function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode, build_only_deps) - assert(type(rockspec_file) == "string") - assert(type(need_to_fetch) == "boolean") - - local rockspec, err, errcode = fetch.load_rockspec(rockspec_file) - if err then - return nil, err, errcode - elseif not rockspec.build then - return nil, "Rockspec error: build table not specified" - elseif not rockspec.build.type then - return nil, "Rockspec error: build type not specified" - end - - local ok - if not build_only_deps then - ok, err, errcode = deps.check_external_deps(rockspec, "build") - if err then - return nil, err, errcode - end - end - - if deps_mode == "none" then - util.printerr("Warning: skipping dependency checks.") - else - local ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) - if err then - return nil, err, errcode - end - end - - local name, version = rockspec.name, rockspec.version - if build_only_deps then - util.printout("Stopping after installing dependencies for " ..name.." "..version) - util.printout() - return name, version - end - - if repos.is_installed(name, version) then - repos.delete_version(name, version, deps_mode) - end - - if not minimal_mode then - local source_dir - if need_to_fetch then - ok, source_dir, errcode = fetch.fetch_sources(rockspec, true) - if not ok then - return nil, source_dir, errcode - end - local ok, err = fs.change_dir(source_dir) - if not ok then return nil, err end - elseif rockspec.source.file then - local ok, err = fs.unpack_archive(rockspec.source.file) - if not ok then - return nil, err - end - end - fs.change_dir(rockspec.source.dir) - end - - local dirs = { - lua = { name = path.lua_dir(name, version), is_module_path = true, perms = cfg.perm_read }, - lib = { name = path.lib_dir(name, version), is_module_path = true, perms = cfg.perm_exec }, - conf = { name = path.conf_dir(name, version), is_module_path = false, perms = cfg.perm_read }, - bin = { name = path.bin_dir(name, version), is_module_path = false, perms = cfg.perm_exec }, - } - - for _, d in pairs(dirs) do - local ok, err = fs.make_dir(d.name) - if not ok then return nil, err end - end - local rollback = util.schedule_function(function() - fs.delete(path.install_dir(name, version)) - fs.remove_dir_if_empty(path.versions_dir(name)) - end) - - local build_spec = rockspec.build - - if not minimal_mode then - ok, err = build.apply_patches(rockspec) - if err then - return nil, err - end - end - - ok, err = check_macosx_deployment_target(rockspec) - if not ok then - return nil, err - end - - if build_spec.type ~= "none" then - - -- Temporary compatibility - if build_spec.type == "module" then - util.printout("Do not use 'module' as a build type. Use 'builtin' instead.") - build_spec.type = "builtin" - end - - if cfg.accepted_build_types and util.array_contains(cfg.accepted_build_types, build_spec.type) then - return nil, "This rockspec uses the '"..build_spec.type.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration." - end - - local build_type - ok, build_type = pcall(require, "luarocks.build." .. build_spec.type) - if not ok or not type(build_type) == "table" then - return nil, "Failed initializing build back-end for build type '"..build_spec.type.."': "..build_type - end - - ok, err = build_type.run(rockspec) - if not ok then - return nil, "Build error: " .. err - end - end - - if build_spec.install then - for id, install_dir in pairs(dirs) do - ok, err = install_files(build_spec.install[id], install_dir.name, install_dir.is_module_path, install_dir.perms) - if not ok then - return nil, err - end - end - end - - local copy_directories = build_spec.copy_directories - local copying_default = false - if not copy_directories then - copy_directories = {"doc"} - copying_default = true - end - - local any_docs = false - for _, copy_dir in pairs(copy_directories) do - if fs.is_dir(copy_dir) then - local dest = dir.path(path.install_dir(name, version), copy_dir) - fs.make_dir(dest) - fs.copy_contents(copy_dir, dest) - any_docs = true - else - if not copying_default then - return nil, "Directory '"..copy_dir.."' not found" - end - end - end - - if not any_docs then - install_default_docs(name, version) - end - - for _, d in pairs(dirs) do - fs.remove_dir_if_empty(d.name) - end - - fs.pop_dir() - - fs.copy(rockspec.local_filename, path.rockspec_file(name, version), cfg.perm_read) - if need_to_fetch then - fs.pop_dir() - end - - ok, err = writer.make_rock_manifest(name, version) - if err then return nil, err end - - ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) - if err then return nil, err end - - util.remove_scheduled_function(rollback) - rollback = util.schedule_function(function() - repos.delete_version(name, version, deps_mode) - end) - - ok, err = repos.run_hook(rockspec, "post_install") - if err then return nil, err end - - util.announce_install(rockspec) - util.remove_scheduled_function(rollback) - return name, version -end - --- Build and install a rock. -- @param rock_file string: local or remote filename of a rock. -- @param need_to_fetch boolean: true if sources need to be fetched, @@ -371,7 +48,7 @@ end -- @param build_only_deps boolean: true to build the listed dependencies only. -- @return boolean or (nil, string, [string]): True if build was successful, -- or false and an error message and an optional error code. -function build.build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps) +local function build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps) assert(type(rock_file) == "string") assert(type(need_to_fetch) == "boolean") @@ -393,13 +70,11 @@ local function do_build(name, version, deps_mode, build_only_deps) if name:match("%.rockspec$") then return build.build_rockspec(name, true, false, deps_mode, build_only_deps) elseif name:match("%.src%.rock$") then - return build.build_rock(name, false, deps_mode, build_only_deps) + return build_rock(name, false, deps_mode, build_only_deps) elseif name:match("%.all%.rock$") then - local install = require("luarocks.install") - local install_fun = build_only_deps and install.install_binary_rock_deps or install.install_binary_rock - return install_fun(name, deps_mode) + return build_rock(name, true, deps_mode, build_only_deps) elseif name:match("%.rock$") then - return build.build_rock(name, true, deps_mode, build_only_deps) + return build_rock(name, true, deps_mode, build_only_deps) elseif not name:match(dir.separator) then local search = require("luarocks.search") return search.act_on_src_or_rockspec(do_build, name:lower(), version, nil, deps_mode, build_only_deps) @@ -415,7 +90,7 @@ end -- also be given. -- @return boolean or (nil, string, exitcode): True if build was successful; nil and an -- error message otherwise. exitcode is optionally returned. -function build.command(flags, name, version) +function cmd_build.command(flags, name, version) if type(name) ~= "string" then return nil, "Argument missing. "..util.see_help("build") end @@ -440,4 +115,4 @@ function build.command(flags, name, version) end end -return build +return cmd_build diff --git a/src/luarocks/cmd/download.lua b/src/luarocks/cmd/download.lua index 557d1b65..9c119f6e 100644 --- a/src/luarocks/cmd/download.lua +++ b/src/luarocks/cmd/download.lua @@ -1,89 +1,27 @@ --- Module implementing the luarocks "download" command. -- Download a rock from the repository. -local download = {} +local cmd_download = {} local util = require("luarocks.util") -local path = require("luarocks.path") -local fetch = require("luarocks.fetch") -local search = require("luarocks.search") -local fs = require("luarocks.fs") -local dir = require("luarocks.dir") -local cfg = require("luarocks.core.cfg") +local download = require("luarocks.download") -download.help_summary = "Download a specific rock file from a rocks server." -download.help_arguments = "[--all] [--arch= | --source | --rockspec] [ []]" - -download.help = [[ +cmd_download.help_summary = "Download a specific rock file from a rocks server." +cmd_download.help_arguments = "[--all] [--arch= | --source | --rockspec] [ []]" +cmd_download.help = [[ --all Download all files if there are multiple matches. --source Download .src.rock if available. --rockspec Download .rockspec if available. --arch= Download rock for a specific architecture. ]] -local function get_file(filename) - local protocol, pathname = dir.split_url(filename) - if protocol == "file" then - local ok, err = fs.copy(pathname, fs.current_dir(), cfg.perm_read) - if ok then - return pathname - else - return nil, err - end - else - return fetch.fetch_url(filename) - end -end - -function download.download(arch, name, version, all) - local query = search.make_query(name, version) - if arch then query.arch = arch end - local search_err - - if all then - if name == "" then query.exact_name = false end - local results = search.search_repos(query) - local has_result = false - local all_ok = true - local any_err = "" - for name, result in pairs(results) do - for version, items in pairs(result) do - for _, item in ipairs(items) do - -- Ignore provided rocks. - if item.arch ~= "installed" then - has_result = true - local filename = path.make_url(item.repo, name, version, item.arch) - local ok, err = get_file(filename) - if not ok then - all_ok = false - any_err = any_err .. "\n" .. err - end - end - end - end - end - - if has_result then - return all_ok, any_err - end - else - local url - url, search_err = search.find_suitable_rock(query) - if url then - return get_file(url) - end - end - return nil, "Could not find a result named "..name..(version and " "..version or "").. - (search_err and ": "..search_err or ".") -end - --- Driver function for the "download" command. -- @param name string: a rock name. -- @param version string or nil: if the name of a package is given, a -- version may also be passed. -- @return boolean or (nil, string): true if successful or nil followed -- by an error message. -function download.command(flags, name, version) +function cmd_download.command(flags, name, version) assert(type(version) == "string" or not version) if type(name) ~= "string" and not flags["all"] then return nil, "Argument missing. "..util.see_help("download") @@ -104,4 +42,4 @@ function download.command(flags, name, version) return dl and true, err end -return download +return cmd_download diff --git a/src/luarocks/cmd/pack.lua b/src/luarocks/cmd/pack.lua index 655cbf37..e43e5b3f 100644 --- a/src/luarocks/cmd/pack.lua +++ b/src/luarocks/cmd/pack.lua @@ -1,168 +1,20 @@ --- Module implementing the LuaRocks "pack" command. -- Creates a rock, packing sources or binaries. -local pack = {} +local cmd_pack = {} -local unpack = unpack or table.unpack - -local path = require("luarocks.path") -local repos = require("luarocks.repos") -local fetch = require("luarocks.fetch") -local fs = require("luarocks.fs") -local cfg = require("luarocks.core.cfg") local util = require("luarocks.util") -local dir = require("luarocks.dir") -local manif = require("luarocks.manif") -local search = require("luarocks.search") +local pack = require("luarocks.pack") -pack.help_summary = "Create a rock, packing sources or binaries." -pack.help_arguments = "{| []}" -pack.help = [[ +cmd_pack.help_summary = "Create a rock, packing sources or binaries." +cmd_pack.help_arguments = "{| []}" +cmd_pack.help = [[ Argument may be a rockspec file, for creating a source rock, or the name of an installed package, for creating a binary rock. In the latter case, the app version may be given as a second argument. ]] ---- Create a source rock. --- Packages a rockspec and its required source files in a rock --- file with the .src.rock extension, which can later be built and --- installed with the "build" command. --- @param rockspec_file string: An URL or pathname for a rockspec file. --- @return string or (nil, string): The filename of the resulting --- .src.rock file; or nil and an error message. -function pack.pack_source_rock(rockspec_file) - assert(type(rockspec_file) == "string") - - local rockspec, err = fetch.load_rockspec(rockspec_file) - if err then - return nil, "Error loading rockspec: "..err - end - rockspec_file = rockspec.local_filename - - local name_version = rockspec.name .. "-" .. rockspec.version - local rock_file = fs.absolute_name(name_version .. ".src.rock") - - local source_file, source_dir = fetch.fetch_sources(rockspec, false) - if not source_file then - return nil, source_dir - end - local ok, err = fs.change_dir(source_dir) - if not ok then return nil, err end - - fs.delete(rock_file) - fs.copy(rockspec_file, source_dir, cfg.perm_read) - if not fs.zip(rock_file, dir.base_name(rockspec_file), dir.base_name(source_file)) then - return nil, "Failed packing "..rock_file - end - fs.pop_dir() - - return rock_file -end - -local function copy_back_files(name, version, file_tree, deploy_dir, pack_dir, perms) - local ok, err = fs.make_dir(pack_dir) - if not ok then return nil, err end - for file, sub in pairs(file_tree) do - local source = dir.path(deploy_dir, file) - local target = dir.path(pack_dir, file) - if type(sub) == "table" then - local ok, err = copy_back_files(name, version, sub, source, target) - if not ok then return nil, err end - else - local versioned = path.versioned_name(source, deploy_dir, name, version) - if fs.exists(versioned) then - fs.copy(versioned, target, perms) - else - fs.copy(source, target, perms) - end - end - end - return true -end - --- @param name string: Name of package to pack. --- @param version string or nil: A version number may also be passed. --- @param tree string or nil: An optional tree to pick the package from. --- @return string or (nil, string): The filename of the resulting --- .src.rock file; or nil and an error message. -local function do_pack_binary_rock(name, version, tree) - assert(type(name) == "string") - assert(type(version) == "string" or not version) - - local repo, repo_url - name, version, repo, repo_url = search.pick_installed_rock(name, version, tree) - if not name then - return nil, version - end - - local root = path.root_dir(repo_url) - local prefix = path.install_dir(name, version, root) - if not fs.exists(prefix) then - return nil, "'"..name.." "..version.."' does not seem to be an installed rock." - end - - local rock_manifest, err = manif.load_rock_manifest(name, version, root) - if not rock_manifest then return nil, err end - - local name_version = name .. "-" .. version - local rock_file = fs.absolute_name(name_version .. "."..cfg.arch..".rock") - - local temp_dir = fs.make_temp_dir("pack") - fs.copy_contents(prefix, temp_dir) - - local is_binary = false - if rock_manifest.lib then - local ok, err = copy_back_files(name, version, rock_manifest.lib, path.deploy_lib_dir(root), dir.path(temp_dir, "lib"), cfg.perm_exec) - if not ok then return nil, "Failed copying back files: " .. err end - is_binary = true - end - if rock_manifest.lua then - local ok, err = copy_back_files(name, version, rock_manifest.lua, path.deploy_lua_dir(root), dir.path(temp_dir, "lua"), cfg.perm_read) - if not ok then return nil, "Failed copying back files: " .. err end - end - - local ok, err = fs.change_dir(temp_dir) - if not ok then return nil, err end - if not is_binary and not repos.has_binaries(name, version) then - rock_file = rock_file:gsub("%."..cfg.arch:gsub("%-","%%-").."%.", ".all.") - end - fs.delete(rock_file) - if not fs.zip(rock_file, unpack(fs.list_dir())) then - return nil, "Failed packing "..rock_file - end - fs.pop_dir() - fs.delete(temp_dir) - return rock_file -end - -function pack.pack_binary_rock(name, version, cmd, ...) - - -- The --pack-binary-rock option for "luarocks build" basically performs - -- "luarocks build" on a temporary tree and then "luarocks pack". The - -- alternative would require refactoring parts of luarocks.build and - -- luarocks.pack, which would save a few file operations: the idea would be - -- to shave off the final deploy steps from the build phase and the initial - -- collect steps from the pack phase. - - local temp_dir, err = fs.make_temp_dir("luarocks-build-pack-"..dir.base_name(name)) - if not temp_dir then - return nil, "Failed creating temporary directory: "..err - end - util.schedule_function(fs.delete, temp_dir) - - path.use_tree(temp_dir) - local ok, err = cmd(...) - if not ok then - return nil, err - end - local rname, rversion = path.parse_name(name) - if not rname then - rname, rversion = name, version - end - return do_pack_binary_rock(rname, rversion, temp_dir) -end - --- Driver function for the "pack" command. -- @param arg string: may be a rockspec file, for creating a source rock, -- or the name of an installed package, for creating a binary rock. @@ -170,7 +22,7 @@ end -- version may also be passed. -- @return boolean or (nil, string): true if successful or nil followed -- by an error message. -function pack.command(flags, arg, version) +function cmd_pack.command(flags, arg, version) assert(type(version) == "string" or not version) if type(arg) ~= "string" then return nil, "Argument missing. "..util.see_help("pack") @@ -180,7 +32,7 @@ function pack.command(flags, arg, version) if arg:match(".*%.rockspec") then file, err = pack.pack_source_rock(arg) else - file, err = do_pack_binary_rock(arg:lower(), version, flags["tree"]) + file, err = pack.pack_installed_rock(arg:lower(), version, flags["tree"]) end if err then return nil, err @@ -190,4 +42,4 @@ function pack.command(flags, arg, version) end end -return pack +return cmd_pack diff --git a/src/luarocks/cmd/remove.lua b/src/luarocks/cmd/remove.lua index e7f37604..08d49058 100644 --- a/src/luarocks/cmd/remove.lua +++ b/src/luarocks/cmd/remove.lua @@ -1,20 +1,18 @@ --- Module implementing the LuaRocks "remove" command. -- Uninstalls rocks. -local remove = {} +local cmd_remove = {} -local search = require("luarocks.search") -local deps = require("luarocks.deps") -local fetch = require("luarocks.fetch") -local repos = require("luarocks.repos") -local path = require("luarocks.path") +local remove = require("luarocks.remove") local util = require("luarocks.util") local cfg = require("luarocks.core.cfg") local fs = require("luarocks.fs") +local search = require("luarocks.search") +local path = require("luarocks.path") -remove.help_summary = "Uninstall a rock." -remove.help_arguments = "[--force|--force-fast] []" -remove.help = [[ +cmd_remove.help_summary = "Uninstall a rock." +cmd_remove.help_arguments = "[--force|--force-fast] []" +cmd_remove.help = [[ Argument is the name of a rock to be uninstalled. If a version is not given, try to remove all versions at once. Will only perform the removal if it does not break dependencies. @@ -24,110 +22,6 @@ use --force-fast. ]]..util.deps_mode_help() ---- Obtain a list of packages that depend on the given set of packages --- (where all packages of the set are versions of one program). --- @param name string: the name of a program --- @param versions array of string: the versions to be deleted. --- @return array of string: an empty table if no packages depend on any --- of the given list, or an array of strings in "name/version" format. -local function check_dependents(name, versions, deps_mode) - local dependents = {} - local blacklist = {} - blacklist[name] = {} - for version, _ in pairs(versions) do - blacklist[name][version] = true - end - local local_rocks = {} - local query_all = search.make_query("") - query_all.exact_name = false - search.manifest_search(local_rocks, cfg.rocks_dir, query_all) - local_rocks[name] = nil - for rock_name, rock_versions in pairs(local_rocks) do - for rock_version, _ in pairs(rock_versions) do - local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version)) - if rockspec then - local _, missing = deps.match_deps(rockspec, blacklist, deps_mode) - if missing[name] then - table.insert(dependents, { name = rock_name, version = rock_version }) - end - end - end - end - return dependents -end - ---- Delete given versions of a program. --- @param name string: the name of a program --- @param versions array of string: the versions to be deleted. --- @param deps_mode: string: Which trees to check dependencies for: --- "one" for the current default tree, "all" for all trees, --- "order" for all trees with priority >= the current default, "none" for no trees. --- @return boolean or (nil, string): true on success or nil and an error message. -local function delete_versions(name, versions, deps_mode) - - for version, _ in pairs(versions) do - util.printout("Removing "..name.." "..version.."...") - local ok, err = repos.delete_version(name, version, deps_mode) - if not ok then return nil, err end - end - - return true -end - -function remove.remove_search_results(results, name, deps_mode, force, fast) - local versions = results[name] - - local version = next(versions) - local second = next(versions, version) - - local dependents = {} - if not fast then - util.printout("Checking stability of dependencies in the absence of") - util.printout(name.." "..table.concat(util.keys(versions), ", ").."...") - util.printout() - dependents = check_dependents(name, versions, deps_mode) - end - - if #dependents > 0 then - if force or fast then - util.printerr("The following packages may be broken by this forced removal:") - for _, dependent in ipairs(dependents) do - util.printerr(dependent.name.." "..dependent.version) - end - util.printerr() - else - if not second then - util.printerr("Will not remove "..name.." "..version..".") - util.printerr("Removing it would break dependencies for: ") - else - util.printerr("Will not remove installed versions of "..name..".") - util.printerr("Removing them would break dependencies for: ") - end - for _, dependent in ipairs(dependents) do - util.printerr(dependent.name.." "..dependent.version) - end - util.printerr() - util.printerr("Use --force to force removal (warning: this may break modules).") - return nil, "Failed removing." - end - end - - local ok, err = delete_versions(name, versions, deps_mode) - if not ok then return nil, err end - - util.printout("Removal successful.") - return true -end - -function remove.remove_other_versions(name, version, force, fast) - local results = {} - search.manifest_search(results, cfg.rocks_dir, { name = name, exact_name = true, constraints = {{ op = "~=", version = version}} }) - if results[name] then - return remove.remove_search_results(results, name, cfg.deps_mode, force, fast) - end - return true -end - --- Driver function for the "remove" command. -- @param name string: name of a rock. If a version is given, refer to -- a specific version; otherwise, try to remove all versions. @@ -135,7 +29,7 @@ end -- may also be given. -- @return boolean or (nil, string, exitcode): True if removal was -- successful, nil and an error message otherwise. exitcode is optionally returned. -function remove.command(flags, name, version) +function cmd_remove.command(flags, name, version) if type(name) ~= "string" then return nil, "Argument missing. "..util.see_help("remove") end @@ -162,4 +56,4 @@ function remove.command(flags, name, version) return remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"]) end -return remove +return cmd_remove diff --git a/src/luarocks/cmd/search.lua b/src/luarocks/cmd/search.lua index 44eff694..109fe805 100644 --- a/src/luarocks/cmd/search.lua +++ b/src/luarocks/cmd/search.lua @@ -3,11 +3,6 @@ -- Queries LuaRocks servers. local search = {} - -local dir = require("luarocks.dir") -local path = require("luarocks.path") -local manif = require("luarocks.manif") -local deps = require("luarocks.deps") local cfg = require("luarocks.core.cfg") local util = require("luarocks.util") @@ -22,354 +17,6 @@ search.help = [[ this platform, do not filter by name. ]] ---- Convert the arch field of a query table to table format. --- @param query table: A query table. -local function query_arch_as_table(query) - local format = type(query.arch) - if format == "table" then - return - elseif format == "nil" then - local accept = {} - accept["src"] = true - accept["all"] = true - accept["rockspec"] = true - accept["installed"] = true - accept[cfg.arch] = true - query.arch = accept - elseif format == "string" then - local accept = {} - for a in query.arch:gmatch("[%w_-]+") do - accept[a] = true - end - query.arch = accept - end -end - ---- Store a search result (a rock or rockspec) in the results table. --- @param results table: The results table, where keys are package names and --- values are tables matching version strings to arrays of --- tables with fields "arch" and "repo". --- @param name string: Package name. --- @param version string: Package version. --- @param arch string: Architecture of rock ("all", "src" or platform --- identifier), "rockspec" or "installed" --- @param repo string: Pathname of a local repository of URL of --- rocks server. -local function store_result(results, name, version, arch, repo) - assert(type(results) == "table") - assert(type(name) == "string") - assert(type(version) == "string") - assert(type(arch) == "string") - assert(type(repo) == "string") - - if not results[name] then results[name] = {} end - if not results[name][version] then results[name][version] = {} end - table.insert(results[name][version], { - arch = arch, - repo = repo - }) -end - ---- Test the name field of a query. --- If query has a boolean field exact_name set to false, --- then substring match is performed; otherwise, exact string --- comparison is done. --- @param query table: A query in dependency table format. --- @param name string: A package name. --- @return boolean: True if names match, false otherwise. -local function match_name(query, name) - assert(type(query) == "table") - assert(type(name) == "string") - if query.exact_name == false then - return name:find(query.name, 0, true) and true or false - else - return name == query.name - end -end - ---- Store a match in a results table if version matches query. --- Name, version, arch and repository path are stored in a given --- table, optionally checking if version and arch (if given) match --- a query. --- @param results table: The results table, where keys are package names and --- values are tables matching version strings to arrays of --- tables with fields "arch" and "repo". --- @param repo string: URL or pathname of the repository. --- @param name string: The name of the package being tested. --- @param version string: The version of the package being tested. --- @param arch string: The arch of the package being tested. --- @param query table: A table describing the query in dependency --- format (for example, {name = "filesystem", exact_name = false, --- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). --- If the arch field is omitted, the local architecture (cfg.arch) --- is used. The special value "any" is also recognized, returning all --- matches regardless of architecture. -local function store_if_match(results, repo, name, version, arch, query) - if match_name(query, name) then - if query.arch[arch] or query.arch["any"] then - if deps.match_constraints(deps.parse_version(version), query.constraints) then - store_result(results, name, version, arch, repo) - end - end - end -end - ---- Perform search on a local repository. --- @param repo string: The pathname of the local repository. --- @param query table: A table describing the query in dependency --- format (for example, {name = "filesystem", exact_name = false, --- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). --- If the arch field is omitted, the local architecture (cfg.arch) --- is used. The special value "any" is also recognized, returning all --- matches regardless of architecture. --- @param results table or nil: If given, this table will store the --- results; if not given, a new table will be created. --- @return table: The results table, where keys are package names and --- values are tables matching version strings to arrays of --- tables with fields "arch" and "repo". --- If a table was given in the "results" parameter, that is the result value. -function search.disk_search(repo, query, results) - assert(type(repo) == "string") - assert(type(query) == "table") - assert(type(results) == "table" or not results) - - local fs = require("luarocks.fs") - - if not results then - results = {} - end - query_arch_as_table(query) - - for name in fs.dir(repo) do - local pathname = dir.path(repo, name) - local rname, rversion, rarch = path.parse_name(name) - - if rname and (pathname:match(".rockspec$") or pathname:match(".rock$")) then - store_if_match(results, repo, rname, rversion, rarch, query) - elseif fs.is_dir(pathname) then - for version in fs.dir(pathname) do - if version:match("-%d+$") then - store_if_match(results, repo, name, version, "installed", query) - end - end - end - end - return results -end - ---- Perform search on a rocks server or tree. --- @param results table: The results table, where keys are package names and --- values are tables matching version strings to arrays of --- tables with fields "arch" and "repo". --- @param repo string: The URL of a rocks server or --- the pathname of a rocks tree (as returned by path.rocks_dir()). --- @param query table: A table describing the query in dependency --- format (for example, {name = "filesystem", exact_name = false, --- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). --- If the arch field is omitted, the local architecture (cfg.arch) --- is used. The special value "any" is also recognized, returning all --- matches regardless of architecture. --- @param lua_version string: Lua version in "5.x" format, defaults to installed version. --- @return true or, in case of errors, nil, an error message and an optional error code. -function search.manifest_search(results, repo, query, lua_version) - assert(type(results) == "table") - assert(type(repo) == "string") - assert(type(query) == "table") - - query_arch_as_table(query) - local manifest, err, errcode = manif.load_manifest(repo, lua_version) - if not manifest then - return nil, err, errcode - end - for name, versions in pairs(manifest.repository) do - for version, items in pairs(versions) do - for _, item in ipairs(items) do - store_if_match(results, repo, name, version, item.arch, query) - end - end - end - return true -end - ---- Search on all configured rocks servers. --- @param query table: A dependency query. --- @param lua_version string: Lua version in "5.x" format, defaults to installed version. --- @return table: A table where keys are package names --- and values are tables matching version strings to arrays of --- tables with fields "arch" and "repo". -function search.search_repos(query, lua_version) - assert(type(query) == "table") - - local results = {} - for _, repo in ipairs(cfg.rocks_servers) do - if not cfg.disabled_servers[repo] then - if type(repo) == "string" then - repo = { repo } - end - for _, mirror in ipairs(repo) do - local protocol, pathname = dir.split_url(mirror) - if protocol == "file" then - mirror = pathname - end - local ok, err, errcode = search.manifest_search(results, mirror, query, lua_version) - if errcode == "network" then - cfg.disabled_servers[repo] = true - end - if ok then - break - else - util.warning("Failed searching manifest: "..err) - end - end - end - end - -- search through rocks in cfg.rocks_provided - local provided_repo = "provided by VM or rocks_provided" - for name, versions in pairs(cfg.rocks_provided) do - store_if_match(results, provided_repo, name, versions, "installed", query) - end - return results -end - ---- Prepare a query in dependency table format. --- @param name string: The query name. --- @param version string or nil: --- @return table: A query in table format -function search.make_query(name, version) - assert(type(name) == "string") - assert(type(version) == "string" or not version) - - local query = { - name = name, - constraints = {} - } - if version then - table.insert(query.constraints, { op = "==", version = deps.parse_version(version)}) - end - return query -end - ---- Get the URL for the latest in a set of versions. --- @param name string: The package name to be used in the URL. --- @param versions table: An array of version informations, as stored --- in search results tables. --- @return string or nil: the URL for the latest version if one could --- be picked, or nil. -local function pick_latest_version(name, versions) - assert(type(name) == "string") - assert(type(versions) == "table") - - local vtables = {} - for v, _ in pairs(versions) do - table.insert(vtables, deps.parse_version(v)) - end - table.sort(vtables) - local version = vtables[#vtables].string - local items = versions[version] - if items then - local pick = 1 - for i, item in ipairs(items) do - if (item.arch == 'src' and items[pick].arch == 'rockspec') - or (item.arch ~= 'src' and item.arch ~= 'rockspec') then - pick = i - end - end - return path.make_url(items[pick].repo, name, version, items[pick].arch) - end - return nil -end - --- Find out which other Lua versions provide rock versions matching a query, --- @param query table: A dependency query matching a single rock. --- @return table: array of Lua versions supported, in "5.x" format. -local function supported_lua_versions(query) - local results = {} - - for lua_version in util.lua_versions() do - if lua_version ~= cfg.lua_version then - if search.search_repos(query, lua_version)[query.name] then - table.insert(results, lua_version) - end - end - end - - return results -end - ---- Attempt to get a single URL for a given search for a rock. --- @param query table: A dependency query matching a single rock. --- @return string or (nil, string): URL for latest matching version --- of the rock if it was found, or nil followed by an error message. -function search.find_suitable_rock(query) - assert(type(query) == "table") - - local results = search.search_repos(query) - local first_rock = next(results) - if not first_rock then - if cfg.rocks_provided[query.name] == nil then - -- Check if constraints are satisfiable with other Lua versions. - local lua_versions = supported_lua_versions(query) - - if #lua_versions ~= 0 then - -- Build a nice message in "only Lua 5.x and 5.y but not 5.z." format - for i, lua_version in ipairs(lua_versions) do - lua_versions[i] = "Lua "..lua_version - end - - local versions_message = "only "..table.concat(lua_versions, " and ").. - " but not Lua "..cfg.lua_version.."." - - if #query.constraints == 0 then - return nil, query.name.." supports "..versions_message - elseif #query.constraints == 1 and query.constraints[1].op == "==" then - return nil, query.name.." "..query.constraints[1].version.string.." supports "..versions_message - else - return nil, "Matching "..query.name.." versions support "..versions_message - end - end - end - - return nil, "No results matching query were found." - elseif next(results, first_rock) then - -- Shouldn't happen as query must match only one package. - return nil, "Several rocks matched query." - elseif cfg.rocks_provided[query.name] ~= nil then - -- Do not install versions listed in cfg.rocks_provided. - return nil, "Rock "..query.name.." "..cfg.rocks_provided[query.name].. - " was found but it is provided by VM or 'rocks_provided' in the config file." - else - return pick_latest_version(query.name, results[first_rock]) - end -end - ---- Print a list of rocks/rockspecs on standard output. --- @param results table: A table where keys are package names and versions --- are tables matching version strings to an array of rocks servers. --- @param porcelain boolean or nil: A flag to force machine-friendly output. -function search.print_results(results, porcelain) - assert(type(results) == "table") - assert(type(porcelain) == "boolean" or not porcelain) - - for package, versions in util.sortedpairs(results) do - if not porcelain then - util.printout(package) - end - for version, repos in util.sortedpairs(versions, deps.compare_versions) do - for _, repo in ipairs(repos) do - repo.repo = dir.normalize(repo.repo) - if porcelain then - util.printout(package, version, repo.arch, repo.repo) - else - util.printout(" "..version.." ("..repo.arch..") - "..repo.repo) - end - end - end - if not porcelain then - util.printout() - end - end -end - --- Splits a list of search results into two lists, one for "source" results -- to be used with the "build" command, and one for "binary" results to be -- used with the "install" command. @@ -385,69 +32,13 @@ local function split_source_and_binary_results(results) if repo.arch == "all" or repo.arch == cfg.arch then where = binaries end - store_result(where, name, version, repo.arch, repo.repo) + search.store_result(where, name, version, repo.arch, repo.repo) end end end return sources, binaries end ---- Given a name and optionally a version, try to find in the rocks --- servers a single .src.rock or .rockspec file that satisfies --- the request, and run the given function on it; or display to the --- user possibilities if it couldn't narrow down a single match. --- @param action function: A function that takes a .src.rock or --- .rockspec URL as a parameter. --- @param name string: A rock name --- @param version string or nil: A version number may also be given. --- @return The result of the action function, or nil and an error message. -function search.act_on_src_or_rockspec(action, name, version, ...) - assert(type(action) == "function") - assert(type(name) == "string") - assert(type(version) == "string" or not version) - - local query = search.make_query(name, version) - query.arch = "src|rockspec" - local url, err = search.find_suitable_rock(query) - if not url then - return nil, "Could not find a result named "..name..(version and " "..version or "")..": "..err - end - return action(url, ...) -end - -function search.pick_installed_rock(name, version, given_tree) - local results = {} - local query = search.make_query(name, version) - query.exact_name = true - local tree_map = {} - local trees = cfg.rocks_trees - if given_tree then - trees = { given_tree } - end - for _, tree in ipairs(trees) do - local rocks_dir = path.rocks_dir(tree) - tree_map[rocks_dir] = tree - search.manifest_search(results, rocks_dir, query) - end - - if not next(results) then -- - return nil,"cannot find package "..name.." "..(version or "").."\nUse 'list' to find installed rocks." - end - - version = nil - local repo_url - local package, versions = util.sortedpairs(results)() - --question: what do we do about multiple versions? This should - --give us the latest version on the last repo (which is usually the global one) - for vs, repositories in util.sortedpairs(versions, deps.compare_versions) do - if not version then version = vs end - for _, rp in ipairs(repositories) do repo_url = rp.repo end - end - - local repo = tree_map[repo_url] - return name, version, repo, repo_url -end - --- Driver function for "search" command. -- @param name string: A substring of a rock name to search. -- @param version string or nil: a version may also be passed. -- cgit v1.2.3-55-g6feb From db7814136e3ce45141d3582738dc36ad3596df2c Mon Sep 17 00:00:00 2001 From: Hisham Date: Sat, 29 Oct 2016 16:13:44 -0200 Subject: This doesn't produce an error, only a warning. --- spec/config_spec.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/config_spec.lua b/spec/config_spec.lua index 4a7f4aea..cdb5ccc4 100644 --- a/spec/config_spec.lua +++ b/spec/config_spec.lua @@ -57,7 +57,8 @@ describe("LuaRocks config tests #blackbox #b_config", function() end) it("LuaRocks config missing user config", function() - assert.is_false(run.luarocks_bool("config --user-config", {LUAROCKS_CONFIG = "missing_file.lua"})) + local output = run.luarocks("config --user-config", {LUAROCKS_CONFIG = "missing_file.lua"}) + assert.truthy(output:match("Warning")) end) end) -- cgit v1.2.3-55-g6feb From 67f73b4a1a18a6ff9535ac4ed5faec0d1eb9671a Mon Sep 17 00:00:00 2001 From: Hisham Date: Sat, 29 Oct 2016 16:14:05 -0200 Subject: Move admin commands to luarocks.admin.cmd.*; fix references to modules. --- src/bin/luarocks | 38 ++++----- src/bin/luarocks-admin | 10 +-- src/luarocks/admin/add.lua | 129 ------------------------------- src/luarocks/admin/cmd/add.lua | 129 +++++++++++++++++++++++++++++++ src/luarocks/admin/cmd/make_manifest.lua | 51 ++++++++++++ src/luarocks/admin/cmd/refresh_cache.lua | 30 +++++++ src/luarocks/admin/cmd/remove.lua | 90 +++++++++++++++++++++ src/luarocks/admin/make_manifest.lua | 51 ------------ src/luarocks/admin/refresh_cache.lua | 30 ------- src/luarocks/admin/remove.lua | 90 --------------------- src/luarocks/cmd/install.lua | 4 +- src/luarocks/cmd/search.lua | 13 ++-- src/luarocks/deps.lua | 2 +- src/luarocks/download.lua | 4 +- 14 files changed, 336 insertions(+), 335 deletions(-) delete mode 100644 src/luarocks/admin/add.lua create mode 100644 src/luarocks/admin/cmd/add.lua create mode 100644 src/luarocks/admin/cmd/make_manifest.lua create mode 100644 src/luarocks/admin/cmd/refresh_cache.lua create mode 100644 src/luarocks/admin/cmd/remove.lua delete mode 100644 src/luarocks/admin/make_manifest.lua delete mode 100644 src/luarocks/admin/refresh_cache.lua delete mode 100644 src/luarocks/admin/remove.lua diff --git a/src/bin/luarocks b/src/bin/luarocks index 21f17da9..88a1d1ca 100755 --- a/src/bin/luarocks +++ b/src/bin/luarocks @@ -9,25 +9,25 @@ local command_line = require("luarocks.command_line") program_description = "LuaRocks main command-line interface" commands = { - help = "luarocks.help", - pack = "luarocks.pack", - unpack = "luarocks.unpack", - build = "luarocks.build", - install = "luarocks.install", - search = "luarocks.search", - list = "luarocks.list", - remove = "luarocks.remove", - make = "luarocks.make", - download = "luarocks.download", - path = "luarocks.path_cmd", - show = "luarocks.show", - new_version = "luarocks.new_version", - lint = "luarocks.lint", - write_rockspec = "luarocks.write_rockspec", - purge = "luarocks.purge", - doc = "luarocks.doc", - upload = "luarocks.upload", - config = "luarocks.config_cmd", + help = "luarocks.cmd.help", + pack = "luarocks.cmd.pack", + unpack = "luarocks.cmd.unpack", + build = "luarocks.cmd.build", + install = "luarocks.cmd.install", + search = "luarocks.cmd.search", + list = "luarocks.cmd.list", + remove = "luarocks.cmd.remove", + make = "luarocks.cmd.make", + download = "luarocks.cmd.download", + path = "luarocks.cmd.path", + show = "luarocks.cmd.show", + new_version = "luarocks.cmd.new_version", + lint = "luarocks.cmd.lint", + write_rockspec = "luarocks.cmd.write_rockspec", + purge = "luarocks.cmd.purge", + doc = "luarocks.cmd.doc", + upload = "luarocks.cmd.upload", + config = "luarocks.cmd.config", } command_line.run_command(...) diff --git a/src/bin/luarocks-admin b/src/bin/luarocks-admin index 660c0a70..5db24640 100755 --- a/src/bin/luarocks-admin +++ b/src/bin/luarocks-admin @@ -9,11 +9,11 @@ local command_line = require("luarocks.command_line") program_description = "LuaRocks repository administration interface" commands = { - help = "luarocks.help", - make_manifest = "luarocks.admin.make_manifest", - add = "luarocks.admin.add", - remove = "luarocks.admin.remove", - refresh_cache = "luarocks.admin.refresh_cache", + help = "luarocks.cmd.help", + make_manifest = "luarocks.admin.cmd.make_manifest", + add = "luarocks.admin.cmd.add", + remove = "luarocks.admin.cmd.remove", + refresh_cache = "luarocks.admin.cmd.refresh_cache", } command_line.run_command(...) diff --git a/src/luarocks/admin/add.lua b/src/luarocks/admin/add.lua deleted file mode 100644 index daf46c1d..00000000 --- a/src/luarocks/admin/add.lua +++ /dev/null @@ -1,129 +0,0 @@ - ---- Module implementing the luarocks-admin "add" command. --- Adds a rock or rockspec to a rocks server. -local add = {} - -local cfg = require("luarocks.core.cfg") -local util = require("luarocks.util") -local dir = require("luarocks.dir") -local writer = require("luarocks.manif.writer") -local index = require("luarocks.index") -local fs = require("luarocks.fs") -local cache = require("luarocks.admin.cache") - -add.help_summary = "Add a rock or rockspec to a rocks server." -add.help_arguments = "[--server=] [--no-refresh] {|...}" -add.help = [[ -Arguments are local files, which may be rockspecs or rocks. -The flag --server indicates which server to use. -If not given, the default server set in the upload_server variable -from the configuration file is used instead. -The flag --no-refresh indicates the local cache should not be refreshed -prior to generation of the updated manifest. -]] - -local function zip_manifests() - for ver in util.lua_versions() do - local file = "manifest-"..ver - local zip = file..".zip" - fs.delete(dir.path(fs.current_dir(), zip)) - fs.zip(zip, file) - end -end - -local function add_files_to_server(refresh, rockfiles, server, upload_server) - assert(type(refresh) == "boolean" or not refresh) - assert(type(rockfiles) == "table") - assert(type(server) == "string") - assert(type(upload_server) == "table" or not upload_server) - - local download_url, login_url = cache.get_server_urls(server, upload_server) - local at = fs.current_dir() - local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url - - local local_cache, protocol, server_path, user, password = refresh_fn(server, download_url, cfg.upload_user, cfg.upload_password) - if not local_cache then - return nil, protocol - end - if protocol == "file" then - return nil, "Server "..server.." is not recognized, check your configuration." - end - - if not login_url then - login_url = protocol.."://"..server_path - end - - local ok, err = fs.change_dir(at) - if not ok then return nil, err end - - local files = {} - for _, rockfile in ipairs(rockfiles) do - if fs.exists(rockfile) then - util.printout("Copying file "..rockfile.." to "..local_cache.."...") - local absolute = fs.absolute_name(rockfile) - fs.copy(absolute, local_cache, cfg.perm_read) - table.insert(files, dir.base_name(absolute)) - else - util.printerr("File "..rockfile.." not found") - end - end - if #files == 0 then - return nil, "No files found" - end - - local ok, err = fs.change_dir(local_cache) - if not ok then return nil, err end - - util.printout("Updating manifest...") - writer.make_manifest(local_cache, "one", true) - - zip_manifests() - - util.printout("Updating index.html...") - index.make_index(local_cache) - - local login_info = "" - if user then login_info = " -u "..user end - if password then login_info = login_info..":"..password end - if not login_url:match("/$") then - login_url = login_url .. "/" - end - - table.insert(files, "index.html") - table.insert(files, "manifest") - for ver in util.lua_versions() do - table.insert(files, "manifest-"..ver) - table.insert(files, "manifest-"..ver..".zip") - end - - -- TODO abstract away explicit 'curl' call - - local cmd - if protocol == "rsync" then - local srv, path = server_path:match("([^/]+)(/.+)") - cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/" - elseif upload_server and upload_server.sftp then - local part1, part2 = upload_server.sftp:match("^([^/]*)/(.*)$") - cmd = cfg.variables.SCP.." "..table.concat(files, " ").." "..user.."@"..part1..":/"..part2 - else - cmd = cfg.variables.CURL.." "..login_info.." -T '{"..table.concat(files, ",").."}' "..login_url - end - - util.printout(cmd) - fs.execute(cmd) - - return true -end - -function add.command(flags, ...) - local files = {...} - if #files < 1 then - return nil, "Argument missing. "..util.see_help("add", "luarocks-admin") - end - local server, server_table = cache.get_upload_server(flags["server"]) - if not server then return nil, server_table end - return add_files_to_server(not flags["no-refresh"], files, server, server_table) -end - - -return add diff --git a/src/luarocks/admin/cmd/add.lua b/src/luarocks/admin/cmd/add.lua new file mode 100644 index 00000000..daf46c1d --- /dev/null +++ b/src/luarocks/admin/cmd/add.lua @@ -0,0 +1,129 @@ + +--- Module implementing the luarocks-admin "add" command. +-- Adds a rock or rockspec to a rocks server. +local add = {} + +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") +local writer = require("luarocks.manif.writer") +local index = require("luarocks.index") +local fs = require("luarocks.fs") +local cache = require("luarocks.admin.cache") + +add.help_summary = "Add a rock or rockspec to a rocks server." +add.help_arguments = "[--server=] [--no-refresh] {|...}" +add.help = [[ +Arguments are local files, which may be rockspecs or rocks. +The flag --server indicates which server to use. +If not given, the default server set in the upload_server variable +from the configuration file is used instead. +The flag --no-refresh indicates the local cache should not be refreshed +prior to generation of the updated manifest. +]] + +local function zip_manifests() + for ver in util.lua_versions() do + local file = "manifest-"..ver + local zip = file..".zip" + fs.delete(dir.path(fs.current_dir(), zip)) + fs.zip(zip, file) + end +end + +local function add_files_to_server(refresh, rockfiles, server, upload_server) + assert(type(refresh) == "boolean" or not refresh) + assert(type(rockfiles) == "table") + assert(type(server) == "string") + assert(type(upload_server) == "table" or not upload_server) + + local download_url, login_url = cache.get_server_urls(server, upload_server) + local at = fs.current_dir() + local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url + + local local_cache, protocol, server_path, user, password = refresh_fn(server, download_url, cfg.upload_user, cfg.upload_password) + if not local_cache then + return nil, protocol + end + if protocol == "file" then + return nil, "Server "..server.." is not recognized, check your configuration." + end + + if not login_url then + login_url = protocol.."://"..server_path + end + + local ok, err = fs.change_dir(at) + if not ok then return nil, err end + + local files = {} + for _, rockfile in ipairs(rockfiles) do + if fs.exists(rockfile) then + util.printout("Copying file "..rockfile.." to "..local_cache.."...") + local absolute = fs.absolute_name(rockfile) + fs.copy(absolute, local_cache, cfg.perm_read) + table.insert(files, dir.base_name(absolute)) + else + util.printerr("File "..rockfile.." not found") + end + end + if #files == 0 then + return nil, "No files found" + end + + local ok, err = fs.change_dir(local_cache) + if not ok then return nil, err end + + util.printout("Updating manifest...") + writer.make_manifest(local_cache, "one", true) + + zip_manifests() + + util.printout("Updating index.html...") + index.make_index(local_cache) + + local login_info = "" + if user then login_info = " -u "..user end + if password then login_info = login_info..":"..password end + if not login_url:match("/$") then + login_url = login_url .. "/" + end + + table.insert(files, "index.html") + table.insert(files, "manifest") + for ver in util.lua_versions() do + table.insert(files, "manifest-"..ver) + table.insert(files, "manifest-"..ver..".zip") + end + + -- TODO abstract away explicit 'curl' call + + local cmd + if protocol == "rsync" then + local srv, path = server_path:match("([^/]+)(/.+)") + cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/" + elseif upload_server and upload_server.sftp then + local part1, part2 = upload_server.sftp:match("^([^/]*)/(.*)$") + cmd = cfg.variables.SCP.." "..table.concat(files, " ").." "..user.."@"..part1..":/"..part2 + else + cmd = cfg.variables.CURL.." "..login_info.." -T '{"..table.concat(files, ",").."}' "..login_url + end + + util.printout(cmd) + fs.execute(cmd) + + return true +end + +function add.command(flags, ...) + local files = {...} + if #files < 1 then + return nil, "Argument missing. "..util.see_help("add", "luarocks-admin") + end + local server, server_table = cache.get_upload_server(flags["server"]) + if not server then return nil, server_table end + return add_files_to_server(not flags["no-refresh"], files, server, server_table) +end + + +return add diff --git a/src/luarocks/admin/cmd/make_manifest.lua b/src/luarocks/admin/cmd/make_manifest.lua new file mode 100644 index 00000000..57851942 --- /dev/null +++ b/src/luarocks/admin/cmd/make_manifest.lua @@ -0,0 +1,51 @@ + +--- Module implementing the luarocks-admin "make_manifest" command. +-- Compile a manifest file for a repository. +local make_manifest = {} + +local writer = require("luarocks.manif.writer") +local index = require("luarocks.index") +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local deps = require("luarocks.deps") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") + +make_manifest.help_summary = "Compile a manifest file for a repository." + +make_manifest.help = [[ +, if given, is a local repository pathname. + +--local-tree If given, do not write versioned versions of the manifest file. + Use this when rebuilding the manifest of a local rocks tree. +]] + +--- Driver function for "make_manifest" command. +-- @param repo string or nil: Pathname of a local repository. If not given, +-- the default local repository configured as cfg.rocks_dir is used. +-- @return boolean or (nil, string): True if manifest was generated, +-- or nil and an error message. +function make_manifest.command(flags, repo) + assert(type(repo) == "string" or not repo) + repo = repo or cfg.rocks_dir + + util.printout("Making manifest for "..repo) + + if repo:match("/lib/luarocks") and not flags["local-tree"] then + util.warning("This looks like a local rocks tree, but you did not pass --local-tree.") + end + + local ok, err = writer.make_manifest(repo, deps.get_deps_mode(flags), not flags["local-tree"]) + if ok and not flags["local-tree"] then + util.printout("Generating index.html for "..repo) + index.make_index(repo) + end + if flags["local-tree"] then + for luaver in util.lua_versions() do + fs.delete(dir.path(repo, "manifest-"..luaver)) + end + end + return ok, err +end + +return make_manifest diff --git a/src/luarocks/admin/cmd/refresh_cache.lua b/src/luarocks/admin/cmd/refresh_cache.lua new file mode 100644 index 00000000..947dbfb0 --- /dev/null +++ b/src/luarocks/admin/cmd/refresh_cache.lua @@ -0,0 +1,30 @@ + +--- Module implementing the luarocks-admin "refresh_cache" command. +local refresh_cache = {} + +local cfg = require("luarocks.core.cfg") +local cache = require("luarocks.admin.cache") + +refresh_cache.help_summary = "Refresh local cache of a remote rocks server." +refresh_cache.help_arguments = "[--from=]" +refresh_cache.help = [[ +The flag --from indicates which server to use. +If not given, the default server set in the upload_server variable +from the configuration file is used instead. +]] + +function refresh_cache.command(flags) + local server, upload_server = cache.get_upload_server(flags["server"]) + if not server then return nil, upload_server end + local download_url = cache.get_server_urls(server, upload_server) + + local ok, err = cache.refresh_local_cache(server, download_url, cfg.upload_user, cfg.upload_password) + if not ok then + return nil, err + else + return true + end +end + + +return refresh_cache diff --git a/src/luarocks/admin/cmd/remove.lua b/src/luarocks/admin/cmd/remove.lua new file mode 100644 index 00000000..763a166f --- /dev/null +++ b/src/luarocks/admin/cmd/remove.lua @@ -0,0 +1,90 @@ + +--- Module implementing the luarocks-admin "remove" command. +-- Removes a rock or rockspec from a rocks server. +local admin_remove = {} + +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") +local writer = require("luarocks.manif.writer") +local index = require("luarocks.index") +local fs = require("luarocks.fs") +local cache = require("luarocks.admin.cache") + +admin_remove.help_summary = "Remove a rock or rockspec from a rocks server." +admin_remove.help_arguments = "[--server=] [--no-refresh] {|...}" +admin_remove.help = [[ +Arguments are local files, which may be rockspecs or rocks. +The flag --server indicates which server to use. +If not given, the default server set in the upload_server variable +from the configuration file is used instead. +The flag --no-refresh indicates the local cache should not be refreshed +prior to generation of the updated manifest. +]] + +local function remove_files_from_server(refresh, rockfiles, server, upload_server) + assert(type(refresh) == "boolean" or not refresh) + assert(type(rockfiles) == "table") + assert(type(server) == "string") + assert(type(upload_server) == "table" or not upload_server) + + local download_url, login_url = cache.get_server_urls(server, upload_server) + local at = fs.current_dir() + local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url + + local local_cache, protocol, server_path, user, password = refresh_fn(server, download_url, cfg.upload_user, cfg.upload_password) + if not local_cache then + return nil, protocol + end + if protocol ~= "rsync" then + return nil, "This command requires 'rsync', check your configuration." + end + + local ok, err = fs.change_dir(at) + if not ok then return nil, err end + + local nr_files = 0 + for _, rockfile in ipairs(rockfiles) do + local basename = dir.base_name(rockfile) + local file = dir.path(local_cache, basename) + util.printout("Removing file "..file.."...") + fs.delete(file) + if not fs.exists(file) then + nr_files = nr_files + 1 + else + util.printerr("Failed removing "..file) + end + end + if nr_files == 0 then + return nil, "No files removed." + end + + local ok, err = fs.change_dir(local_cache) + if not ok then return nil, err end + + util.printout("Updating manifest...") + writer.make_manifest(local_cache, "one", true) + util.printout("Updating index.html...") + index.make_index(local_cache) + + local srv, path = server_path:match("([^/]+)(/.+)") + local cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." --delete -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/" + + util.printout(cmd) + fs.execute(cmd) + + return true +end + +function admin_remove.command(flags, ...) + local files = {...} + if #files < 1 then + return nil, "Argument missing. "..util.see_help("remove", "luarocks-admin") + end + local server, server_table = cache.get_upload_server(flags["server"]) + if not server then return nil, server_table end + return remove_files_from_server(not flags["no-refresh"], files, server, server_table) +end + + +return admin_remove diff --git a/src/luarocks/admin/make_manifest.lua b/src/luarocks/admin/make_manifest.lua deleted file mode 100644 index 57851942..00000000 --- a/src/luarocks/admin/make_manifest.lua +++ /dev/null @@ -1,51 +0,0 @@ - ---- Module implementing the luarocks-admin "make_manifest" command. --- Compile a manifest file for a repository. -local make_manifest = {} - -local writer = require("luarocks.manif.writer") -local index = require("luarocks.index") -local cfg = require("luarocks.core.cfg") -local util = require("luarocks.util") -local deps = require("luarocks.deps") -local fs = require("luarocks.fs") -local dir = require("luarocks.dir") - -make_manifest.help_summary = "Compile a manifest file for a repository." - -make_manifest.help = [[ -, if given, is a local repository pathname. - ---local-tree If given, do not write versioned versions of the manifest file. - Use this when rebuilding the manifest of a local rocks tree. -]] - ---- Driver function for "make_manifest" command. --- @param repo string or nil: Pathname of a local repository. If not given, --- the default local repository configured as cfg.rocks_dir is used. --- @return boolean or (nil, string): True if manifest was generated, --- or nil and an error message. -function make_manifest.command(flags, repo) - assert(type(repo) == "string" or not repo) - repo = repo or cfg.rocks_dir - - util.printout("Making manifest for "..repo) - - if repo:match("/lib/luarocks") and not flags["local-tree"] then - util.warning("This looks like a local rocks tree, but you did not pass --local-tree.") - end - - local ok, err = writer.make_manifest(repo, deps.get_deps_mode(flags), not flags["local-tree"]) - if ok and not flags["local-tree"] then - util.printout("Generating index.html for "..repo) - index.make_index(repo) - end - if flags["local-tree"] then - for luaver in util.lua_versions() do - fs.delete(dir.path(repo, "manifest-"..luaver)) - end - end - return ok, err -end - -return make_manifest diff --git a/src/luarocks/admin/refresh_cache.lua b/src/luarocks/admin/refresh_cache.lua deleted file mode 100644 index 947dbfb0..00000000 --- a/src/luarocks/admin/refresh_cache.lua +++ /dev/null @@ -1,30 +0,0 @@ - ---- Module implementing the luarocks-admin "refresh_cache" command. -local refresh_cache = {} - -local cfg = require("luarocks.core.cfg") -local cache = require("luarocks.admin.cache") - -refresh_cache.help_summary = "Refresh local cache of a remote rocks server." -refresh_cache.help_arguments = "[--from=]" -refresh_cache.help = [[ -The flag --from indicates which server to use. -If not given, the default server set in the upload_server variable -from the configuration file is used instead. -]] - -function refresh_cache.command(flags) - local server, upload_server = cache.get_upload_server(flags["server"]) - if not server then return nil, upload_server end - local download_url = cache.get_server_urls(server, upload_server) - - local ok, err = cache.refresh_local_cache(server, download_url, cfg.upload_user, cfg.upload_password) - if not ok then - return nil, err - else - return true - end -end - - -return refresh_cache diff --git a/src/luarocks/admin/remove.lua b/src/luarocks/admin/remove.lua deleted file mode 100644 index 763a166f..00000000 --- a/src/luarocks/admin/remove.lua +++ /dev/null @@ -1,90 +0,0 @@ - ---- Module implementing the luarocks-admin "remove" command. --- Removes a rock or rockspec from a rocks server. -local admin_remove = {} - -local cfg = require("luarocks.core.cfg") -local util = require("luarocks.util") -local dir = require("luarocks.dir") -local writer = require("luarocks.manif.writer") -local index = require("luarocks.index") -local fs = require("luarocks.fs") -local cache = require("luarocks.admin.cache") - -admin_remove.help_summary = "Remove a rock or rockspec from a rocks server." -admin_remove.help_arguments = "[--server=] [--no-refresh] {|...}" -admin_remove.help = [[ -Arguments are local files, which may be rockspecs or rocks. -The flag --server indicates which server to use. -If not given, the default server set in the upload_server variable -from the configuration file is used instead. -The flag --no-refresh indicates the local cache should not be refreshed -prior to generation of the updated manifest. -]] - -local function remove_files_from_server(refresh, rockfiles, server, upload_server) - assert(type(refresh) == "boolean" or not refresh) - assert(type(rockfiles) == "table") - assert(type(server) == "string") - assert(type(upload_server) == "table" or not upload_server) - - local download_url, login_url = cache.get_server_urls(server, upload_server) - local at = fs.current_dir() - local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url - - local local_cache, protocol, server_path, user, password = refresh_fn(server, download_url, cfg.upload_user, cfg.upload_password) - if not local_cache then - return nil, protocol - end - if protocol ~= "rsync" then - return nil, "This command requires 'rsync', check your configuration." - end - - local ok, err = fs.change_dir(at) - if not ok then return nil, err end - - local nr_files = 0 - for _, rockfile in ipairs(rockfiles) do - local basename = dir.base_name(rockfile) - local file = dir.path(local_cache, basename) - util.printout("Removing file "..file.."...") - fs.delete(file) - if not fs.exists(file) then - nr_files = nr_files + 1 - else - util.printerr("Failed removing "..file) - end - end - if nr_files == 0 then - return nil, "No files removed." - end - - local ok, err = fs.change_dir(local_cache) - if not ok then return nil, err end - - util.printout("Updating manifest...") - writer.make_manifest(local_cache, "one", true) - util.printout("Updating index.html...") - index.make_index(local_cache) - - local srv, path = server_path:match("([^/]+)(/.+)") - local cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." --delete -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/" - - util.printout(cmd) - fs.execute(cmd) - - return true -end - -function admin_remove.command(flags, ...) - local files = {...} - if #files < 1 then - return nil, "Argument missing. "..util.see_help("remove", "luarocks-admin") - end - local server, server_table = cache.get_upload_server(flags["server"]) - if not server then return nil, server_table end - return remove_files_from_server(not flags["no-refresh"], files, server, server_table) -end - - -return admin_remove diff --git a/src/luarocks/cmd/install.lua b/src/luarocks/cmd/install.lua index c9b085f5..33cc8fbf 100644 --- a/src/luarocks/cmd/install.lua +++ b/src/luarocks/cmd/install.lua @@ -10,6 +10,7 @@ local fs = require("luarocks.fs") local deps = require("luarocks.deps") local writer = require("luarocks.manif.writer") local remove = require("luarocks.remove") +local search = require("luarocks.search") local cfg = require("luarocks.core.cfg") install.help_summary = "Install a rock." @@ -154,7 +155,7 @@ function install.command(flags, name, version) if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end if name:match("%.rockspec$") or name:match("%.src%.rock$") then - local build = require("luarocks.build") + local build = require("luarocks.cmd.build") return build.command(flags, name) elseif name:match("%.rock$") then if flags["only-deps"] then @@ -170,7 +171,6 @@ function install.command(flags, name, version) end return name, version else - local search = require("luarocks.search") local url, err = search.find_suitable_rock(search.make_query(name:lower(), version)) if not url then return nil, err diff --git a/src/luarocks/cmd/search.lua b/src/luarocks/cmd/search.lua index 109fe805..c4e4058d 100644 --- a/src/luarocks/cmd/search.lua +++ b/src/luarocks/cmd/search.lua @@ -1,14 +1,15 @@ --- Module implementing the LuaRocks "search" command. -- Queries LuaRocks servers. -local search = {} +local cmd_search = {} local cfg = require("luarocks.core.cfg") local util = require("luarocks.util") +local search = require("luarocks.search") -search.help_summary = "Query the LuaRocks servers." -search.help_arguments = "[--source] [--binary] { [] | --all }" -search.help = [[ +cmd_search.help_summary = "Query the LuaRocks servers." +cmd_search.help_arguments = "[--source] [--binary] { [] | --all }" +cmd_search.help = [[ --source Return only rockspecs and source rocks, to be used with the "build" command. --binary Return only pure Lua and binary rocks (rocks that can be used @@ -44,7 +45,7 @@ end -- @param version string or nil: a version may also be passed. -- @return boolean or (nil, string): True if build was successful; nil and an -- error message otherwise. -function search.command(flags, name, version) +function cmd_search.command(flags, name, version) if flags["all"] then name, version = "", nil end @@ -70,4 +71,4 @@ function search.command(flags, name, version) return true end -return search +return cmd_search diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua index acbf1dd6..081265a5 100644 --- a/src/luarocks/deps.lua +++ b/src/luarocks/deps.lua @@ -240,7 +240,7 @@ end function deps.fulfill_dependencies(rockspec, deps_mode) local search = require("luarocks.search") - local install = require("luarocks.install") + local install = require("luarocks.cmd.install") if rockspec.supported_platforms then if not deps.platforms_set then diff --git a/src/luarocks/download.lua b/src/luarocks/download.lua index c0f2678f..ec9996b2 100644 --- a/src/luarocks/download.lua +++ b/src/luarocks/download.lua @@ -32,8 +32,8 @@ function download.download(arch, name, version, all) local has_result = false local all_ok = true local any_err = "" - for _, result in pairs(results) do - for _, items in pairs(result) do + for name, result in pairs(results) do + for version, items in pairs(result) do for _, item in ipairs(items) do -- Ignore provided rocks. if item.arch ~= "installed" then -- cgit v1.2.3-55-g6feb From bcdb901611b05870692de69bd2a7cd1eec88a114 Mon Sep 17 00:00:00 2001 From: Hisham Date: Sat, 29 Oct 2016 17:47:27 -0200 Subject: Adjust changes by @mpeterv in #638 to new locations. --- src/luarocks/manif/writer.lua | 189 +++++++++++++++++++++++++++--------------- src/luarocks/repos.lua | 4 +- 2 files changed, 126 insertions(+), 67 deletions(-) diff --git a/src/luarocks/manif/writer.lua b/src/luarocks/manif/writer.lua index 1eb5ee7c..df971fca 100644 --- a/src/luarocks/manif/writer.lua +++ b/src/luarocks/manif/writer.lua @@ -13,33 +13,60 @@ local path = require("luarocks.path") local persist = require("luarocks.persist") local manif = require("luarocks.manif") ---- Output a table listing items of a package. --- @param itemsfn function: a function for obtaining items of a package. --- pkg and version will be passed to it; it should return a table with --- items as keys. --- @param pkg string: package name --- @param version string: package version --- @param tbl table: the package matching table: keys should be item names --- and values arrays of strings with packages names in "name/version" format. -local function store_package_items(itemsfn, pkg, version, tbl) - assert(type(itemsfn) == "function") - assert(type(pkg) == "string") +--- Update storage table to account for items provided by a package. +-- @param storage table: a table storing items in the following format: +-- keys are item names and values are arrays of packages providing each item, +-- where a package is specified as string `name/version`. +-- @param items table: a table mapping item names to paths. +-- @param name string: package name. +-- @param version string: package version. +local function store_package_items(storage, name, version, items) + assert(type(storage) == "table") + assert(type(items) == "table") + assert(type(name) == "string") assert(type(version) == "string") - assert(type(tbl) == "table") - local pkg_version = pkg.."/"..version - local result = {} + local package_identifier = name.."/"..version - for item, path in pairs(itemsfn(pkg, version)) do - result[item] = path - if not tbl[item] then - tbl[item] = {} + for item_name, path in pairs(items) do + if not storage[item_name] then + storage[item_name] = {} end - table.insert(tbl[item], pkg_version) + + table.insert(storage[item_name], package_identifier) end - return result end +--- Update storage table removing items provided by a package. +-- @param storage table: a table storing items in the following format: +-- keys are item names and values are arrays of packages providing each item, +-- where a package is specified as string `name/version`. +-- @param items table: a table mapping item names to paths. +-- @param name string: package name. +-- @param version string: package version. +local function remove_package_items(storage, name, version, items) + assert(type(storage) == "table") + assert(type(items) == "table") + assert(type(name) == "string") + assert(type(version) == "string") + + local package_identifier = name.."/"..version + + for item_name, path in pairs(items) do + local all_identifiers = storage[item_name] + + for i, identifier in ipairs(all_identifiers) do + if identifier == package_identifier then + table.remove(all_identifiers, i) + break + end + end + + if #all_identifiers == 0 then + storage[item_name] = nil + end + end +end --- Process the dependencies of a manifest table to determine its dependency -- chains for loading modules. The manifest dependencies information is filled @@ -174,12 +201,10 @@ end -- @param results table: The search results as returned by search.disk_search. -- @param manifest table: A manifest table (must contain repository, modules, commands tables). -- It will be altered to include the search results. --- @param dep_handler: dependency handler function -- @return boolean or (nil, string): true in case of success, or nil followed by an error message. -local function store_results(results, manifest, dep_handler) +local function store_results(results, manifest) assert(type(results) == "table") assert(type(manifest) == "table") - assert((not dep_handler) or type(dep_handler) == "function") for name, versions in pairs(results) do local pkgtable = manifest.repository[name] or {} @@ -189,10 +214,15 @@ local function store_results(results, manifest, dep_handler) local entrytable = {} entrytable.arch = entry.arch if entry.arch == "installed" then - local rock_manifest, err = manif.load_rock_manifest(name, version) - if not rock_manifest then return nil, err end - entrytable.modules = store_package_items(repos.package_modules, name, version, manifest.modules) - entrytable.commands = store_package_items(repos.package_commands, name, version, manifest.commands) + local rock_manifest = manif.load_rock_manifest(name, version) + if not rock_manifest then + return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?" + end + + entrytable.modules = repos.package_modules(name, version) + store_package_items(manifest.modules, name, version, entrytable.modules) + entrytable.commands = repos.package_commands(name, version) + store_package_items(manifest.commands, name, version, entrytable.commands) end table.insert(versiontable, entrytable) end @@ -200,9 +230,6 @@ local function store_results(results, manifest, dep_handler) end manifest.repository[name] = pkgtable end - if dep_handler then - dep_handler(manifest) - end sort_package_matching_table(manifest.modules) sort_package_matching_table(manifest.commands) return true @@ -265,7 +292,7 @@ end -- @param deps_mode string: Dependency mode: "one" for the current default tree, -- "all" for all trees, "order" for all trees with priority >= the current default, -- "none" for the default dependency mode from the configuration. --- @param versioned boolean: if versioned versions of the manifest should be created. +-- @param remote boolean: 'true' if making a manifest for a rocks server. -- @return boolean or (nil, string): True if manifest was generated, -- or nil and an error message. function writer.make_manifest(repo, deps_mode, remote) @@ -286,34 +313,26 @@ function writer.make_manifest(repo, deps_mode, remote) manif.cache_manifest(repo, nil, manifest) - local dep_handler = nil - if not remote then - dep_handler = function(manifest) - update_dependencies(manifest, deps_mode) - end - end - local ok, err = store_results(results, manifest, dep_handler) + local ok, err = store_results(results, manifest) if not ok then return nil, err end if remote then local cache = {} for luaver in util.lua_versions() do local vmanifest = { repository = {}, modules = {}, commands = {} } - local dep_handler = function(manifest) - filter_by_lua_version(manifest, luaver, repo, cache) - end - store_results(results, vmanifest, dep_handler) + local ok, err = store_results(results, vmanifest) + filter_by_lua_version(vmanifest, luaver, repo, cache) save_table(repo, "manifest-"..luaver, vmanifest) end + else + update_dependencies(manifest, deps_mode) end return save_table(repo, "manifest", manifest) end ---- Load a manifest file from a local repository and add to the repository --- information with regard to the given name and version. --- A file called 'manifest' will be written in the root of the given --- repository directory. +--- Update manifest file for a local repository +-- adding information about a version of a package installed in that repository. -- @param name string: Name of a package from the repository. -- @param version string: Version of a package from the repository. -- @param repo string or nil: Pathname of a local repository. If not given, @@ -321,38 +340,78 @@ end -- @param deps_mode string: Dependency mode: "one" for the current default tree, -- "all" for all trees, "order" for all trees with priority >= the current default, -- "none" for using the default dependency mode from the configuration. --- @return boolean or (nil, string): True if manifest was generated, +-- @return boolean or (nil, string): True if manifest was updated successfully, -- or nil and an error message. -function writer.update_manifest(name, version, repo, deps_mode) +function writer.add_to_manifest(name, version, repo, deps_mode) assert(type(name) == "string") assert(type(version) == "string") - repo = path.rocks_dir(repo or cfg.root_dir) + local rocks_dir = path.rocks_dir(repo or cfg.root_dir) assert(type(deps_mode) == "string") - + if deps_mode == "none" then deps_mode = cfg.deps_mode end - local manifest, err = manif.load_manifest(repo) + local manifest, err = manif.load_local_manifest(rocks_dir) if not manifest then util.printerr("No existing manifest. Attempting to rebuild...") - local ok, err = writer.make_manifest(repo, deps_mode) - if not ok then - return nil, err - end - manifest, err = manif.load_manifest(repo) - if not manifest then - return nil, err - end + -- Manifest built by `manif.make_manifest` should already + -- include information about given name and version, + -- no need to update it. + return manif.make_manifest(rocks_dir, deps_mode) end - local results = {[name] = {[version] = {{arch = "installed", repo = repo}}}} + local results = {[name] = {[version] = {{arch = "installed", repo = rocks_dir}}}} - local dep_handler = function(manifest) - update_dependencies(manifest, deps_mode) - end - local ok, err = store_results(results, manifest, dep_handler) + local ok, err = store_results(results, manifest) if not ok then return nil, err end - return save_table(repo, "manifest", manifest) + update_dependencies(manifest, deps_mode) + return save_table(rocks_dir, "manifest", manifest) +end + +--- Update manifest file for a local repository +-- removing information about a version of a package. +-- @param name string: Name of a package removed from the repository. +-- @param version string: Version of a package removed from the repository. +-- @param repo string or nil: Pathname of a local repository. If not given, +-- the default local repository is used. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for using the default dependency mode from the configuration. +-- @return boolean or (nil, string): True if manifest was updated successfully, +-- or nil and an error message. +function writer.remove_from_manifest(name, version, repo, deps_mode) + assert(type(name) == "string") + assert(type(version) == "string") + local rocks_dir = path.rocks_dir(repo or cfg.root_dir) + assert(type(deps_mode) == "string") + + if deps_mode == "none" then deps_mode = cfg.deps_mode end + + local manifest, err = manif.load_local_manifest(rocks_dir) + if not manifest then + util.printerr("No existing manifest. Attempting to rebuild...") + -- Manifest built by `manif.make_manifest` should already + -- include up-to-date information, no need to update it. + return manif.make_manifest(rocks_dir, deps_mode) + end + + local package_entry = manifest.repository[name] + + local version_entry = package_entry[version][1] + remove_package_items(manifest.modules, name, version, version_entry.modules) + remove_package_items(manifest.commands, name, version, version_entry.commands) + + package_entry[version] = nil + manifest.dependencies[name][version] = nil + + if not next(package_entry) then + -- No more versions of this package. + manifest.repository[name] = nil + manifest.dependencies[name] = nil + end + + update_dependencies(manifest, deps_mode) + return save_table(rocks_dir, "manifest", manifest) end return writer diff --git a/src/luarocks/repos.lua b/src/luarocks/repos.lua index 9616fe03..bbc9da79 100644 --- a/src/luarocks/repos.lua +++ b/src/luarocks/repos.lua @@ -315,7 +315,7 @@ function repos.deploy_files(name, version, wrap_bin_scripts, deps_mode) end local writer = require("luarocks.manif.writer") - return writer.update_manifest(name, version, nil, deps_mode) + return writer.add_to_manifest(name, version, nil, deps_mode) end --- Delete a package from the local repository. @@ -400,7 +400,7 @@ function repos.delete_version(name, version, deps_mode, quick) end local writer = require("luarocks.manif.writer") - return writer.make_manifest(cfg.rocks_dir, deps_mode) + return writer.remove_from_manifest(name, version, nil, deps_mode) end return repos -- cgit v1.2.3-55-g6feb From 339ad9a31c73d31f09a3db56b1c7e86d026fef16 Mon Sep 17 00:00:00 2001 From: Hisham Date: Sat, 29 Oct 2016 18:23:32 -0200 Subject: Fix names of modules that no longer require _cmd --- src/luarocks/cmd/config.lua | 71 +++++++++++++++++++++++++++++++++++++++++ src/luarocks/cmd/config_cmd.lua | 71 ----------------------------------------- src/luarocks/cmd/path.lua | 68 +++++++++++++++++++++++++++++++++++++++ src/luarocks/cmd/path_cmd.lua | 68 --------------------------------------- 4 files changed, 139 insertions(+), 139 deletions(-) create mode 100644 src/luarocks/cmd/config.lua delete mode 100644 src/luarocks/cmd/config_cmd.lua create mode 100644 src/luarocks/cmd/path.lua delete mode 100644 src/luarocks/cmd/path_cmd.lua diff --git a/src/luarocks/cmd/config.lua b/src/luarocks/cmd/config.lua new file mode 100644 index 00000000..b68f7898 --- /dev/null +++ b/src/luarocks/cmd/config.lua @@ -0,0 +1,71 @@ +--- Module implementing the LuaRocks "config" command. +-- Queries information about the LuaRocks configuration. +local config_cmd = {} + +local cfg = require("luarocks.core.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") + +config_cmd.help_summary = "Query information about the LuaRocks configuration." +config_cmd.help_arguments = "" +config_cmd.help = [[ +--lua-incdir Path to Lua header files. + +--lua-libdir Path to Lua library files. + +--lua-ver Lua version (in major.minor format). e.g. 5.1 + +--system-config Location of the system config file. + +--user-config Location of the user config file. + +--rock-trees Rocks trees in use. First the user tree, then the system tree. +]] + +local function config_file(conf) + print(dir.normalize(conf.file)) + if conf.ok then + return true + else + return nil, "file not found" + end +end + +--- Driver function for "config" command. +-- @return boolean: True if succeeded, nil on errors. +function config_cmd.command(flags) + if flags["lua-incdir"] then + print(cfg.variables.LUA_INCDIR) + return true + end + if flags["lua-libdir"] then + print(cfg.variables.LUA_LIBDIR) + return true + end + if flags["lua-ver"] then + print(cfg.lua_version) + return true + end + local conf = cfg.which_config() + if flags["system-config"] then + return config_file(conf.system) + end + if flags["user-config"] then + return config_file(conf.user) + end + if flags["rock-trees"] then + for _, tree in ipairs(cfg.rocks_trees) do + if type(tree) == "string" then + util.printout(dir.normalize(tree)) + else + local name = tree.name and "\t"..tree.name or "" + util.printout(dir.normalize(tree.root)..name) + end + end + return true + end + + return nil, "Please provide a flag for querying configuration values. "..util.see_help("config") +end + +return config_cmd diff --git a/src/luarocks/cmd/config_cmd.lua b/src/luarocks/cmd/config_cmd.lua deleted file mode 100644 index b68f7898..00000000 --- a/src/luarocks/cmd/config_cmd.lua +++ /dev/null @@ -1,71 +0,0 @@ ---- Module implementing the LuaRocks "config" command. --- Queries information about the LuaRocks configuration. -local config_cmd = {} - -local cfg = require("luarocks.core.cfg") -local util = require("luarocks.util") -local dir = require("luarocks.dir") - -config_cmd.help_summary = "Query information about the LuaRocks configuration." -config_cmd.help_arguments = "" -config_cmd.help = [[ ---lua-incdir Path to Lua header files. - ---lua-libdir Path to Lua library files. - ---lua-ver Lua version (in major.minor format). e.g. 5.1 - ---system-config Location of the system config file. - ---user-config Location of the user config file. - ---rock-trees Rocks trees in use. First the user tree, then the system tree. -]] - -local function config_file(conf) - print(dir.normalize(conf.file)) - if conf.ok then - return true - else - return nil, "file not found" - end -end - ---- Driver function for "config" command. --- @return boolean: True if succeeded, nil on errors. -function config_cmd.command(flags) - if flags["lua-incdir"] then - print(cfg.variables.LUA_INCDIR) - return true - end - if flags["lua-libdir"] then - print(cfg.variables.LUA_LIBDIR) - return true - end - if flags["lua-ver"] then - print(cfg.lua_version) - return true - end - local conf = cfg.which_config() - if flags["system-config"] then - return config_file(conf.system) - end - if flags["user-config"] then - return config_file(conf.user) - end - if flags["rock-trees"] then - for _, tree in ipairs(cfg.rocks_trees) do - if type(tree) == "string" then - util.printout(dir.normalize(tree)) - else - local name = tree.name and "\t"..tree.name or "" - util.printout(dir.normalize(tree.root)..name) - end - end - return true - end - - return nil, "Please provide a flag for querying configuration values. "..util.see_help("config") -end - -return config_cmd diff --git a/src/luarocks/cmd/path.lua b/src/luarocks/cmd/path.lua new file mode 100644 index 00000000..516a0c47 --- /dev/null +++ b/src/luarocks/cmd/path.lua @@ -0,0 +1,68 @@ + +--- @module luarocks.path_cmd +-- Driver for the `luarocks path` command. +local path_cmd = {} + +local util = require("luarocks.util") +local cfg = require("luarocks.core.cfg") + +path_cmd.help_summary = "Return the currently configured package path." +path_cmd.help_arguments = "" +path_cmd.help = [[ +Returns the package path currently configured for this installation +of LuaRocks, formatted as shell commands to update LUA_PATH and LUA_CPATH. + +--bin Adds the system path to the output + +--append Appends the paths to the existing paths. Default is to prefix + the LR paths to the existing paths. + +--lr-path Exports the Lua path (not formatted as shell command) + +--lr-cpath Exports the Lua cpath (not formatted as shell command) + +--lr-bin Exports the system path (not formatted as shell command) + + +On Unix systems, you may run: + eval `luarocks path` +And on Windows: + luarocks path > "%temp%\_lrp.bat" && call "%temp%\_lrp.bat" && del "%temp%\_lrp.bat" +]] + +--- Driver function for "path" command. +-- @return boolean This function always succeeds. +function path_cmd.command(flags) + local lr_path, lr_cpath, lr_bin = cfg.package_paths(flags["tree"]) + local path_sep = cfg.export_path_separator + + if flags["lr-path"] then + util.printout(util.remove_path_dupes(lr_path, ';')) + return true + elseif flags["lr-cpath"] then + util.printout(util.remove_path_dupes(lr_cpath, ';')) + return true + elseif flags["lr-bin"] then + util.printout(util.remove_path_dupes(lr_bin, path_sep)) + return true + end + + if flags["append"] then + lr_path = package.path .. ";" .. lr_path + lr_cpath = package.cpath .. ";" .. lr_cpath + lr_bin = os.getenv("PATH") .. path_sep .. lr_bin + else + lr_path = lr_path.. ";" .. package.path + lr_cpath = lr_cpath .. ";" .. package.cpath + lr_bin = lr_bin .. path_sep .. os.getenv("PATH") + end + + util.printout(cfg.export_lua_path:format(util.remove_path_dupes(lr_path, ';'))) + util.printout(cfg.export_lua_cpath:format(util.remove_path_dupes(lr_cpath, ';'))) + if flags["bin"] then + util.printout(cfg.export_path:format(util.remove_path_dupes(lr_bin, path_sep))) + end + return true +end + +return path_cmd diff --git a/src/luarocks/cmd/path_cmd.lua b/src/luarocks/cmd/path_cmd.lua deleted file mode 100644 index 516a0c47..00000000 --- a/src/luarocks/cmd/path_cmd.lua +++ /dev/null @@ -1,68 +0,0 @@ - ---- @module luarocks.path_cmd --- Driver for the `luarocks path` command. -local path_cmd = {} - -local util = require("luarocks.util") -local cfg = require("luarocks.core.cfg") - -path_cmd.help_summary = "Return the currently configured package path." -path_cmd.help_arguments = "" -path_cmd.help = [[ -Returns the package path currently configured for this installation -of LuaRocks, formatted as shell commands to update LUA_PATH and LUA_CPATH. - ---bin Adds the system path to the output - ---append Appends the paths to the existing paths. Default is to prefix - the LR paths to the existing paths. - ---lr-path Exports the Lua path (not formatted as shell command) - ---lr-cpath Exports the Lua cpath (not formatted as shell command) - ---lr-bin Exports the system path (not formatted as shell command) - - -On Unix systems, you may run: - eval `luarocks path` -And on Windows: - luarocks path > "%temp%\_lrp.bat" && call "%temp%\_lrp.bat" && del "%temp%\_lrp.bat" -]] - ---- Driver function for "path" command. --- @return boolean This function always succeeds. -function path_cmd.command(flags) - local lr_path, lr_cpath, lr_bin = cfg.package_paths(flags["tree"]) - local path_sep = cfg.export_path_separator - - if flags["lr-path"] then - util.printout(util.remove_path_dupes(lr_path, ';')) - return true - elseif flags["lr-cpath"] then - util.printout(util.remove_path_dupes(lr_cpath, ';')) - return true - elseif flags["lr-bin"] then - util.printout(util.remove_path_dupes(lr_bin, path_sep)) - return true - end - - if flags["append"] then - lr_path = package.path .. ";" .. lr_path - lr_cpath = package.cpath .. ";" .. lr_cpath - lr_bin = os.getenv("PATH") .. path_sep .. lr_bin - else - lr_path = lr_path.. ";" .. package.path - lr_cpath = lr_cpath .. ";" .. package.cpath - lr_bin = lr_bin .. path_sep .. os.getenv("PATH") - end - - util.printout(cfg.export_lua_path:format(util.remove_path_dupes(lr_path, ';'))) - util.printout(cfg.export_lua_cpath:format(util.remove_path_dupes(lr_cpath, ';'))) - if flags["bin"] then - util.printout(cfg.export_path:format(util.remove_path_dupes(lr_bin, path_sep))) - end - return true -end - -return path_cmd -- cgit v1.2.3-55-g6feb From f8bd89d27323403dd9de9772a7974b439b37b29d Mon Sep 17 00:00:00 2001 From: Hisham Date: Sat, 29 Oct 2016 21:52:20 -0200 Subject: Fix references to writer.make_manifest --- src/luarocks/manif/writer.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/luarocks/manif/writer.lua b/src/luarocks/manif/writer.lua index df971fca..daa6b005 100644 --- a/src/luarocks/manif/writer.lua +++ b/src/luarocks/manif/writer.lua @@ -353,10 +353,10 @@ function writer.add_to_manifest(name, version, repo, deps_mode) local manifest, err = manif.load_local_manifest(rocks_dir) if not manifest then util.printerr("No existing manifest. Attempting to rebuild...") - -- Manifest built by `manif.make_manifest` should already + -- Manifest built by `writer.make_manifest` should already -- include information about given name and version, -- no need to update it. - return manif.make_manifest(rocks_dir, deps_mode) + return writer.make_manifest(rocks_dir, deps_mode) end local results = {[name] = {[version] = {{arch = "installed", repo = rocks_dir}}}} @@ -390,9 +390,9 @@ function writer.remove_from_manifest(name, version, repo, deps_mode) local manifest, err = manif.load_local_manifest(rocks_dir) if not manifest then util.printerr("No existing manifest. Attempting to rebuild...") - -- Manifest built by `manif.make_manifest` should already + -- Manifest built by `writer.make_manifest` should already -- include up-to-date information, no need to update it. - return manif.make_manifest(rocks_dir, deps_mode) + return writer.make_manifest(rocks_dir, deps_mode) end local package_entry = manifest.repository[name] -- cgit v1.2.3-55-g6feb