diff options
author | Ronan Collobert <locronan@fb.com> | 2014-09-11 01:51:39 +0400 |
---|---|---|
committer | Ronan Collobert <locronan@fb.com> | 2014-09-11 01:51:39 +0400 |
commit | d4d49fd9fbdd07a5e4b4fca85544e99cd17d63d4 (patch) | |
tree | e49da758e42936f6d9408d75e0faddd686f6f811 | |
parent | 6819e2edda2db910b9e82fb302448f7d45eacac7 (diff) | |
parent | 31690a82a01bf8a336699e6a278f85d5a420561f (diff) |
Merge commit '31690a82a01bf8a336699e6a278f85d5a420561f' into vanilla
Conflicts:
luarocks/install.bat
luarocks/src/luarocks/cfg.lua
100 files changed, 4877 insertions, 1634 deletions
diff --git a/luarocks/.travis.yml b/luarocks/.travis.yml new file mode 100644 index 0000000..cf2b1e6 --- /dev/null +++ b/luarocks/.travis.yml @@ -0,0 +1,10 @@ +language: c + +compiler: gcc + +env: + matrix: + - LUA_VER=5.1.5 + - LUA_VER=5.2.3 + +script: cd test && ./testing.sh --travis --lua $LUA_VER diff --git a/luarocks/COPYING b/luarocks/COPYING index 4311883..34a3de3 100644 --- a/luarocks/COPYING +++ b/luarocks/COPYING @@ -1,31 +1,4 @@ -LuaRocks is free software: it can be used for both academic and commercial -purposes at absolutely no cost. There are no royalties or GNU-like "copyleft" -restrictions. LuaRocks qualifies as Open Source software. Its licenses are -compatible with the GPL. LuaRocks is not in the public domain and the Kepler -Project keeps its copyright. The legal details are below. - -The spirit of the license is that you are free to use LuaRocks for any purpose -at no cost without having to ask us. The only requirement is that if you do -use LuaRocks, then you should give us credit by including the appropriate -copyright notice somewhere in your product or its documentation. - -7z.exe and 7z.dll are covered by another license, please see COPYING.7z for -details. - -find.exe, mv.exe, rm.exe, wget.exe, ls.exe, pwd.exe, rmdir.exe, chmod.exe, -md5sum.exe, test.exe, cp.exe, mkdir.exe, and uname.exe are part of UnxUtils, -check http://unxutils.sourceforge.net/ for license information. - -Files under win32/lua5.1, except for Microsoft.VC80.CRT.manifest and msv*.*, -are covered by another license, please see COPYING.lua for details. - -Microsoft.VC80.CRT.manifest, msvcm80.dll, msvcp80.dll, msvcr80.dll are part -of the Microsoft Visual C++ 2005 SP1 Redistributable Package (x86) and -are Copyright Microsoft, Inc. 2007. - ------------------------------------------------------------------------------- - -Copyright 2007-2010 Kepler Project. +Copyright 2007-2014 Kepler Project. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/luarocks/COPYING_win b/luarocks/COPYING_win new file mode 100644 index 0000000..3413b8c --- /dev/null +++ b/luarocks/COPYING_win @@ -0,0 +1,16 @@ + +This package includes additional Windows components which are under the +following licenses: + +* For 7z.exe and 7z.dll, please see COPYING_7z for details. + +* find.exe, mv.exe, wget.exe, ls.exe, pwd.exe, rmdir.exe, md5sum.exe, + test.exe, cp.exe, mkdir.exe, and uname.exe are part of UnxUtils, check + http://unxutils.sourceforge.net/ for license information. + +* Files under win32/lua5.1, except for Microsoft.VC80.CRT.manifest and msv*.*, + are covered by another license, please see COPYING_lua for details. + +* Microsoft.VC80.CRT.manifest, msvcm80.dll, msvcp80.dll, msvcr80.dll are part + of the Microsoft Visual C++ 2005 SP1 Redistributable Package (x86) and are + Copyright Microsoft, Inc. 2007. diff --git a/luarocks/Makefile b/luarocks/Makefile index be83c01..1f9d0e2 100644 --- a/luarocks/Makefile +++ b/luarocks/Makefile @@ -1,7 +1,10 @@ -# $Id: Makefile,v 1.30 2008/08/18 14:07:35 hisham Exp $ include config.unix +.PHONY: all build dev build_bins luadoc check_makefile cleanup_bins clean \ + install_bins install_luas install_site_config write_sysconfig \ + install bootstrap install_rock + DESTDIR = PREFIX ?= /usr/local ROCKS_TREE ?= $(PREFIX) @@ -16,14 +19,18 @@ LUAROCKS_FILES = 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 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 path.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 +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 CONFIG_FILE = $(SYSCONFDIR)/config-$(LUA_VERSION).lua +SAFEPWD=`echo "$$PWD" | sed -e 's/\([][]\)\1/]]..'\''\1\1'\''..[[/g'` + all: @echo "- Type 'make build' and 'make install':" @echo " to install to $(PREFIX) as usual." @@ -31,55 +38,64 @@ all: @echo " to install LuaRocks in $(PREFIX) as a rock." @echo -build: built +build: src/luarocks/site_config.lua build_bins + @echo + @echo "Done. Type 'make install' to install into $(PREFIX)." + @echo src/luarocks/site_config.lua: config.unix rm -f src/luarocks/site_config.lua - echo 'module("luarocks.site_config")' >> src/luarocks/site_config.lua + echo 'local site_config = {}' >> src/luarocks/site_config.lua if [ -n "$(PREFIX)" ] ;\ then \ - echo "LUAROCKS_PREFIX=[[$(PREFIX)]]" >> src/luarocks/site_config.lua ;\ + echo "site_config.LUAROCKS_PREFIX=[[$(PREFIX)]]" >> src/luarocks/site_config.lua ;\ fi if [ -n "$(LUA_INCDIR)" ] ;\ then \ - echo "LUA_INCDIR=[[$(LUA_INCDIR)]]" >> src/luarocks/site_config.lua ;\ + echo "site_config.LUA_INCDIR=[[$(LUA_INCDIR)]]" >> src/luarocks/site_config.lua ;\ fi if [ -n "$(LUA_LIBDIR)" ] ;\ then \ - echo "LUA_LIBDIR=[[$(LUA_LIBDIR)]]" >> src/luarocks/site_config.lua ;\ + echo "site_config.LUA_LIBDIR=[[$(LUA_LIBDIR)]]" >> src/luarocks/site_config.lua ;\ fi if [ -n "$(LUA_BINDIR)" ] ;\ then \ - echo "LUA_BINDIR=[[$(LUA_BINDIR)]]" >> src/luarocks/site_config.lua ;\ + echo "site_config.LUA_BINDIR=[[$(LUA_BINDIR)]]" >> src/luarocks/site_config.lua ;\ fi if [ -n "$(LUA_SUFFIX)" ] ;\ then \ - echo "LUA_INTERPRETER=[[lua$(LUA_SUFFIX)]]" >> src/luarocks/site_config.lua ;\ + echo "site_config.LUA_INTERPRETER=[[lua$(LUA_SUFFIX)]]" >> src/luarocks/site_config.lua ;\ fi if [ -n "$(SYSCONFDIR)" ] ;\ then \ - echo "LUAROCKS_SYSCONFDIR=[[$(SYSCONFDIR)]]" >> src/luarocks/site_config.lua ;\ + echo "site_config.LUAROCKS_SYSCONFDIR=[[$(SYSCONFDIR)]]" >> src/luarocks/site_config.lua ;\ fi if [ -n "$(ROCKS_TREE)" ] ;\ then \ - echo "LUAROCKS_ROCKS_TREE=[[$(ROCKS_TREE)]]" >> src/luarocks/site_config.lua ;\ + echo "site_config.LUAROCKS_ROCKS_TREE=[[$(ROCKS_TREE)]]" >> src/luarocks/site_config.lua ;\ fi if [ -n "$(FORCE_CONFIG)" ] ;\ then \ - echo "LUAROCKS_FORCE_CONFIG=true" >> src/luarocks/site_config.lua ;\ + echo "site_config.LUAROCKS_FORCE_CONFIG=true" >> src/luarocks/site_config.lua ;\ fi if [ -n "$(LUAROCKS_ROCKS_SUBDIR)" ] ;\ then \ - echo "LUAROCKS_ROCKS_SUBDIR=[[$(LUAROCKS_ROCKS_SUBDIR)]]" >> src/luarocks/site_config.lua ;\ + echo "site_config.LUAROCKS_ROCKS_SUBDIR=[[$(LUAROCKS_ROCKS_SUBDIR)]]" >> src/luarocks/site_config.lua ;\ fi if [ "$(LUA_DIR_SET)" = "yes" ] ;\ then \ - echo "LUA_DIR_SET=true" >> src/luarocks/site_config.lua ;\ + echo "site_config.LUA_DIR_SET=true" >> src/luarocks/site_config.lua ;\ fi - echo "LUAROCKS_UNAME_S=[[$(LUAROCKS_UNAME_S)]]" >> src/luarocks/site_config.lua - echo "LUAROCKS_UNAME_M=[[$(LUAROCKS_UNAME_M)]]" >> src/luarocks/site_config.lua - echo "LUAROCKS_DOWNLOADER=[[$(LUAROCKS_DOWNLOADER)]]" >> src/luarocks/site_config.lua - echo "LUAROCKS_MD5CHECKER=[[$(LUAROCKS_MD5CHECKER)]]" >> src/luarocks/site_config.lua + echo "site_config.LUAROCKS_UNAME_S=[[$(LUAROCKS_UNAME_S)]]" >> src/luarocks/site_config.lua + echo "site_config.LUAROCKS_UNAME_M=[[$(LUAROCKS_UNAME_M)]]" >> src/luarocks/site_config.lua + echo "site_config.LUAROCKS_DOWNLOADER=[[$(LUAROCKS_DOWNLOADER)]]" >> src/luarocks/site_config.lua + echo "site_config.LUAROCKS_MD5CHECKER=[[$(LUAROCKS_MD5CHECKER)]]" >> src/luarocks/site_config.lua + if [ -n "$(MULTIARCH_SUBDIR)" ] ;\ + then \ + echo 'site_config.LUAROCKS_EXTERNAL_DEPS_SUBDIRS={ bin="bin", lib={ "lib", [[$(MULTIARCH_SUBDIR)]] }, include="include" }' >> src/luarocks/site_config.lua ;\ + echo 'site_config.LUAROCKS_RUNTIME_EXTERNAL_DEPS_SUBDIRS={ bin="bin", lib={ "lib", [[$(MULTIARCH_SUBDIR)]] }, include="include" }' >> src/luarocks/site_config.lua ;\ + fi + echo "return site_config" >> src/luarocks/site_config.lua dev: $(MAKE) build_bins LUADIR=$(PWD)/src @@ -89,18 +105,12 @@ build_bins: cleanup_bins do \ sed "1d" src/bin/$$f > src/bin/$$f.bak ;\ echo "#!$(LUA_BINDIR)/lua$(LUA_SUFFIX)" > src/bin/$$f ;\ - echo "package.path = [[$(LUADIR)/?.lua;$(LUADIR)/?/init.lua;]]..package.path" >> src/bin/$$f ;\ + echo "package.path = [[$(LUADIR)/?.lua;]]..package.path" | sed "s,//,/,g" >> src/bin/$$f ;\ cat src/bin/$$f.bak >> src/bin/$$f ;\ chmod +x src/bin/$$f ;\ rm -f src/bin/$$f.bak ;\ done -built: src/luarocks/site_config.lua build_bins - touch built - @echo - @echo "Done. Type 'make install' to install into $(PREFIX)." - @echo - luadoc: rm -rf doc/luadoc mkdir -p doc/luadoc @@ -128,17 +138,16 @@ cleanup_bins: clean: cleanup_bins rm -f src/luarocks/site_config.lua - rm -f built -install_bins: built +install_bins: mkdir -p "$(DESTDIR)$(BINDIR)" cd src/bin && for f in $(BIN_FILES); \ do \ cp "$$f" "$(DESTDIR)$(BINDIR)/$$f-$(LUA_VERSION)"; \ - ln -nfs "$(DESTDIR)$(BINDIR)/$$f-$(LUA_VERSION)" "$(DESTDIR)$(BINDIR)/$$f"; \ + ln -nfs "$$f-$(LUA_VERSION)" "$(DESTDIR)$(BINDIR)/$$f"; \ done -install_luas: built +install_luas: mkdir -p "$(DESTDIR)$(LUADIR)/luarocks" cd src/luarocks && for f in $(LUAROCKS_FILES); \ do \ @@ -147,11 +156,11 @@ install_luas: built cp "$$f" "$$d" || exit 1; \ done -install_site_config: built +install_site_config: src/luarocks/site_config.lua mkdir -p "$(DESTDIR)$(LUADIR)/luarocks" - cd src/luarocks && cp site_config.lua "$(DESTDIR)$(LUADIR)/luarocks" + cp src/luarocks/site_config.lua "$(DESTDIR)$(LUADIR)/luarocks" -write_sysconfig: built +write_sysconfig: mkdir -p "$(DESTDIR)$(ROCKS_TREE)" if [ ! -f "$(DESTDIR)$(CONFIG_FILE)" ] ;\ then \ @@ -159,15 +168,15 @@ write_sysconfig: built echo 'rocks_trees = {' >> "$(DESTDIR)$(CONFIG_FILE)" ;\ if [ ! -n "$(FORCE_CONFIG)" ] ;\ then \ - echo ' home..[[/.luarocks]],' >> "$(DESTDIR)$(CONFIG_FILE)" ;\ + echo ' { name = [[user]], root = home..[[/.luarocks]] },' >> "$(DESTDIR)$(CONFIG_FILE)" ;\ fi ;\ - echo ' [[$(ROCKS_TREE)]]' >> "$(DESTDIR)$(CONFIG_FILE)" ;\ + echo ' { name = [[system]], root = [[$(ROCKS_TREE)]] }' >> "$(DESTDIR)$(CONFIG_FILE)" ;\ echo '}' >> "$(DESTDIR)$(CONFIG_FILE)" ;\ fi install: install_bins install_luas install_site_config write_sysconfig -bootstrap: src/luarocks/site_config.lua install_site_config write_sysconfig - LUA_PATH="$$PWD/src/?.lua;$$LUA_PATH" src/bin/luarocks make rockspec --tree="$(PREFIX)" +bootstrap: src/luarocks/site_config.lua install_site_config write_sysconfig cleanup_bins + '$(LUA_BINDIR)/lua$(LUA_SUFFIX)' -e "package.path=[[$(SAFEPWD)/src/?.lua;]]..package.path" src/bin/luarocks make rockspec --tree="$(PREFIX)" install_rock: install_bins install_luas diff --git a/luarocks/README.md b/luarocks/README.md index 87345a2..c779c3d 100644 --- a/luarocks/README.md +++ b/luarocks/README.md @@ -1,8 +1,13 @@ -This is LuaRocks, a deployment and management system for Lua modules. +LuaRocks +======== + +A package manager for Lua modules. + +[![Build Status](https://travis-ci.org/keplerproject/luarocks.png?branch=master)](https://travis-ci.org/keplerproject/luarocks) Main website: [luarocks.org](http://www.luarocks.org) -LuaRocks allows you to install Lua modules as self-contained packages called +It allows you to install Lua modules as self-contained packages called [*rocks*][1], which also contain version [dependency][2] information. This information is used both during installation, so that when one rock is requested all rocks it depends on are installed as well, and at run time, so diff --git a/luarocks/configure b/luarocks/configure index a5aa1f4..a7b8529 100755 --- a/luarocks/configure +++ b/luarocks/configure @@ -14,6 +14,7 @@ LUA_BINDIR="/usr/bin" LUA_INCDIR="/usr/include" LUA_LIBDIR="/usr/lib" LUA_VERSION="5.1" +MULTIARCH_SUBDIR="" # ---------------------------------------------------------------------------- # FUNCTION DEFINITIONS @@ -35,21 +36,24 @@ Where to install files installed by rocks, to make the accessible to Lua and your \$PATH. Beware of clashes between files installed by LuaRocks and by your system's package manager. ---rocks-tree=FILE Root of the local tree of installed rocks. +--rocks-tree=DIR Root of the local tree of installed rocks. Default is \$PREFIX ---lua-version=VERSION Use specific Lua version: 5.1 or 5.2 +--lua-version=VERSION Use specific Lua version: 5.1, 5.2, or 5.3 Default is "$LUA_VERSION" --lua-suffix=SUFFIX Versioning suffix to use in Lua filenames. Default is "$LUA_SUFFIX" (lua$LUA_SUFFIX...) --with-lua=PREFIX Use Lua from given prefix. - Default is $LUA_DIR + Default is auto-detected (the parent directory of \$LUA_BINDIR). +--with-lua-bin=DIR You can also specify Lua's bin dir. + Default is the directory of the auto-detected Lua interpreter, + or \$LUA_DIR/bin if --with-lua is used. --with-lua-include=DIR You can also specify Lua's includes dir. Default is \$LUA_DIR/include --with-lua-lib=DIR You can also specify Lua's libraries dir. Default is \$LUA_DIR/lib --with-downloader=TOOL Which tool to use as a downloader. - Valid options are: wget, curl. + Valid options are: curl, wget. Default is to auto-detect. --with-md5-checker=TOOL Which tool to use as a downloader. Valid options are: md5sum, openssl @@ -176,7 +180,7 @@ do --lua-version) [ -n "$value" ] || die "Missing value in flag $key." LUA_VERSION="$value" - [ "$LUA_VERSION" = "5.1" -o "$LUA_VERSION" = "5.2" ] || die "Invalid Lua version in flag $key." + [ "$LUA_VERSION" = "5.1" -o "$LUA_VERSION" = "5.2" -o "$LUA_VERSION" = "5.3" ] || die "Invalid Lua version in flag $key." LUA_VERSION_SET=yes ;; --with-lua) @@ -184,6 +188,11 @@ do LUA_DIR="$value" LUA_DIR_SET=yes ;; + --with-lua-bin) + [ -n "$value" ] || die "Missing value in flag $key." + LUA_BINDIR="$value" + LUA_BINDIR_SET=yes + ;; --with-lua-include) [ -n "$value" ] || die "Missing value in flag $key." LUA_INCDIR="$value" @@ -233,7 +242,7 @@ fi detect_lua_version() { detected_lua=`$1 -e 'print(_VERSION:sub(5))' 2> /dev/null` - if [ "$detected_lua" = "5.1" -o "$detected_lua" = "5.2" ] + if [ "$detected_lua" = "5.1" -o "$detected_lua" = "5.2" -o "$detected_lua" = "5.3" ] then echo "Lua version detected: $detected_lua" if [ "$LUA_VERSION_SET" != "yes" ] @@ -248,7 +257,10 @@ detect_lua_version() { search_interpreter() { LUA_SUFFIX="$1" - if [ "$LUA_DIR_SET" = "yes" ] + if [ "$LUA_BINDIR_SET" = "yes" ] + then + find_lua="$LUA_BINDIR" + elif [ "$LUA_DIR_SET" = "yes" ] then if [ -f "$LUA_DIR/bin/lua$suffix" ] then @@ -259,7 +271,7 @@ search_interpreter() { fi if [ -n "$find_lua" ] then - echo "Lua interpreter found: $find_lua/lua$suffix..." + echo "Lua interpreter: $find_lua/lua$suffix..." detect_lua_version "$find_lua/lua$suffix" return 0 fi @@ -270,14 +282,17 @@ if [ "$LUA_SUFFIX_SET" != "yes" ] then if [ "$LUA_VERSION_SET" = "yes" -a "$LUA_VERSION" = "5.1" ] then - suffixes="5.1 51" + suffixes="5.1 51 -5.1 -51" elif [ "$LUA_VERSION_SET" = "yes" -a "$LUA_VERSION" = "5.2" ] then - suffixes="5.2 52" + suffixes="5.2 52 -5.2 -52" + elif [ "$LUA_VERSION_SET" = "yes" -a "$LUA_VERSION" = "5.3" ] + then + suffixes="5.3 53 -5.3 -53" else - suffixes="5.2 52 5.1 51" + suffixes="5.3 53 -5.3 -53 5.2 52 -5.2 -52 5.1 51 -5.1 -51" fi - for suffix in "" `echo $suffixes` "" + for suffix in `echo $suffixes` "" do search_interpreter "$suffix" && break done @@ -285,9 +300,9 @@ fi if [ "$LUA_DIR_SET" != "yes" ] then - echo_n "Looking for Lua... " - if [ ! -n "$find_lua" ] + if [ -z "$find_lua" ] then + echo_n "Looking for Lua... " find_lua=`find_program lua$LUA_SUFFIX` fi @@ -312,7 +327,7 @@ then LUA_LIBDIR="$LUA_DIR/lib" fi -if [ "$LUA_DIR_SET" = "yes" ] +if [ "$LUA_DIR_SET" = "yes" -a "$LUA_BINDIR_SET" != "yes" ] then LUA_BINDIR="$LUA_DIR/bin" fi @@ -345,7 +360,7 @@ fi if [ "$LUAROCKS_DOWNLOADER_SET" != "yes" ] then - find_helper "downloader helper program" wget curl fetch + find_helper "downloader helper program" curl wget fetch LUAROCKS_DOWNLOADER=$HELPER fi @@ -370,12 +385,14 @@ else die "Could not determine processor architecture. 'uname -m' failed." fi -if [ "$LUA_VERSION" = "5.2" ] -then - LUA_OTHER_VERSION=5.1 -else - LUA_OTHER_VERSION=5.2 -fi +for v in 5.1 5.2 5.3; do + if [ "$v" != "$LUA_VERSION" ]; then + if [ -e "$PREFIX/share/lua/$v/luarocks/site_config.lua" ]; then + LUA_OTHER_VERSION="$v" + break + fi + fi +done LUAROCKS_ROCKS_SUBDIR=/lib/luarocks/rocks if [ "$VERSIONED_ROCKS_DIR" = "yes" ] @@ -387,7 +404,7 @@ then echo "Existing installation detected." LUAROCKS_ROCKS_SUBDIR=`grep "LUAROCKS_ROCKS_SUBDIR" "$PREFIX/share/lua/$LUA_VERSION/luarocks/site_config.lua" | sed 's,.*=\[\[\(.*\)\]\],\1,'` echo "Using previously configured rocks dir: $PREFIX$LUAROCKS_ROCKS_SUBDIR" -elif [ -e "$PREFIX/share/lua/$LUA_OTHER_VERSION/luarocks/site_config.lua" ] +elif [ -n "$LUA_OTHER_VERSION" ] then echo "Existing installation detected for other Lua version ($LUA_OTHER_VERSION)." LUAROCKS_ROCKS_SUBDIR=$LUAROCKS_ROCKS_SUBDIR-$LUA_VERSION @@ -396,6 +413,15 @@ else echo "Using unversioned rocks dir: $PREFIX$LUAROCKS_ROCKS_SUBDIR" fi +if [ "$LUAROCKS_UNAME_S" = Linux ] +then + GCC_ARCH=`gcc -print-multiarch 2>/dev/null` + if [ -n "$GCC_ARCH" -a -d "/usr/lib/$GCC_ARCH" ] + then + MULTIARCH_SUBDIR="lib/$GCC_ARCH" + fi +fi + if [ -f config.unix ]; then rm -f config.unix fi @@ -426,6 +452,7 @@ LUAROCKS_UNAME_S=$LUAROCKS_UNAME_S LUAROCKS_DOWNLOADER=$LUAROCKS_DOWNLOADER LUAROCKS_MD5CHECKER=$LUAROCKS_MD5CHECKER LUAROCKS_ROCKS_SUBDIR=$LUAROCKS_ROCKS_SUBDIR +MULTIARCH_SUBDIR=$MULTIARCH_SUBDIR EOF diff --git a/luarocks/install.bat b/luarocks/install.bat index 417b095..ae7df63 100644 --- a/luarocks/install.bat +++ b/luarocks/install.bat @@ -1,14 +1,17 @@ rem=rem --[[
@setlocal& set luafile="%~f0" & if exist "%~f0.bat" set luafile="%~f0.bat"
-@lua5.1\bin\lua5.1.exe %luafile% %*& exit /b ]]
+@win32\lua5.1\bin\lua5.1.exe %luafile% %*& exit /b ]]
local vars = {}
-vars.PREFIX = [[C:\LuaRocks]]
-vars.VERSION = "2.1"
-vars.SYSCONFDIR = [[C:\LuaRocks]]
-vars.ROCKS_TREE = [[C:\LuaRocks]]
-vars.SCRIPTS_DIR = nil
+
+vars.PREFIX = nil
+vars.VERSION = "2.2"
+vars.SYSCONFDIR = nil
+vars.TREE_ROOT = nil
+vars.TREE_BIN = nil
+vars.TREE_LMODULE = nil
+vars.TREE_CMODULE = nil
vars.LUA_INTERPRETER = nil
vars.LUA_PREFIX = nil
vars.LUA_BINDIR = nil
@@ -16,20 +19,29 @@ vars.LUA_INCDIR = nil vars.LUA_LIBDIR = nil
vars.LUA_LIBNAME = nil
vars.LUA_VERSION = "5.1"
-vars.LUA_SHORTV = nil
-vars.LUA_LIB_NAMES = "lua5.1.lib lua51.dll liblua.dll.a"
+vars.LUA_SHORTV = nil -- "51"
+-- MinGW does not generate .lib, nor needs it to link, but MSVC does
+-- so .lib must be listed first to ensure they are found first if present.
+-- To prevent MSVC trying to link to a .dll, which won't work.
+vars.LUA_LIB_NAMES = "lua5.1.lib lua51.lib lua5.1.dll lua51.dll liblua.dll.a"
vars.LUA_RUNTIME = nil
+vars.UNAME_M = nil
-local P_SET = false
local FORCE = false
local FORCE_CONFIG = false
local INSTALL_LUA = false
local USE_MINGW = false
-local REGISTRY = false
+local REGISTRY = true
+local NOADMIN = false
+local PROMPT = true
+local SELFCONTAINED = false
---
-- Some helpers
--
+
+local pe = assert(loadfile(".\\win32\\pe-parser.lua"))()
+
local function die(message)
if message then print(message) end
print()
@@ -37,19 +49,64 @@ local function die(message) os.exit(1)
end
+function 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
+
+
local function exec(cmd)
--print(cmd)
local status = os.execute(cmd)
- return status == 0
+ return (status == 0 or status == true) -- compat 5.1/5.2
end
local function exists(filename)
- local cmd = [[.\bin\test -e "]]..filename..[["]]
+ local cmd = [[.\win32\tools\test -e "]]..filename..[["]]
return exec(cmd)
end
local function mkdir (dir)
- return exec([[.\bin\mkdir -p "]]..dir..[[" >NUL]])
+ return exec([[.\win32\tools\mkdir -p "]]..dir..[[" >NUL]])
+end
+
+-- does the current user have admin priviledges ( = elevated)
+local function permission()
+ return exec("net session >NUL 2>&1") -- fails if not admin
+end
+
+-- rename file (full path) to backup (name only), appending number if required
+-- returns the new name (name only)
+local function backup(filename, backupname)
+ local path = filename:match("(.+)%\\.-$").."\\"
+ local nname = backupname
+ local i = 0
+ while exists(path..nname) do
+ i = i + 1
+ nname = backupname..tostring(i)
+ end
+ exec([[REN "]]..filename..[[" "]]..nname..[[" > NUL]])
+ return nname
end
-- interpolate string with values from 'vars' table
@@ -61,44 +118,69 @@ local function print_help() print(S[[
Installs LuaRocks.
-/P [dir] (REQUIRED) Where to install.
- Note that version; $VERSION, will be
- appended to this path.
-/CONFIG [dir] Location where the config file should be installed.
- Default is same place of installation
-/TREE [dir] Root of the local tree of installed rocks.
- Default is same place of installation
-/SCRIPTS [dir] Where to install scripts installed by rocks.
- Default is TREE/bin.
+/P [dir] Where to install LuaRocks.
+ Note that version; $VERSION, will be appended to this
+ path, so "/P c:\luarocks\" will install in
+ "c:\luarocks\$VERSION\"
+ Default is %PROGRAMFILES%\LuaRocks
-/LV [version] Lua version to use; either 5.1 or 5.2.
+Configuring the destinations:
+/TREE [dir] Root of the local tree of installed rocks.
+ Default is %PROGRAMFILES%\LuaRocks\systree
+/SCRIPTS [dir] Where to install commandline scripts installed by
+ rocks. Default is {TREE}\bin.
+/LUAMOD [dir] Where to install Lua modules installed by rocks.
+ Default is {TREE}\share\lua\{LV}.
+/CMOD [dir] Where to install c modules installed by rocks.
+ Default is {TREE}\lib\lua\{LV}.
+/CONFIG [dir] Location where the config file should be installed.
+ Default is to follow /P option
+/SELFCONTAINED Creates a self contained installation in a single
+ directory given by /P.
+ Sets the /TREE and /CONFIG options to the same
+ location as /P. And does not load registry info
+ with option /NOREG. The only option NOT self
+ contained is the user rock tree, so don't use that
+ if you create a self contained installation.
+
+Configuring the Lua interpreter:
+/LV [version] Lua version to use; either 5.1, 5.2, or 5.3.
Default is 5.1
-/L Install LuaRocks' own copy of Lua even if detected,
- this will always be a 5.1 installation.
- (/LUA, /INC, /LIB, /BIN cannot be used with /L)
/LUA [dir] Location where Lua is installed - e.g. c:\lua\5.1\
+ If not provided, the installer will search the system
+ path and some default locations for a valid Lua
+ installation.
This is the base directory, the installer will look
for subdirectories bin, lib, include. Alternatively
these can be specified explicitly using the /INC,
/LIB, and /BIN options.
/INC [dir] Location of Lua includes - e.g. c:\lua\5.1\include
If provided overrides sub directory found using /LUA.
-/LIB [dir] Location of Lua libraries -e.g. c:\lua\5.1\lib
+/LIB [dir] Location of Lua libraries (.dll/.lib) - e.g. c:\lua\5.1\lib
If provided overrides sub directory found using /LUA.
/BIN [dir] Location of Lua executables - e.g. c:\lua\5.1\bin
If provided overrides sub directory found using /LUA.
+/L Install LuaRocks' own copy of Lua even if detected,
+ this will always be a 5.1 installation.
+ (/LUA, /INC, /LIB, /BIN cannot be used with /L)
+Compiler configuration:
/MW Use mingw as build system instead of MSVC
+Other options:
/FORCECONFIG Use a single config location. Do not use the
LUAROCKS_CONFIG variable or the user's home directory.
Useful to avoid conflicts when LuaRocks
is embedded within an application.
-
/F Remove installation directory if it already exists.
-
-/R Load registry information to register '.rockspec'
+/NOREG Do not load registry info to register '.rockspec'
extension with LuaRocks commands (right-click).
+/NOADMIN The installer requires admin priviledges. If not
+ available it will elevate a new process. Use this
+ switch to prevent elevation, but make sure the
+ destination paths are all accessible for the current
+ user.
+/Q Do not prompt for confirmation of settings
]])
end
@@ -114,15 +196,16 @@ local function parse_options(args) os.exit(0)
elseif name == "/P" then
vars.PREFIX = option.value
- vars.SYSCONFDIR = option.value
- vars.ROCKS_TREE = option.value
- P_SET = true
elseif name == "/CONFIG" then
vars.SYSCONFDIR = option.value
elseif name == "/TREE" then
- vars.ROCKS_TREE = option.value
+ vars.TREE_ROOT = option.value
elseif name == "/SCRIPTS" then
- vars.SCRIPTS_DIR = option.value
+ vars.TREE_BIN = option.value
+ elseif name == "/LUAMOD" then
+ vars.TREE_LMODULE = option.value
+ elseif name == "/CMOD" then
+ vars.TREE_CMODULE = option.value
elseif name == "/LV" then
vars.LUA_VERSION = option.value
elseif name == "/L" then
@@ -141,8 +224,14 @@ local function parse_options(args) FORCE_CONFIG = true
elseif name == "/F" then
FORCE = true
- elseif name == "/R" then
- REGISTRY = true
+ elseif name == "/SELFCONTAINED" then
+ SELFCONTAINED = true
+ elseif name == "/NOREG" then
+ REGISTRY = false
+ elseif name == "/NOADMIN" then
+ NOADMIN = true
+ elseif name == "/Q" then
+ PROMPT = false
else
die("Unrecognized option: " .. name)
end
@@ -151,8 +240,13 @@ end -- check for combination/required flags
local function check_flags()
- if not P_SET then
- die("Missing required parameter /P")
+ if SELFCONTAINED then
+ if not vars.PREFIX then
+ die("Option /P is required when using /SELFCONTAINED")
+ end
+ if vars.SYSCONFDIR or vars.TREE_ROOT or vars.TREE_BIN or vars.TREE_LMODULE or vars.TREE_CMODULE then
+ die("Cannot combine /TREE, /SCRIPTS, /LUAMOD, /CMOD, or /CONFIG with /SELFCONTAINED")
+ end
end
if INSTALL_LUA then
if vars.LUA_INCDIR or vars.LUA_BINDIR or vars.LUA_LIBDIR or vars.LUA_PREFIX then
@@ -165,8 +259,10 @@ local function check_flags() if vars.LUA_VERSION ~= "5.1" then
if vars.LUA_VERSION == "5.2" then
vars.LUA_LIB_NAMES = vars.LUA_LIB_NAMES:gsub("5([%.]?)1", "5%12")
+ elseif vars.LUA_VERSION == "5.3" then
+ vars.LUA_LIB_NAMES = vars.LUA_LIB_NAMES:gsub("5([%.]?)1", "5%13")
else
- die("Bad argument: /LV must either be 5.1 or 5.2")
+ die("Bad argument: /LV must either be 5.1, 5.2, or 5.3")
end
end
end
@@ -176,10 +272,15 @@ end -- ***********************************************************
local function look_for_interpreter (directory)
if vars.LUA_BINDIR then
+ -- if LUA_BINDIR is specified, it must be there, otherwise we fail
if exists( S"$LUA_BINDIR\\lua$LUA_VERSION.exe" ) then
vars.LUA_INTERPRETER = S"lua$LUA_VERSION.exe"
print(S" Found $LUA_BINDIR\\$LUA_INTERPRETER")
return true
+ elseif exists( S"$LUA_BINDIR\\lua$LUA_SHORTV.exe" ) then
+ vars.LUA_INTERPRETER = S"lua$LUA_SHORTV.exe"
+ print(S" Found $LUA_BINDIR\\$LUA_INTERPRETER")
+ return true
elseif exists(S"$LUA_BINDIR\\lua.exe") then
vars.LUA_INTERPRETER = "lua.exe"
print(S" Found $LUA_BINDIR\\$LUA_INTERPRETER")
@@ -189,7 +290,7 @@ local function look_for_interpreter (directory) print(S" Found $LUA_BINDIR\\$LUA_INTERPRETER")
return true
end
- die(S"Lua executable lua.exe, luajit.exe or lua$LUA_VERSION.exe not found in $LUA_BINDIR")
+ die(S"Lua executable lua.exe, luajit.exe, lua$LUA_SHORTV.exe or lua$LUA_VERSION.exe not found in $LUA_BINDIR")
end
for _, e in ipairs{ [[\]], [[\bin\]] } do
@@ -199,6 +300,12 @@ local function look_for_interpreter (directory) print(" Found ."..e..vars.LUA_INTERPRETER)
return true
+ elseif exists(directory..e.."\\lua"..vars.LUA_SHORTV..".exe") then
+ vars.LUA_INTERPRETER = S"lua$LUA_SHORTV.exe"
+ vars.LUA_BINDIR = directory .. e
+ print(" Found ."..e..vars.LUA_INTERPRETER)
+ return true
+
elseif exists(directory..e.."\\lua.exe") then
vars.LUA_INTERPRETER = "lua.exe"
vars.LUA_BINDIR = directory..e
@@ -264,76 +371,62 @@ local function look_for_headers (directory) return false
end
--- Checks a binary file for the runtime dll used by it. If nu runtime is found, it returns an
--- array of dll's is depends upon.
--- result: string = runtime used, table = list of dll's depended upon, nil = nothing found.
-local function get_file_runtime(p,f) -- path, filename
- local infile = p.."\\"..f
- local outfile = "output.txt"
- local content
- -- analyze binary
- if exec([[.\bin\objdump -x "]]..infile..[[" > ]]..outfile..[[ 2<&1]]) then
- -- read temp file
- local fh = io.open(outfile)
- content = fh:read("*a")
- fh:close()
- end
- -- delete temp file
- os.remove(outfile)
- if not content then
- print(" Failed to analyze "..infile.." for the runtime used")
- return nil
- end
-
- -- lookup
- content = content:upper()
- local result = content:match('DLL NAME%: (MSVCR%d*)%.DLL')
- if not result then
- result = content:match('DLL NAME%: (MSVCRT)%.DLL')
- end
-
- if result then
- print(" "..f.." uses "..tostring(result)..".DLL as runtime")
- else
- print(" No runtime found for "..f)
- -- so; create a list of dll's this file is depending upon, next level of the tree
- result = {}
- for name in content:gmatch("DLL NAME%: (.-%.DLL)") do
- --print("found dll:", name)
- table.insert(result, name)
- end
- end
- return result
-end
local function get_runtime()
- -- first check interpreter
- vars.LUA_RUNTIME = get_file_runtime(vars.LUA_BINDIR, vars.LUA_INTERPRETER)
- if type(vars.LUA_RUNTIME) == "table" then
- -- a table with dll's depended upon was returned, check this list
- -- note: we only check 1 level deep
- for _,dll in ipairs(vars.LUA_RUNTIME) do
- local t = get_file_runtime(vars.LUA_BINDIR, dll)
- if type(t) == "string" then
- -- found it
- vars.LUA_RUNTIME = t
- break
- end
- end
- end
+ local f
+ vars.LUA_RUNTIME, f = pe.msvcrt(vars.LUA_BINDIR.."\\"..vars.LUA_INTERPRETER)
if type(vars.LUA_RUNTIME) ~= "string" then
-- analysis failed, issue a warning
vars.LUA_RUNTIME = "MSVCR80"
print("*** WARNING ***: could not analyse the runtime used, defaulting to "..vars.LUA_RUNTIME)
+ else
+ print(" "..f.." uses "..vars.LUA_RUNTIME..".DLL as runtime")
end
return true
end
+local function get_architecture()
+ -- detect processor arch interpreter was compiled for
+ local proc = (pe.parse(vars.LUA_BINDIR.."\\"..vars.LUA_INTERPRETER) or {}).Machine
+ if not proc then
+ die("Could not detect processor architecture used in "..vars.LUA_INTERPRETER)
+ end
+ proc = pe.const.Machine[proc] -- collect name from constant value
+ if proc == "IMAGE_FILE_MACHINE_I386" then
+ proc = "x86"
+ else
+ proc = "x86_64"
+ end
+ return proc
+end
+
local function look_for_lua_install ()
print("Looking for Lua interpreter")
- local directories = { [[c:\lua5.1.2]], [[c:\lua]], [[c:\kepler\1.1]] }
+ local directories
if vars.LUA_PREFIX then
- table.insert(directories, 1, vars.LUA_PREFIX)
+ directories = { vars.LUA_PREFIX }
+ else
+ -- no prefix given, so use path
+ directories = (os.getenv("PATH",";") or "")
+ local i = 1
+ while i ~= 0 do directories, i = directories:gsub(";;",";") end --remove all doubles
+ directories = split_string(directories,";")
+ -- if a path element ends with "\bin\" then remove it, as the searcher will check there anyway
+ for i, val in ipairs(directories) do
+ -- remove trailing backslash
+ while val:sub(-1,-1) == "\\" and val:sub(-2,-1) ~= ":\\" do
+ val = val:sub(1,-2)
+ end
+ -- remove trailing 'bin'
+ if val:upper():sub(-4,-1) == "\\BIN" or val:upper():sub(-4,-1) == ":BIN" then
+ val = val:sub(1,-5)
+ end
+ directories[i] = val
+ end
+ -- finaly add some other default paths
+ table.insert(directories, [[c:\lua5.1.2]])
+ table.insert(directories, [[c:\lua]])
+ table.insert(directories, [[c:\kepler\1.1]])
end
if vars.LUA_BINDIR and vars.LUA_LIBDIR and vars.LUA_INCDIR then
if look_for_interpreter(vars.LUA_BINDIR) and
@@ -342,7 +435,7 @@ local function look_for_lua_install () then
if get_runtime() then
print("Runtime check completed, now testing interpreter...")
- if exec(S[[$LUA_BINDIR\$LUA_INTERPRETER -v 2>NUL]]) then
+ if exec(S[["$LUA_BINDIR\$LUA_INTERPRETER" -v 2>NUL]]) then
print(" Ok")
return true
end
@@ -363,7 +456,7 @@ local function look_for_lua_install () print("Headers found, checking runtime to use...")
if get_runtime() then
print("Runtime check completed, now testing interpreter...")
- if exec(S[[$LUA_BINDIR\$LUA_INTERPRETER -v 2>NUL]]) then
+ if exec(S[["$LUA_BINDIR\$LUA_INTERPRETER" -v 2>NUL]]) then
print(" Ok")
return true
end
@@ -377,7 +470,11 @@ local function look_for_lua_install () return false
end
----
+
+-- ***********************************************************
+-- Installer script start
+-- ***********************************************************
+
-- Poor man's command-line parsing
local config = {}
local with_arg = { -- options followed by an argument, others are flags
@@ -385,16 +482,46 @@ local with_arg = { -- options followed by an argument, others are flags ["/CONFIG"] = true,
["/TREE"] = true,
["/SCRIPTS"] = true,
+ ["/LUAMOD"] = true,
+ ["/CMOD"] = true,
["/LV"] = true,
["/LUA"] = true,
["/INC"] = true,
["/BIN"] = true,
["/LIB"] = true,
}
+-- reconstruct argument values with spaces and double quotes
+-- this will be damaged by the batch construction at the start of this file
+local oarg = arg -- retain old table
+if #arg > 0 then
+ farg = table.concat(arg, " ") .. " "
+ arg = {}
+ farg = farg:gsub('%"', "")
+ local i = 0
+ while #farg>0 do
+ i = i + 1
+ if (farg:sub(1,1) ~= "/") and ((arg[i-1] or ""):sub(1,1) ~= "/") then
+ i = i - 1 -- continued previous arg
+ if i == 0 then i = 1 end
+ end
+ if arg[i] then
+ arg[i] = arg[i] .. " "
+ else
+ arg[i] = ""
+ end
+ local v,r = farg:match("^(.-)%s(.*)$")
+ arg[i], farg = arg[i]..v, r
+ while farg:sub(1,1) == " " do farg = farg:sub(2,-1) end -- remove prefix spaces
+ end
+end
+for k,v in pairs(oarg) do if k < 1 then arg[k] = v end end -- copy 0 and negative indexes
+oarg = nil
+
+-- build config option table with name and value elements
local i = 1
while i <= #arg do
local opt = arg[i]
- if with_arg[opt] then
+ if with_arg[opt:upper()] then
local value = arg[i + 1]
if not value then
die("Missing value for option "..opt)
@@ -410,8 +537,46 @@ end print(S"LuaRocks $VERSION.x installer.\n")
parse_options(config)
+
+print([[
+
+========================
+== Checking system... ==
+========================
+
+]])
+
check_flags()
+if not permission() then
+ if not NOADMIN then
+ -- must elevate the process with admin priviledges
+ if not exec("PowerShell /? >NUL 2>&1") then
+ -- powershell is not available, so error out
+ die("No administrative priviledges detected and cannot auto-elevate. Please run with admin priviledges or use the /NOADMIN switch")
+ end
+ print("Need admin priviledges, now elevating a new process to continue installing...")
+ local runner = os.getenv("TEMP").."\\".."LuaRocks_Installer.bat"
+ local f = io.open(runner, "w")
+ f:write("@echo off\n")
+ f:write("CHDIR /D "..arg[0]:match("(.+)%\\.-$").."\n") -- return to current dir, elevation changes current path
+ f:write('"'..arg[-1]..'" "'..table.concat(arg, '" "', 0)..'"\n')
+ f:write("ECHO Press any key to close this window...\n")
+ f:write("PAUSE > NUL\n")
+ f:write('DEL "'..runner..'"') -- temp batch file deletes itself
+ f:close()
+ -- run the created temp batch file in elevated mode
+ exec("PowerShell -Command (New-Object -com 'Shell.Application').ShellExecute('"..runner.."', '', '', 'runas')\n")
+ print("Now exiting unpriviledged installer")
+ os.exit() -- exit here, the newly created elevated process will do the installing
+ else
+ print("Attempting to install without admin priviledges...")
+ end
+else
+ print("Admin priviledges available for installing")
+end
+
+vars.PREFIX = vars.PREFIX or os.getenv("PROGRAMFILES")..[[\LuaRocks]]
vars.FULL_PREFIX = S"$PREFIX\\$VERSION"
vars.BINDIR = vars.FULL_PREFIX
vars.LIBDIR = vars.FULL_PREFIX
@@ -419,33 +584,72 @@ vars.LUADIR = S"$FULL_PREFIX\\lua" vars.INCDIR = S"$FULL_PREFIX\\include"
vars.LUA_SHORTV = vars.LUA_VERSION:gsub("%.", "")
-if not look_for_lua_install() then
- print("Could not find Lua. Will install its own copy.")
- print("See /? for options for specifying the location of Lua.")
+if INSTALL_LUA then
if vars.LUA_VERSION ~= "5.1" then
- die("Cannot install own copy because no 5.2 version is bundled")
+ die("Cannot install own copy of Lua because only 5.1 is bundled")
end
- INSTALL_LUA = true
vars.LUA_INTERPRETER = "lua5.1"
vars.LUA_BINDIR = vars.BINDIR
vars.LUA_LIBDIR = vars.LIBDIR
vars.LUA_INCDIR = vars.INCDIR
vars.LUA_LIBNAME = "lua5.1.lib"
- vars.LUA_RUNTIME = "MSVCR80"
+ vars.LUA_RUNTIME = "MSVCR80"
+ vars.UNAME_M = "x86"
else
- print(S[[
+ if not look_for_lua_install() then
+ die("Could not find Lua. See /? for options for specifying the location of Lua, or installing a bundled copy of Lua 5.1.")
+ end
+ vars.UNAME_M = get_architecture() -- can only do when installation was found
+end
+
+local datapath
+if vars.UNAME_M == "x86" then
+ datapath = os.getenv("PROGRAMFILES") .. [[\LuaRocks]]
+else
+ -- our target interpreter is 64bit, so the tree (with binaries) should go into 64bit program files
+ datapath = os.getenv("ProgramW6432") .. [[\LuaRocks]]
+end
+vars.SYSCONFDIR = vars.SYSCONFDIR or vars.PREFIX
+vars.TREE_ROOT = vars.TREE_ROOT or datapath..[[\systree]]
+if SELFCONTAINED then
+ vars.SYSCONFDIR = vars.PREFIX
+ vars.TREE_ROOT = vars.PREFIX..[[\systree]]
+ REGISTRY = false
+end
+
+print(S[[
+
+==========================
+== System check results ==
+==========================
Will configure LuaRocks with the following paths:
-LuaRocks : $FULL_PREFIX
-Lua interpreter: $LUA_BINDIR\$LUA_INTERPRETER
-Lua binaries : $LUA_BINDIR
-Lua libraries : $LUA_LIBDIR
-Lua includes : $LUA_INCDIR
-Binaries will be linked against: $LUA_LIBNAME with runtime $LUA_RUNTIME
+LuaRocks : $FULL_PREFIX
+Config file : $SYSCONFDIR\config.lua
+Rocktree : $TREE_ROOT
+
+Lua interpreter : $LUA_BINDIR\$LUA_INTERPRETER
+ binaries : $LUA_BINDIR
+ libraries : $LUA_LIBDIR
+ includes : $LUA_INCDIR
+ architecture: $UNAME_M
+ binary link : $LUA_LIBNAME with runtime $LUA_RUNTIME.dll
]])
+
+if PROMPT then
+ print("Press <ENTER> to start installing, or press <CTRL>+<C> to abort. Use install /? for installation options.")
+ io.read()
end
+print([[
+
+============================
+== Installing LuaRocks... ==
+============================
+
+]])
+
-- ***********************************************************
-- Install LuaRocks files
-- ***********************************************************
@@ -474,13 +678,22 @@ if INSTALL_LUA then if not exists(vars.LUA_INCDIR) then
mkdir(vars.LUA_INCDIR)
end
- exec(S[[COPY lua5.1\bin\*.* "$LUA_BINDIR" >NUL]])
- exec(S[[COPY lua5.1\include\*.* "$LUA_INCDIR" >NUL]])
+ exec(S[[COPY win32\lua5.1\bin\*.* "$LUA_BINDIR" >NUL]])
+ exec(S[[COPY win32\lua5.1\include\*.* "$LUA_INCDIR" >NUL]])
print(S"Installed the LuaRocks bundled Lua interpreter in $LUA_BINDIR")
end
-- Copy the LuaRocks binaries
-if not exec(S[[COPY bin\*.* "$BINDIR" >NUL]]) then
+if not exists(S[[$BINDIR\tools]]) then
+ if not mkdir(S[[$BINDIR\tools]]) then
+ die()
+ end
+end
+if not exec(S[[COPY win32\tools\*.* "$BINDIR\tools" >NUL]]) then
+ die()
+end
+-- Copy LR bin helper files
+if not exec(S[[COPY win32\*.* "$BINDIR" >NUL]]) then
die()
end
-- Copy the LuaRocks lua source files
@@ -507,61 +720,104 @@ for _, c in ipairs{"luarocks", "luarocks-admin"} do f:write(S[[
@ECHO OFF
SETLOCAL
-SET LUA_PATH=$LUADIR\?.lua;$LUADIR\?\init.lua;%LUA_PATH%
-SET PATH=$BINDIR\;%PATH%
-"$LUA_INTERPRETER" "$BINDIR\]]..c..[[.lua" %*
+SET "LUA_PATH=$LUADIR\?.lua;$LUADIR\?\init.lua;%LUA_PATH%"
+IF NOT "%LUA_PATH_5_2%"=="" (
+ SET "LUA_PATH_5_2=$LUADIR\?.lua;$LUADIR\?\init.lua;%LUA_PATH_5_2%"
+)
+IF NOT "%LUA_PATH_5_3%"=="" (
+ SET "LUA_PATH_5_3=$LUADIR\?.lua;$LUADIR\?\init.lua;%LUA_PATH_5_3%"
+)
+SET "PATH=$BINDIR;%PATH%"
+"$LUA_BINDIR\$LUA_INTERPRETER" "$BINDIR\]]..c..[[.lua" %*
+IF NOT "%ERRORLEVEL%"=="2" GOTO EXITLR
+
+REM Permission denied error, try and auto elevate...
+REM already an admin? (checking to prevent loops)
+NET SESSION >NUL 2>&1
+IF "%ERRORLEVEL%"=="0" GOTO EXITLR
+
+REM Do we have PowerShell available?
+PowerShell /? >NUL 2>&1
+IF NOT "%ERRORLEVEL%"=="0" GOTO EXITLR
+
+:GETTEMPNAME
+SET TMPFILE=%TEMP%\LuaRocks-Elevator-%RANDOM%.bat
+IF EXIST "%TMPFILE%" GOTO :GETTEMPNAME
+
+ECHO @ECHO OFF > "%TMPFILE%"
+ECHO CHDIR /D %CD% >> "%TMPFILE%"
+ECHO ECHO %0 %* >> "%TMPFILE%"
+ECHO ECHO. >> "%TMPFILE%"
+ECHO CALL %0 %* >> "%TMPFILE%"
+ECHO ECHO. >> "%TMPFILE%"
+ECHO ECHO Press any key to close this window... >> "%TMPFILE%"
+ECHO PAUSE ^> NUL >> "%TMPFILE%"
+ECHO DEL "%TMPFILE%" >> "%TMPFILE%"
+
+ECHO Now retrying as a priviledged user...
+PowerShell -Command (New-Object -com 'Shell.Application').ShellExecute('%TMPFILE%', '', '', 'runas')
+
+:EXITLR
ENDLOCAL
]])
f:close()
print(S"Created LuaRocks command: $BINDIR\\"..c..".bat")
end
+
+-- part below was commented out as its purpose was unclear
+-- see https://github.com/keplerproject/luarocks/pull/197#issuecomment-30176548
+
-- configure 'scripts' directory
-if vars.SCRIPTS_DIR then
- mkdir(vars.SCRIPTS_DIR)
- if not USE_MINGW then
- -- definitly not for MinGW because of conflicting runtimes
- -- but is it ok to do it for others???
- exec(S[[COPY lua5.1\bin\*.dll "$SCRIPTS_DIR" >NUL]])
- end
-else
- if not USE_MINGW then
- mkdir(S[[$ROCKS_TREE\bin]])
- -- definitly not for MinGW because of conflicting runtimes
- -- but is it ok to do it for others???
- exec(S[[COPY lua5.1\bin\*.dll "$ROCKS_TREE"\bin >NUL]])
- end
-end
+-- if vars.TREE_BIN then
+-- mkdir(vars.TREE_BIN)
+-- if not USE_MINGW then
+-- -- definitly not for MinGW because of conflicting runtimes
+-- -- but is it ok to do it for others???
+-- exec(S[[COPY lua5.1\bin\*.dll "$TREE_BIN" >NUL]])
+-- end
+-- else
+-- if not USE_MINGW then
+-- mkdir(S[[$TREE_ROOT\bin]])
+-- -- definitly not for MinGW because of conflicting runtimes
+-- -- but is it ok to do it for others???
+-- exec(S[[COPY lua5.1\bin\*.dll "$TREE_ROOT"\bin >NUL]])
+-- end
+-- end
+-- ***********************************************************
+-- Configure LuaRocks
+-- ***********************************************************
print()
print("Configuring LuaRocks...")
+
-- Create a site-config file
if exists(S[[$LUADIR\luarocks\site_config.lua]]) then
exec(S[[RENAME "$LUADIR\luarocks\site_config.lua" site_config.lua.bak]])
end
local f = io.open(vars.LUADIR.."\\luarocks\\site_config.lua", "w")
f:write(S[=[
-module("luarocks.site_config")
-LUA_INCDIR=[[$LUA_INCDIR]]
-LUA_LIBDIR=[[$LUA_LIBDIR]]
-LUA_BINDIR=[[$LUA_BINDIR]]
-LUA_INTERPRETER=[[$LUA_INTERPRETER]]
+local site_config = {}
+site_config.LUA_INCDIR=[[$LUA_INCDIR]]
+site_config.LUA_LIBDIR=[[$LUA_LIBDIR]]
+site_config.LUA_BINDIR=[[$LUA_BINDIR]]
+site_config.LUA_INTERPRETER=[[$LUA_INTERPRETER]]
]=])
if USE_MINGW then
- f:write("LUAROCKS_UNAME_S=[[MINGW]]\n")
+ f:write("site_config.LUAROCKS_UNAME_S=[[MINGW]]\n")
else
- f:write("LUAROCKS_UNAME_S=[[WindowsNT]]\n")
+ f:write("site_config.LUAROCKS_UNAME_S=[[WindowsNT]]\n")
end
f:write(S[=[
-LUAROCKS_UNAME_M=[[x86]]
-LUAROCKS_SYSCONFIG=[[$SYSCONFDIR\config.lua]]
-LUAROCKS_ROCKS_TREE=[[$ROCKS_TREE]]
-LUAROCKS_PREFIX=[[$PREFIX]]
-LUAROCKS_DOWNLOADER=[[wget]]
-LUAROCKS_MD5CHECKER=[[md5sum]]
+site_config.LUAROCKS_UNAME_M=[[$UNAME_M]]
+site_config.LUAROCKS_SYSCONFIG=[[$SYSCONFDIR\config.lua]]
+site_config.LUAROCKS_ROCKS_TREE=[[$TREE_ROOT]]
+site_config.LUAROCKS_PREFIX=[[$PREFIX]]
+site_config.LUAROCKS_DOWNLOADER=[[wget]]
+site_config.LUAROCKS_MD5CHECKER=[[md5sum]]
]=])
if FORCE_CONFIG then
- f:write("local LUAROCKS_FORCE_CONFIG=true\n")
+ f:write("site_config.LUAROCKS_FORCE_CONFIG=true\n")
end
if exists(vars.LUADIR.."\\luarocks\\site_config.lua.bak") then
for line in io.lines(vars.LUADIR.."\\luarocks\\site_config.lua.bak", "r") do
@@ -570,6 +826,7 @@ if exists(vars.LUADIR.."\\luarocks\\site_config.lua.bak") then end
exec(S[[DEL /F /Q "$LUADIR\luarocks\site_config.lua.bak"]])
end
+f:write("return site_config\n")
f:close()
print(S[[Created LuaRocks site-config file: $LUADIR\luarocks\site_config.lua]])
@@ -578,51 +835,69 @@ vars.CONFIG_FILE = vars.SYSCONFDIR.."\\config.lua" if not exists(vars.SYSCONFDIR) then
mkdir(vars.SYSCONFDIR)
end
-if not exists(vars.CONFIG_FILE) then
- local f = io.open(vars.CONFIG_FILE, "w")
- f:write([=[
+if exists(vars.CONFIG_FILE) then
+ local nname = backup(vars.CONFIG_FILE, "config.bak")
+ print("***************")
+ print(S"*** WARNING *** LuaRocks config file already exists: '$CONFIG_FILE'. The old file has been renamed to '"..nname.."'")
+ print("***************")
+end
+local f = io.open(vars.CONFIG_FILE, "w")
+f:write([=[
rocks_servers = {
[[https://raw.githubusercontent.com/torch/rocks/master]],
[[https://raw.githubusercontent.com/torch/luarocks-mirror/master/rocks]]
}
rocks_trees = {
]=])
- if FORCE_CONFIG then
- f:write(" home..[[/luarocks]],\n")
- end
- f:write(S" [[$ROCKS_TREE]]\n")
- f:write("}\n")
- if vars.SCRIPTS_DIR then
- f:write(S"scripts_dir=[[$SCRIPTS_DIR]]\n")
- end
- f:write("variables = {\n")
- if USE_MINGW and vars.LUA_RUNTIME == "MSVCRT" then
- f:write(" MSVCRT = 'm', -- make MinGW use MSVCRT.DLL as runtime\n")
- else
- f:write(" MSVCRT = '"..vars.LUA_RUNTIME.."',\n")
- end
- f:write(S" LUALIB = '$LUA_LIBNAME'\n")
- f:write("}\n")
- f:close()
- print(S"Created LuaRocks config file: $CONFIG_FILE")
+if FORCE_CONFIG then
+ f:write(" home..[[/luarocks]],\n")
+end
+f:write(S" { name = [[user]],\n")
+f:write(S" root = home..[[/luarocks]],\n")
+f:write(S" },\n")
+f:write(S" { name = [[system]],\n")
+f:write(S" root = [[$TREE_ROOT]],\n")
+if vars.TREE_BIN then
+ f:write(S" bin_dir = [[$TREE_BIN]],\n")
+end
+if vars.TREE_CMODULE then
+ f:write(S" lib_dir = [[$TREE_CMODULE]],\n")
+end
+if vars.TREE_LMODULE then
+ f:write(S" lua_dir = [[$TREE_LMODULE]],\n")
+end
+f:write(S" },\n")
+f:write("}\n")
+f:write("variables = {\n")
+if USE_MINGW and vars.LUA_RUNTIME == "MSVCRT" then
+ f:write(" MSVCRT = 'm', -- make MinGW use MSVCRT.DLL as runtime\n")
else
- print(S"LuaRocks config file already exists: $CONFIG_FILE")
+ f:write(" MSVCRT = '"..vars.LUA_RUNTIME.."',\n")
end
+f:write(S" LUALIB = '$LUA_LIBNAME'\n")
+f:write("}\n")
+f:write("verbose = false -- set to 'true' to enable verbose output\n")
+f:close()
+
+print(S"Created LuaRocks config file: $CONFIG_FILE")
+
print()
print("Creating rocktrees...")
-if not exists(vars.ROCKS_TREE) then
- mkdir(vars.ROCKS_TREE)
- print(S[[Created rocktree: "$ROCKS_TREE"]])
+if not exists(vars.TREE_ROOT) then
+ mkdir(vars.TREE_ROOT)
+ print(S[[Created system rocktree : "$TREE_ROOT"]])
else
- print(S[[Rocktree exists: "$ROCKS_TREE"]])
+ print(S[[System rocktree exists : "$TREE_ROOT"]])
end
-local APPDATA = os.getenv("APPDATA")
-if not exists(APPDATA.."\\luarocks") then
- mkdir(APPDATA.."\\luarocks")
- print([[Created rocktree: "]]..APPDATA..[[\luarocks"]])
+
+vars.APPDATA = os.getenv("APPDATA")
+vars.LOCAL_TREE = vars.APPDATA..[[\LuaRocks]]
+if not exists(vars.LOCAL_TREE) then
+ mkdir(vars.LOCAL_TREE)
+ print(S[[Created local user rocktree: "$LOCAL_TREE"]])
else
- print([[Rocktree exists: "]]..APPDATA..[[\luarocks"]])
+ print(S[[Local user rocktree exists : "$LOCAL_TREE"]])
end
-- Load registry information
@@ -630,21 +905,49 @@ if REGISTRY then -- expand template with correct path information
print()
print([[Loading registry information for ".rockspec" files]])
- exec( S[[lua5.1\bin\lua5.1.exe "$FULL_PREFIX\create_reg_file.lua" "$FULL_PREFIX\LuaRocks.reg.template"]] )
- exec( S"$FULL_PREFIX\\LuaRocks.reg" )
+ exec( S[[win32\lua5.1\bin\lua5.1.exe "$FULL_PREFIX\LuaRocks.reg.lua" "$FULL_PREFIX\LuaRocks.reg.template"]] )
+ exec( S[[regedit /S "$FULL_PREFIX\\LuaRocks.reg"]] )
end
-- ***********************************************************
--- Exit handlers
+-- Cleanup
-- ***********************************************************
+-- remove regsitry related files, no longer needed
+exec( S[[del "$FULL_PREFIX\LuaRocks.reg.*" >NUL]] )
+-- remove pe-parser module
+exec( S[[del "$FULL_PREFIX\pe-parser.lua" >NUL]] )
+-- ***********************************************************
+-- Exit handlers
+-- ***********************************************************
+vars.TREE_BIN = vars.TREE_BIN or vars.TREE_ROOT..[[\bin]]
+vars.TREE_LMODULE = vars.TREE_LMODULE or vars.TREE_ROOT..[[\share\lua\]]..vars.LUA_VERSION
+vars.TREE_CMODULE = vars.TREE_CMODULE or vars.TREE_ROOT..[[\lib\lua\]]..vars.LUA_VERSION
print(S[[
-*** LuaRocks is installed! ***
- You may want to add the following elements to your paths;
-PATH : $LUA_BINDIR;$FULL_PREFIX
-LUA_PATH : $ROCKS_TREE\share\lua\$LUA_VERSION\?.lua;$ROCKS_TREE\share\lua\$LUA_VERSION\?\init.lua
-LUA_CPATH: $LUA_LIBDIR\lua\$LUA_VERSION\?.dll
+============================
+== LuaRocks is installed! ==
+============================
+
+
+You may want to add the following elements to your paths;
+Lua interpreter;
+ PATH : $LUA_BINDIR
+ PATHEXT : .LUA
+LuaRocks;
+ PATH : $FULL_PREFIX
+ LUA_PATH : $FULL_PREFIX\lua\?.lua;$FULL_PREFIX\lua\?\init.lua
+Local user rocktree (Note: %APPDATA% is user dependent);
+ PATH : %APPDATA%\LuaRocks\bin
+ LUA_PATH : %APPDATA%\LuaRocks\share\lua\$LUA_VERSION\?.lua;%APPDATA%\LuaRocks\share\lua\$LUA_VERSION\?\init.lua
+ LUA_CPATH: %APPDATA%\LuaRocks\lib\lua\$LUA_VERSION\?.dll
+System rocktree
+ PATH : $TREE_BIN
+ LUA_PATH : $TREE_LMODULE\?.lua;$TREE_LMODULE\?\init.lua
+ LUA_CPATH: $TREE_CMODULE\?.dll
+
+Note that the %APPDATA% element in the paths above is user specific and it MUST be replaced by its actual value.
+For the current user that value is: $APPDATA.
]])
os.exit(0)
diff --git a/luarocks/makedist b/luarocks/makedist index 9b3308e..141f670 100755 --- a/luarocks/makedist +++ b/luarocks/makedist @@ -61,15 +61,14 @@ mkdir "release-windows" mv "$out" "release-windows/$out-win32" cd "release-unix/$out" -rm -rf makedist install.bat COPYING.lua COPYING.7z win32 lfw +rm -rf makedist install.bat COPYING_lua COPYING_7z COPYING_win win32 lfw .travis.yml .gitignore cd .. tar czvpf ../"$out.tar.gz" "$out" cd .. rm -rf "release-unix" cd "release-windows/$out-win32" -cp -va win32/* . -rm -rf makedist Makefile configure lfw win32 +rm -rf makedist Makefile configure lfw .travis.yml .gitignore cd .. zip -r ../"$out-win32.zip" "$out-win32" cd .. diff --git a/luarocks/rockspec b/luarocks/rockspec index 69f253b..16ac635 100644 --- a/luarocks/rockspec +++ b/luarocks/rockspec @@ -1,5 +1,5 @@ package = "LuaRocks" -local VER = "2.1.0" +local VER = "2.2.0beta1" local REV = "1" version = VER.."-"..REV diff --git a/luarocks/src/bin/luarocks b/luarocks/src/bin/luarocks index 05f5df6..426542c 100755 --- a/luarocks/src/bin/luarocks +++ b/luarocks/src/bin/luarocks @@ -1,25 +1,33 @@ #!/Users/ronan/usr72/bin/luajit package.path = [[/Users/ronan/usr72/share/lua/5.1//?.lua;/Users/ronan/usr72/share/lua/5.1//?/init.lua;]]..package.path +-- this should be loaded first. +local cfg = require("luarocks.cfg") + +local loader = require("luarocks.loader") local command_line = require("luarocks.command_line") program_description = "LuaRocks main command-line interface" -commands = {} -commands.help = require("luarocks.help") -commands.pack = require("luarocks.pack") -commands.unpack = require("luarocks.unpack") -commands.build = require("luarocks.build") -commands.install = require("luarocks.install") -commands.search = require("luarocks.search") -commands.list = require("luarocks.list") -commands.remove = require("luarocks.remove") -commands.make = require("luarocks.make") -commands.download = require("luarocks.download") -commands.path = require("luarocks.path") -commands.show = require("luarocks.show") -commands.new_version = require("luarocks.new_version") -commands.lint = require("luarocks.lint") -commands.purge = require("luarocks.purge") +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", +} command_line.run_command(...) diff --git a/luarocks/src/bin/luarocks-admin b/luarocks/src/bin/luarocks-admin index 419485f..4b44dbc 100755 --- a/luarocks/src/bin/luarocks-admin +++ b/luarocks/src/bin/luarocks-admin @@ -1,17 +1,20 @@ #!/Users/ronan/usr72/bin/luajit package.path = [[/Users/ronan/usr72/share/lua/5.1//?.lua;/Users/ronan/usr72/share/lua/5.1//?/init.lua;]]..package.path +-- this should be loaded first. +local cfg = require("luarocks.cfg") + +local loader = require("luarocks.loader") local command_line = require("luarocks.command_line") 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", } -commands.help = require("luarocks.help") -commands.make_manifest = require("luarocks.make_manifest") -commands.add = require("luarocks.add") -commands.remove = require("luarocks.admin_remove") -commands.refresh_cache = require("luarocks.refresh_cache") - command_line.run_command(...) diff --git a/luarocks/src/luarocks/add.lua b/luarocks/src/luarocks/add.lua index d7c293e..fc913c9 100644 --- a/luarocks/src/luarocks/add.lua +++ b/luarocks/src/luarocks/add.lua @@ -1,20 +1,21 @@ --- Module implementing the luarocks-admin "add" command. -- Adds a rock or rockspec to a rocks server. -module("luarocks.add", package.seeall) +--module("luarocks.add", package.seeall) +local add = {} +package.loaded["luarocks.add"] = add local cfg = require("luarocks.cfg") local util = require("luarocks.util") -local fetch = require("luarocks.fetch") 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") -help_summary = "Add a rock or rockspec to a rocks server." -help_arguments = "[--server=<server>] [--no-refresh] {<rockspec>|<rock>...}" -help = [[ +add.help_summary = "Add a rock or rockspec to a rocks server." +add.help_arguments = "[--server=<server>] [--no-refresh] {<rockspec>|<rock>...}" +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 @@ -45,7 +46,8 @@ local function add_files_to_server(refresh, rockfiles, server, upload_server) login_url = protocol.."://"..server_path end - fs.change_dir(at) + local ok, err = fs.change_dir(at) + if not ok then return nil, err end local files = {} for i, rockfile in ipairs(rockfiles) do @@ -62,10 +64,14 @@ local function add_files_to_server(refresh, rockfiles, server, upload_server) return nil, "No files found" end - fs.change_dir(local_cache) + 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) @@ -78,15 +84,17 @@ local function add_files_to_server(refresh, rockfiles, server, upload_server) table.insert(files, "index.html") table.insert(files, "manifest") - table.insert(files, "manifest-5.1") - table.insert(files, "manifest-5.2") + 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.." --exclude=.git -Oavz -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/" + 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 @@ -100,7 +108,7 @@ local function add_files_to_server(refresh, rockfiles, server, upload_server) return true end -function run(...) +function add.run(...) local files = { util.parse_flags(...) } local flags = table.remove(files, 1) if #files < 1 then @@ -111,3 +119,5 @@ function run(...) return add_files_to_server(not flags["no-refresh"], files, server, server_table) end + +return add diff --git a/luarocks/src/luarocks/admin_remove.lua b/luarocks/src/luarocks/admin_remove.lua index 83b57fc..839121d 100644 --- a/luarocks/src/luarocks/admin_remove.lua +++ b/luarocks/src/luarocks/admin_remove.lua @@ -1,22 +1,23 @@ --- Module implementing the luarocks-admin "remove" command. -- Removes a rock or rockspec from a rocks server. -module("luarocks.admin_remove", package.seeall) +--module("luarocks.admin_remove", package.seeall) +local admin_remove = {} +package.loaded["luarocks.admin_remove"] = admin_remove local cfg = require("luarocks.cfg") local util = require("luarocks.util") -local fetch = require("luarocks.fetch") 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") -help_summary = "Remove a rock or rockspec from a rocks server." -help_arguments = "[--from=<server>] [--no-refresh] {<rockspec>|<rock>...}" -help = [[ +admin_remove.help_summary = "Remove a rock or rockspec from a rocks server." +admin_remove.help_arguments = "[--server=<server>] [--no-refresh] {<rockspec>|<rock>...}" +admin_remove.help = [[ Arguments are local files, which may be rockspecs or rocks. -The flag --from indicates which server to use. +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 @@ -41,14 +42,16 @@ local function remove_files_from_server(refresh, rockfiles, server, upload_serve return nil, "This command requires 'rsync', check your configuration." end - fs.change_dir(at) + local ok, err = fs.change_dir(at) + if not ok then return nil, err end local nr_files = 0 for i, rockfile in ipairs(rockfiles) do local basename = dir.base_name(rockfile) local file = dir.path(local_cache, basename) util.printout("Removing file "..file.."...") - if fs.delete(file) then + fs.delete(file) + if not fs.exists(file) then nr_files = nr_files + 1 else util.printerr("Failed removing "..file) @@ -58,7 +61,8 @@ local function remove_files_from_server(refresh, rockfiles, server, upload_serve return nil, "No files removed." end - fs.change_dir(local_cache) + 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) @@ -66,7 +70,7 @@ local function remove_files_from_server(refresh, rockfiles, server, upload_serve index.make_index(local_cache) local srv, path = server_path:match("([^/]+)(/.+)") - local cmd = "rsync -Oavz --delete -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/" + local cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." --delete -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/" util.printout(cmd) fs.execute(cmd) @@ -74,7 +78,7 @@ local function remove_files_from_server(refresh, rockfiles, server, upload_serve return true end -function run(...) +function admin_remove.run(...) local files = { util.parse_flags(...) } local flags = table.remove(files, 1) if #files < 1 then @@ -85,3 +89,5 @@ function run(...) return remove_files_from_server(not flags["no-refresh"], files, server, server_table) end + +return admin_remove diff --git a/luarocks/src/luarocks/build.lua b/luarocks/src/luarocks/build.lua index dbe450a..01bfa9d 100644 --- a/luarocks/src/luarocks/build.lua +++ b/luarocks/src/luarocks/build.lua @@ -1,7 +1,9 @@ --- Module implementing the LuaRocks "build" command. -- Builds a rock, compiling its C parts if any. -module("luarocks.build", package.seeall) +--module("luarocks.build", package.seeall) +local build = {} +package.loaded["luarocks.build"] = build local pack = require("luarocks.pack") local path = require("luarocks.path") @@ -12,13 +14,12 @@ local fs = require("luarocks.fs") local dir = require("luarocks.dir") local deps = require("luarocks.deps") local manif = require("luarocks.manif") -local search = require("luarocks.search") local remove = require("luarocks.remove") local cfg = require("luarocks.cfg") -help_summary = "Build/compile a rock." -help_arguments = "[--pack-binary-rock] [--keep] {<rockspec>|<rock>|<name> [<version>]}" -help = [[ +build.help_summary = "Build/compile a rock." +build.help_arguments = "[--pack-binary-rock] [--keep] {<rockspec>|<rock>|<name> [<version>]}" +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. @@ -31,7 +32,12 @@ or the name of a rock to be fetched from a repository. rock after building a new one. This behavior can be made permanent by setting keep_other_versions=true in the configuration file. -]] + +--branch=<name> Override the `source.branch` field in the loaded + rockspec. Allows to specify a different branch to + fetch. Particularly for SCM rocks. + +]]..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. @@ -53,19 +59,31 @@ local function install_files(files, location, is_module_path) 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(k)) - fs.make_dir(dest) + 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("([^.]+)$") + local baseinfo = filename:gsub("%.lua$", "") + if basename ~= baseinfo then + filename = basename..".lua" + end + end else - dest = dir.path(location, dir.dir_name(k)) - fs.make_dir(dest) - dest = dir.path(dest, dir.base_name(k)) + 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 - fs.make_dir(dest) + local ok, err = fs.make_dir(dest) + if not ok then return nil, err end end - local ok = fs.copy(dir.path(file), dest) + local ok = fs.copy(dir.path(file), dir.path(dest, filename)) if not ok then return nil, "Failed copying "..file end @@ -91,16 +109,16 @@ end -- @param rockspec table: A rockspec table. -- @return boolean or (nil, string): True if succeeded or -- nil and an error message. -function apply_patches(rockspec) +function build.apply_patches(rockspec) assert(type(rockspec) == "table") - local build = rockspec.build - if build.extra_files then - extract_from_rockspec(build.extra_files) + local build_spec = rockspec.build + if build_spec.extra_files then + extract_from_rockspec(build_spec.extra_files) end - if build.patches then - extract_from_rockspec(build.patches) - for patch, patchdata in util.sortedpairs(build.patches) do + 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 @@ -111,6 +129,24 @@ function apply_patches(rockspec) return true end +local function install_default_docs(name, version) + local patterns = { "readme", "license", "copying" } + local dest = dir.path(path.install_dir(name, version), "doc") + local has_dir = false + for name in fs.dir() do + for _, pattern in ipairs(patterns) do + if name:lower():match("^"..pattern) then + if not has_dir then + fs.make_dir(dest) + has_dir = true + end + fs.copy(name, dest) + break + end + end + end +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, @@ -123,7 +159,7 @@ end -- "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 build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode) +function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode) assert(type(rockspec_file) == "string") assert(type(need_to_fetch) == "boolean") @@ -145,7 +181,7 @@ function build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode) end end - ok, err, errcode = deps.check_external_deps(rockspec, "build") + local ok, err, errcode = deps.check_external_deps(rockspec, "build") if err then return nil, err, errcode end @@ -162,7 +198,8 @@ function build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode) if not ok then return nil, source_dir, errcode end - fs.change_dir(source_dir) + 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 @@ -180,38 +217,39 @@ function build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode) } for _, d in pairs(dirs) do - fs.make_dir(d.name) + 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 = rockspec.build + local build_spec = rockspec.build if not minimal_mode then - ok, err = apply_patches(rockspec) + ok, err = build.apply_patches(rockspec) if err then return nil, err end end - if build.type ~= "none" then + if build_spec.type ~= "none" then -- Temporary compatibility - if build.type == "module" then + if build_spec.type == "module" then util.printout("Do not use 'module' as a build type. Use 'builtin' instead.") - build.type = "builtin" + build_spec.type = "builtin" end - if cfg.accepted_build_types and util.array_contains(cfg.accepted_build_types, build.type) then - return nil, "This rockspec uses the '"..build.type.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration." + 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.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.type.."': "..build_type + return nil, "Failed initializing build back-end for build type '"..build_spec.type.."': "..build_type end ok, err = build_type.run(rockspec) @@ -220,27 +258,40 @@ function build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode) end end - if build.install then + if build_spec.install then for id, install_dir in pairs(dirs) do - ok, err = install_files(build.install[id], install_dir.name, install_dir.is_module_path) + ok, err = install_files(build_spec.install[id], install_dir.name, install_dir.is_module_path) if not ok then return nil, err end end end - local copy_directories = build.copy_directories or {"doc"} + 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 - util.warning("Directory '"..copy_dir.."' not found") + 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 @@ -275,8 +326,8 @@ function build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode) end local root_dir = path.root_dir(cfg.rocks_dir) - util.printout() util.printout(name.." "..version.." is now built and installed in "..root_dir.." "..license) + util.printout() util.remove_scheduled_function(rollback) return name, version @@ -291,7 +342,7 @@ end -- "order" for all trees with priority >= the current default, "none" for no trees. -- @return boolean or (nil, string, [string]): True if build was successful, -- or false and an error message and an optional error code. -function build_rock(rock_file, need_to_fetch, deps_mode) +function build.build_rock(rock_file, need_to_fetch, deps_mode) assert(type(rock_file) == "string") assert(type(need_to_fetch) == "boolean") @@ -300,25 +351,26 @@ function build_rock(rock_file, need_to_fetch, deps_mode) return nil, err, errcode end local rockspec_file = path.rockspec_name_from_rock(rock_file) - fs.change_dir(unpack_dir) - local ok, err, errcode = build_rockspec(rockspec_file, need_to_fetch, false, deps_mode) + local ok, err = fs.change_dir(unpack_dir) + if not ok then return nil, err end + local ok, err, errcode = build.build_rockspec(rockspec_file, need_to_fetch, false, deps_mode) fs.pop_dir() return ok, err, errcode end local function do_build(name, version, deps_mode) if name:match("%.rockspec$") then - return build_rockspec(name, true, false, deps_mode) + return build.build_rockspec(name, true, false, deps_mode) elseif name:match("%.src%.rock$") then - return build_rock(name, false, deps_mode) + return build.build_rock(name, false, deps_mode) elseif name:match("%.all%.rock$") then local install = require("luarocks.install") return install.install_binary_rock(name, deps_mode) elseif name:match("%.rock$") then - return build_rock(name, true, deps_mode) + return build.build_rock(name, true, deps_mode) elseif not name:match(dir.separator) then local search = require("luarocks.search") - return search.act_on_src_or_rockspec(run, name:lower(), version, deps.deps_mode_to_flag(deps_mode)) + return search.act_on_src_or_rockspec(build.run, name:lower(), version, deps.deps_mode_to_flag(deps_mode)) end return nil, "Don't know what to do with "..name end @@ -329,9 +381,9 @@ end -- 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): True if build was successful; nil and an --- error message otherwise. -function run(...) +-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an +-- error message otherwise. exitcode is optionally returned. +function build.run(...) local flags, name, version = util.parse_flags(...) if type(name) ~= "string" then return nil, "Argument missing. "..util.see_help("build") @@ -342,7 +394,7 @@ function run(...) 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 end + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end ok, err = do_build(name, version, deps.get_deps_mode(flags)) if not ok then return nil, err end local name, version = ok, err @@ -353,3 +405,5 @@ function run(...) return name, version end end + +return build diff --git a/luarocks/src/luarocks/build/builtin.lua b/luarocks/src/luarocks/build/builtin.lua index 9d97d58..47aa71f 100644 --- a/luarocks/src/luarocks/build/builtin.lua +++ b/luarocks/src/luarocks/build/builtin.lua @@ -1,6 +1,9 @@ --- A builtin build system: back-end to provide a portable way of building C-based Lua modules. -module("luarocks.build.builtin", package.seeall) +--module("luarocks.build.builtin", package.seeall) +local builtin = {} + +local unpack = unpack or table.unpack local fs = require("luarocks.fs") local path = require("luarocks.path") @@ -19,6 +22,7 @@ end --- Makes an RC file with an embedded Lua script, for building .exes on Windows -- @return nil if could open files, error otherwise local function make_rc(luafilename, rcfilename) + --TODO EXEWRAPPER local rcfile = io.open(rcfilename, "w") if not rcfile then error("Could not open "..rcfilename.." for writing.") @@ -44,9 +48,9 @@ end -- @param rockspec table: the loaded rockspec. -- @return boolean or (nil, string): true if no errors ocurred, -- nil and an error message otherwise. -function run(rockspec) +function builtin.run(rockspec) assert(type(rockspec) == "table") - local compile_object, compile_library, compile_wrapper_binary + local compile_object, compile_library, compile_wrapper_binary --TODO EXEWRAPPER local build = rockspec.build local variables = rockspec.variables @@ -80,6 +84,7 @@ function run(rockspec) return ok end compile_wrapper_binary = function(fullname, name) + --TODO EXEWRAPPER local fullbasename = fullname:gsub("%.lua$", ""):gsub("/", "\\") local basename = name:gsub("%.lua$", ""):gsub("/", "\\") local rcname = basename..".rc" @@ -112,13 +117,20 @@ function run(rockspec) def:write("luaopen_"..name:gsub("%.", "_").."\n") def:close() local ok = execute(variables.LD, "-dll", "-def:"..deffile, "-out:"..library, dir.path(variables.LUA_LIBDIR, variables.LUALIB), unpack(extras)) - local manifestfile = basename..".dll.manifest" + local basedir = "" + if name:find("%.") ~= nil then + basedir = name:gsub("%.%w+$", "\\") + basedir = basedir:gsub("%.", "\\") + end + local manifestfile = basedir .. basename..".dll.manifest" + if ok and fs.exists(manifestfile) then - ok = execute(variables.MT, "-manifest", manifestfile, "-outputresource:"..basename..".dll;2") + ok = execute(variables.MT, "-manifest", manifestfile, "-outputresource:"..basedir..basename..".dll;2") end return ok end compile_wrapper_binary = function(fullname, name) + --TODO EXEWRAPPER local fullbasename = fullname:gsub("%.lua$", ""):gsub("/", "\\") local basename = name:gsub("%.lua$", ""):gsub("/", "\\") local object = basename..".obj" @@ -159,47 +171,48 @@ function run(rockspec) return execute(variables.LD.." "..variables.LIBFLAG, "-o", library, "-L"..variables.LUA_LIBDIR, unpack(extras)) end compile_wrapper_binary = function(fullname, name) return true, name end + --TODO EXEWRAPPER end local ok = true - local err = "Build error" local built_modules = {} local luadir = path.lua_dir(rockspec.name, rockspec.version) local libdir = path.lib_dir(rockspec.name, rockspec.version) - local docdir = path.doc_dir(rockspec.name, rockspec.version) + --TODO EXEWRAPPER -- On Windows, compiles an .exe for each Lua file in build.install.bin, and -- replaces the filename with the .exe name. Strips the .lua extension if it exists, - -- otherwise just appends .exe to the name + -- otherwise just appends .exe to the name. Only if `cfg.exewrapper = true` if build.install and build.install.bin then - for i, name in ipairs(build.install.bin) do + for key, name in pairs(build.install.bin) do local fullname = dir.path(fs.current_dir(), name) - local match = name:match("%.lua$") - local basename = name:gsub("%.lua$", "") - local file - if not match then - file = io.open(fullname) - end - if match or (file and file:read():match("#!.*lua.*")) then + if cfg.exewrapper and fs.is_lua(fullname) then ok, name = compile_wrapper_binary(fullname, name) if ok then - build.install.bin[i] = name + build.install.bin[key] = name else - if file then file:close() end return nil, "Build error in wrapper binaries" end end - if file then file:close() end end end + + for name, info in pairs(build.modules) do local moddir = path.module_to_path(name) if type(info) == "string" then local ext = info:match(".([^.]+)$") if ext == "lua" then + local filename = dir.base_name(info) if info:match("init%.lua$") and not name:match("%.init$") then moddir = path.module_to_path(name..".init") + else + local basename = name:match("([^.]+)$") + local baseinfo = filename:gsub("%.lua$", "") + if basename ~= baseinfo then + filename = basename..".lua" + end end - local dest = dir.path(luadir, moddir) + local dest = dir.path(luadir, moddir, filename) built_modules[info] = dest else info = {info} @@ -222,12 +235,13 @@ function run(rockspec) table.insert(objects, object) end if not ok then break end - local module_name = dir.path(moddir, name:match("([^.]*)$").."."..util.matchquote(cfg.lib_extension)):gsub("//", "/") + local module_name = name:match("([^.]*)$").."."..util.matchquote(cfg.lib_extension) if moddir ~= "" then - fs.make_dir(moddir) + module_name = dir.path(moddir, module_name) + local ok, err = fs.make_dir(moddir) + if not ok then return nil, err end end - local dest = dir.path(libdir, moddir) - built_modules[module_name] = dest + built_modules[module_name] = dir.path(libdir, module_name) ok = compile_library(module_name, objects, info.libraries, info.libdirs, name) if not ok then return nil, "Failed compiling module "..module_name @@ -235,17 +249,19 @@ function run(rockspec) end end for name, dest in pairs(built_modules) do - fs.make_dir(dest) + fs.make_dir(dir.dir_name(dest)) ok = fs.copy(name, dest) if not ok then return nil, "Failed installing "..name.." in "..dest end end if fs.is_dir("lua") then - ok, err = fs.copy_contents("lua", luadir) - if not ok then + local ok, err = fs.copy_contents("lua", luadir) + if not ok then return nil, "Failed copying contents of 'lua' directory: "..err end end return true end + +return builtin diff --git a/luarocks/src/luarocks/build/cmake.lua b/luarocks/src/luarocks/build/cmake.lua index 82f4ff5..ed2af3f 100644 --- a/luarocks/src/luarocks/build/cmake.lua +++ b/luarocks/src/luarocks/build/cmake.lua @@ -1,6 +1,7 @@ --- Build back-end for CMake-based modules. -module("luarocks.build.cmake", package.seeall) +--module("luarocks.build.cmake", package.seeall) +local cmake = {} local fs = require("luarocks.fs") local util = require("luarocks.util") @@ -10,7 +11,7 @@ local cfg = require("luarocks.cfg") -- @param rockspec table: the loaded rockspec. -- @return boolean or (nil, string): true if no errors ocurred, -- nil and an error message otherwise. -function run(rockspec) +function cmake.run(rockspec) assert(type(rockspec) == "table") local build = rockspec.build local variables = build.variables or {} @@ -22,15 +23,15 @@ function run(rockspec) util.variable_substitutions(variables, rockspec.variables) - if not fs.execute_string(fs.quiet(rockspec.variables.CMAKE.." --help")) then + if not fs.execute_quiet(rockspec.variables.CMAKE, "--help") then return nil, "'"..rockspec.variables.CMAKE.."' program not found. Is cmake installed? You may want to edit variables.CMAKE" end -- If inline cmake is present create CMakeLists.txt from it. if type(build.cmake) == "string" then - local cmake = assert(io.open(fs.current_dir().."/CMakeLists.txt", "w")) - cmake:write(build.cmake) - cmake:close() + local cmake_handler = assert(io.open(fs.current_dir().."/CMakeLists.txt", "w")) + cmake_handler:write(build.cmake) + cmake_handler:close() end @@ -56,3 +57,5 @@ function run(rockspec) end return true end + +return cmake diff --git a/luarocks/src/luarocks/build/command.lua b/luarocks/src/luarocks/build/command.lua index aeec0da..650e323 100644 --- a/luarocks/src/luarocks/build/command.lua +++ b/luarocks/src/luarocks/build/command.lua @@ -1,6 +1,7 @@ --- Build back-end for raw listing of commands in rockspec files. -module("luarocks.build.command", package.seeall) +--module("luarocks.build.command", package.seeall) +local command = {} local fs = require("luarocks.fs") local util = require("luarocks.util") @@ -9,7 +10,7 @@ local util = require("luarocks.util") -- @param rockspec table: the loaded rockspec. -- @return boolean or (nil, string): true if no errors ocurred, -- nil and an error message otherwise. -function run(rockspec) +function command.run(rockspec) assert(type(rockspec) == "table") local build = rockspec.build @@ -30,3 +31,5 @@ function run(rockspec) end return true end + +return command diff --git a/luarocks/src/luarocks/build/make.lua b/luarocks/src/luarocks/build/make.lua index c4b2157..0da183e 100644 --- a/luarocks/src/luarocks/build/make.lua +++ b/luarocks/src/luarocks/build/make.lua @@ -1,6 +1,9 @@ --- Build back-end for using Makefile-based packages. -module("luarocks.build.make", package.seeall) +--module("luarocks.build.make", package.seeall) +local make = {} + +local unpack = unpack or table.unpack local fs = require("luarocks.fs") local util = require("luarocks.util") @@ -36,7 +39,7 @@ end -- @param rockspec table: the loaded rockspec. -- @return boolean or (nil, string): true if no errors ocurred, -- nil and an error message otherwise. -function run(rockspec) +function make.run(rockspec) assert(type(rockspec) == "table") local build = rockspec.build @@ -90,3 +93,5 @@ function run(rockspec) end return true end + +return make diff --git a/luarocks/src/luarocks/cache.lua b/luarocks/src/luarocks/cache.lua index 21185c1..dbea840 100644 --- a/luarocks/src/luarocks/cache.lua +++ b/luarocks/src/luarocks/cache.lua @@ -1,14 +1,16 @@ --- Module handling the LuaRocks local cache. -- Adds a rock or rockspec to a rocks server. -module("luarocks.cache", package.seeall) +--module("luarocks.cache", package.seeall) +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 get_upload_server(server) +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." @@ -16,7 +18,7 @@ function get_upload_server(server) return server, cfg.upload_servers and cfg.upload_servers[server] end -function get_server_urls(server, upload_server) +function cache.get_server_urls(server, upload_server) local download_url = server local login_url = nil if upload_server then @@ -32,7 +34,7 @@ function get_server_urls(server, upload_server) return download_url, login_url end -function split_server_url(server, url, user, password) +function cache.split_server_url(server, url, user, password) local protocol, server_path = dir.split_url(url) if server_path:match("@") then local credentials @@ -50,28 +52,31 @@ function split_server_url(server, url, user, password) return local_cache, protocol, server_path, user, password end -function refresh_local_cache(server, url, user, password) - local local_cache, protocol, server_path, user, password = split_server_url(server, url, user, password) +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) - fs.make_dir(cfg.local_cache) + local ok, err = fs.make_dir(cfg.local_cache) + if not ok then return nil, err end local tmp_cache = false if not local_cache then - local_cache = fs.make_temp_dir("local_cache") + local err + local_cache, err = fs.make_temp_dir("local_cache") tmp_cache = true end - local ok = fs.make_dir(local_cache) + local ok, err = fs.make_dir(local_cache) if not ok then - return nil, "Failed creating local cache dir." + 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.." -avz -e ssh "..user.."@"..srv..":"..path.."/ "..local_cache.."/") + 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 @@ -83,3 +88,5 @@ function refresh_local_cache(server, url, user, password) end return local_cache, protocol, server_path, user, password end + +return cache diff --git a/luarocks/src/luarocks/cfg.lua b/luarocks/src/luarocks/cfg.lua index a1345db..a08c7c8 100644 --- a/luarocks/src/luarocks/cfg.lua +++ b/luarocks/src/luarocks/cfg.lua @@ -13,10 +13,14 @@ 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") +--module("luarocks.cfg") +local cfg = {} +package.loaded["luarocks.cfg"] = cfg -lua_version = _VERSION:sub(5) -local version_suffix = lua_version:gsub("%.", "_") +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) @@ -28,12 +32,28 @@ if not ok then site_config = {} end -_M.site_config = site_config +cfg.site_config = site_config -program_version = "2.1.0" +cfg.program_version = "2.2.0beta1" +cfg.major_version = cfg.program_version:match("([^.]%.[^.])") local persist = require("luarocks.persist") +cfg.errorcodes = setmetatable({ + OK = 0, + UNSPECIFIED = 1, + PERMISSIONDENIED = 2, +},{ + __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 @@ -42,7 +62,7 @@ if popen_ok then else io.stderr:write("Your version of Lua does not support io.popen,\n") io.stderr:write("which is required by LuaRocks. Please check your Lua installation.\n") - os.exit(1) + os.exit(cfg.errorcodes.UNSPECIFIED) end -- System detection: @@ -105,31 +125,32 @@ end local sys_config_file, home_config_file 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 if detected.windows then - home = os.getenv("APPDATA") or "c:" + cfg.home = os.getenv("APPDATA") or "c:" sys_config_dir = sys_config_dir or "c:/luarocks" - home_config_dir = home.."/luarocks" - home_tree = home.."/luarocks/" + home_config_dir = cfg.home.."/luarocks" + cfg.home_tree = cfg.home.."/luarocks/" else - home = os.getenv("HOME") or "" + cfg.home = os.getenv("HOME") or "" sys_config_dir = sys_config_dir or "/etc/luarocks" - home_config_dir = home.."/.luarocks" - home_tree = home.."/.luarocks/" + home_config_dir = cfg.home.."/.luarocks" + cfg.home_tree = cfg.home.."/.luarocks/" end -variables = {} -rocks_trees = {} +cfg.variables = {} +cfg.rocks_trees = {} -sys_config_file = site_config.LUAROCKS_SYSCONFIG or sys_config_dir.."/config-"..lua_version..".lua" +sys_config_file = site_config.LUAROCKS_SYSCONFIG or sys_config_dir.."/config-"..cfg.lua_version..".lua" local err -sys_config_ok, err = persist.load_into_table(sys_config_file, _M) +sys_config_ok, err = persist.load_into_table(sys_config_file, cfg) if not sys_config_ok then sys_config_file = sys_config_dir.."/config.lua" - sys_config_ok, err = persist.load_into_table(sys_config_file, _M) + sys_config_ok, err = persist.load_into_table(sys_config_file, cfg) end -if err and ok == nil then +if err and sys_config_ok == nil then io.stderr:write(err.."\n") end @@ -137,25 +158,24 @@ if not site_config.LUAROCKS_FORCE_CONFIG then local home_overrides, err home_config_file = os.getenv("LUAROCKS_CONFIG_" .. version_suffix) or os.getenv("LUAROCKS_CONFIG") if home_config_file then - home_overrides, err = persist.load_into_table(home_config_file, { home = home, lua_version = lua_version }) + home_overrides, err = persist.load_into_table(home_config_file, { home = cfg.home, lua_version = cfg.lua_version }) else - home_config_file = home_config_dir.."/config-"..lua_version..".lua" - home_overrides, err = persist.load_into_table(home_config_file, { home = home, lua_version = lua_version }) + home_config_file = home_config_dir.."/config-"..cfg.lua_version..".lua" + home_overrides, err = persist.load_into_table(home_config_file, { home = cfg.home, lua_version = cfg.lua_version }) if not home_overrides then home_config_file = home_config_dir.."/config.lua" - home_overrides, err = persist.load_into_table(home_config_file, { home = home, lua_version = lua_version }) + home_overrides, err = persist.load_into_table(home_config_file, { home = cfg.home, lua_version = cfg.lua_version }) end end if home_overrides then home_config_ok = true - local util = require("luarocks.util") if home_overrides.rocks_trees then - _M.rocks_trees = nil + cfg.rocks_trees = nil end if home_overrides.rocks_servers then - _M.rocks_servers = nil + cfg.rocks_servers = nil end - util.deep_merge(_M, home_overrides) + util.deep_merge(cfg, home_overrides) else -- nil or false home_config_ok = home_overrides if err and home_config_ok == nil then @@ -164,18 +184,18 @@ if not site_config.LUAROCKS_FORCE_CONFIG then end end -if not next(rocks_trees) then - if home_tree then - table.insert(rocks_trees, home_tree) +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(rocks_trees, site_config.LUAROCKS_ROCKS_TREE) + table.insert(cfg.rocks_trees, { name = "system", root = site_config.LUAROCKS_ROCKS_TREE } ) end end -- Configure defaults: -local root = rocks_trees[#rocks_trees] +local root = cfg.rocks_trees[#cfg.rocks_trees] local defaults = { local_by_default = false, @@ -185,8 +205,8 @@ local defaults = { hooks_enabled = true, deps_mode = "one", - lua_modules_path = "/share/lua/"..lua_version, - lib_modules_path = "/lib/lua/"..lua_version, + 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", @@ -199,11 +219,19 @@ local defaults = { "https://raw.githubusercontent.com/torch/luarocks-mirror/master/rocks/" } }, + disabled_servers = {}, + + upload = { + server = "https://rocks.moonscript.org", + tool_version = "0.0.1", + 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", @@ -246,22 +274,28 @@ local defaults = { CMAKE = "cmake", SEVENZ = "7z", + RSYNCFLAGS = "--exclude=.git -Oavz", STATFLAG = "-c '%a'", }, - external_deps_subdirs = { + external_deps_subdirs = site_config.LUAROCKS_EXTERNAL_DEPS_SUBDIRS or { bin = "bin", lib = "lib", include = "include" }, - runtime_external_deps_subdirs = { + runtime_external_deps_subdirs = site_config.LUAROCKS_RUNTIME_EXTERNAL_DEPS_SUBDIRS or { bin = "bin", lib = "lib", include = "include" }, + + rocks_provided = {} } if detected.windows then + local full_prefix = site_config.LUAROCKS_PREFIX.."\\"..cfg.major_version + 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-"..proc @@ -270,21 +304,29 @@ if detected.windows then defaults.external_lib_extension = "dll" 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"..lua_version.."/bin" - defaults.variables.LUA_INCDIR = site_config.LUA_INCDIR and site_config.LUA_INCDIR:gsub("\\", "/") or "c:/lua"..lua_version.."/include" - defaults.variables.LUA_LIBDIR = site_config.LUA_LIBDIR and site_config.LUA_LIBDIR:gsub("\\", "/") or "c:/lua"..lua_version.."/lib" + 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.cmake_generator = "MinGW Makefiles" defaults.makefile = "Makefile.win" defaults.variables.MAKE = "nmake" defaults.variables.CC = "cl" defaults.variables.RC = "rc" - defaults.variables.WRAPPER = site_config.LUAROCKS_PREFIX .. "\\2.0\\rclauncher.c" + defaults.variables.WRAPPER = full_prefix.."\\rclauncher.c" defaults.variables.LD = "link" defaults.variables.MT = "mt" - defaults.variables.LUALIB = "lua"..lua_version..".lib" + defaults.variables.LUALIB = "lua"..cfg.lua_version..".lib" defaults.variables.CFLAGS = "/MD /O2" defaults.variables.LIBFLAG = "/dll" - defaults.variables.LUALIB = "lua"..lua_version..".lib" + + 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" }, @@ -299,7 +341,15 @@ if detected.windows then defaults.export_path_separator = ";" defaults.export_lua_path = "SET LUA_PATH=%s" defaults.export_lua_cpath = "SET LUA_CPATH=%s" - defaults.local_cache = home.."/cache/luarocks" + 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 detected.mingw32 then @@ -309,10 +359,22 @@ if detected.mingw32 then defaults.variables.MAKE = "mingw32-make" defaults.variables.CC = "mingw32-gcc" defaults.variables.RC = "windres" - defaults.variables.WRAPPER = site_config.LUAROCKS_PREFIX .. "\\2.0\\rclauncher.c" defaults.variables.LD = "mingw32-gcc" 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 detected.unix then @@ -344,10 +406,12 @@ if detected.unix then defaults.export_path_separator = ":" defaults.export_lua_path = "export LUA_PATH='%s'" defaults.export_lua_cpath = "export LUA_CPATH='%s'" - defaults.local_cache = home.."/.cache/luarocks" + 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 detected.cygwin then @@ -381,6 +445,7 @@ if detected.macosx then end defaults.variables.CC = "export MACOSX_DEPLOYMENT_TARGET=10."..version.."; gcc" defaults.variables.LD = "export MACOSX_DEPLOYMENT_TARGET=10."..version.."; gcc" + defaults.web_browser = "open" end if detected.linux then @@ -391,6 +456,9 @@ end if detected.freebsd then defaults.arch = "freebsd-"..proc defaults.platforms = {"unix", "bsd", "freebsd"} + defaults.gcc_rpath = false + defaults.variables.CC = "cc" + defaults.variables.LD = "cc" end if detected.openbsd then @@ -415,16 +483,38 @@ 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 --- Use defaults: +-- Add built-in modules to rocks_provided +defaults.rocks_provided["lua"] = cfg.lua_version.."-1" + +if cfg.lua_version >= "5.2" then + -- Lua 5.2+ + defaults.rocks_provided["bit32"] = cfg.lua_version.."-1" +end + +if cfg.lua_version >= "5.3" then + -- Lua 5.3+ + defaults.rocks_provided["utf8"] = cfg.lua_version.."-1" +end --- Populate values from 'defaults.variables' in 'variables' if they were not --- already set by user. -if not _M.variables then - _M.variables = {} +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" end -for k,v in pairs(defaults.variables) do - if not _M.variables[k] then - _M.variables[k] = v + +-- 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 @@ -438,44 +528,60 @@ local cfg_mt = { return default end } -setmetatable(_M, cfg_mt) - -function package_paths() - local new_path, new_cpath = {}, {} - for _,tree in ipairs(rocks_trees) do - if type(tree) == "string" then - table.insert(new_path, 1, tree..lua_modules_path.."/?.lua;"..tree..lua_modules_path.."/?/init.lua") - table.insert(new_cpath, 1, tree..lib_modules_path.."/?."..lib_extension) - else - table.insert(new_path, 1, (tree.lua_dir or tree.root..lua_modules_path).."/?.lua;".. - (tree.lua_dir or tree.root..lua_modules_path).."/?/init.lua") - table.insert(new_cpath, 1, (tree.lib_dir or tree.root..lib_modules_path).."/?."..lib_extension) - end +setmetatable(cfg, cfg_mt) + +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 table.concat(new_path, ";"), table.concat(new_cpath, ";") + return lua_path, lib_path, bin_path end -do - local new_path, new_cpath = package_paths() - package.path = new_path..";"..package.path - package.cpath = new_cpath..";"..package.cpath +function cfg.package_paths() + local new_path, new_cpath, new_bin = {}, {}, {} + for _,tree in ipairs(cfg.rocks_trees) do + 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 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 which_config() +function cfg.which_config() return sys_config_file, sys_config_ok, home_config_file, home_config_ok end -user_agent = "LuaRocks/"..program_version.." "..arch +cfg.user_agent = "LuaRocks/"..cfg.program_version.." "..cfg.arch --- 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 is_platform(query) +function cfg.is_platform(query) assert(type(query) == "string") - for _, platform in ipairs(platforms) do + for _, platform in ipairs(cfg.platforms) do if platform == query then return true end end end + +return cfg diff --git a/luarocks/src/luarocks/command_line.lua b/luarocks/src/luarocks/command_line.lua index d16ef8e..cc2e168 100644 --- a/luarocks/src/luarocks/command_line.lua +++ b/luarocks/src/luarocks/command_line.lua @@ -1,6 +1,9 @@ --- Functions for command-line scripts. -module("luarocks.command_line", package.seeall) +--module("luarocks.command_line", package.seeall) +local command_line = {} + +local unpack = unpack or table.unpack local util = require("luarocks.util") local cfg = require("luarocks.cfg") @@ -12,7 +15,8 @@ local program = util.this_program("luarocks") --- Display an error message and exit. -- @param message string: The error message. -local function die(message) +-- @param exitcode number: the exitcode to use +local function die(message, exitcode) assert(type(message) == "string") local ok, err = pcall(util.run_scheduled_functions) @@ -20,7 +24,19 @@ local function die(message) util.printerr("\nLuaRocks "..cfg.program_version.." internal bug (please report at luarocks-developers@lists.sourceforge.net):\n"..err) end util.printerr("\nError: "..message) - os.exit(1) + os.exit(exitcode or cfg.errorcodes.UNSPECIFIED) +end + +local function replace_tree(flags, args, tree) + tree = dir.normalize(tree) + flags["tree"] = tree + for i = 1, #args do + if args[i]:match("%-%-tree=") then + args[i] = "--tree="..tree + break + end + end + path.use_tree(tree) end --- Main command-line processor. @@ -30,7 +46,7 @@ end -- Uses the global table "commands", which contains -- the loaded modules representing commands. -- @param ... string: Arguments given on the command-line. -function run_command(...) +function command_line.run_command(...) local args = {...} local cmdline_vars = {} for i = #args, 1, -1 do @@ -61,11 +77,26 @@ function run_command(...) local command + if flags["verbose"] then -- setting it in the config file will kick-in earlier in the process + cfg.verbose = true + local fs = require("luarocks.fs") + fs.verbose() + end + + if flags["timeout"] then -- setting it in the config file will kick-in earlier in the process + local timeout = tonumber(flags["timeout"]) + if timeout then + cfg.connection_timeout = timeout + else + die "Argument error: --timeout expects a numeric argument." + end + end + if flags["version"] then util.printout(program.." "..cfg.program_version) util.printout(program_description) util.printout() - os.exit(0) + os.exit(cfg.errorcodes.OK) elseif flags["help"] or #nonflags == 0 then command = "help" args = nonflags @@ -94,15 +125,35 @@ function run_command(...) die("Invalid entry for --deps-mode.") end + if flags["branch"] then + if flags["branch"] == true or flags["branch"] == "" then + die("Argument error: use --branch=<branch-name>") + end + cfg.branch = flags["branch"] + end + if flags["tree"] then if flags["tree"] == true or flags["tree"] == "" then die("Argument error: use --tree=<path>") end - local fs = require("luarocks.fs") - local root_dir = fs.absolute_name(flags["tree"]) - path.use_tree(root_dir) + local named = false + for _, tree in ipairs(cfg.rocks_trees) do + if type(tree) == "table" and flags["tree"] == tree.name then + if not tree.root then + die("Configuration error: tree '"..tree.name.."' has no 'root' field.") + end + replace_tree(flags, args, tree.root) + named = true + break + end + end + if not named then + local fs = require("luarocks.fs") + local root_dir = fs.absolute_name(flags["tree"]) + replace_tree(flags, args, root_dir) + end elseif flags["local"] then - path.use_tree(cfg.home_tree) + replace_tree(flags, args, cfg.home_tree) else local trees = cfg.rocks_trees path.use_tree(trees[#trees]) @@ -153,16 +204,19 @@ function run_command(...) -- I'm not changing this now to avoid messing with the run() -- interface, which I know some people use (even though -- I never published it as a public API...) - local xp, ok, err = xpcall(function() return commands[command].run(unpack(args)) end, function(err) + local cmd = require(commands[command]) + local xp, ok, err, exitcode = xpcall(function() return cmd.run(unpack(args)) end, function(err) die(debug.traceback("LuaRocks "..cfg.program_version .." bug (please report at luarocks-developers@lists.sourceforge.net).\n" ..err, 2)) end) if xp and (not ok) then - die(err) + die(err, exitcode) end else die("Unknown command: "..command) end util.run_scheduled_functions() end + +return command_line diff --git a/luarocks/src/luarocks/deps.lua b/luarocks/src/luarocks/deps.lua index 5ac822c..3f75f9b 100644 --- a/luarocks/src/luarocks/deps.lua +++ b/luarocks/src/luarocks/deps.lua @@ -11,7 +11,9 @@ -- comparison criteria is the source code of this module, but the -- test/test_deps.lua file included with LuaRocks provides some -- insights on what these criteria are. -module("luarocks.deps", package.seeall) +--module("luarocks.deps", package.seeall) +local deps = {} +package.loaded["luarocks.deps"] = deps local cfg = require("luarocks.cfg") local manif_core = require("luarocks.manif_core") @@ -34,7 +36,7 @@ local operators = { } local deltas = { - scm = 1000, + scm = 1100, cvs = 1000, rc = -1000, pre = -10000, @@ -101,7 +103,7 @@ setmetatable(version_cache, { -- @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 parse_version(vstring) +function deps.parse_version(vstring) if not vstring then return nil end assert(type(vstring) == "string") @@ -140,7 +142,6 @@ function parse_version(vstring) version[i] = 0 break end - local last = #version version[i] = deltas[token] or (token:byte() / 1000) end vstring = rest @@ -154,8 +155,8 @@ end -- @param a string: one version. -- @param b string: another version. -- @return boolean: True if a > b. -function compare_versions(a, b) - return parse_version(a) > parse_version(b) +function deps.compare_versions(a, b) + return deps.parse_version(a) > deps.parse_version(b) end --- Consumes a constraint from a string, converting it to table format. @@ -171,7 +172,7 @@ local function parse_constraint(input) local no_upgrade, op, version, rest = input:match("^(@?)([<>=~!]*)%s*([%w%.%_%-]+)[%s,]*(.*)") local _op = operators[op] - version = parse_version(version) + version = deps.parse_version(version) if not _op then return nil, "Encountered bad constraint operator: '"..tostring(op).."' in '"..input.."'" end @@ -189,7 +190,7 @@ end -- @param input string: A list of constraints in string format. -- @return table or nil: A table representing the same constraints, -- or nil if the input string is invalid. -function parse_constraints(input) +function deps.parse_constraints(input) assert(type(input) == "string") local constraints, constraint, oinput = {}, nil, input @@ -214,12 +215,12 @@ end -- as entered in rockspec files. -- @return table or nil: A table representing the same dependency relation, -- or nil if the input string is invalid. -function parse_dep(dep) +function deps.parse_dep(dep) assert(type(dep) == "string") - local name, rest = dep:match("^%s*([a-zA-Z][a-zA-Z0-9%.%-%_]*)%s*(.*)") + local name, rest = dep:match("^%s*([a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*(.*)") if not name then return nil, "failed to extract dependency name from '"..tostring(dep).."'" end - local constraints, err = parse_constraints(rest) + local constraints, err = deps.parse_constraints(rest) if not constraints then return nil, err end return { name = name, constraints = constraints } end @@ -229,7 +230,7 @@ end -- @param internal boolean or nil: Whether to display versions in their -- internal representation format or how they were specified. -- @return string: The dependency information pretty-printed as a string. -function show_version(v, internal) +function deps.show_version(v, internal) assert(type(v) == "table") assert(type(internal) == "boolean" or not internal) @@ -243,13 +244,13 @@ end -- @param internal boolean or nil: Whether to display versions in their -- internal representation format or how they were specified. -- @return string: The dependency information pretty-printed as a string. -function show_dep(dep, internal) +function deps.show_dep(dep, internal) assert(type(dep) == "table") assert(type(internal) == "boolean" or not internal) local pretty = {} for _, c in ipairs(dep.constraints) do - table.insert(pretty, c.op .. " " .. show_version(c.version, internal)) + table.insert(pretty, c.op .. " " .. deps.show_version(c.version, internal)) end return dep.name.." "..table.concat(pretty, ", ") end @@ -270,8 +271,8 @@ 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 = parse_version(version) end - if type(requested) ~= "table" then requested = parse_version(requested) end + 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 @@ -289,14 +290,14 @@ end -- @param constraints table: An array of constraints in table format. -- @return boolean: True if version satisfies all constraints, -- false otherwise. -function match_constraints(version, constraints) +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 = parse_version(constr.version) + constr.version = deps.parse_version(constr.version) end local constr_version, constr_op = constr.version, constr.op setmetatable(constr_version, version_mt) @@ -323,9 +324,10 @@ end local function match_dep(dep, blacklist, deps_mode) assert(type(dep) == "table") - local versions - if dep.name == "lua" then - versions = { cfg.lua_version } + local versions = cfg.rocks_provided[dep.name] + if cfg.rocks_provided[dep.name] then + -- provided rocks have higher priority than manifest's rocks + versions = { cfg.rocks_provided[dep.name] } else versions = manif_core.get_versions(dep.name, deps_mode) end @@ -344,8 +346,8 @@ local function match_dep(dep, blacklist, deps_mode) end local candidates = {} for _, vstring in ipairs(versions) do - local version = parse_version(vstring) - if match_constraints(version, dep.constraints) then + local version = deps.parse_version(vstring) + if deps.match_constraints(version, dep.constraints) then table.insert(candidates, version) end end @@ -365,19 +367,21 @@ end -- @param blacklist table or nil: Program versions to not use as valid matches. -- Table where keys are program names and values are tables where keys -- are program versions and values are 'true'. --- @return table, table: A table where keys are dependencies parsed +-- @return table, table, table: A table where keys are dependencies parsed -- in table format and values are tables containing fields 'name' and --- version' representing matches, and a table of missing dependencies --- parsed as tables. -function match_deps(rockspec, blacklist, deps_mode) +-- version' representing matches; a table of missing dependencies +-- parsed as tables; and a table of "no-upgrade" missing dependencies +-- (to be used in plugin modules so that a plugin does not force upgrade of +-- its parent application). +function deps.match_deps(rockspec, blacklist, deps_mode) assert(type(rockspec) == "table") assert(type(blacklist) == "table" or not blacklist) 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) if found then - if dep.name ~= "lua" then + if not cfg.rocks_provided[dep.name] then matched[dep] = found end else @@ -409,24 +413,24 @@ end -- @return boolean or (nil, string, [string]): True if no errors occurred, or -- nil and an error message if any test failed, followed by an optional -- error code. -function fulfill_dependencies(rockspec, deps_mode) +function deps.fulfill_dependencies(rockspec, deps_mode) local search = require("luarocks.search") local install = require("luarocks.install") if rockspec.supported_platforms then - if not platforms_set then - platforms_set = values_set(cfg.platforms) + if not deps.platforms_set then + deps.platforms_set = values_set(cfg.platforms) end local supported = nil for _, plat in pairs(rockspec.supported_platforms) do local neg, plat = plat:match("^(!?)(.*)") if neg == "!" then - if platforms_set[plat] then + if deps.platforms_set[plat] then return nil, "This rockspec for "..rockspec.package.." does not support "..plat.." platforms." end else - if platforms_set[plat] then + if deps.platforms_set[plat] then supported = true else if supported == nil then @@ -441,22 +445,22 @@ function fulfill_dependencies(rockspec, deps_mode) end end - local matched, missing, no_upgrade = match_deps(rockspec, nil, deps_mode) + local _, missing, no_upgrade = deps.match_deps(rockspec, nil, deps_mode) if next(no_upgrade) then util.printerr("Missing dependencies for "..rockspec.name.." "..rockspec.version..":") for _, dep in pairs(no_upgrade) do - util.printerr(show_dep(dep)) + util.printerr(deps.show_dep(dep)) end if next(missing) then for _, dep in pairs(missing) do - util.printerr(show_dep(dep)) + util.printerr(deps.show_dep(dep)) end end util.printerr() for _, dep in pairs(no_upgrade) do util.printerr("This version of "..rockspec.name.." is designed for use with") - util.printerr(show_dep(dep)..", but is configured to avoid upgrading it") + util.printerr(deps.show_dep(dep)..", but is configured to avoid upgrading it") util.printerr("automatically. Please upgrade "..dep.name.." with") util.printerr(" luarocks install "..dep.name) util.printerr("or choose an older version of "..rockspec.name.." with") @@ -469,7 +473,7 @@ function fulfill_dependencies(rockspec, deps_mode) util.printerr() util.printerr("Missing dependencies for "..rockspec.name..":") for _, dep in pairs(missing) do - util.printerr(show_dep(dep)) + util.printerr(deps.show_dep(dep)) end util.printerr() @@ -478,7 +482,7 @@ function fulfill_dependencies(rockspec, deps_mode) if not match_dep(dep, nil, deps_mode) then local rock = search.find_suitable_rock(dep) if not rock then - return nil, "Could not satisfy dependency: "..show_dep(dep) + return nil, "Could not satisfy dependency: "..deps.show_dep(dep) end local ok, err, errcode = install.run(rock) if not ok then @@ -528,7 +532,7 @@ end -- if "install" is given, do not scan for headers. -- @return boolean or (nil, string): True if no errors occurred, or -- nil and an error message if any test failed. -function check_external_deps(rockspec, mode) +function deps.check_external_deps(rockspec, mode) assert(type(rockspec) == "table") local fs = require("luarocks.fs") @@ -574,7 +578,19 @@ function check_external_deps(rockspec, mode) prefix = prefix.prefix end for dirname, dirdata in pairs(dirs) do - dirdata.dir = vars[name.."_"..dirname] or dir.path(prefix, dirdata.subdir) + local paths + local path_var_value = vars[name.."_"..dirname] + if path_var_value then + paths = { path_var_value } + elseif type(dirdata.subdir) == "table" then + paths = {} + for i,v in ipairs(dirdata.subdir) do + paths[i] = dir.path(prefix, v) + end + else + paths = { dir.path(prefix, dirdata.subdir) } + end + dirdata.dir = paths[1] local file = files[dirdata.testfile] if file then local files = {} @@ -596,16 +612,22 @@ function check_external_deps(rockspec, mode) if f:match("%.so$") or f:match("%.dylib$") or f:match("%.dll$") then f = f:gsub("%.[^.]+$", "."..cfg.external_lib_extension) end - if f:match("%*") then - local replaced = f:gsub("%.", "%%."):gsub("%*", ".*") - for _, entry in ipairs(fs.list_dir(dirdata.dir)) do - if entry:match(replaced) then - found = true - break + for _, d in ipairs(paths) do + if f:match("%*") then + local replaced = f:gsub("%.", "%%."):gsub("%*", ".*") + for entry in fs.dir(d) do + if entry:match(replaced) then + found = true + break + end end + else + found = fs.is_file(dir.path(d, f)) + end + if found then + dirdata.dir = d + break end - else - found = fs.is_file(dir.path(dirdata.dir, f)) end if found then break @@ -648,7 +670,7 @@ end -- @param name string: Package name. -- @param version string: Package version. -- @return (table, table): The results and a table of missing dependencies. -function scan_deps(results, missing, manifest, name, version, deps_mode) +function deps.scan_deps(results, missing, manifest, name, version, deps_mode) assert(type(results) == "table") assert(type(missing) == "table") assert(type(manifest) == "table") @@ -668,7 +690,7 @@ function scan_deps(results, missing, manifest, name, version, deps_mode) local deplist = dependencies_name[version] local rockspec, err if not deplist then - rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version)) + rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version), false) if err then missing[name.." "..version] = err return results, missing @@ -677,13 +699,14 @@ function scan_deps(results, missing, manifest, name, version, deps_mode) else rockspec = { dependencies = deplist } end - local matched, failures = match_deps(rockspec, nil, deps_mode) + local matched, failures = deps.match_deps(rockspec, nil, deps_mode) + results[name] = results for _, match in pairs(matched) do - results, missing = scan_deps(results, missing, manifest, match.name, match.version, deps_mode) + results, missing = deps.scan_deps(results, missing, manifest, match.name, match.version, deps_mode) end if next(failures) then for _, failure in pairs(failures) do - missing[show_dep(failure)] = "failed" + missing[deps.show_dep(failure)] = "failed" end end results[name] = version @@ -697,11 +720,11 @@ local valid_deps_modes = { none = true, } -function check_deps_mode_flag(flag) +function deps.check_deps_mode_flag(flag) return valid_deps_modes[flag] end -function get_deps_mode(flags) +function deps.get_deps_mode(flags) if flags["deps-mode"] then return flags["deps-mode"] else @@ -709,6 +732,8 @@ function get_deps_mode(flags) end end -function deps_mode_to_flag(deps_mode) +function deps.deps_mode_to_flag(deps_mode) return "--deps-mode="..deps_mode end + +return deps diff --git a/luarocks/src/luarocks/dir.lua b/luarocks/src/luarocks/dir.lua index b496c96..2ef9881 100644 --- a/luarocks/src/luarocks/dir.lua +++ b/luarocks/src/luarocks/dir.lua @@ -1,14 +1,16 @@ --- Generic utilities for handling pathnames. -module("luarocks.dir", package.seeall) +--module("luarocks.dir", package.seeall) +local dir = {} +package.loaded["luarocks.dir"] = dir -separator = "/" +dir.separator = "/" --- Strip the path off a path+filename. -- @param pathname string: A path+name, such as "/a/b/c" -- or "\a\b\c". -- @return string: The filename without its path, such as "c". -function base_name(pathname) +function dir.base_name(pathname) assert(type(pathname) == "string") local base = pathname:gsub("[/\\]*$", ""):match(".*[/\\]([^/\\]*)") @@ -17,13 +19,12 @@ end --- Strip the name off a path+filename. -- @param pathname string: A path+name, such as "/a/b/c". --- @return string: The filename without its path, such as "/a/b/". --- For entries such as "/a/b/", "/a/" is returned. If there are +-- @return string: The filename without its path, such as "/a/b". +-- For entries such as "/a/b/", "/a" is returned. If there are -- no directory separators in input, "" is returned. -function dir_name(pathname) +function dir.dir_name(pathname) assert(type(pathname) == "string") - - return (pathname:gsub("/*$", ""):match("(.*/)[^/]*")) or "" + return (pathname:gsub("/*$", ""):match("(.*)[/]+[^/]*")) or "" end --- Describe a path in a cross-platform way. @@ -35,18 +36,12 @@ end -- @param ... strings representing directories -- @return string: a string with a platform-specific representation -- of the path. -function path(...) - local items = {...} - local i = 1 - while items[i] do - items[i] = items[i]:gsub("(.+)/+$", "%1") - if items[i] == "" then - table.remove(items, i) - else - i = i + 1 - end +function dir.path(...) + local t = {...} + while t[1] == "" do + table.remove(t, 1) end - return (table.concat(items, "/"):gsub("(.+)/+$", "%1")) + return (table.concat(t, "/"):gsub("([^:])/+", "%1/"):gsub("^/+", "/"):gsub("/*$", "")) end --- Split protocol and path from an URL or local pathname. @@ -54,7 +49,7 @@ end -- 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 split_url(url) +function dir.split_url(url) assert(type(url) == "string") local protocol, pathname = url:match("^([^:]*)://(.*)") @@ -65,6 +60,16 @@ function split_url(url) return protocol, pathname end -function normalize(name) - return name:gsub("\\", "/"):gsub("(.)/*$", "%1") +--- 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/luarocks/src/luarocks/doc.lua b/luarocks/src/luarocks/doc.lua new file mode 100644 index 0000000..6dee106 --- /dev/null +++ b/luarocks/src/luarocks/doc.lua @@ -0,0 +1,156 @@ + +--- Module implementing the LuaRocks "doc" command. +-- Shows documentation for an installed rock. +--module("luarocks.doc", package.seeall) +local doc = {} +package.loaded["luarocks.doc"] = doc + +local util = require("luarocks.util") +local show = require("luarocks.show") +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 = "Shows documentation for an installed rock." + +doc.help = [[ +<argument> 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: + +--homepage 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.run(...) + local flags, name, version = util.parse_flags(...) + if not name then + return nil, "Argument missing. "..util.see_help("doc") + end + + local iname, iversion, repo = show.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["homepage"] 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/luarocks/src/luarocks/download.lua b/luarocks/src/luarocks/download.lua index 0012cb1..74ed40e 100644 --- a/luarocks/src/luarocks/download.lua +++ b/luarocks/src/luarocks/download.lua @@ -1,24 +1,42 @@ --- Module implementing the luarocks "download" command. -- Download a rock from the repository. -module("luarocks.download", package.seeall) +--module("luarocks.download", package.seeall) +local download = {} +package.loaded["luarocks.download"] = 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") -help_summary = "Download a specific rock file from a rocks server." -help_arguments = "[--all] [--arch=<arch> | --source | --rockspec] [<name> [<version>]]" +download.help_summary = "Download a specific rock file from a rocks server." +download.help_arguments = "[--all] [--arch=<arch> | --source | --rockspec] [<name> [<version>]]" -help = [[ +download.help = [[ --all Download all files if there are multiple matches. --source Download .src.rock if available. --rockspec Download .rockspec if available. --arch=<arch> Download rock for a specific architecture. ]] -function download(arch, name, version, all) +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()) + 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 results, err local query = search.make_query(name, version) if arch then query.arch = arch end @@ -29,8 +47,7 @@ function download(arch, name, version, all) results, err = search.find_suitable_rock(query) end if type(results) == "string" then - local file = fetch.fetch_url(results) - return file + return get_file(results) elseif type(results) == "table" and next(results) then if all then local all_ok = true @@ -39,7 +56,7 @@ function download(arch, name, version, all) for version, versions in pairs(result) do for _,items in pairs(versions) do local filename = path.make_url(items.repo, name, version, items.arch) - local ok, err = fetch.fetch_url(filename) + local ok, err = get_file(filename) if not ok then all_ok = false any_err = any_err .. "\n" .. err @@ -64,7 +81,7 @@ end -- version may also be passed. -- @return boolean or (nil, string): true if successful or nil followed -- by an error message. -function run(...) +function download.run(...) local flags, name, version = util.parse_flags(...) assert(type(version) == "string" or not version) @@ -83,6 +100,8 @@ function run(...) arch = flags["arch"] end - local dl, err = download(arch, name, version, flags["all"]) + local dl, err = download.download(arch, name, version, flags["all"]) return dl and true, err end + +return download diff --git a/luarocks/src/luarocks/fetch.lua b/luarocks/src/luarocks/fetch.lua index bfdbace..980c8fe 100644 --- a/luarocks/src/luarocks/fetch.lua +++ b/luarocks/src/luarocks/fetch.lua @@ -1,6 +1,8 @@ --- Functions related to fetching and loading local and remote files. -module("luarocks.fetch", package.seeall) +--module("luarocks.fetch", package.seeall) +local fetch = {} +package.loaded["luarocks.fetch"] = fetch local fs = require("luarocks.fs") local dir = require("luarocks.dir") @@ -11,6 +13,10 @@ local persist = require("luarocks.persist") local util = require("luarocks.util") local cfg = require("luarocks.cfg") +function fetch.is_basic_protocol(protocol, remote) + return protocol == "http" or protocol == "https" or protocol == "ftp" or (not remote and protocol == "file") +end + --- Fetch a local or remote file. -- Make a remote or local URL/pathname local, fetching the file if necessary. -- Other "fetch" and "load" functions use this function to obtain files. @@ -23,19 +29,19 @@ local cfg = require("luarocks.cfg") -- @return string or (nil, string, [string]): the absolute local pathname for the -- fetched file, or nil and a message in case of errors, followed by -- an optional error code. -function fetch_url(url, filename) +function fetch.fetch_url(url, filename, cache) assert(type(url) == "string") assert(type(filename) == "string" or not filename) local protocol, pathname = dir.split_url(url) if protocol == "file" then return fs.absolute_name(pathname) - elseif protocol == "http" or protocol == "ftp" or protocol == "https" then - local ok, err = fs.download(url, filename) + elseif fetch.is_basic_protocol(protocol, true) then + local ok, filename = fs.download(url, filename, cache) if not ok then - return nil, "Failed downloading "..url..(err and " - "..err or ""), "network" + return nil, "Failed downloading "..url..(filename and " - "..filename or ""), "network" end - return dir.path(fs.current_dir(), filename or dir.base_name(url)) + return filename else return nil, "Unsupported protocol "..protocol end @@ -52,7 +58,7 @@ end -- @return (string, string) or (nil, string, [string]): absolute local pathname of -- the fetched file and temporary directory name; or nil and an error message -- followed by an optional error code -function fetch_url_at_temp_dir(url, tmpname, filename) +function fetch.fetch_url_at_temp_dir(url, tmpname, filename) assert(type(url) == "string") assert(type(tmpname) == "string") assert(type(filename) == "string" or not filename) @@ -66,13 +72,14 @@ function fetch_url_at_temp_dir(url, tmpname, filename) return nil, "File not found: "..pathname end else - local temp_dir = fs.make_temp_dir(tmpname) + local temp_dir, err = fs.make_temp_dir(tmpname) if not temp_dir then - return nil, "Failed creating temporary directory." + return nil, "Failed creating temporary directory "..tmpname..": "..err end util.schedule_function(fs.delete, temp_dir) - fs.change_dir(temp_dir) - local file, err, errcode = fetch_url(url, filename) + local ok, err = fs.change_dir(temp_dir) + if not ok then return nil, err end + local file, err, errcode = fetch.fetch_url(url, filename) fs.pop_dir() if not file then return nil, "Error fetching file: "..err, errcode @@ -81,6 +88,44 @@ function fetch_url_at_temp_dir(url, tmpname, filename) end end +-- Determine base directory of a fetched URL by extracting its +-- archive and looking for a directory in the root. +-- @param file string: absolute local pathname of the fetched file +-- @param temp_dir string: temporary directory in which URL was fetched. +-- @param src_url string: URL to use when inferring base directory. +-- @param src_dir string or nil: expected base directory (inferred +-- from src_url if not given). +-- @return (string, string) or (string, nil) or (nil, string): +-- The inferred base directory and the one actually found (which may +-- be nil if not found), or nil followed by an error message. +-- The inferred dir is returned first to avoid confusion with errors, +-- because it is never nil. +function fetch.find_base_dir(file, temp_dir, src_url, src_dir) + local ok, err = fs.change_dir(temp_dir) + if not ok then return nil, err end + fs.unpack_archive(file) + local inferred_dir = src_dir or fetch.url_to_base_dir(src_url) + local found_dir = nil + if fs.exists(inferred_dir) then + found_dir = inferred_dir + else + util.printerr("Directory "..inferred_dir.." not found") + local files = fs.list_dir() + if files then + table.sort(files) + for i,filename in ipairs(files) do + if fs.is_dir(filename) then + util.printerr("Found "..filename) + found_dir = filename + break + end + end + end + end + fs.pop_dir() + return inferred_dir, found_dir +end + --- Obtain a rock and unpack it. -- If a directory is not given, a temporary directory will be created, -- which will be deleted on program termination. @@ -89,13 +134,13 @@ end -- a permanent destination. -- @return string or (nil, string, [string]): the directory containing the contents -- of the unpacked rock. -function fetch_and_unpack_rock(rock_file, dest) +function fetch.fetch_and_unpack_rock(rock_file, dest) assert(type(rock_file) == "string") assert(type(dest) == "string" or not dest) local name = dir.base_name(rock_file):match("(.*)%.[^.]*%.rock") - local rock_file, err, errcode = fetch_url_at_temp_dir(rock_file,"luarocks-rock-"..name) + local rock_file, err, errcode = fetch.fetch_url_at_temp_dir(rock_file,"luarocks-rock-"..name) if not rock_file then return nil, "Could not fetch rock file: " .. err, errcode end @@ -104,15 +149,19 @@ function fetch_and_unpack_rock(rock_file, dest) local unpack_dir if dest then unpack_dir = dest - fs.make_dir(unpack_dir) + local ok, err = fs.make_dir(unpack_dir) + if not ok then + return nil, "Failed unpacking rock file: " .. err + end else unpack_dir = fs.make_temp_dir(name) end if not dest then util.schedule_function(fs.delete, unpack_dir) end - fs.change_dir(unpack_dir) - local ok = fs.unzip(rock_file) + local ok, err = fs.change_dir(unpack_dir) + if not ok then return nil, err end + ok = fs.unzip(rock_file) if not ok then return nil, "Failed unpacking rock file: " .. rock_file end @@ -120,7 +169,7 @@ function fetch_and_unpack_rock(rock_file, dest) return unpack_dir end -function url_to_base_dir(url) +function fetch.url_to_base_dir(url) local base = dir.base_name(url) return base:gsub("%.[^.]*$", ""):gsub("%.tar$", "") end @@ -128,19 +177,28 @@ end --- Back-end function that actually loads the local rockspec. -- Performs some validation and postprocessing of the rockspec contents. -- @param filename string: The local filename of the rockspec file. +-- @param quick boolean: if true, skips some steps when loading +-- rockspec. -- @return table or (nil, string): A table representing the rockspec -- or nil followed by an error message. -function load_local_rockspec(filename) +function fetch.load_local_rockspec(filename, quick) assert(type(filename) == "string") filename = fs.absolute_name(filename) local rockspec, err = persist.load_into_table(filename) if not rockspec then return nil, "Could not load rockspec file "..filename.." ("..err..")" end + if cfg.branch and (type(rockspec.source) == "table") then + rockspec.source.branch = cfg.branch + end + local globals = err - local ok, err = type_check.type_check_rockspec(rockspec) - if not ok then - return nil, filename..": "..err + local ok, err = true, nil + if not quick then + ok, err = type_check.type_check_rockspec(rockspec, globals) + if not ok then + return nil, filename..": "..err + end end if rockspec.rockspec_format then @@ -166,7 +224,7 @@ function load_local_rockspec(filename) end local protocol, pathname = dir.split_url(rockspec.source.url) - if protocol == "http" or protocol == "https" or protocol == "ftp" or protocol == "file" then + if fetch.is_basic_protocol(protocol) then rockspec.source.file = rockspec.source.file or dir.base_name(rockspec.source.url) end rockspec.source.protocol, rockspec.source.pathname = protocol, pathname @@ -182,10 +240,10 @@ function load_local_rockspec(filename) rockspec.local_filename = filename local filebase = rockspec.source.file or rockspec.source.url - local base = url_to_base_dir(filebase) + 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 ".") or base if rockspec.dependencies then for i = 1, #rockspec.dependencies do @@ -198,9 +256,8 @@ function load_local_rockspec(filename) else rockspec.dependencies = {} end - local ok, err = path.configure_paths(rockspec) - if err then - return nil, "Error verifying paths: "..err + if not quick then + path.configure_paths(rockspec) end return rockspec @@ -215,7 +272,7 @@ end -- a temporary dir is created. -- @return table or (nil, string, [string]): A table representing the rockspec -- or nil followed by an error message and optional error code. -function load_rockspec(filename, location) +function fetch.load_rockspec(filename, location) assert(type(filename) == "string") local name @@ -224,35 +281,37 @@ function load_rockspec(filename, location) name = "rockspec" else name = basename:match("(.*)%.rockspec") - if not name and not basename == "rockspec" then + if not name then return nil, "Filename '"..filename.."' does not look like a rockspec." end end local err, errcode if location then - fs.change_dir(location) - filename, err = fetch_url(filename) + local ok, err = fs.change_dir(location) + if not ok then return nil, err end + filename, err = fetch.fetch_url(filename) fs.pop_dir() else - filename, err, errcode = fetch_url_at_temp_dir(filename,"luarocks-rockspec-"..name) + filename, err, errcode = fetch.fetch_url_at_temp_dir(filename,"luarocks-rockspec-"..name) end if not filename then return nil, err, errcode end - return load_local_rockspec(filename) + return fetch.load_local_rockspec(filename) end --- Download sources for building a rock using the basic URL downloader. -- @param rockspec table: The rockspec table -- @param extract boolean: Whether to extract the sources from -- the fetched source tarball or not. --- @param dest_dir string or nil: If set, will extract to the given directory. +-- @param dest_dir string or nil: If set, will extract to the given directory; +-- if not given, will extract to a temporary directory. -- @return (string, string) or (nil, string, [string]): The absolute pathname of -- the fetched source tarball and the temporary directory created to -- store it; or nil and an error message and optional error code. -function get_sources(rockspec, extract, dest_dir) +function fetch.get_sources(rockspec, extract, dest_dir) assert(type(rockspec) == "table") assert(type(extract) == "boolean") assert(type(dest_dir) == "string" or not dest_dir) @@ -262,26 +321,28 @@ function get_sources(rockspec, extract, dest_dir) local filename = rockspec.source.file local source_file, store_dir, err, errcode if dest_dir then - fs.change_dir(dest_dir) - source_file, err, errcode = fetch_url(url, filename) + local ok, err = fs.change_dir(dest_dir) + if not ok then return nil, err, "dest_dir" end + source_file, err, errcode = fetch.fetch_url(url, filename) fs.pop_dir() store_dir = dest_dir else - source_file, store_dir, errcode = fetch_url_at_temp_dir(url, "luarocks-source-"..name, filename) + source_file, store_dir, errcode = fetch.fetch_url_at_temp_dir(url, "luarocks-source-"..name, filename) end if not source_file then return nil, err or store_dir, errcode end if rockspec.source.md5 then if not fs.check_md5(source_file, rockspec.source.md5) then - return nil, "MD5 check for "..filename.." has failed." + return nil, "MD5 check for "..filename.." has failed.", "md5" end end if extract then - fs.change_dir(store_dir) + local ok, err = fs.change_dir(store_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 + return nil, "Directory "..rockspec.source.dir.." not found inside archive "..rockspec.source.file, "source.dir", source_file, store_dir end fs.pop_dir() end @@ -293,17 +354,18 @@ end -- @param extract boolean: When downloading compressed formats, whether to extract -- the sources from the fetched archive or not. -- @param dest_dir string or nil: If set, will extract to the given directory. +-- if not given, will extract to a temporary directory. -- @return (string, string) or (nil, string): The absolute pathname of -- the fetched source tarball and the temporary directory created to -- store it; or nil and an error message. -function fetch_sources(rockspec, extract, dest_dir) +function fetch.fetch_sources(rockspec, extract, dest_dir) assert(type(rockspec) == "table") assert(type(extract) == "boolean") assert(type(dest_dir) == "string" or not dest_dir) local protocol = rockspec.source.protocol local ok, proto - if protocol == "http" or protocol == "https" or protocol == "ftp" or protocol == "file" then + if fetch.is_basic_protocol(protocol) then proto = require("luarocks.fetch") else ok, proto = pcall(require, "luarocks.fetch."..protocol:gsub("[+-]", "_")) @@ -323,3 +385,5 @@ function fetch_sources(rockspec, extract, dest_dir) end return proto.get_sources(rockspec, extract, dest_dir) end + +return fetch diff --git a/luarocks/src/luarocks/fetch/cvs.lua b/luarocks/src/luarocks/fetch/cvs.lua index a622cdc..cc9fd65 100644 --- a/luarocks/src/luarocks/fetch/cvs.lua +++ b/luarocks/src/luarocks/fetch/cvs.lua @@ -1,6 +1,9 @@ --- Fetch back-end for retrieving sources from CVS. -module("luarocks.fetch.cvs", package.seeall) +--module("luarocks.fetch.cvs", package.seeall) +local cvs = {} + +local unpack = unpack or table.unpack local fs = require("luarocks.fs") local dir = require("luarocks.dir") @@ -13,7 +16,7 @@ local util = require("luarocks.util") -- @return (string, string) or (nil, string): The absolute pathname of -- the fetched source tarball and the temporary directory created to -- store it; or nil and an error message. -function get_sources(rockspec, extract, dest_dir) +function cvs.get_sources(rockspec, extract, dest_dir) assert(type(rockspec) == "table") assert(type(dest_dir) == "string" or not dest_dir) @@ -34,7 +37,8 @@ function get_sources(rockspec, extract, dest_dir) else store_dir = dest_dir end - fs.change_dir(store_dir) + local ok, err = fs.change_dir(store_dir) + if not ok then return nil, err end if not fs.execute(unpack(command)) then return nil, "Failed fetching files from CVS." end @@ -42,3 +46,5 @@ function get_sources(rockspec, extract, dest_dir) return module, store_dir end + +return cvs diff --git a/luarocks/src/luarocks/fetch/git.lua b/luarocks/src/luarocks/fetch/git.lua index e7c8943..53fd444 100644 --- a/luarocks/src/luarocks/fetch/git.lua +++ b/luarocks/src/luarocks/fetch/git.lua @@ -1,6 +1,9 @@ --- Fetch back-end for retrieving sources from GIT. -module("luarocks.fetch.git", package.seeall) +--module("luarocks.fetch.git", package.seeall) +local git = {} + +local unpack = unpack or table.unpack local fs = require("luarocks.fs") local dir = require("luarocks.dir") @@ -11,13 +14,13 @@ local util = require("luarocks.util") -- clone by tag then we'll have to issue a subsequent command to check out the -- given tag. -- @return boolean: Whether Git can clone by tag. -local function git_can_clone_by_tag() - local version_string = io.popen('git --version'):read() +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 git_can_clone_by_tag() + return value end --- Download sources for building a rock, using git. @@ -27,7 +30,7 @@ end -- @return (string, string) or (nil, string): The absolute pathname of -- the fetched source tarball and the temporary directory created to -- store it; or nil and an error message. -function get_sources(rockspec, extract, dest_dir) +function git.get_sources(rockspec, extract, dest_dir, depth) assert(type(rockspec) == "table") assert(type(dest_dir) == "string" or not dest_dir) @@ -48,15 +51,16 @@ function get_sources(rockspec, extract, dest_dir) store_dir = dest_dir end store_dir = fs.absolute_name(store_dir) - fs.change_dir(store_dir) + local ok, err = fs.change_dir(store_dir) + if not ok then return nil, err end - local command = {git_cmd, "clone", "--depth=1", rockspec.source.url, module} + local command = {fs.Q(git_cmd), "clone", depth or "--depth=1", rockspec.source.url, module} local tag_or_branch = rockspec.source.tag or rockspec.source.branch -- If the tag or branch is explicitly set to "master" in the rockspec, then -- we can avoid passing it to Git since it's the default. if tag_or_branch == "master" then tag_or_branch = nil end if tag_or_branch then - if git_can_clone_by_tag() then + if git_can_clone_by_tag(git_cmd) then -- The argument to `--branch` can actually be a branch or a tag as of -- Git 1.7.10. table.insert(command, 4, "--branch=" .. tag_or_branch) @@ -65,9 +69,10 @@ function get_sources(rockspec, extract, dest_dir) if not fs.execute(unpack(command)) then return nil, "Failed cloning git repository." end - fs.change_dir(module) + 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 = {git_cmd, "checkout", tag_or_branch} + local checkout_command = {fs.Q(git_cmd), "checkout", tag_or_branch} if not fs.execute(unpack(checkout_command)) then return nil, 'Failed to check out the "' .. tag_or_branch ..'" tag or branch.' end @@ -79,3 +84,5 @@ function get_sources(rockspec, extract, dest_dir) fs.pop_dir() return module, store_dir end + +return git diff --git a/luarocks/src/luarocks/fetch/git_file.lua b/luarocks/src/luarocks/fetch/git_file.lua index 1b18d0f..0144bc2 100644 --- a/luarocks/src/luarocks/fetch/git_file.lua +++ b/luarocks/src/luarocks/fetch/git_file.lua @@ -1,6 +1,7 @@ --- Fetch back-end for retrieving sources from local Git repositories. -module("luarocks.fetch.git_file", package.seeall) +--module("luarocks.fetch.git_file", package.seeall) +local git_file = {} local git = require("luarocks.fetch.git") @@ -11,7 +12,9 @@ local git = require("luarocks.fetch.git") -- @return (string, string) or (nil, string): The absolute pathname of -- the fetched source tarball and the temporary directory created to -- store it; or nil and an error message. -function get_sources(rockspec, extract, dest_dir) +function git_file.get_sources(rockspec, extract, dest_dir) rockspec.source.url = rockspec.source.url:gsub("^git.file://", "") return git.get_sources(rockspec, extract, dest_dir) end + +return git_file diff --git a/luarocks/src/luarocks/fetch/git_http.lua b/luarocks/src/luarocks/fetch/git_http.lua new file mode 100644 index 0000000..4ecd481 --- /dev/null +++ b/luarocks/src/luarocks/fetch/git_http.lua @@ -0,0 +1,27 @@ + +--- Fetch back-end for retrieving sources from Git repositories +-- that use http:// transport. For example, for fetching a repository +-- that requires the following command line: +-- `git clone http://example.com/foo.git` +-- you can use this in the rockspec: +-- source = { url = "git+http://example.com/foo.git" } +-- Prefer using the normal git:// fetch mode as it is more widely +-- available in older versions of LuaRocks. +--module("luarocks.fetch.git_http", package.seeall) +local git_http = {} + +local git = require("luarocks.fetch.git") + +--- Fetch sources for building a rock from a local Git repository. +-- @param rockspec table: The rockspec table +-- @param extract boolean: Unused in this module (required for API purposes.) +-- @param dest_dir string or nil: If set, will extract to the given directory. +-- @return (string, string) or (nil, string): The absolute pathname of +-- the fetched source tarball and the temporary directory created to +-- store it; or nil and an error message. +function git_http.get_sources(rockspec, extract, dest_dir) + rockspec.source.url = rockspec.source.url:gsub("^git.", "") + return git.get_sources(rockspec, extract, dest_dir, "--") +end + +return git_http diff --git a/luarocks/src/luarocks/fetch/hg.lua b/luarocks/src/luarocks/fetch/hg.lua index a08520a..b2ba56e 100644 --- a/luarocks/src/luarocks/fetch/hg.lua +++ b/luarocks/src/luarocks/fetch/hg.lua @@ -1,6 +1,9 @@ --- Fetch back-end for retrieving sources from HG. -module("luarocks.fetch.hg", package.seeall) +--module("luarocks.fetch.hg", package.seeall) +local hg = {} + +local unpack = unpack or table.unpack local fs = require("luarocks.fs") local dir = require("luarocks.dir") @@ -13,7 +16,7 @@ local util = require("luarocks.util") -- @return (string, string) or (nil, string): The absolute pathname of -- the fetched source tarball and the temporary directory created to -- store it; or nil and an error message. -function get_sources(rockspec, extract, dest_dir) +function hg.get_sources(rockspec, extract, dest_dir) assert(type(rockspec) == "table") assert(type(dest_dir) == "string" or not dest_dir) @@ -39,11 +42,13 @@ function get_sources(rockspec, extract, dest_dir) else store_dir = dest_dir end - fs.change_dir(store_dir) + local ok, err = fs.change_dir(store_dir) + if not ok then return nil, err end if not fs.execute(unpack(command)) then return nil, "Failed cloning hg repository." end - fs.change_dir(module) + ok, err = fs.change_dir(module) + if not ok then return nil, err end fs.delete(dir.path(store_dir, module, ".hg")) fs.delete(dir.path(store_dir, module, ".hgignore")) @@ -52,3 +57,5 @@ function get_sources(rockspec, extract, dest_dir) return module, store_dir end + +return hg diff --git a/luarocks/src/luarocks/fetch/sscm.lua b/luarocks/src/luarocks/fetch/sscm.lua index e52e801..53ae86a 100644 --- a/luarocks/src/luarocks/fetch/sscm.lua +++ b/luarocks/src/luarocks/fetch/sscm.lua @@ -1,6 +1,7 @@ --- Fetch back-end for retrieving sources from Surround SCM Server -module("luarocks.fetch.sscm", package.seeall) +--module("luarocks.fetch.sscm", package.seeall) +local sscm = {} local fs = require("luarocks.fs") local dir = require("luarocks.dir") @@ -12,7 +13,7 @@ local dir = require("luarocks.dir") -- @return (string, string) or (nil, string): The absolute pathname of -- the fetched source tarball and the temporary directory created to -- store it; or nil and an error message. -function get_sources(rockspec, extract, dest_dir) +function sscm.get_sources(rockspec, extract, dest_dir) assert(type(rockspec) == "table") assert(type(dest_dir) == "string" or not dest_dir) @@ -40,3 +41,5 @@ function get_sources(rockspec, extract, dest_dir) -- FIXME: This function does not honor the dest_dir parameter. return module, working_dir end + +return sscm diff --git a/luarocks/src/luarocks/fetch/svn.lua b/luarocks/src/luarocks/fetch/svn.lua index a4e952d..abeacf9 100644 --- a/luarocks/src/luarocks/fetch/svn.lua +++ b/luarocks/src/luarocks/fetch/svn.lua @@ -1,6 +1,9 @@ --- Fetch back-end for retrieving sources from Subversion. -module("luarocks.fetch.svn", package.seeall) +--module("luarocks.fetch.svn", package.seeall) +local svn = {} + +local unpack = unpack or table.unpack local fs = require("luarocks.fs") local dir = require("luarocks.dir") @@ -13,7 +16,7 @@ local util = require("luarocks.util") -- @return (string, string) or (nil, string): The absolute pathname of -- the fetched source tarball and the temporary directory created to -- store it; or nil and an error message. -function get_sources(rockspec, extract, dest_dir) +function svn.get_sources(rockspec, extract, dest_dir) assert(type(rockspec) == "table") assert(type(dest_dir) == "string" or not dest_dir) @@ -36,11 +39,13 @@ function get_sources(rockspec, extract, dest_dir) else store_dir = dest_dir end - fs.change_dir(store_dir) + local ok, err = fs.change_dir(store_dir) + if not ok then return nil, err end if not fs.execute(unpack(command)) then return nil, "Failed fetching files from Subversion." end - fs.change_dir(module) + ok, err = fs.change_dir(module) + if not ok then return nil, err end for _, d in ipairs(fs.find(".")) do if dir.base_name(d) == ".svn" then fs.delete(dir.path(store_dir, module, d)) @@ -51,3 +56,5 @@ function get_sources(rockspec, extract, dest_dir) return module, store_dir end + +return svn diff --git a/luarocks/src/luarocks/fs.lua b/luarocks/src/luarocks/fs.lua index 467b194..72e11c0 100644 --- a/luarocks/src/luarocks/fs.lua +++ b/luarocks/src/luarocks/fs.lua @@ -7,14 +7,45 @@ local pairs = pairs -module("luarocks.fs", package.seeall) +--module("luarocks.fs", package.seeall) +local fs = {} +package.loaded["luarocks.fs"] = fs local cfg = require("luarocks.cfg") +local pack = table.pack or function(...) return { n = select("#", ...), ... } end +local unpack = table.unpack or unpack + +local old_popen, old_exec +fs.verbose = function() -- patch io.popen and os.execute to display commands in verbose mode + if old_popen or old_exec then return end + old_popen = io.popen + io.popen = function(one, two) + if two == nil then + print("\nio.popen: ", one) + else + print("\nio.popen: ", one, "Mode:", two) + end + return old_popen(one, two) + end + + old_exec = os.execute + os.execute = function(cmd) + print("\nos.execute: ", cmd) + local code = pack(old_exec(cmd)) + print("Results: "..tostring(code.n)) + for i = 1,code.n do + print(" "..tostring(i).." ("..type(code[i]).."): "..tostring(code[i])) + end + return unpack(code, 1, code.n) + end +end +if cfg.verbose then fs.verbose() end + local function load_fns(fs_table) for name, fn in pairs(fs_table) do - if not _M[name] then - _M[name] = fn + if not fs[name] then + fs[name] = fn end end end @@ -38,3 +69,5 @@ load_fns(fs_lua) local ok, fs_plat_tools = pcall(require, "luarocks.fs."..loaded_platform..".tools") if ok and fs_plat_tools then load_fns(fs_plat_tools) end + +return fs diff --git a/luarocks/src/luarocks/fs/lua.lua b/luarocks/src/luarocks/fs/lua.lua index 36b3369..b261905 100644 --- a/luarocks/src/luarocks/fs/lua.lua +++ b/luarocks/src/luarocks/fs/lua.lua @@ -1,7 +1,8 @@ --- Native Lua implementation of filesystem and platform abstractions, -- using LuaFileSystem, LZLib, MD5 and LuaCurl. -module("luarocks.fs.lua", package.seeall) +-- module("luarocks.fs.lua") +local fs_lua = {} local fs = require("luarocks.fs") @@ -29,17 +30,17 @@ local dir_stack = {} math.randomseed(os.time()) -dir_separator = "/" +local dir_separator = "/" --- Quote argument for shell processing. -- Adds single quotes and escapes. -- @param arg string: Unquoted argument. -- @return string: Quoted argument. -function Q(arg) +function fs_lua.Q(arg) assert(type(arg) == "string") -- FIXME Unix-specific - return "'" .. arg:gsub("\\", "\\\\"):gsub("'", "'\\''") .. "'" + return "'" .. arg:gsub("'", "'\\''") .. "'" end --- Test is file/dir is writable. @@ -48,7 +49,7 @@ end -- for checking the result of subsequent operations. -- @param file string: filename to test -- @return boolean: true if file exists, false otherwise. -function is_writable(file) +function fs_lua.is_writable(file) assert(file) file = dir.normalize(file) local result @@ -69,17 +70,27 @@ end --- Create a temporary directory. -- @param name string: name pattern to use for avoiding conflicts -- when creating temporary directory. --- @return string or nil: name of temporary directory or nil on failure. -function make_temp_dir(name) +-- @return string or (nil, string): name of temporary directory or (nil, error message) on failure. +function fs_lua.make_temp_dir(name) assert(type(name) == "string") name = dir.normalize(name) local temp_dir = (os.getenv("TMP") or "/tmp") .. "/luarocks_" .. name:gsub(dir.separator, "_") .. "-" .. tostring(math.floor(math.random() * 10000)) - if fs.make_dir(temp_dir) then + local ok, err = fs.make_dir(temp_dir) + if ok then return temp_dir else - return nil + return nil, err + end +end + +local function quote_args(command, ...) + local out = { command } + for _, arg in ipairs({...}) do + assert(type(arg) == "string") + out[#out+1] = fs.Q(arg) end + return table.concat(out, " ") end --- Run the given command, quoting its arguments. @@ -89,34 +100,75 @@ end -- @param ... Strings containing additional arguments, which are quoted. -- @return boolean: true if command succeeds (status code 0), false -- otherwise. -function execute(command, ...) +function fs_lua.execute(command, ...) assert(type(command) == "string") + return fs.execute_string(quote_args(command, ...)) +end - for _, arg in ipairs({...}) do - assert(type(arg) == "string") - command = command .. " " .. fs.Q(arg) +--- Run the given command, quoting its arguments, silencing its output. +-- The command is executed in the current directory in the dir stack. +-- Silencing is omitted if 'verbose' mode is enabled. +-- @param command string: The command to be executed. No quoting/escaping +-- is applied. +-- @param ... Strings containing additional arguments, which will be quoted. +-- @return boolean: true if command succeeds (status code 0), false +-- otherwise. +function fs_lua.execute_quiet(command, ...) + assert(type(command) == "string") + if cfg.verbose then -- omit silencing output + return fs.execute_string(quote_args(command, ...)) + else + return fs.execute_string(fs.quiet(quote_args(command, ...))) end - return fs.execute_string(command) end --- Check the MD5 checksum for a file. -- @param file string: The file to be checked. -- @param md5sum string: The string with the expected MD5 checksum. --- @return boolean: true if the MD5 checksum for 'file' equals 'md5sum', false if not +-- @return boolean: true if the MD5 checksum for 'file' equals 'md5sum', false + msg if not -- or if it could not perform the check for any reason. -function check_md5(file, md5sum) +function fs_lua.check_md5(file, md5sum) file = dir.normalize(file) - local computed = fs.get_md5(file) + local computed, msg = fs.get_md5(file) if not computed then - return false + return false, msg end if computed:match("^"..md5sum) then return true else - return false + return false, "Mismatch MD5 hash for file "..file end end +--- List the contents of a directory. +-- @param at string or nil: directory to list (will be the current +-- directory if none is given). +-- @return table: an array of strings with the filenames representing +-- the contents of a directory. +function fs_lua.list_dir(at) + local result = {} + for file in fs.dir(at) do + result[#result+1] = file + end + return result +end + +--- Iterate over the contents of a directory. +-- @param at string or nil: directory to list (will be the current +-- directory if none is given). +-- @return function: an iterator function suitable for use with +-- the for statement. +function fs_lua.dir(at) + if not at then + at = fs.current_dir() + end + at = dir.normalize(at) + if not fs.is_dir(at) then + return function() end + end + return coroutine.wrap(function() fs.dir_iterator(at) end) +end + --------------------------------------------------------------------- -- LuaFileSystem functions --------------------------------------------------------------------- @@ -128,20 +180,15 @@ if lfs_ok then -- @param cmd string: No quoting/escaping is applied to the command. -- @return boolean: true if command succeeds (status code 0), false -- otherwise. -function execute_string(cmd) - if cfg.verbose then print("Executing: "..cmd) end +function fs_lua.execute_string(cmd) local code = os.execute(cmd) - if code == 0 or code == true then - return true - else - return false - end + return (code == 0 or code == true) end --- Obtain current directory. -- Uses the module's internal dir stack. -- @return string: the absolute pathname of the current directory. -function current_dir() +function fs_lua.current_dir() return lfs.currentdir() end @@ -150,23 +197,23 @@ end -- semantics of chdir, as it does not handle errors the same way, -- but works well for our purposes for now. -- @param d string: The directory to switch to. -function change_dir(d) +function fs_lua.change_dir(d) table.insert(dir_stack, lfs.currentdir()) d = dir.normalize(d) - lfs.chdir(d) + return lfs.chdir(d) end --- Change directory to root. -- Allows leaving a directory (e.g. for deleting it) in -- a crossplatform way. -function change_dir_to_root() +function fs_lua.change_dir_to_root() table.insert(dir_stack, lfs.currentdir()) lfs.chdir("/") -- works on Windows too end --- Change working directory to the previous in the dir stack. -- @return true if a pop ocurred, false if the stack was empty. -function pop_dir() +function fs_lua.pop_dir() local d = table.remove(dir_stack) if d then lfs.chdir(d) @@ -177,11 +224,11 @@ function pop_dir() end --- Create a directory if it does not already exist. --- If any of the higher levels in the path name does not exist +-- If any of the higher levels in the path name do not exist -- too, they are created as well. -- @param directory string: pathname of directory to create. --- @return boolean: true on success, false on failure. -function make_dir(directory) +-- @return boolean or (boolean, string): true on success or (false, error message) on failure. +function fs_lua.make_dir(directory) assert(type(directory) == "string") directory = dir.normalize(directory) local path = nil @@ -197,11 +244,12 @@ function make_dir(directory) path = path and path .. dir.separator .. d or d local mode = lfs.attributes(path, "mode") if not mode then - if not lfs.mkdir(path) then - return false + local ok, err = lfs.mkdir(path) + if not ok then + return false, err end elseif mode ~= "directory" then - return false + return false, path.." is not a directory" end end return true @@ -211,7 +259,7 @@ end -- Does not return errors (for example, if directory is not empty or -- if already does not exist) -- @param d string: pathname of directory to remove. -function remove_dir_if_empty(d) +function fs_lua.remove_dir_if_empty(d) assert(d) d = dir.normalize(d) lfs.rmdir(d) @@ -221,7 +269,7 @@ end -- Does not return errors (for example, if directory is not empty or -- if already does not exist) -- @param d string: pathname of directory to remove. -function remove_dir_tree_if_empty(d) +function fs_lua.remove_dir_tree_if_empty(d) assert(d) d = dir.normalize(d) for i=1,10 do @@ -237,7 +285,7 @@ end -- or nil to use the source filename permissions -- @return boolean or (boolean, string): true on success, false on failure, -- plus an error message. -function copy(src, dest, perms) +function fs_lua.copy(src, dest, perms) assert(src and dest) src = dir.normalize(src) dest = dir.normalize(dest) @@ -274,7 +322,8 @@ local function recursive_copy(src, dest) if not ok then return false end elseif srcmode == "directory" then local subdir = dir.path(dest, dir.base_name(src)) - fs.make_dir(subdir) + local ok, err = fs.make_dir(subdir) + if not ok then return nil, err end for file in lfs.dir(src) do if file ~= "." and file ~= ".." then local ok = recursive_copy(dir.path(src, file), subdir) @@ -290,7 +339,7 @@ end -- @param dest string: Pathname of destination -- @return boolean or (boolean, string): true on success, false on failure, -- plus an error message. -function copy_contents(src, dest) +function fs_lua.copy_contents(src, dest) assert(src and dest) src = dir.normalize(src) dest = dir.normalize(dest) @@ -313,11 +362,9 @@ end -- @return boolean or (boolean, string): true on success, -- or nil and an error message on failure. local function recursive_delete(name) - local mode = lfs.attributes(name, "mode") - - if mode == "file" then - return os.remove(name) - elseif mode == "directory" then + local ok = os.remove(name) + if ok then return true end + local pok, ok, err = pcall(function() for file in lfs.dir(name) do if file ~= "." and file ~= ".." then local ok, err = recursive_delete(dir.path(name, file)) @@ -325,40 +372,33 @@ local function recursive_delete(name) end end local ok, err = lfs.rmdir(name) - if not ok then return nil, err end + return ok, (not ok) and err + end) + if pok then + return ok, err + else + return pok, ok end - return true end --- Delete a file or a directory and all its contents. -- @param name string: Pathname of source --- @return boolean: true on success, false on failure. -function delete(name) +-- @return nil +function fs_lua.delete(name) name = dir.normalize(name) - return recursive_delete(name) or false + recursive_delete(name) end ---- List the contents of a directory. --- @param at string or nil: directory to list (will be the current --- directory if none is given). --- @return table: an array of strings with the filenames representing --- the contents of a directory. -function list_dir(at) - assert(type(at) == "string" or not at) - if not at then - at = fs.current_dir() - end - at = dir.normalize(at) - if not fs.is_dir(at) then - return {} - end - local result = {} +--- Internal implementation function for fs.dir. +-- Yields a filename on each iteration. +-- @param at string: directory to list +-- @return nil +function fs_lua.dir_iterator(at) for file in lfs.dir(at) do if file ~= "." and file ~= ".." then - table.insert(result, file) + coroutine.yield(file) end end - return result end --- Implementation function for recursive find. @@ -384,7 +424,7 @@ end -- directory if none is given). -- @return table: an array of strings with the filenames representing -- the contents of a directory. -function find(at) +function fs_lua.find(at) assert(type(at) == "string" or not at) if not at then at = fs.current_dir() @@ -401,7 +441,7 @@ end --- Test for existance of a file. -- @param file string: filename to test -- @return boolean: true if file exists, false otherwise. -function exists(file) +function fs_lua.exists(file) assert(file) file = dir.normalize(file) return type(lfs.attributes(file)) == "table" @@ -410,7 +450,7 @@ end --- Test is pathname is a directory. -- @param file string: pathname to test -- @return boolean: true if it is a directory, false otherwise. -function is_dir(file) +function fs_lua.is_dir(file) assert(file) file = dir.normalize(file) return lfs.attributes(file, "mode") == "directory" @@ -419,13 +459,13 @@ end --- Test is pathname is a regular file. -- @param file string: pathname to test -- @return boolean: true if it is a file, false otherwise. -function is_file(file) +function fs_lua.is_file(file) assert(file) file = dir.normalize(file) return lfs.attributes(file, "mode") == "file" end -function set_time(file, time) +function fs_lua.set_time(file, time) file = dir.normalize(file) return lfs.touch(file, time) end @@ -438,7 +478,7 @@ end if zip_ok then -function zip(zipfile, ...) +function fs_lua.zip(zipfile, ...) return lrzip.zip(zipfile, ...) end @@ -448,15 +488,24 @@ if unzip_ok then --- Uncompress files from a .zip archive. -- @param zipfile string: pathname of .zip archive to be extracted. -- @return boolean: true on success, false on failure. -function unzip(zipfile) +function fs_lua.unzip(zipfile) local zipfile, err = luazip.open(zipfile) if not zipfile then return nil, err end local files = zipfile:files() local file = files() repeat if file.filename:sub(#file.filename) == "/" then - fs.make_dir(dir.path(fs.current_dir(), file.filename)) + local ok, err = fs.make_dir(dir.path(fs.current_dir(), file.filename)) + if not ok then return nil, err end else + local base = dir.dir_name(file.filename) + if base ~= "" then + base = dir.path(fs.current_dir(), base) + if not fs.is_dir(base) then + local ok, err = fs.make_dir(base) + if not ok then return nil, err end + end + end local rf, err = zipfile:open(file.filename) if not rf then zipfile:close(); return nil, err end local contents = rf:read("*a") @@ -482,12 +531,13 @@ if socket_ok then local ltn12 = require("ltn12") local luasec_ok, https = pcall(require, "ssl.https") + local redirect_protocols = { http = http, https = luasec_ok and https, } -local function http_request(url, http, loop_control) +local function request(url, method, http, loop_control) local result = {} local proxy = cfg.proxy @@ -497,15 +547,36 @@ local function http_request(url, http, loop_control) proxy = "http://" .. proxy end + if cfg.show_downloads then + io.write(method.." "..url.." ...\n") + end + local dots = 0 + if cfg.connection_timeout and cfg.connection_timeout > 0 then + http.TIMEOUT = cfg.connection_timeout + end local res, status, headers, err = http.request { url = url, proxy = proxy, + method = method, redirect = false, sink = ltn12.sink.table(result), + step = cfg.show_downloads and function(...) + io.write(".") + io.flush() + dots = dots + 1 + if dots == 70 then + io.write("\n") + dots = 0 + end + return ltn12.pump.step(...) + end, headers = { ["user-agent"] = cfg.user_agent.." via LuaSocket" }, } + if cfg.show_downloads then + io.write("\n") + end if not res then return nil, status elseif status == 301 or status == 302 then @@ -519,46 +590,89 @@ local function http_request(url, http, loop_control) return nil, "Redirection loop -- broken URL?" end loop_control[url] = true - return http_request(location, redirect_protocols[protocol], loop_control) + return request(location, method, redirect_protocols[protocol], loop_control) else - return nil, "URL redirected to unsupported protocol - install luasec to get HTTPS support." + return nil, "URL redirected to unsupported protocol - install luasec to get HTTPS support.", "https" end end return nil, err elseif status ~= 200 then return nil, err else + return result, status, headers, err + end +end + +local function http_request(url, http, cached) + if cached then + local tsfd = io.open(cached..".timestamp", "r") + if tsfd then + local timestamp = tsfd:read("*a") + tsfd:close() + local result, status, headers, err = request(url, "HEAD", http) + if status == 200 and headers["last-modified"] == timestamp then + return true + end + if not result then + return nil, status, headers + end + end + end + local result, status, headers, err = request(url, "GET", http) + if result then + if cached and headers["last-modified"] then + local tsfd = io.open(cached..".timestamp", "w") + if tsfd then + tsfd:write(headers["last-modified"]) + tsfd:close() + end + end return table.concat(result) + else + return nil, status, headers end end +local downloader_warning = false + --- Download a remote file. -- @param url string: URL to be fetched. -- @param filename string or nil: this function attempts to detect the -- resulting local filename of the remote file as the basename of the URL; -- if that is not correct (due to a redirection, for example), the local -- filename can be given explicitly as this second argument. --- @return boolean: true on success, false on failure. -function download(url, filename) +-- @return (boolean, string): true and the filename on success, +-- false and the error message on failure. +function fs_lua.download(url, filename, cache) assert(type(url) == "string") assert(type(filename) == "string" or not filename) - filename = dir.path(fs.current_dir(), filename or dir.base_name(url)) + filename = fs.absolute_name(filename or dir.base_name(url)) - local content, err + local content, err, https_err if util.starts_with(url, "http:") then - content, err = http_request(url, http) + content, err, https_err = http_request(url, http, cache and filename) elseif util.starts_with(url, "ftp:") then content, err = ftp.get(url) elseif util.starts_with(url, "https:") then if luasec_ok then - content, err = http_request(url, https) + content, err = http_request(url, https, cache and filename) else - err = "Unsupported protocol - install luasec to get HTTPS support." + https_err = true end else err = "Unsupported protocol" end + if https_err then + if not downloader_warning then + util.printerr("Warning: falling back to "..cfg.downloader.." - install luasec to get native HTTPS support") + downloader_warning = true + end + return fs.use_downloader(url, filename, cache) + end + if cache and content == true then + return true, filename + end if not content then return false, tostring(err) end @@ -566,7 +680,13 @@ function download(url, filename) if not file then return false end file:write(content) file:close() - return true + return true, filename +end + +else --...if socket_ok == false then + +function fs_lua.download(url, filename, cache) + return fs.use_downloader(url, filename, cache) end end @@ -578,14 +698,15 @@ if md5_ok then --- Get the MD5 checksum for a file. -- @param file string: The file to be computed. --- @return string: The MD5 checksum -function get_md5(file) +-- @return string: The MD5 checksum or nil + error +function fs_lua.get_md5(file) file = fs.absolute_name(file) - local file = io.open(file, "rb") - if not file then return false end - local computed = md5.sumhexa(file:read("*a")) - file:close() - return computed + local file_handler = io.open(file, "rb") + if not file_handler then return nil, "Failed to open file for reading: "..file end + local computed = md5.sumhexa(file_handler:read("*a")) + file_handler:close() + if computed then return computed end + return nil, "Failed to compute MD5 hash for file "..file end end @@ -607,7 +728,7 @@ local octal_to_rwx = { ["7"] = "rwx", } -function chmod(file, mode) +function fs_lua.chmod(file, mode) -- LuaPosix (as of 5.1.15) does not support octal notation... if mode:sub(1,1) == "0" then local new_mode = {} @@ -620,7 +741,7 @@ function chmod(file, mode) return err == 0 end -function get_permissions(file) +function fs_lua.get_permissions(file) return posix.stat(file, "mode") end @@ -633,7 +754,7 @@ end --- Apply a patch. -- @param patchname string: The filename of the patch. -- @param patchdata string or nil: The actual patch as a string. -function apply_patch(patchname, patchdata) +function fs_lua.apply_patch(patchname, patchdata) local p, all_ok = patch.read_patch(patchname, patchdata) if not all_ok then return nil, "Failed reading patch "..patchname @@ -648,7 +769,7 @@ end -- @param dest string: Pathname of destination -- @return boolean or (boolean, string): true on success, false on failure, -- plus an error message. -function move(src, dest) +function fs_lua.move(src, dest) assert(src and dest) if fs.exists(dest) and not fs.is_dir(dest) then return false, "File already exists: "..dest @@ -657,8 +778,8 @@ function move(src, dest) if not ok then return false, err end - ok = fs.delete(src) - if not ok then + fs.delete(src) + if fs.exists(src) then return false, "Failed move: could not delete "..src.." after copy." end return true @@ -669,7 +790,7 @@ end -- @param flags table: the flags table passed to run() drivers. -- @return boolean or (boolean, string): true on success, false on failure, -- plus an error message. -function check_command_permissions(flags) +function fs_lua.check_command_permissions(flags) local root_dir = path.root_dir(cfg.rocks_dir) local ok = true local err = "" @@ -696,3 +817,18 @@ function check_command_permissions(flags) return nil, err end end + +--- Check whether a file is a Lua script +-- When the file can be succesfully compiled by the configured +-- Lua interpreter, it's considered to be a valid Lua file. +-- @param name filename of file to check +-- @return boolean true, if it is a Lua script, false otherwise +function fs_lua.is_lua(name) + name = name:gsub([[%\]],"/") -- normalize on fw slash to prevent escaping issues + local lua = fs.Q(dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)) -- get lua interpreter configured + -- execute on configured interpreter, might not be the same as the interpreter LR is run on + local result = fs.execute_string(lua..[[ -e "if loadfile(']]..name..[[') then os.exit() else os.exit(1) end"]]) + return (result == true) +end + +return fs_lua diff --git a/luarocks/src/luarocks/fs/unix.lua b/luarocks/src/luarocks/fs/unix.lua index cccbbd3..6ad5a67 100644 --- a/luarocks/src/luarocks/fs/unix.lua +++ b/luarocks/src/luarocks/fs/unix.lua @@ -1,16 +1,12 @@ --- Unix implementation of filesystem and platform abstractions. - -local assert, type, table, io, package, math, os, ipairs = - assert, type, table, io, package, math, os, ipairs - -module("luarocks.fs.unix", package.seeall) +--module("luarocks.fs.unix", package.seeall) +local unix = {} local fs = require("luarocks.fs") local cfg = require("luarocks.cfg") local dir = require("luarocks.dir") -local fs = require("luarocks.fs") local util = require("luarocks.util") math.randomseed(os.time()) @@ -18,7 +14,7 @@ math.randomseed(os.time()) --- Annotate command string for quiet execution. -- @param cmd string: A command-line string. -- @return string: The command-line, with silencing annotation. -function quiet(cmd) +function unix.quiet(cmd) return cmd.." 1> /dev/null 2> /dev/null" end @@ -28,7 +24,7 @@ end -- pathname absolute, or the current dir in the dir stack if -- not given. -- @return string: The pathname converted to absolute. -function absolute_name(pathname, relative_to) +function unix.absolute_name(pathname, relative_to) assert(type(pathname) == "string") assert(type(relative_to) == "string" or not relative_to) @@ -47,21 +43,22 @@ end -- @param version string: rock version to be used in loader context. -- @return boolean or (nil, string): True if succeeded, or nil and -- an error message. -function wrap_script(file, dest, name, version) +function unix.wrap_script(file, dest, name, version) assert(type(file) == "string") assert(type(dest) == "string") local base = dir.base_name(file) local wrapname = fs.is_dir(dest) and dest.."/"..base or dest + local lpath, lcpath = cfg.package_paths() local wrapper = io.open(wrapname, "w") if not wrapper then return nil, "Could not open "..wrapname.." for writing." end wrapper:write("#!/bin/sh\n\n") - wrapper:write('LUA_PATH="'..package.path..';$LUA_PATH"\n') - wrapper:write('LUA_CPATH="'..package.cpath..';$LUA_CPATH"\n') - wrapper:write('export LUA_PATH LUA_CPATH\n') - wrapper:write('exec "'..dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)..'" -lluarocks.loader -e\'luarocks.loader.add_context([['..name..']],[['..version..']])\' "'..file..'" "$@"\n') + local lua = dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter) + local ppaths = "package.path="..util.LQ(lpath..";").."..package.path; package.cpath="..util.LQ(lcpath..";").."..package.cpath" + local addctx = "local k,l,_=pcall(require,"..util.LQ("luarocks.loader")..") _=k and l.add_context("..util.LQ(name)..","..util.LQ(version)..")" + wrapper:write('exec '..fs.Q(lua)..' -e '..fs.Q(ppaths)..' -e '..fs.Q(addctx)..' '..fs.Q(file)..' "$@"\n') wrapper:close() if fs.chmod(wrapname, "0755") then return true @@ -75,7 +72,7 @@ end -- @param filename string: the file name with full path. -- @return boolean: returns true if file is an actual binary -- (or if it couldn't check) or false if it is a Lua wrapper. -function is_actual_binary(filename) +function unix.is_actual_binary(filename) if filename:match("%.lua$") then return false end @@ -92,7 +89,7 @@ function is_actual_binary(filename) return first ~= "#!" end -function copy_binary(filename, dest) +function unix.copy_binary(filename, dest) return fs.copy(filename, dest, "0755") end @@ -106,6 +103,12 @@ end -- which will replace old_file. -- @return boolean or (nil, string): True if succeeded, or nil and -- an error message. -function replace_file(old_file, new_file) +function unix.replace_file(old_file, new_file) return os.rename(new_file, old_file) end + +function unix.tmpname() + return os.tmpname() +end + +return unix diff --git a/luarocks/src/luarocks/fs/unix/tools.lua b/luarocks/src/luarocks/fs/unix/tools.lua index 3b853be..f36e815 100644 --- a/luarocks/src/luarocks/fs/unix/tools.lua +++ b/luarocks/src/luarocks/fs/unix/tools.lua @@ -1,6 +1,7 @@ --- fs operations implemented with third-party tools for Unix platform abstractions. -module("luarocks.fs.unix.tools", package.seeall) +--module("luarocks.fs.unix.tools", package.seeall) +local tools = {} local fs = require("luarocks.fs") local dir = require("luarocks.dir") @@ -17,10 +18,14 @@ end --- Obtain current directory. -- Uses the module's internal directory stack. -- @return string: the absolute pathname of the current directory. -function current_dir() - local pipe = io.popen(vars.PWD) - local current = pipe:read("*l") - pipe:close() +function tools.current_dir() + local current = cfg.cache_pwd + if not current then + local pipe = io.popen(fs.Q(vars.PWD)) + current = pipe:read("*l") + pipe:close() + cfg.cache_pwd = current + end for _, directory in ipairs(dir_stack) do current = fs.absolute_name(directory, current) end @@ -32,8 +37,8 @@ end -- @param cmd string: No quoting/escaping is applied to the command. -- @return boolean: true if command succeeds (status code 0), false -- otherwise. -function execute_string(cmd) - local code = os.execute(command_at(fs.current_dir(), cmd)) +function tools.execute_string(cmd) + local code, err = os.execute(command_at(fs.current_dir(), cmd)) if code == 0 or code == true then return true else @@ -46,20 +51,24 @@ end -- semantics of chdir, as it does not handle errors the same way, -- but works well for our purposes for now. -- @param directory string: The directory to switch to. -function change_dir(directory) +function tools.change_dir(directory) assert(type(directory) == "string") - table.insert(dir_stack, directory) + if fs.is_dir(directory) then + table.insert(dir_stack, directory) + return true + end + return nil, "directory not found: "..directory end --- Change directory to root. -- Allows leaving a directory (e.g. for deleting it) in -- a crossplatform way. -function change_dir_to_root() +function tools.change_dir_to_root() table.insert(dir_stack, "/") end --- Change working directory to the previous in the directory stack. -function pop_dir() +function tools.pop_dir() local directory = table.remove(dir_stack) return directory ~= nil end @@ -69,27 +78,31 @@ end -- too, they are created as well. -- @param directory string: pathname of directory to create. -- @return boolean: true on success, false on failure. -function make_dir(directory) +function tools.make_dir(directory) assert(directory) - return fs.execute(vars.MKDIR.." -p", directory) + local ok, err = fs.execute(vars.MKDIR.." -p", directory) + if not ok then + err = "failed making directory "..directory + end + return ok, err end --- Remove a directory if it is empty. -- Does not return errors (for example, if directory is not empty or -- if already does not exist) -- @param directory string: pathname of directory to remove. -function remove_dir_if_empty(directory) +function tools.remove_dir_if_empty(directory) assert(directory) - fs.execute_string(fs.quiet(vars.RMDIR.." "..fs.Q(directory))) + fs.execute_quiet(vars.RMDIR, directory) end --- Remove a directory if it is empty. -- Does not return errors (for example, if directory is not empty or -- if already does not exist) -- @param directory string: pathname of directory to remove. -function remove_dir_tree_if_empty(directory) +function tools.remove_dir_tree_if_empty(directory) assert(directory) - fs.execute_string(fs.quiet(vars.RMDIR.." -p "..fs.Q(directory))) + fs.execute_quiet(vars.RMDIR, "-p", directory) end --- Copy a file. @@ -98,7 +111,7 @@ end -- @param perm string or nil: Permissions for destination file, -- @return boolean or (boolean, string): true on success, false on failure, -- plus an error message. -function copy(src, dest, perm) +function tools.copy(src, dest, perm) assert(src and dest) if fs.execute(vars.CP, src, dest) then if perm then @@ -122,9 +135,9 @@ end -- @param dest string: Pathname of destination -- @return boolean or (boolean, string): true on success, false on failure, -- plus an error message. -function copy_contents(src, dest) +function tools.copy_contents(src, dest) assert(src and dest) - if fs.execute_string(fs.quiet(vars.CP.." -pPR "..fs.Q(src).."/* "..fs.Q(dest))) then + if fs.execute_quiet(vars.CP.." -pPR "..fs.Q(src).."/* "..fs.Q(dest)) then return true else return false, "Failed copying "..src.." to "..dest @@ -133,33 +146,25 @@ end --- Delete a file or a directory and all its contents. -- For safety, this only accepts absolute paths. -- @param arg string: Pathname of source --- @return boolean: true on success, false on failure. -function delete(arg) +-- @return nil +function tools.delete(arg) assert(arg) assert(arg:sub(1,1) == "/") - return fs.execute_string(fs.quiet(vars.RM.." -rf " .. fs.Q(arg))) + fs.execute_quiet(vars.RM, "-rf", arg) end ---- List the contents of a directory. --- @param at string or nil: directory to list (will be the current --- directory if none is given). --- @return table: an array of strings with the filenames representing --- the contents of a directory. -function list_dir(at) - assert(type(at) == "string" or not at) - if not at then - at = fs.current_dir() - end - if not fs.is_dir(at) then - return {} - end - local result = {} +--- Internal implementation function for fs.dir. +-- Yields a filename on each iteration. +-- @param at string: directory to list +-- @return nil +function tools.dir_iterator(at) local pipe = io.popen(command_at(at, vars.LS)) for file in pipe:lines() do - table.insert(result, file) + if file ~= "." and file ~= ".." then + coroutine.yield(file) + end end pipe:close() - return result end --- Recursively scan the contents of a directory. @@ -167,7 +172,7 @@ end -- directory if none is given). -- @return table: an array of strings with the filenames representing -- the contents of a directory. -function find(at) +function tools.find(at) assert(type(at) == "string" or not at) if not at then at = fs.current_dir() @@ -189,22 +194,22 @@ end -- @param ... Filenames to be stored in the archive are given as -- additional arguments. -- @return boolean: true on success, false on failure. -function zip(zipfile, ...) +function tools.zip(zipfile, ...) return fs.execute(vars.ZIP.." -r", zipfile, ...) end --- Uncompress files from a .zip archive. -- @param zipfile string: pathname of .zip archive to be extracted. -- @return boolean: true on success, false on failure. -function unzip(zipfile) +function tools.unzip(zipfile) assert(zipfile) - return fs.execute(vars.UNZIP, zipfile) + return fs.execute_quiet(vars.UNZIP, zipfile) end --- Test is file/directory exists -- @param file string: filename to test -- @return boolean: true if file exists, false otherwise. -function exists(file) +function tools.exists(file) assert(file) return fs.execute(vars.TEST, "-e", file) end @@ -212,7 +217,7 @@ end --- Test is pathname is a directory. -- @param file string: pathname to test -- @return boolean: true if it is a directory, false otherwise. -function is_dir(file) +function tools.is_dir(file) assert(file) return fs.execute(vars.TEST, "-d", file) end @@ -220,7 +225,7 @@ end --- Test is pathname is a regular file. -- @param file string: pathname to test -- @return boolean: true if it is a regular file, false otherwise. -function is_file(file) +function tools.is_file(file) assert(file) return fs.execute(vars.TEST, "-f", file) end @@ -231,25 +236,46 @@ end -- resulting local filename of the remote file as the basename of the URL; -- if that is not correct (due to a redirection, for example), the local -- filename can be given explicitly as this second argument. --- @return boolean: true on success, false on failure. -function download(url, filename) +-- @return (boolean, string): true and the filename on success, +-- false and the error message on failure. +function tools.use_downloader(url, filename, cache) assert(type(url) == "string") assert(type(filename) == "string" or not filename) + filename = fs.absolute_name(filename or dir.base_name(url)) + + local ok if cfg.downloader == "wget" then - local wget_cmd = vars.WGET.." --no-check-certificate --no-cache --user-agent='"..cfg.user_agent.." via wget' --quiet --continue " - if filename then - return fs.execute(wget_cmd.." --output-document ", filename, url) + local wget_cmd = fs.Q(vars.WGET).." --no-check-certificate --no-cache --user-agent='"..cfg.user_agent.." via wget' --quiet " + if cfg.connection_timeout and cfg.connection_timeout > 0 then + wget_cmd = wget_cmd .. "--timeout="..tonumber(cfg.connection_timeout).." --tries=1 " + end + if cache then + -- --timestamping is incompatible with --output-document, + -- but that's not a problem for our use cases. + fs.change_dir(dir.dir_name(filename)) + ok = fs.execute_quiet(wget_cmd.." --timestamping ", url) + fs.pop_dir() + elseif filename then + ok = fs.execute_quiet(wget_cmd.." --output-document ", filename, url) else - return fs.execute(wget_cmd, url) + ok = fs.execute_quiet(wget_cmd, url) end elseif cfg.downloader == "curl" then - filename = filename or dir.base_name(url) - return fs.execute_string(vars.CURL.." -L --user-agent '"..cfg.user_agent.." via curl' "..fs.Q(url).." 2> /dev/null 1> "..fs.Q(filename)) + local curl_cmd = fs.Q(vars.CURL).." -f -k -L --user-agent '"..cfg.user_agent.." via curl' " + if cfg.connection_timeout and cfg.connection_timeout > 0 then + curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." " + end + ok = fs.execute_string(curl_cmd..fs.Q(url).." 2> /dev/null 1> "..fs.Q(filename)) + end + if ok then + return true, filename + else + return false end end -function chmod(pathname, mode) +function tools.chmod(pathname, mode) if mode then return fs.execute(vars.CHMOD, mode, pathname) else @@ -259,7 +285,7 @@ end --- Apply a patch. -- @param patchname string: The filename of the patch. -function apply_patch(patchname) +function tools.apply_patch(patchname) return fs.execute(vars.PATCH.." -p1 -f -i ", patchname) end @@ -268,7 +294,7 @@ end -- filename extension. -- @param archive string: Filename of archive. -- @return boolean or (boolean, string): true on success, false and an error message on failure. -function unpack_archive(archive) +function tools.unpack_archive(archive) assert(type(archive) == "string") local ok @@ -300,19 +326,28 @@ local md5_cmd = { --- Get the MD5 checksum for a file. -- @param file string: The file to be computed. -- @return string: The MD5 checksum -function get_md5(file) +function tools.get_md5(file) local cmd = md5_cmd[cfg.md5checker] - if not cmd then return nil end - local pipe = io.popen(cmd.." "..fs.absolute_name(file)) + if not cmd then return nil, "no MD5 checker command configured" end + local pipe = io.popen(cmd.." "..fs.Q(fs.absolute_name(file))) local computed = pipe:read("*a") pipe:close() - if not computed then return nil end - return computed:match("("..("%x"):rep(32)..")") + if computed then + computed = computed:match("("..("%x"):rep(32)..")") + end + if computed then return computed end + return nil, "Failed to compute MD5 hash for file "..tostring(fs.absolute_name(file)) end -function get_permissions(filename) +function tools.get_permissions(filename) local pipe = io.popen(vars.STAT.." "..vars.STATFLAG.." "..fs.Q(filename)) local ret = pipe:read("*l") pipe:close() return ret end + +function tools.browser(url) + return fs.execute(cfg.web_browser, url) +end + +return tools diff --git a/luarocks/src/luarocks/fs/win32.lua b/luarocks/src/luarocks/fs/win32.lua index 2b5bb95..12d86d1 100644 --- a/luarocks/src/luarocks/fs/win32.lua +++ b/luarocks/src/luarocks/fs/win32.lua @@ -1,32 +1,84 @@ --- Windows implementation of filesystem and platform abstractions. -- Download http://unxutils.sourceforge.net/ for Windows GNU utilities -- used by this module. -module("luarocks.fs.win32", package.seeall) +--module("luarocks.fs.win32", package.seeall) +local win32 = {} local fs = require("luarocks.fs") local cfg = require("luarocks.cfg") local dir = require("luarocks.dir") +local util = require("luarocks.util") + +-- Monkey patch io.popen and os.execute to make sure quoting +-- works as expected. +-- See http://lua-users.org/lists/lua-l/2013-11/msg00367.html +local _prefix = "type NUL && " +local _popen, _execute = io.popen, os.execute +io.popen = function(cmd, ...) return _popen(_prefix..cmd, ...) end +os.execute = function(cmd, ...) return _execute(_prefix..cmd, ...) end + --- Annotate command string for quiet execution. -- @param cmd string: A command-line string. -- @return string: The command-line, with silencing annotation. -function quiet(cmd) +function win32.quiet(cmd) return cmd.." 2> NUL 1> NUL" end + +local win_escape_chars = { + ["%"] = "%%", + ['"'] = '\\"', +} + +local function q_escaper(bs, q) + return ("\\"):rep(2*#bs-1) .. (q or "\\") +end + +local function p_escaper(bs) + return bs .. bs .. '"%"' +end + --- Quote argument for shell processing. Fixes paths on Windows. --- Adds single quotes and escapes. +-- Adds double quotes and escapes. -- @param arg string: Unquoted argument. -- @return string: Quoted argument. -function Q(arg) +function win32.Q(arg) assert(type(arg) == "string") -- Quote DIR for Windows - if arg:match("^[%.a-zA-Z]?:?[\\/]") then - return '"' .. arg:gsub("/", "\\"):gsub('"', '\\"') .. '"' - end + if arg:match("^[%.a-zA-Z]?:?[\\/]") then + arg = arg:gsub("/", "\\") + end + if arg == "\\" then + return '\\' -- CHDIR needs special handling for root dir + end -- URLs and anything else - return '"' .. arg:gsub('"', '\\"') .. '"' + arg = arg:gsub('(\\+)(")', q_escaper) + arg = arg:gsub('(\\+)$', q_escaper) + arg = arg:gsub('"', win_escape_chars) + arg = arg:gsub('(\\*)%%', p_escaper) + return '"' .. arg .. '"' +end + +--- Quote argument for shell processing in batch files. +-- Adds double quotes and escapes. +-- @param arg string: Unquoted argument. +-- @return string: Quoted argument. +function win32.Qb(arg) + assert(type(arg) == "string") + -- Quote DIR for Windows + if arg:match("^[%.a-zA-Z]?:?[\\/]") then + arg = arg:gsub("/", "\\") + end + if arg == "\\" then + return '\\' -- CHDIR needs special handling for root dir + end + -- URLs and anything else + arg = arg:gsub('(\\+)(")', q_escaper) + arg = arg:gsub('(\\+)$', q_escaper) + arg = arg:gsub('[%%"]', win_escape_chars) + return '"' .. arg .. '"' end --- Return an absolute pathname from a potentially relative one. @@ -35,7 +87,7 @@ end -- pathname absolute, or the current dir in the dir stack if -- not given. -- @return string: The pathname converted to absolute. -function absolute_name(pathname, relative_to) +function win32.absolute_name(pathname, relative_to) assert(type(pathname) == "string") assert(type(relative_to) == "string" or not relative_to) @@ -56,28 +108,28 @@ end -- @param version string: rock version to be used in loader context. -- @return boolean or (nil, string): True if succeeded, or nil and -- an error message. -function wrap_script(file, dest, name, version) +function win32.wrap_script(file, dest, name, version) assert(type(file) == "string") assert(type(dest) == "string") local base = dir.base_name(file) local wrapname = fs.is_dir(dest) and dest.."/"..base or dest wrapname = wrapname..".bat" + local lpath, lcpath = cfg.package_paths() local wrapper = io.open(wrapname, "w") if not wrapper then return nil, "Could not open "..wrapname.." for writing." end wrapper:write("@echo off\n") - wrapper:write("setlocal\n") - wrapper:write('set LUA_PATH='..package.path..";%LUA_PATH%\n") - wrapper:write('set LUA_CPATH='..package.cpath..";%LUA_CPATH%\n") - wrapper:write('"'..dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)..'" -lluarocks.loader -e\'luarocks.loader.add_context([['..name..']],[['..version..']])\' "'..file..'" %*\n') - wrapper:write("endlocal\n") + local lua = dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter) + local ppaths = "package.path="..util.LQ(lpath..";").."..package.path; package.cpath="..util.LQ(lcpath..";").."..package.cpath" + local addctx = "local k,l,_=pcall(require,"..util.LQ("luarocks.loader")..") _=k and l.add_context("..util.LQ(name)..","..util.LQ(version)..")" + wrapper:write(fs.Qb(lua)..' -e '..fs.Qb(ppaths)..' -e '..fs.Qb(addctx)..' '..fs.Qb(file)..' %*\n') wrapper:close() return true end -function is_actual_binary(name) +function win32.is_actual_binary(name) name = name:lower() if name:match("%.bat$") or name:match("%.exe$") then return true @@ -85,14 +137,14 @@ function is_actual_binary(name) return false end -function copy_binary(filename, dest) +function win32.copy_binary(filename, dest) local ok, err = fs.copy(filename, dest) if not ok then return nil, err end local exe_pattern = "%.[Ee][Xx][Ee]$" local base = dir.base_name(filename) - local dest = dir.dir_name(dest) + dest = dir.dir_name(dest) if base:match(exe_pattern) then base = base:gsub(exe_pattern, ".lua") local helpname = dest.."/"..base @@ -107,11 +159,11 @@ function copy_binary(filename, dest) return true end -function chmod(filename, mode) +function win32.chmod(filename, mode) return true end -function get_permissions(filename) +function win32.get_permissions(filename) return "" end @@ -126,8 +178,44 @@ end -- which will replace old_file. -- @return boolean or (nil, string): True if succeeded, or nil and -- an error message. -function replace_file(old_file, new_file) +function win32.replace_file(old_file, new_file) os.remove(old_file) return os.rename(new_file, old_file) end +--- Test is file/dir is writable. +-- Warning: testing if a file/dir is writable does not guarantee +-- that it will remain writable and therefore it is no replacement +-- for checking the result of subsequent operations. +-- @param file string: filename to test +-- @return boolean: true if file exists, false otherwise. +function win32.is_writable(file) + assert(file) + file = dir.normalize(file) + local result + local tmpname = 'tmpluarockstestwritable.deleteme' + if fs.is_dir(file) then + local file2 = dir.path(file, tmpname) + local fh = io.open(file2, 'wb') + result = fh ~= nil + if fh then fh:close() end + if result then + -- the above test might give a false positive when writing to + -- c:\program files\ because of VirtualStore redirection on Vista and up + -- So check whether it's really there + result = fs.exists(file2) + end + os.remove(file2) + else + local fh = io.open(file, 'r+b') + result = fh ~= nil + if fh then fh:close() end + end + return result +end + +function win32.tmpname() + return os.getenv("TMP")..os.tmpname() +end + +return win32 diff --git a/luarocks/src/luarocks/fs/win32/tools.lua b/luarocks/src/luarocks/fs/win32/tools.lua index 1af1dd9..f970f36 100644 --- a/luarocks/src/luarocks/fs/win32/tools.lua +++ b/luarocks/src/luarocks/fs/win32/tools.lua @@ -2,7 +2,8 @@ --- fs operations implemented with third-party tools for Windows platform abstractions. -- Download http://unxutils.sourceforge.net/ for Windows GNU utilities -- used by this module. -module("luarocks.fs.win32.tools", package.seeall) +--module("luarocks.fs.win32.tools", package.seeall) +local tools = {} local fs = require("luarocks.fs") local dir = require("luarocks.dir") @@ -12,10 +13,6 @@ local dir_stack = {} local vars = cfg.variables -local function pack(...) - return { n = select("#", ...), ... } -end - --- Strip the last extension of a filename. -- Example: "foo.tar.gz" becomes "foo.tar". -- If filename has no dots, returns it unchanged. @@ -39,10 +36,14 @@ end --- Obtain current directory. -- Uses the module's internal directory stack. -- @return string: the absolute pathname of the current directory. -function current_dir() - local pipe = io.popen(vars.PWD) - local current = pipe:read("*l") - pipe:close() +function tools.current_dir() + local current = cfg.cache_pwd + if not current then + local pipe = io.popen(fs.Q(vars.PWD)) + current = pipe:read("*l") + pipe:close() + cfg.cache_pwd = current + end for _, directory in ipairs(dir_stack) do current = fs.absolute_name(directory, current) end @@ -54,18 +55,10 @@ end -- @param cmd string: No quoting/escaping is applied to the command. -- @return boolean: true if command succeeds (status code 0), false -- otherwise. -function execute_string(cmd) +function tools.execute_string(cmd) cmd = command_at(fs.current_dir(), cmd) - if cfg.verbose then print("Executing: "..tostring(cmd)) end - local code = pack(os.execute(cmd)) - if cfg.verbose then - print("Results: "..tostring(code.n)) - for i = 1,code.n do - print(" "..tostring(i).." ("..type(code[i]).."): "..tostring(code[i])) - end - print() - end - if code[1] == 0 or code[1] == true then + local code = os.execute(cmd) + if code == 0 or code == true then return true else return false @@ -77,20 +70,25 @@ end -- semantics of chdir, as it does not handle errors the same way, -- but works well for our purposes for now. -- @param directory string: The directory to switch to. -function change_dir(directory) +-- @return boolean or (nil, string): true if successful, (nil, error message) if failed. +function tools.change_dir(directory) assert(type(directory) == "string") - table.insert(dir_stack, directory) + if fs.is_dir(directory) then + table.insert(dir_stack, directory) + return true + end + return nil, "directory not found: "..directory end --- Change directory to root. -- Allows leaving a directory (e.g. for deleting it) in -- a crossplatform way. -function change_dir_to_root() +function tools.change_dir_to_root() table.insert(dir_stack, "/") end --- Change working directory to the previous in the directory stack. -function pop_dir() +function tools.pop_dir() local directory = table.remove(dir_stack) return directory ~= nil end @@ -100,28 +98,32 @@ end -- too, they are created as well. -- @param directory string: pathname of directory to create. -- @return boolean: true on success, false on failure. -function make_dir(directory) +function tools.make_dir(directory) assert(directory) - fs.execute(fs.quiet(vars.MKDIR.." "..fs.Q(directory))) - return 1 + directory = dir.normalize(directory) + fs.execute_quiet(fs.Q(vars.MKDIR).." -p ", directory) + if not fs.is_dir(directory) then + return false, "failed making directory "..directory + end + return true end --- Remove a directory if it is empty. -- Does not return errors (for example, if directory is not empty or -- if already does not exist) -- @param directory string: pathname of directory to remove. -function remove_dir_if_empty(directory) +function tools.remove_dir_if_empty(directory) assert(directory) - fs.execute_string(fs.quiet(vars.RMDIR.." "..fs.Q(directory))) + fs.execute_quiet(fs.Q(vars.RMDIR), directory) end --- Remove a directory if it is empty. -- Does not return errors (for example, if directory is not empty or -- if already does not exist) -- @param directory string: pathname of directory to remove. -function remove_dir_tree_if_empty(directory) +function tools.remove_dir_tree_if_empty(directory) assert(directory) - fs.execute_string(fs.quiet(vars.RMDIR.." "..fs.Q(directory))) + fs.execute_quiet(fs.Q(vars.RMDIR), directory) end --- Copy a file. @@ -129,10 +131,11 @@ end -- @param dest string: Pathname of destination -- @return boolean or (boolean, string): true on success, false on failure, -- plus an error message. -function copy(src, dest) +function tools.copy(src, dest) assert(src and dest) if dest:match("[/\\]$") then dest = dest:sub(1, -2) end - if fs.execute(vars.CP, src, dest) then + local ok = fs.execute(fs.Q(vars.CP), src, dest) + if ok then return true else return false, "Failed copying "..src.." to "..dest @@ -144,9 +147,9 @@ end -- @param dest string: Pathname of destination -- @return boolean or (boolean, string): true on success, false on failure, -- plus an error message. -function copy_contents(src, dest) +function tools.copy_contents(src, dest) assert(src and dest) - if fs.execute_string(fs.quiet(vars.CP.." -dR "..src.."\\*.* "..fs.Q(dest))) then + if fs.execute_quiet(fs.Q(vars.CP).." -dR "..src.."\\*.* "..fs.Q(dest)) then return true else return false, "Failed copying "..src.." to "..dest @@ -156,35 +159,25 @@ end --- Delete a file or a directory and all its contents. -- For safety, this only accepts absolute paths. -- @param arg string: Pathname of source --- @return boolean: true on success, false on failure. -function delete(arg) +-- @return nil +function tools.delete(arg) assert(arg) - assert(arg:match("^[\a-zA-Z]?:?[\\/]")) - fs.execute(vars.CHMOD.." a+rw -R ", arg) - return fs.execute_string(fs.quiet(vars.RM.." -rf " .. fs.Q(arg))) + assert(arg:match("^[a-zA-Z]?:?[\\/]")) + fs.execute_quiet("if exist "..fs.Q(arg.."\\").." ( RMDIR /S /Q "..fs.Q(arg).." ) else ( DEL /Q /F "..fs.Q(arg).." )") end ---- List the contents of a directory. --- @param at string or nil: directory to list (will be the current --- directory if none is given). --- @return table: an array of strings with the filenames representing --- the contents of a directory. -function list_dir(at) - assert(type(at) == "string" or not at) - if not at then - at = fs.current_dir() - end - if not fs.is_dir(at) then - return {} - end - local result = {} - local pipe = io.popen(command_at(at, vars.LS)) +--- Internal implementation function for fs.dir. +-- Yields a filename on each iteration. +-- @param at string: directory to list +-- @return nil +function tools.dir_iterator(at) + local pipe = io.popen(command_at(at, fs.Q(vars.LS))) for file in pipe:lines() do - table.insert(result, file) + if file ~= "." and file ~= ".." then + coroutine.yield(file) + end end pipe:close() - - return result end --- Recursively scan the contents of a directory. @@ -192,7 +185,7 @@ end -- directory if none is given). -- @return table: an array of strings with the filenames representing -- the contents of a directory. Paths are returned with forward slashes. -function find(at) +function tools.find(at) assert(type(at) == "string" or not at) if not at then at = fs.current_dir() @@ -201,7 +194,7 @@ function find(at) return {} end local result = {} - local pipe = io.popen(command_at(at, vars.FIND.." 2> NUL")) + local pipe = io.popen(command_at(at, fs.Q(vars.FIND).." 2> NUL")) for file in pipe:lines() do -- Windows find is a bit different local first_two = file:sub(1,2) @@ -219,32 +212,32 @@ end -- @param ... Filenames to be stored in the archive are given as -- additional arguments. -- @return boolean: true on success, false on failure. -function zip(zipfile, ...) - return fs.execute(vars.SEVENZ.." a -tzip", zipfile, ...) +function tools.zip(zipfile, ...) + return fs.execute_quiet(fs.Q(vars.SEVENZ).." -aoa a -tzip", zipfile, ...) end --- Uncompress files from a .zip archive. -- @param zipfile string: pathname of .zip archive to be extracted. -- @return boolean: true on success, false on failure. -function unzip(zipfile) +function tools.unzip(zipfile) assert(zipfile) - return fs.execute(vars.SEVENZ.." x", zipfile) + return fs.execute_quiet(fs.Q(vars.SEVENZ).." -aoa x", zipfile) end --- Test is pathname is a directory. -- @param file string: pathname to test -- @return boolean: true if it is a directory, false otherwise. -function is_dir(file) +function tools.is_dir(file) assert(file) - return fs.execute(fs.quiet(vars.TEST.." -d " .. fs.Q(file))) + return fs.execute_quiet("if not exist " .. fs.Q(file.."\\").." invalidcommandname") end --- Test is pathname is a regular file. -- @param file string: pathname to test -- @return boolean: true if it is a regular file, false otherwise. -function is_file(file) +function tools.is_file(file) assert(file) - return fs.execute(vars.TEST.." -f", file) + return fs.execute(fs.Q(vars.TEST).." -f", file) end --- Download a remote file. @@ -253,21 +246,42 @@ end -- resulting local filename of the remote file as the basename of the URL; -- if that is not correct (due to a redirection, for example), the local -- filename can be given explicitly as this second argument. --- @return boolean: true on success, false on failure. -function download(url, filename) +-- @return (boolean, string): true and the filename on success, +-- false and the error message on failure. +function tools.use_downloader(url, filename, cache) assert(type(url) == "string") assert(type(filename) == "string" or not filename) + filename = fs.absolute_name(filename or dir.base_name(url)) + + local ok if cfg.downloader == "wget" then - local wget_cmd = vars.WGET.." --no-check-certificate --no-cache --user-agent=\""..cfg.user_agent.." via wget\" --quiet --continue " - if filename then - return fs.execute(wget_cmd.." --output-document ", filename, url) + local wget_cmd = fs.Q(vars.WGET).." --no-check-certificate --no-cache --user-agent=\""..cfg.user_agent.." via wget\" --quiet " + if cfg.connection_timeout and cfg.connection_timeout > 0 then + wget_cmd = wget_cmd .. "--timeout="..tonumber(cfg.connection_timeout).." --tries=1 " + end + if cache then + -- --timestamping is incompatible with --output-document, + -- but that's not a problem for our use cases. + fs.change_dir(dir.dir_name(filename)) + ok = fs.execute_quiet(wget_cmd.." --timestamping ", url) + fs.pop_dir() + elseif filename then + ok = fs.execute_quiet(wget_cmd.." --output-document ", filename, url) else - return fs.execute(wget_cmd, url) + ok = fs.execute_quiet(wget_cmd, url) end elseif cfg.downloader == "curl" then - filename = filename or dir.base_name(url) - return fs.execute_string(vars.CURL.." -L --user-agent \""..cfg.user_agent.." via curl\" "..fs.Q(url).." 2> NUL 1> "..fs.Q(filename)) + local curl_cmd = vars.CURL.." -f -k -L --user-agent \""..cfg.user_agent.." via curl\" " + if cfg.connection_timeout and cfg.connection_timeout > 0 then + curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." " + end + ok = fs.execute_string(curl_cmd..fs.Q(url).." 2> NUL 1> "..fs.Q(filename)) + end + if ok then + return true, filename + else + return false end end @@ -275,7 +289,7 @@ end -- @param archive string: Filename of archive. -- @return boolean : success status local function gunzip(archive) - return fs.execute(vars.SEVENZ.." x", archive) + return fs.execute_quiet(fs.Q(vars.SEVENZ).." -aoa x", archive) end --- Unpack an archive. @@ -283,28 +297,28 @@ end -- filename extension. -- @param archive string: Filename of archive. -- @return boolean or (boolean, string): true on success, false and an error message on failure. -function unpack_archive(archive) +function tools.unpack_archive(archive) assert(type(archive) == "string") local ok - local sevenzx = vars.SEVENZ.." x" + local sevenzx = fs.Q(vars.SEVENZ).." -aoa x" if archive:match("%.tar%.gz$") then ok = gunzip(archive) if ok then - ok = fs.execute(sevenzx, strip_extension(archive)) + ok = fs.execute_quiet(sevenzx, strip_extension(archive)) end elseif archive:match("%.tgz$") then ok = gunzip(archive) if ok then - ok = fs.execute(sevenzx, strip_extension(archive)..".tar") + ok = fs.execute_quiet(sevenzx, strip_extension(archive)..".tar") end elseif archive:match("%.tar%.bz2$") then - ok = fs.execute(sevenzx, archive) + ok = fs.execute_quiet(sevenzx, archive) if ok then - ok = fs.execute(sevenzx, strip_extension(archive)) + ok = fs.execute_quiet(sevenzx, strip_extension(archive)) end elseif archive:match("%.zip$") then - ok = fs.execute(sevenzx, archive) + ok = fs.execute_quiet(sevenzx, archive) elseif archive:match("%.lua$") or archive:match("%.c$") then -- Ignore .lua and .c files; they don't need to be extracted. return true @@ -319,28 +333,37 @@ function unpack_archive(archive) end local md5_cmd = { - md5sum = vars.MD5SUM, - openssl = vars.OPENSSL.." md5", - md5 = vars.MD5, + md5sum = fs.Q(vars.MD5SUM), + openssl = fs.Q(vars.OPENSSL).." md5", + md5 = fs.Q(vars.MD5), } --- Get the MD5 checksum for a file. -- @param file string: The file to be computed. --- @return string: The MD5 checksum -function get_md5(file) +-- @return string: The MD5 checksum or nil + message +function tools.get_md5(file) local cmd = md5_cmd[cfg.md5checker] - if not cmd then return nil end - local pipe = io.popen(cmd.." "..fs.absolute_name(file)) + if not cmd then return nil, "no MD5 checker command configured" end + local pipe = io.popen(cmd.." "..fs.Q(fs.absolute_name(file))) local computed = pipe:read("*a") pipe:close() - if not computed then return nil end - return computed:match("("..("%x"):rep(32)..")") + if computed then + computed = computed:match("("..("%x"):rep(32)..")") + end + if computed then return computed end + return nil, "Failed to compute MD5 hash for file "..tostring(fs.absolute_name(file)) end --- Test for existance of a file. -- @param file string: filename to test -- @return boolean: true if file exists, false otherwise. -function exists(file) +function tools.exists(file) assert(file) - return fs.execute(fs.quiet("if not exist " .. fs.Q(file) .. " invalidcommandname")) + return fs.execute_quiet("if not exist " .. fs.Q(file) .. " invalidcommandname") +end + +function tools.browser(url) + return fs.execute(cfg.web_browser..' "Starting docs..." '..fs.Q(url)) end + +return tools diff --git a/luarocks/src/luarocks/help.lua b/luarocks/src/luarocks/help.lua index 2944dfd..0a15550 100644 --- a/luarocks/src/luarocks/help.lua +++ b/luarocks/src/luarocks/help.lua @@ -4,17 +4,19 @@ -- 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. -module("luarocks.help", package.seeall) +--module("luarocks.help", package.seeall) +local help = {} local util = require("luarocks.util") local cfg = require("luarocks.cfg") +local dir = require("luarocks.dir") local program = util.this_program("luarocks") -help_summary = "Help on commands. Type '"..program.." help <command>' for more." +help.help_summary = "Help on commands. Type '"..program.." help <command>' for more." -help_arguments = "[<command>]" -help = [[ +help.help_arguments = "[<command>]" +help.help = [[ <command> is the command to show help for. ]] @@ -41,7 +43,7 @@ end -- 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 run(...) +function help.run(...) local flags, command = util.parse_flags(...) if not command then @@ -63,40 +65,51 @@ function run(...) given URL. --tree=<tree> Which tree to operate on. --local Use the tree in the user's home directory. - To enable it, see ']]..program..[[ help path'.]]) + To enable it, see ']]..program..[[ help path'. + --verbose Display verbose output of commands executed. + --timeout=<seconds> Timeout on network operations, in seconds. + 0 means no timeout (wait forever). + Default is ]]..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") - local names = {} - for name, command in pairs(commands) do - table.insert(names, name) - end - table.sort(names) - for _, name in ipairs(names) do - local command = commands[name] + for name, command in util.sortedpairs(commands) do + local cmd = require(command) util.printout("", name) - util.printout("\t", command.help_summary) + util.printout("\t", cmd.help_summary) end print_section("CONFIGURATION") - util.printout("\tSystem configuration file: ".. sys_file .. " (" .. get_status(sys_ok) ..")") + util.printout("\tLua version: " .. cfg.lua_version) + util.printout("\tConfiguration files:") + util.printout("\t\tSystem: ".. dir.normalize(sys_file) .. " (" .. get_status(sys_ok) ..")") if home_file then - util.printout("\tUser configuration file: ".. home_file .. " (" .. get_status(home_ok) ..")\n") + util.printout("\t\tUser : ".. dir.normalize(home_file) .. " (" .. get_status(home_ok) ..")\n") else - util.printout("\tUser configuration file disabled in this LuaRocks installation.\n") + 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("-", "_") - if commands[command] then - local arguments = commands[command].help_arguments or "<argument>" + local cmd = require(commands[command]) + if cmd then + local arguments = cmd.help_arguments or "<argument>" print_banner() print_section("NAME") - util.printout("\t"..program.." "..command.." - "..commands[command].help_summary) + util.printout("\t"..program.." "..command.." - "..cmd.help_summary) print_section("SYNOPSIS") util.printout("\t"..program.." "..command.." "..arguments) print_section("DESCRIPTION") - util.printout("",(commands[command].help:gsub("\n","\n\t"):gsub("\n\t$",""))) + 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 @@ -105,3 +118,5 @@ function run(...) end return true end + +return help diff --git a/luarocks/src/luarocks/index.lua b/luarocks/src/luarocks/index.lua index e1391cc..116bdfd 100644 --- a/luarocks/src/luarocks/index.lua +++ b/luarocks/src/luarocks/index.lua @@ -1,6 +1,8 @@ --- Module which builds the index.html page to be used in rocks servers. -module("luarocks.index", package.seeall) +--module("luarocks.index", package.seeall) +local index = {} +package.loaded["luarocks.index"] = index local util = require("luarocks.util") local fs = require("luarocks.fs") @@ -27,6 +29,9 @@ a { color: #0000c0; text-decoration: none; } +a.pkg { + color: black; +} a:hover { text-decoration: underline; } @@ -63,9 +68,9 @@ Lua modules available from this location for use with <a href="http://www.luaroc <table class="main"> ]] -local index_package_start = [[ +local index_package_begin = [[ <td class="package"> -<p><a name="$anchor"></a><b>$package</b> - $summary<br/> +<p><a name="$anchor"></a><a href="#$anchor" class="pkg"><b>$package</b></a> - $summary<br/> </p><blockquote><p>$detailed<br/> $externaldependencies <font size="-1"><a href="$original">latest sources</a> $homepage | License: $license</font></p> @@ -78,16 +83,21 @@ local index_package_end = [[ <tr><td colspan="2" class="spacer"></td></tr> ]] -local index_footer = [[ +local index_footer_begin = [[ </table> <p class="manifest"> -<a href="manifest">manifest file</a> • <a href="manifest-5.1">Lua 5.1 manifest file</a> • <a href="manifest-5.2">Lua 5.2 manifest file</a> +<a href="manifest">manifest file</a> +]] +local index_manifest_ver = [[ +• <a href="manifest-$VER">Lua $VER manifest file</a> (<a href="manifest-$VER.zip">zip</a>) +]] +local index_footer_end = [[ </p> </body> </html> ]] -function format_external_dependencies(rockspec) +function index.format_external_dependencies(rockspec) if rockspec.external_dependencies then local deplist = {} local listed_set = {} @@ -115,7 +125,7 @@ function format_external_dependencies(rockspec) end end -function make_index(repo) +function index.make_index(repo) if not fs.is_dir(repo) then return nil, "Cannot access repository at "..repo end @@ -125,21 +135,20 @@ function make_index(repo) out:write(index_header) for package, version_list in util.sortedpairs(manifest.repository) do local latest_rockspec = nil - local output = index_package_start + local output = index_package_begin for version, data in util.sortedpairs(version_list, deps.compare_versions) do local versions = {} output = output..version..': ' table.sort(data, function(a,b) return a.arch < b.arch end) for _, item in ipairs(data) do - local link = '<a href="$url">'..item.arch..'</a>' + local file if item.arch == 'rockspec' then - local rs = ("%s-%s.rockspec"):format(package, version) - if not latest_rockspec then latest_rockspec = rs end - link = link:gsub("$url", rs) + file = ("%s-%s.rockspec"):format(package, version) + if not latest_rockspec then latest_rockspec = file end else - link = link:gsub("$url", ("%s-%s.%s.rock"):format(package, version, item.arch)) + file = ("%s-%s.%s.rock"):format(package, version, item.arch) end - table.insert(versions, link) + table.insert(versions, '<a href="'..file..'">'..item.arch..'</a>') end output = output .. table.concat(versions, ', ') .. '<br/>' end @@ -155,7 +164,7 @@ function make_index(repo) detailed = descript.detailed or "", license = descript.license or "N/A", homepage = descript.homepage and ('| <a href="'..descript.homepage..'"'..ext_url_target..'>project homepage</a>') or "", - externaldependencies = format_external_dependencies(rockspec) + externaldependencies = index.format_external_dependencies(rockspec) } vars.detailed = vars.detailed:gsub("\n\n", "</p><p>"):gsub("%s+", " ") vars.detailed = vars.detailed:gsub("(https?://[a-zA-Z0-9%.%%-_%+%[%]=%?&/$@;:]+)", '<a href="%1"'..ext_url_target..'>%1</a>') @@ -167,6 +176,12 @@ function make_index(repo) end out:write(output) end - out:write(index_footer) + out:write(index_footer_begin) + for ver in util.lua_versions() do + out:write((index_manifest_ver:gsub("$VER", ver))) + end + out:write(index_footer_end) out:close() end + +return index diff --git a/luarocks/src/luarocks/install.lua b/luarocks/src/luarocks/install.lua index c181d61..7678c0c 100644 --- a/luarocks/src/luarocks/install.lua +++ b/luarocks/src/luarocks/install.lua @@ -1,7 +1,8 @@ - --- Module implementing the LuaRocks "install" command. -- Installs binary rocks. -module("luarocks.install", package.seeall) +--module("luarocks.install", package.seeall) +local install = {} +package.loaded["luarocks.install"] = install local path = require("luarocks.path") local repos = require("luarocks.repos") @@ -13,11 +14,11 @@ local manif = require("luarocks.manif") local remove = require("luarocks.remove") local cfg = require("luarocks.cfg") -help_summary = "Install a rock." +install.help_summary = "Install a rock." -help_arguments = "{<rock>|<name> [<version>]}" +install.help_arguments = "{<rock>|<name> [<version>]}" -help = [[ +install.help = [[ Argument may be the name of a rock to be fetched from a repository or a filename of a locally available rock. @@ -25,7 +26,8 @@ or a filename of a locally available rock. rock after installing a new one. This behavior can be made permanent by setting keep_other_versions=true in the configuration file. -]] +]]..util.deps_mode_help() + --- Install a binary rock. -- @param rock_file string: local or remote filename of a rock. @@ -34,7 +36,7 @@ or a filename of a locally available rock. -- "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_binary_rock(rock_file, deps_mode) +function install.install_binary_rock(rock_file, deps_mode) assert(type(rock_file) == "string") local name, version, arch = path.parse_name(rock_file) @@ -80,11 +82,6 @@ function install_binary_rock(rock_file, deps_mode) if err then return nil, err, errcode end end - local wrap_bin_scripts = true - if rockspec.deploy and rockspec.deploy.wrap_bin_scripts == false then - wrap_bin_scripts = false - end - ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec)) if err then return nil, err end @@ -120,23 +117,23 @@ end -- 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): True if installation was --- successful, nil and an error message otherwise. -function run(...) +-- @return boolean or (nil, string, exitcode): True if installation was +-- successful, nil and an error message otherwise. exitcode is optionally returned. +function install.run(...) local flags, name, version = util.parse_flags(...) 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 end + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end if name:match("%.rockspec$") or name:match("%.src%.rock$") then util.printout("Using "..name.."... switching to 'build' mode") local build = require("luarocks.build") return build.run(name, util.forward_flags(flags, "local", "keep", "deps-mode")) elseif name:match("%.rock$") then - ok, err = install_binary_rock(name, deps.get_deps_mode(flags)) + ok, err = install.install_binary_rock(name, 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 @@ -152,7 +149,7 @@ function run(...) elseif type(results) == "string" then local url = results util.printout("Installing "..url.."...") - return run(url, util.forward_flags(flags)) + return install.run(url, util.forward_flags(flags)) else util.printout() util.printerr("Could not determine which rock to install.") @@ -162,3 +159,5 @@ function run(...) end end end + +return install diff --git a/luarocks/src/luarocks/lint.lua b/luarocks/src/luarocks/lint.lua index 62ced64..091c8de 100644 --- a/luarocks/src/luarocks/lint.lua +++ b/luarocks/src/luarocks/lint.lua @@ -1,22 +1,24 @@ --- Module implementing the LuaRocks "lint" command. -- Utility function that checks syntax of the rockspec. -module("luarocks.lint", package.seeall) +--module("luarocks.lint", package.seeall) +local lint = {} +package.loaded["luarocks.lint"] = lint local util = require("luarocks.util") local download = require("luarocks.download") local fetch = require("luarocks.fetch") -help_summary = "Check syntax of a rockspec." -help_arguments = "<rockspec>" -help = [[ +lint.help_summary = "Check syntax of a rockspec." +lint.help_arguments = "<rockspec>" +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 run(...) +function lint.run(...) local flags, input = util.parse_flags(...) if not input then @@ -37,5 +39,19 @@ function run(...) return nil, "Failed loading rockspec: "..err end - return true + 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/luarocks/src/luarocks/list.lua b/luarocks/src/luarocks/list.lua index 6081ed4..319909d 100644 --- a/luarocks/src/luarocks/list.lua +++ b/luarocks/src/luarocks/list.lua @@ -1,16 +1,18 @@ --- Module implementing the LuaRocks "list" command. -- Lists currently installed rocks. -module("luarocks.list", package.seeall) +--module("luarocks.list", package.seeall) +local list = {} +package.loaded["luarocks.list"] = list local search = require("luarocks.search") local cfg = require("luarocks.cfg") local util = require("luarocks.util") local path = require("luarocks.path") -help_summary = "Lists currently installed rocks." -help_arguments = "[--porcelain] <filter>" -help = [[ +list.help_summary = "Lists currently installed rocks." +list.help_arguments = "[--porcelain] <filter>" +list.help = [[ <filter> is a substring of a rock name to filter by. --porcelain Produce machine-friendly output. @@ -20,7 +22,7 @@ help = [[ -- @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 run(...) +function list.run(...) local flags, filter, version = util.parse_flags(...) local results = {} local query = search.make_query(filter and filter:lower() or "", version) @@ -36,3 +38,5 @@ function run(...) search.print_results(results, flags["porcelain"]) return true end + +return list diff --git a/luarocks/src/luarocks/loader.lua b/luarocks/src/luarocks/loader.lua index a116766..1eaa672 100644 --- a/luarocks/src/luarocks/loader.lua +++ b/luarocks/src/luarocks/loader.lua @@ -5,23 +5,28 @@ -- table in the environment, which records which versions of packages were -- used to load previous modules, so that the loader chooses versions -- that are declared to be compatible with the ones loaded earlier. -local global_env = _G -local package, require, ipairs, pairs, table, type, next, unpack = - package, require, ipairs, pairs, table, type, next, unpack +local loaders = package.loaders or package.searchers +local package, require, ipairs, pairs, table, type, next, tostring, error = + package, require, ipairs, pairs, table, type, next, tostring, error +local unpack = unpack or table.unpack -module("luarocks.loader") +--module("luarocks.loader") +local loader = {} +package.loaded["luarocks.loader"] = loader + +local cfg = require("luarocks.cfg") +cfg.init_package_paths() local path = require("luarocks.path") local manif_core = require("luarocks.manif_core") local deps = require("luarocks.deps") -local cfg = require("luarocks.cfg") -context = {} +loader.context = {} -- Contains a table when rocks trees are loaded, -- or 'false' to indicate rocks trees failed to load. -- 'nil' indicates rocks trees were not attempted to be loaded yet. -rocks_trees = nil +loader.rocks_trees = nil local function load_rocks_trees() local any_ok = false @@ -34,10 +39,10 @@ local function load_rocks_trees() end end if not any_ok then - rocks_trees = false + loader.rocks_trees = false return false end - rocks_trees = trees + loader.rocks_trees = trees return true end @@ -45,21 +50,20 @@ end -- chain for loading modules. -- @param name string: The name of an installed rock. -- @param version string: The version of the rock, in string format -function add_context(name, version) +function loader.add_context(name, version) -- assert(type(name) == "string") -- assert(type(version) == "string") - if context[name] then + if loader.context[name] then return end - context[name] = version + loader.context[name] = version - if not rocks_trees and not load_rocks_trees() then + if not loader.rocks_trees and not load_rocks_trees() then return nil end - local providers = {} - for _, tree in ipairs(rocks_trees) do + for _, tree in ipairs(loader.rocks_trees) do local manifest = tree.manifest local pkgdeps @@ -72,12 +76,12 @@ function add_context(name, version) for _, dep in ipairs(pkgdeps) do local pkg, constraints = dep.name, dep.constraints - for _, tree in ipairs(rocks_trees) do + for _, tree in ipairs(loader.rocks_trees) do local entries = tree.manifest.repository[pkg] if entries then for version, pkgs in pairs(entries) do if (not constraints) or deps.match_constraints(deps.parse_version(version), constraints) then - add_context(pkg, version) + loader.add_context(pkg, version) end end end @@ -108,9 +112,9 @@ end -- @return table or (nil, string): The module table as returned by some other loader, -- or nil followed by an error message if no other loader managed to load the module. local function call_other_loaders(module, name, version, module_name) - for i, loader in pairs(package.loaders) do - if loader ~= luarocks_loader then - local results = { loader(module_name) } + for i, a_loader in ipairs(loaders) do + if a_loader ~= loader.luarocks_loader then + local results = { a_loader(module_name) } if type(results[1]) == "function" then return unpack(results) end @@ -126,34 +130,35 @@ end -- (eg "luasocket"), the version (eg "2.0.2-1"), the path of the rocks tree -- (eg "/usr/local"), and the numeric index of the matching entry, so the -- filter function can know if the matching module was the first entry or not. --- @return string, string, string: name of the rock containing the module --- (eg. "luasocket"), version of the rock (eg. "2.0.2-1"), --- name of the module (eg. "socket.core", or "socket.core_2_0_2" if file is --- stored versioned). +-- @return string, string, string, (string or table): +-- * name of the rock containing the module (eg. "luasocket") +-- * version of the rock (eg. "2.0.2-1") +-- * name of the module (eg. "socket.core", or "socket.core_2_0_2" if file is stored versioned). +-- * tree of the module (string or table in `rocks_trees` format) local function select_module(module, filter_module_name) --assert(type(module) == "string") --assert(type(filter_module_name) == "function") - if not rocks_trees and not load_rocks_trees() then + if not loader.rocks_trees and not load_rocks_trees() then return nil end local providers = {} - for _, tree in ipairs(rocks_trees) do + for _, tree in ipairs(loader.rocks_trees) do local entries = tree.manifest.modules[module] if entries then for i, entry in ipairs(entries) do local name, version = entry:match("^([^/]*)/(.*)$") local module_name = tree.manifest.repository[name][version][1].modules[module] if type(module_name) ~= "string" then - error("Invalid format in manifest file (invalid data for "..tostring(name).." "..tostring(version)..")") + error("Invalid data in manifest file for module "..tostring(module).." (invalid data for "..tostring(name).." "..tostring(version)..")") end module_name = filter_module_name(module_name, name, version, tree.tree, i) - if context[name] == version then + if loader.context[name] == version then return name, version, module_name end version = deps.parse_version(version) - table.insert(providers, {name = name, version = version, module_name = module_name}) + table.insert(providers, {name = name, version = version, module_name = module_name, tree = tree}) end end end @@ -161,16 +166,17 @@ local function select_module(module, filter_module_name) if next(providers) then table.sort(providers, sort_versions) local first = providers[1] - return first.name, first.version.string, first.module_name + return first.name, first.version.string, first.module_name, first.tree end end --- Search for a module -- @param module string: module name (eg. "socket.core") --- @return string, string, string: name of the rock containing the module --- (eg. "luasocket"), version of the rock (eg. "2.0.2-1"), --- name of the module (eg. "socket.core", or "socket.core_2_0_2" if file is --- stored versioned). +-- @return string, string, string, (string or table): +-- * name of the rock containing the module (eg. "luasocket") +-- * version of the rock (eg. "2.0.2-1") +-- * name of the module (eg. "socket.core", or "socket.core_2_0_2" if file is stored versioned). +-- * tree of the module (string or table in `rocks_trees` format) local function pick_module(module) return select_module(module, function(module_name, name, version, tree, i) @@ -185,7 +191,7 @@ end --- Return the pathname of the file that would be loaded for a module. -- @param module string: module name (eg. "socket.core") -- @return string: filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so") -function which(module) +function loader.which(module) local name, version, module_name = select_module(module, path.which_i) return module_name end @@ -199,14 +205,16 @@ end -- @return table: The module table (typically), like in plain -- require(). See <a href="http://www.lua.org/manual/5.1/manual.html#pdf-require">require()</a> -- in the Lua reference manual for details. -function luarocks_loader(module) +function loader.luarocks_loader(module) local name, version, module_name = pick_module(module) if not name then return "No LuaRocks module found for "..module else - add_context(name, version) + loader.add_context(name, version) return call_other_loaders(module, name, version, module_name) end end -table.insert(global_env.package.loaders, 1, luarocks_loader) +table.insert(loaders, 1, loader.luarocks_loader) + +return loader diff --git a/luarocks/src/luarocks/make.lua b/luarocks/src/luarocks/make.lua index eef49d0..1dfe647 100644 --- a/luarocks/src/luarocks/make.lua +++ b/luarocks/src/luarocks/make.lua @@ -3,7 +3,9 @@ -- Builds sources in the current directory, but unlike "build", -- it does not fetch sources, etc., assuming everything is -- available in the current directory. -module("luarocks.make", package.seeall) +--module("luarocks.make", package.seeall) +local make = {} +package.loaded["luarocks.make"] = make local build = require("luarocks.build") local fs = require("luarocks.fs") @@ -14,9 +16,9 @@ local pack = require("luarocks.pack") local remove = require("luarocks.remove") local deps = require("luarocks.deps") -help_summary = "Compile package in current directory using a rockspec." -help_arguments = "[--pack-binary-rock] [<rockspec>]" -help = [[ +make.help_summary = "Compile package in current directory using a rockspec." +make.help_arguments = "[--pack-binary-rock] [<rockspec>]" +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, @@ -36,19 +38,22 @@ To install rocks, you'll normally want to use the "install" and be made permanent by setting keep_other_versions=true in the configuration file. +--branch=<name> 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): True if build was successful; nil and an --- error message otherwise. -function run(...) +-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an +-- error message otherwise. exitcode is optionally returned. +function make.run(...) local flags, rockspec = util.parse_flags(...) assert(type(rockspec) == "string" or not rockspec) if not rockspec then - local files = fs.list_dir(fs.current_dir()) - for _, file in pairs(files) do + for file in fs.dir() do if file:match("rockspec$") then if rockspec then return nil, "Please specify which rockspec file to use." @@ -73,7 +78,7 @@ function run(...) 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 end + 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 @@ -84,3 +89,5 @@ function run(...) return name, version end end + +return make diff --git a/luarocks/src/luarocks/make_manifest.lua b/luarocks/src/luarocks/make_manifest.lua index a698f83..b6e65bf 100644 --- a/luarocks/src/luarocks/make_manifest.lua +++ b/luarocks/src/luarocks/make_manifest.lua @@ -1,17 +1,21 @@ --- Module implementing the luarocks-admin "make_manifest" command. -- Compile a manifest file for a repository. -module("luarocks.make_manifest", package.seeall) +--module("luarocks.make_manifest", package.seeall) +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") -help_summary = "Compile a manifest file for a repository." +make_manifest.help_summary = "Compile a manifest file for a repository." -help = [[ +make_manifest.help = [[ <argument>, if given, is a local repository pathname. --local-tree If given, do not write versioned versions of the manifest file. @@ -23,7 +27,7 @@ help = [[ -- 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 run(...) +function make_manifest.run(...) local flags, repo = util.parse_flags(...) assert(type(repo) == "string" or not repo) @@ -31,10 +35,21 @@ function run(...) 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/luarocks/src/luarocks/manif.lua b/luarocks/src/luarocks/manif.lua index c17da71..f53f8dc 100644 --- a/luarocks/src/luarocks/manif.lua +++ b/luarocks/src/luarocks/manif.lua @@ -1,9 +1,10 @@ - --- Module for handling manifest files and tables. -- Manifest files describe the contents of a LuaRocks tree or server. -- They are loaded into manifest tables, which are then used for -- performing searches, matching dependencies, etc. -module("luarocks.manif", package.seeall) +--module("luarocks.manif", package.seeall) +local manif = {} +package.loaded["luarocks.manif"] = manif local manif_core = require("luarocks.manif_core") local persist = require("luarocks.persist") @@ -17,7 +18,7 @@ local path = require("luarocks.path") local repos = require("luarocks.repos") local deps = require("luarocks.deps") -rock_manifest_cache = {} +manif.rock_manifest_cache = {} --- Commit a table to disk in given local path. -- @param where string: The directory where the table should be saved. @@ -38,22 +39,22 @@ local function save_table(where, name, tbl) return ok, err end -function load_rock_manifest(name, version, root) +function manif.load_rock_manifest(name, version, root) assert(type(name) == "string") assert(type(version) == "string") local name_version = name.."/"..version - if rock_manifest_cache[name_version] then - return rock_manifest_cache[name_version].rock_manifest + if manif.rock_manifest_cache[name_version] then + return manif.rock_manifest_cache[name_version].rock_manifest 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 - rock_manifest_cache[name_version] = rock_manifest + manif.rock_manifest_cache[name_version] = rock_manifest return rock_manifest.rock_manifest end -function make_rock_manifest(name, version) +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 = {} @@ -73,18 +74,27 @@ function make_rock_manifest(name, version) walk = next end if fs.is_file(full_path) then - last[last_name] = fs.get_md5(full_path) + 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 } - rock_manifest_cache[name.."/"..version] = rock_manifest + 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("[/:]","_") - local file, err, errcode = fetch.fetch_url_at_temp_dir(url, "luarocks-manifest-"..name) + local cache_dir = dir.path(cfg.local_cache, name) + local ok = fs.make_dir(cache_dir) + if not ok then + return nil, "Failed creating temporary cache directory "..cache_dir + end + local file, err, errcode = fetch.fetch_url(url, dir.path(cache_dir, filename), true) if not file then return nil, "Failed fetching manifest for "..repo_url..(err and " - "..err or ""), errcode end @@ -97,32 +107,54 @@ end -- @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 optional error code. -function load_manifest(repo_url) +function manif.load_manifest(repo_url) assert(type(repo_url) == "string") if manif_core.manifest_cache[repo_url] then return manif_core.manifest_cache[repo_url] end - - local vmanifest = "manifest-"..cfg.lua_version - - local protocol, pathname = dir.split_url(repo_url) + + local filenames = { + "manifest-"..cfg.lua_version..".zip", + "manifest-"..cfg.lua_version, + "manifest", + } + + local protocol, repodir = dir.split_url(repo_url) + local pathname if protocol == "file" then - local file = dir.path(pathname, vmanifest) - if fs.exists(file) then - pathname = file - else - pathname = dir.path(pathname, "manifest") + for _, filename in ipairs(filenames) do + pathname = dir.path(repodir, filename) + if fs.exists(pathname) then + break + end end else - local file, err = fetch_manifest_from(repo_url, vmanifest) - if not file then - file, err = fetch_manifest_from(repo_url, "manifest") + local err, errcode + for _, filename in ipairs(filenames) do + pathname, err, errcode = fetch_manifest_from(repo_url, filename) + if pathname then + break + end end - if not file then - return nil, err + if not pathname then + return nil, err, errcode + end + end + if pathname:match(".*%.zip$") then + pathname = fs.absolute_name(pathname) + local dir = dir.dir_name(pathname) + fs.change_dir(dir) + local nozip = pathname:match("(.*)%.zip$") + fs.delete(nozip) + local ok = fs.unzip(pathname) + fs.pop_dir() + if not ok then + fs.delete(pathname) + fs.delete(pathname..".timestamp") + return nil, "Failed extracting manifest file" end - pathname = file + pathname = nozip end return manif_core.manifest_loader(pathname, repo_url) end @@ -206,18 +238,11 @@ 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 no trees. --- @param repodir string: directory of repository being scanned --- @param filter_lua string or nil: filter by Lua version --- @param cache table: temporary rockspec cache table -local function update_dependencies(manifest, deps_mode, repodir, filter_lua, cache) +local function update_dependencies(manifest, deps_mode) assert(type(manifest) == "table") assert(type(deps_mode) == "string") - cache = cache or {} - local lua_version = filter_lua and deps.parse_version(filter_lua) - for pkg, versions in pairs(manifest.repository) do - local to_remove = {} for version, repositories in pairs(versions) do local current = pkg.." "..version for _, repo in ipairs(repositories) do @@ -229,16 +254,39 @@ local function update_dependencies(manifest, deps_mode, repodir, filter_lua, cac for miss, err in pairs(missing) do if miss == current then util.printerr("Tree inconsistency detected: "..current.." has no rockspec. "..err) - else + elseif deps_mode ~= "none" then util.printerr("Missing dependency for "..pkg.." "..version..": "..miss) end end end - elseif filter_lua and repo.arch == "rockspec" then + 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) + rockspec, err = fetch.load_local_rockspec(pathname, true) end if rockspec then cache[pathname] = rockspec @@ -258,9 +306,9 @@ local function update_dependencies(manifest, deps_mode, repodir, filter_lua, cac end if next(to_remove) then for _, incompat in ipairs(to_remove) do - manifest.repository[pkg][incompat] = nil + versions[incompat] = nil end - if not next(manifest.repository[pkg]) then + if not next(versions) then manifest.repository[pkg] = nil end end @@ -271,17 +319,12 @@ 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 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 repo string: directory of repository --- @param filter_lua string or nil: filter by Lua version --- @param cache table: temporary rockspec cache table +-- @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, deps_mode, repo, filter_lua, cache) +local function store_results(results, manifest, dep_handler) assert(type(results) == "table") assert(type(manifest) == "table") - assert(type(deps_mode) == "string") + assert((not dep_handler) or type(dep_handler) == "function") for name, versions in pairs(results) do local pkgtable = manifest.repository[name] or {} @@ -291,7 +334,7 @@ local function store_results(results, manifest, deps_mode, repo, filter_lua, cac local entrytable = {} entrytable.arch = entry.arch if entry.arch == "installed" then - local rock_manifest = load_rock_manifest(name, version) + 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 @@ -304,7 +347,9 @@ local function store_results(results, manifest, deps_mode, repo, filter_lua, cac end manifest.repository[name] = pkgtable end - update_dependencies(manifest, deps_mode, repo, filter_lua, cache) + if dep_handler then + dep_handler(manifest) + end sort_package_matching_table(manifest.modules) sort_package_matching_table(manifest.commands) return true @@ -320,7 +365,7 @@ end -- @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 make_manifest(repo, deps_mode, versioned) +function manif.make_manifest(repo, deps_mode, remote) assert(type(repo) == "string") assert(type(deps_mode) == "string") @@ -338,14 +383,23 @@ function make_manifest(repo, deps_mode, versioned) manif_core.manifest_cache[repo] = manifest - local cache = {} - local ok, err = store_results(results, manifest, deps_mode, repo, nil, cache) + 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 versioned then - for _, luaver in ipairs({"5.1", "5.2"}) do + if remote then + local cache = {} + for luaver in util.lua_versions() do local vmanifest = { repository = {}, modules = {}, commands = {} } - local ok, err = store_results(results, vmanifest, deps_mode, repo, luaver, cache) + 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 @@ -360,13 +414,13 @@ end -- @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 configured as cfg.rocks_dir is used. +-- 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 update_manifest(name, version, repo, deps_mode) +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) @@ -376,14 +430,14 @@ function update_manifest(name, version, repo, deps_mode) util.printout("Updating manifest for "..repo) - local manifest, err = load_manifest(repo) + local manifest, err = manif.load_manifest(repo) if not manifest then util.printerr("No existing manifest. Attempting to rebuild...") - local ok, err = make_manifest(repo, deps_mode) + local ok, err = manif.make_manifest(repo, deps_mode) if not ok then return nil, err end - manifest, err = load_manifest(repo) + manifest, err = manif.load_manifest(repo) if not manifest then return nil, err end @@ -391,19 +445,31 @@ function update_manifest(name, version, repo, deps_mode) local results = {[name] = {[version] = {{arch = "installed", repo = repo}}}} - local ok, err = store_results(results, manifest, deps_mode, 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 +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)) if not manifest then - return nil, err .. " -- corrupted local rocks tree?" + return nil, "untracked" end local deploy_bin = path.deploy_bin_dir(root) local deploy_lua = path.deploy_lua_dir(root) @@ -435,13 +501,13 @@ end -- @param file string: The full path of a deployed file. -- @param root string or nil: A local root dir for a rocks tree. If not given, the default is used. -- @return string, string: name and version of the provider rock. -function find_current_provider(file, root) +function manif.find_current_provider(file, root) local providers, err = find_providers(file, root) if not providers then return nil, err end return providers[1]:match("([^/]*)/([^/]*)") end -function find_next_provider(file, root) +function manif.find_next_provider(file, root) local providers, err = find_providers(file, root) if not providers then return nil, err end if providers[2] then @@ -450,3 +516,5 @@ function find_next_provider(file, root) return nil end end + +return manif diff --git a/luarocks/src/luarocks/manif_core.lua b/luarocks/src/luarocks/manif_core.lua index 6424a1e..1a2c111 100644 --- a/luarocks/src/luarocks/manif_core.lua +++ b/luarocks/src/luarocks/manif_core.lua @@ -1,7 +1,9 @@ --- Core functions for querying manifest files. -- This module requires no specific 'fs' functionality. -module("luarocks.manif_core", package.seeall) +--module("luarocks.manif_core", package.seeall) +local manif_core = {} +package.loaded["luarocks.manif_core"] = manif_core local persist = require("luarocks.persist") local type_check = require("luarocks.type_check") @@ -10,26 +12,27 @@ local util = require("luarocks.util") local cfg = require("luarocks.cfg") local path = require("luarocks.path") -manifest_cache = {} +manif_core.manifest_cache = {} --- 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 quick boolean: If given, skips type checking. -function manifest_loader(file, repo_url, quick) +function manif_core.manifest_loader(file, repo_url, quick) local manifest, err = persist.load_into_table(file) if not manifest then return nil, "Failed loading manifest for "..repo_url..": "..err end + local globals = err if not quick then - local ok, err = type_check.type_check_manifest(manifest) + local ok, err = type_check.type_check_manifest(manifest, globals) if not ok then return nil, "Error checking manifest: "..err end end - manifest_cache[repo_url] = manifest + manif_core.manifest_cache[repo_url] = manifest return manifest end @@ -39,16 +42,16 @@ end -- @param repo_url string: URL or pathname for the repository. -- @return table or (nil, string): A table representing the manifest, -- or nil followed by an error message. -function load_local_manifest(repo_url) +function manif_core.load_local_manifest(repo_url) assert(type(repo_url) == "string") - if manifest_cache[repo_url] then - return manifest_cache[repo_url] + if manif_core.manifest_cache[repo_url] then + return manif_core.manifest_cache[repo_url] end local pathname = dir.path(repo_url, "manifest") - return manifest_loader(pathname, repo_url, true) + return manif_core.manifest_loader(pathname, repo_url, true) end --- Get all versions of a package listed in a manifest file. @@ -59,13 +62,13 @@ end -- or "all", to use all trees. -- @return table: An array of strings listing installed -- versions of a package. -function get_versions(name, deps_mode) +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 = load_local_manifest(path.rocks_dir(tree)) + local loaded = manif_core.load_local_manifest(path.rocks_dir(tree)) if loaded then util.deep_merge(manifest, loaded) end @@ -77,3 +80,5 @@ function get_versions(name, deps_mode) end return {} end + +return manif_core diff --git a/luarocks/src/luarocks/new_version.lua b/luarocks/src/luarocks/new_version.lua index cb17032..9ef0cfb 100644 --- a/luarocks/src/luarocks/new_version.lua +++ b/luarocks/src/luarocks/new_version.lua @@ -1,19 +1,20 @@ --- Module implementing the LuaRocks "new_version" command. -- Utility function that writes a new rockspec, updating data from a previous one. -module("luarocks.new_version", package.seeall) +--module("luarocks.new_version", package.seeall) +local new_version = {} +package.loaded["luarocks.new_version"] = new_version local util = require("luarocks.util") -local cfg = require("luarocks.cfg") local download = require("luarocks.download") local fetch = require("luarocks.fetch") local persist = require("luarocks.persist") -local dir = require("luarocks.dir") local fs = require("luarocks.fs") +local type_check = require("luarocks.type_check") -help_summary = "Auto-write a rockspec for a new version of a rock." -help_arguments = "{<package>|<rockspec>} [<new_version>] [<new_url>]" -help = [[ +new_version.help_summary = "Auto-write a rockspec for a new version of a rock." +new_version.help_arguments = "{<package>|<rockspec>} [<new_version>] [<new_url>]" +new_version.help = [[ This is a utility function that writes a new rockspec, updating data from a previous one. @@ -33,13 +34,6 @@ WARNING: it writes the new rockspec to the current directory, overwriting the file if it already exists. ]] -local order = {"rockspec_format", "package", "version", - { "source", { "url", "tag", "branch", "md5" } }, - { "description", {"summary", "detailed", "homepage", "license" } }, - "supported_platforms", "dependencies", "external_dependencies", - { "build", {"type", "modules", "copy_directories", "platforms"} }, - "hooks"} - local function try_replace(tbl, field, old, new) if not tbl[field] then return false @@ -64,18 +58,13 @@ local function check_url_and_update_md5(out_rs, out_name) end util.printout("File successfully downloaded. Updating MD5 checksum...") out_rs.source.md5 = fs.get_md5(file) - fs.change_dir(temp_dir) - fs.unpack_archive(file) - local base_dir = out_rs.source.dir or fetch.url_to_base_dir(out_rs.source.url) - if not fs.exists(base_dir) then - util.printerr("Directory "..base_dir.." not found") - local files = fs.list_dir() - if files[1] and fs.is_dir(files[1]) then - util.printerr("Found "..files[1]) - out_rs.source.dir = files[1] - 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 - fs.pop_dir() return out_rs.source.md5 ~= old_md5 end @@ -115,7 +104,7 @@ local function update_source_section(out_rs, out_name, url, old_ver, new_ver) return true end -function run(...) +function new_version.run(...) local flags, input, version, url = util.parse_flags(...) if not input then return nil, "Missing arguments: expected program or rockspec. "..util.see_help("new_version") @@ -165,7 +154,7 @@ function run(...) local out_filename = out_name.."-"..new_rockver.."-"..new_rev..".rockspec" - persist.save_from_table(out_filename, out_rs, order) + persist.save_from_table(out_filename, out_rs, type_check.rockspec_order) util.printout("Wrote "..out_filename) @@ -176,3 +165,5 @@ function run(...) return true end + +return new_version diff --git a/luarocks/src/luarocks/pack.lua b/luarocks/src/luarocks/pack.lua index 583932e..c73d66a 100644 --- a/luarocks/src/luarocks/pack.lua +++ b/luarocks/src/luarocks/pack.lua @@ -1,7 +1,11 @@ --- Module implementing the LuaRocks "pack" command. -- Creates a rock, packing sources or binaries. -module("luarocks.pack", package.seeall) +--module("luarocks.pack", package.seeall) +local pack = {} +package.loaded["luarocks.pack"] = pack + +local unpack = unpack or table.unpack local path = require("luarocks.path") local repos = require("luarocks.repos") @@ -13,9 +17,9 @@ local dir = require("luarocks.dir") local manif = require("luarocks.manif") local search = require("luarocks.search") -help_summary = "Create a rock, packing sources or binaries." -help_arguments = "{<rockspec>|<name> [<version>]}" -help = [[ +pack.help_summary = "Create a rock, packing sources or binaries." +pack.help_arguments = "{<rockspec>|<name> [<version>]}" +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 @@ -29,7 +33,7 @@ argument. -- @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. -local function pack_source_rock(rockspec_file) +function pack.pack_source_rock(rockspec_file) assert(type(rockspec_file) == "string") local rockspec, err = fetch.load_rockspec(rockspec_file) @@ -45,7 +49,8 @@ local function pack_source_rock(rockspec_file) if not source_file then return nil, source_dir end - fs.change_dir(source_dir) + 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) @@ -58,12 +63,14 @@ local function pack_source_rock(rockspec_file) end local function copy_back_files(name, version, file_tree, deploy_dir, pack_dir) - fs.make_dir(pack_dir) + 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 @@ -128,14 +135,17 @@ local function do_pack_binary_rock(name, version) local is_binary = false if rock_manifest.lib then - copy_back_files(name, version, rock_manifest.lib, path.deploy_lib_dir(root), dir.path(temp_dir, "lib")) + local ok, err = copy_back_files(name, version, rock_manifest.lib, path.deploy_lib_dir(root), dir.path(temp_dir, "lib")) + if not ok then return nil, "Failed copying back files: " .. err end is_binary = true end if rock_manifest.lua then - copy_back_files(name, version, rock_manifest.lua, path.deploy_lua_dir(root), dir.path(temp_dir, "lua")) + local ok, err = copy_back_files(name, version, rock_manifest.lua, path.deploy_lua_dir(root), dir.path(temp_dir, "lua")) + if not ok then return nil, "Failed copying back files: " .. err end end - fs.change_dir(temp_dir) + 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 @@ -148,7 +158,7 @@ local function do_pack_binary_rock(name, version) return rock_file end -function pack_binary_rock(name, version, cmd, ...) +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 @@ -157,9 +167,9 @@ function pack_binary_rock(name, version, cmd, ...) -- to shave off the final deploy steps from the build phase and the initial -- collect steps from the pack phase. - local temp_dir = fs.make_temp_dir("luarocks-build-pack-"..dir.base_name(name)) + 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." + return nil, "Failed creating temporary directory: "..err end util.schedule_function(fs.delete, temp_dir) @@ -182,7 +192,7 @@ end -- version may also be passed. -- @return boolean or (nil, string): true if successful or nil followed -- by an error message. -function run(...) +function pack.run(...) local flags, arg, version = util.parse_flags(...) assert(type(version) == "string" or not version) if type(arg) ~= "string" then @@ -191,7 +201,7 @@ function run(...) local file, err if arg:match(".*%.rockspec") then - file, err = pack_source_rock(arg) + file, err = pack.pack_source_rock(arg) else file, err = do_pack_binary_rock(arg, version) end @@ -202,3 +212,5 @@ function run(...) return true end end + +return pack diff --git a/luarocks/src/luarocks/path.lua b/luarocks/src/luarocks/path.lua index 7af81c0..598e51d 100644 --- a/luarocks/src/luarocks/path.lua +++ b/luarocks/src/luarocks/path.lua @@ -2,31 +2,23 @@ --- LuaRocks-specific path handling functions. -- All paths are configured in this module, making it a single -- point where the layout of the local installation is defined in LuaRocks. -module("luarocks.path", package.seeall) +--module("luarocks.path", package.seeall) +local path = {} local dir = require("luarocks.dir") local cfg = require("luarocks.cfg") local util = require("luarocks.util") -local deps = require("luarocks.deps") - -help_summary = "Return the currently configured package path." -help_arguments = "" -help = [[ -Returns the package path currently configured for this installation -of LuaRocks, formatted as shell commands to update LUA_PATH and -LUA_CPATH. (On Unix systems, you may run: eval `luarocks path`) -]] --- Infer rockspec filename from a rock filename. -- @param rock_name string: Pathname of a rock file. -- @return string: Filename of the rockspec, without path. -function rockspec_name_from_rock(rock_name) +function path.rockspec_name_from_rock(rock_name) assert(type(rock_name) == "string") local base_name = dir.base_name(rock_name) return base_name:match("(.*)%.[^.]*.rock") .. ".rockspec" end -function rocks_dir(tree) +function path.rocks_dir(tree) if type(tree) == "string" then return dir.path(tree, cfg.rocks_subdir) else @@ -35,12 +27,12 @@ function rocks_dir(tree) end end -function root_dir(rocks_dir) +function path.root_dir(rocks_dir) assert(type(rocks_dir) == "string") return rocks_dir:match("(.*)" .. util.matchquote(cfg.rocks_subdir) .. ".*$") end -function rocks_tree_to_string(tree) +function path.rocks_tree_to_string(tree) if type(tree) == "string" then return tree else @@ -49,7 +41,7 @@ function rocks_tree_to_string(tree) end end -function deploy_bin_dir(tree) +function path.deploy_bin_dir(tree) if type(tree) == "string" then return dir.path(tree, "bin") else @@ -58,7 +50,7 @@ function deploy_bin_dir(tree) end end -function deploy_lua_dir(tree) +function path.deploy_lua_dir(tree) if type(tree) == "string" then return dir.path(tree, cfg.lua_modules_path) else @@ -67,7 +59,7 @@ function deploy_lua_dir(tree) end end -function deploy_lib_dir(tree) +function path.deploy_lib_dir(tree) if type(tree) == "string" then return dir.path(tree, cfg.lib_modules_path) else @@ -76,7 +68,7 @@ function deploy_lib_dir(tree) end end -function manifest_file(tree) +function path.manifest_file(tree) if type(tree) == "string" then return dir.path(tree, cfg.rocks_subdir, "manifest") else @@ -90,10 +82,10 @@ end -- @return string: The resulting path -- does not guarantee that -- @param tree string or nil: If given, specifies the local tree to use. -- the package (and by extension, the path) exists. -function versions_dir(name, tree) +function path.versions_dir(name, tree) assert(type(name) == "string") tree = tree or cfg.root_dir - return dir.path(rocks_dir(tree), name) + return dir.path(path.rocks_dir(tree), name) end --- Get the local installation directory (prefix) for a package. @@ -102,11 +94,11 @@ end -- @param tree string or nil: If given, specifies the local tree to use. -- @return string: The resulting path -- does not guarantee that -- the package (and by extension, the path) exists. -function install_dir(name, version, tree) +function path.install_dir(name, version, tree) assert(type(name) == "string") assert(type(version) == "string") tree = tree or cfg.root_dir - return dir.path(rocks_dir(tree), name, version) + return dir.path(path.rocks_dir(tree), name, version) end --- Get the local filename of the rockspec of an installed rock. @@ -115,11 +107,11 @@ end -- @param tree string or nil: If given, specifies the local tree to use. -- @return string: The resulting path -- does not guarantee that -- the package (and by extension, the file) exists. -function rockspec_file(name, version, tree) +function path.rockspec_file(name, version, tree) assert(type(name) == "string") assert(type(version) == "string") tree = tree or cfg.root_dir - return dir.path(rocks_dir(tree), name, version, name.."-"..version..".rockspec") + return dir.path(path.rocks_dir(tree), name, version, name.."-"..version..".rockspec") end --- Get the local filename of the rock_manifest file of an installed rock. @@ -128,11 +120,11 @@ end -- @param tree string or nil: If given, specifies the local tree to use. -- @return string: The resulting path -- does not guarantee that -- the package (and by extension, the file) exists. -function rock_manifest_file(name, version, tree) +function path.rock_manifest_file(name, version, tree) assert(type(name) == "string") assert(type(version) == "string") tree = tree or cfg.root_dir - return dir.path(rocks_dir(tree), name, version, "rock_manifest") + return dir.path(path.rocks_dir(tree), name, version, "rock_manifest") end --- Get the local installation directory for C libraries of a package. @@ -141,11 +133,11 @@ end -- @param tree string or nil: If given, specifies the local tree to use. -- @return string: The resulting path -- does not guarantee that -- the package (and by extension, the path) exists. -function lib_dir(name, version, tree) +function path.lib_dir(name, version, tree) assert(type(name) == "string") assert(type(version) == "string") tree = tree or cfg.root_dir - return dir.path(rocks_dir(tree), name, version, "lib") + return dir.path(path.rocks_dir(tree), name, version, "lib") end --- Get the local installation directory for Lua modules of a package. @@ -154,11 +146,11 @@ end -- @param tree string or nil: If given, specifies the local tree to use. -- @return string: The resulting path -- does not guarantee that -- the package (and by extension, the path) exists. -function lua_dir(name, version, tree) +function path.lua_dir(name, version, tree) assert(type(name) == "string") assert(type(version) == "string") tree = tree or cfg.root_dir - return dir.path(rocks_dir(tree), name, version, "lua") + return dir.path(path.rocks_dir(tree), name, version, "lua") end --- Get the local installation directory for documentation of a package. @@ -167,11 +159,11 @@ end -- @param tree string or nil: If given, specifies the local tree to use. -- @return string: The resulting path -- does not guarantee that -- the package (and by extension, the path) exists. -function doc_dir(name, version, tree) +function path.doc_dir(name, version, tree) assert(type(name) == "string") assert(type(version) == "string") tree = tree or cfg.root_dir - return dir.path(rocks_dir(tree), name, version, "doc") + return dir.path(path.rocks_dir(tree), name, version, "doc") end --- Get the local installation directory for configuration files of a package. @@ -180,11 +172,11 @@ end -- @param tree string or nil: If given, specifies the local tree to use. -- @return string: The resulting path -- does not guarantee that -- the package (and by extension, the path) exists. -function conf_dir(name, version, tree) +function path.conf_dir(name, version, tree) assert(type(name) == "string") assert(type(version) == "string") tree = tree or cfg.root_dir - return dir.path(rocks_dir(tree), name, version, "conf") + return dir.path(path.rocks_dir(tree), name, version, "conf") end --- Get the local installation directory for command-line scripts @@ -194,11 +186,11 @@ end -- @param tree string or nil: If given, specifies the local tree to use. -- @return string: The resulting path -- does not guarantee that -- the package (and by extension, the path) exists. -function bin_dir(name, version, tree) +function path.bin_dir(name, version, tree) assert(type(name) == "string") assert(type(version) == "string") tree = tree or cfg.root_dir - return dir.path(rocks_dir(tree), name, version, "bin") + return dir.path(path.rocks_dir(tree), name, version, "bin") end --- Extract name, version and arch of a rock filename, @@ -206,7 +198,7 @@ end -- @param file_name string: pathname of a rock or rockspec -- @return (string, string, string) or nil: name, version and arch -- or nil if name could not be parsed -function parse_name(file_name) +function path.parse_name(file_name) assert(type(file_name) == "string") if file_name:match("%.rock$") then return dir.base_name(file_name):match("(.*)-([^-]+-%d+)%.([^.]+)%.rock$") @@ -221,7 +213,7 @@ end -- @param version string: Package version. -- @param arch string: Architecture identifier, or "rockspec" or "installed". -- @return string: A URL or pathname following LuaRocks naming conventions. -function make_url(pathname, name, version, arch) +function path.make_url(pathname, name, version, arch) assert(type(pathname) == "string") assert(type(name) == "string") assert(type(version) == "string") @@ -245,7 +237,7 @@ end -- @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_to_module(file) +function path.path_to_module(file) assert(type(file) == "string") local name = file:match("(.*)%."..cfg.lua_extension.."$") @@ -270,7 +262,7 @@ end -- For example, on Unix, "foo.bar.baz" will return "foo/bar". -- @param mod string: A module name in Lua dot-separated format. -- @return string: A directory name using the platform's separator. -function module_to_path(mod) +function path.module_to_path(mod) assert(type(mod) == "string") return (mod:gsub("[^.]*$", ""):gsub("%.", dir.separator)) end @@ -279,23 +271,29 @@ end -- Create a "variables" table in the rockspec table, containing -- adjusted variables according to the configuration file. -- @param rockspec table: The rockspec table. -function configure_paths(rockspec) +function path.configure_paths(rockspec) assert(type(rockspec) == "table") local vars = {} for k,v in pairs(cfg.variables) do vars[k] = v end local name, version = rockspec.name, rockspec.version - vars.PREFIX = install_dir(name, version) - vars.LUADIR = lua_dir(name, version) - vars.LIBDIR = lib_dir(name, version) - vars.CONFDIR = conf_dir(name, version) - vars.BINDIR = bin_dir(name, version) - vars.DOCDIR = doc_dir(name, version) + vars.PREFIX = path.install_dir(name, version) + vars.LUADIR = path.lua_dir(name, version) + vars.LIBDIR = path.lib_dir(name, version) + vars.CONFDIR = path.conf_dir(name, version) + vars.BINDIR = path.bin_dir(name, version) + vars.DOCDIR = path.doc_dir(name, version) rockspec.variables = vars end -function versioned_name(file, prefix, name, version) +--- 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") @@ -305,12 +303,12 @@ function versioned_name(file, prefix, name, version) return dir.path(prefix, name_version.."-"..rest) end -function use_tree(tree) +function path.use_tree(tree) cfg.root_dir = tree - cfg.rocks_dir = rocks_dir(tree) - cfg.deploy_bin_dir = deploy_bin_dir(tree) - cfg.deploy_lua_dir = deploy_lua_dir(tree) - cfg.deploy_lib_dir = deploy_lib_dir(tree) + cfg.rocks_dir = path.rocks_dir(tree) + cfg.deploy_bin_dir = path.deploy_bin_dir(tree) + cfg.deploy_lua_dir = path.deploy_lua_dir(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. @@ -320,7 +318,7 @@ end -- @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 map_trees(deps_mode, fn, ...) +function path.map_trees(deps_mode, fn, ...) local result = {} if deps_mode == "one" then table.insert(result, (fn(cfg.root_dir, ...)) or 0) @@ -330,7 +328,7 @@ function map_trees(deps_mode, fn, ...) use = true end for _, tree in ipairs(cfg.rocks_trees) do - if dir.normalize(tree) == dir.normalize(cfg.root_dir) then + 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 @@ -349,17 +347,17 @@ end -- @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 which_i(module_name, name, version, tree, i) +function path.which_i(module_name, name, version, tree, i) local deploy_dir if module_name:match("%.lua$") then - deploy_dir = deploy_lua_dir(tree) + deploy_dir = path.deploy_lua_dir(tree) module_name = dir.path(deploy_dir, module_name) else - deploy_dir = deploy_lib_dir(tree) + deploy_dir = path.deploy_lib_dir(tree) module_name = dir.path(deploy_dir, module_name) end if i > 1 then - module_name = versioned_name(module_name, deploy_dir, name, version) + module_name = path.versioned_name(module_name, deploy_dir, name, version) end return module_name end @@ -373,45 +371,15 @@ end -- @param tree string: repository path (eg. "/usr/local") -- @param manifest table: the manifest table for the tree. -- @return string: filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so") -function which(module_name, filename, name, version, tree, manifest) +function path.which(module_name, filename, name, version, tree, manifest) local versions = manifest.modules[module_name] assert(versions) for i, name_version in ipairs(versions) do if name_version == name.."/"..version then - return which_i(filename, name, version, tree, i):gsub("//", "/") + return path.which_i(filename, name, version, tree, i):gsub("//", "/") end end assert(false) end ---- Driver function for "path" command. --- @return boolean This function always succeeds. -function run(...) - local flags = util.parse_flags(...) - local deps_mode = deps.get_deps_mode(flags) - - local lr_path, lr_cpath = cfg.package_paths() - local bin_dirs = map_trees(deps_mode, deploy_bin_dir) - - 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 - local lr_bin = util.remove_path_dupes(table.concat(bin_dirs, cfg.export_path_separator), cfg.export_path_separator) - util.printout(util.remove_path_dupes(lr_bin, ';')) - return true - end - - util.printout(cfg.export_lua_path:format(util.remove_path_dupes(package.path, ';'))) - util.printout(cfg.export_lua_cpath:format(util.remove_path_dupes(package.cpath, ';'))) - if flags["bin"] then - table.insert(bin_dirs, 1, os.getenv("PATH")) - local lr_bin = util.remove_path_dupes(table.concat(bin_dirs, cfg.export_path_separator), cfg.export_path_separator) - util.printout(cfg.export_path:format(lr_bin)) - end - return true -end - +return path diff --git a/luarocks/src/luarocks/path_cmd.lua b/luarocks/src/luarocks/path_cmd.lua new file mode 100644 index 0000000..95532f9 --- /dev/null +++ b/luarocks/src/luarocks/path_cmd.lua @@ -0,0 +1,73 @@ + +--- @module luarocks.path_cmd +-- Driver for the `luarocks path` command. +local path_cmd = {} + +local util = require("luarocks.util") +local deps = require("luarocks.deps") +local cfg = require("luarocks.cfg") +local path = require("luarocks.path") + +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.run(...) + local flags = util.parse_flags(...) + local deps_mode = deps.get_deps_mode(flags) + + local lr_path, lr_cpath, lr_bin = cfg.package_paths() + 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/luarocks/src/luarocks/persist.lua b/luarocks/src/luarocks/persist.lua index 208aee0..9d601a4 100644 --- a/luarocks/src/luarocks/persist.lua +++ b/luarocks/src/luarocks/persist.lua @@ -3,7 +3,9 @@ -- saving tables into files. -- Implemented separately to avoid interdependencies, -- as it is used in the bootstrapping stage of the cfg module. -module("luarocks.persist", package.seeall) +--module("luarocks.persist", package.seeall) +local persist = {} +package.loaded["luarocks.persist"] = persist local util = require("luarocks.util") @@ -14,13 +16,22 @@ local util = require("luarocks.util") -- loaded values. -- @return table or (nil, string): a table with the file's assignments -- as fields, or nil and a message in case of errors. -function load_into_table(filename, tbl) +function persist.load_into_table(filename, tbl) assert(type(filename) == "string") assert(type(tbl) == "table" or not tbl) local result, chunk, ran, err - local result = tbl or {} - if setfenv then -- Lua 5.1 + result = tbl or {} + local globals = {} + local globals_mt = { + __index = function(t, n) + globals[n] = true + return rawget(t, n) + end + } + local save_mt = getmetatable(result) + setmetatable(result, globals_mt) + if _VERSION == "Lua 5.1" then -- Lua 5.1 chunk, err = loadfile(filename) if chunk then setfenv(chunk, result) @@ -32,6 +43,7 @@ function load_into_table(filename, tbl) ran, err = pcall(chunk) end end + setmetatable(result, save_mt) if not chunk then if err:sub(1,5) ~= filename:sub(1,5) then @@ -42,7 +54,7 @@ function load_into_table(filename, tbl) if not ran then return nil, "Error running file: "..err end - return result + return result, globals end local write_table @@ -146,7 +158,7 @@ end -- @param tbl table: the table containing the data to be written -- @param field_order table: an optional array indicating the order of top-level fields. -- @return string -function save_from_table_to_string(tbl, field_order) +function persist.save_from_table_to_string(tbl, field_order) local out = {buffer = {}} function out:write(data) table.insert(self.buffer, data) end write_table(out, tbl, field_order) @@ -162,7 +174,7 @@ end -- @param field_order table: an optional array indicating the order of top-level fields. -- @return boolean or (nil, string): true if successful, or nil and a -- message in case of errors. -function save_from_table(filename, tbl, field_order) +function persist.save_from_table(filename, tbl, field_order) local out = io.open(filename, "w") if not out then return nil, "Cannot create file at "..filename @@ -171,3 +183,5 @@ function save_from_table(filename, tbl, field_order) out:close() return true end + +return persist diff --git a/luarocks/src/luarocks/purge.lua b/luarocks/src/luarocks/purge.lua index e76a82e..ba9b870 100644 --- a/luarocks/src/luarocks/purge.lua +++ b/luarocks/src/luarocks/purge.lua @@ -1,7 +1,9 @@ --- Module implementing the LuaRocks "purge" command. -- Remove all rocks from a given tree. -module("luarocks.purge", package.seeall) +--module("luarocks.purge", package.seeall) +local purge = {} +package.loaded["luarocks.purge"] = purge local util = require("luarocks.util") local fs = require("luarocks.fs") @@ -13,9 +15,9 @@ local manif = require("luarocks.manif") local cfg = require("luarocks.cfg") local remove = require("luarocks.remove") -help_summary = "Remove all installed rocks from a tree." -help_arguments = "--tree=<tree> [--old-versions]" -help = [[ +purge.help_summary = "Remove all installed rocks from a tree." +purge.help_arguments = "--tree=<tree> [--old-versions]" +purge.help = [[ This command removes rocks en masse from a given tree. By default, it removes all rocks from a tree. @@ -29,7 +31,7 @@ assume a default tree. overridden with the flag --force. ]] -function run(...) +function purge.run(...) local flags = util.parse_flags(...) local tree = flags["tree"] @@ -41,6 +43,13 @@ function run(...) 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 @@ -68,3 +77,5 @@ function run(...) end return manif.make_manifest(cfg.rocks_dir, "one") end + +return purge diff --git a/luarocks/src/luarocks/refresh_cache.lua b/luarocks/src/luarocks/refresh_cache.lua index 8073037..193e599 100644 --- a/luarocks/src/luarocks/refresh_cache.lua +++ b/luarocks/src/luarocks/refresh_cache.lua @@ -1,20 +1,22 @@ --- Module implementing the luarocks-admin "refresh_cache" command. -module("luarocks.refresh_cache", package.seeall) +--module("luarocks.refresh_cache", package.seeall) +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") -help_summary = "Refresh local cache of a remote rocks server." -help_arguments = "[--from=<server>]" -help = [[ +refresh_cache.help_summary = "Refresh local cache of a remote rocks server." +refresh_cache.help_arguments = "[--from=<server>]" +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 run(...) +function refresh_cache.run(...) local flags = util.parse_flags(...) local server, upload_server = cache.get_upload_server(flags["server"]) if not server then return nil, upload_server end @@ -28,3 +30,5 @@ function run(...) end end + +return refresh_cache diff --git a/luarocks/src/luarocks/remove.lua b/luarocks/src/luarocks/remove.lua index 9681641..e1cd44f 100644 --- a/luarocks/src/luarocks/remove.lua +++ b/luarocks/src/luarocks/remove.lua @@ -1,7 +1,9 @@ --- Module implementing the LuaRocks "remove" command. -- Uninstalls rocks. -module("luarocks.remove", package.seeall) +--module("luarocks.remove", package.seeall) +local remove = {} +package.loaded["luarocks.remove"] = remove local search = require("luarocks.search") local deps = require("luarocks.deps") @@ -13,14 +15,17 @@ local cfg = require("luarocks.cfg") local manif = require("luarocks.manif") local fs = require("luarocks.fs") -help_summary = "Uninstall a rock." -help_arguments = "[--force] <name> [<version>]" -help = [[ +remove.help_summary = "Uninstall a rock." +remove.help_arguments = "[--force[=fast]] <name> [<version>]" +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). @@ -69,54 +74,58 @@ local function delete_versions(name, versions) return true end -function remove_search_results(results, name, deps_mode, force) +function remove.remove_search_results(results, name, deps_mode, force) local versions = results[name] local version = next(versions) local second = next(versions, version) - util.printout("Checking stability of dependencies on the absence of") - util.printout(name.." "..table.concat(util.keys(versions), ", ").."...") - util.printout() - - local dependents = check_dependents(name, versions, deps_mode) + local dependents = {} + if force ~= "fast" then + util.printout("Checking stability of dependencies on the absence of") + util.printout(name.." "..table.concat(util.keys(versions), ", ").."...") + util.printout() + dependents = check_dependents(name, versions, deps_mode) + end - if #dependents == 0 or force then - if #dependents > 0 then + if #dependents > 0 then + if force 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() - end - 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) - if not ok then return nil, err end - 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) + 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 - util.printerr() - util.printerr("Use --force to force removal (warning: this may break modules).") - return nil, "Failed removing." end + + 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) + if not ok then return nil, err end + util.printout("Removal successful.") return true end -function remove_other_versions(name, version, force) +function remove.remove_other_versions(name, version, force) local results = {} search.manifest_search(results, cfg.rocks_dir, { name = name, exact_name = true, constraints = {{ op = "~=", version = version}} }) if results[name] then - return remove_search_results(results, name, cfg.deps_mode, force) + return remove.remove_search_results(results, name, cfg.deps_mode, force) end return true end @@ -126,9 +135,9 @@ end -- 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): True if removal was --- successful, nil and an error message otherwise. -function run(...) +-- @return boolean or (nil, string, exitcode): True if removal was +-- successful, nil and an error message otherwise. exitcode is optionally returned. +function remove.run(...) local flags, name, version = util.parse_flags(...) if type(name) ~= "string" then @@ -138,7 +147,7 @@ function run(...) 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 end + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$") local filename = name @@ -150,8 +159,10 @@ function run(...) local results = {} 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 local tree." + return nil, "Could not find rock '"..name..(version and " "..version or "").."' in "..cfg.root_dir end - return remove_search_results(results, name, deps_mode, flags["force"]) + return remove.remove_search_results(results, name, deps_mode, flags["force"]) end + +return remove diff --git a/luarocks/src/luarocks/repos.lua b/luarocks/src/luarocks/repos.lua index b7e64d4..1745659 100644 --- a/luarocks/src/luarocks/repos.lua +++ b/luarocks/src/luarocks/repos.lua @@ -1,6 +1,8 @@ --- Functions for managing the repository on disk. -module("luarocks.repos", package.seeall) +--module("luarocks.repos", package.seeall) +local repos = {} +package.loaded["luarocks.repos"] = repos local fs = require("luarocks.fs") local path = require("luarocks.path") @@ -27,7 +29,7 @@ end -- @param version string: package version in string format -- @return boolean: true if a package is installed, -- false otherwise. -function is_installed(name, version) +function repos.is_installed(name, version) assert(type(name) == "string") assert(type(version) == "string") @@ -72,7 +74,7 @@ end -- in "foo.bar" format and values are pathnames in architecture-dependent -- "foo/bar.so" format. If no modules are found or if package or version -- are invalid, an empty table is returned. -function package_modules(package, version) +function repos.package_modules(package, version) assert(type(package) == "string") assert(type(version) == "string") @@ -91,7 +93,7 @@ end -- as strings and values are pathnames in architecture-dependent -- ".../bin/foo" format. If no modules are found or if package or version -- are invalid, an empty table is returned. -function package_commands(package, version) +function repos.package_commands(package, version) assert(type(package) == "string") assert(type(version) == "string") @@ -107,7 +109,7 @@ end -- @param version string: version of an installed rock -- @return boolean: returns true if rock contains platform-specific -- binary executables, or false if it is a pure-Lua rock. -function has_binaries(name, version) +function repos.has_binaries(name, version) assert(type(name) == "string") assert(type(version) == "string") @@ -123,7 +125,7 @@ function has_binaries(name, version) return false end -function run_hook(rockspec, hook_name) +function repos.run_hook(rockspec, hook_name) assert(type(rockspec) == "table") assert(type(hook_name) == "string") @@ -153,19 +155,13 @@ end local function install_binary(source, target, name, version) assert(type(source) == "string") assert(type(target) == "string") - - local match = source:match("%.lua$") - local file, ok, err - if not match then - file = io.open(source) - end - if match or (file and file:read():match("^#!.*lua.*")) then - ok, err = fs.wrap_script(source, target, name, version) + + if fs.is_lua(source) then + repos.ok, repos.err = fs.wrap_script(source, target, name, version) else - ok, err = fs.copy_binary(source, target) + repos.ok, repos.err = fs.copy_binary(source, target) end - if file then file:close() end - return ok, err + return repos.ok, repos.err end local function resolve_conflict(target, deploy_dir, name, version) @@ -175,7 +171,8 @@ local function resolve_conflict(target, deploy_dir, name, version) end if name ~= cname or deps.compare_versions(version, cversion) then local versioned = path.versioned_name(target, deploy_dir, cname, cversion) - fs.make_dir(dir.dir_name(versioned)) + local ok, err = fs.make_dir(dir.dir_name(versioned)) + if not ok then return nil, err end fs.move(target, versioned) return target else @@ -183,7 +180,7 @@ local function resolve_conflict(target, deploy_dir, name, version) end end -function should_wrap_bin_scripts(rockspec) +function repos.should_wrap_bin_scripts(rockspec) assert(type(rockspec) == "table") if cfg.wrap_bin_scripts ~= nil then @@ -195,7 +192,7 @@ function should_wrap_bin_scripts(rockspec) return true end -function deploy_files(name, version, wrap_bin_scripts) +function repos.deploy_files(name, version, wrap_bin_scripts) assert(type(name) == "string") assert(type(version) == "string") assert(type(wrap_bin_scripts) == "boolean") @@ -213,14 +210,20 @@ function deploy_files(name, version, wrap_bin_scripts) if fs.exists(target) then local new_target, err = resolve_conflict(target, deploy_dir, name, version) if err == "untracked" then - fs.delete(target) + local backup = target + repeat + backup = backup.."~" + until not fs.exists(backup) -- slight race condition here, but shouldn't be a problem. + util.printerr("Warning: "..target.." is not tracked by this installation of LuaRocks. Moving it to "..backup) + fs.move(target, backup) elseif err then return nil, err.." Cannot install new version." else target = new_target end end - fs.make_dir(dir.dir_name(target)) + ok, err = fs.make_dir(dir.dir_name(target)) + if not ok then return nil, err end ok, err = move_fn(source, target, name, version) fs.remove_dir_tree_if_empty(dir.dir_name(source)) if not ok then return nil, err end @@ -245,6 +248,21 @@ function deploy_files(name, version, wrap_bin_scripts) return ok, err end +local function delete_suffixed(filename, suffix) + local filenames = { filename } + if suffix and suffix ~= "" then filenames = { filename..suffix, filename } end + for _, name in ipairs(filenames) do + if fs.exists(name) then + fs.delete(name) + if fs.exists(name) then + return nil, "Failed deleting "..name, "fail" + end + return true, name + end + end + return false, "File not found", "not found" +end + --- Delete a package from the local repository. -- Version numbers are compared as exact string comparison. -- @param name string: name of package @@ -253,32 +271,32 @@ end -- of another version that provides the same module that -- was deleted. This is used during 'purge', as every module -- will be eventually deleted. -function delete_version(name, version, quick) +function repos.delete_version(name, version, quick) assert(type(name) == "string") assert(type(version) == "string") - local function delete_deployed_file_tree(file_tree, deploy_dir) + local function delete_deployed_file_tree(file_tree, deploy_dir, suffix) return recurse_rock_manifest_tree(file_tree, function(parent_path, parent_module, file) local target = dir.path(deploy_dir, parent_path, file) local versioned = path.versioned_name(target, deploy_dir, name, version) - if fs.exists(versioned) then - local ok = fs.delete(versioned) + local ok, name, err = delete_suffixed(versioned, suffix) + if ok then fs.remove_dir_tree_if_empty(dir.dir_name(versioned)) - if not ok then return nil, "Failed deleting "..versioned end - else - local ok = fs.delete(target) - if not quick then - local next_name, next_version = manif.find_next_provider(target) - if next_name then - local versioned = path.versioned_name(target, deploy_dir, next_name, next_version) - fs.move(versioned, target) - fs.remove_dir_tree_if_empty(dir.dir_name(versioned)) - end + return true + end + if err == "fail" then return nil, name end + ok, name, err = delete_suffixed(target, suffix) + if err == "fail" then return nil, name end + if not quick then + local next_name, next_version = manif.find_next_provider(target) + if next_name then + local versioned = path.versioned_name(name, deploy_dir, next_name, next_version) + fs.move(versioned, name) + fs.remove_dir_tree_if_empty(dir.dir_name(versioned)) end - fs.remove_dir_tree_if_empty(dir.dir_name(target)) - if not ok then return nil, "Failed deleting "..target end end + fs.remove_dir_tree_if_empty(dir.dir_name(target)) return true end ) @@ -291,7 +309,7 @@ function delete_version(name, version, quick) local ok, err = true if rock_manifest.bin then - ok, err = delete_deployed_file_tree(rock_manifest.bin, cfg.deploy_bin_dir) + ok, err = delete_deployed_file_tree(rock_manifest.bin, cfg.deploy_bin_dir, cfg.wrapper_suffix) end if ok and rock_manifest.lua then ok, err = delete_deployed_file_tree(rock_manifest.lua, cfg.deploy_lua_dir) @@ -307,3 +325,5 @@ function delete_version(name, version, quick) end return true end + +return repos diff --git a/luarocks/src/luarocks/require.lua b/luarocks/src/luarocks/require.lua index 9917770..902bd1a 100644 --- a/luarocks/src/luarocks/require.lua +++ b/luarocks/src/luarocks/require.lua @@ -1,6 +1,2 @@ --- Retained for compatibility reasons only. Use luarocks.loader instead. -local require, pairs = require, pairs -module("luarocks.require") -for k,v in pairs(require("luarocks.loader")) do - _M[k] = v -end +return require("luarocks.loader") diff --git a/luarocks/src/luarocks/search.lua b/luarocks/src/luarocks/search.lua index 1fc1f9a..5343938 100644 --- a/luarocks/src/luarocks/search.lua +++ b/luarocks/src/luarocks/search.lua @@ -1,7 +1,9 @@ --- Module implementing the LuaRocks "search" command. -- Queries LuaRocks servers. -module("luarocks.search", package.seeall) +--module("luarocks.search", package.seeall) +local search = {} +package.loaded["luarocks.search"] = search local dir = require("luarocks.dir") local path = require("luarocks.path") @@ -10,9 +12,9 @@ local deps = require("luarocks.deps") local cfg = require("luarocks.cfg") local util = require("luarocks.util") -help_summary = "Query the LuaRocks servers." -help_arguments = "[--source] [--binary] { <name> [<version>] | --all }" -help = [[ +search.help_summary = "Query the LuaRocks servers." +search.help_arguments = "[--source] [--binary] { <name> [<version>] | --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 @@ -124,7 +126,7 @@ end -- @param table: The results table, where keys are package names and -- versions are tables matching version strings to an array of servers. -- If a table was given in the "results" parameter, that is the result value. -function disk_search(repo, query, results) +function search.disk_search(repo, query, results) assert(type(repo) == "string") assert(type(query) == "table") assert(type(results) == "table" or not results) @@ -136,17 +138,18 @@ function disk_search(repo, query, results) end query_arch_as_table(query) - for _, name in pairs(fs.list_dir(repo)) do + for name in fs.dir(repo) do local pathname = dir.path(repo, name) local rname, rversion, rarch = path.parse_name(name) - if fs.is_dir(pathname) then - for _, version in pairs(fs.list_dir(pathname)) do + + 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 - elseif rname then - store_if_match(results, repo, rname, rversion, rarch, query) end end return results @@ -163,15 +166,15 @@ end -- is used. The special value "any" is also recognized, returning all -- matches regardless of architecture. -- @return true or, in case of errors, nil and an error message. -function manifest_search(results, repo, query) +function search.manifest_search(results, repo, query) assert(type(results) == "table") assert(type(repo) == "string") assert(type(query) == "table") query_arch_as_table(query) - local manifest, err = manif.load_manifest(repo) + local manifest, err, errcode = manif.load_manifest(repo) if not manifest then - return nil, "Failed loading manifest: "..err + return nil, err, errcode end for name, versions in pairs(manifest.repository) do for version, items in pairs(versions) do @@ -188,27 +191,37 @@ end -- @return table: A table where keys are package names -- and values are tables matching version strings to an array of -- rocks servers; if no results are found, an empty table is returned. -function search_repos(query) +function search.search_repos(query) assert(type(query) == "table") local results = {} for _, repo in ipairs(cfg.rocks_servers) do - 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 + if not cfg.disabled_servers[repo] then + if type(repo) == "string" then + repo = { repo } end - local ok, err = manifest_search(results, mirror, query) - if ok then - break - else - util.warning("Failed searching manifest: "..err) + 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) + 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 @@ -216,7 +229,7 @@ end -- @param name string: The query name. -- @param version string or nil: -- @return table: A query in table format -function make_query(name, version) +function search.make_query(name, version) assert(type(name) == "string") assert(type(version) == "string" or not version) @@ -265,14 +278,20 @@ end -- @return string or table or (nil, string): URL for matching rock if -- a single one was found, a table of candidates if it could not narrow to -- a single result, or nil followed by an error message. -function find_suitable_rock(query) +function search.find_suitable_rock(query) assert(type(query) == "table") - local results = search_repos(query) + local results = search.search_repos(query) local first = next(results) if not first then return nil, "No results matching query were found." elseif not next(results, first) then + if cfg.rocks_provided[query.name] ~= nil then + -- do not install versions that 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." + end return pick_latest_version(query.name, results[first]) else return results @@ -283,7 +302,7 @@ end -- @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 print_results(results, porcelain) +function search.print_results(results, porcelain) assert(type(results) == "table") assert(type(porcelain) == "boolean" or not porcelain) @@ -293,6 +312,7 @@ function print_results(results, porcelain) 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 @@ -337,14 +357,14 @@ end -- @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 act_on_src_or_rockspec(action, name, version, ...) +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 = make_query(name, version) + local query = search.make_query(name, version) query.arch = "src|rockspec" - local results, err = find_suitable_rock(query) + local results, err = search.find_suitable_rock(query) if type(results) == "string" then return action(results, ...) else @@ -357,7 +377,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 run(...) +function search.run(...) local flags, name, version = util.parse_flags(...) if flags["all"] then @@ -368,19 +388,21 @@ function run(...) return nil, "Enter name and version or use --all. "..util.see_help("search") end - local query = make_query(name:lower(), version) + local query = search.make_query(name:lower(), version) query.exact_name = false - local results, err = search_repos(query) + 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) - print_results(sources, porcelain) + search.print_results(sources, porcelain) end if next(binaries) and not flags["source"] then util.title("Binary and pure-Lua rocks:", porcelain) - print_results(binaries, porcelain) + search.print_results(binaries, porcelain) end return true end + +return search diff --git a/luarocks/src/luarocks/show.lua b/luarocks/src/luarocks/show.lua index 536085a..3243c0c 100644 --- a/luarocks/src/luarocks/show.lua +++ b/luarocks/src/luarocks/show.lua @@ -1,19 +1,19 @@ - --- Module implementing the LuaRocks "show" command. -- Shows information about an installed rock. -module("luarocks.show", package.seeall) +--module("luarocks.show", package.seeall) +local show = {} +package.loaded["luarocks.show"] = show local search = require("luarocks.search") local cfg = require("luarocks.cfg") local util = require("luarocks.util") local path = require("luarocks.path") -local dir = require("luarocks.dir") local deps = require("luarocks.deps") local fetch = require("luarocks.fetch") local manif = require("luarocks.manif") -help_summary = "Shows information about an installed rock." +show.help_summary = "Shows information about an installed rock." -help = [[ +show.help = [[ <argument> is an existing package name. Without any flags, show all module information. With these flags, return only the desired information: @@ -55,33 +55,14 @@ local function format_text(text) return (table.concat(paragraphs, "\n\n"):gsub("%s$", "")) end -local function module_name(mod, filename, name, version, repo, manifest) - local base_dir - if filename:match("%.lua$") then - base_dir = path.deploy_lua_dir(repo) - else - base_dir = path.deploy_lib_dir(repo) - end - - return dir.path(base_dir, filename) -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 run(...) - local flags, name, version = util.parse_flags(...) - if not name then - return nil, "Argument missing. "..util.see_help("show") - end +function show.pick_installed_rock(name, version, tree) local results = {} local query = search.make_query(name, version) query.exact_name = true local tree_map = {} local trees = cfg.rocks_trees - if flags["tree"] then - trees = { flags["tree"] } + if tree then + trees = { tree } end for _, tree in ipairs(trees) do local rocks_dir = path.rocks_dir(tree) @@ -103,8 +84,26 @@ function run(...) 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 "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.run(...) + local flags, name, version = util.parse_flags(...) + if not name then + return nil, "Argument missing. "..util.see_help("show") + end + + local repo, repo_url + name, version, repo, repo_url = show.pick_installed_rock(name, 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) @@ -118,7 +117,7 @@ function run(...) 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["modules"] then util.printout(keys_as_string(minfo.modules)) + 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) @@ -154,3 +153,5 @@ function run(...) return true end + +return show diff --git a/luarocks/src/luarocks/tools/patch.lua b/luarocks/src/luarocks/tools/patch.lua index baaacfc..8df3093 100644 --- a/luarocks/src/luarocks/tools/patch.lua +++ b/luarocks/src/luarocks/tools/patch.lua @@ -6,12 +6,13 @@ -- Code is heavilly based on the Python-based patch.py version 8.06-1 -- Copyright (c) 2008 rainforce.org, MIT License -- Project home: http://code.google.com/p/python-patch/ . +-- Version 0.1 -module("luarocks.tools.patch", package.seeall) +--module("luarocks.tools.patch", package.seeall) +local patch = {} local fs = require("luarocks.fs") - -local version = '0.1' +local util = require("luarocks.util") local io = io local os = os @@ -47,12 +48,6 @@ local function table_copy(t) return t2 end --- Returns boolean whether array t contains value v. -local function array_contains(t, v) - for _,v2 in ipairs(t) do if v == v2 then return true end end - return false -end - local function exists(filename) local fh = io.open(filename) local result = fh ~= nil @@ -158,7 +153,7 @@ local function match_linerange(line) return m1, m2, m3, m4 end -function read_patch(filename, data) +function patch.read_patch(filename, data) -- define possible file regions that will direct the parser flow local state = 'header' -- 'header' - comments before the patch body @@ -284,7 +279,7 @@ function read_patch(filename, data) local advance if state == 'filenames' then if startswith(line, "--- ") then - if array_contains(files.source, nextfileno) then + if util.array_contains(files.source, nextfileno) then all_ok = false warning(format("skipping invalid patch for %s", files.source[nextfileno+1])) @@ -304,7 +299,7 @@ function read_patch(filename, data) table.insert(files.source, match) end elseif not startswith(line, "+++ ") then - if array_contains(files.source, nextfileno) then + if util.array_contains(files.source, nextfileno) then all_ok = false warning(format("skipping invalid patch with no target for %s", files.source[nextfileno+1])) @@ -315,7 +310,7 @@ function read_patch(filename, data) end state = 'header' else - if array_contains(files.target, nextfileno) then + if util.array_contains(files.target, nextfileno) then all_ok = false warning(format("skipping invalid patch - double target at line %d", lineno+1)) @@ -354,7 +349,7 @@ function read_patch(filename, data) if not advance and state == 'hunkhead' then local m1, m2, m3, m4 = match_linerange(line) if not m1 then - if not array_contains(files.hunks, nextfileno-1) then + if not util.array_contains(files.hunks, nextfileno-1) then all_ok = false warning(format("skipping invalid patch with no hunks for file %s", files.target[nextfileno])) @@ -401,7 +396,6 @@ local function find_hunk(file, h, hno) for i=0,#file do local found = true local location = lineno - local total = #h.text - fuzz for l, hline in ipairs(h.text) do if l > fuzz then -- todo: \ No newline at the end of file @@ -452,9 +446,6 @@ local function load_file(filename) end local function find_hunks(file, hunks) - local matched = true - local lineno = 1 - local hno = nil for hno, h in ipairs(hunks) do find_hunk(file, h, hno) end @@ -463,7 +454,6 @@ end local function check_patched(file, hunks) local matched = true local lineno = 1 - local hno = nil local ok, err = pcall(function() if #file == 0 then error 'nomatch' @@ -569,15 +559,15 @@ local function strip_dirs(filename, strip) return filename end -function apply_patch(patch, strip) +function patch.apply_patch(the_patch, strip) local all_ok = true - local total = #patch.source - for fileno, filename in ipairs(patch.source) do + local total = #the_patch.source + for fileno, filename in ipairs(the_patch.source) do filename = strip_dirs(filename, strip) local continue local f2patch = filename if not exists(f2patch) then - f2patch = strip_dirs(patch.target[fileno], strip) + f2patch = strip_dirs(the_patch.target[fileno], strip) f2patch = fs.absolute_name(f2patch) if not exists(f2patch) then --FIX:if f2patch nil warning(format("source/target file does not exist\n--- %s\n+++ %s", @@ -598,7 +588,7 @@ function apply_patch(patch, strip) info(format("processing %d/%d:\t %s", fileno, total, filename)) -- validate before patching - local hunks = patch.hunks[fileno] + local hunks = the_patch.hunks[fileno] local file = load_file(filename) local hunkno = 1 local hunk = hunks[hunkno] @@ -710,3 +700,5 @@ function apply_patch(patch, strip) -- todo: check for premature eof return all_ok end + +return patch diff --git a/luarocks/src/luarocks/tools/tar.lua b/luarocks/src/luarocks/tools/tar.lua index ba01a41..03f7de3 100644 --- a/luarocks/src/luarocks/tools/tar.lua +++ b/luarocks/src/luarocks/tools/tar.lua @@ -1,6 +1,7 @@ --- A pure-Lua implementation of untar (unpacking .tar archives) -module("luarocks.tools.tar", package.seeall) +--module("luarocks.tools.tar", package.seeall) +local tar = {} local fs = require("luarocks.fs") local dir = require("luarocks.dir") @@ -82,7 +83,7 @@ local function read_header_block(block) return header end -function untar(filename, destdir) +function tar.untar(filename, destdir) assert(type(filename) == "string") assert(type(destdir) == "string") @@ -119,11 +120,13 @@ function untar(filename, destdir) end local pathname = dir.path(destdir, header.name) if header.typeflag == "directory" then - fs.make_dir(pathname) + local ok, err = fs.make_dir(pathname) + if not ok then return nil, err end elseif header.typeflag == "file" then local dirname = dir.dir_name(pathname) if dirname ~= "" then - fs.make_dir(dirname) + local ok, err = fs.make_dir(dirname) + if not ok then return nil, err end end local file_handle = io.open(pathname, "wb") file_handle:write(file_data) @@ -142,3 +145,5 @@ function untar(filename, destdir) end return true end + +return tar diff --git a/luarocks/src/luarocks/tools/zip.lua b/luarocks/src/luarocks/tools/zip.lua index 35428d9..40cc089 100644 --- a/luarocks/src/luarocks/tools/zip.lua +++ b/luarocks/src/luarocks/tools/zip.lua @@ -1,7 +1,8 @@ --- A Lua implementation of .zip file archiving (used for creating .rock files), -- using only lua-zlib. -module("luarocks.tools.zip", package.seeall) +--module("luarocks.tools.zip", package.seeall) +local zip = {} local zlib = require("zlib") local fs = require("luarocks.fs") @@ -188,7 +189,7 @@ end --- Return a zip handle open for writing. -- @param name filename of the zipfile to be created. -- @return a zip handle, or nil in case of error. -function new_zipwriter(name) +function zip.new_zipwriter(name) local zw = {} @@ -214,8 +215,8 @@ end -- additional arguments. -- @return boolean or (boolean, string): true on success, -- false and an error message on failure. -function zip(zipfile, ...) - local zw = new_zipwriter(zipfile) +function zip.zip(zipfile, ...) + local zw = zip.new_zipwriter(zipfile) if not zw then return nil, "error opening "..zipfile end @@ -243,3 +244,5 @@ function zip(zipfile, ...) return ok, err end + +return zip diff --git a/luarocks/src/luarocks/type_check.lua b/luarocks/src/luarocks/type_check.lua index 28e6e7b..a78c484 100644 --- a/luarocks/src/luarocks/type_check.lua +++ b/luarocks/src/luarocks/type_check.lua @@ -1,14 +1,15 @@ - --- Type-checking functions. -- Functions and definitions for doing a basic lint check on files -- loaded by LuaRocks. -module("luarocks.type_check", package.seeall) +--module("luarocks.type_check", package.seeall) +local type_check = {} +package.loaded["luarocks.type_check"] = type_check local cfg = require("luarocks.cfg") -rockspec_format = "1.0" +type_check.rockspec_format = "1.0" -rockspec_types = { +local rockspec_types = { rockspec_format = "string", MUST_package = "string", MUST_version = "[%w.]+-[%d]+", @@ -74,15 +75,22 @@ rockspec_types = { } } -function load_extensions() - rockspec_format = "1.1" +type_check.rockspec_order = {"rockspec_format", "package", "version", + { "source", { "url", "tag", "branch", "md5" } }, + { "description", {"summary", "detailed", "homepage", "license" } }, + "supported_platforms", "dependencies", "external_dependencies", + { "build", {"type", "modules", "copy_directories", "platforms"} }, + "hooks"} + +function type_check.load_extensions() + type_check.rockspec_format = "1.1" rockspec_types.deploy = { wrap_bin_scripts = true, } end if cfg.use_extensions then - load_extensions() + type_check.load_extensions() end rockspec_types.build.platforms.ANY = rockspec_types.build @@ -91,7 +99,7 @@ rockspec_types.external_dependencies.platforms.ANY = rockspec_types.external_dep rockspec_types.MUST_source.platforms.ANY = rockspec_types.MUST_source rockspec_types.hooks.platforms.ANY = rockspec_types.hooks -manifest_types = { +local manifest_types = { MUST_repository = { -- packages ANY = { @@ -176,8 +184,10 @@ local function type_check_item(name, item, expected, context) return nil, "Type mismatch on field "..context..name..": expected a string" end if expected ~= "string" then - if not item:match("^"..expected.."$") then - return nil, "Type mismatch on field "..context..name..": invalid value "..item + if item_type ~= "string" then + return nil, "Type mismatch on field "..context..name..": expected a string, got a "..type(item) + elseif not item:match("^"..expected.."$") then + return nil, "Type mismatch on field "..context..name..": invalid value "..item.." does not match '"..expected.."'" end end elseif expected_type == "table" then @@ -240,18 +250,35 @@ type_check_table = function(tbl, types, context) return true end +local function check_undeclared_globals(globals, types) + local undeclared = {} + for glob, _ in pairs(globals) do + if not (types[glob] or types["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 -- mismatches. -- @return boolean or (nil, string): true if type checking -- succeeded, or nil and an error message if it failed. -function type_check_rockspec(rockspec) +function type_check.type_check_rockspec(rockspec, globals) assert(type(rockspec) == "table") if rockspec.rockspec_format then -- relies on global state - load_extensions() + type_check.load_extensions() end + local ok, err = check_undeclared_globals(globals, rockspec_types) + if not ok then return nil, err end return type_check_table(rockspec, rockspec_types, "") end @@ -261,7 +288,11 @@ end -- mismatches. -- @return boolean or (nil, string): true if type checking -- succeeded, or nil and an error message if it failed. -function type_check_manifest(manifest) +function type_check.type_check_manifest(manifest, globals) assert(type(manifest) == "table") + local ok, err = check_undeclared_globals(globals, manifest_types) + if not ok then return nil, err end return type_check_table(manifest, manifest_types, "") end + +return type_check diff --git a/luarocks/src/luarocks/unpack.lua b/luarocks/src/luarocks/unpack.lua index 6715381..9204e26 100644 --- a/luarocks/src/luarocks/unpack.lua +++ b/luarocks/src/luarocks/unpack.lua @@ -1,7 +1,9 @@ --- Module implementing the LuaRocks "unpack" command. -- Unpack the contents of a rock. -module("luarocks.unpack", package.seeall) +--module("luarocks.unpack", package.seeall) +local unpack = {} +package.loaded["luarocks.unpack"] = unpack local fetch = require("luarocks.fetch") local fs = require("luarocks.fs") @@ -9,12 +11,14 @@ local util = require("luarocks.util") local build = require("luarocks.build") local dir = require("luarocks.dir") -help_summary = "Unpack the contents of a rock." -help_arguments = "{<rock>|<name> [<version>]}" -help = [[ +unpack.help_summary = "Unpack the contents of a rock." +unpack.help_arguments = "[--force] {<rock>|<name> [<version>]}" +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 @@ -31,14 +35,17 @@ local function unpack_rockspec(rockspec_file, dir_name) if not rockspec then return nil, "Failed loading rockspec "..rockspec_file..": "..err end - fs.change_dir(dir_name) + 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 - fs.change_dir(dir_name) + ok, err = fs.change_dir(sources_dir) + if not ok then return nil, err end build.apply_patches(rockspec) fs.pop_dir() + fs.pop_dir() return rockspec end @@ -57,7 +64,8 @@ local function unpack_rock(rock_file, dir_name, kind) if not ok then return nil, "Failed unzipping rock "..rock_file, errcode end - fs.change_dir(dir_name) + 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 @@ -69,7 +77,8 @@ local function unpack_rock(rock_file, dir_name, kind) if not ok then return nil, err end - fs.change_dir(rockspec.source.dir) + ok, err = fs.change_dir(rockspec.source.dir) + if not ok then return nil, err end build.apply_patches(rockspec) fs.pop_dir() end @@ -83,7 +92,7 @@ end -- @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) +local function run_unpacker(file, force) assert(type(file) == "string") local base_name = dir.base_name(file) @@ -95,11 +104,15 @@ local function run_unpacker(file) if not extension then return nil, file.." does not seem to be a valid filename." end - - if (fs.exists(dir_name)) then + + local exists = fs.exists(dir_name) + if exists and not force then return nil, "Directory "..dir_name.." already exists." end - fs.make_dir(dir_name) + 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 @@ -134,7 +147,7 @@ end -- version may also be passed. -- @return boolean or (nil, string): true if successful or nil followed -- by an error message. -function run(...) +function unpack.run(...) local flags, name, version = util.parse_flags(...) assert(type(version) == "string" or not version) @@ -143,9 +156,11 @@ function run(...) end if name:match(".*%.rock") or name:match(".*%.rockspec") then - return run_unpacker(name) + return run_unpacker(name, flags["force"]) else local search = require("luarocks.search") return search.act_on_src_or_rockspec(run_unpacker, name, version) end end + +return unpack diff --git a/luarocks/src/luarocks/upload.lua b/luarocks/src/luarocks/upload.lua new file mode 100644 index 0000000..d87313a --- /dev/null +++ b/luarocks/src/luarocks/upload.lua @@ -0,0 +1,92 @@ + +local upload = {} + +local util = require("luarocks.util") +local fetch = require("luarocks.fetch") +local pack = require("luarocks.pack") +local cfg = require("luarocks.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=<key>] [--force] <rockspec>" +upload.help = [[ +<rockspec> 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=<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.run(...) + local flags, fname = util.parse_flags(...) + 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"] 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 + util.printout(("Sending " .. tostring(rock_fname) .. " ...")) + res, err = api:method("upload_rock/" .. tostring(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/luarocks/src/luarocks/upload/api.lua b/luarocks/src/luarocks/upload/api.lua new file mode 100644 index 0000000..c588335 --- /dev/null +++ b/luarocks/src/luarocks/upload/api.lua @@ -0,0 +1,270 @@ + +local api = {} + +local cfg = require("luarocks.cfg") +local fs = require("luarocks.fs") +local util = require("luarocks.util") +local persist = require("luarocks.persist") +local multipart = require("luarocks.upload.multipart") + +local Api = {} + +local function upload_config_file() + local _, _, home_conf, home_ok = cfg.which_config() + if not home_conf then + return nil + end + return (home_conf:gsub("/[^/]+$", "/upload_config.lua")) +end + +function Api:load_config() + local upload_conf = upload_config_file() + print(upload_conf) + if not upload_conf then return nil end + local cfg, err = persist.load_into_table(upload_conf) + return cfg +end + +function Api:save_config() + -- Test configuration before saving it. + local res, err = self:raw_method("status") + if not res then + return nil, err + end + if res.errors then + util.printerr("Server says: " .. tostring(res.errors[1])) + return + end + local upload_conf = upload_config_file() + if not upload_conf then return nil end + persist.save_from_table(upload_conf, self.config) + fs.chmod(upload_conf, "0600") +end + +function Api:check_version() + if not self._server_tool_version then + local tool_version = cfg.upload.tool_version + local res, err = self:request(tostring(self.config.server) .. "/api/tool_version", { + current = tool_version + }) + if not res then + return nil, err + end + if not res.version then + return nil, "failed to fetch tool version" + end + self._server_tool_version = res.version + if res.force_update then + return nil, "Your upload client is too out of date to continue, please upgrade LuaRocks." + end + if res.version ~= tool_version then + util.printerr("Warning: Your LuaRocks is out of date, consider upgrading.") + end + end + return true +end + +function Api:method(...) + local res, err = self:raw_method(...) + if not res then + return nil, err + end + if res.errors then + if res.errors[1] == "Invalid key" then + return nil, res.errors[1] .. " (use the --api-key flag to change)" + end + local msg = table.concat(res.errors, ", ") + return nil, "API Failed: " .. msg + end + return res +end + +function Api:raw_method(path, ...) + self:check_version() + local url = tostring(self.config.server) .. "/api/" .. tostring(cfg.upload.api_version) .. "/" .. tostring(self.config.key) .. "/" .. tostring(path) + return self:request(url, ...) +end + +local function encode_query_string(t, sep) + if sep == nil then + sep = "&" + end + local i = 0 + local buf = { } + for k, v in pairs(t) do + if type(k) == "number" and type(v) == "table" then + k, v = v[1], v[2] + end + buf[i + 1] = multipart.url_escape(k) + buf[i + 2] = "=" + buf[i + 3] = multipart.url_escape(v) + buf[i + 4] = sep + i = i + 4 + end + buf[i] = nil + return table.concat(buf) +end + +-- An ode to the multitude of JSON libraries out there... +local function require_json() + for _, lib in ipairs({ "cjson", "dkjson", "json" }) do + local json_ok, json = pcall(require, lib) + if json_ok then + return json_ok, json + end + end + return nil +end + +local ltn12_ok, ltn12 = pcall(require, "ltn12") +if not ltn12_ok then -- If not using LuaSocket and/or LuaSec... + +function Api:request(url, params, post_params) + local vars = cfg.variables + local json_ok, json = require_json() + if not json_ok then return nil, "A JSON library is required for this command." end + + if cfg.downloader == "wget" then + local curl_ok = fs.execute_quiet(vars.CURL, "--version") + if not curl_ok then + return nil, "Missing network helper program 'curl'.\nMake sure 'curl' is installed and available from your path." + end + end + + if not self.config.key then + return nil, "Must have API key before performing any actions." + end + local body + local headers = {} + if params and next(params) then + url = url .. ("?" .. encode_query_string(params)) + end + local method = "GET" + local out + local tmpfile = fs.tmpname() + if post_params then + method = "POST" + local curl_cmd = fs.Q(vars.CURL).." -f -k -L --silent --user-agent \""..cfg.user_agent.." via curl\" " + for k,v in pairs(post_params) do + local var = v + if type(v) == "table" then + var = "@"..v.fname + end + curl_cmd = curl_cmd .. "--form \""..k.."="..var.."\" " + end + if cfg.connection_timeout and cfg.connection_timeout > 0 then + curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." " + end + ok = fs.execute_string(curl_cmd..fs.Q(url).." -o "..fs.Q(tmpfile)) + else + local ok, err = fs.download(url, tmpfile) + if not ok then + return nil, "API failure: " .. tostring(err) .. " - " .. tostring(url) + end + end + + local tmpfd = io.open(tmpfile) + if not tmpfd then + os.remove(tmpfile) + return nil, "API failure reading temporary file - " .. tostring(url) + end + out = tmpfd:read("*a") + tmpfd:close() + os.remove(tmpfile) + + if self.debug then + util.printout("[" .. tostring(method) .. " via curl] " .. tostring(url) .. " ... ") + end + + return json.decode(out) +end + +else -- use LuaSocket and LuaSec + +local warned_luasec = false + +function Api:request(url, params, post_params) + local json_ok, json = require_json() + if not json_ok then return nil, "A JSON library is required for this command." end + local server = tostring(self.config.server) + local http_ok, http + local via = "luasocket" + if server:match("^https://") then + http_ok, http = pcall(require, "ssl.https") + if http_ok then + via = "luasec" + else + if not warned_luasec then + util.printerr("LuaSec is not available; using plain HTTP. Install 'luasec' to enable HTTPS.") + warned_luasec = true + end + http_ok, http = pcall(require, "socket.http") + server = server:gsub("^https", "http") + url = url:gsub("^https", "http") + via = "luasocket" + end + else + http_ok, http = pcall(require, "socket.http") + end + if not http_ok then + return nil, "Failed loading socket library!" + end + + if not self.config.key then + return nil, "Must have API key before performing any actions." + end + local body + local headers = {} + if params and next(params) then + url = url .. ("?" .. encode_query_string(params)) + end + if post_params then + local boundary + body, boundary = multipart.encode(post_params) + headers["Content-length"] = #body + headers["Content-type"] = "multipart/form-data; boundary=" .. tostring(boundary) + end + local method = post_params and "POST" or "GET" + if self.debug then + util.printout("[" .. tostring(method) .. " via "..via.."] " .. tostring(url) .. " ... ") + end + local out = {} + local _, status = http.request({ + url = url, + headers = headers, + method = method, + sink = ltn12.sink.table(out), + source = body and ltn12.source.string(body) + }) + if self.debug then + util.printout(tostring(status)) + end + if status ~= 200 then + return nil, "API returned " .. tostring(status) .. " - " .. tostring(url) + end + return json.decode(table.concat(out)) +end + +end + +function api.new(flags, name) + local self = {} + setmetatable(self, { __index = Api }) + self.config = self:load_config() or {} + self.config.server = flags["server"] or self.config.server or cfg.upload.server + self.config.version = self.config.version or cfg.upload.version + self.config.key = flags["api-key"] or self.config.key + self.debug = flags["debug"] + if not self.config.key then + return nil, "You need an API key to upload rocks.\n" .. + "Navigate to "..self.config.server.."/settings to get a key\n" .. + "and then pass it through the --api-key=<key> flag." + end + if flags["api-key"] then + self:save_config() + end + return self +end + +return api + diff --git a/luarocks/src/luarocks/upload/multipart.lua b/luarocks/src/luarocks/upload/multipart.lua new file mode 100644 index 0000000..5677657 --- /dev/null +++ b/luarocks/src/luarocks/upload/multipart.lua @@ -0,0 +1,112 @@ + +local multipart = {} + +local File = {} + +local unpack = unpack or table.unpack + +math.randomseed(os.time()) + +-- socket.url.escape(s) from LuaSocket 3.0rc1 +function multipart.url_escape(s) + return (string.gsub(s, "([^A-Za-z0-9_])", function(c) + return string.format("%%%02x", string.byte(c)) + end)) +end + +function File:mime() + if not self.mimetype then + local mimetypes_ok, mimetypes = pcall(require, "mimetypes") + if mimetypes_ok then + self.mimetype = mimetypes.guess(self.fname) + else + self.mimetype = "application/octet-stream" + end + end + return self.mimetype +end + +function File:content() + local fd = io.open(self.fname) + if not fd then + return nil, "Failed to open file: "..self.fname + end + local data = fd:read("*a") + fd:close() + return data +end + +local function rand_string(len) + local shuffled = {} + for i = 1, len do + local r = math.random(97, 122) + if math.random() >= 0.5 then + r = r - 32 + end + shuffled[i] = r + end + return string.char(unpack(shuffled)) +end + +-- multipart encodes params +-- returns encoded string,boundary +-- params is an a table of tuple tables: +-- params = { +-- {key1, value2}, +-- {key2, value2}, +-- key3: value3 +-- } +function multipart.encode(params) + local tuples = { } + for i = 1, #params do + tuples[i] = params[i] + end + for k,v in pairs(params) do + if type(k) == "string" then + table.insert(tuples, {k, v}) + end + end + local chunks = {} + for _, tuple in ipairs(tuples) do + local k,v = unpack(tuple) + k = multipart.url_escape(k) + local buffer = { 'Content-Disposition: form-data; name="' .. k .. '"' } + local content + if type(v) == "table" and v.__class == File then + buffer[1] = buffer[1] .. ('; filename="' .. v.fname:gsub(".*/", "") .. '"') + table.insert(buffer, "Content-type: " .. v:mime()) + content = v:content() + else + content = v + end + table.insert(buffer, "") + table.insert(buffer, content) + table.insert(chunks, table.concat(buffer, "\r\n")) + end + local boundary + while not boundary do + boundary = "Boundary" .. rand_string(16) + for _, chunk in ipairs(chunks) do + if chunk:find(boundary) then + boundary = nil + break + end + end + end + local inner = "\r\n--" .. boundary .. "\r\n" + return table.concat({ "--", boundary, "\r\n", + table.concat(chunks, inner), + "\r\n", "--", boundary, "--", "\r\n" }), boundary +end + +function multipart.new_file(fname, mime) + local self = {} + setmetatable(self, { __index = File }) + self.__class = File + self.fname = fname + self.mimetype = mime + return self +end + +return multipart + diff --git a/luarocks/src/luarocks/util.lua b/luarocks/src/luarocks/util.lua index ba20acf..8772883 100644 --- a/luarocks/src/luarocks/util.lua +++ b/luarocks/src/luarocks/util.lua @@ -4,9 +4,10 @@ -- inside specific functions) to avoid interdependencies, -- as this is used in the bootstrapping stage of luarocks.cfg. -local global_env = _G +--module("luarocks.util", package.seeall) +local util = {} -module("luarocks.util", package.seeall) +local unpack = unpack or table.unpack local scheduled_functions = {} local debug = require("debug") @@ -18,7 +19,7 @@ local debug = require("debug") -- @param ... arguments to be passed to function. -- @return table: A token representing the scheduled execution, -- which can be used to remove the item later from the list. -function schedule_function(f, ...) +function util.schedule_function(f, ...) assert(type(f) == "function") local item = { fn = f, args = {...} } @@ -30,7 +31,7 @@ end -- This is useful for cancelling a rollback of a completed operation. -- @param item table: The token representing the scheduled function that was -- returned from the schedule_function call. -function remove_scheduled_function(item) +function util.remove_scheduled_function(item) for k, v in pairs(scheduled_functions) do if v == item then table.remove(scheduled_functions, k) @@ -44,7 +45,7 @@ end -- corresponding cleanup functions. Calling this function will run -- these function, erasing temporaries. -- Functions are executed in the inverse order they were scheduled. -function run_scheduled_functions() +function util.run_scheduled_functions() local fs = require("luarocks.fs") fs.change_dir_to_root() for i = #scheduled_functions, 1, -1 do @@ -58,7 +59,7 @@ end -- so it does not include beginning- and end-of-string markers (^$) -- @param s string: The input string -- @return string: The equivalent pattern -function matchquote(s) +function util.matchquote(s) return (s:gsub("[?%-+*%[%].%%()$^]","%%%1")) end @@ -67,7 +68,7 @@ end -- For example, given "foo", "--tux=beep", "--bla", "bar", "--baz", -- it would return the following: -- {["bla"] = true, ["tux"] = "beep", ["baz"] = true}, "foo", "bar". -function parse_flags(...) +function util.parse_flags(...) local args = {...} local flags = {} for i = #args, 1, -1 do @@ -92,7 +93,7 @@ end -- in the flags table. If no flags are passed as varargs, the -- entire flags table is forwarded. -- @return string... A variable number of strings -function forward_flags(flags, ...) +function util.forward_flags(flags, ...) assert(type(flags) == "table") local out = {} local filter = select('#', ...) @@ -121,14 +122,14 @@ end -- @param dst Destination table, which will receive src's contents. -- @param src Table which provides new contents to dst. -- @see platform_overrides -function deep_merge(dst, src) +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 - deep_merge(dst[k], v) + util.deep_merge(dst[k], v) else dst[k] = v end @@ -151,7 +152,7 @@ end -- tbl.x are preserved). -- @param tbl table or nil: Table which may contain a "platforms" field; -- if it doesn't (or if nil is passed), this function does nothing. -function platform_overrides(tbl) +function util.platform_overrides(tbl) assert(type(tbl) == "table" or not tbl) local cfg = require("luarocks.cfg") @@ -162,7 +163,7 @@ function platform_overrides(tbl) for _, platform in ipairs(cfg.platforms) do local platform_tbl = tbl.platforms[platform] if platform_tbl then - deep_merge(tbl, platform_tbl) + util.deep_merge(tbl, platform_tbl) end end end @@ -194,15 +195,15 @@ end -- @param needed_set: a set where keys are the names of -- needed variables. -- @param msg string: the warning message to display. -function warn_if_not_used(var_defs, needed_set, msg) +function util.warn_if_not_used(var_defs, needed_set, msg) needed_set = make_shallow_copy(needed_set) - for var,val in pairs(var_defs) do + for _, val in pairs(var_defs) do for used in val:gmatch(var_format_pattern) do needed_set[used] = nil end end - for var,_ in pairs(needed_set) do - warning(msg:format(var)) + for var, _ in pairs(needed_set) do + util.warning(msg:format(var)) end end @@ -213,7 +214,7 @@ local function warn_failed_matches(line) local any_failed = false if line:match(var_format_pattern) then for unmatched in line:gmatch(var_format_pattern) do - warning("unmatched variable " .. unmatched) + util.warning("unmatched variable " .. unmatched) any_failed = true end end @@ -228,7 +229,7 @@ end -- @param tbl table: Table to have its string values modified. -- @param vars table: Table containing string-string key-value pairs -- representing variables to replace in the strings values of tbl. -function variable_substitutions(tbl, vars) +function util.variable_substitutions(tbl, vars) assert(type(tbl) == "table") assert(type(vars) == "table") @@ -249,7 +250,7 @@ end --- Return an array of keys of a table. -- @param tbl table: The input table. -- @return table: The array of keys. -function keys(tbl) +function util.keys(tbl) local ks = {} for k,_ in pairs(tbl) do table.insert(ks, k) @@ -277,7 +278,7 @@ end -- to be used by table.sort when sorting keys. -- @see sortedpairs local function sortedpairs_iterator(tbl, sort_function) - local ks = keys(tbl) + 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 @@ -315,41 +316,50 @@ end -- is a string representing the field name, and the second element is a priority table -- for that key. -- @return function: the iterator function. -function sortedpairs(tbl, sort_function) +function util.sortedpairs(tbl, sort_function) return coroutine.wrap(function() sortedpairs_iterator(tbl, sort_function) end) end -function starts_with(s, prefix) +function util.lua_versions() + local versions = { "5.1", "5.2", "5.3" } + local i = 0 + return function() + i = i + 1 + return versions[i] + end +end + +function util.starts_with(s, prefix) return s:sub(1,#prefix) == prefix end --- Print a line to standard output -function printout(...) +function util.printout(...) io.stdout:write(table.concat({...},"\t")) io.stdout:write("\n") end --- Print a line to standard error -function printerr(...) +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 warning(msg) - printerr("Warning: "..msg) +function util.warning(msg) + util.printerr("Warning: "..msg) end -function title(msg, porcelain, underline) +function util.title(msg, porcelain, underline) if porcelain then return end - printout() - printout(msg) - printout((underline or "-"):rep(#msg)) - printout() + util.printout() + util.printout(msg) + util.printout((underline or "-"):rep(#msg)) + util.printout() end -function this_program(default) +function util.this_program(default) local i = 1 local last, cur = default, default while i do @@ -362,13 +372,32 @@ function this_program(default) return last:sub(2) end -function see_help(command, program) - return "See '"..this_program(program or "luarocks")..' help '..command.."'." +function util.deps_mode_help(program) + local cfg = require("luarocks.cfg") + return [[ +--deps-mode=<mode> How to handle dependencies. Four modes are supported: + * all - use all trees from the rocks_trees list + for finding dependencies + * one - use only the current tree (possibly set + with --tree) + * order - use trees based on order (use the current + tree and all trees below it on the rocks_trees list) + * none - ignore dependencies altogether. + The default mode may be set with the deps_mode entry + in the configuration file. + The current default is "]]..cfg.deps_mode..[[". + Type ']]..util.this_program(program or "luarocks")..[[' with no arguments to see + your list of rocks trees. +]] +end + +function util.see_help(command, program) + return "See '"..util.this_program(program or "luarocks")..' help'..(command and " "..command or "").."'." end -- from http://lua-users.org/wiki/SplitJoin -- by PhilippeLhoste -function split_string(str, delim, maxNb) +function util.split_string(str, delim, maxNb) -- Eliminate bad cases... if string.find(str, delim) == nil then return { str } @@ -397,12 +426,13 @@ end -- 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 remove_path_dupes(list, sep) +function util.remove_path_dupes(list, sep) assert(type(list) == "string") assert(type(sep) == "string") - local parts = split_string(list, sep) + 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 @@ -428,7 +458,7 @@ end -- @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 show_table(t, name, indent) +function util.show_table(t, name, indent) local cart -- a container local autoref -- for self references @@ -494,7 +524,7 @@ function show_table(t, name, indent) return cart .. autoref end -function array_contains(tbl, value) +function util.array_contains(tbl, value) for _, v in ipairs(tbl) do if v == value then return true @@ -502,3 +532,12 @@ function array_contains(tbl, value) end return false end + +-- Quote Lua string, analogous to fs.Q. +-- @param s A string, such as "hello" +-- @return string: A quoted string, such as '"hello"' +function util.LQ(s) + return ("%q"):format(s) +end + +return util diff --git a/luarocks/src/luarocks/validate.lua b/luarocks/src/luarocks/validate.lua index 1e1e69e..e6e09c3 100644 --- a/luarocks/src/luarocks/validate.lua +++ b/luarocks/src/luarocks/validate.lua @@ -1,6 +1,8 @@ --- Sandboxed test of build/install of all packages in a repository (unfinished and disabled). -module("luarocks.validate", package.seeall) +--module("luarocks.validate", package.seeall) +local validate = {} +package.loaded["luarocks.validate"] = validate local fs = require("luarocks.fs") local dir = require("luarocks.dir") @@ -10,9 +12,9 @@ local build = require("luarocks.build") local install = require("luarocks.install") local util = require("luarocks.util") -help_summary = "Sandboxed test of build/install of all packages in a repository." +validate.help_summary = "Sandboxed test of build/install of all packages in a repository." -help = [[ +validate.help = [[ <argument>, if given, is a local repository pathname. ]] @@ -73,7 +75,12 @@ local function validate_rock(file) return ok, err, errcode end -local function validate(repo, flags) +function validate.run(...) + local flags, repo = util.parse_flags(...) + repo = repo or cfg.rocks_dir + + util.printout("Verifying contents of "..repo) + local results = { ok = {} } @@ -85,7 +92,7 @@ local function validate(repo, flags) if not fs.exists(repo) then return nil, repo.." is not a local repository." end - for _, file in pairs(fs.list_dir(repo)) do for _=1,1 do + for file in fs.dir(repo) do for _=1,1 do if file == "manifest" or file == "index.html" then break -- continue for end @@ -149,12 +156,5 @@ local function validate(repo, flags) return true end -function run(...) - local flags, repo = util.parse_flags(...) - repo = repo or cfg.rocks_dir - - util.printout("Verifying contents of "..repo) - - return validate(repo, flags) -end +return validate diff --git a/luarocks/src/luarocks/write_rockspec.lua b/luarocks/src/luarocks/write_rockspec.lua new file mode 100644 index 0000000..403cbc8 --- /dev/null +++ b/luarocks/src/luarocks/write_rockspec.lua @@ -0,0 +1,340 @@ + +--module("luarocks.write_rockspec", package.seeall) +local write_rockspec = {} +package.loaded["luarocks.write_rockspec"] = write_rockspec + +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") + +write_rockspec.help_summary = "Write a template for a rockspec file." +write_rockspec.help_arguments = "[--output=<file> ...] [<name>] [<version>] {<url>|<path>}" +write_rockspec.help = [[ +This command writes an initial version of a rockspec file, +based on an URL or a local path. You may use a relative path such as '.'. +If a local path is given, name and version arguments are mandatory. +For URLs, LuaRocks will attempt to infer name and version if not given. + +If a repository URL is given with no version, it creates an 'scm' rock. + +Note that the generated file is a _starting point_ for writing a +rockspec, and is not guaranteed to be complete or correct. + +--output=<file> 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="<string>" A license string, such as "MIT/X11" or "GNU GPL v3". +--summary="<txt>" A short one-line description summary. +--detailed="<txt>" A longer description string. +--homepage=<url> Project homepage. +--lua-version=<ver> 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". +--tag=<tag> Tag to use. Will attempt to extract version number from it. +--lib=<lib>[,<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=<ver>. "..util.see_help("write_rockspec")) + end +end + +local function detect_description(rockspec) + 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 + if paragraph then + if #paragraph < 80 then + rockspec.description.summary = paragraph:gsub("\n", "") + rockspec.description.detailed = paragraph + else + local summary = paragraph:gsub("\n", " "):match("([^.]*%.) ") + if summary then + rockspec.description.summary = summary:gsub("\n", "") + end + rockspec.description.detailed = paragraph + end + end +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 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 +end + +function write_rockspec.run(...) + local flags, name, version, url_or_dir = util.parse_flags(...) + + if not name then + return nil, "Missing arguments. "..util.see_help("write_rockspec") + end + + if name and not version then + url_or_dir = name + name = nil + elseif not url_or_dir then + url_or_dir = version + end + + if flags["tag"] == true then + return nil, "Incorrect usage: --tag requires an argument. "..util.see_help("write_rockspec") + 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 not fetch.is_basic_protocol(protocol) then + if not name then + name = dir.base_name(url_or_dir):gsub("%.[^.]+$", "") + end + if not version then + version = "scm" + end + elseif protocol ~= "file" then + local filename = dir.base_name(url_or_dir) + local newname, newversion = filename:match("(.*)-([^-]+)") + if (not name) and newname then + name = newname + end + if (not version) and newversion then + version = newversion:gsub(".[a-z]+$", ""):gsub(".tar$", "") + end + if not (name and version) then + return nil, "Missing name and version arguments. "..util.see_help("write_rockspec") + end + elseif not version then + return nil, "Missing name and version arguments. "..util.see_help("write_rockspec") + end + + local filename = flags["output"] or dir.path(fs.current_dir(), name:lower().."-"..version.."-1.rockspec") + + if not flags["homepage"] and url_or_dir:match("^git://github.com") then + flags["homepage"] = "http://"..url_or_dir:match("^[^:]+://(.*)") + end + + local rockspec = { + 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 + + 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 + end + + if not local_dir then + local_dir = "." + 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 + + detect_description(rockspec) + + 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/luarocks/test/testfiles/missing_external-0.1-1.rockspec b/luarocks/test/testfiles/missing_external-0.1-1.rockspec new file mode 100644 index 0000000..5f8e621 --- /dev/null +++ b/luarocks/test/testfiles/missing_external-0.1-1.rockspec @@ -0,0 +1,24 @@ +package = "missing_external" +version = "0.1-1" +source = { + -- any valid URL + url = "https://raw.github.com/keplerproject/luarocks/master/src/luarocks/build.lua" +} +description = { + summary = "Missing external dependency", +} +external_dependencies = { + INEXISTENT = { + library = "inexistentlib*", + header = "inexistentheader*.h", + } +} +dependencies = { + "lua >= 5.1" +} +build = { + type = "builtin", + modules = { + build = "build.lua" + } +} diff --git a/luarocks/test/testing.sh b/luarocks/test/testing.sh index c06bd27..b0d95c3 100755 --- a/luarocks/test/testing.sh +++ b/luarocks/test/testing.sh @@ -1,4 +1,4 @@ -#!/bin/sh -e +#!/bin/bash -e # Setup ######################################### @@ -7,13 +7,45 @@ exit 1 } +if [ -z "$*" ] +then + ps aux | grep -q '[s]shd' || { + echo "Run sudo /bin/sshd in order to perform all tests." + exit 1 + } +fi + +if [ "$1" == "--travis" ] +then + travis=true + shift +fi + +luaversion=5.1.5 + +if [ "$1" == "--lua" ] +then + shift + luaversion=$1 + shift +fi + testing_dir="$PWD" -testing_tree="$testing_dir/testing" -testing_sys_tree="$testing_dir/testing_sys" -testing_tree_copy="$testing_dir/testing_copy" -testing_sys_tree_copy="$testing_dir/testing_sys_copy" -testing_cache="$testing_dir/testing_cache" +testing_tree="$testing_dir/testing-$luaversion" +testing_sys_tree="$testing_dir/testing_sys-$luaversion" +testing_tree_copy="$testing_dir/testing_copy-$luaversion" +testing_sys_tree_copy="$testing_dir/testing_sys_copy-$luaversion" +testing_cache="$testing_dir/testing_cache-$luaversion" +testing_server="$testing_dir/testing_server-$luaversion" + + +if [ "$1" == "--clean" ] +then + shift + rm -rf "$testing_cache" + rm -rf "$testing_server" +fi [ "$1" ] || rm -f luacov.stats.out rm -f luacov.report.out @@ -23,12 +55,25 @@ rm -rf "$testing_tree" rm -rf "$testing_sys_tree" rm -rf "$testing_tree_copy" rm -rf "$testing_sys_tree_copy" -rm -rf "$testing_cache" +rm -rf "$testing_dir/testing_config.lua" +rm -rf "$testing_dir/testing_config_show_downloads.lua" +rm -rf "$testing_dir/testing_config_sftp.lua" +rm -rf "$testing_dir/luacov.config" + +mkdir -p "$testing_cache" + +[ "$1" = "clean" ] && { + rm -f luacov.stats.out + exit 0 +} cat <<EOF > $testing_dir/testing_config.lua rocks_trees = { "$testing_tree", - "$testing_sys_tree", + { name = "system", root = "$testing_sys_tree" }, +} +rocks_servers = { + "$testing_server" } local_cache = "$testing_cache" upload_server = "testing" @@ -38,7 +83,23 @@ upload_servers = { rsync = "localhost/tmp/luarocks_testing", }, } +external_deps_dirs = { + "/usr/local", + "/usr", + -- These are used for a test that fails, so it + -- can point to invalid paths: + { + prefix = "/opt", + bin = "bin", + include = "include", + lib = { "lib", "lib64" }, + } +} EOF +( + cat $testing_dir/testing_config.lua + echo "show_downloads = true" +) > $testing_dir/testing_config_show_downloads.lua cat <<EOF > $testing_dir/testing_config_sftp.lua rocks_trees = { "$testing_tree", @@ -76,23 +137,123 @@ export LUAROCKS_CONFIG="$testing_dir/testing_config.lua" export LUA_PATH= export LUA_CPATH= -luadir="/Programs/Lua/Current" +if [ "$travis" ] +then + luadir=/tmp/lua-$luaversion + pushd /tmp + 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 + fi + popd +else + luadir="/Programs/Lua/Current" +fi + +if [ `uname -m` = i686 ] +then + platform="linux-x86" +else + platform="linux-x86_64" +fi + lua="$luadir/bin/lua" +version_luasocket=3.0rc1 +verrev_luasocket=${version_luasocket}-1 +srcdir_luasocket=luasocket-3.0-rc1 + +version_cprint=0.1 +verrev_cprint=0.1-1 + +version_luacov=0.5 +verrev_luacov=0.5-1 +version_lxsh=0.8.6 +version_validate_args=1.5.4 +verrev_validate_args=1.5.4-1 +verrev_lxsh=${version_lxsh}-2 + +luasec=luasec + cd .. ./configure --with-lua="$luadir" make clean make src/luarocks/site_config.lua make dev cd src - -echo $LUA_PATH - -luarocks_nocov="$lua $PWD/bin/luarocks" -luarocks="$lua -erequire('luacov.runner')('$testing_dir/luacov.config') $PWD/bin/luarocks" -luarocks_admin="$lua -erequire('luacov.runner')('$testing_dir/luacov.config') $PWD/bin/luarocks-admin" - -$luarocks_nocov download luacov +basedir=$PWD +run_lua() { + if [ "$1" = "--noecho" ]; then shift; noecho=1; else noecho=0; fi + if [ "$1" = "--nocov" ]; then shift; nocov=1; else nocov=0; fi + if [ "$noecho" = 0 ] + then + echo $* + fi + cmd=$1 + shift + if [ "$nocov" = 0 ] + then + "$lua" -e"require('luacov.runner')('$testing_dir/luacov.config')" "$basedir/bin/$cmd" "$@" + else + "$lua" "$basedir/bin/$cmd" "$@" + fi +} +luarocks="run_lua luarocks" +luarocks_nocov="run_lua --nocov luarocks" +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" + +################################################### + +mkdir -p "$testing_server" +( + cd "$testing_server" + luarocks_repo="http://luarocks.org/repositories/rocks" + luarocks_scm_repo="http://luarocks.org/repositories/rocks-scm" + get() { [ -e `basename "$1"` ] || wget -c "$1"; } + get "$luarocks_repo/luacov-${verrev_luacov}.src.rock" + get "$luarocks_repo/luacov-${verrev_luacov}.rockspec" + get "$luarocks_repo/luadoc-3.0.1-1.src.rock" + get "$luarocks_repo/lualogging-1.3.0-1.src.rock" + get "$luarocks_repo/luasocket-${verrev_luasocket}.src.rock" + get "$luarocks_repo/luasocket-${verrev_luasocket}.rockspec" + get "$luarocks_repo/luafilesystem-1.6.2-1.src.rock" + get "$luarocks_repo/stdlib-35-1.src.rock" + get "$luarocks_repo/luarepl-0.4-1.src.rock" + get "$luarocks_repo/validate-args-1.5.4-1.rockspec" + get "$luarocks_scm_repo/luasec-scm-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" + get "$luarocks_repo/cprint-${verrev_cprint}.rockspec" + get "$luarocks_repo/wsapi-1.6-1.src.rock" + get "$luarocks_repo/lxsh-${verrev_lxsh}.src.rock" + get "$luarocks_repo/abelhas-1.0-1.rockspec" + get "$luarocks_repo/lzlib-0.4.work3-1.src.rock" + get "$luarocks_repo/lpeg-0.12-1.src.rock" + get "$luarocks_repo/luaposix-31-1.src.rock" + get "$luarocks_repo/md5-1.2-1.src.rock" + get "$luarocks_repo/lrandom-20120430.51-1.src.rock" + get "$luarocks_repo/lrandom-20120430.52-1.src.rock" + get "$luarocks_repo/lrandom-20120430.51-1.rockspec" + get "$luarocks_repo/lrandom-20120430.52-1.rockspec" +) +$luarocks_admin_nocov make_manifest "$testing_server" + +################################################### + +checksum_path() { + ( cd "$1"; find . -printf "%s %p\n" | md5sum ) +} build_environment() { rm -rf "$testing_tree" @@ -101,21 +262,55 @@ build_environment() { rm -rf "$testing_sys_tree_copy" mkdir -p "$testing_tree" mkdir -p "$testing_sys_tree" + $luarocks_admin_nocov make_manifest "$testing_cache" for package in "$@" do - $luarocks_nocov build --tree="$testing_sys_tree" $package + $luarocks_nocov install --only-server="$testing_cache" --tree="$testing_sys_tree" $package || { + $luarocks_nocov build --tree="$testing_sys_tree" $package + $luarocks_nocov pack --tree="$testing_sys_tree" $package; mv $package-*.rock "$testing_cache" + } done - eval `$luarocks_nocov path --bin` + eval `$luarocks_noecho_nocov path --bin` cp -a "$testing_tree" "$testing_tree_copy" cp -a "$testing_sys_tree" "$testing_sys_tree_copy" + testing_tree_copy_md5=`checksum_path "$testing_tree_copy"` + testing_sys_tree_copy_md5=`checksum_path "$testing_sys_tree_copy"` } reset_environment() { - rm -rf "$testing_tree" - rm -rf "$testing_sys_tree" - cp -a "$testing_tree_copy" "$testing_tree" - cp -a "$testing_sys_tree_copy" "$testing_sys_tree" + testing_tree_md5=`checksum_path "$testing_tree"` + testing_sys_tree_md5=`checksum_path "$testing_sys_tree"` + if [ "$testing_tree_md5" != "$testing_tree_copy_md5" ] + then + rm -rf "$testing_tree" + cp -a "$testing_tree_copy" "$testing_tree" + fi + if [ "$testing_sys_tree_md5" != "$testing_sys_tree_copy_md5" ] + then + rm -rf "$testing_sys_tree" + cp -a "$testing_sys_tree_copy" "$testing_sys_tree" + fi +} + +need() { + echo "Obtaining $1 $2..." + if $luarocks show $1 &> /dev/null + then + echo "Already available" + return + fi + platrock="$1-$2.$platform.rock" + if [ ! -e "$testing_cache/$platrock" ] + then + echo "Building $1 $2..." + $luarocks_nocov build --pack-binary-rock $1 $2 + mv "$platrock" "$testing_cache" + fi + echo "Installing $1 $2..." + $luarocks_nocov install "$testing_cache/$platrock" + return } +need_luasocket() { need luasocket $verrev_luasocket; } # Tests ######################################### @@ -134,7 +329,10 @@ fail_lint_noarg() { $luarocks lint; } fail_search_noarg() { $luarocks search; } fail_show_noarg() { $luarocks show; } fail_unpack_noarg() { $luarocks unpack; } +fail_remove_noarg() { $luarocks remove; } +fail_doc_noarg() { $luarocks doc; } fail_new_version_noarg() { $luarocks new_version; } +fail_write_rockspec_noarg() { $luarocks write_rockspec; } fail_build_invalid() { $luarocks build invalid; } fail_download_invalid() { $luarocks download invalid; } @@ -146,77 +344,103 @@ fail_new_version_invalid() { $luarocks new_version invalid; } fail_make_norockspec() { $luarocks make; } fail_build_blank_arg() { $luarocks build --tree="" lpeg; } -test_build_withpatch() { $luarocks build luadoc; } -test_build_diffversion() { $luarocks build luacov 0.1; } +test_build_withpatch() { need_luasocket; $luarocks build luadoc; } +test_build_diffversion() { $luarocks build luacov ${version_luacov}; } test_build_command() { $luarocks build stdlib; } test_build_install_bin() { $luarocks build luarepl; } -fail_build_nohttps() { $luarocks install luasocket && $luarocks download --rockspec validate-args 1.5.4 && $luarocks build ./validate-args-1.5.4-1.rockspec && rm ./validate-args-1.5.4-1.rockspec; } -test_build_https() { $luarocks download --rockspec validate-args 1.5.4 && $luarocks install luasec && $luarocks build ./validate-args-1.5.4-1.rockspec && rm ./validate-args-1.5.4-1.rockspec; } -test_build_supported_platforms() { $luarocks build xctrl; } +test_build_nohttps() { need_luasocket; $luarocks download --rockspec validate-args ${verrev_validate_args} && $luarocks build ./validate-args-${version_validate_args}-1.rockspec && rm ./validate-args-${version_validate_args}-1.rockspec; } +test_build_https() { need_luasocket; $luarocks download --rockspec validate-args ${verrev_validate_args} && $luarocks install $luasec && $luarocks build ./validate-args-${verrev_validate_args}.rockspec && rm ./validate-args-${verrev_validate_args}.rockspec; } +test_build_supported_platforms() { $luarocks build lpty; } +fail_build_missing_external() { $luarocks build "$testing_dir/testfiles/missing_external-0.1-1.rockspec" INEXISTENT_INCDIR="/invalid/dir"; } + +test_build_deps_partial_match() { $luarocks build lrandom; } +test_build_show_downloads() { export LUAROCKS_CONFIG="$testing_dir/testing_config_show_downloads.lua" && $luarocks build alien; export LUAROCKS_CONFIG="$testing_dir/testing_config.lua"; } test_download_all() { $luarocks download --all validate-args && rm validate-args-*; } -test_download_rockspecversion() { $luarocks download --rockspec validate-args 1.5.4 && rm validate-args-*; } +test_download_rockspecversion() { $luarocks download --rockspec validate-args ${verrev_validate_args} && rm validate-args-*; } test_help() { $luarocks help; } -test_install_binaryrock() { $luarocks build luasocket && $luarocks pack luasocket && $luarocks install ./luasocket-2.0.2-5.linux-x86.rock && rm ./luasocket-2.0.2-5.linux-x86.rock; } +test_install_binaryrock() { $luarocks build --pack-binary-rock cprint && $luarocks install ./cprint-${verrev_cprint}.${platform}.rock && rm ./cprint-${verrev_cprint}.${platform}.rock; } test_install_with_bin() { $luarocks install wsapi; } +fail_install_notazipfile() { $luarocks install "$testing_dir/testfiles/not_a_zipfile-1.0-1.src.rock"; } -test_lint_ok() { $luarocks download --rockspec validate-args 1.5.4 && $luarocks lint ./validate-args-1.5.4-1.rockspec && rm ./validate-args-1.5.4-1.rockspec; } +test_lint_ok() { $luarocks download --rockspec validate-args ${verrev_validate_args} && $luarocks lint ./validate-args-${verrev_validate_args}.rockspec && rm ./validate-args-${verrev_validate_args}.rockspec; } +fail_lint_type_mismatch_string() { $luarocks lint "$testing_dir/testfiles/type_mismatch_string-1.0-1.rockspec"; } +fail_lint_type_mismatch_version() { $luarocks lint "$testing_dir/testfiles/type_mismatch_version-1.0-1.rockspec"; } +fail_lint_type_mismatch_table() { $luarocks lint "$testing_dir/testfiles/type_mismatch_table-1.0-1.rockspec"; } test_list() { $luarocks list; } test_list_porcelain() { $luarocks list --porcelain; } -test_make() { rm -rf ./luasocket-2.0.2-5 && $luarocks download --src luasocket && $luarocks unpack ./luasocket-2.0.2-5.src.rock && cd luasocket-2.0.2-5/luasocket-2.0.2 && $luarocks make && cd ../.. && rm -rf ./luasocket-2.0.2-5; } -test_make_pack_binary_rock() { rm -rf ./lxsh-0.8.6-1 && $luarocks download --src lxsh 0.8.6-1 && $luarocks unpack ./lxsh-0.8.6-1.src.rock && cd lxsh-0.8.6-1/lxsh-0.8.6-1 && $luarocks make --deps-mode=none --pack-binary-rock && [ -e ./lxsh-0.8.6-1.all.rock ] && cd ../.. && rm -rf ./lxsh-0.8.6-1; } +test_make_with_rockspec() { rm -rf ./luasocket-${verrev_luasocket} && $luarocks download --src luasocket && $luarocks unpack ./luasocket-${verrev_luasocket}.src.rock && cd luasocket-${verrev_luasocket}/${srcdir_luasocket} && $luarocks make luasocket-${verrev_luasocket}.rockspec && cd ../.. && rm -rf ./luasocket-${verrev_luasocket}; } +test_make_default_rockspec() { rm -rf ./lxsh-${verrev_lxsh} && $luarocks download --src lxsh ${verrev_lxsh} && $luarocks unpack ./lxsh-${verrev_lxsh}.src.rock && cd lxsh-${verrev_lxsh}/lxsh-${version_lxsh}-1 && $luarocks make && cd ../.. && rm -rf ./lxsh-${verrev_lxsh}; } +test_make_pack_binary_rock() { rm -rf ./lxsh-${verrev_lxsh} && $luarocks download --src lxsh ${verrev_lxsh} && $luarocks unpack ./lxsh-${verrev_lxsh}.src.rock && cd lxsh-${verrev_lxsh}/lxsh-${version_lxsh}-1 && $luarocks make --deps-mode=none --pack-binary-rock && [ -e ./lxsh-${verrev_lxsh}.all.rock ] && cd ../.. && rm -rf ./lxsh-${verrev_lxsh}; } +fail_make_which_rockspec() { rm -rf ./luasocket-${verrev_luasocket} && $luarocks download --src luasocket && $luarocks unpack ./luasocket-${verrev_luasocket}.src.rock && cd luasocket-${verrev_luasocket}/${srcdir_luasocket} && $luarocks make && cd ../.. && rm -rf ./luasocket-${verrev_luasocket}; } -test_new_version() { $luarocks download --rockspec luacov 0.1 && $luarocks new_version ./luacov-0.1-1.rockspec 0.2 && rm ./luacov-0.*; } +test_new_version() { $luarocks download --rockspec luacov ${version_luacov} && $luarocks new_version ./luacov-${version_luacov}-1.rockspec 0.2 && rm ./luacov-0.*; } +test_new_version_url() { $luarocks download --rockspec abelhas 1.0 && $luarocks new_version ./abelhas-1.0-1.rockspec 1.1 https://github.com/downloads/ittner/abelhas/abelhas-1.1.tar.gz && rm ./abelhas-*; } test_pack() { $luarocks list && $luarocks pack luacov && rm ./luacov-*.rock; } -test_pack_src() { $luarocks download --rockspec luasocket && $luarocks pack ./luasocket-2.0.2-5.rockspec && rm ./luasocket-2.0.2-*.rock; } +test_pack_src() { $luarocks install $luasec && $luarocks download --rockspec luasocket && $luarocks pack ./luasocket-${verrev_luasocket}.rockspec && rm ./luasocket-${version_luasocket}-*.rock; } test_path() { $luarocks path --bin; } +test_path_lr_path() { $luarocks path --lr-path; } +test_path_lr_cpath() { $luarocks path --lr-cpath; } +test_path_lr_bin() { $luarocks path --lr-bin; } fail_purge_missing_tree() { $luarocks purge --tree="$testing_tree"; } test_purge() { $luarocks purge --tree="$testing_sys_tree"; } -test_remove() { $luarocks build luacov 0.1 && $luarocks remove luacov 0.1; } -#fail_remove_deps() { $luarocks build luadoc && $luarocks remove luasocket; } +test_remove() { $luarocks build luacov ${version_luacov} && $luarocks remove luacov ${version_luacov}; } +test_remove_force() { need_luasocket; $luarocks build lualogging && $luarocks remove --force luasocket; } +fail_remove_deps() { need_luasocket; $luarocks build lualogging && $luarocks remove luasocket; } +fail_remove_invalid_name() { $luarocks remove invalid.rock; } test_search_found() { $luarocks search zlib; } test_search_missing() { $luarocks search missing_rock; } test_show() { $luarocks show luacov; } test_show_modules() { $luarocks show --modules luacov; } -test_show_depends() { $luarocks install luasec && $luarocks show luasec; } -test_show_oldversion() { $luarocks install luacov 0.1 && $luarocks show luacov 0.1; } +test_show_depends() { need_luasocket; $luarocks install $luasec && $luarocks show luasec; } +test_show_oldversion() { $luarocks install luacov ${version_luacov} && $luarocks show luacov ${version_luacov}; } -test_unpack_download() { rm -rf ./luasocket-2.0.2-5 && $luarocks unpack luasocket && rm -rf ./luasocket-2.0.2-5; } -test_unpack_src() { rm -rf ./luasocket-2.0.2-5 && $luarocks download --src luasocket && $luarocks unpack ./luasocket-2.0.2-5.src.rock && rm -rf ./luasocket-2.0.2-5; } -test_unpack_rockspec() { rm -rf ./luasocket-2.0.2-5 && $luarocks download --rockspec luasocket && $luarocks unpack ./luasocket-2.0.2-5.rockspec && rm -rf ./luasocket-2.0.2-5; } -test_unpack_binary() { rm -rf ./luasocket-2.0.2-5 && $luarocks build luasocket && $luarocks pack luasocket && $luarocks unpack ./luasocket-2.0.2-5.linux-x86.rock && rm -rf ./luasocket-2.0.2-5; } +test_unpack_download() { rm -rf ./cprint-${verrev_cprint} && $luarocks unpack cprint && rm -rf ./cprint-${verrev_cprint}; } +test_unpack_src() { rm -rf ./cprint-${verrev_cprint} && $luarocks download --src cprint && $luarocks unpack ./cprint-${verrev_cprint}.src.rock && rm -rf ./cprint-${verrev_cprint}; } +test_unpack_rockspec() { rm -rf ./cprint-${verrev_cprint} && $luarocks download --rockspec cprint && $luarocks unpack ./cprint-${verrev_cprint}.rockspec && rm -rf ./cprint-${verrev_cprint}; } +test_unpack_binary() { rm -rf ./cprint-${verrev_cprint} && $luarocks build cprint && $luarocks pack cprint && $luarocks unpack ./cprint-${verrev_cprint}.${platform}.rock && rm -rf ./cprint-${verrev_cprint}; } test_admin_help() { $luarocks_admin help; } test_admin_make_manifest() { $luarocks_admin make_manifest; } -test_admin_add_rsync() { $luarocks_admin --server=testing add ./luasocket-2.0.2-5.src.rock; } -test_admin_add_sftp() { export LUAROCKS_CONFIG="$testing_dir/testing_config_sftp.lua" && $luarocks_admin --server=testing add ./luasocket-2.0.2-5.src.rock; export LUAROCKS_CONFIG="$testing_dir/testing_config.lua"; } +test_admin_add_rsync() { if [ "$travis" ]; then return; fi; $luarocks_admin --server=testing add "$testing_server/luasocket-${verrev_luasocket}.src.rock"; } +test_admin_add_sftp() { if [ "$travis" ]; then return; fi; export LUAROCKS_CONFIG="$testing_dir/testing_config_sftp.lua" && $luarocks_admin --server=testing add ./luasocket-${verrev_luasocket}.src.rock; export LUAROCKS_CONFIG="$testing_dir/testing_config.lua"; } fail_admin_add_missing() { $luarocks_admin --server=testing add; } -fail_admin_invalidserver() { $luarocks_admin --server=invalid add ./luasocket-2.0.2-5.src.rock; } -fail_admin_invalidrock() { $luarocks_admin --server=testing add invalid; } -test_admin_refresh_cache() { $luarocks_admin --server=testing refresh_cache; } -test_admin_remove() { $luarocks_admin --server=testing remove luasocket; } +fail_admin_invalidserver() { $luarocks_admin --server=invalid add "$testing_server/luasocket-${verrev_luasocket}.src.rock"; } +fail_admin_invalidrock() { if [ "$travis" ]; then return 1; fi; $luarocks_admin --server=testing add invalid; } +test_admin_refresh_cache() { if [ "$travis" ]; then return; fi; $luarocks_admin --server=testing refresh_cache; } +test_admin_remove() { if [ "$travis" ]; then return; fi; $luarocks_admin --server=testing remove luasocket-${verrev_luasocket}.src.rock; } fail_admin_remove_missing() { $luarocks_admin --server=testing remove; } fail_deps_mode_invalid_arg() { $luarocks remove luacov --deps-mode; } -test_deps_mode_one() { $luarocks build --tree="$testing_sys_tree" lpeg && $luarocks list && $luarocks build --deps-mode=one --tree="$testing_tree" lxsh && [ `$luarocks list --tree="$testing_tree" --porcelain lpeg | wc -l` = 1 ]; } -test_deps_mode_order() { $luarocks build --tree="$testing_sys_tree" lpeg && $luarocks build --deps-mode=order --tree="$testing_tree" lxsh && [ `$luarocks list --tree="$testing_tree" --porcelain lpeg | wc -l` = 0 ]; } -test_deps_mode_order_sys() { $luarocks build --tree="$testing_tree" lpeg && $luarocks build --deps-mode=order --tree="$testing_sys_tree" lxsh && [ `$luarocks list --tree="$testing_sys_tree" --porcelain lpeg | wc -l` = 1 ]; } -test_deps_mode_all_sys() { $luarocks build --tree="$testing_tree" lpeg && $luarocks build --deps-mode=all --tree="$testing_sys_tree" lxsh && [ `$luarocks list --tree="$testing_sys_tree" --porcelain lpeg | wc -l` = 0 ]; } -test_deps_mode_none() { $luarocks build --tree="$testing_tree" --deps-mode=none lxsh; [ `$luarocks list --tree="$testing_tree" --porcelain lpeg | wc -l` = 0 ]; } -test_deps_mode_nodeps_alias() { $luarocks build --tree="$testing_tree" --nodeps lxsh; [ `$luarocks list --tree="$testing_tree" --porcelain lpeg | wc -l` = 0 ]; } -test_deps_mode_make_order() { $luarocks build --tree="$testing_sys_tree" lpeg && rm -rf ./lxsh-0.8.6-1 && $luarocks download --src lxsh 0.8.6-1 && $luarocks unpack ./lxsh-0.8.6-1.src.rock && cd lxsh-0.8.6-1/lxsh-0.8.6-1 && $luarocks make --tree="$testing_tree" --deps-mode=order && cd ../.. && [ `$luarocks list --tree="$testing_tree" --porcelain lpeg | wc -l` = 0 ] && rm -rf ./lxsh-0.8.6-1; } -test_deps_mode_make_order_sys() { $luarocks build --tree="$testing_tree" lpeg && rm -rf ./lxsh-0.8.6-1 && $luarocks download --src lxsh 0.8.6-1 && $luarocks unpack ./lxsh-0.8.6-1.src.rock && cd lxsh-0.8.6-1/lxsh-0.8.6-1 && $luarocks make --tree="$testing_sys_tree" --deps-mode=order && cd ../.. && [ `$luarocks list --tree="$testing_tree" --porcelain lpeg | wc -l` = 1 ] && rm -rf ./lxsh-0.8.6-1; } +test_deps_mode_one() { $luarocks build --tree="system" lpeg && $luarocks list && $luarocks build --deps-mode=one --tree="$testing_tree" lxsh && [ `$luarocks_noecho list --tree="$testing_tree" --porcelain lpeg | wc -l` = 1 ]; } +test_deps_mode_order() { $luarocks build --tree="system" lpeg && $luarocks build --deps-mode=order --tree="$testing_tree" lxsh && $luarocks_noecho list --tree="$testing_tree" --porcelain lpeg && [ `$luarocks_noecho list --tree="$testing_tree" --porcelain lpeg | wc -l` = 0 ]; } +test_deps_mode_order_sys() { $luarocks build --tree="$testing_tree" lpeg && $luarocks build --deps-mode=order --tree="$testing_sys_tree" lxsh && [ `$luarocks_noecho list --tree="$testing_sys_tree" --porcelain lpeg | wc -l` = 1 ]; } +test_deps_mode_all_sys() { $luarocks build --tree="$testing_tree" lpeg && $luarocks build --deps-mode=all --tree="$testing_sys_tree" lxsh && [ `$luarocks_noecho list --tree="$testing_sys_tree" --porcelain lpeg | wc -l` = 0 ]; } +test_deps_mode_none() { $luarocks build --tree="$testing_tree" --deps-mode=none lxsh; [ `$luarocks_noecho list --tree="$testing_tree" --porcelain lpeg | wc -l` = 0 ]; } +test_deps_mode_nodeps_alias() { $luarocks build --tree="$testing_tree" --nodeps lxsh; [ `$luarocks_noecho list --tree="$testing_tree" --porcelain lpeg | wc -l` = 0 ]; } +test_deps_mode_make_order() { $luarocks build --tree="$testing_sys_tree" lpeg && rm -rf ./lxsh-${verrev_lxsh} && $luarocks download --src lxsh ${verrev_lxsh} && $luarocks unpack ./lxsh-${verrev_lxsh}.src.rock && cd lxsh-${verrev_lxsh}/lxsh-${version_lxsh}-1 && $luarocks make --tree="$testing_tree" --deps-mode=order && cd ../.. && [ `$luarocks_noecho list --tree="$testing_tree" --porcelain lpeg | wc -l` = 0 ] && rm -rf ./lxsh-${verrev_lxsh}; } +test_deps_mode_make_order_sys() { $luarocks build --tree="$testing_tree" lpeg && rm -rf ./lxsh-${verrev_lxsh} && $luarocks download --src lxsh ${verrev_lxsh} && $luarocks unpack ./lxsh-${verrev_lxsh}.src.rock && cd lxsh-${verrev_lxsh}/lxsh-${version_lxsh}-1 && $luarocks make --tree="$testing_sys_tree" --deps-mode=order && cd ../.. && [ `$luarocks_noecho list --tree="$testing_tree" --porcelain lpeg | wc -l` = 1 ] && rm -rf ./lxsh-${verrev_lxsh}; } + +test_write_rockspec() { $luarocks write_rockspec git://github.com/keplerproject/luarocks; } +test_write_rockspec_lib() { $luarocks write_rockspec git://github.com/mbalmer/luafcgi --lib=fcgi --license="3-clause BSD" --lua-version=5.1,5.2; } +test_write_rockspec_fullargs() { $luarocks write_rockspec git://github.com/keplerproject/luarocks --lua-version=5.1,5.2 --license="MIT/X11" --homepage="http://www.luarocks.org" --summary="A package manager for Lua modules"; } +fail_write_rockspec_args() { $luarocks write_rockspec invalid; } +fail_write_rockspec_args_url() { $luarocks write_rockspec http://example.com/invalid.zip; } +test_write_rockspec_http() { $luarocks write_rockspec http://luarocks.org/releases/luarocks-2.1.0.tar.gz --lua-version=5.1; } +test_write_rockspec_basedir() { $luarocks write_rockspec https://github.com/downloads/Olivine-Labs/luassert/luassert-1.2.tar.gz --lua-version=5.1; } + +test_doc() { $luarocks install luarepl; $luarocks doc luarepl; } # Driver ######################################### @@ -232,7 +456,6 @@ run_tests() { else echo "FAIL: Unexpected failure."; exit 1 fi done - grep "^fail_$1.*(" < $testing_dir/testing.sh | cut -d'(' -f1 | while read test do echo "-------------------------------------------" @@ -247,11 +470,17 @@ run_tests() { } run_with_minimal_environment() { + echo "===========================================" + echo "Running with minimal environment" + echo "===========================================" build_environment luacov run_tests $1 } run_with_full_environment() { + echo "===========================================" + echo "Running with full environment" + echo "===========================================" build_environment luacov luafilesystem luasocket luabitop luaposix md5 lzlib run_tests $1 } @@ -266,4 +495,10 @@ run_all_tests $1 $testing_sys_tree/bin/luacov -c $testing_dir/luacov.config src/luarocks src/bin -cat $testing_dir/luacov.report.out +if [ "$travis" ] +then + grep "Summary" -B1 -A1000 $testing_dir/luacov.report.out +else + cat "$testing_dir/luacov.report.out" +fi + diff --git a/luarocks/win32/bin/create_reg_file.lua b/luarocks/win32/LuaRocks.reg.lua index 2eb7583..2eb7583 100644 --- a/luarocks/win32/bin/create_reg_file.lua +++ b/luarocks/win32/LuaRocks.reg.lua diff --git a/luarocks/win32/bin/LuaRocks.reg.template b/luarocks/win32/LuaRocks.reg.template index 82cc180..82cc180 100644 --- a/luarocks/win32/bin/LuaRocks.reg.template +++ b/luarocks/win32/LuaRocks.reg.template diff --git a/luarocks/win32/bin/chmod.exe b/luarocks/win32/bin/chmod.exe Binary files differdeleted file mode 100644 index 9655cfd..0000000 --- a/luarocks/win32/bin/chmod.exe +++ /dev/null diff --git a/luarocks/win32/bin/objdump.exe b/luarocks/win32/bin/objdump.exe Binary files differdeleted file mode 100644 index 4429d10..0000000 --- a/luarocks/win32/bin/objdump.exe +++ /dev/null diff --git a/luarocks/win32/bin/rm.exe b/luarocks/win32/bin/rm.exe Binary files differdeleted file mode 100755 index 8e79306..0000000 --- a/luarocks/win32/bin/rm.exe +++ /dev/null diff --git a/luarocks/win32/bin/lua.ico b/luarocks/win32/lua.ico Binary files differindex 56dc4fe..56dc4fe 100644 --- a/luarocks/win32/bin/lua.ico +++ b/luarocks/win32/lua.ico diff --git a/luarocks/win32/bin/luarocksw.bat b/luarocks/win32/luarocksw.bat index 313508d..8ac0292 100644 --- a/luarocks/win32/bin/luarocksw.bat +++ b/luarocks/win32/luarocksw.bat @@ -34,14 +34,14 @@ if [%1]==[removeall] goto REMOVEALL REM execute LuaRocks and wait for results echo executing: luarocks %* -call %MYPATH%luarocks %* +call "%MYPATH%luarocks" %* pause goto END :REMOVEALL for /f "delims=-" %%a in ("%~n2") do ( echo executing: luarocks remove %%a - %MYPATH%luarocks remove "%%a" + "%MYPATH%luarocks" remove "%%a" pause goto END ) diff --git a/luarocks/win32/pe-parser.lua b/luarocks/win32/pe-parser.lua new file mode 100644 index 0000000..30bb839 --- /dev/null +++ b/luarocks/win32/pe-parser.lua @@ -0,0 +1,546 @@ +--------------------------------------------------------------------------------------- +-- Lua module to parse a Portable Executable (.exe , .dll, etc.) file and extract metadata. +-- +-- Version 0.1, [copyright (c) 2013 - Thijs Schreijer](http://www.thijsschreijer.nl) +-- @name pe-parser +-- @class module + +local M = {} + +--- Table with named constants/flag-constants. +-- Named elements can be looked up by their name in the `const` table. The sub tables are index by value. +-- For flag fields the name is extended with `_flags`. +-- @usage -- lookup descriptive name for the myobj.Magic value +-- local desc = pe.const.Magic(myobj.Magic) +-- +-- -- get list of flag names, indexed by flag values, for the Characteristics field +-- local flag_list = pe.const.Characteristics_flags +M.const = { + Magic = { + ["10b"] = "PE32", + ["20b"] = "PE32+", + }, + Machine = { + ["0"] = "IMAGE_FILE_MACHINE_UNKNOWN", + ["1d3"] = "IMAGE_FILE_MACHINE_AM33", + ["8664"] = "IMAGE_FILE_MACHINE_AMD64", + ["1c0"] = "IMAGE_FILE_MACHINE_ARM", + ["1c4"] = "IMAGE_FILE_MACHINE_ARMNT", + ["aa64"] = "IMAGE_FILE_MACHINE_ARM64", + ["ebc"] = "IMAGE_FILE_MACHINE_EBC", + ["14c"] = "IMAGE_FILE_MACHINE_I386", + ["200"] = "IMAGE_FILE_MACHINE_IA64", + ["9041"] = "IMAGE_FILE_MACHINE_M32R", + ["266"] = "IMAGE_FILE_MACHINE_MIPS16", + ["366"] = "IMAGE_FILE_MACHINE_MIPSFPU", + ["466"] = "IMAGE_FILE_MACHINE_MIPSFPU16", + ["1f0"] = "IMAGE_FILE_MACHINE_POWERPC", + ["1f1"] = "IMAGE_FILE_MACHINE_POWERPCFP", + ["166"] = "IMAGE_FILE_MACHINE_R4000", + ["1a2"] = "IMAGE_FILE_MACHINE_SH3", + ["1a3"] = "IMAGE_FILE_MACHINE_SH3DSP", + ["1a6"] = "IMAGE_FILE_MACHINE_SH4", + ["1a8"] = "IMAGE_FILE_MACHINE_SH5", + ["1c2"] = "IMAGE_FILE_MACHINE_THUMB", + ["169"] = "IMAGE_FILE_MACHINE_WCEMIPSV2", + }, + Characteristics_flags = { + ["1"] = "IMAGE_FILE_RELOCS_STRIPPED", + ["2"] = "IMAGE_FILE_EXECUTABLE_IMAGE", + ["4"] = "IMAGE_FILE_LINE_NUMS_STRIPPED", + ["8"] = "IMAGE_FILE_LOCAL_SYMS_STRIPPED", + ["10"] = "IMAGE_FILE_AGGRESSIVE_WS_TRIM", + ["20"] = "IMAGE_FILE_LARGE_ADDRESS_AWARE", + ["40"] = "Reserved for future use", + ["80"] = "IMAGE_FILE_BYTES_REVERSED_LO", + ["100"] = "IMAGE_FILE_32BIT_MACHINE", + ["200"] = "IMAGE_FILE_DEBUG_STRIPPED", + ["400"] = "IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP", + ["800"] = "IMAGE_FILE_NET_RUN_FROM_SWAP", + ["1000"] = "IMAGE_FILE_SYSTEM", + ["2000"] = "IMAGE_FILE_DLL", + ["4000"] = "IMAGE_FILE_UP_SYSTEM_ONLY", + ["8000"] = "IMAGE_FILE_BYTES_REVERSED_HI", + }, + Subsystem = { + ["0"] = "IMAGE_SUBSYSTEM_UNKNOWN", + ["1"] = "IMAGE_SUBSYSTEM_NATIVE", + ["2"] = "IMAGE_SUBSYSTEM_WINDOWS_GUI", + ["3"] = "IMAGE_SUBSYSTEM_WINDOWS_CUI", + ["7"] = "IMAGE_SUBSYSTEM_POSIX_CUI", + ["9"] = "IMAGE_SUBSYSTEM_WINDOWS_CE_GUI", + ["a"] = "IMAGE_SUBSYSTEM_EFI_APPLICATION", + ["b"] = "IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER", + ["c"] = "IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER", + ["d"] = "IMAGE_SUBSYSTEM_EFI_ROM", + ["e"] = "IMAGE_SUBSYSTEM_XBOX", + }, + DllCharacteristics_flags = { + ["40"] = "IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE", + ["80"] = "IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY", + ["100"] = "IMAGE_DLL_CHARACTERISTICS_NX_COMPAT", + ["200"] = "IMAGE_DLLCHARACTERISTICS_NO_ISOLATION", + ["400"] = "IMAGE_DLLCHARACTERISTICS_NO_SEH", + ["800"] = "IMAGE_DLLCHARACTERISTICS_NO_BIND", + ["2000"] = "IMAGE_DLLCHARACTERISTICS_WDM_DRIVER", + ["8000"] = "IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE", + }, + Sections = { + Characteristics_flags = { + ["8"] = "IMAGE_SCN_TYPE_NO_PAD", + ["20"] = "IMAGE_SCN_CNT_CODE", + ["40"] = "IMAGE_SCN_CNT_INITIALIZED_DATA", + ["80"] = "IMAGE_SCN_CNT_UNINITIALIZED_ DATA", + ["100"] = "IMAGE_SCN_LNK_OTHER", + ["200"] = "IMAGE_SCN_LNK_INFO", + ["800"] = "IMAGE_SCN_LNK_REMOVE", + ["1000"] = "IMAGE_SCN_LNK_COMDAT", + ["8000"] = "IMAGE_SCN_GPREL", + ["20000"] = "IMAGE_SCN_MEM_PURGEABLE", + ["20000"] = "IMAGE_SCN_MEM_16BIT", + ["40000"] = "IMAGE_SCN_MEM_LOCKED", + ["80000"] = "IMAGE_SCN_MEM_PRELOAD", + ["100000"] = "IMAGE_SCN_ALIGN_1BYTES", + ["200000"] = "IMAGE_SCN_ALIGN_2BYTES", + ["300000"] = "IMAGE_SCN_ALIGN_4BYTES", + ["400000"] = "IMAGE_SCN_ALIGN_8BYTES", + ["500000"] = "IMAGE_SCN_ALIGN_16BYTES", + ["600000"] = "IMAGE_SCN_ALIGN_32BYTES", + ["700000"] = "IMAGE_SCN_ALIGN_64BYTES", + ["800000"] = "IMAGE_SCN_ALIGN_128BYTES", + ["900000"] = "IMAGE_SCN_ALIGN_256BYTES", + ["a00000"] = "IMAGE_SCN_ALIGN_512BYTES", + ["b00000"] = "IMAGE_SCN_ALIGN_1024BYTES", + ["c00000"] = "IMAGE_SCN_ALIGN_2048BYTES", + ["d00000"] = "IMAGE_SCN_ALIGN_4096BYTES", + ["e00000"] = "IMAGE_SCN_ALIGN_8192BYTES", + ["1000000"] = "IMAGE_SCN_LNK_NRELOC_OVFL", + ["2000000"] = "IMAGE_SCN_MEM_DISCARDABLE", + ["4000000"] = "IMAGE_SCN_MEM_NOT_CACHED", + ["8000000"] = "IMAGE_SCN_MEM_NOT_PAGED", + ["10000000"] = "IMAGE_SCN_MEM_SHARED", + ["20000000"] = "IMAGE_SCN_MEM_EXECUTE", + ["40000000"] = "IMAGE_SCN_MEM_READ", + ["80000000"] = "IMAGE_SCN_MEM_WRITE", + }, + }, + +} + + +--- convert integer to HEX representation +-- @param IN the number to convert to hex +-- @param len the size to return, any result smaller will be prefixed by "0"s +-- @return string containing hex representation +function M.toHex(IN, len) + local B,K,OUT,I,D=16,"0123456789abcdef","",0 + while IN>0 do + I=I+1 + IN,D=math.floor(IN/B),math.fmod(IN,B)+1 + OUT=string.sub(K,D,D)..OUT + end + len = len or string.len(OUT) + if len<1 then len = 1 end + return (string.rep("0",len) .. OUT):sub(-len,-1) +end + +--- convert HEX to integer +-- @param IN the string to convert to dec +-- @return number in dec format +function M.toDec(IN) + assert(type(IN)=="string") + local OUT = 0 + IN = IN:lower() + while #IN > 0 do + local b = string.find("0123456789abcdef",IN:sub(1,1)) + OUT = OUT * 16 + (b-1) + IN = IN:sub(2,-1) + end + return OUT +end + +local function get_int(str) + -- convert a byte-sequence to an integer + assert(str) + local r = 0 + for i = #str, 1, -1 do + r = r*256 + string.byte(str,i,i) + end + return r +end + +local function get_hex(str) + -- convert a byte-sequence to a hex string + assert(str) + local r = "" + for i = #str, 1, -1 do + r = r .. M.toHex(string.byte(str,i,i),2) + end + while (#r > 1) and (r:sub(1,1) == "0") do + r = r:sub(2, -1) + end + return r +end + +local function get_list(list, f, add_to) + -- list: list of tables with 'size' and 'name' and is_str + -- f: file to read from + -- add_to: table to add results to (optional) + local r = add_to or {} + for i, t in ipairs(list) do + assert(r[t.name] == nil, "Value for '"..t.name.."' already set") + local val,err = f:read(t.size) -- read specified size in bytes + val = val or "\0" + if t.is_str then -- entry is marked as a string value, read as such + for i = 1, #val do + if val:sub(i,i) == "\0" then + r[t.name] = val:sub(1,i-1) + break + end + end + r[t.name] = r[t.name] or val + else -- entry not marked, so always read as hex value + r[t.name] = get_hex(val) + end + end + return r +end + +--- Calculates the fileoffset of a given RVA. +-- This function is also available as a method on the parsed output table +-- @param obj a parsed object (return value from `parse`) +-- @param RVA an RVA value to convert to a fileoffset (either number or hex-string) +-- @return fileoffset of the given RVA (number) +M.get_fileoffset = function(obj, RVA) + -- given an object with a section table, and an RVA, it returns + -- the fileoffset for the data + if type(RVA)=="string" then RVA = M.toDec(RVA) end + local section + for i, s in ipairs(obj.Sections) do + if M.toDec(s.VirtualAddress) <= RVA and M.toDec(s.VirtualAddress) + M.toDec(s.VirtualSize) >= RVA then + section = s + break + end + end + if not section then return nil, "No match RVA with Section list, RVA out of bounds" end + return RVA - M.toDec(section.VirtualAddress) + M.toDec(section.PointerToRawData) +end + +local function readstring(f) + -- reads a null-terminated string from the current file posistion + local name = "" + while true do + local c = f:read(1) + if c == "\0" then break end + name = name .. c + end + return name +end + +--- Parses a file and extracts the information. +-- All numbers are delivered as "string" types containing hex values, see `toHex` and `toDec` conversion functions. +-- @return table with data, or nil + error +-- @usage local pe = require("pe-parser") +-- local obj = pe.parse("c:\lua\lua.exe") +-- obj:dump() +M.parse = function(target) + + local list = { -- list of known architectures + [332] = "x86", -- IMAGE_FILE_MACHINE_I386 + [512] = "x86_64", -- IMAGE_FILE_MACHINE_IA64 + [34404] = "x86_64", -- IMAGE_FILE_MACHINE_AMD64 + } + + local f, err = io.open(target, "rb") + if not f then return nil, err end + + local MZ = f:read(2) + if MZ ~= "MZ" then + f:close() + return nil, "Not a valid image" + end + + f:seek("set", 60) -- position of PE header position + local peoffset = get_int(f:read(4)) -- read position of PE header + + f:seek("set", peoffset) -- move to position of PE header + local out = get_list({ + { size = 4, + name = "PEheader", + is_str = true }, + { size = 2, + name = "Machine" }, + { size = 2, + name = "NumberOfSections"}, + { size = 4, + name = "TimeDateStamp" }, + { size = 4, + name = "PointerToSymbolTable"}, + { size = 4, + name = "NumberOfSymbols"}, + { size = 2, + name = "SizeOfOptionalHeader"}, + { size = 2, + name = "Characteristics"}, + }, f) + + if out.PEheader ~= "PE" then + f:close() + return nil, "Invalid PE header" + end + out.PEheader = nil -- remove it, has no value + out.dump = M.dump -- export dump function as a method + + if M.toDec(out.SizeOfOptionalHeader) > 0 then + -- parse optional header; standard + get_list({ + { size = 2, + name = "Magic" }, + { size = 1, + name = "MajorLinkerVersion"}, + { size = 1, + name = "MinorLinkerVersion"}, + { size = 4, + name = "SizeOfCode"}, + { size = 4, + name = "SizeOfInitializedData"}, + { size = 4, + name = "SizeOfUninitializedData"}, + { size = 4, + name = "AddressOfEntryPoint"}, + { size = 4, + name = "BaseOfCode"}, + }, f, out) + local plus = (out.Magic == "20b") + if not plus then -- plain PE32, not PE32+ + get_list({ + { size = 4, + name = "BaseOfData" }, + }, f, out) + end + -- parse optional header; windows-fields + local plussize = 4 + if plus then plussize = 8 end + get_list({ + { size = plussize, + name = "ImageBase"}, + { size = 4, + name = "SectionAlignment"}, + { size = 4, + name = "FileAlignment"}, + { size = 2, + name = "MajorOperatingSystemVersion"}, + { size = 2, + name = "MinorOperatingSystemVersion"}, + { size = 2, + name = "MajorImageVersion"}, + { size = 2, + name = "MinorImageVersion"}, + { size = 2, + name = "MajorSubsystemVersion"}, + { size = 2, + name = "MinorSubsystemVersion"}, + { size = 4, + name = "Win32VersionValue"}, + { size = 4, + name = "SizeOfImage"}, + { size = 4, + name = "SizeOfHeaders"}, + { size = 4, + name = "CheckSum"}, + { size = 2, + name = "Subsystem"}, + { size = 2, + name = "DllCharacteristics"}, + { size = plussize, + name = "SizeOfStackReserve"}, + { size = plussize, + name = "SizeOfStackCommit"}, + { size = plussize, + name = "SizeOfHeapReserve"}, + { size = plussize, + name = "SizeOfHeapCommit"}, + { size = 4, + name = "LoaderFlags"}, + { size = 4, + name = "NumberOfRvaAndSizes"}, + }, f, out) + -- Read data directory entries + for i = 1, M.toDec(out.NumberOfRvaAndSizes) do + out.DataDirectory = out.DataDirectory or {} + out.DataDirectory[i] = get_list({ + { size = 4, + name = "VirtualAddress"}, + { size = 4, + name = "Size"}, + }, f) + end + for i, name in ipairs{"ExportTable", "ImportTable", "ResourceTable", + "ExceptionTable", "CertificateTable", "BaseRelocationTable", + "Debug", "Architecture", "GlobalPtr", "TLSTable", + "LoadConfigTable", "BoundImport", "IAT", + "DelayImportDescriptor", "CLRRuntimeHeader", "Reserved"} do + out.DataDirectory[name] = out.DataDirectory[i] + if out.DataDirectory[name] then out.DataDirectory[name].name = name end + end + end + + -- parse section table + for i = 1, M.toDec(out.NumberOfSections) do + out.Sections = out.Sections or {} + out.Sections[i] = get_list({ + { size = 8, + name = "Name", + is_str = true}, + { size = 4, + name = "VirtualSize"}, + { size = 4, + name = "VirtualAddress"}, + { size = 4, + name = "SizeOfRawData"}, + { size = 4, + name = "PointerToRawData"}, + { size = 4, + name = "PointerToRelocations"}, + { size = 4, + name = "PointerToLinenumbers"}, + { size = 2, + name = "NumberOfRelocations"}, + { size = 2, + name = "NumberOfLinenumbers"}, + { size = 4, + name = "Characteristics"}, + }, f) + end + -- we now have section data, so add RVA convertion method + out.get_fileoffset = M.get_fileoffset + + -- get the import table + f:seek("set", out:get_fileoffset(out.DataDirectory.ImportTable.VirtualAddress)) + local done = false + local cnt = 1 + while not done do + local dll = get_list({ + { size = 4, + name = "ImportLookupTableRVA"}, + { size = 4, + name = "TimeDateStamp"}, + { size = 4, + name = "ForwarderChain"}, + { size = 4, + name = "NameRVA"}, + { size = 4, + name = "ImportAddressTableRVA"}, + }, f) + if M.toDec(dll.NameRVA) == 0 then + -- this is the final NULL entry, so we're done + done = true + else + -- store the import entry + out.DataDirectory.ImportTable[cnt] = dll + cnt = cnt + 1 + end + end + -- resolve imported DLL names + for i, dll in ipairs(out.DataDirectory.ImportTable) do + f:seek("set", out:get_fileoffset(dll.NameRVA)) + dll.Name = readstring(f) + end + + f:close() + return out +end + +-- pad a string (prefix) to a specific length +local function pad(str, l, chr) + chr = chr or " " + l = l or 0 + return string.rep(chr,l-#str)..str +end + +--- Dumps the output parsed. +-- This function is also available as a method on the parsed output table +M.dump = function(obj) + local l = 0 + for k,v in pairs(obj) do if #k > l then l = #k end end + + for k,v in pairs(obj) do + if (M.const[k] and type(v)=="string") then + -- look up named value + print(k..string.rep(" ", l - #k + 1)..": "..M.const[k][v]) + elseif M.const[k.."_flags"] then + -- flags should be listed + print(k..string.rep(" ", l - #k + 1)..": "..v.." (flag field)") + else + -- regular values + if type(v) == "number" then + print(k..string.rep(" ", l - #k + 1)..": "..v.." (dec)") + else + if (type(v)=="string") and (k ~= "DataDirectory") and (k ~= "Sections") then + print(k..string.rep(" ", l - #k + 1)..": "..v) + end + end + end + end + + if obj.DataDirectory then + print("DataDirectory (RVA, size):") + for i, v in ipairs(obj.DataDirectory) do + print(" Entry "..M.toHex(i-1).." "..pad(v.VirtualAddress,8,"0").." "..pad(v.Size,8,"0").." "..v.name) + end + end + + if obj.Sections then + print("Sections:") + print("idx name RVA VSize Offset RawSize") + for i, v in ipairs(obj.Sections) do + print(" "..i.." "..v.Name.. string.rep(" ",9-#v.Name)..pad(v.VirtualAddress,8,"0").." "..pad(v.VirtualSize,8,"0").." "..pad(v.PointerToRawData,8,"0").." "..pad(v.SizeOfRawData,8,"0")) + end + end + + print("Imports:") + for i, dll in ipairs(obj.DataDirectory.ImportTable) do + print(" "..dll.Name) + end +end + +--- Checks the msvcrt dll the binary was linked against. +-- Mixing and matching dlls only works when they all are using the same runtime, if +-- not unexpected errors will probably occur. +-- Checks the binary provided and then traverses all imported dlls to find the msvcrt +-- used (it will only look for the dlls in the same directory). +-- @param infile binary file to check +-- @return msvcrt name (uppercase, without extension) + file where the reference was found, or nil + error +function M.msvcrt(infile) + local path, file = infile:match("(.+)\\(.+)$") + if not path then + path = "" + file = infile + else + path=path .. "\\" + end + local obj, err = M.parse(path..file) + if not obj then return obj, err end + + for i, dll in ipairs(obj.DataDirectory.ImportTable) do + dll = dll.Name:upper() + local result = dll:match('(MSVCR%d*)%.DLL') + if not result then + result = dll:match('(MSVCRT)%.DLL') + end + -- success, found it return name + binary where it was found + if result then return result, infile end + end + + -- not found, so traverse all imported dll's + for i, dll in ipairs(obj.DataDirectory.ImportTable) do + local rt, ref = M.msvcrt(path..dll.Name) + if rt then + return rt, ref -- found it + end + end + + return nil, "No msvcrt found" +end + +return M diff --git a/luarocks/win32/bin/rclauncher.c b/luarocks/win32/rclauncher.c index 77459f4..77459f4 100644 --- a/luarocks/win32/bin/rclauncher.c +++ b/luarocks/win32/rclauncher.c diff --git a/luarocks/win32/bin/7z.dll b/luarocks/win32/tools/7z.dll Binary files differindex c0ff7fb..c0ff7fb 100644 --- a/luarocks/win32/bin/7z.dll +++ b/luarocks/win32/tools/7z.dll diff --git a/luarocks/win32/bin/7z.exe b/luarocks/win32/tools/7z.exe Binary files differindex 5e3d6f9..5e3d6f9 100644 --- a/luarocks/win32/bin/7z.exe +++ b/luarocks/win32/tools/7z.exe diff --git a/luarocks/win32/bin/cp.exe b/luarocks/win32/tools/cp.exe Binary files differindex 0ef4fe8..0ef4fe8 100755..100644 --- a/luarocks/win32/bin/cp.exe +++ b/luarocks/win32/tools/cp.exe diff --git a/luarocks/win32/bin/find.exe b/luarocks/win32/tools/find.exe Binary files differindex 85192fb..85192fb 100755..100644 --- a/luarocks/win32/bin/find.exe +++ b/luarocks/win32/tools/find.exe diff --git a/luarocks/win32/bin/libeay32.dll b/luarocks/win32/tools/libeay32.dll Binary files differindex 8d31f86..8d31f86 100644 --- a/luarocks/win32/bin/libeay32.dll +++ b/luarocks/win32/tools/libeay32.dll diff --git a/luarocks/win32/bin/libiconv2.dll b/luarocks/win32/tools/libiconv2.dll Binary files differindex 544dd92..544dd92 100644 --- a/luarocks/win32/bin/libiconv2.dll +++ b/luarocks/win32/tools/libiconv2.dll diff --git a/luarocks/win32/bin/libintl3.dll b/luarocks/win32/tools/libintl3.dll Binary files differindex ec11e6b..ec11e6b 100644 --- a/luarocks/win32/bin/libintl3.dll +++ b/luarocks/win32/tools/libintl3.dll diff --git a/luarocks/win32/bin/libssl32.dll b/luarocks/win32/tools/libssl32.dll Binary files differindex a30ff0e..a30ff0e 100644 --- a/luarocks/win32/bin/libssl32.dll +++ b/luarocks/win32/tools/libssl32.dll diff --git a/luarocks/win32/bin/ls.exe b/luarocks/win32/tools/ls.exe Binary files differindex 96ff2e5..96ff2e5 100755..100644 --- a/luarocks/win32/bin/ls.exe +++ b/luarocks/win32/tools/ls.exe diff --git a/luarocks/win32/bin/md5sum.exe b/luarocks/win32/tools/md5sum.exe Binary files differindex 4ae9f74..4ae9f74 100644 --- a/luarocks/win32/bin/md5sum.exe +++ b/luarocks/win32/tools/md5sum.exe diff --git a/luarocks/win32/bin/mkdir.exe b/luarocks/win32/tools/mkdir.exe Binary files differindex 83e57d9..83e57d9 100755..100644 --- a/luarocks/win32/bin/mkdir.exe +++ b/luarocks/win32/tools/mkdir.exe diff --git a/luarocks/win32/bin/mv.exe b/luarocks/win32/tools/mv.exe Binary files differindex 9fb65bb..9fb65bb 100755..100644 --- a/luarocks/win32/bin/mv.exe +++ b/luarocks/win32/tools/mv.exe diff --git a/luarocks/win32/bin/pwd.exe b/luarocks/win32/tools/pwd.exe Binary files differindex 7dd114d..7dd114d 100755..100644 --- a/luarocks/win32/bin/pwd.exe +++ b/luarocks/win32/tools/pwd.exe diff --git a/luarocks/win32/bin/rmdir.exe b/luarocks/win32/tools/rmdir.exe Binary files differindex 6a85c3c..6a85c3c 100755..100644 --- a/luarocks/win32/bin/rmdir.exe +++ b/luarocks/win32/tools/rmdir.exe diff --git a/luarocks/win32/bin/test.exe b/luarocks/win32/tools/test.exe Binary files differindex 94c95f9..94c95f9 100755..100644 --- a/luarocks/win32/bin/test.exe +++ b/luarocks/win32/tools/test.exe diff --git a/luarocks/win32/bin/uname.exe b/luarocks/win32/tools/uname.exe Binary files differindex 3e2f4cf..3e2f4cf 100755..100644 --- a/luarocks/win32/bin/uname.exe +++ b/luarocks/win32/tools/uname.exe diff --git a/luarocks/win32/bin/wget.exe b/luarocks/win32/tools/wget.exe Binary files differindex 54b372e..54b372e 100644 --- a/luarocks/win32/bin/wget.exe +++ b/luarocks/win32/tools/wget.exe |