diff options
author | Ronan Collobert <ronan@collobert.com> | 2013-10-25 13:40:44 +0400 |
---|---|---|
committer | Ronan Collobert <ronan@collobert.com> | 2013-10-25 13:40:44 +0400 |
commit | 009a990f201a9b57c768cd96a97466d044d122b1 (patch) | |
tree | 28e7d19df2e31db204c42df3773e9adad4244b53 |
Squashed 'luarocks/' content from commit c7a3685
git-subtree-dir: luarocks
git-subtree-split: c7a3685013ad5bfa60bce6a91707903b699df431
136 files changed, 15397 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..13a0772 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/config.* +/src/luarocks/site_config.lua @@ -0,0 +1,46 @@ +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. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/COPYING_7z b/COPYING_7z new file mode 100644 index 0000000..e25910a --- /dev/null +++ b/COPYING_7z @@ -0,0 +1,56 @@ + 7-Zip
+ ~~~~~
+ License for use and distribution
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ 7-Zip Copyright (C) 1999-2010 Igor Pavlov.
+
+ Licenses for files are:
+
+ 1) 7z.dll: GNU LGPL + unRAR restriction
+ 2) All other files: GNU LGPL
+
+ The GNU LGPL + unRAR restriction means that you must follow both
+ GNU LGPL rules and unRAR restriction rules.
+
+
+ Note:
+ You can use 7-Zip on any computer, including a computer in a commercial
+ organization. You don't need to register or pay for 7-Zip.
+
+
+ GNU LGPL information
+ --------------------
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You can receive a copy of the GNU Lesser General Public License from
+ http://www.gnu.org/
+
+
+ unRAR restriction
+ -----------------
+
+ The decompression engine for RAR archives was developed using source
+ code of unRAR program.
+ All copyrights to original unRAR code are owned by Alexander Roshal.
+
+ The license for original unRAR code has the following restriction:
+
+ The unRAR sources cannot be used to re-create the RAR compression algorithm,
+ which is proprietary. Distribution of modified unRAR sources in separate form
+ or as a part of other software is permitted, provided that it is clearly
+ stated in the documentation and source comments that the code may
+ not be used to develop a RAR (WinRAR) compatible archiver.
+
+
+ --
+ Igor Pavlov
diff --git a/COPYING_lua b/COPYING_lua new file mode 100644 index 0000000..3a53e74 --- /dev/null +++ b/COPYING_lua @@ -0,0 +1,34 @@ +Lua License +----------- + +Lua is licensed under the terms of the MIT license reproduced below. +This means that Lua is free software and can be used for both academic +and commercial purposes at absolutely no cost. + +For details and rationale, see http://www.lua.org/license.html . + +=============================================================================== + +Copyright (C) 1994-2008 Lua.org, PUC-Rio. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +=============================================================================== + +(end of COPYRIGHT) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..be83c01 --- /dev/null +++ b/Makefile @@ -0,0 +1,173 @@ +# $Id: Makefile,v 1.30 2008/08/18 14:07:35 hisham Exp $ + +include config.unix + +DESTDIR = +PREFIX ?= /usr/local +ROCKS_TREE ?= $(PREFIX) +SYSCONFDIR ?= $(PREFIX)/etc/luarocks +BINDIR ?= $(PREFIX)/bin +LUADIR ?= $(PREFIX)/share/lua/$(LUA_VERSION)/ +LUA_DIR ?= /usr/local +LUA_BINDIR ?= $(LUA_DIR)/bin + +BIN_FILES = luarocks luarocks-admin +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 \ +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 + +CONFIG_FILE = $(SYSCONFDIR)/config-$(LUA_VERSION).lua + +all: + @echo "- Type 'make build' and 'make install':" + @echo " to install to $(PREFIX) as usual." + @echo "- Type 'make bootstrap':" + @echo " to install LuaRocks in $(PREFIX) as a rock." + @echo + +build: built + +src/luarocks/site_config.lua: config.unix + rm -f src/luarocks/site_config.lua + echo 'module("luarocks.site_config")' >> src/luarocks/site_config.lua + if [ -n "$(PREFIX)" ] ;\ + then \ + echo "LUAROCKS_PREFIX=[[$(PREFIX)]]" >> src/luarocks/site_config.lua ;\ + fi + if [ -n "$(LUA_INCDIR)" ] ;\ + then \ + echo "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 ;\ + fi + if [ -n "$(LUA_BINDIR)" ] ;\ + then \ + echo "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 ;\ + fi + if [ -n "$(SYSCONFDIR)" ] ;\ + then \ + echo "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 ;\ + fi + if [ -n "$(FORCE_CONFIG)" ] ;\ + then \ + echo "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 ;\ + fi + if [ "$(LUA_DIR_SET)" = "yes" ] ;\ + then \ + echo "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 + +dev: + $(MAKE) build_bins LUADIR=$(PWD)/src + +build_bins: cleanup_bins + for f in $(BIN_FILES) ;\ + 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 ;\ + 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 + cd src && luadoc -d ../doc/luadoc --nofiles luarocks/*.lua + +check_makefile: clean + echo $(BIN_FILES) | tr " " "\n" | sort > makefile_list.txt + ( cd src/bin && ls -d * ) | sort > luarocks_dir.txt + echo $(LUAROCKS_FILES) | tr " " "\n" | sort >> makefile_list.txt + ( cd src/luarocks && find * -name "*.lua" ) | sort >> luarocks_dir.txt + diff makefile_list.txt luarocks_dir.txt + rm -f makefile_list.txt luarocks_dir.txt + @echo + @echo "Makefile is sane." + @echo + +cleanup_bins: + for f in $(BIN_FILES) ;\ + do \ + mv src/bin/$$f src/bin/$$f.bak ;\ + sed "s,^#!.*lua.*,#!/usr/bin/env lua,;/^package.path/d" < src/bin/$$f.bak > src/bin/$$f ;\ + chmod +x src/bin/$$f ;\ + rm -f src/bin/$$f.bak ;\ + done + +clean: cleanup_bins + rm -f src/luarocks/site_config.lua + rm -f built + +install_bins: built + 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"; \ + done + +install_luas: built + mkdir -p "$(DESTDIR)$(LUADIR)/luarocks" + cd src/luarocks && for f in $(LUAROCKS_FILES); \ + do \ + d="$(DESTDIR)$(LUADIR)/luarocks"/`dirname "$$f"` && \ + mkdir -p "$$d" && \ + cp "$$f" "$$d" || exit 1; \ + done + +install_site_config: built + mkdir -p "$(DESTDIR)$(LUADIR)/luarocks" + cd src/luarocks && cp site_config.lua "$(DESTDIR)$(LUADIR)/luarocks" + +write_sysconfig: built + mkdir -p "$(DESTDIR)$(ROCKS_TREE)" + if [ ! -f "$(DESTDIR)$(CONFIG_FILE)" ] ;\ + then \ + mkdir -p `dirname "$(DESTDIR)$(CONFIG_FILE)"` ;\ + echo 'rocks_trees = {' >> "$(DESTDIR)$(CONFIG_FILE)" ;\ + if [ ! -n "$(FORCE_CONFIG)" ] ;\ + then \ + echo ' home..[[/.luarocks]],' >> "$(DESTDIR)$(CONFIG_FILE)" ;\ + fi ;\ + echo ' [[$(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)" + +install_rock: install_bins install_luas diff --git a/README.md b/README.md new file mode 100644 index 0000000..87345a2 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +This is LuaRocks, a deployment and management system for Lua modules. + +Main website: [luarocks.org](http://www.luarocks.org) + +LuaRocks 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 +that when a module is required, the correct version is loaded. LuaRocks +supports both local and [remote][3] repositories, and multiple local rocks +trees. You can [download][4] and install LuaRocks on [Unix][5] and +[Windows][6]. + +LuaRocks is free software and uses the same [license][7] as Lua 5.1. + +[1]: http://luarocks.org/en/Types_of_rocks +[2]: http://luarocks.org/en/Dependencies +[3]: http://luarocks.org/en/Rocks_repositories +[4]: http://luarocks.org/en/Download +[5]: http://luarocks.org/en/Installation_instructions_for_Unix +[6]: http://luarocks.org/en/Installation_instructions_for_Windows +[7]: http://luarocks.org/en/License diff --git a/config.ld b/config.ld new file mode 100644 index 0000000..231a89f --- /dev/null +++ b/config.ld @@ -0,0 +1,6 @@ +file = "src/luarocks" +project = "LuaRocks" +dir = "docs/ldoc" +description = [[ +LuaRocks - a deployment and management system for Lua modules +]] diff --git a/configure b/configure new file mode 100755 index 0000000..a5aa1f4 --- /dev/null +++ b/configure @@ -0,0 +1,444 @@ +#!/bin/sh + +# A basic configure script for LuaRocks. +# Not doing any fancy shell stuff here to keep good compatibility. + +# Defaults + +PREFIX="/usr/local" +SYSCONFDIR="$PREFIX/etc/luarocks" +ROCKS_TREE="$PREFIX" +LUA_SUFFIX="" +LUA_DIR="/usr" +LUA_BINDIR="/usr/bin" +LUA_INCDIR="/usr/include" +LUA_LIBDIR="/usr/lib" +LUA_VERSION="5.1" + +# ---------------------------------------------------------------------------- +# FUNCTION DEFINITIONS +# ---------------------------------------------------------------------------- + +# Help + +show_help() { +cat <<EOF +Configure LuaRocks. + +--help This help. +--prefix=DIR Prefix where LuaRocks should be installed. + Default is $PREFIX +--sysconfdir=DIR Location where the config file should be installed. + Default is \$PREFIX/etc/luarocks + +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. + Default is \$PREFIX + +--lua-version=VERSION Use specific Lua version: 5.1 or 5.2 + 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 +--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. + Default is to auto-detect. +--with-md5-checker=TOOL Which tool to use as a downloader. + Valid options are: md5sum, openssl + Default is to auto-detect. +--versioned-rocks-dir Use a versioned rocks dir, such as + \$PREFIX/lib/luarocks/rocks-$LUA_VERSION/. + Default is to auto-detect the necessity. +--force-config 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. +EOF +} + +# Helper functions + +find_program() { + path="$PATH" + item="`echo "$path" | sed 's/\([^:]*\):.*/\1/'`" + path="`echo "$path" | sed -n 's/[^:]*::*\(.*\)/\1/p'`" + found="no" + while [ -n "$item" ] + do + if [ -f "$item/$1" ] + then + found="yes" + break + fi + item="`echo "$path" | sed 's/\([^:]*\):.*/\1/'`" + path="`echo "$path" | sed -n 's/[^:]*::*\(.*\)/\1/p'`" + done + if [ "$found" = "yes" ] + then + echo "$item" + else + echo "" + fi +} + +die() { + echo "$*" + echo + echo "configure failed." + echo + exit 1 +} + +find_helper() { + explanation="$1" + shift + tried="$*" + while [ -n "$1" ] + do + found=`find_program "$1"` + if [ -n "$found" ] + then + echo "$1 found at $found" + HELPER=$1 + return + fi + shift + done + echo "Could not find a $explanation. Tried: $tried." + die "Make sure one of them is installed and available in your PATH." +} + +case `echo -n x` in +-n*) echo_n_flag='';; +*) echo_n_flag='-n';; +esac + +echo_n() { + echo $echo_n_flag "$*" +} + +# ---------------------------------------------------------------------------- +# MAIN PROGRAM +# ---------------------------------------------------------------------------- + +# Parse options + +while [ -n "$1" ] +do + value="`echo $1 | sed 's/[^=]*.\(.*\)/\1/'`" + key="`echo $1 | sed 's/=.*//'`" + if `echo "$value" | grep "~" >/dev/null 2>/dev/null` + then + echo + echo '*WARNING*: the "~" sign is not expanded in flags.' + echo 'If you mean the home directory, use $HOME instead.' + echo + fi + case "$key" in + --help) + show_help + exit 0 + ;; + --prefix) + [ -n "$value" ] || die "Missing value in flag $key." + PREFIX="$value" + PREFIX_SET=yes + ;; + --sysconfdir) + [ -n "$value" ] || die "Missing value in flag $key." + SYSCONFDIR="$value" + SYSCONFDIR_SET=yes + ;; + --rocks-tree) + [ -n "$value" ] || die "Missing value in flag $key." + ROCKS_TREE="$value" + ROCKS_TREE_SET=yes + ;; + --force-config) + FORCE_CONFIG=yes + ;; + --versioned-rocks-dir) + VERSIONED_ROCKS_DIR=yes + ;; + --lua-suffix) + [ -n "$value" ] || die "Missing value in flag $key." + LUA_SUFFIX="$value" + LUA_SUFFIX_SET=yes + ;; + --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_SET=yes + ;; + --with-lua) + [ -n "$value" ] || die "Missing value in flag $key." + LUA_DIR="$value" + LUA_DIR_SET=yes + ;; + --with-lua-include) + [ -n "$value" ] || die "Missing value in flag $key." + LUA_INCDIR="$value" + LUA_INCDIR_SET=yes + ;; + --with-lua-lib) + [ -n "$value" ] || die "Missing value in flag $key." + LUA_LIBDIR="$value" + LUA_LIBDIR_SET=yes + ;; + --with-downloader) + [ -n "$value" ] || die "Missing value in flag $key." + case "$value" in + wget|curl) LUAROCKS_DOWNLOADER="$value" ;; + *) die "Invalid option: $value. See --help." ;; + esac + LUAROCKS_DOWNLOADER_SET=yes + ;; + --with-md5-checker) + [ -n "$value" ] || die "Missing value in flag $key." + case "$value" in + md5sum|openssl|md5) LUAROCKS_MD5CHECKER="$value" ;; + *) die "Invalid option: $value. See --help." ;; + esac + LUAROCKS_MD5CHECKER_SET=yes + ;; + *) + die "Error: Unknown flag: $1" + ;; + esac + shift +done + +if [ "$PREFIX_SET" = "yes" -a ! "$SYSCONFDIR_SET" = "yes" ] +then + if [ "$PREFIX" = "/usr" ] + then SYSCONFDIR=/etc/luarocks + else SYSCONFDIR=$PREFIX/etc/luarocks + fi +fi + + +if [ "$PREFIX_SET" = "yes" -a ! "$ROCKS_TREE_SET" = "yes" ] +then + ROCKS_TREE=$PREFIX +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" ] + then + echo "Lua version detected: $detected_lua" + if [ "$LUA_VERSION_SET" != "yes" ] + then + LUA_VERSION=$detected_lua + elif [ "$LUA_VERSION" != "$detected_lua" ] + then + die "This clashes with the value of --with-lua-version. Please check your configuration." + fi + fi +} + +search_interpreter() { + LUA_SUFFIX="$1" + if [ "$LUA_DIR_SET" = "yes" ] + then + if [ -f "$LUA_DIR/bin/lua$suffix" ] + then + find_lua="$LUA_DIR/bin" + fi + else + find_lua=`find_program lua$suffix` + fi + if [ -n "$find_lua" ] + then + echo "Lua interpreter found: $find_lua/lua$suffix..." + detect_lua_version "$find_lua/lua$suffix" + return 0 + fi + return 1 +} + +if [ "$LUA_SUFFIX_SET" != "yes" ] +then + if [ "$LUA_VERSION_SET" = "yes" -a "$LUA_VERSION" = "5.1" ] + then + suffixes="5.1 51" + elif [ "$LUA_VERSION_SET" = "yes" -a "$LUA_VERSION" = "5.2" ] + then + suffixes="5.2 52" + else + suffixes="5.2 52 5.1 51" + fi + for suffix in "" `echo $suffixes` "" + do + search_interpreter "$suffix" && break + done +fi + +if [ "$LUA_DIR_SET" != "yes" ] +then + echo_n "Looking for Lua... " + if [ ! -n "$find_lua" ] + then + find_lua=`find_program lua$LUA_SUFFIX` + fi + + if [ -n "$find_lua" ] + then + LUA_DIR=`dirname $find_lua` + LUA_BINDIR="$find_lua" + echo "lua$LUA_SUFFIX found in \$PATH: $find_lua" + else + echo "lua$LUA_SUFFIX not found in \$PATH." + die "You may want to use the flags --with-lua and/or --lua-suffix. See --help." + fi +fi + +if [ "$LUA_INCDIR_SET" != "yes" ] +then + LUA_INCDIR="$LUA_DIR/include" +fi + +if [ "$LUA_LIBDIR_SET" != "yes" ] +then + LUA_LIBDIR="$LUA_DIR/lib" +fi + +if [ "$LUA_DIR_SET" = "yes" ] +then + LUA_BINDIR="$LUA_DIR/bin" +fi + +echo_n "Checking Lua includes... " +lua_h="$LUA_INCDIR/lua.h" +if [ -f "$lua_h" ] +then + echo "lua.h found in $lua_h" +else + v_dir="$LUA_INCDIR/lua/$LUA_VERSION" + lua_h="$v_dir/lua.h" + if [ -f "$lua_h" ] + then + echo "lua.h found in $lua_h" + LUA_INCDIR="$v_dir" + else + d_dir="$LUA_INCDIR/lua$LUA_VERSION" + lua_h="$d_dir/lua.h" + if [ -f "$lua_h" ] + then + echo "lua.h found in $lua_h (Debian/Ubuntu)" + LUA_INCDIR="$d_dir" + else + echo "lua.h not found (looked in $LUA_INCDIR, $v_dir, $d_dir)" + die "You may want to use the flag --with-lua or --with-lua-include. See --help." + fi + fi +fi + +if [ "$LUAROCKS_DOWNLOADER_SET" != "yes" ] +then + find_helper "downloader helper program" wget curl fetch + LUAROCKS_DOWNLOADER=$HELPER +fi + +if [ "$LUAROCKS_MD5CHECKER_SET" != "yes" ] +then + find_helper "MD5 checksum calculator" md5sum openssl md5 + LUAROCKS_MD5CHECKER=$HELPER +fi + +echo_n "Configuring for system... " +if uname -s +then + LUAROCKS_UNAME_S=`uname -s` +else + die "Could not determine operating system. 'uname -s' failed." +fi +echo_n "Configuring for architecture... " +if uname -m +then + LUAROCKS_UNAME_M=`uname -m` +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 + +LUAROCKS_ROCKS_SUBDIR=/lib/luarocks/rocks +if [ "$VERSIONED_ROCKS_DIR" = "yes" ] +then + LUAROCKS_ROCKS_SUBDIR=$LUAROCKS_ROCKS_SUBDIR-$LUA_VERSION + echo "Using versioned rocks dir: $PREFIX$LUAROCKS_ROCKS_SUBDIR" +elif [ -e "$PREFIX/share/lua/$LUA_VERSION/luarocks/site_config.lua" ] +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" ] +then + echo "Existing installation detected for other Lua version ($LUA_OTHER_VERSION)." + LUAROCKS_ROCKS_SUBDIR=$LUAROCKS_ROCKS_SUBDIR-$LUA_VERSION + echo "Using versioned rocks dir: $PREFIX$LUAROCKS_ROCKS_SUBDIR" +else + echo "Using unversioned rocks dir: $PREFIX$LUAROCKS_ROCKS_SUBDIR" +fi + +if [ -f config.unix ]; then + rm -f config.unix +fi + +# Write config + +echo "Writing configuration..." +echo + +rm -f built +cat <<EOF > config.unix +# This file was automatically generated by the configure script. +# Run "./configure --help" for details. + +LUA_VERSION=$LUA_VERSION +PREFIX=$PREFIX +SYSCONFDIR=$SYSCONFDIR +ROCKS_TREE=$ROCKS_TREE +LUA_SUFFIX=$LUA_SUFFIX +LUA_DIR=$LUA_DIR +LUA_DIR_SET=$LUA_DIR_SET +LUA_INCDIR=$LUA_INCDIR +LUA_LIBDIR=$LUA_LIBDIR +LUA_BINDIR=$LUA_BINDIR +FORCE_CONFIG=$FORCE_CONFIG +LUAROCKS_UNAME_M=$LUAROCKS_UNAME_M +LUAROCKS_UNAME_S=$LUAROCKS_UNAME_S +LUAROCKS_DOWNLOADER=$LUAROCKS_DOWNLOADER +LUAROCKS_MD5CHECKER=$LUAROCKS_MD5CHECKER +LUAROCKS_ROCKS_SUBDIR=$LUAROCKS_ROCKS_SUBDIR + +EOF + +echo "Installation prefix: $PREFIX" +echo "LuaRocks configuration directory: $SYSCONFDIR" +echo "Using Lua from: $LUA_DIR" + +make clean > /dev/null 2> /dev/null + +echo +echo "Done configuring." +echo "- Type 'make build' and 'make install':" +echo " to install to $PREFIX as usual." +echo "- Type 'make bootstrap':" +echo " to install LuaRocks in $PREFIX as a rock." +echo diff --git a/install.bat b/install.bat new file mode 100644 index 0000000..15f2607 --- /dev/null +++ b/install.bat @@ -0,0 +1,649 @@ +rem=rem --[[
+@setlocal& set luafile="%~f0" & if exist "%~f0.bat" set luafile="%~f0.bat"
+@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.LUA_INTERPRETER = nil
+vars.LUA_PREFIX = nil
+vars.LUA_BINDIR = nil
+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_RUNTIME = nil
+
+local P_SET = false
+local FORCE = false
+local FORCE_CONFIG = false
+local INSTALL_LUA = false
+local USE_MINGW = false
+local REGISTRY = false
+
+---
+-- Some helpers
+--
+local function die(message)
+ if message then print(message) end
+ print()
+ print("Failed installing LuaRocks. Run with /? for help.")
+ os.exit(1)
+end
+
+local function exec(cmd)
+ --print(cmd)
+ local status = os.execute(cmd)
+ return status == 0
+end
+
+local function exists(filename)
+ local cmd = [[.\bin\test -e "]]..filename..[["]]
+ return exec(cmd)
+end
+
+local function mkdir (dir)
+ return exec([[.\bin\mkdir -p "]]..dir..[[" >NUL]])
+end
+
+-- interpolate string with values from 'vars' table
+local function S (tmpl)
+ return (tmpl:gsub('%$([%a_][%w_]*)', vars))
+end
+
+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.
+
+/LV [version] Lua version to use; either 5.1 or 5.2.
+ 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\
+ 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
+ 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.
+
+/MW Use mingw as build system instead of MSVC
+
+/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'
+ extension with LuaRocks commands (right-click).
+
+]])
+end
+
+-- ***********************************************************
+-- Option parser
+-- ***********************************************************
+local function parse_options(args)
+ for _, option in ipairs(args) do
+ local name = option.name:upper()
+ if name == "/?" then
+ print_help()
+ 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
+ elseif name == "/SCRIPTS" then
+ vars.SCRIPTS_DIR = option.value
+ elseif name == "/LV" then
+ vars.LUA_VERSION = option.value
+ elseif name == "/L" then
+ INSTALL_LUA = true
+ elseif name == "/MW" then
+ USE_MINGW = true
+ elseif name == "/LUA" then
+ vars.LUA_PREFIX = option.value
+ elseif name == "/LIB" then
+ vars.LUA_LIBDIR = option.value
+ elseif name == "/INC" then
+ vars.LUA_INCDIR = option.value
+ elseif name == "/BIN" then
+ vars.LUA_BINDIR = option.value
+ elseif name == "/FORCECONFIG" then
+ FORCE_CONFIG = true
+ elseif name == "/F" then
+ FORCE = true
+ elseif name == "/R" then
+ REGISTRY = true
+ else
+ die("Unrecognized option: " .. name)
+ end
+ end
+end
+
+-- check for combination/required flags
+local function check_flags()
+ if not P_SET then
+ die("Missing required parameter /P")
+ end
+ if INSTALL_LUA then
+ if vars.LUA_INCDIR or vars.LUA_BINDIR or vars.LUA_LIBDIR or vars.LUA_PREFIX then
+ die("Cannot combine option /L with any of /LUA /BIN /LIB /INC")
+ end
+ if vars.LUA_VERSION ~= "5.1" then
+ die("Bundled Lua version is 5.1, cannot install 5.2")
+ end
+ end
+ 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")
+ else
+ die("Bad argument: /LV must either be 5.1 or 5.2")
+ end
+ end
+end
+
+-- ***********************************************************
+-- Detect Lua
+-- ***********************************************************
+local function look_for_interpreter (directory)
+ if vars.LUA_BINDIR then
+ 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.exe") then
+ vars.LUA_INTERPRETER = "lua.exe"
+ print(S" Found $LUA_BINDIR\\$LUA_INTERPRETER")
+ return true
+ elseif exists(S"$LUA_BINDIR\\luajit.exe") then
+ vars.LUA_INTERPRETER = "luajit.exe"
+ 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")
+ end
+
+ for _, e in ipairs{ [[\]], [[\bin\]] } do
+ if exists(directory..e.."\\lua"..vars.LUA_VERSION..".exe") then
+ vars.LUA_INTERPRETER = S"lua$LUA_VERSION.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
+ print(" Found ."..e..vars.LUA_INTERPRETER)
+ return true
+
+ elseif exists(directory..e.."\\luajit.exe") then
+ vars.LUA_INTERPRETER = "luajit.exe"
+ vars.LUA_BINDIR = directory..e
+ print(" Found ."..e..vars.LUA_INTERPRETER)
+ return true
+ end
+ end
+ print(" No Lua interpreter found")
+ return false
+end
+
+local function look_for_link_libraries (directory)
+ if vars.LUA_LIBDIR then
+ for name in vars.LUA_LIB_NAMES:gmatch("[^%s]+") do
+ print(S" checking for $LUA_LIBDIR\\"..name)
+ if exists(vars.LUA_LIBDIR.."\\"..name) then
+ vars.LUA_LIBNAME = name
+ print(" Found "..name)
+ return true
+ end
+ end
+ die(S"link library (one of; $LUA_LIB_NAMES) not found in $LUA_LIBDIR")
+ end
+
+ for _, e in ipairs{ [[\]], [[\lib\]], [[\bin\]]} do
+ for name in vars.LUA_LIB_NAMES:gmatch("[^%s]+") do
+ print(" checking for "..directory..e.."\\"..name)
+ if exists(directory..e.."\\"..name) then
+ vars.LUA_LIBDIR = directory .. e
+ vars.LUA_LIBNAME = name
+ print(" Found "..name)
+ return true
+ end
+ end
+ end
+ return false
+end
+
+local function look_for_headers (directory)
+ if vars.LUA_INCDIR then
+ print(S" checking for $LUA_INCDIR\\lua.h")
+ if exists(S"$LUA_INCDIR\\lua.h") then
+ print(" Found lua.h")
+ return true
+ end
+ die(S"lua.h not found in $LUA_INCDIR")
+ end
+
+ for _, e in ipairs{ [[\]], [[\include\]]} do
+ print(" checking for "..directory..e.."\\lua.h")
+ if exists(directory..e.."\\lua.h") then
+ vars.LUA_INCDIR = directory..e
+ print(" Found lua.h")
+ return true
+ end
+ end
+ 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
+ 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)
+ end
+ return true
+end
+
+local function look_for_lua_install ()
+ print("Looking for Lua interpreter")
+ local directories = { [[c:\lua5.1.2]], [[c:\lua]], [[c:\kepler\1.1]] }
+ if vars.LUA_PREFIX then
+ table.insert(directories, 1, vars.LUA_PREFIX)
+ end
+ if vars.LUA_BINDIR and vars.LUA_LIBDIR and vars.LUA_INCDIR then
+ if look_for_interpreter(vars.LUA_BINDIR) and
+ look_for_link_libraries(vars.LUA_LIBDIR) and
+ look_for_headers(vars.LUA_INCDIR)
+ then
+ if get_runtime() then
+ print("Runtime check completed, now testing interpreter...")
+ if exec(S[[$LUA_BINDIR\$LUA_INTERPRETER -v 2>NUL]]) then
+ print(" Ok")
+ return true
+ end
+ print(" Interpreter returned an error, not ok")
+ end
+ end
+ return false
+ end
+
+ for _, directory in ipairs(directories) do
+ print(" checking " .. directory)
+ if exists(directory) then
+ if look_for_interpreter(directory) then
+ print("Interpreter found, now looking for link libraries...")
+ if look_for_link_libraries(directory) then
+ print("Link library found, now looking for headers...")
+ if look_for_headers(directory) then
+ 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
+ print(" Ok")
+ return true
+ end
+ print(" Interpreter returned an error, not ok")
+ end
+ end
+ end
+ end
+ end
+ end
+ return false
+end
+
+---
+-- Poor man's command-line parsing
+local config = {}
+local with_arg = { -- options followed by an argument, others are flags
+ ["/P"] = true,
+ ["/CONFIG"] = true,
+ ["/TREE"] = true,
+ ["/SCRIPTS"] = true,
+ ["/LV"] = true,
+ ["/LUA"] = true,
+ ["/INC"] = true,
+ ["/BIN"] = true,
+ ["/LIB"] = true,
+}
+local i = 1
+while i <= #arg do
+ local opt = arg[i]
+ if with_arg[opt] then
+ local value = arg[i + 1]
+ if not value then
+ die("Missing value for option "..opt)
+ end
+ config[#config + 1] = { name = opt, value = value }
+ i = i + 1
+ else
+ config[#config + 1] = { name = opt }
+ end
+ i = i + 1
+end
+
+print(S"LuaRocks $VERSION.x installer.\n")
+
+parse_options(config)
+check_flags()
+
+vars.FULL_PREFIX = S"$PREFIX\\$VERSION"
+vars.BINDIR = vars.FULL_PREFIX
+vars.LIBDIR = vars.FULL_PREFIX
+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 vars.LUA_VERSION ~= "5.1" then
+ die("Cannot install own copy because no 5.2 version 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"
+else
+ print(S[[
+
+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
+
+]])
+end
+
+-- ***********************************************************
+-- Install LuaRocks files
+-- ***********************************************************
+if FORCE then
+ print(S"Removing $FULL_PREFIX...")
+ exec(S[[RD /S /Q "$FULL_PREFIX"]])
+ print()
+end
+
+if exists(vars.FULL_PREFIX) then
+ die(S"$FULL_PREFIX exists. Use /F to force removal and reinstallation.")
+end
+
+print(S"Installing LuaRocks in $FULL_PREFIX...")
+if not exists(vars.BINDIR) then
+ if not mkdir(vars.BINDIR) then
+ die()
+ end
+end
+
+if INSTALL_LUA then
+ -- Copy the included Lua interpreter binaries
+ if not exists(vars.LUA_BINDIR) then
+ mkdir(vars.LUA_BINDIR)
+ end
+ 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]])
+ 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
+ die()
+end
+-- Copy the LuaRocks lua source files
+if not exists(S[[$LUADIR\luarocks]]) then
+ if not mkdir(S[[$LUADIR\luarocks]]) then
+ die()
+ end
+end
+if not exec(S[[XCOPY /S src\luarocks\*.* "$LUADIR\luarocks" >NUL]]) then
+ die()
+end
+-- Create start scripts
+if not exec(S[[COPY src\bin\*.* "$BINDIR" >NUL]]) then
+ die()
+end
+for _, c in ipairs{"luarocks", "luarocks-admin"} do
+ -- rename unix-lua scripts to .lua files
+ if not exec( (S[[RENAME "$BINDIR\%s" %s.lua]]):format(c, c) ) then
+ die()
+ end
+ -- create a bootstrap batch file for the lua file, to start them
+ exec(S[[DEL /F /Q "$BINDIR\]]..c..[[.bat" 2>NUL]])
+ local f = io.open(vars.BINDIR.."\\"..c..".bat", "w")
+ 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" %*
+ENDLOCAL
+]])
+ f:close()
+ print(S"Created LuaRocks command: $BINDIR\\"..c..".bat")
+end
+-- 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
+
+
+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]]
+]=])
+if USE_MINGW then
+ f:write("LUAROCKS_UNAME_S=[[MINGW]]\n")
+else
+ f:write("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]]
+]=])
+if FORCE_CONFIG then
+ f:write("local 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
+ f:write(line)
+ f:write("\n")
+ end
+ exec(S[[DEL /F /Q "$LUADIR\luarocks\site_config.lua.bak"]])
+end
+f:close()
+print(S[[Created LuaRocks site-config file: $LUADIR\luarocks\site_config.lua]])
+
+-- create config file
+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([=[
+rocks_servers = {
+ [[http://luarocks.org/repositories/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")
+else
+ print(S"LuaRocks config file already exists: $CONFIG_FILE")
+end
+
+print()
+print("Creating rocktrees...")
+if not exists(vars.ROCKS_TREE) then
+ mkdir(vars.ROCKS_TREE)
+ print(S[[Created rocktree: "$ROCKS_TREE"]])
+else
+ print(S[[Rocktree exists: "$ROCKS_TREE"]])
+end
+local APPDATA = os.getenv("APPDATA")
+if not exists(APPDATA.."\\luarocks") then
+ mkdir(APPDATA.."\\luarocks")
+ print([[Created rocktree: "]]..APPDATA..[[\luarocks"]])
+else
+ print([[Rocktree exists: "]]..APPDATA..[[\luarocks"]])
+end
+
+-- Load registry information
+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" )
+end
+
+-- ***********************************************************
+-- Exit handlers
+-- ***********************************************************
+
+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
+
+]])
+os.exit(0)
diff --git a/lfw/7z.dll b/lfw/7z.dll Binary files differnew file mode 100644 index 0000000..c0ff7fb --- /dev/null +++ b/lfw/7z.dll diff --git a/lfw/7z.exe b/lfw/7z.exe Binary files differnew file mode 100644 index 0000000..5e3d6f9 --- /dev/null +++ b/lfw/7z.exe diff --git a/lfw/lua/luarocks/config.lua b/lfw/lua/luarocks/config.lua new file mode 100644 index 0000000..fd0e2fb --- /dev/null +++ b/lfw/lua/luarocks/config.lua @@ -0,0 +1,16 @@ +local os = os + +module("luarocks.config") +LFW_ROOT = os.getenv("LUA_DEV") +LUA_INCDIR=LFW_ROOT..[[\include]] +LUA_LIBDIR=LFW_ROOT..[[\lib]] +LUA_BINDIR=LFW_ROOT +LUA_INTERPRETER=[[lua]] +LUAROCKS_UNAME_S=[[WindowsNT]] +LUAROCKS_UNAME_M=[[x86]] +LUAROCKS_SYSCONFIG=LFW_ROOT..[[\luarocks_config.lua]] +LUAROCKS_ROCKS_TREE=LFW_ROOT +LUAROCKS_PREFIX=LFW_ROOT +LUAROCKS_DOWNLOADER=[[wget]] +LUAROCKS_MD5CHECKER=[[md5sum]] + diff --git a/lfw/luarocks-admin.bat b/lfw/luarocks-admin.bat new file mode 100644 index 0000000..fb95d0f --- /dev/null +++ b/lfw/luarocks-admin.bat @@ -0,0 +1,4 @@ +@ECHO OFF
+SETLOCAL
+"%LUA_DEV%\lua" "%LUA_DEV%\luarocks-admin.lua" %*
+ENDLOCAL
diff --git a/lfw/luarocks-admin.lua b/lfw/luarocks-admin.lua new file mode 100644 index 0000000..4f7a9a2 --- /dev/null +++ b/lfw/luarocks-admin.lua @@ -0,0 +1,16 @@ +#!/usr/local/bin/lua + +local command_line = require("luarocks.command_line") + +program_name = "luarocks-admin" +program_description = "LuaRocks repository administration interface" + +commands = { +} + +commands.help = require("luarocks.help") +commands.make_manifest = require("luarocks.make_manifest") +commands.add = require("luarocks.add") +commands.refresh_cache = require("luarocks.refresh_cache") + +command_line.run_command(...) diff --git a/lfw/luarocks.bat b/lfw/luarocks.bat new file mode 100644 index 0000000..44360aa --- /dev/null +++ b/lfw/luarocks.bat @@ -0,0 +1,4 @@ +@ECHO OFF
+SETLOCAL
+"%LUA_DEV%\lua" "%LUA_DEV%\luarocks.lua" %*
+ENDLOCAL
diff --git a/lfw/luarocks.lua b/lfw/luarocks.lua new file mode 100644 index 0000000..1c78125 --- /dev/null +++ b/lfw/luarocks.lua @@ -0,0 +1,20 @@ +#!/usr/local/bin/lua + +local command_line = require("luarocks.command_line") + +program_name = "luarocks" +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") + +command_line.run_command(...) diff --git a/lfw/luarocks_config.lua b/lfw/luarocks_config.lua new file mode 100644 index 0000000..557890f --- /dev/null +++ b/lfw/luarocks_config.lua @@ -0,0 +1,10 @@ +local LFW_ROOT = config.LFW_ROOT
+rocks_servers = {
+ [[http://luarocks.org/repositories/rocks]]
+}
+rocks_trees = {
+ { root = LFW_ROOT, rocks_dir = LFW_ROOT..[[\rocks]],
+ bin_dir = LFW_ROOT, lua_dir = LFW_ROOT..[[\lua]],
+ lib_dir = LFW_ROOT..[[\clibs]] }
+}
+variables.WRAPPER = LFW_ROOT..[[\rclauncher.c]]
diff --git a/lfw/rclauncher.o b/lfw/rclauncher.o Binary files differnew file mode 100644 index 0000000..0fe5d95 --- /dev/null +++ b/lfw/rclauncher.o diff --git a/lfw/rclauncher.obj b/lfw/rclauncher.obj Binary files differnew file mode 100644 index 0000000..86a3279 --- /dev/null +++ b/lfw/rclauncher.obj diff --git a/lfw/rocks/index.html b/lfw/rocks/index.html new file mode 100644 index 0000000..d6baff4 --- /dev/null +++ b/lfw/rocks/index.html @@ -0,0 +1,87 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<title>Available rocks</title>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<style>
+body {
+ background-color: white;
+ font-family: "bitstream vera sans", "verdana", "sans";
+ font-size: 14px;
+}
+a {
+ color: #0000c0;
+ text-decoration: none;
+}
+a:hover {
+ text-decoration: underline;
+}
+td.main {
+ border-style: none;
+}
+blockquote {
+ font-size: 12px;
+}
+td.package {
+ background-color: #f0f0f0;
+ vertical-align: top;
+}
+td.spacer {
+ height: 5px;
+}
+td.version {
+ background-color: #d0d0d0;
+ vertical-align: top;
+ text-align: left;
+ padding: 5px;
+ width: 100px;
+}
+p.manifest {
+ font-size: 8px;
+}
+</style>
+</head>
+<body>
+<h1>Available rocks</h1>
+<p>
+Lua modules available from this location for use with <a href="http://www.luarocks.org">LuaRocks</a>:
+</p>
+<table class="main">
+<td class="package">
+<p><a name="luafilesystem"></a><b>luafilesystem</b> - <br/>
+</p><blockquote><p><br/>
+<font size="-1"><a href="">latest sources</a> | License: </font></p>
+</blockquote></a></td>
+<td class="version">
+1.5.0-1: <a href="luafilesystem-1.5.0-1.installed.rock">installed</a><br/></td></tr>
+<tr><td colspan="2" class="spacer"></td></tr>
+<td class="package">
+<p><a name="luasocket"></a><b>luasocket</b> - <br/>
+</p><blockquote><p><br/>
+<font size="-1"><a href="">latest sources</a> | License: </font></p>
+</blockquote></a></td>
+<td class="version">
+2.0.2-3: <a href="luasocket-2.0.2-3.installed.rock">installed</a><br/></td></tr>
+<tr><td colspan="2" class="spacer"></td></tr>
+<td class="package">
+<p><a name="luazip"></a><b>luazip</b> - <br/>
+</p><blockquote><p><br/>
+<font size="-1"><a href="">latest sources</a> | License: </font></p>
+</blockquote></a></td>
+<td class="version">
+1.2.3-2: <a href="luazip-1.2.3-2.installed.rock">installed</a><br/></td></tr>
+<tr><td colspan="2" class="spacer"></td></tr>
+<td class="package">
+<p><a name="md5"></a><b>md5</b> - <br/>
+</p><blockquote><p><br/>
+<font size="-1"><a href="">latest sources</a> | License: </font></p>
+</blockquote></a></td>
+<td class="version">
+1.1.2-1: <a href="md5-1.1.2-1.installed.rock">installed</a><br/></td></tr>
+<tr><td colspan="2" class="spacer"></td></tr>
+</table>
+<p class="manifest">
+<a href="manifest">manifest file</a>
+</p>
+</body>
+</html>
diff --git a/lfw/rocks/luafilesystem/1.5.0-1/doc/us/examples.html b/lfw/rocks/luafilesystem/1.5.0-1/doc/us/examples.html new file mode 100644 index 0000000..746df62 --- /dev/null +++ b/lfw/rocks/luafilesystem/1.5.0-1/doc/us/examples.html @@ -0,0 +1,103 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> + <title>LuaFileSystem</title> + <link rel="stylesheet" href="http://www.keplerproject.org/doc.css" type="text/css"/> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +</head> + +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"> + <a href="http://www.keplerproject.org"> + <img alt="LuaFileSystem" src="luafilesystem.png"/> + </a> + </div> + <div id="product_name"><big><strong>LuaFileSystem</strong></big></div> + <div id="product_description">File System Library for the Lua Programming Language</div> +</div> <!-- id="product" --> + +<div id="main"> + +<div id="navigation"> +<h1>LuaFileSystem</h1> + <ul> + <li><a href="index.html">Home</a> + <ul> + <li><a href="index.html#overview">Overview</a></li> + <li><a href="index.html#status">Status</a></li> + <li><a href="index.html#download">Download</a></li> + <li><a href="index.html#history">History</a></li> + <li><a href="index.html#credits">Credits</a></li> + <li><a href="index.html#contact">Contact us</a></li> + </ul> + </li> + <li><a href="manual.html">Manual</a> + <ul> + <li><a href="manual.html#introduction">Introduction</a></li> + <li><a href="manual.html#building">Building</a></li> + <li><a href="manual.html#installation">Installation</a></li> + <li><a href="manual.html#reference">Reference</a></li> + </ul> + </li> + <li><strong>Examples</strong></li> + <li><a href="http://luaforge.net/projects/luafilesystem/">Project</a> + <ul> + <li><a href="http://luaforge.net/tracker/?group_id=66">Bug Tracker</a></li> + <li><a href="http://luaforge.net/scm/?group_id=66">CVS</a></li> + </ul> + </li> + <li><a href="license.html">License</a></li> + </ul> +</div> <!-- id="navigation" --> + +<div id="content"> + +<h2><a name="example"></a>Examples</h2> + +<h3>Directory iterator</h3> + +<p>The following example iterates over a directory and recursively lists the +attributes for each file inside it.</p> + +<pre class="example"> +require"lfs" + +function attrdir (path) + for file in lfs.dir(path) do + if file ~= "." and file ~= ".." then + local f = path..'/'..file + print ("\t "..f) + local attr = lfs.attributes (f) + assert (type(attr) == "table") + if attr.mode == "directory" then + attrdir (f) + else + for name, value in pairs(attr) do + print (name, value) + end + end + end + end +end + +attrdir (".") +</pre> + +</div> <!-- id="content" --> + +</div> <!-- id="main" --> + +<div id="about"> + <p><a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.0!</a></p> + <p><small>$Id: examples.html,v 1.8 2007/12/14 15:28:04 carregal Exp $</small></p> +</div> <!-- id="about" --> + +</div> <!-- id="container" --> + +</body> +</html> diff --git a/lfw/rocks/luafilesystem/1.5.0-1/doc/us/index.html b/lfw/rocks/luafilesystem/1.5.0-1/doc/us/index.html new file mode 100644 index 0000000..43edefc --- /dev/null +++ b/lfw/rocks/luafilesystem/1.5.0-1/doc/us/index.html @@ -0,0 +1,192 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> + <title>LuaFileSystem</title> + <link rel="stylesheet" href="http://www.keplerproject.org/doc.css" type="text/css"/> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +</head> + +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"> + <a href="http://www.keplerproject.org"> + <img alt="LuaFileSystem" src="luafilesystem.png"/> + </a> + </div> + <div id="product_name"><big><strong>LuaFileSystem</strong></big></div> + <div id="product_description">File System Library for the Lua Programming Language</div> +</div> <!-- id="product" --> + +<div id="main"> + +<div id="navigation"> +<h1>LuaFileSystem</h1> + <ul> + <li><strong>Home</strong> + <ul> + <li><a href="index.html#overview">Overview</a></li> + <li><a href="index.html#status">Status</a></li> + <li><a href="index.html#download">Download</a></li> + <li><a href="index.html#history">History</a></li> + <li><a href="index.html#credits">Credits</a></li> + <li><a href="index.html#contact">Contact us</a></li> + </ul> + </li> + <li><a href="manual.html">Manual</a> + <ul> + <li><a href="manual.html#introduction">Introduction</a></li> + <li><a href="manual.html#building">Building</a></li> + <li><a href="manual.html#installation">Installation</a></li> + <li><a href="manual.html#reference">Reference</a></li> + </ul> + </li> + <li><a href="examples.html">Examples</a></li> + <li><a href="http://luaforge.net/projects/luafilesystem/">Project</a> + <ul> + <li><a href="http://luaforge.net/tracker/?group_id=66">Bug Tracker</a></li> + <li><a href="http://luaforge.net/scm/?group_id=66">CVS</a></li> + </ul> + </li> + <li><a href="license.html">License</a></li> + </ul> +</div> <!-- id="navigation" --> + +<div id="content"> + +<h2><a name="overview"></a>Overview</h2> + +<p>LuaFileSystem is a <a href="http://www.lua.org">Lua</a> library +developed to complement the set of functions related to file +systems offered by the standard Lua distribution.</p> + +<p>LuaFileSystem offers a portable way to access +the underlying directory structure and file attributes.</p> + +<p>LuaFileSystem is free software and uses the same +<a href="license.html">license</a> as Lua 5.1.</p> + +<h2><a name="status"></a>Status</h2> + +<p>Current version is 1.5.0. It was developed for Lua 5.1.</p> + +<h2><a name="download"></a>Download</h2> + +<p>LuaFileSystem source can be downloaded from its +<a href="http://github.com/keplerproject/luafilesystem">Github</a> +page.</p> + +<h2><a name="history"></a>History</h2> + +<dl class="history"> + <dt><strong>Version 1.5.0</strong> [20/Oct/2009]</dt> + <li>Added explicit next and close methods to second return value of lfs.dir +(the directory object), for explicit iteration or explicit closing.</li> + <li>Added directory locking via lfs.lock_dir function (see the <a href="manual.html">manual</a>).</li> + <dt><strong>Version 1.4.2</strong> [03/Feb/2009]</dt> + <dd> + <ul> + <li>fixed bug [<a href="http://luaforge.net/tracker/?func=detail&group_id=66&aid=13198&atid=356">#13198</a>] + lfs.attributes(filename, 'size') overflow on files > 2 Gb again (bug report and patch by KUBO Takehiro).</li> + <li>fixed bug [<a href="http://luaforge.net/tracker/?group_id=66&atid=356&func=detail&aid=39794">#39794</a>] + Compile error on Solaris 10 (bug report and patch by Aaron B).</li> + <li>fixed compilation problems with Borland C.</li> + </ul> + </dd> + + <dt><strong>Version 1.4.1</strong> [07/May/2008]</dt> + <dd> + <ul> + <li>documentation review</li> + <li>fixed Windows compilation issues</li> + <li>fixed bug in the Windows tests (patch by Shmuel Zeigerman)</li> + <li>fixed bug [<a href="http://luaforge.net/tracker/?func=detail&group_id=66&aid=2185&atid=356">#2185</a>] + <code>lfs.attributes(filename, 'size')</code> overflow on files > 2 Gb + </li> + </ul> + </dd> + + <dt><strong>Version 1.4.0</strong> [13/Feb/2008]</dt> + <dd> + <ul> + <li>added function + <a href="manual.html#setmode"><code>lfs.setmode</code></a> + (works only in Windows systems).</li> + <li><a href="manual.html#attributes"><code>lfs.attributes</code></a> + raises an error if attribute does not exist</li> + </ul> + </dd> + + <dt><strong><a href="http://www.keplerproject.org/luafilesystem/1.3/">Version 1.3.0</a></strong> [26/Oct/2007]</dt> + <dd> + <ul> + <li>added function + <a href="manual.html#symlinkattributes"><code>lfs.symlinkattributes</code></a> + (works only in non Windows systems).</li> + </ul> + </dd> + + <dt><strong><a href="http://www.keplerproject.org/luafilesystem/1.2/">Version 1.2.1</a></strong> [08/May/2007]</dt> + <dd> + <ul> + <li>compatible only with Lua 5.1 (Lua 5.0 support was dropped)</li> + </ul> + </dd> + + <dt><strong><a href="http://www.keplerproject.org/luafilesystem/1.2/">Version 1.2</a></strong> [15/Mar/2006]</dt> + <dd> + <ul> + <li>added optional argument to + <a href="manual.html#attributes"><code>lfs.attributes</code></a></li> + <li>added function + <a href="manual.html#rmdir"><code>lfs.rmdir</code></a></li> + <li>bug correction on <a href="manual.html#dir"><code>lfs.dir</code></a></li> + </ul> + </dd> + + <dt><strong><a href="http://www.keplerproject.org/luafilesystem/1.1/">Version 1.1</a></strong> [30/May/2005]</dt> + <dd> + <ul> + <li>added function <a href="manual.html#touch"><code>lfs.touch</code></a>.</li> + </ul> + </dd> + + <dt><strong><a href="http://www.keplerproject.org/luafilesystem/1.0/">Version 1.0</a></strong> [21/Jan/2005]</dt> + <dd /> + + <dt><strong>Version 1.0 Beta</strong> [10/Nov/2004]</dt> + <dd /> +</dl> + +<h2><a name="credits"></a>Credits</h2> + +<p>LuaFileSystem was designed by Roberto Ierusalimschy, +André Carregal and Tomás Guisasola as part of the +<a href="http://www.keplerproject.org">Kepler Project</a>, +which holds its copyright. LuaFileSystem is currently maintained by Fábio Mascarenhas.</p> + +<h2><a name="contact"></a>Contact us</h2> + +<p>For more information please +<a href="mailto:info-NO-SPAM-THANKS@keplerproject.org">contact us</a>. +Comments are welcome!</p> + +<p>You can also reach other Kepler developers and users on the Kepler Project +<a href="http://luaforge.net/mail/?group_id=104">mailing list</a>.</p> + +</div> <!-- id="content" --> + +</div> <!-- id="main" --> + +<div id="about"> + <p><a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.0!</a></p> + <p><small>$Id: index.html,v 1.44 2009/02/04 21:21:33 carregal Exp $</small></p> +</div> <!-- id="about" --> + +</div> <!-- id="container" --> + +</body> +</html> diff --git a/lfw/rocks/luafilesystem/1.5.0-1/doc/us/license.html b/lfw/rocks/luafilesystem/1.5.0-1/doc/us/license.html new file mode 100644 index 0000000..4ecad4b --- /dev/null +++ b/lfw/rocks/luafilesystem/1.5.0-1/doc/us/license.html @@ -0,0 +1,122 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> + <title>LuaFileSystem</title> + <link rel="stylesheet" href="http://www.keplerproject.org/doc.css" type="text/css"/> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +</head> + +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"> + <a href="http://www.keplerproject.org"> + <img alt="LuaFileSystem" src="luafilesystem.png"/> + </a> + </div> + <div id="product_name"><big><strong>LuaFileSystem</strong></big></div> + <div id="product_description">File System Library for the Lua Programming Language</div> +</div> <!-- id="product" --> + +<div id="main"> + +<div id="navigation"> +<h1>LuaFileSystem</h1> + <ul> + <li><a href="index.html">Home</a> + <ul> + <li><a href="index.html#overview">Overview</a></li> + <li><a href="index.html#status">Status</a></li> + <li><a href="index.html#download">Download</a></li> + <li><a href="index.html#history">History</a></li> + <li><a href="index.html#credits">Credits</a></li> + <li><a href="index.html#contact">Contact us</a></li> + </ul> + </li> + <li><a href="manual.html">Manual</a> + <ul> + <li><a href="manual.html#introduction">Introduction</a></li> + <li><a href="manual.html#building">Building</a></li> + <li><a href="manual.html#installation">Installation</a></li> + <li><a href="manual.html#reference">Reference</a></li> + </ul> + </li> + <li><a href="examples.html">Examples</a></li> + <li><a href="http://luaforge.net/projects/luafilesystem/">Project</a> + <ul> + <li><a href="http://luaforge.net/tracker/?group_id=66">Bug Tracker</a></li> + <li><a href="http://luaforge.net/scm/?group_id=66">CVS</a></li> + </ul> + </li> + <li><strong>License</strong></li> + </ul> +</div> <!-- id="navigation" --> + +<div id="content"> + +<h1>License</h1> + +<p> +LuaFileSystem 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. LuaFileSystem +qualifies as +<a href="http://www.opensource.org/docs/definition.html">Open Source</a> +software. +Its licenses are compatible with +<a href="http://www.gnu.org/licenses/gpl.html">GPL</a>. +LuaFileSystem is not in the public domain and the +<a href="http://www.keplerproject.org">Kepler Project</a> +keep its copyright. +The legal details are below. +</p> + +<p>The spirit of the license is that you are free to use +LuaFileSystem for any purpose at no cost without having to ask us. +The only requirement is that if you do use LuaFileSystem, then you +should give us credit by including the appropriate copyright notice +somewhere in your product or its documentation.</p> + +<p>The LuaFileSystem library is designed and implemented by Roberto +Ierusalimschy, André Carregal and Tomás Guisasola. +The implementation is not derived from licensed software.</p> + +<hr/> +<p>Copyright © 2003 Kepler Project.</p> + +<p>Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions:</p> + +<p>The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software.</p> + +<p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.</p> + +</div> <!-- id="content" --> + +</div> <!-- id="main" --> + +<div id="about"> + <p><a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.0!</a></p> + <p><small>$Id: license.html,v 1.13 2008/02/11 22:42:21 carregal Exp $</small></p> +</div><!-- id="about" --> + +</div><!-- id="container" --> + +</body> +</html> diff --git a/lfw/rocks/luafilesystem/1.5.0-1/doc/us/luafilesystem.png b/lfw/rocks/luafilesystem/1.5.0-1/doc/us/luafilesystem.png Binary files differnew file mode 100644 index 0000000..e1dd8c6 --- /dev/null +++ b/lfw/rocks/luafilesystem/1.5.0-1/doc/us/luafilesystem.png diff --git a/lfw/rocks/luafilesystem/1.5.0-1/doc/us/manual.html b/lfw/rocks/luafilesystem/1.5.0-1/doc/us/manual.html new file mode 100644 index 0000000..1409c40 --- /dev/null +++ b/lfw/rocks/luafilesystem/1.5.0-1/doc/us/manual.html @@ -0,0 +1,271 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> + <title>LuaFileSystem</title> + <link rel="stylesheet" href="http://www.keplerproject.org/doc.css" type="text/css"/> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +</head> + +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"> + <a href="http://www.keplerproject.org"><img alt="LuaFileSystem" src="luafilesystem.png"/></a> + </div> + <div id="product_name"><big><strong>LuaFileSystem</strong></big></div> + <div id="product_description">File System Library for the Lua Programming Language</div> +</div> <!-- id="product" --> + +<div id="main"> + +<div id="navigation"> +<h1>LuaFileSystem</h1> + <ul> + <li><a href="index.html">Home</a> + <ul> + <li><a href="index.html#overview">Overview</a></li> + <li><a href="index.html#status">Status</a></li> + <li><a href="index.html#download">Download</a></li> + <li><a href="index.html#history">History</a></li> + <li><a href="index.html#credits">Credits</a></li> + <li><a href="index.html#contact">Contact us</a></li> + </ul> + </li> + <li><strong>Manual</strong> + <ul> + <li><a href="manual.html#introduction">Introduction</a></li> + <li><a href="manual.html#building">Building</a></li> + <li><a href="manual.html#installation">Installation</a></li> + <li><a href="manual.html#reference">Reference</a></li> + </ul> + </li> + <li><a href="examples.html">Examples</a></li> + <li><a href="http://luaforge.net/projects/luafilesystem/">Project</a> + <ul> + <li><a href="http://luaforge.net/tracker/?group_id=66">Bug Tracker</a></li> + <li><a href="http://luaforge.net/scm/?group_id=66">CVS</a></li> + </ul> + </li> + <li><a href="license.html">License</a></li> + </ul> +</div> <!-- id="navigation" --> + +<div id="content"> + +<h2><a name="introduction"></a>Introduction</h2> + +<p>LuaFileSystem is a <a href="http://www.lua.org">Lua</a> library +developed to complement the set of functions related to file +systems offered by the standard Lua distribution.</p> + +<p>LuaFileSystem offers a portable way to access +the underlying directory structure and file attributes.</p> + +<h2><a name="building"></a>Building</h2> + +<p> +LuaFileSystem should be built with Lua 5.1 so the language library +and header files for the target version must be installed properly. +</p> + +<p> +LuaFileSystem offers a Makefile and a separate configuration file, +<code>config</code>, +which should be edited to suit your installation before running +<code>make</code>. +The file has some definitions like paths to the external libraries, +compiler options and the like. +</p> + +<p>On Windows, the C runtime used to compile LuaFileSystem must be the same +runtime that Lua uses, or some LuaFileSystem functions will not work.</p> + +<h2><a name="installation"></a>Installation</h2> + +<p>The easiest way to install LuaFileSystem is to use LuaRocks:</p> + +<pre class="example"> +luarocks install luafilesystem +</pre> + +<p>If you prefer to install LuaFileSystem manually, the compiled binary should be copied to a directory in your +<a href="http://www.lua.org/manual/5.1/manual.html#pdf-package.cpath">C path</a>.</p> + +<h2><a name="reference"></a>Reference</h2> + +<p> +LuaFileSystem offers the following functions: +</p> + +<dl class="reference"> + <dt><a name="attributes"></a><strong><code>lfs.attributes (filepath [, aname])</code></strong></dt> + <dd>Returns a table with the file attributes corresponding to + <code>filepath</code> (or <code>nil</code> followed by an error message + in case of error). + If the second optional argument is given, then only the value of the + named attribute is returned (this use is equivalent to + <code>lfs.attributes(filepath).aname</code>, but the table is not created + and only one attribute is retrieved from the O.S.). + The attributes are described as follows; + attribute <code>mode</code> is a string, all the others are numbers, + and the time related attributes use the same time reference of + <a href="http://www.lua.org/manual/5.1/manual.html#pdf-os.time"><code>os.time</code></a>: + <dl> + <dt><strong><code>dev</code></strong></dt> + <dd>on Unix systems, this represents the device that the inode resides on. On Windows systems, + represents the drive number of the disk containing the file</dd> + + <dt><strong><code>ino</code></strong></dt> + <dd>on Unix systems, this represents the inode number. On Windows systems this has no meaning</dd> + + <dt><strong><code>mode</code></strong></dt> + <dd>string representing the associated protection mode (the values could be + <code>file</code>, <code>directory</code>, <code>link</code>, <code>socket</code>, + <code>named pipe</code>, <code>char device</code>, <code>block device</code> or + <code>other</code>)</dd> + + <dt><strong><code>nlink</code></strong></dt> + <dd>number of hard links to the file</dd> + + <dt><strong><code>uid</code></strong></dt> + <dd>user-id of owner (Unix only, always 0 on Windows)</dd> + + <dt><strong><code>gid</code></strong></dt> + <dd>group-id of owner (Unix only, always 0 on Windows)</dd> + + <dt><strong><code>rdev</code></strong></dt> + <dd>on Unix systems, represents the device type, for special file inodes. + On Windows systems represents the same as <code>dev</code></dd> + + <dt><strong><code>access</code></strong></dt> + <dd>time of last access</dd> + + <dt><strong><code>modification</code></strong></dt> + <dd>time of last data modification</dd> + + <dt><strong><code>change</code></strong></dt> + <dd>time of last file status change</dd> + + <dt><strong><code>size</code></strong></dt> + <dd>file size, in bytes</dd> + + <dt><strong><code>blocks</code></strong></dt> + <dd>block allocated for file; (Unix only)</dd> + + <dt><strong><code>blksize</code></strong></dt> + <dd>optimal file system I/O blocksize; (Unix only)</dd> + </dl> + This function uses <code>stat</code> internally thus if the given + <code>filepath</code> is a symbolic link, it is followed (if it points to + another link the chain is followed recursively) and the information + is about the file it refers to. + To obtain information about the link itself, see function + <a href="#symlinkattributes">lfs.symlinkattributes</a>. + </dd> + + <dt><a name="chdir"></a><strong><code>lfs.chdir (path)</code></strong></dt> + <dd>Changes the current working directory to the given + <code>path</code>.<br /> + Returns <code>true</code> in case of success or <code>nil</code> plus an + error string.</dd> + + <dt><a name="chdir"></a><strong><code>lfs.lock_dir(path, [seconds_stale])</code></strong></dt> + <dd>Creates a lockfile (called lockfile.lfs) in <code>path</code> if it does not + exist and returns the lock. If the lock already exists checks it + it's stale, using the second parameter (default for the second + parameter is <code>INT_MAX</code>, which in practice means the lock will never + be stale. To free the the lock call <code>lock:free()</code>. <br/> + In case of any errors it returns nil and the error message. In + particular, if the lock exists and is not stale it returns the + "File exists" message.</dd> + + <dt><a name="getcwd"></a><strong><code>lfs.currentdir ()</code></strong></dt> + <dd>Returns a string with the current working directory or <code>nil</code> + plus an error string.</dd> + + <dt><a name="dir"></a><strong><code>iter, dir_obj = lfs.dir (path)</code></strong></dt> + <dd> + Lua iterator over the entries of a given directory. + Each time the iterator is called with <code>dir_obj</code> it returns a directory entry's name as a string, or + <code>nil</code> if there are no more entries. You can also iterate by calling <code>dir_obj:next()</code>, and + explicitly close the directory before the iteration finished with <code>dir_obj:close()</code>. + Raises an error if <code>path</code> is not a directory. + </dd> + + <dt><a name="lock"></a><strong><code>lfs.lock (filehandle, mode[, start[, length]])</code></strong></dt> + <dd>Locks a file or a part of it. This function works on <em>open files</em>; the + file handle should be specified as the first argument. + The string <code>mode</code> could be either + <code>r</code> (for a read/shared lock) or <code>w</code> (for a + write/exclusive lock). The optional arguments <code>start</code> + and <code>length</code> can be used to specify a starting point and + its length; both should be numbers.<br /> + Returns <code>true</code> if the operation was successful; in + case of error, it returns <code>nil</code> plus an error string. + </dd> + + <dt><a name="mkdir"></a><strong><code>lfs.mkdir (dirname)</code></strong></dt> + <dd>Creates a new directory. The argument is the name of the new + directory.<br /> + Returns <code>true</code> if the operation was successful; + in case of error, it returns <code>nil</code> plus an error string. + </dd> + + <dt><a name="rmdir"></a><strong><code>lfs.rmdir (dirname)</code></strong></dt> + <dd>Removes an existing directory. The argument is the name of the directory.<br /> + Returns <code>true</code> if the operation was successful; + in case of error, it returns <code>nil</code> plus an error string.</dd> + + <dt><a name="setmode"></a><strong><code>lfs.setmode (file, mode)</code></strong></dt> + <dd>Sets the writing mode for a file. The mode string can be either <code>binary</code> or <code>text</code>. + Returns the previous mode string for the file. This function is only available in Windows, so you may want to make sure that + <code>lfs.setmode</code> exists before using it. + </dd> + + <dt><a name="symlinkattributes"></a><strong><code>lfs.symlinkattributes (filepath [, aname])</code></strong></dt> + <dd>Identical to <a href="#attributes">lfs.attributes</a> except that + it obtains information about the link itself (not the file it refers to). + This function is not available in Windows so you may want to make sure that + <code>lfs.symlinkattributes</code> exists before using it. + </dd> + + <dt><a name="touch"></a><strong><code>lfs.touch (filepath [, atime [, mtime]])</code></strong></dt> + <dd>Set access and modification times of a file. This function is + a bind to <code>utime</code> function. The first argument is the + filename, the second argument (<code>atime</code>) is the access time, + and the third argument (<code>mtime</code>) is the modification time. + Both times are provided in seconds (which should be generated with + Lua standard function <code>os.time</code>). + If the modification time is omitted, the access time provided is used; + if both times are omitted, the current time is used.<br /> + Returns <code>true</code> if the operation was successful; + in case of error, it returns <code>nil</code> plus an error string. + </dd> + + <dt><a name="unlock"></a><strong><code>lfs.unlock (filehandle[, start[, length]])</code></strong></dt> + <dd>Unlocks a file or a part of it. This function works on + <em>open files</em>; the file handle should be specified as the first + argument. The optional arguments <code>start</code> and + <code>length</code> can be used to specify a starting point and its + length; both should be numbers.<br /> + Returns <code>true</code> if the operation was successful; + in case of error, it returns <code>nil</code> plus an error string. + </dd> +</dl> + +</div> <!-- id="content" --> + +</div> <!-- id="main" --> + +<div id="about"> + <p><a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.0!</a></p> + <p><small>$Id: manual.html,v 1.45 2009/06/03 20:53:55 mascarenhas Exp $</small></p> +</div> <!-- id="about" --> + +</div> <!-- id="container" --> + +</body> +</html> diff --git a/lfw/rocks/luafilesystem/1.5.0-1/luafilesystem-1.5.0-1.rockspec b/lfw/rocks/luafilesystem/1.5.0-1/luafilesystem-1.5.0-1.rockspec new file mode 100644 index 0000000..1170ad2 --- /dev/null +++ b/lfw/rocks/luafilesystem/1.5.0-1/luafilesystem-1.5.0-1.rockspec @@ -0,0 +1,27 @@ +package = "LuaFileSystem" + +version = "1.5.0-1" + +source = { + url = "http://cloud.github.com/downloads/keplerproject/luafilesystem/luafilesystem-1.5.0.tar.gz", +} + +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]] +} + +dependencies = { + "lua >= 5.1" +} + +build = { + type = "module", + modules = { lfs = "src/lfs.c" }, + copy_directories = { "doc", "tests" } +} diff --git a/lfw/rocks/luafilesystem/1.5.0-1/rock_manifest b/lfw/rocks/luafilesystem/1.5.0-1/rock_manifest new file mode 100644 index 0000000..f04415b --- /dev/null +++ b/lfw/rocks/luafilesystem/1.5.0-1/rock_manifest @@ -0,0 +1,18 @@ +rock_manifest = {
+ doc={
+ us={
+ ['examples.html']='bc2c38e7bb14ee8a2c7dfde31d847589',
+ ['license.html']='d6b3b3fc89fc8e2632120161dbccb91c',
+ ['luafilesystem.png']='81e923e976e99f894ea0aa8b52baff29',
+ ['index.html']='3280eecd8a8213280ea6fc63aeca85d3',
+ ['manual.html']='4ce5fbcb073538cb5509c20e7fa4b13b'
+ }
+ },
+ ['luafilesystem-1.5.0-1.rockspec']='f24df0bf7653276bcff6e80c53b45cb0',
+ lib={
+ ['lfs.dll']='e0500be912db2d07f08b4c4de202046e'
+ },
+ tests={
+ ['test.lua']='ce1edc52d74c6d9a28aefb73c80c6f29'
+ }
+}
diff --git a/lfw/rocks/luafilesystem/1.5.0-1/tests/test.lua b/lfw/rocks/luafilesystem/1.5.0-1/tests/test.lua new file mode 100644 index 0000000..7111074 --- /dev/null +++ b/lfw/rocks/luafilesystem/1.5.0-1/tests/test.lua @@ -0,0 +1,130 @@ +#!/usr/local/bin/lua5.1 + +local tmp = "/tmp" +local sep = "/" +local upper = ".." + +require"lfs" +print (lfs._VERSION) + +function attrdir (path) + for file in lfs.dir(path) do + if file ~= "." and file ~= ".." then + local f = path..sep..file + print ("\t=> "..f.." <=") + local attr = lfs.attributes (f) + assert (type(attr) == "table") + if attr.mode == "directory" then + attrdir (f) + else + for name, value in pairs(attr) do + print (name, value) + end + end + end + end +end + +-- Checking changing directories +local current = assert (lfs.currentdir()) +local reldir = string.gsub (current, "^.*%"..sep.."([^"..sep.."])$", "%1") +assert (lfs.chdir (upper), "could not change to upper directory") +assert (lfs.chdir (reldir), "could not change back to current directory") +assert (lfs.currentdir() == current, "error trying to change directories") +assert (lfs.chdir ("this couldn't be an actual directory") == nil, "could change to a non-existent directory") + +-- Changing creating and removing directories +local tmpdir = current..sep.."lfs_tmp_dir" +local tmpfile = tmpdir..sep.."tmp_file" +-- Test for existence of a previous lfs_tmp_dir +-- that may have resulted from an interrupted test execution and remove it +if lfs.chdir (tmpdir) then + assert (lfs.chdir (upper), "could not change to upper directory") + assert (os.remove (tmpfile), "could not remove file from previous test") + assert (lfs.rmdir (tmpdir), "could not remove directory from previous test") +end + +-- tries to create a directory +assert (lfs.mkdir (tmpdir), "could not make a new directory") +local attrib, errmsg = lfs.attributes (tmpdir) +if not attrib then + error ("could not get attributes of file `"..tmpdir.."':\n"..errmsg) +end +local f = io.open(tmpfile, "w") +f:close() + +-- Change access time +local testdate = os.time({ year = 2007, day = 10, month = 2, hour=0}) +assert (lfs.touch (tmpfile, testdate)) +local new_att = assert (lfs.attributes (tmpfile)) +assert (new_att.access == testdate, "could not set access time") +assert (new_att.modification == testdate, "could not set modification time") + +-- Change access and modification time +local testdate1 = os.time({ year = 2007, day = 10, month = 2, hour=0}) +local testdate2 = os.time({ year = 2007, day = 11, month = 2, hour=0}) + +assert (lfs.touch (tmpfile, testdate2, testdate1)) +local new_att = assert (lfs.attributes (tmpfile)) +assert (new_att.access == testdate2, "could not set access time") +assert (new_att.modification == testdate1, "could not set modification time") + +local res, err = lfs.symlinkattributes(tmpfile) +if err ~= "symlinkattributes not supported on this platform" then + -- Checking symbolic link information (does not work in Windows) + assert (os.execute ("ln -s "..tmpfile.." _a_link_for_test_")) + assert (lfs.attributes"_a_link_for_test_".mode == "file") + assert (lfs.symlinkattributes"_a_link_for_test_".mode == "link") + assert (os.remove"_a_link_for_test_") +end + +if lfs.setmode then + -- Checking text/binary modes (works only in Windows) + local f = io.open(tmpfile, "w") + local result, mode = lfs.setmode(f, "binary") + assert((result and mode == "text") or (not result and mode == "setmode not supported on this platform")) + result, mode = lfs.setmode(f, "text") + assert((result and mode == "binary") or (not result and mode == "setmode not supported on this platform")) + f:close() +end + +-- Restore access time to current value +assert (lfs.touch (tmpfile, attrib.access, attrib.modification)) +new_att = assert (lfs.attributes (tmpfile)) +assert (new_att.access == attrib.access) +assert (new_att.modification == attrib.modification) + +-- Remove new file and directory +assert (os.remove (tmpfile), "could not remove new file") +assert (lfs.rmdir (tmpdir), "could not remove new directory") +assert (lfs.mkdir (tmpdir..sep.."lfs_tmp_dir") == nil, "could create a directory inside a non-existent one") + +-- Trying to get attributes of a non-existent file +assert (lfs.attributes ("this couldn't be an actual file") == nil, "could get attributes of a non-existent file") +assert (type(lfs.attributes (upper)) == "table", "couldn't get attributes of upper directory") + +-- Stressing directory iterator +count = 0 +for i = 1, 4000 do + for file in lfs.dir (tmp) do + count = count + 1 + end +end + +-- Stressing directory iterator, explicit version +count = 0 +for i = 1, 4000 do + local iter, dir = lfs.dir(tmp) + local file = dir:next() + while file do + count = count + 1 + file = dir:next() + end + assert(not pcall(dir.next, dir)) +end + +-- directory explicit close +local iter, dir = lfs.dir(tmp) +dir:close() +assert(not pcall(dir.next, dir)) +print"Ok!" diff --git a/lfw/rocks/luasocket/2.0.2-3/luasocket-2.0.2-3.rockspec b/lfw/rocks/luasocket/2.0.2-3/luasocket-2.0.2-3.rockspec new file mode 100644 index 0000000..aa1fa91 --- /dev/null +++ b/lfw/rocks/luasocket/2.0.2-3/luasocket-2.0.2-3.rockspec @@ -0,0 +1,34 @@ +package = "LuaSocket" +version = "2.0.2-3" +source = { + url = "http://luaforge.net/frs/download.php/2664/luasocket-2.0.2.tar.gz", + md5 = "41445b138deb7bcfe97bff957503da8e" +} +description = { + summary = "Network support for the Lua language", + detailed = [[ + LuaSocket is a Lua extension library that is composed by two parts: a C core + that provides support for the TCP and UDP transport layers, and a set of Lua + modules that add support for functionality commonly needed by applications + that deal with the Internet. + ]] +} +build = { + type = "make", + build_variables = { + CFLAGS = "$(CFLAGS) -DLUASOCKET_DEBUG -I$(LUA_INCDIR)", + LDFLAGS = "$(LIBFLAG) -O -fpic", + LD = "$(CC)" + }, + install_variables = { + INSTALL_TOP_SHARE = "$(LUADIR)", + INSTALL_TOP_LIB = "$(LIBDIR)" + }, + platforms = { + macosx = { + build_variables = { + CFLAGS = "$(CFLAGS) -DLUASOCKET_DEBUG -DUNIX_HAS_SUN_LEN -fno-common -I$(LUA_INCDIR)" + } + } + } +} diff --git a/lfw/rocks/luasocket/2.0.2-3/rock_manifest b/lfw/rocks/luasocket/2.0.2-3/rock_manifest new file mode 100644 index 0000000..08776c0 --- /dev/null +++ b/lfw/rocks/luasocket/2.0.2-3/rock_manifest @@ -0,0 +1,23 @@ +rock_manifest = {
+ ['luasocket-2.0.2-3.rockspec']='eb546dbbd806881232d4164ac2121d1a',
+ lua={
+ ['ltn12.lua']='3dfc54ef5c4d74c9558b503ea5595d0d',
+ socket={
+ ['ftp.lua']='a8fbe980f396bccfdaa5671a981e60cd',
+ ['url.lua']='2bb6b15f77e9fcf4fc028435d41ad748',
+ ['http.lua']='811e12534358f97907079ec49db9d753',
+ ['smtp.lua']='1d32e3dc5060e7e2f67b59e8f8376c63',
+ ['tp.lua']='b98814d6a43aa13dd01f78f3b573f4f6'
+ },
+ ['mime.lua']='6042de20ee4cacb2089388f1038bb92b',
+ ['socket.lua']='f8d9f766ce71816a68c90bb12a29eed9'
+ },
+ lib={
+ socket={
+ ['core.dll']='58a9887fb51f92e707c80f9ce4196d5c'
+ },
+ mime={
+ ['core.dll']='05efb07488c0ba45f35230b0066422f4'
+ }
+ }
+}
diff --git a/lfw/rocks/luazip/1.2.3-2/luazip-1.2.3-2.rockspec b/lfw/rocks/luazip/1.2.3-2/luazip-1.2.3-2.rockspec new file mode 100644 index 0000000..92a9987 --- /dev/null +++ b/lfw/rocks/luazip/1.2.3-2/luazip-1.2.3-2.rockspec @@ -0,0 +1,37 @@ +package = "LuaZip" +version = "1.2.3-2" +source = { + url = "http://luaforge.net/frs/download.php/2493/luazip-1.2.3.tar.gz" +} +description = { + summary = "Library for reading files inside zip files", + detailed = [[ + LuaZip is a lightweight Lua extension library used to read files + stored inside zip files. The API is very similar to the standard + Lua I/O library API. + ]], + license = "MIT/X11", + homepage = "http://www.keplerproject.org/luaexpat/" +} +dependencies = { + "lua >= 5.1" +} +external_dependencies = { + ZZIP = { + header = "zzip.h" + } +} +build = { + type = "make", + variables = { + LUA_VERSION_NUM="501", + }, + build_variables = { + LIB_OPTION = "$(LIBFLAG) -L$(ZZIP_LIBDIR)", + CFLAGS = "$(CFLAGS) -I$(LUA_INCDIR) -I$(ZZIP_INCDIR)", + }, + install_variables = { + LUA_LIBDIR = "$(LIBDIR)", + LUA_DIR = "$(LUADIR)" + } +} diff --git a/lfw/rocks/luazip/1.2.3-2/rock_manifest b/lfw/rocks/luazip/1.2.3-2/rock_manifest new file mode 100644 index 0000000..8b71eec --- /dev/null +++ b/lfw/rocks/luazip/1.2.3-2/rock_manifest @@ -0,0 +1,6 @@ +rock_manifest = {
+ lib={
+ ['zip.dll']='2d450eb4b48a5d6fd8a7ce99f3e24e8e'
+ },
+ ['luazip-1.2.3-2.rockspec']='b0086cd00fa9693c0083199e9b57e717'
+}
diff --git a/lfw/rocks/manifest b/lfw/rocks/manifest new file mode 100644 index 0000000..8f462a9 --- /dev/null +++ b/lfw/rocks/manifest @@ -0,0 +1,159 @@ +modules = {
+ ['socket.http']={
+ 'luasocket/2.0.2-3'
+ },
+ socket={
+ 'luasocket/2.0.2-3'
+ },
+ ['des56']={
+ 'md5/1.1.2-1'
+ },
+ ['md5.core']={
+ 'md5/1.1.2-1'
+ },
+ lfs={
+ 'luafilesystem/1.5.0-1'
+ },
+ ['md5']={
+ 'md5/1.1.2-1'
+ },
+ ['socket.ftp']={
+ 'luasocket/2.0.2-3'
+ },
+ ['socket.url']={
+ 'luasocket/2.0.2-3'
+ },
+ ['socket.smtp']={
+ 'luasocket/2.0.2-3'
+ },
+ ['mime.core']={
+ 'luasocket/2.0.2-3'
+ },
+ zip={
+ 'luazip/1.2.3-2'
+ },
+ ['ltn12']={
+ 'luasocket/2.0.2-3'
+ },
+ mime={
+ 'luasocket/2.0.2-3'
+ },
+ ['socket.tp']={
+ 'luasocket/2.0.2-3'
+ },
+ ['socket.core']={
+ 'luasocket/2.0.2-3'
+ }
+}
+commands = {}
+dependencies = {
+ luazip={
+ ['1.2.3-2']={
+ {
+ name='lua',
+ constraints={
+ {
+ op='>=',
+ version={
+ 5, 1, string='5.1'
+ }
+ }
+ }
+ }
+ }
+ },
+ luafilesystem={
+ ['1.5.0-1']={
+ {
+ name='lua',
+ constraints={
+ {
+ op='>=',
+ version={
+ 5, 1, string='5.1'
+ }
+ }
+ }
+ }
+ }
+ },
+ luasocket={
+ ['2.0.2-3']={}
+ },
+ ['md5']={
+ ['1.1.2-1']={
+ {
+ name='lua',
+ constraints={
+ {
+ op='>=',
+ version={
+ 5, 1, string='5.1'
+ }
+ }
+ }
+ }
+ }
+ }
+}
+repository = {
+ luafilesystem={
+ ['1.5.0-1']={
+ {
+ modules={
+ lfs='lfs.dll'
+ },
+ commands={},
+ dependencies={},
+ arch='installed'
+ }
+ }
+ },
+ luazip={
+ ['1.2.3-2']={
+ {
+ modules={
+ zip='zip.dll'
+ },
+ commands={},
+ dependencies={},
+ arch='installed'
+ }
+ }
+ },
+ luasocket={
+ ['2.0.2-3']={
+ {
+ modules={
+ ['socket.http']='socket/http.lua',
+ socket='socket.lua',
+ ['socket.url']='socket/url.lua',
+ ['socket.smtp']='socket/smtp.lua',
+ ['socket.ftp']='socket/ftp.lua',
+ ['socket.core']='socket/core.dll',
+ ['ltn12']='ltn12.lua',
+ mime='mime.lua',
+ ['mime.core']='mime/core.dll',
+ ['socket.tp']='socket/tp.lua'
+ },
+ commands={},
+ dependencies={},
+ arch='installed'
+ }
+ }
+ },
+ ['md5']={
+ ['1.1.2-1']={
+ {
+ modules={
+ ['md5.core']='md5/core.dll',
+ ['des56']='des56.dll',
+ ['md5']='md5.lua'
+ },
+ commands={},
+ dependencies={},
+ arch='installed'
+ }
+ }
+ }
+}
diff --git a/lfw/rocks/md5/1.1.2-1/md5-1.1.2-1.rockspec b/lfw/rocks/md5/1.1.2-1/md5-1.1.2-1.rockspec new file mode 100644 index 0000000..6626701 --- /dev/null +++ b/lfw/rocks/md5/1.1.2-1/md5-1.1.2-1.rockspec @@ -0,0 +1,39 @@ +package = "MD5"
+version = "1.1.2-1"
+source = {
+ url = ""
+}
+description = {
+ summary = "Basic cryptographic library",
+ detailed = [[
+ MD5 offers basic cryptographic facilities for Lua 5.1:
+ a hash (digest) function, a pair crypt/decrypt based on MD5 and CFB,
+ and a pair crypt/decrypt based on DES with 56-bit keys.
+ ]],
+ license = "MIT/X11",
+ homepage = "http://www.keplerproject.org/md5/"
+}
+dependencies = {
+ "lua >= 5.1"
+}
+build = {
+ type = "make",
+ variables = {
+ LUA_VERSION_NUM="501",
+ },
+ build_variables = {
+ LIB_OPTION = "$(LIBFLAG)",
+ CFLAGS = "$(CFLAGS) -I$(LUA_INCDIR)",
+ },
+ install_variables = {
+ LUA_LIBDIR = "$(LIBDIR)",
+ LUA_DIR = "$(LUADIR)"
+ },
+ platforms = {
+ win32 = {
+ build_variables = {
+ LUA_LIB = "$(LUA_LIBDIR)\\lua5.1.lib"
+ }
+ }
+ }
+}
diff --git a/lfw/rocks/md5/1.1.2-1/rock_manifest b/lfw/rocks/md5/1.1.2-1/rock_manifest new file mode 100644 index 0000000..120edb5 --- /dev/null +++ b/lfw/rocks/md5/1.1.2-1/rock_manifest @@ -0,0 +1,12 @@ +rock_manifest = {
+ ['md5-1.1.2-1.rockspec']='a3953425b9c63515ae647a874fc07f96',
+ lua={
+ ['md5.lua']='d4cd3727e9968f1cddbbe02d85288a9c'
+ },
+ lib={
+ ['des56.dll']='13ffbb58e4b1685d5d27b41840b88d43',
+ ['md5']={
+ ['core.dll']='b925346324ca57f32d8c42541f881119'
+ }
+ }
+}
diff --git a/makedist b/makedist new file mode 100755 index 0000000..9b3308e --- /dev/null +++ b/makedist @@ -0,0 +1,76 @@ +#!/bin/sh + +if ! [ "$1" ] +then + echo "usage: $0 <version>" + exit 1 +fi + +if ! [ -d ".git" ] +then + echo "Should be run inside a git repo dir." + exit 1 +fi + +make clean || exit 1 + +grep -q "\"$1\"" rockspec || { + echo + echo "Version in rockspec is incorrect. Please fix it." + exit 1 +} + +grep -q "program_version = \"$1\"" src/luarocks/cfg.lua || { + echo + echo "Version in src/luarocks/cfg.lua is incorrect. Please fix it." + exit 1 +} + +out="luarocks-$1" +rm -rf "$out" +mkdir "$out" +rm -f "missing_ref" +git ls-files | while read i +do + if [ -f "$i" ] + then + dir=`dirname $i` + mkdir -p "$out/$dir" + cp "$i" "$out/$dir" + if echo "$i" | grep -v "/bin/" | grep -q "^src/" + then + grep -qw `basename "$i"` Makefile || { + echo "Missing ref in makefile: $i" + touch "missing_ref" + exit 1 + } + fi + fi +done +if [ -e "missing_ref" ] +then + rm -f "missing_ref" + exit 1 +fi + +rm -rf "release-unix" "release-windows" "$out.tar.gz" "$out-win32.zip" + +mkdir "release-unix" +cp -a "$out" "release-unix" +mkdir "release-windows" +mv "$out" "release-windows/$out-win32" + +cd "release-unix/$out" +rm -rf makedist install.bat COPYING.lua COPYING.7z win32 lfw +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 +cd .. +zip -r ../"$out-win32.zip" "$out-win32" +cd .. +rm -rf "release-windows" diff --git a/rockspec b/rockspec new file mode 100644 index 0000000..69f253b --- /dev/null +++ b/rockspec @@ -0,0 +1,38 @@ +package = "LuaRocks" +local VER = "2.1.0" +local REV = "1" +version = VER.."-"..REV + +description = { + summary = "A deployment and management system for Lua modules.", + detailed = [[ + LuaRocks allows you to install Lua modules as self-contained + packages called "rocks", which also contain version dependency + 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 that when a module is + required, the correct version is loaded. LuaRocks supports both + local and remote repositories, and multiple local rocks trees. + ]], + license = "MIT/X11", + homepage = "http://www.luarocks.org", + maintainer = "Hisham Muhammad" +} + +dependencies = { + "lua >= 5.1" +} + +source = { + url = "http://luarocks.org/releases/luarocks-"..VER..".tar.gz", +} + +build = { + type = "make", + install_target = "install_rock", + build_pass=false, + install_variables = { + BINDIR="$(BINDIR)", + LUADIR="$(LUADIR)" + } +} diff --git a/src/bin/luarocks b/src/bin/luarocks new file mode 100755 index 0000000..b64051d --- /dev/null +++ b/src/bin/luarocks @@ -0,0 +1,24 @@ +#!/usr/bin/env lua + +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") + +command_line.run_command(...) diff --git a/src/bin/luarocks-admin b/src/bin/luarocks-admin new file mode 100755 index 0000000..4c5613c --- /dev/null +++ b/src/bin/luarocks-admin @@ -0,0 +1,16 @@ +#!/usr/bin/env lua + +local command_line = require("luarocks.command_line") + +program_description = "LuaRocks repository administration interface" + +commands = { +} + +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/src/luarocks/add.lua b/src/luarocks/add.lua new file mode 100644 index 0000000..d7c293e --- /dev/null +++ b/src/luarocks/add.lua @@ -0,0 +1,113 @@ + +--- Module implementing the luarocks-admin "add" command. +-- Adds a rock or rockspec to a rocks server. +module("luarocks.add", package.seeall) + +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 = [[ +Arguments are local files, which may be rockspecs or rocks. +The flag --server indicates which server to use. +If not given, the default server set in the upload_server variable +from the configuration file is used instead. +The flag --no-refresh indicates the local cache should not be refreshed +prior to generation of the updated manifest. +]] + +local function add_files_to_server(refresh, rockfiles, server, upload_server) + assert(type(refresh) == "boolean" or not refresh) + assert(type(rockfiles) == "table") + assert(type(server) == "string") + assert(type(upload_server) == "table" or not upload_server) + + local download_url, login_url = cache.get_server_urls(server, upload_server) + local at = fs.current_dir() + local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url + + local local_cache, protocol, server_path, user, password = refresh_fn(server, download_url, cfg.upload_user, cfg.upload_password) + if not local_cache then + return nil, protocol + end + if protocol == "file" then + return nil, "Server "..server.." is not recognized, check your configuration." + end + + if not login_url then + login_url = protocol.."://"..server_path + end + + fs.change_dir(at) + + local files = {} + for i, rockfile in ipairs(rockfiles) do + if fs.exists(rockfile) then + util.printout("Copying file "..rockfile.." to "..local_cache.."...") + local absolute = fs.absolute_name(rockfile) + fs.copy(absolute, local_cache) + table.insert(files, dir.base_name(absolute)) + else + util.printerr("File "..rockfile.." not found") + end + end + if #files == 0 then + return nil, "No files found" + end + + fs.change_dir(local_cache) + + util.printout("Updating manifest...") + manif.make_manifest(local_cache, "one", true) + util.printout("Updating index.html...") + index.make_index(local_cache) + + local login_info = "" + if user then login_info = " -u "..user end + if password then login_info = login_info..":"..password end + if not login_url:match("/$") then + login_url = login_url .. "/" + end + + table.insert(files, "index.html") + table.insert(files, "manifest") + table.insert(files, "manifest-5.1") + table.insert(files, "manifest-5.2") + + -- 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.."/" + elseif upload_server and upload_server.sftp then + local part1, part2 = upload_server.sftp:match("^([^/]*)/(.*)$") + cmd = cfg.variables.SCP.." "..table.concat(files, " ").." "..user.."@"..part1..":/"..part2 + else + cmd = cfg.variables.CURL.." "..login_info.." -T '{"..table.concat(files, ",").."}' "..login_url + end + + util.printout(cmd) + fs.execute(cmd) + + return true +end + +function run(...) + local files = { util.parse_flags(...) } + local flags = table.remove(files, 1) + if #files < 1 then + return nil, "Argument missing. "..util.see_help("add", "luarocks-admin") + end + local server, server_table = cache.get_upload_server(flags["server"]) + if not server then return nil, server_table end + return add_files_to_server(not flags["no-refresh"], files, server, server_table) +end + diff --git a/src/luarocks/admin_remove.lua b/src/luarocks/admin_remove.lua new file mode 100644 index 0000000..83b57fc --- /dev/null +++ b/src/luarocks/admin_remove.lua @@ -0,0 +1,87 @@ + +--- Module implementing the luarocks-admin "remove" command. +-- Removes a rock or rockspec from a rocks server. +module("luarocks.admin_remove", package.seeall) + +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 = [[ +Arguments are local files, which may be rockspecs or rocks. +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. +The flag --no-refresh indicates the local cache should not be refreshed +prior to generation of the updated manifest. +]] + +local function remove_files_from_server(refresh, rockfiles, server, upload_server) + assert(type(refresh) == "boolean" or not refresh) + assert(type(rockfiles) == "table") + assert(type(server) == "string") + assert(type(upload_server) == "table" or not upload_server) + + local download_url, login_url = cache.get_server_urls(server, upload_server) + local at = fs.current_dir() + local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url + + local local_cache, protocol, server_path, user, password = refresh_fn(server, download_url, cfg.upload_user, cfg.upload_password) + if not local_cache then + return nil, protocol + end + if protocol ~= "rsync" then + return nil, "This command requires 'rsync', check your configuration." + end + + fs.change_dir(at) + + 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 + nr_files = nr_files + 1 + else + util.printerr("Failed removing "..file) + end + end + if nr_files == 0 then + return nil, "No files removed." + end + + fs.change_dir(local_cache) + + util.printout("Updating manifest...") + manif.make_manifest(local_cache, "one", true) + util.printout("Updating index.html...") + index.make_index(local_cache) + + local srv, path = server_path:match("([^/]+)(/.+)") + local cmd = "rsync -Oavz --delete -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/" + + util.printout(cmd) + fs.execute(cmd) + + return true +end + +function run(...) + local files = { util.parse_flags(...) } + local flags = table.remove(files, 1) + if #files < 1 then + return nil, "Argument missing. "..util.see_help("remove", "luarocks-admin") + end + local server, server_table = cache.get_upload_server(flags["server"]) + if not server then return nil, server_table end + return remove_files_from_server(not flags["no-refresh"], files, server, server_table) +end + diff --git a/src/luarocks/build.lua b/src/luarocks/build.lua new file mode 100644 index 0000000..dbe450a --- /dev/null +++ b/src/luarocks/build.lua @@ -0,0 +1,355 @@ + +--- Module implementing the LuaRocks "build" command. +-- Builds a rock, compiling its C parts if any. +module("luarocks.build", package.seeall) + +local pack = require("luarocks.pack") +local path = require("luarocks.path") +local util = require("luarocks.util") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local deps = require("luarocks.deps") +local 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 and install a rock, compiling its C parts if any. +Argument may be a rockspec file, a source rock file +or the name of a rock to be fetched from a repository. + +--pack-binary-rock Do not install rock. Instead, produce a .rock file + with the contents of compilation in the current + directory. + +--keep Do not remove previously installed versions of the + rock after building a new one. This behavior can + be made permanent by setting keep_other_versions=true + in the configuration file. +]] + +--- Install files to a given location. +-- Takes a table where the array part is a list of filenames to be copied. +-- In the hash part, other keys, if is_module_path is set, are identifiers +-- in Lua module format, to indicate which subdirectory the file should be +-- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo") +-- will copy src/bar.lua to boo/foo. +-- @param files table or nil: A table containing a list of files to copy in +-- the format described above. If nil is passed, this function is a no-op. +-- Directories should be delimited by forward slashes as in internet URLs. +-- @param location string: The base directory files should be copied to. +-- @param is_module_path boolean: True if string keys in files should be +-- interpreted as dotted module paths. +-- @return boolean or (nil, string): True if succeeded or +-- nil and an error message. +local function install_files(files, location, is_module_path) + assert(type(files) == "table" or not files) + assert(type(location) == "string") + if files then + for k, file in pairs(files) do + local dest = location + if type(k) == "string" then + if is_module_path then + dest = dir.path(location, path.module_to_path(k)) + fs.make_dir(dest) + else + dest = dir.path(location, dir.dir_name(k)) + fs.make_dir(dest) + dest = dir.path(dest, dir.base_name(k)) + end + else + fs.make_dir(dest) + end + local ok = fs.copy(dir.path(file), dest) + if not ok then + return nil, "Failed copying "..file + end + end + end + return true +end + +--- Write to the current directory the contents of a table, +-- where each key is a file name and its value is the file content. +-- @param files table: The table of files to be written. +local function extract_from_rockspec(files) + for name, content in pairs(files) do + local fd = io.open(dir.path(fs.current_dir(), name), "w+") + fd:write(content) + fd:close() + end +end + +--- Applies patches inlined in the build.patches section +-- and extracts files inlined in the build.extra_files section +-- of a rockspec. +-- @param rockspec table: A rockspec table. +-- @return boolean or (nil, string): True if succeeded or +-- nil and an error message. +function apply_patches(rockspec) + assert(type(rockspec) == "table") + + local build = rockspec.build + if build.extra_files then + extract_from_rockspec(build.extra_files) + end + if build.patches then + extract_from_rockspec(build.patches) + for patch, patchdata in util.sortedpairs(build.patches) do + util.printout("Applying patch "..patch.."...") + local ok, err = fs.apply_patch(tostring(patch), patchdata) + if not ok then + return nil, "Failed applying patch "..patch + end + end + end + return true +end + +--- Build and install a rock given a rockspec. +-- @param rockspec_file string: local or remote filename of a rockspec. +-- @param need_to_fetch boolean: true if sources need to be fetched, +-- false if the rockspec was obtained from inside a source rock. +-- @param minimal_mode boolean: true if there's no need to fetch, +-- unpack or change dir (this is used by "luarocks make"). Implies +-- need_to_fetch = false. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for no trees. +-- @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) + assert(type(rockspec_file) == "string") + assert(type(need_to_fetch) == "boolean") + + local rockspec, err, errcode = fetch.load_rockspec(rockspec_file) + if err then + return nil, err, errcode + elseif not rockspec.build then + return nil, "Rockspec error: build table not specified" + elseif not rockspec.build.type then + return nil, "Rockspec error: build type not specified" + end + + if deps_mode == "none" then + util.printerr("Warning: skipping dependency checks.") + else + local ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) + if err then + return nil, err, errcode + end + end + + ok, err, errcode = deps.check_external_deps(rockspec, "build") + if err then + return nil, err, errcode + end + + local name, version = rockspec.name, rockspec.version + if repos.is_installed(name, version) then + repos.delete_version(name, version) + end + + if not minimal_mode then + local _, source_dir + if need_to_fetch then + ok, source_dir, errcode = fetch.fetch_sources(rockspec, true) + if not ok then + return nil, source_dir, errcode + end + fs.change_dir(source_dir) + elseif rockspec.source.file then + local ok, err = fs.unpack_archive(rockspec.source.file) + if not ok then + return nil, err + end + end + fs.change_dir(rockspec.source.dir) + end + + local dirs = { + lua = { name = path.lua_dir(name, version), is_module_path = true }, + lib = { name = path.lib_dir(name, version), is_module_path = true }, + conf = { name = path.conf_dir(name, version), is_module_path = false }, + bin = { name = path.bin_dir(name, version), is_module_path = false }, + } + + for _, d in pairs(dirs) do + fs.make_dir(d.name) + 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 + + if not minimal_mode then + ok, err = apply_patches(rockspec) + if err then + return nil, err + end + end + + if build.type ~= "none" then + + -- Temporary compatibility + if build.type == "module" then + util.printout("Do not use 'module' as a build type. Use 'builtin' instead.") + build.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." + end + + local build_type + ok, build_type = pcall(require, "luarocks.build." .. build.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 + end + + ok, err = build_type.run(rockspec) + if not ok then + return nil, "Build error: " .. err + end + end + + if build.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) + if not ok then + return nil, err + end + end + end + + local copy_directories = build.copy_directories or {"doc"} + + 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) + else + util.warning("Directory '"..copy_dir.."' not found") + end + end + + for _, d in pairs(dirs) do + fs.remove_dir_if_empty(d.name) + end + + fs.pop_dir() + + fs.copy(rockspec.local_filename, path.rockspec_file(name, version)) + if need_to_fetch then + fs.pop_dir() + end + + ok, err = manif.make_rock_manifest(name, version) + if err then return nil, err end + + ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec)) + if err then return nil, err end + + util.remove_scheduled_function(rollback) + rollback = util.schedule_function(function() + repos.delete_version(name, version) + end) + + ok, err = repos.run_hook(rockspec, "post_install") + if err then return nil, err end + + ok, err = manif.update_manifest(name, version, nil, deps_mode) + if err then return nil, err end + + local license = "" + if rockspec.description and rockspec.description.license then + license = ("(license: "..rockspec.description.license..")") + 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.remove_scheduled_function(rollback) + return name, version +end + +--- Build and install a rock. +-- @param rock_file string: local or remote filename of a rock. +-- @param need_to_fetch boolean: true if sources need to be fetched, +-- false if the rockspec was obtained from inside a source rock. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @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) + assert(type(rock_file) == "string") + assert(type(need_to_fetch) == "boolean") + + local unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file) + if not unpack_dir then + return nil, err, errcode + end + local rockspec_file = path.rockspec_name_from_rock(rock_file) + fs.change_dir(unpack_dir) + local ok, err, errcode = 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) + elseif name:match("%.src%.rock$") then + return 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) + 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)) + end + return nil, "Don't know what to do with "..name +end + +--- Driver function for "build" command. +-- @param name string: A local or remote rockspec or rock file. +-- If a package name is given, forwards the request to "search" and, +-- if returned a result, installs the matching rock. +-- @param version string: When passing a package name, a version number may +-- also be given. +-- @return boolean or (nil, string): True if build was successful; nil and an +-- error message otherwise. +function run(...) + local flags, name, version = util.parse_flags(...) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("build") + end + assert(type(version) == "string" or not version) + + if flags["pack-binary-rock"] then + return pack.pack_binary_rock(name, version, do_build, name, version, deps.get_deps_mode(flags)) + else + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err 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 + if (not flags["keep"]) and not cfg.keep_other_versions then + local ok, err = remove.remove_other_versions(name, version, flags["force"]) + if not ok then util.printerr(err) end + end + return name, version + end +end diff --git a/src/luarocks/build/builtin.lua b/src/luarocks/build/builtin.lua new file mode 100644 index 0000000..9d97d58 --- /dev/null +++ b/src/luarocks/build/builtin.lua @@ -0,0 +1,251 @@ + +--- A builtin build system: back-end to provide a portable way of building C-based Lua modules. +module("luarocks.build.builtin", package.seeall) + +local fs = require("luarocks.fs") +local path = require("luarocks.path") +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") +local dir = require("luarocks.dir") + +--- Run a command displaying its execution on standard output. +-- @return boolean: true if command succeeds (status code 0), false +-- otherwise. +local function execute(...) + io.stdout:write(table.concat({...}, " ").."\n") + return fs.execute(...) +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) + local rcfile = io.open(rcfilename, "w") + if not rcfile then + error("Could not open "..rcfilename.." for writing.") + end + rcfile:write("STRINGTABLE\r\nBEGIN\r\n") + + local i = 1 + for line in io.lines(luafilename) do + if not line:match("^#!") then + rcfile:write(i .. " \"") + line = line:gsub("\\", "\\\\"):gsub('"', '""'):gsub("[\r\n]+", "") + rcfile:write(line .. "\\r\\n\"\r\n") + i = i + 1 + end + end + + rcfile:write("END\r\n") + + rcfile:close() +end + +--- Driver function for the builtin build back-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) + assert(type(rockspec) == "table") + local compile_object, compile_library, compile_wrapper_binary + + local build = rockspec.build + local variables = rockspec.variables + + local function add_flags(extras, flag, flags) + if flags then + if type(flags) ~= "table" then + flags = { tostring(flags) } + end + util.variable_substitutions(flags, variables) + for _, v in ipairs(flags) do + table.insert(extras, flag:format(v)) + end + end + end + + if cfg.is_platform("mingw32") then + compile_object = function(object, source, defines, incdirs) + local extras = {} + add_flags(extras, "-D%s", defines) + add_flags(extras, "-I%s", incdirs) + return execute(variables.CC.." "..variables.CFLAGS, "-c", "-o", object, "-I"..variables.LUA_INCDIR, source, unpack(extras)) + end + compile_library = function(library, objects, libraries, libdirs, name) + local extras = { unpack(objects) } + add_flags(extras, "-L%s", libdirs) + add_flags(extras, "-l%s", libraries) + extras[#extras+1] = dir.path(variables.LUA_LIBDIR, variables.LUALIB) + extras[#extras+1] = "-l" .. (variables.MSVCRT or "m") + local ok = execute(variables.LD.." "..variables.LIBFLAG, "-o", library, unpack(extras)) + return ok + end + compile_wrapper_binary = function(fullname, name) + local fullbasename = fullname:gsub("%.lua$", ""):gsub("/", "\\") + local basename = name:gsub("%.lua$", ""):gsub("/", "\\") + local rcname = basename..".rc" + local resname = basename..".o" + local wrapname = basename..".exe" + make_rc(fullname, fullbasename..".rc") + local ok = execute(variables.RC, "-o", resname, rcname) + if not ok then return ok end + ok = execute(variables.CC.." "..variables.CFLAGS, "-I"..variables.LUA_INCDIR, + "-o", wrapname, resname, variables.WRAPPER, + dir.path(variables.LUA_LIBDIR, variables.LUALIB), + "-l" .. (variables.MSVCRT or "m"), "-luser32") + return ok, wrapname + end + elseif cfg.is_platform("win32") then + compile_object = function(object, source, defines, incdirs) + local extras = {} + add_flags(extras, "-D%s", defines) + add_flags(extras, "-I%s", incdirs) + return execute(variables.CC.." "..variables.CFLAGS, "-c", "-Fo"..object, "-I"..variables.LUA_INCDIR, source, unpack(extras)) + end + compile_library = function(library, objects, libraries, libdirs, name) + local extras = { unpack(objects) } + add_flags(extras, "-libpath:%s", libdirs) + add_flags(extras, "%s.lib", libraries) + local basename = dir.base_name(library):gsub(".[^.]*$", "") + local deffile = basename .. ".def" + local def = io.open(dir.path(fs.current_dir(), deffile), "w+") + def:write("EXPORTS\n") + 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" + if ok and fs.exists(manifestfile) then + ok = execute(variables.MT, "-manifest", manifestfile, "-outputresource:"..basename..".dll;2") + end + return ok + end + compile_wrapper_binary = function(fullname, name) + local fullbasename = fullname:gsub("%.lua$", ""):gsub("/", "\\") + local basename = name:gsub("%.lua$", ""):gsub("/", "\\") + local object = basename..".obj" + local rcname = basename..".rc" + local resname = basename..".res" + local wrapname = basename..".exe" + make_rc(fullname, fullbasename..".rc") + local ok = execute(variables.RC, "-r", "-fo"..resname, rcname) + if not ok then return ok end + ok = execute(variables.CC.." "..variables.CFLAGS, "-c", "-Fo"..object, + "-I"..variables.LUA_INCDIR, variables.WRAPPER) + if not ok then return ok end + ok = execute(variables.LD, "-out:"..wrapname, resname, object, + dir.path(variables.LUA_LIBDIR, variables.LUALIB), "user32.lib") + local manifestfile = wrapname..".manifest" + if ok and fs.exists(manifestfile) then + ok = execute(variables.MT, "-manifest", manifestfile, "-outputresource:"..wrapname..";1") + end + return ok, wrapname + end + else + compile_object = function(object, source, defines, incdirs) + local extras = {} + add_flags(extras, "-D%s", defines) + add_flags(extras, "-I%s", incdirs) + return execute(variables.CC.." "..variables.CFLAGS, "-I"..variables.LUA_INCDIR, "-c", source, "-o", object, unpack(extras)) + end + compile_library = function (library, objects, libraries, libdirs) + local extras = { unpack(objects) } + add_flags(extras, "-L%s", libdirs) + if cfg.gcc_rpath then + add_flags(extras, "-Wl,-rpath,%s:", libdirs) + end + add_flags(extras, "-l%s", libraries) + if cfg.is_platform("cygwin") then + add_flags(extras, "-l%s", {"lua"}) + end + 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 + 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) + -- 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 + if build.install and build.install.bin then + for i, name in ipairs(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 + ok, name = compile_wrapper_binary(fullname, name) + if ok then + build.install.bin[i] = 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 + if info:match("init%.lua$") and not name:match("%.init$") then + moddir = path.module_to_path(name..".init") + end + local dest = dir.path(luadir, moddir) + built_modules[info] = dest + else + info = {info} + end + end + if type(info) == "table" then + local objects = {} + local sources = info.sources + if info[1] then sources = info end + if type(sources) == "string" then sources = {sources} end + for _, source in ipairs(sources) do + local object = source:gsub(".[^.]*$", "."..cfg.obj_extension) + if not object then + object = source.."."..cfg.obj_extension + end + ok = compile_object(object, source, info.defines, info.incdirs) + if not ok then + return nil, "Failed compiling object "..object + end + 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("//", "/") + if moddir ~= "" then + fs.make_dir(moddir) + end + local dest = dir.path(libdir, moddir) + built_modules[module_name] = dest + ok = compile_library(module_name, objects, info.libraries, info.libdirs, name) + if not ok then + return nil, "Failed compiling module "..module_name + end + end + end + for name, dest in pairs(built_modules) do + fs.make_dir(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 + return nil, "Failed copying contents of 'lua' directory: "..err + end + end + return true +end diff --git a/src/luarocks/build/cmake.lua b/src/luarocks/build/cmake.lua new file mode 100644 index 0000000..82f4ff5 --- /dev/null +++ b/src/luarocks/build/cmake.lua @@ -0,0 +1,58 @@ + +--- Build back-end for CMake-based modules. +module("luarocks.build.cmake", package.seeall) + +local fs = require("luarocks.fs") +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") + +--- Driver function for the "cmake" build back-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) + assert(type(rockspec) == "table") + local build = rockspec.build + local variables = build.variables or {} + + -- Pass Env variables + variables.CMAKE_MODULE_PATH=os.getenv("CMAKE_MODULE_PATH") + variables.CMAKE_LIBRARY_PATH=os.getenv("CMAKE_LIBRARY_PATH") + variables.CMAKE_INCLUDE_PATH=os.getenv("CMAKE_INCLUDE_PATH") + + util.variable_substitutions(variables, rockspec.variables) + + if not fs.execute_string(fs.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() + end + + + -- Execute cmake with variables. + local args = "" + if cfg.cmake_generator then + args = args .. ' -G"'..cfg.cmake_generator.. '"' + end + for k,v in pairs(variables) do + args = args .. ' -D' ..k.. '="' ..v.. '"' + end + + if not fs.execute_string(rockspec.variables.CMAKE.." . " ..args) then + return nil, "Failed cmake." + end + + if not fs.execute_string(rockspec.variables.MAKE.." -fMakefile") then + return nil, "Failed building." + end + + if not fs.execute_string(rockspec.variables.MAKE.." -fMakefile install") then + return nil, "Failed installing." + end + return true +end diff --git a/src/luarocks/build/command.lua b/src/luarocks/build/command.lua new file mode 100644 index 0000000..aeec0da --- /dev/null +++ b/src/luarocks/build/command.lua @@ -0,0 +1,32 @@ + +--- Build back-end for raw listing of commands in rockspec files. +module("luarocks.build.command", package.seeall) + +local fs = require("luarocks.fs") +local util = require("luarocks.util") + +--- Driver function for the "command" build back-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) + assert(type(rockspec) == "table") + + local build = rockspec.build + + util.variable_substitutions(build, rockspec.variables) + + if build.build_command then + util.printout(build.build_command) + if not fs.execute(build.build_command) then + return nil, "Failed building." + end + end + if build.install_command then + util.printout(build.install_command) + if not fs.execute(build.install_command) then + return nil, "Failed installing." + end + end + return true +end diff --git a/src/luarocks/build/make.lua b/src/luarocks/build/make.lua new file mode 100644 index 0000000..c4b2157 --- /dev/null +++ b/src/luarocks/build/make.lua @@ -0,0 +1,92 @@ + +--- Build back-end for using Makefile-based packages. +module("luarocks.build.make", package.seeall) + +local fs = require("luarocks.fs") +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") + +--- Call "make" with given target and variables +-- @param make_cmd string: the make command to be used (typically +-- configured through variables.MAKE in the config files, or +-- the appropriate platform-specific default). +-- @param pass boolean: If true, run make; if false, do nothing. +-- @param target string: The make target; an empty string indicates +-- the default target. +-- @param variables table: A table containing string-string key-value +-- pairs representing variable assignments to be passed to make. +-- @return boolean: false if any errors occurred, true otherwise. +local function make_pass(make_cmd, pass, target, variables) + assert(type(pass) == "boolean") + assert(type(target) == "string") + assert(type(variables) == "table") + + local assignments = {} + for k,v in pairs(variables) do + table.insert(assignments, k.."="..v) + end + if pass then + return fs.execute(make_cmd.." "..target, unpack(assignments)) + else + return true + end +end + +--- Driver function for the "make" build back-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) + assert(type(rockspec) == "table") + + local build = rockspec.build + + if build.build_pass == nil then build.build_pass = true end + if build.install_pass == nil then build.install_pass = true end + build.build_variables = build.build_variables or {} + build.install_variables = build.install_variables or {} + build.build_target = build.build_target or "" + build.install_target = build.install_target or "install" + local makefile = build.makefile or cfg.makefile + if makefile then + -- Assumes all make's accept -f. True for POSIX make, GNU make and Microsoft nmake. + build.build_target = "-f "..makefile.." "..build.build_target + build.install_target = "-f "..makefile.." "..build.install_target + end + + if build.variables then + for var, val in pairs(build.variables) do + build.build_variables[var] = val + build.install_variables[var] = val + end + end + + util.warn_if_not_used(build.build_variables, { CFLAGS=true }, "variable %s was not passed in build_variables") + + util.variable_substitutions(build.build_variables, rockspec.variables) + util.variable_substitutions(build.install_variables, rockspec.variables) + + local auto_variables = { "CC" } + + for _, variable in pairs(auto_variables) do + if not build.build_variables[variable] then + build.build_variables[variable] = rockspec.variables[variable] + end + if not build.install_variables[variable] then + build.install_variables[variable] = rockspec.variables[variable] + end + end + + -- backwards compatibility + local make_cmd = cfg.make or rockspec.variables.MAKE + + local ok = make_pass(make_cmd, build.build_pass, build.build_target, build.build_variables) + if not ok then + return nil, "Failed building." + end + ok = make_pass(make_cmd, build.install_pass, build.install_target, build.install_variables) + if not ok then + return nil, "Failed installing." + end + return true +end diff --git a/src/luarocks/cache.lua b/src/luarocks/cache.lua new file mode 100644 index 0000000..21185c1 --- /dev/null +++ b/src/luarocks/cache.lua @@ -0,0 +1,85 @@ + +--- Module handling the LuaRocks local cache. +-- Adds a rock or rockspec to a rocks server. +module("luarocks.cache", package.seeall) + +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) + if not server then server = cfg.upload_server end + if not server then + return nil, "No server specified and no default configured with upload_server." + end + return server, cfg.upload_servers and cfg.upload_servers[server] +end + +function get_server_urls(server, upload_server) + local download_url = server + local login_url = nil + if upload_server then + if upload_server.rsync then download_url = "rsync://"..upload_server.rsync + elseif upload_server.http then download_url = "http://"..upload_server.http + elseif upload_server.ftp then download_url = "ftp://"..upload_server.ftp + end + + if upload_server.ftp then login_url = "ftp://"..upload_server.ftp + elseif upload_server.sftp then login_url = "sftp://"..upload_server.sftp + end + end + return download_url, login_url +end + +function split_server_url(server, url, user, password) + local protocol, server_path = dir.split_url(url) + if server_path:match("@") then + local credentials + credentials, server_path = server_path:match("([^@]*)@(.*)") + if credentials:match(":") then + user, password = credentials:match("([^:]*):(.*)") + else + user = credentials + end + end + local local_cache + if cfg.local_cache then + local_cache = cfg.local_cache .. "/" .. server + end + 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) + + fs.make_dir(cfg.local_cache) + + local tmp_cache = false + if not local_cache then + local_cache = fs.make_temp_dir("local_cache") + tmp_cache = true + end + local ok = fs.make_dir(local_cache) + if not ok then + return nil, "Failed creating local cache dir." + end + fs.change_dir(local_cache) + 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.."/") + else + local login_info = "" + if user then login_info = " --user="..user end + if password then login_info = login_info .. " --password="..password end + ok = fs.execute(cfg.variables.WGET.." --no-cache -q -m -np -nd "..protocol.."://"..server_path..login_info) + end + if not ok then + return nil, "Failed downloading cache." + end + return local_cache, protocol, server_path, user, password +end diff --git a/src/luarocks/cfg.lua b/src/luarocks/cfg.lua new file mode 100644 index 0000000..5d918b1 --- /dev/null +++ b/src/luarocks/cfg.lua @@ -0,0 +1,484 @@ +--- Configuration for LuaRocks. +-- Tries to load the user's configuration file and +-- defines defaults for unset values. See the +-- <a href="http://luarocks.org/en/Config_file_format">config +-- file format documentation</a> for details. +-- +-- End-users shouldn't edit this file. They can override any defaults +-- set in this file using their system-wide $LUAROCKS_SYSCONFIG file +-- (see luarocks.site_config) or their user-specific configuration file +-- (~/.luarocks/config.lua on Unix or %APPDATA%/luarocks/config.lua on +-- Windows). + +local rawset, next, table, pairs, require, io, os, setmetatable, pcall, ipairs, package, tonumber, type, assert, _VERSION = + rawset, next, table, pairs, require, io, os, setmetatable, pcall, ipairs, package, tonumber, type, assert, _VERSION + +module("luarocks.cfg") + +lua_version = _VERSION:sub(5) +local version_suffix = lua_version:gsub("%.", "_") + +-- Load site-local global configurations +local ok, site_config = pcall(require, "luarocks.site_config_"..version_suffix) +if not ok then + ok, site_config = pcall(require, "luarocks.site_config") +end +if not ok then + io.stderr:write("Site-local luarocks/site_config.lua file not found. Incomplete installation?\n") + site_config = {} +end + +_M.site_config = site_config + +program_version = "2.1.0" + +local persist = require("luarocks.persist") + +local popen_ok, popen_result = pcall(io.popen, "") +if popen_ok then + if popen_result then + popen_result:close() + end +else + io.stderr:write("Your version of Lua does not support io.popen,\n") + io.stderr:write("which is required by LuaRocks. Please check your Lua installation.\n") + os.exit(1) +end + +-- System detection: + +local detected = {} +local system,proc + +-- A proper installation of LuaRocks will hardcode the system +-- and proc values with site_config.LUAROCKS_UNAME_S and site_config.LUAROCKS_UNAME_M, +-- so that this detection does not run every time. When it is +-- performed, we use the Unix way to identify the system, +-- even on Windows (assuming UnxUtils or Cygwin). +system = site_config.LUAROCKS_UNAME_S or io.popen("uname -s"):read("*l") +proc = site_config.LUAROCKS_UNAME_M or io.popen("uname -m"):read("*l") +if proc:match("i[%d]86") then + proc = "x86" +elseif proc:match("amd64") or proc:match("x86_64") then + proc = "x86_64" +elseif proc:match("Power Macintosh") then + proc = "powerpc" +end + +if system == "FreeBSD" then + detected.unix = true + detected.freebsd = true + detected.bsd = true +elseif system == "OpenBSD" then + detected.unix = true + detected.openbsd = true + detected.bsd = true +elseif system == "NetBSD" then + detected.unix = true + detected.netbsd = true + detected.bsd = true +elseif system == "Darwin" then + detected.unix = true + detected.macosx = true + detected.bsd = true +elseif system == "Linux" then + detected.unix = true + detected.linux = true +elseif system == "SunOS" then + detected.unix = true + detected.solaris = true +elseif system and system:match("^CYGWIN") then + detected.unix = true + detected.cygwin = true +elseif system and system:match("^Windows") then + detected.windows = true +elseif system and system:match("^MINGW") then + detected.windows = true + detected.mingw32 = true +else + detected.unix = true + -- Fall back to Unix in unknown systems. +end + +-- Path configuration: + +local sys_config_file, home_config_file +local sys_config_dir, home_config_dir +local sys_config_ok, home_config_ok = false, false +sys_config_dir = site_config.LUAROCKS_SYSCONFDIR +if detected.windows then + home = os.getenv("APPDATA") or "c:" + sys_config_dir = sys_config_dir or "c:/luarocks" + home_config_dir = home.."/luarocks" + home_tree = home.."/luarocks/" +else + home = os.getenv("HOME") or "" + sys_config_dir = sys_config_dir or "/etc/luarocks" + home_config_dir = home.."/.luarocks" + home_tree = home.."/.luarocks/" +end + +variables = {} +rocks_trees = {} + +sys_config_file = site_config.LUAROCKS_SYSCONFIG or sys_config_dir.."/config-"..lua_version..".lua" +local err +sys_config_ok, err = persist.load_into_table(sys_config_file, _M) + +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) +end +if err and ok == nil then + io.stderr:write(err.."\n") +end + +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 }) + 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 }) + 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 }) + 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 + end + if home_overrides.rocks_servers then + _M.rocks_servers = nil + end + util.deep_merge(_M, home_overrides) + else -- nil or false + home_config_ok = home_overrides + if err and home_config_ok == nil then + io.stderr:write(err.."\n") + end + end +end + +if not next(rocks_trees) then + if home_tree then + table.insert(rocks_trees, home_tree) + end + if site_config.LUAROCKS_ROCKS_TREE then + table.insert(rocks_trees, site_config.LUAROCKS_ROCKS_TREE) + end +end + +-- Configure defaults: + +local root = rocks_trees[#rocks_trees] +local defaults = { + + local_by_default = false, + use_extensions = false, + accept_unknown_fields = false, + fs_use_modules = true, + hooks_enabled = true, + deps_mode = "one", + + lua_modules_path = "/share/lua/"..lua_version, + lib_modules_path = "/lib/lua/"..lua_version, + rocks_subdir = site_config.LUAROCKS_ROCKS_SUBDIR or "/lib/luarocks/rocks", + + arch = "unknown", + lib_extension = "unknown", + obj_extension = "unknown", + + rocks_servers = { + { + "http://www.luarocks.org/repositories/rocks", + "http://luarocks.giga.puc-rio.br/", + "http://luafr.org/luarocks/rocks", + "http://liblua.so/luarocks/repositories/rocks", + "http://luarocks.logiceditor.com/rocks", + } + }, + + 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", + + variables = { + MAKE = "make", + CC = "cc", + LD = "ld", + + CVS = "cvs", + GIT = "git", + SSCM = "sscm", + SVN = "svn", + HG = "hg", + + RSYNC = "rsync", + WGET = "wget", + SCP = "scp", + CURL = "curl", + + PWD = "pwd", + MKDIR = "mkdir", + RMDIR = "rmdir", + CP = "cp", + LS = "ls", + RM = "rm", + FIND = "find", + TEST = "test", + CHMOD = "chmod", + PATCH = "patch", + + ZIP = "zip", + UNZIP = "unzip -n", + GUNZIP = "gunzip", + BUNZIP2 = "bunzip2", + TAR = "tar", + + MD5SUM = "md5sum", + OPENSSL = "openssl", + MD5 = "md5", + STAT = "stat", + + CMAKE = "cmake", + SEVENZ = "7z", + + STATFLAG = "-c '%a'", + }, + + external_deps_subdirs = { + bin = "bin", + lib = "lib", + include = "include" + }, + runtime_external_deps_subdirs = { + bin = "bin", + lib = "lib", + include = "include" + }, +} + +if detected.windows then + home_config_file = home_config_file and home_config_file:gsub("\\","/") + defaults.fs_use_modules = false + defaults.arch = "win32-"..proc + defaults.platforms = {"win32", "windows" } + defaults.lib_extension = "dll" + 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.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.LD = "link" + defaults.variables.MT = "mt" + defaults.variables.LUALIB = "lua"..lua_version..".lib" + defaults.variables.CFLAGS = "/MD /O2" + defaults.variables.LIBFLAG = "/dll" + defaults.variables.LUALIB = "lua"..lua_version..".lib" + defaults.external_deps_patterns = { + bin = { "?.exe", "?.bat" }, + lib = { "?.lib", "?.dll", "lib?.dll" }, + include = { "?.h" } + } + defaults.runtime_external_deps_patterns = { + bin = { "?.exe", "?.bat" }, + lib = { "?.dll", "lib?.dll" }, + include = { "?.h" } + } + defaults.export_path = "SET PATH=%s" + defaults.export_path_separator = ";" + defaults.export_lua_path = "SET LUA_PATH=%s" + defaults.export_lua_cpath = "SET LUA_CPATH=%s" + defaults.local_cache = home.."/cache/luarocks" +end + +if detected.mingw32 then + defaults.platforms = { "win32", "mingw32", "windows" } + defaults.obj_extension = "o" + defaults.cmake_generator = "MinGW Makefiles" + 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" +end + +if detected.unix then + defaults.lib_extension = "so" + defaults.external_lib_extension = "so" + defaults.obj_extension = "o" + defaults.external_deps_dirs = { "/usr/local", "/usr" } + defaults.variables.LUA_BINDIR = site_config.LUA_BINDIR or "/usr/local/bin" + defaults.variables.LUA_INCDIR = site_config.LUA_INCDIR or "/usr/local/include" + defaults.variables.LUA_LIBDIR = site_config.LUA_LIBDIR or "/usr/local/lib" + defaults.variables.CFLAGS = "-O2" + defaults.cmake_generator = "Unix Makefiles" + defaults.platforms = { "unix" } + defaults.variables.CC = "gcc" + defaults.variables.LD = "gcc" + defaults.gcc_rpath = true + defaults.variables.LIBFLAG = "-shared" + defaults.external_deps_patterns = { + bin = { "?" }, + lib = { "lib?.a", "lib?.so", "lib?.so.*" }, + include = { "?.h" } + } + defaults.runtime_external_deps_patterns = { + bin = { "?" }, + lib = { "lib?.so", "lib?.so.*" }, + include = { "?.h" } + } + defaults.export_path = "export PATH='%s'" + defaults.export_path_separator = ":" + defaults.export_lua_path = "export LUA_PATH='%s'" + defaults.export_lua_cpath = "export LUA_CPATH='%s'" + defaults.local_cache = home.."/.cache/luarocks" + if not defaults.variables.CFLAGS:match("-fPIC") then + defaults.variables.CFLAGS = defaults.variables.CFLAGS.." -fPIC" + end +end + +if detected.cygwin then + defaults.lib_extension = "so" -- can be overridden in the config file for mingw builds + defaults.arch = "cygwin-"..proc + defaults.platforms = {"unix", "cygwin"} + defaults.cmake_generator = "Unix Makefiles" + defaults.variables.CC = "echo -llua | xargs gcc" + defaults.variables.LD = "echo -llua | xargs gcc" + defaults.variables.LIBFLAG = "-shared" +end + +if detected.bsd then + defaults.variables.MAKE = "gmake" + defaults.variables.STATFLAG = "-f '%OLp'" +end + +if detected.macosx then + defaults.variables.MAKE = "make" + defaults.external_lib_extension = "dylib" + defaults.arch = "macosx-"..proc + defaults.platforms = {"unix", "bsd", "macosx"} + defaults.variables.LIBFLAG = "-bundle -undefined dynamic_lookup -all_load" + defaults.variables.STATFLAG = "-f '%A'" + local version = io.popen("sw_vers -productVersion"):read("*l") + version = tonumber(version and version:match("^[^.]+%.([^.]+)")) or 3 + if version >= 5 then + version = 5 + else + defaults.gcc_rpath = false + end + defaults.variables.CC = "export MACOSX_DEPLOYMENT_TARGET=10."..version.."; gcc" + defaults.variables.LD = "export MACOSX_DEPLOYMENT_TARGET=10."..version.."; gcc" +end + +if detected.linux then + defaults.arch = "linux-"..proc + defaults.platforms = {"unix", "linux"} +end + +if detected.freebsd then + defaults.arch = "freebsd-"..proc + defaults.platforms = {"unix", "bsd", "freebsd"} +end + +if detected.openbsd then + defaults.arch = "openbsd-"..proc + defaults.platforms = {"unix", "bsd", "openbsd"} +end + +if detected.netbsd then + defaults.arch = "netbsd-"..proc + defaults.platforms = {"unix", "bsd", "netbsd"} +end + +if detected.solaris then + defaults.arch = "solaris-"..proc + defaults.platforms = {"unix", "solaris"} + defaults.variables.MAKE = "gmake" +end + +-- Expose some more values detected by LuaRocks for use by rockspec authors. +defaults.variables.LIB_EXTENSION = defaults.lib_extension +defaults.variables.OBJ_EXTENSION = defaults.obj_extension +defaults.variables.LUAROCKS_PREFIX = site_config.LUAROCKS_PREFIX +defaults.variables.LUA = site_config.LUA_DIR_SET and (defaults.variables.LUA_BINDIR.."/"..defaults.lua_interpreter) or defaults.lua_interpreter + +-- Use defaults: + +-- Populate values from 'defaults.variables' in 'variables' if they were not +-- already set by user. +if not _M.variables then + _M.variables = {} +end +for k,v in pairs(defaults.variables) do + if not _M.variables[k] then + _M.variables[k] = v + end +end + +-- For values not set in the config file, use values from the 'defaults' table. +local cfg_mt = { + __index = function(t, k) + local default = defaults[k] + if default then + rawset(t, k, default) + end + return default + end +} +setmetatable(_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 + end + return table.concat(new_path, ";"), table.concat(new_cpath, ";") +end + +do + local new_path, new_cpath = package_paths() + package.path = new_path..";"..package.path + package.cpath = new_cpath..";"..package.cpath +end + +function which_config() + return sys_config_file, sys_config_ok, home_config_file, home_config_ok +end + +user_agent = "LuaRocks/"..program_version.." "..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) + assert(type(query) == "string") + + for _, platform in ipairs(platforms) do + if platform == query then + return true + end + end +end diff --git a/src/luarocks/command_line.lua b/src/luarocks/command_line.lua new file mode 100644 index 0000000..d16ef8e --- /dev/null +++ b/src/luarocks/command_line.lua @@ -0,0 +1,168 @@ + +--- Functions for command-line scripts. +module("luarocks.command_line", package.seeall) + +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") +local path = require("luarocks.path") +local dir = require("luarocks.dir") +local deps = require("luarocks.deps") + +local program = util.this_program("luarocks") + +--- Display an error message and exit. +-- @param message string: The error message. +local function die(message) + assert(type(message) == "string") + + local ok, err = pcall(util.run_scheduled_functions) + if not ok then + 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) +end + +--- Main command-line processor. +-- Parses input arguments and calls the appropriate driver function +-- to execute the action requested on the command-line, forwarding +-- to it any additional arguments passed by the user. +-- Uses the global table "commands", which contains +-- the loaded modules representing commands. +-- @param ... string: Arguments given on the command-line. +function run_command(...) + local args = {...} + local cmdline_vars = {} + for i = #args, 1, -1 do + local arg = args[i] + if arg:match("^[^-][^=]*=") then + local var, val = arg:match("^([A-Z_][A-Z0-9_]*)=(.*)") + if val then + cmdline_vars[var] = val + table.remove(args, i) + else + die("Invalid assignment: "..arg) + end + end + end + local nonflags = { util.parse_flags(unpack(args)) } + local flags = table.remove(nonflags, 1) + + if flags["from"] then flags["server"] = flags["from"] end + if flags["only-from"] then flags["only-server"] = flags["only-from"] end + if flags["only-sources-from"] then flags["only-sources"] = flags["only-sources-from"] end + if flags["to"] then flags["tree"] = flags["to"] end + if flags["nodeps"] then + flags["deps-mode"] = "none" + table.insert(args, "--deps-mode=none") + end + + cfg.flags = flags + + local command + + if flags["version"] then + util.printout(program.." "..cfg.program_version) + util.printout(program_description) + util.printout() + os.exit(0) + elseif flags["help"] or #nonflags == 0 then + command = "help" + args = nonflags + else + command = nonflags[1] + for i, arg in ipairs(args) do + if arg == command then + table.remove(args, i) + break + end + end + end + command = command:gsub("-", "_") + + if flags["extensions"] then + cfg.use_extensions = true + local type_check = require("luarocks.type_check") + type_check.load_extensions() + end + + if cfg.local_by_default then + flags["local"] = true + end + + if flags["deps-mode"] and not deps.check_deps_mode_flag(flags["deps-mode"]) then + die("Invalid entry for --deps-mode.") + end + + if flags["tree"] then + 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) + elseif flags["local"] then + path.use_tree(cfg.home_tree) + else + local trees = cfg.rocks_trees + path.use_tree(trees[#trees]) + end + + if type(cfg.root_dir) == "string" then + cfg.root_dir = cfg.root_dir:gsub("/+$", "") + else + cfg.root_dir.root = cfg.root_dir.root:gsub("/+$", "") + end + cfg.rocks_dir = cfg.rocks_dir:gsub("/+$", "") + cfg.deploy_bin_dir = cfg.deploy_bin_dir:gsub("/+$", "") + cfg.deploy_lua_dir = cfg.deploy_lua_dir:gsub("/+$", "") + cfg.deploy_lib_dir = cfg.deploy_lib_dir:gsub("/+$", "") + + cfg.variables.ROCKS_TREE = cfg.rocks_dir + cfg.variables.SCRIPTS_DIR = cfg.deploy_bin_dir + + if flags["server"] then + if flags["server"] == true then + die("Argument error: use --server=<url>") + end + local protocol, path = dir.split_url(flags["server"]) + table.insert(cfg.rocks_servers, 1, protocol.."://"..path) + end + + if flags["only-server"] then + if flags["only-server"] == true then + die("Argument error: use --only-server=<url>") + end + cfg.rocks_servers = { flags["only-server"] } + end + + if flags["only-sources"] then + cfg.only_sources_from = flags["only-sources"] + end + + if command ~= "help" then + for k, v in pairs(cmdline_vars) do + cfg.variables[k] = v + end + end + + if commands[command] then + -- TODO the interface of run should be modified, to receive the + -- flags table and the (possibly unpacked) nonflags arguments. + -- This would remove redundant parsing of arguments. + -- 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) + 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) + end + else + die("Unknown command: "..command) + end + util.run_scheduled_functions() +end diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua new file mode 100644 index 0000000..5ac822c --- /dev/null +++ b/src/luarocks/deps.lua @@ -0,0 +1,714 @@ + +--- Dependency handling functions. +-- Dependencies are represented in LuaRocks through strings with +-- a package name followed by a comma-separated list of constraints. +-- Each constraint consists of an operator and a version number. +-- In this string format, version numbers are represented as +-- naturally as possible, like they are used by upstream projects +-- (e.g. "2.0beta3"). Internally, LuaRocks converts them to a purely +-- numeric representation, allowing comparison following some +-- "common sense" heuristics. The precise specification of the +-- 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) + +local cfg = require("luarocks.cfg") +local manif_core = require("luarocks.manif_core") +local path = require("luarocks.path") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +local operators = { + ["=="] = "==", + ["~="] = "~=", + [">"] = ">", + ["<"] = "<", + [">="] = ">=", + ["<="] = "<=", + ["~>"] = "~>", + -- plus some convenience translations + [""] = "==", + ["="] = "==", + ["!="] = "~=" +} + +local deltas = { + scm = 1000, + cvs = 1000, + rc = -1000, + pre = -10000, + beta = -100000, + alpha = -1000000 +} + +local version_mt = { + --- Equality comparison for versions. + -- All version numbers must be equal. + -- If both versions have revision numbers, they must be equal; + -- otherwise the revision number is ignored. + -- @param v1 table: version table to compare. + -- @param v2 table: version table to compare. + -- @return boolean: true if they are considered equivalent. + __eq = function(v1, v2) + if #v1 ~= #v2 then + return false + end + for i = 1, #v1 do + if v1[i] ~= v2[i] then + return false + end + end + if v1.revision and v2.revision then + return (v1.revision == v2.revision) + end + return true + end, + --- Size comparison for versions. + -- All version numbers are compared. + -- If both versions have revision numbers, they are compared; + -- otherwise the revision number is ignored. + -- @param v1 table: version table to compare. + -- @param v2 table: version table to compare. + -- @return boolean: true if v1 is considered lower than v2. + __lt = function(v1, v2) + for i = 1, math.max(#v1, #v2) do + local v1i, v2i = v1[i] or 0, v2[i] or 0 + if v1i ~= v2i then + return (v1i < v2i) + end + end + if v1.revision and v2.revision then + return (v1.revision < v2.revision) + end + return false + end +} + +local version_cache = {} +setmetatable(version_cache, { + __mode = "kv" +}) + +--- Parse a version string, converting to table format. +-- A version table contains all components of the version string +-- converted to numeric format, stored in the array part of the table. +-- If the version contains a revision, it is stored numerically +-- in the 'revision' field. The original string representation of +-- the string is preserved in the 'string' field. +-- Returned version tables use a metatable +-- allowing later comparison through relational operators. +-- @param vstring string: A version number in string format. +-- @return table or nil: A version table or nil +-- if the input string contains invalid characters. +function parse_version(vstring) + if not vstring then return nil end + assert(type(vstring) == "string") + + local cached = version_cache[vstring] + if cached then + return cached + end + + local version = {} + local i = 1 + + local function add_token(number) + version[i] = version[i] and version[i] + number/100000 or number + i = i + 1 + end + + -- trim leading and trailing spaces + vstring = vstring:match("^%s*(.*)%s*$") + version.string = vstring + -- store revision separately if any + local main, revision = vstring:match("(.*)%-(%d+)$") + if revision then + vstring = main + version.revision = tonumber(revision) + end + while #vstring > 0 do + -- extract a number + local token, rest = vstring:match("^(%d+)[%.%-%_]*(.*)") + if token then + add_token(tonumber(token)) + else + -- extract a word + token, rest = vstring:match("^(%a+)[%.%-%_]*(.*)") + if not token then + util.printerr("Warning: version number '"..vstring.."' could not be parsed.") + version[i] = 0 + break + end + local last = #version + version[i] = deltas[token] or (token:byte() / 1000) + end + vstring = rest + end + setmetatable(version, version_mt) + version_cache[vstring] = version + return version +end + +--- Utility function to compare version numbers given as strings. +-- @param a string: one version. +-- @param b string: another version. +-- @return boolean: True if a > b. +function compare_versions(a, b) + return parse_version(a) > parse_version(b) +end + +--- Consumes a constraint from a string, converting it to table format. +-- For example, a string ">= 1.0, > 2.0" is converted to a table in the +-- format {op = ">=", version={1,0}} and the rest, "> 2.0", is returned +-- back to the caller. +-- @param input string: A list of constraints in string format. +-- @return (table, string) or nil: A table representing the same +-- constraints and the string with the unused input, or nil if the +-- input string is invalid. +local function parse_constraint(input) + assert(type(input) == "string") + + local no_upgrade, op, version, rest = input:match("^(@?)([<>=~!]*)%s*([%w%.%_%-]+)[%s,]*(.*)") + local _op = operators[op] + version = parse_version(version) + if not _op then + return nil, "Encountered bad constraint operator: '"..tostring(op).."' in '"..input.."'" + end + if not version then + return nil, "Could not parse version from constraint: '"..input.."'" + end + return { op = _op, version = version, no_upgrade = no_upgrade=="@" and true or nil }, rest +end + +--- Convert a list of constraints from string to table format. +-- For example, a string ">= 1.0, < 2.0" is converted to a table in the format +-- {{op = ">=", version={1,0}}, {op = "<", version={2,0}}}. +-- Version tables use a metatable allowing later comparison through +-- relational operators. +-- @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) + assert(type(input) == "string") + + local constraints, constraint, oinput = {}, nil, input + while #input > 0 do + constraint, input = parse_constraint(input) + if constraint then + table.insert(constraints, constraint) + else + return nil, "Failed to parse constraint '"..tostring(oinput).."' with error: ".. input + end + end + return constraints +end + +--- Convert a dependency from string to table format. +-- For example, a string "foo >= 1.0, < 2.0" +-- is converted to a table in the format +-- {name = "foo", constraints = {{op = ">=", version={1,0}}, +-- {op = "<", version={2,0}}}}. Version tables use a metatable +-- allowing later comparison through relational operators. +-- @param dep string: A dependency in string format +-- 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) + assert(type(dep) == "string") + + local name, rest = dep:match("^%s*([a-zA-Z][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) + if not constraints then return nil, err end + return { name = name, constraints = constraints } +end + +--- Convert a version table to a string. +-- @param v table: The version table +-- @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) + assert(type(v) == "table") + assert(type(internal) == "boolean" or not internal) + + return (internal + and table.concat(v, ":")..(v.revision and tostring(v.revision) or "") + or v.string) +end + +--- Convert a dependency in table format to a string. +-- @param dep table: The dependency in table format +-- @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) + 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)) + end + return dep.name.." "..table.concat(pretty, ", ") +end + +--- A more lenient check for equivalence between versions. +-- This returns true if the requested components of a version +-- match and ignore the ones that were not given. For example, +-- when requesting "2", then "2", "2.1", "2.3.5-9"... all match. +-- When requesting "2.1", then "2.1", "2.1.3" match, but "2.2" +-- doesn't. +-- @param version string or table: Version to be tested; may be +-- in string format or already parsed into a table. +-- @param requested string or table: Version requested; may be +-- in string format or already parsed into a table. +-- @return boolean: True if the tested version matches the requested +-- version, false otherwise. +local function partial_match(version, requested) + assert(type(version) == "string" or type(version) == "table") + assert(type(requested) == "string" or type(version) == "table") + + if type(version) ~= "table" then version = parse_version(version) end + if type(requested) ~= "table" then requested = parse_version(requested) end + if not version or not requested then return false end + + for i, ri in ipairs(requested) do + local vi = version[i] or 0 + if ri ~= vi then return false end + end + if requested.revision then + return requested.revision == version.revision + end + return true +end + +--- Check if a version satisfies a set of constraints. +-- @param version table: A version in table format +-- @param constraints table: An array of constraints in table format. +-- @return boolean: True if version satisfies all constraints, +-- false otherwise. +function 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) + end + local constr_version, constr_op = constr.version, constr.op + setmetatable(constr_version, version_mt) + if constr_op == "==" then ok = version == constr_version + elseif constr_op == "~=" then ok = version ~= constr_version + elseif constr_op == ">" then ok = version > constr_version + elseif constr_op == "<" then ok = version < constr_version + elseif constr_op == ">=" then ok = version >= constr_version + elseif constr_op == "<=" then ok = version <= constr_version + elseif constr_op == "~>" then ok = partial_match(version, constr_version) + end + if not ok then break end + end + return ok +end + +--- Attempt to match a dependency to an installed rock. +-- @param dep table: A dependency parsed in table format. +-- @param blacklist table: Versions that can't be accepted. Table where keys +-- are program versions and values are 'true'. +-- @return table or nil: A table containing fields 'name' and 'version' +-- representing an installed rock which matches the given dependency, +-- or nil if it could not be matched. +local function match_dep(dep, blacklist, deps_mode) + assert(type(dep) == "table") + + local versions + if dep.name == "lua" then + versions = { cfg.lua_version } + else + versions = manif_core.get_versions(dep.name, deps_mode) + end + if not versions then + return nil + end + if blacklist then + local i = 1 + while versions[i] do + if blacklist[versions[i]] then + table.remove(versions, i) + else + i = i + 1 + end + end + end + local candidates = {} + for _, vstring in ipairs(versions) do + local version = parse_version(vstring) + if match_constraints(version, dep.constraints) then + table.insert(candidates, version) + end + end + if #candidates == 0 then + return nil + else + table.sort(candidates) + return { + name = dep.name, + version = candidates[#candidates].string + } + end +end + +--- Attempt to match dependencies of a rockspec to installed rocks. +-- @param rockspec table: The rockspec loaded as a table. +-- @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 +-- 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) + 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 + matched[dep] = found + end + else + if dep.constraints[1] and dep.constraints[1].no_upgrade then + no_upgrade[dep.name] = dep + else + missing[dep.name] = dep + end + end + end + return matched, missing, no_upgrade +end + +--- Return a set of values of a table. +-- @param tbl table: The input table. +-- @return table: The array of keys. +local function values_set(tbl) + local set = {} + for _, v in pairs(tbl) do + set[v] = true + end + return set +end + +--- Check dependencies of a rock and attempt to install any missing ones. +-- Packages are installed using the LuaRocks "install" command. +-- Aborts the program if a dependency could not be fulfilled. +-- @param rockspec table: A rockspec in table format. +-- @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) + + 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) + 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 + return nil, "This rockspec for "..rockspec.package.." does not support "..plat.." platforms." + end + else + if platforms_set[plat] then + supported = true + else + if supported == nil then + supported = false + end + end + end + end + if supported == false then + local plats = table.concat(cfg.platforms, ", ") + return nil, "This rockspec for "..rockspec.package.." does not support "..plats.." platforms." + end + end + + local matched, missing, no_upgrade = 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)) + end + if next(missing) then + for _, dep in pairs(missing) do + util.printerr(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("automatically. Please upgrade "..dep.name.." with") + util.printerr(" luarocks install "..dep.name) + util.printerr("or choose an older version of "..rockspec.name.." with") + util.printerr(" luarocks search "..rockspec.name) + end + return nil, "Failed matching dependencies." + end + + if next(missing) then + util.printerr() + util.printerr("Missing dependencies for "..rockspec.name..":") + for _, dep in pairs(missing) do + util.printerr(show_dep(dep)) + end + util.printerr() + + for _, dep in pairs(missing) do + -- Double-check in case dependency was filled during recursion. + 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) + end + local ok, err, errcode = install.run(rock) + if not ok then + return nil, "Failed installing dependency: "..rock.." - "..err, errcode + end + end + end + end + return true +end + +--- If filename matches a pattern, return the capture. +-- For example, given "libfoo.so" and "lib?.so" is a pattern, +-- returns "foo" (which can then be used to build names +-- based on other patterns. +-- @param file string: a filename +-- @param pattern string: a pattern, where ? is to be matched by the filename. +-- @return string The pattern, if found, or nil. +local function deconstruct_pattern(file, pattern) + local depattern = "^"..(pattern:gsub("%.", "%%."):gsub("%*", ".*"):gsub("?", "(.*)")).."$" + return (file:match(depattern)) +end + +--- Construct all possible patterns for a name and add to the files array. +-- Run through the patterns array replacing all occurrences of "?" +-- with the given file name and store them in the files array. +-- @param file string A raw name (e.g. "foo") +-- @param array of string An array of patterns with "?" as the wildcard +-- (e.g. {"?.so", "lib?.so"}) +-- @param files The array of constructed names +local function add_all_patterns(file, patterns, files) + for _, pattern in ipairs(patterns) do + table.insert(files, (pattern:gsub("?", file))) + end +end + +--- Set up path-related variables for external dependencies. +-- For each key in the external_dependencies table in the +-- rockspec file, four variables are created: <key>_DIR, <key>_BINDIR, +-- <key>_INCDIR and <key>_LIBDIR. These are not overwritten +-- if already set (e.g. by the LuaRocks config file or through the +-- command-line). Values in the external_dependencies table +-- are tables that may contain a "header" or a "library" field, +-- with filenames to be tested for existence. +-- @param rockspec table: The rockspec table. +-- @param mode string: if "build" is given, checks all files; +-- 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) + assert(type(rockspec) == "table") + + local fs = require("luarocks.fs") + + local vars = rockspec.variables + local patterns = cfg.external_deps_patterns + local subdirs = cfg.external_deps_subdirs + if mode == "install" then + patterns = cfg.runtime_external_deps_patterns + subdirs = cfg.runtime_external_deps_subdirs + end + if rockspec.external_dependencies then + for name, files in pairs(rockspec.external_dependencies) do + local ok = true + local failed_file = nil + local failed_dirname = nil + for _, extdir in ipairs(cfg.external_deps_dirs) do + ok = true + local prefix = vars[name.."_DIR"] + local dirs = { + BINDIR = { subdir = subdirs.bin, testfile = "program", pattern = patterns.bin }, + INCDIR = { subdir = subdirs.include, testfile = "header", pattern = patterns.include }, + LIBDIR = { subdir = subdirs.lib, testfile = "library", pattern = patterns.lib } + } + if mode == "install" then + dirs.INCDIR = nil + end + if not prefix then + prefix = extdir + end + if type(prefix) == "table" then + if prefix.bin then + dirs.BINDIR.subdir = prefix.bin + end + if prefix.include then + if dirs.INCDIR then + dirs.INCDIR.subdir = prefix.include + end + end + if prefix.lib then + dirs.LIBDIR.subdir = prefix.lib + end + prefix = prefix.prefix + end + for dirname, dirdata in pairs(dirs) do + dirdata.dir = vars[name.."_"..dirname] or dir.path(prefix, dirdata.subdir) + local file = files[dirdata.testfile] + if file then + local files = {} + if not file:match("%.") then + add_all_patterns(file, dirdata.pattern, files) + else + for _, pattern in ipairs(dirdata.pattern) do + local matched = deconstruct_pattern(file, pattern) + if matched then + add_all_patterns(matched, dirdata.pattern, files) + end + end + table.insert(files, file) + end + local found = false + failed_file = nil + for _, f in pairs(files) do + -- small convenience hack + 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 + end + end + else + found = fs.is_file(dir.path(dirdata.dir, f)) + end + if found then + break + else + if failed_file then + failed_file = failed_file .. ", or " .. f + else + failed_file = f + end + end + end + if not found then + ok = false + failed_dirname = dirname + break + end + end + end + if ok then + for dirname, dirdata in pairs(dirs) do + vars[name.."_"..dirname] = dirdata.dir + end + vars[name.."_DIR"] = prefix + break + end + end + if not ok then + return nil, "Could not find expected file "..failed_file.." for "..name.." -- you may have to install "..name.." in your system and/or pass "..name.."_DIR or "..name.."_"..failed_dirname.." to the luarocks command. Example: luarocks install "..rockspec.name.." "..name.."_DIR=/usr/local", "dependency" + end + end + end + return true +end + +--- Recursively scan dependencies, to build a transitive closure of all +-- dependent packages. +-- @param results table: The results table being built. +-- @param missing table: The table of missing dependencies being recursively built. +-- @param manifest table: The manifest table containing dependencies. +-- @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) + assert(type(results) == "table") + assert(type(missing) == "table") + assert(type(manifest) == "table") + assert(type(name) == "string") + assert(type(version) == "string") + + local fetch = require("luarocks.fetch") + + local err + if results[name] then + return results, missing + end + if not manifest.dependencies then manifest.dependencies = {} end + local dependencies = manifest.dependencies + if not dependencies[name] then dependencies[name] = {} end + local dependencies_name = dependencies[name] + local deplist = dependencies_name[version] + local rockspec, err + if not deplist then + rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version)) + if err then + missing[name.." "..version] = err + return results, missing + end + dependencies_name[version] = rockspec.dependencies + else + rockspec = { dependencies = deplist } + end + local matched, failures = match_deps(rockspec, nil, deps_mode) + for _, match in pairs(matched) do + results, missing = 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" + end + end + results[name] = version + return results, missing +end + +local valid_deps_modes = { + one = true, + order = true, + all = true, + none = true, +} + +function check_deps_mode_flag(flag) + return valid_deps_modes[flag] +end + +function get_deps_mode(flags) + if flags["deps-mode"] then + return flags["deps-mode"] + else + return cfg.deps_mode + end +end + +function deps_mode_to_flag(deps_mode) + return "--deps-mode="..deps_mode +end diff --git a/src/luarocks/dir.lua b/src/luarocks/dir.lua new file mode 100644 index 0000000..b496c96 --- /dev/null +++ b/src/luarocks/dir.lua @@ -0,0 +1,70 @@ + +--- Generic utilities for handling pathnames. +module("luarocks.dir", package.seeall) + +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) + assert(type(pathname) == "string") + + local base = pathname:gsub("[/\\]*$", ""):match(".*[/\\]([^/\\]*)") + return base or pathname +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 +-- no directory separators in input, "" is returned. +function dir_name(pathname) + assert(type(pathname) == "string") + + return (pathname:gsub("/*$", ""):match("(.*/)[^/]*")) or "" +end + +--- Describe a path in a cross-platform way. +-- Use this function to avoid platform-specific directory +-- separators in other modules. Removes trailing slashes from +-- each component given, to avoid repeated separators. +-- Separators inside strings are kept, to handle URLs containing +-- protocols. +-- @param ... strings representing directories +-- @return string: a string with a platform-specific representation +-- of the path. +function 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 + end + return (table.concat(items, "/"):gsub("(.+)/+$", "%1")) +end + +--- Split protocol and path from an URL or local pathname. +-- URLs should be in the "protocol://path" format. +-- For local pathnames, "file" is returned as the protocol. +-- @param url string: an URL or a local pathname. +-- @return string, string: the protocol, and the pathname without the protocol. +function split_url(url) + assert(type(url) == "string") + + local protocol, pathname = url:match("^([^:]*)://(.*)") + if not protocol then + protocol = "file" + pathname = url + end + return protocol, pathname +end + +function normalize(name) + return name:gsub("\\", "/"):gsub("(.)/*$", "%1") +end diff --git a/src/luarocks/download.lua b/src/luarocks/download.lua new file mode 100644 index 0000000..0012cb1 --- /dev/null +++ b/src/luarocks/download.lua @@ -0,0 +1,88 @@ + +--- Module implementing the luarocks "download" command. +-- Download a rock from the repository. +module("luarocks.download", package.seeall) + +local util = require("luarocks.util") +local path = require("luarocks.path") +local fetch = require("luarocks.fetch") +local search = require("luarocks.search") + +help_summary = "Download a specific rock file from a rocks server." +help_arguments = "[--all] [--arch=<arch> | --source | --rockspec] [<name> [<version>]]" + +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 results, err + local query = search.make_query(name, version) + if arch then query.arch = arch end + if all then + if name == "" then query.exact_name = false end + results = search.search_repos(query) + else + results, err = search.find_suitable_rock(query) + end + if type(results) == "string" then + local file = fetch.fetch_url(results) + return file + elseif type(results) == "table" and next(results) then + if all then + local all_ok = true + local any_err = "" + for name, result in pairs(results) do + 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) + if not ok then + all_ok = false + any_err = any_err .. "\n" .. err + end + end + end + end + return all_ok, any_err + else + util.printerr("Multiple search results were returned.") + util.title("Search results:") + search.print_results(results) + return nil, "Please narrow your query or use --all." + end + end + return nil, "Could not find a result named "..name..(version and " "..version or "").."." +end + +--- Driver function for the "download" command. +-- @param name string: a rock name. +-- @param version string or nil: if the name of a package is given, a +-- version may also be passed. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +function run(...) + local flags, name, version = util.parse_flags(...) + + assert(type(version) == "string" or not version) + if type(name) ~= "string" and not flags["all"] then + return nil, "Argument missing, see help." + end + if not name then name, version = "", "" end + + local arch + + if flags["source"] then + arch = "src" + elseif flags["rockspec"] then + arch = "rockspec" + elseif flags["arch"] then + arch = flags["arch"] + end + + local dl, err = download(arch, name, version, flags["all"]) + return dl and true, err +end diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua new file mode 100644 index 0000000..bfdbace --- /dev/null +++ b/src/luarocks/fetch.lua @@ -0,0 +1,325 @@ + +--- Functions related to fetching and loading local and remote files. +module("luarocks.fetch", package.seeall) + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local type_check = require("luarocks.type_check") +local path = require("luarocks.path") +local deps = require("luarocks.deps") +local persist = require("luarocks.persist") +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") + +--- 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. +-- If a local pathname is given, it is returned as a result. +-- @param url string: a local pathname or a remote URL. +-- @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 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) + 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) + if not ok then + return nil, "Failed downloading "..url..(err and " - "..err or ""), "network" + end + return dir.path(fs.current_dir(), filename or dir.base_name(url)) + else + return nil, "Unsupported protocol "..protocol + end +end + +--- For remote URLs, create a temporary directory and download URL inside it. +-- This temporary directory will be deleted on program termination. +-- For local URLs, just return the local pathname and its directory. +-- @param url string: URL to be downloaded +-- @param tmpname string: name pattern to use for avoiding conflicts +-- when creating temporary directory. +-- @param filename string or nil: local filename of URL to be downloaded, +-- in case it can't be inferred from the URL. +-- @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) + assert(type(url) == "string") + assert(type(tmpname) == "string") + assert(type(filename) == "string" or not filename) + filename = filename or dir.base_name(url) + + local protocol, pathname = dir.split_url(url) + if protocol == "file" then + if fs.exists(pathname) then + return pathname, dir.dir_name(fs.absolute_name(pathname)) + else + return nil, "File not found: "..pathname + end + else + local temp_dir = fs.make_temp_dir(tmpname) + if not temp_dir then + return nil, "Failed creating temporary directory." + end + util.schedule_function(fs.delete, temp_dir) + fs.change_dir(temp_dir) + local file, err, errcode = fetch_url(url, filename) + fs.pop_dir() + if not file then + return nil, "Error fetching file: "..err, errcode + end + return file, temp_dir + end +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. +-- @param rock_file string: URL or filename of the rock. +-- @param dest string or nil: if given, directory will be used as +-- 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) + 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) + if not rock_file then + return nil, "Could not fetch rock file: " .. err, errcode + end + + rock_file = fs.absolute_name(rock_file) + local unpack_dir + if dest then + unpack_dir = dest + fs.make_dir(unpack_dir) + 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) + if not ok then + return nil, "Failed unpacking rock file: " .. rock_file + end + fs.pop_dir() + return unpack_dir +end + +function url_to_base_dir(url) + local base = dir.base_name(url) + return base:gsub("%.[^.]*$", ""):gsub("%.tar$", "") +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. +-- @return table or (nil, string): A table representing the rockspec +-- or nil followed by an error message. +function load_local_rockspec(filename) + 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 + + local ok, err = type_check.type_check_rockspec(rockspec) + if not ok then + return nil, filename..": "..err + end + + if rockspec.rockspec_format then + if deps.compare_versions(rockspec.rockspec_format, type_check.rockspec_format) then + return nil, "Rockspec format "..rockspec.rockspec_format.." is not supported, please upgrade LuaRocks." + end + end + + util.platform_overrides(rockspec.build) + util.platform_overrides(rockspec.dependencies) + util.platform_overrides(rockspec.external_dependencies) + util.platform_overrides(rockspec.source) + util.platform_overrides(rockspec.hooks) + + local basename = dir.base_name(filename) + if basename == "rockspec" then + rockspec.name = rockspec.package:lower() + else + rockspec.name = basename:match("(.*)-[^-]*-[0-9]*") + if not rockspec.name then + return nil, "Expected filename in format 'name-version-revision.rockspec'." + end + end + + local protocol, pathname = dir.split_url(rockspec.source.url) + if protocol == "http" or protocol == "https" or protocol == "ftp" or protocol == "file" then + rockspec.source.file = rockspec.source.file or dir.base_name(rockspec.source.url) + end + rockspec.source.protocol, rockspec.source.pathname = protocol, pathname + + -- Temporary compatibility + if rockspec.source.cvs_module then rockspec.source.module = rockspec.source.cvs_module end + if rockspec.source.cvs_tag then rockspec.source.tag = rockspec.source.cvs_tag end + + local name_version = rockspec.package:lower() .. "-" .. rockspec.version + if basename ~= "rockspec" and basename ~= name_version .. ".rockspec" then + return nil, "Inconsistency between rockspec filename ("..basename..") and its contents ("..name_version..".rockspec)." + end + + rockspec.local_filename = filename + local filebase = rockspec.source.file or rockspec.source.url + local base = 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 base + if rockspec.dependencies then + for i = 1, #rockspec.dependencies do + local parsed, err = deps.parse_dep(rockspec.dependencies[i]) + if not parsed then + return nil, "Parse error processing dependency '"..rockspec.dependencies[i].."': "..tostring(err) + end + rockspec.dependencies[i] = parsed + end + else + rockspec.dependencies = {} + end + local ok, err = path.configure_paths(rockspec) + if err then + return nil, "Error verifying paths: "..err + end + + return rockspec +end + +--- Load a local or remote rockspec into a table. +-- This is the entry point for the LuaRocks tools. +-- Only the LuaRocks runtime loader should use +-- load_local_rockspec directly. +-- @param filename string: Local or remote filename of a rockspec. +-- @param location string or nil: Where to download. If not given, +-- 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) + assert(type(filename) == "string") + + local name + local basename = dir.base_name(filename) + if basename == "rockspec" then + name = "rockspec" + else + name = basename:match("(.*)%.rockspec") + if not name and not basename == "rockspec" 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) + fs.pop_dir() + else + filename, err, errcode = fetch_url_at_temp_dir(filename,"luarocks-rockspec-"..name) + end + if not filename then + return nil, err, errcode + end + + return 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. +-- @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) + assert(type(rockspec) == "table") + assert(type(extract) == "boolean") + assert(type(dest_dir) == "string" or not dest_dir) + + local url = rockspec.source.url + local name = rockspec.name.."-"..rockspec.version + 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) + fs.pop_dir() + store_dir = dest_dir + else + source_file, store_dir, errcode = 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." + end + end + if extract then + fs.change_dir(store_dir) + 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 + end + fs.pop_dir() + end + return source_file, store_dir +end + +--- Download sources for building a rock, calling the appropriate protocol method. +-- @param rockspec table: The rockspec table +-- @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. +-- @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) + 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 + proto = require("luarocks.fetch") + else + ok, proto = pcall(require, "luarocks.fetch."..protocol:gsub("[+-]", "_")) + if not ok then + return nil, "Unknown protocol "..protocol + end + end + + if cfg.only_sources_from + and rockspec.source.pathname + and #rockspec.source.pathname > 0 then + if #cfg.only_sources_from == 0 then + return nil, "Can't download "..rockspec.source.url.." -- download from remote servers disabled" + elseif rockspec.source.pathname:find(cfg.only_sources_from, 1, true) ~= 1 then + return nil, "Can't download "..rockspec.source.url.." -- only downloading from "..cfg.only_sources_from + end + end + return proto.get_sources(rockspec, extract, dest_dir) +end diff --git a/src/luarocks/fetch/cvs.lua b/src/luarocks/fetch/cvs.lua new file mode 100644 index 0000000..a622cdc --- /dev/null +++ b/src/luarocks/fetch/cvs.lua @@ -0,0 +1,44 @@ + +--- Fetch back-end for retrieving sources from CVS. +module("luarocks.fetch.cvs", package.seeall) + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +--- Download sources for building a rock, using CVS. +-- @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 get_sources(rockspec, extract, dest_dir) + assert(type(rockspec) == "table") + assert(type(dest_dir) == "string" or not dest_dir) + + local name_version = rockspec.name .. "-" .. rockspec.version + local module = rockspec.source.module or dir.base_name(rockspec.source.url) + local command = {rockspec.variables.CVS, "-d"..rockspec.source.pathname, "export", module} + if rockspec.source.tag then + table.insert(command, 4, "-r") + table.insert(command, 5, rockspec.source.tag) + end + local store_dir + if not dest_dir then + store_dir = fs.make_temp_dir(name_version) + if not store_dir then + return nil, "Failed creating temporary directory." + end + util.schedule_function(fs.delete, store_dir) + else + store_dir = dest_dir + end + fs.change_dir(store_dir) + if not fs.execute(unpack(command)) then + return nil, "Failed fetching files from CVS." + end + fs.pop_dir() + return module, store_dir +end + diff --git a/src/luarocks/fetch/git.lua b/src/luarocks/fetch/git.lua new file mode 100644 index 0000000..e7c8943 --- /dev/null +++ b/src/luarocks/fetch/git.lua @@ -0,0 +1,81 @@ + +--- Fetch back-end for retrieving sources from GIT. +module("luarocks.fetch.git", package.seeall) + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +--- Git >= 1.7.10 can clone a branch **or tag**, < 1.7.10 by branch only. We +-- need to know this in order to build the appropriate command; if we can't +-- clone by tag then we'll have to issue a subsequent command to check out the +-- given tag. +-- @return boolean: Whether Git can clone by tag. +local function git_can_clone_by_tag() + local version_string = io.popen('git --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() +end + +--- Download sources for building a rock, using git. +-- @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 get_sources(rockspec, extract, dest_dir) + assert(type(rockspec) == "table") + assert(type(dest_dir) == "string" or not dest_dir) + + local git_cmd = rockspec.variables.GIT + local name_version = rockspec.name .. "-" .. rockspec.version + local module = dir.base_name(rockspec.source.url) + -- Strip off .git from base name if present + module = module:gsub("%.git$", "") + + local store_dir + if not dest_dir then + store_dir = fs.make_temp_dir(name_version) + if not store_dir then + return nil, "Failed creating temporary directory." + end + util.schedule_function(fs.delete, store_dir) + else + store_dir = dest_dir + end + store_dir = fs.absolute_name(store_dir) + fs.change_dir(store_dir) + + local command = {git_cmd, "clone", "--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 + -- 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) + end + end + if not fs.execute(unpack(command)) then + return nil, "Failed cloning git repository." + end + fs.change_dir(module) + if tag_or_branch and not git_can_clone_by_tag() then + local checkout_command = {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 + end + + fs.delete(dir.path(store_dir, module, ".git")) + fs.delete(dir.path(store_dir, module, ".gitignore")) + fs.pop_dir() + fs.pop_dir() + return module, store_dir +end diff --git a/src/luarocks/fetch/git_file.lua b/src/luarocks/fetch/git_file.lua new file mode 100644 index 0000000..1b18d0f --- /dev/null +++ b/src/luarocks/fetch/git_file.lua @@ -0,0 +1,17 @@ + +--- Fetch back-end for retrieving sources from local Git repositories. +module("luarocks.fetch.git_file", package.seeall) + +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 get_sources(rockspec, extract, dest_dir) + rockspec.source.url = rockspec.source.url:gsub("^git.file://", "") + return git.get_sources(rockspec, extract, dest_dir) +end diff --git a/src/luarocks/fetch/hg.lua b/src/luarocks/fetch/hg.lua new file mode 100644 index 0000000..a08520a --- /dev/null +++ b/src/luarocks/fetch/hg.lua @@ -0,0 +1,54 @@ + +--- Fetch back-end for retrieving sources from HG. +module("luarocks.fetch.hg", package.seeall) + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +--- Download sources for building a rock, using hg. +-- @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 get_sources(rockspec, extract, dest_dir) + assert(type(rockspec) == "table") + assert(type(dest_dir) == "string" or not dest_dir) + + local hg_cmd = rockspec.variables.HG + local name_version = rockspec.name .. "-" .. rockspec.version + -- Strip off special hg:// protocol type + local url = rockspec.source.url:gsub("^hg://", "") + + local module = dir.base_name(url) + + local command = {hg_cmd, "clone", url, module} + local tag_or_branch = rockspec.source.tag or rockspec.source.branch + if tag_or_branch then + command = {hg_cmd, "clone", "--rev", url, module} + end + local store_dir + if not dest_dir then + store_dir = fs.make_temp_dir(name_version) + if not store_dir then + return nil, "Failed creating temporary directory." + end + util.schedule_function(fs.delete, store_dir) + else + store_dir = dest_dir + end + fs.change_dir(store_dir) + if not fs.execute(unpack(command)) then + return nil, "Failed cloning hg repository." + end + fs.change_dir(module) + + fs.delete(dir.path(store_dir, module, ".hg")) + fs.delete(dir.path(store_dir, module, ".hgignore")) + fs.pop_dir() + fs.pop_dir() + return module, store_dir +end + diff --git a/src/luarocks/fetch/sscm.lua b/src/luarocks/fetch/sscm.lua new file mode 100644 index 0000000..e52e801 --- /dev/null +++ b/src/luarocks/fetch/sscm.lua @@ -0,0 +1,42 @@ + +--- Fetch back-end for retrieving sources from Surround SCM Server +module("luarocks.fetch.sscm", package.seeall) + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") + +--- Download sources via Surround SCM Server for building a rock. +-- @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 get_sources(rockspec, extract, dest_dir) + assert(type(rockspec) == "table") + assert(type(dest_dir) == "string" or not dest_dir) + + local sscm_cmd = rockspec.variables.SSCM + local module = rockspec.source.module or dir.base_name(rockspec.source.url) + local branch, repository = string.match(rockspec.source.pathname, "^([^/]*)/(.*)") + if not branch or not repository then + return nil, "Error retrieving branch and repository from rockspec." + end + -- Search for working directory. + local working_dir + local tmp = io.popen(string.format(sscm_cmd..[[ property "/" -d -b%s -p%s]], branch, repository)) + for line in tmp:lines() do + --%c because a chr(13) comes in the end. + working_dir = string.match(line, "Working directory:[%s]*(.*)%c$") + if working_dir then break end + end + tmp:close() + if not working_dir then + return nil, "Error retrieving working directory from SSCM." + end + if not fs.execute(sscm_cmd, "get", "*", "-e" , "-r", "-b"..branch, "-p"..repository, "-tmodify", "-wreplace") then + return nil, "Failed fetching files from SSCM." + end + -- FIXME: This function does not honor the dest_dir parameter. + return module, working_dir +end diff --git a/src/luarocks/fetch/svn.lua b/src/luarocks/fetch/svn.lua new file mode 100644 index 0000000..a4e952d --- /dev/null +++ b/src/luarocks/fetch/svn.lua @@ -0,0 +1,53 @@ + +--- Fetch back-end for retrieving sources from Subversion. +module("luarocks.fetch.svn", package.seeall) + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +--- Download sources for building a rock, using Subversion. +-- @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 get_sources(rockspec, extract, dest_dir) + assert(type(rockspec) == "table") + assert(type(dest_dir) == "string" or not dest_dir) + + local svn_cmd = rockspec.variables.SVN + local name_version = rockspec.name .. "-" .. rockspec.version + local module = rockspec.source.module or dir.base_name(rockspec.source.url) + local url = rockspec.source.url:gsub("^svn://", "") + local command = {svn_cmd, "checkout", url, module} + if rockspec.source.tag then + table.insert(command, 5, "-r") + table.insert(command, 6, rockspec.source.tag) + end + local store_dir + if not dest_dir then + store_dir = fs.make_temp_dir(name_version) + if not store_dir then + return nil, "Failed creating temporary directory." + end + util.schedule_function(fs.delete, store_dir) + else + store_dir = dest_dir + end + fs.change_dir(store_dir) + if not fs.execute(unpack(command)) then + return nil, "Failed fetching files from Subversion." + end + fs.change_dir(module) + for _, d in ipairs(fs.find(".")) do + if dir.base_name(d) == ".svn" then + fs.delete(dir.path(store_dir, module, d)) + end + end + fs.pop_dir() + fs.pop_dir() + return module, store_dir +end + diff --git a/src/luarocks/fs.lua b/src/luarocks/fs.lua new file mode 100644 index 0000000..467b194 --- /dev/null +++ b/src/luarocks/fs.lua @@ -0,0 +1,40 @@ + +--- Proxy module for filesystem and platform abstractions. +-- All code using "fs" code should require "luarocks.fs", +-- and not the various platform-specific implementations. +-- However, see the documentation of the implementation +-- for the API reference. + +local pairs = pairs + +module("luarocks.fs", package.seeall) + +local cfg = require("luarocks.cfg") + +local function load_fns(fs_table) + for name, fn in pairs(fs_table) do + if not _M[name] then + _M[name] = fn + end + end +end + +-- Load platform-specific functions +local loaded_platform = nil +for _, platform in ipairs(cfg.platforms) do + local ok, fs_plat = pcall(require, "luarocks.fs."..platform) + if ok and fs_plat then + loaded_platform = platform + load_fns(fs_plat) + break + end +end + +-- Load platform-independent pure-Lua functionality +local fs_lua = require("luarocks.fs.lua") +load_fns(fs_lua) + +-- Load platform-specific fallbacks for missing Lua modules +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 + diff --git a/src/luarocks/fs/lua.lua b/src/luarocks/fs/lua.lua new file mode 100644 index 0000000..36b3369 --- /dev/null +++ b/src/luarocks/fs/lua.lua @@ -0,0 +1,698 @@ + +--- Native Lua implementation of filesystem and platform abstractions, +-- using LuaFileSystem, LZLib, MD5 and LuaCurl. +module("luarocks.fs.lua", package.seeall) + +local fs = require("luarocks.fs") + +local cfg = require("luarocks.cfg") +local dir = require("luarocks.dir") +local util = require("luarocks.util") +local path = require("luarocks.path") + +local socket_ok, zip_ok, unzip_ok, lfs_ok, md5_ok, posix_ok, _ +local http, ftp, lrzip, luazip, lfs, md5, posix + +if cfg.fs_use_modules then + socket_ok, http = pcall(require, "socket.http") + _, ftp = pcall(require, "socket.ftp") + zip_ok, lrzip = pcall(require, "luarocks.tools.zip") + unzip_ok, luazip = pcall(require, "zip"); _G.zip = nil + lfs_ok, lfs = pcall(require, "lfs") + md5_ok, md5 = pcall(require, "md5") + posix_ok, posix = pcall(require, "posix") +end + +local patch = require("luarocks.tools.patch") + +local dir_stack = {} + +math.randomseed(os.time()) + +dir_separator = "/" + +--- Quote argument for shell processing. +-- Adds single quotes and escapes. +-- @param arg string: Unquoted argument. +-- @return string: Quoted argument. +function Q(arg) + assert(type(arg) == "string") + + -- FIXME Unix-specific + return "'" .. arg:gsub("\\", "\\\\"):gsub("'", "'\\''") .. "'" +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 is_writable(file) + assert(file) + file = dir.normalize(file) + local result + if fs.is_dir(file) then + local file2 = dir.path(file, '.tmpluarockstestwritable') + local fh = io.open(file2, 'wb') + result = fh ~= nil + if fh then fh:close() 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 + +--- 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) + 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 + return temp_dir + else + return nil + end +end + +--- Run the given command, quoting its arguments. +-- The command is executed in the current directory in the dir stack. +-- @param command string: The command to be executed. No quoting/escaping +-- is applied. +-- @param ... Strings containing additional arguments, which are quoted. +-- @return boolean: true if command succeeds (status code 0), false +-- otherwise. +function execute(command, ...) + assert(type(command) == "string") + + for _, arg in ipairs({...}) do + assert(type(arg) == "string") + command = command .. " " .. fs.Q(arg) + 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 +-- or if it could not perform the check for any reason. +function check_md5(file, md5sum) + file = dir.normalize(file) + local computed = fs.get_md5(file) + if not computed then + return false + end + if computed:match("^"..md5sum) then + return true + else + return false + end +end + +--------------------------------------------------------------------- +-- LuaFileSystem functions +--------------------------------------------------------------------- + +if lfs_ok then + +--- Run the given command. +-- The command is executed in the current directory in the dir stack. +-- @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 + local code = os.execute(cmd) + if code == 0 or code == true then + return true + else + return false + end +end + +--- Obtain current directory. +-- Uses the module's internal dir stack. +-- @return string: the absolute pathname of the current directory. +function current_dir() + return lfs.currentdir() +end + +--- Change the current directory. +-- Uses the module's internal dir stack. This does not have exact +-- 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) + table.insert(dir_stack, lfs.currentdir()) + d = dir.normalize(d) + 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() + 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() + local d = table.remove(dir_stack) + if d then + lfs.chdir(d) + return true + else + return false + end +end + +--- Create a directory if it does not already exist. +-- If any of the higher levels in the path name does 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) + assert(type(directory) == "string") + directory = dir.normalize(directory) + local path = nil + if directory:sub(2, 2) == ":" then + path = directory:sub(1, 2) + directory = directory:sub(4) + else + if directory:match("^/") then + path = "" + end + end + for d in directory:gmatch("([^"..dir.separator.."]+)"..dir.separator.."*") do + 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 + end + elseif mode ~= "directory" then + return false + end + 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 d string: pathname of directory to remove. +function remove_dir_if_empty(d) + assert(d) + d = dir.normalize(d) + lfs.rmdir(d) +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 d string: pathname of directory to remove. +function remove_dir_tree_if_empty(d) + assert(d) + d = dir.normalize(d) + for i=1,10 do + lfs.rmdir(d) + d = dir.dir_name(d) + end +end + +--- Copy a file. +-- @param src string: Pathname of source +-- @param dest string: Pathname of destination +-- @param perms string or nil: Permissions for destination file, +-- 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) + assert(src and dest) + src = dir.normalize(src) + dest = dir.normalize(dest) + local destmode = lfs.attributes(dest, "mode") + if destmode == "directory" then + dest = dir.path(dest, dir.base_name(src)) + end + if not perms then perms = fs.get_permissions(src) end + local src_h, err = io.open(src, "rb") + if not src_h then return nil, err end + local dest_h, err = io.open(dest, "w+b") + if not dest_h then src_h:close() return nil, err end + while true do + local block = src_h:read(8192) + if not block then break end + dest_h:write(block) + end + src_h:close() + dest_h:close() + fs.chmod(dest, perms) + return true +end + +--- Implementation function for recursive copy of directory contents. +-- Assumes paths are normalized. +-- @param src string: Pathname of source +-- @param dest string: Pathname of destination +-- @return boolean or (boolean, string): true on success, false on failure +local function recursive_copy(src, dest) + local srcmode = lfs.attributes(src, "mode") + + if srcmode == "file" then + local ok = fs.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) + for file in lfs.dir(src) do + if file ~= "." and file ~= ".." then + local ok = recursive_copy(dir.path(src, file), subdir) + if not ok then return false end + end + end + end + return true +end + +--- Recursively copy the contents of a directory. +-- @param src string: Pathname of source +-- @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) + assert(src and dest) + src = dir.normalize(src) + dest = dir.normalize(dest) + assert(lfs.attributes(src, "mode") == "directory") + + for file in lfs.dir(src) do + if file ~= "." and file ~= ".." then + local ok = recursive_copy(dir.path(src, file), dest) + if not ok then + return false, "Failed copying "..src.." to "..dest + end + end + end + return true +end + +--- Implementation function for recursive removal of directories. +-- Assumes paths are normalized. +-- @param name string: Pathname of file +-- @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 + for file in lfs.dir(name) do + if file ~= "." and file ~= ".." then + local ok, err = recursive_delete(dir.path(name, file)) + if not ok then return nil, err end + end + end + local ok, err = lfs.rmdir(name) + if not ok then return nil, err end + 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) + name = dir.normalize(name) + return recursive_delete(name) or false +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 = {} + for file in lfs.dir(at) do + if file ~= "." and file ~= ".." then + table.insert(result, file) + end + end + return result +end + +--- Implementation function for recursive find. +-- Assumes paths are normalized. +-- @param cwd string: Current working directory in recursion. +-- @param prefix string: Auxiliary prefix string to form pathname. +-- @param result table: Array of strings where results are collected. +local function recursive_find(cwd, prefix, result) + for file in lfs.dir(cwd) do + if file ~= "." and file ~= ".." then + local item = prefix .. file + table.insert(result, item) + local pathname = dir.path(cwd, file) + if lfs.attributes(pathname, "mode") == "directory" then + recursive_find(pathname, item..dir_separator, result) + end + end + end +end + +--- Recursively scan the contents of a directory. +-- @param at string or nil: directory to scan (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 find(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 = {} + recursive_find(at, "", result) + return result +end + +--- Test for existance of a file. +-- @param file string: filename to test +-- @return boolean: true if file exists, false otherwise. +function exists(file) + assert(file) + file = dir.normalize(file) + return type(lfs.attributes(file)) == "table" +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) + assert(file) + file = dir.normalize(file) + return lfs.attributes(file, "mode") == "directory" +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) + assert(file) + file = dir.normalize(file) + return lfs.attributes(file, "mode") == "file" +end + +function set_time(file, time) + file = dir.normalize(file) + return lfs.touch(file, time) +end + +end + +--------------------------------------------------------------------- +-- LuaZip functions +--------------------------------------------------------------------- + +if zip_ok then + +function zip(zipfile, ...) + return lrzip.zip(zipfile, ...) +end + +end + +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) + 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)) + else + local rf, err = zipfile:open(file.filename) + if not rf then zipfile:close(); return nil, err end + local contents = rf:read("*a") + rf:close() + local wf, err = io.open(dir.path(fs.current_dir(), file.filename), "wb") + if not wf then zipfile:close(); return nil, err end + wf:write(contents) + wf:close() + end + file = files() + until not file + zipfile:close() + return true +end + +end + +--------------------------------------------------------------------- +-- LuaSocket functions +--------------------------------------------------------------------- + +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 result = {} + + local proxy = cfg.proxy + if type(proxy) ~= "string" then proxy = nil end + -- LuaSocket's http.request crashes when given URLs missing the scheme part. + if proxy and not proxy:find("://") then + proxy = "http://" .. proxy + end + + local res, status, headers, err = http.request { + url = url, + proxy = proxy, + redirect = false, + sink = ltn12.sink.table(result), + headers = { + ["user-agent"] = cfg.user_agent.." via LuaSocket" + }, + } + if not res then + return nil, status + elseif status == 301 or status == 302 then + local location = headers.location + if location then + local protocol, rest = dir.split_url(location) + if redirect_protocols[protocol] then + if not loop_control then + loop_control = {} + elseif loop_control[location] then + return nil, "Redirection loop -- broken URL?" + end + loop_control[url] = true + return http_request(location, redirect_protocols[protocol], loop_control) + else + return nil, "URL redirected to unsupported protocol - install luasec to get HTTPS support." + end + end + return nil, err + elseif status ~= 200 then + return nil, err + else + return table.concat(result) + end +end + +--- 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) + assert(type(url) == "string") + assert(type(filename) == "string" or not filename) + + filename = dir.path(fs.current_dir(), filename or dir.base_name(url)) + + local content, err + if util.starts_with(url, "http:") then + content, err = http_request(url, http) + 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) + else + err = "Unsupported protocol - install luasec to get HTTPS support." + end + else + err = "Unsupported protocol" + end + if not content then + return false, tostring(err) + end + local file = io.open(filename, "wb") + if not file then return false end + file:write(content) + file:close() + return true +end + +end +--------------------------------------------------------------------- +-- MD5 functions +--------------------------------------------------------------------- + +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) + 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 +end + +end + +--------------------------------------------------------------------- +-- POSIX functions +--------------------------------------------------------------------- + +if posix_ok then + +local octal_to_rwx = { + ["0"] = "---", + ["1"] = "--x", + ["2"] = "-w-", + ["3"] = "-wx", + ["4"] = "r--", + ["5"] = "r-x", + ["6"] = "rw-", + ["7"] = "rwx", +} + +function chmod(file, mode) + -- LuaPosix (as of 5.1.15) does not support octal notation... + if mode:sub(1,1) == "0" then + local new_mode = {} + for c in mode:sub(2):gmatch(".") do + table.insert(new_mode, octal_to_rwx[c]) + end + mode = table.concat(new_mode) + end + local err = posix.chmod(file, mode) + return err == 0 +end + +function get_permissions(file) + return posix.stat(file, "mode") +end + +end + +--------------------------------------------------------------------- +-- Other functions +--------------------------------------------------------------------- + +--- 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) + local p, all_ok = patch.read_patch(patchname, patchdata) + if not all_ok then + return nil, "Failed reading patch "..patchname + end + if p then + return patch.apply_patch(p, 1) + end +end + +--- Move a file. +-- @param src string: Pathname of source +-- @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) + assert(src and dest) + if fs.exists(dest) and not fs.is_dir(dest) then + return false, "File already exists: "..dest + end + local ok, err = fs.copy(src, dest) + if not ok then + return false, err + end + ok = fs.delete(src) + if not ok then + return false, "Failed move: could not delete "..src.." after copy." + end + return true +end + +--- Check if user has write permissions for the command. +-- Assumes the configuration variables under cfg have been previously set up. +-- @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) + local root_dir = path.root_dir(cfg.rocks_dir) + local ok = true + local err = "" + for _, dir in ipairs { cfg.rocks_dir, root_dir } do + if fs.exists(dir) and not fs.is_writable(dir) then + ok = false + err = "Your user does not have write permissions in " .. dir + break + end + end + local root_parent = dir.dir_name(root_dir) + if ok and not fs.exists(root_dir) and not fs.is_writable(root_parent) then + ok = false + err = root_dir.." does not exist and your user does not have write permissions in " .. root_parent + end + if ok then + return true + else + if flags["local"] then + err = err .. " \n-- please check your permissions." + else + err = err .. " \n-- you may want to run as a privileged user or use your local tree with --local." + end + return nil, err + end +end diff --git a/src/luarocks/fs/unix.lua b/src/luarocks/fs/unix.lua new file mode 100644 index 0000000..cccbbd3 --- /dev/null +++ b/src/luarocks/fs/unix.lua @@ -0,0 +1,111 @@ + +--- 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) + +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()) + +--- Annotate command string for quiet execution. +-- @param cmd string: A command-line string. +-- @return string: The command-line, with silencing annotation. +function quiet(cmd) + return cmd.." 1> /dev/null 2> /dev/null" +end + +--- Return an absolute pathname from a potentially relative one. +-- @param pathname string: pathname to convert. +-- @param relative_to string or nil: path to prepend when making +-- 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) + assert(type(pathname) == "string") + assert(type(relative_to) == "string" or not relative_to) + + relative_to = relative_to or fs.current_dir() + if pathname:sub(1,1) == "/" then + return pathname + else + return relative_to .. "/" .. pathname + end +end + +--- Create a wrapper to make a script executable from the command-line. +-- @param file string: Pathname of script to be made executable. +-- @param dest string: Directory where to put the wrapper. +-- @param name string: rock name to be used in loader context. +-- @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) + 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 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') + wrapper:close() + if fs.chmod(wrapname, "0755") then + return true + else + return nil, "Could not make "..wrapname.." executable." + end +end + +--- Check if a file (typically inside path.bin_dir) is an actual binary +-- or a Lua wrapper. +-- @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) + if filename:match("%.lua$") then + return false + end + local file = io.open(filename) + if not file then + return true + end + local first = file:read(2) + file:close() + if not first then + util.printerr("Warning: could not read "..filename) + return true + end + return first ~= "#!" +end + +function copy_binary(filename, dest) + return fs.copy(filename, dest, "0755") +end + +--- Move a file on top of the other. +-- The new file ceases to exist under its original name, +-- and takes over the name of the old file. +-- On Unix this is done through a single rename operation. +-- @param old_file The name of the original file, +-- which will be the new name of new_file. +-- @param new_file The name of the new file, +-- 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) + return os.rename(new_file, old_file) +end diff --git a/src/luarocks/fs/unix/tools.lua b/src/luarocks/fs/unix/tools.lua new file mode 100644 index 0000000..3b853be --- /dev/null +++ b/src/luarocks/fs/unix/tools.lua @@ -0,0 +1,318 @@ + +--- fs operations implemented with third-party tools for Unix platform abstractions. +module("luarocks.fs.unix.tools", package.seeall) + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local cfg = require("luarocks.cfg") + +local dir_stack = {} + +local vars = cfg.variables + +local function command_at(directory, cmd) + return "cd " .. fs.Q(directory) .. " && " .. cmd +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() + for _, directory in ipairs(dir_stack) do + current = fs.absolute_name(directory, current) + end + return current +end + +--- Run the given command. +-- The command is executed in the current directory in the directory stack. +-- @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)) + if code == 0 or code == true then + return true + else + return false + end +end + +--- Change the current directory. +-- Uses the module's internal directory stack. This does not have exact +-- 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) + assert(type(directory) == "string") + table.insert(dir_stack, directory) +end + +--- Change directory to root. +-- Allows leaving a directory (e.g. for deleting it) in +-- a crossplatform way. +function change_dir_to_root() + table.insert(dir_stack, "/") +end + +--- Change working directory to the previous in the directory stack. +function pop_dir() + local directory = table.remove(dir_stack) + return directory ~= nil +end + +--- Create a directory if it does not already exist. +-- If any of the higher levels in the path name does 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) + assert(directory) + return fs.execute(vars.MKDIR.." -p", 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_if_empty(directory) + assert(directory) + fs.execute_string(fs.quiet(vars.RMDIR.." "..fs.Q(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) + assert(directory) + fs.execute_string(fs.quiet(vars.RMDIR.." -p "..fs.Q(directory))) +end + +--- Copy a file. +-- @param src string: Pathname of source +-- @param dest string: Pathname of destination +-- @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) + assert(src and dest) + if fs.execute(vars.CP, src, dest) then + if perm then + if fs.is_dir(dest) then + dest = dir.path(dest, dir.base_name(src)) + end + if fs.chmod(dest, perm) then + return true + else + return false, "Failed setting permissions of "..dest + end + end + return true + else + return false, "Failed copying "..src.." to "..dest + end +end + +--- Recursively copy the contents of a directory. +-- @param src string: Pathname of source +-- @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) + assert(src and dest) + if fs.execute_string(fs.quiet(vars.CP.." -pPR "..fs.Q(src).."/* "..fs.Q(dest))) then + return true + else + return false, "Failed copying "..src.." to "..dest + end +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) + assert(arg) + assert(arg:sub(1,1) == "/") + return fs.execute_string(fs.quiet(vars.RM.." -rf " .. 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)) + for file in pipe:lines() do + table.insert(result, file) + end + pipe:close() + return result +end + +--- Recursively scan the contents of a directory. +-- @param at string or nil: directory to scan (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 find(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.FIND.." * 2>/dev/null")) + for file in pipe:lines() do + table.insert(result, file) + end + pipe:close() + return result +end + +--- Compress files in a .zip archive. +-- @param zipfile string: pathname of .zip archive to be created. +-- @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.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) + assert(zipfile) + return fs.execute(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) + assert(file) + return fs.execute(vars.TEST, "-e", file) +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) + assert(file) + return fs.execute(vars.TEST, "-d", file) +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) + assert(file) + return fs.execute(vars.TEST, "-f", file) +end + +--- 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) + assert(type(url) == "string") + assert(type(filename) == "string" or not filename) + + 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) + else + return fs.execute(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)) + end +end + +function chmod(pathname, mode) + if mode then + return fs.execute(vars.CHMOD, mode, pathname) + else + return false + end +end + +--- Apply a patch. +-- @param patchname string: The filename of the patch. +function apply_patch(patchname) + return fs.execute(vars.PATCH.." -p1 -f -i ", patchname) +end + +--- Unpack an archive. +-- Extract the contents of an archive, detecting its format by +-- 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) + assert(type(archive) == "string") + + local ok + if archive:match("%.tar%.gz$") or archive:match("%.tgz$") then + ok = fs.execute_string(vars.GUNZIP.." -c "..archive.."|"..vars.TAR.." -xf -") + elseif archive:match("%.tar%.bz2$") then + ok = fs.execute_string(vars.BUNZIP2.." -c "..archive.."|tar -xf -") + elseif archive:match("%.zip$") then + ok = fs.execute(vars.UNZIP, archive) + elseif archive:match("%.lua$") or archive:match("%.c$") then + -- Ignore .lua and .c files; they don't need to be extracted. + return true + else + local ext = archive:match(".*(%..*)") + return false, "Unrecognized filename extension "..(ext or "") + end + if not ok then + return false, "Failed extracting "..archive + end + return true +end + +local md5_cmd = { + md5sum = vars.MD5SUM, + openssl = vars.OPENSSL.." md5", + md5 = 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) + local cmd = md5_cmd[cfg.md5checker] + if not cmd then return nil end + local pipe = io.popen(cmd.." "..fs.absolute_name(file)) + local computed = pipe:read("*a") + pipe:close() + if not computed then return nil end + return computed:match("("..("%x"):rep(32)..")") +end + +function get_permissions(filename) + local pipe = io.popen(vars.STAT.." "..vars.STATFLAG.." "..fs.Q(filename)) + local ret = pipe:read("*l") + pipe:close() + return ret +end diff --git a/src/luarocks/fs/win32.lua b/src/luarocks/fs/win32.lua new file mode 100644 index 0000000..2b5bb95 --- /dev/null +++ b/src/luarocks/fs/win32.lua @@ -0,0 +1,133 @@ +--- 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) + +local fs = require("luarocks.fs") + +local cfg = require("luarocks.cfg") +local dir = require("luarocks.dir") + +--- Annotate command string for quiet execution. +-- @param cmd string: A command-line string. +-- @return string: The command-line, with silencing annotation. +function quiet(cmd) + return cmd.." 2> NUL 1> NUL" +end + +--- Quote argument for shell processing. Fixes paths on Windows. +-- Adds single quotes and escapes. +-- @param arg string: Unquoted argument. +-- @return string: Quoted argument. +function Q(arg) + assert(type(arg) == "string") + -- Quote DIR for Windows + if arg:match("^[%.a-zA-Z]?:?[\\/]") then + return '"' .. arg:gsub("/", "\\"):gsub('"', '\\"') .. '"' + end + -- URLs and anything else + return '"' .. arg:gsub('"', '\\"') .. '"' +end + +--- Return an absolute pathname from a potentially relative one. +-- @param pathname string: pathname to convert. +-- @param relative_to string or nil: path to prepend when making +-- 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) + assert(type(pathname) == "string") + assert(type(relative_to) == "string" or not relative_to) + + relative_to = relative_to or fs.current_dir() + -- FIXME I'm not sure this first \\ should be there at all. + -- What are the Windows rules for drive letters? + if pathname:match("^[\\.a-zA-Z]?:?[\\/]") then + return pathname + else + return relative_to .. "/" .. pathname + end +end + +--- Create a wrapper to make a script executable from the command-line. +-- @param file string: Pathname of script to be made executable. +-- @param dest string: Directory where to put the wrapper. +-- @param name string: rock name to be used in loader context. +-- @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) + 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 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") + wrapper:close() + return true +end + +function is_actual_binary(name) + name = name:lower() + if name:match("%.bat$") or name:match("%.exe$") then + return true + end + return false +end + +function 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) + if base:match(exe_pattern) then + base = base:gsub(exe_pattern, ".lua") + local helpname = dest.."/"..base + local helper = io.open(helpname, "w") + if not helper then + return nil, "Could not open "..helpname.." for writing." + end + helper:write('package.path=\"'..package.path:gsub("\\","\\\\")..';\"..package.path\n') + helper:write('package.cpath=\"'..package.path:gsub("\\","\\\\")..';\"..package.cpath\n') + helper:close() + end + return true +end + +function chmod(filename, mode) + return true +end + +function get_permissions(filename) + return "" +end + +--- Move a file on top of the other. +-- The new file ceases to exist under its original name, +-- and takes over the name of the old file. +-- On Windows this is done by removing the original file and +-- renaming the new file to its original name. +-- @param old_file The name of the original file, +-- which will be the new name of new_file. +-- @param new_file The name of the new file, +-- 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) + os.remove(old_file) + return os.rename(new_file, old_file) +end + diff --git a/src/luarocks/fs/win32/tools.lua b/src/luarocks/fs/win32/tools.lua new file mode 100644 index 0000000..1af1dd9 --- /dev/null +++ b/src/luarocks/fs/win32/tools.lua @@ -0,0 +1,346 @@ + +--- 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) + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local cfg = require("luarocks.cfg") + +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. +-- @param filename string: The file name to strip. +-- @return string: The stripped name. +local function strip_extension(filename) + assert(type(filename) == "string") + + return (filename:gsub("%.[^.]+$", "")) or filename +end + +local function command_at(directory, cmd) + local drive = directory:match("^([A-Za-z]:)") + cmd = "cd " .. fs.Q(directory) .. " & " .. cmd + if drive then + cmd = drive .. " & " .. cmd + end + return cmd +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() + for _, directory in ipairs(dir_stack) do + current = fs.absolute_name(directory, current) + end + return current +end + +--- Run the given command. +-- The command is executed in the current directory in the directory stack. +-- @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) + 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 + return true + else + return false + end +end + +--- Change the current directory. +-- Uses the module's internal directory stack. This does not have exact +-- 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) + assert(type(directory) == "string") + table.insert(dir_stack, directory) +end + +--- Change directory to root. +-- Allows leaving a directory (e.g. for deleting it) in +-- a crossplatform way. +function change_dir_to_root() + table.insert(dir_stack, "/") +end + +--- Change working directory to the previous in the directory stack. +function pop_dir() + local directory = table.remove(dir_stack) + return directory ~= nil +end + +--- Create a directory if it does not already exist. +-- If any of the higher levels in the path name does 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) + assert(directory) + fs.execute(fs.quiet(vars.MKDIR.." "..fs.Q(directory))) + return 1 +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) + assert(directory) + fs.execute_string(fs.quiet(vars.RMDIR.." "..fs.Q(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) + assert(directory) + fs.execute_string(fs.quiet(vars.RMDIR.." "..fs.Q(directory))) +end + +--- Copy a file. +-- @param src string: Pathname of source +-- @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) + assert(src and dest) + if dest:match("[/\\]$") then dest = dest:sub(1, -2) end + if fs.execute(vars.CP, src, dest) then + return true + else + return false, "Failed copying "..src.." to "..dest + end +end + +--- Recursively copy the contents of a directory. +-- @param src string: Pathname of source +-- @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) + assert(src and dest) + if fs.execute_string(fs.quiet(vars.CP.." -dR "..src.."\\*.* "..fs.Q(dest))) then + return true + else + return false, "Failed copying "..src.." to "..dest + end +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) + 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))) +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)) + for file in pipe:lines() do + table.insert(result, file) + end + pipe:close() + + return result +end + +--- Recursively scan the contents of a directory. +-- @param at string or nil: directory to scan (will be the current +-- 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) + 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.FIND.." 2> NUL")) + for file in pipe:lines() do + -- Windows find is a bit different + local first_two = file:sub(1,2) + if first_two == ".\\" or first_two == "./" then file=file:sub(3) end + if file ~= "." then + table.insert(result, (file:gsub("\\", "/"))) + end + end + pipe:close() + return result +end + +--- Compress files in a .zip archive. +-- @param zipfile string: pathname of .zip archive to be created. +-- @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, ...) +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) + assert(zipfile) + return fs.execute(vars.SEVENZ.." 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) + assert(file) + return fs.execute(fs.quiet(vars.TEST.." -d " .. fs.Q(file))) +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) + assert(file) + return fs.execute(vars.TEST.." -f", file) +end + +--- 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) + assert(type(url) == "string") + assert(type(filename) == "string" or not filename) + + 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) + else + return fs.execute(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)) + end +end + +--- Uncompress gzip file. +-- @param archive string: Filename of archive. +-- @return boolean : success status +local function gunzip(archive) + return fs.execute(vars.SEVENZ.." x", archive) +end + +--- Unpack an archive. +-- Extract the contents of an archive, detecting its format by +-- 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) + assert(type(archive) == "string") + + local ok + local sevenzx = vars.SEVENZ.." x" + if archive:match("%.tar%.gz$") then + ok = gunzip(archive) + if ok then + ok = fs.execute(sevenzx, strip_extension(archive)) + end + elseif archive:match("%.tgz$") then + ok = gunzip(archive) + if ok then + ok = fs.execute(sevenzx, strip_extension(archive)..".tar") + end + elseif archive:match("%.tar%.bz2$") then + ok = fs.execute(sevenzx, archive) + if ok then + ok = fs.execute(sevenzx, strip_extension(archive)) + end + elseif archive:match("%.zip$") then + ok = fs.execute(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 + else + local ext = archive:match(".*(%..*)") + return false, "Unrecognized filename extension "..(ext or "") + end + if not ok then + return false, "Failed extracting "..archive + end + return true +end + +local md5_cmd = { + md5sum = vars.MD5SUM, + openssl = vars.OPENSSL.." md5", + md5 = 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) + local cmd = md5_cmd[cfg.md5checker] + if not cmd then return nil end + local pipe = io.popen(cmd.." "..fs.absolute_name(file)) + local computed = pipe:read("*a") + pipe:close() + if not computed then return nil end + return computed:match("("..("%x"):rep(32)..")") +end + +--- Test for existance of a file. +-- @param file string: filename to test +-- @return boolean: true if file exists, false otherwise. +function exists(file) + assert(file) + return fs.execute(fs.quiet("if not exist " .. fs.Q(file) .. " invalidcommandname")) +end diff --git a/src/luarocks/help.lua b/src/luarocks/help.lua new file mode 100644 index 0000000..2944dfd --- /dev/null +++ b/src/luarocks/help.lua @@ -0,0 +1,107 @@ + +--- Module implementing the LuaRocks "help" command. +-- This is a generic help display module, which +-- uses a global table called "commands" to find commands +-- to show help for; each command should be represented by a +-- table containing "help" and "help_summary" fields. +module("luarocks.help", package.seeall) + +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") + +local program = util.this_program("luarocks") + +help_summary = "Help on commands. Type '"..program.." help <command>' for more." + +help_arguments = "[<command>]" +help = [[ +<command> is the command to show help for. +]] + +local function print_banner() + util.printout("\nLuaRocks "..cfg.program_version..", a module deployment system for Lua") +end + +local function print_section(section) + util.printout("\n"..section) +end + +local function get_status(status) + if status then + return "ok" + elseif status == false then + return "not found" + else + return "failed" + end +end + +--- Driver function for the "help" command. +-- @param command string or nil: command to show help for; if not +-- given, help summaries for all commands are shown. +-- @return boolean or (nil, string): true if there were no errors +-- or nil and an error message if an invalid command was requested. +function run(...) + local flags, command = util.parse_flags(...) + + if not command then + local sys_file, sys_ok, home_file, home_ok = cfg.which_config() + print_banner() + print_section("NAME") + util.printout("\t"..program..[[ - ]]..program_description) + print_section("SYNOPSIS") + util.printout("\t"..program..[[ [--from=<server> | --only-from=<server>] [--to=<tree>] [VAR=VALUE]... <command> [<argument>] ]]) + print_section("GENERAL OPTIONS") + util.printout([[ + These apply to all commands, as appropriate: + + --server=<server> Fetch rocks/rockspecs from this server + (takes priority over config file) + --only-server=<server> Fetch rocks/rockspecs from this server only + (overrides any entries in the config file) + --only-sources=<url> Restrict downloads to paths matching the + 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'.]]) + 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] + util.printout("", name) + util.printout("\t", command.help_summary) + end + print_section("CONFIGURATION") + util.printout("\tSystem configuration file: ".. sys_file .. " (" .. get_status(sys_ok) ..")") + if home_file then + util.printout("\tUser configuration file: ".. home_file .. " (" .. get_status(home_ok) ..")\n") + else + util.printout("\tUser configuration file disabled in this LuaRocks installation.\n") + end + else + command = command:gsub("-", "_") + if commands[command] then + local arguments = commands[command].help_arguments or "<argument>" + print_banner() + print_section("NAME") + util.printout("\t"..program.." "..command.." - "..commands[command].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$",""))) + print_section("SEE ALSO") + util.printout("","'"..program.." help' for general options and configuration.\n") + else + return nil, "Unknown command '"..command.."'" + end + end + return true +end diff --git a/src/luarocks/index.lua b/src/luarocks/index.lua new file mode 100644 index 0000000..e1391cc --- /dev/null +++ b/src/luarocks/index.lua @@ -0,0 +1,172 @@ + +--- Module which builds the index.html page to be used in rocks servers. +module("luarocks.index", package.seeall) + +local util = require("luarocks.util") +local fs = require("luarocks.fs") +local deps = require("luarocks.deps") +local persist = require("luarocks.persist") +local dir = require("luarocks.dir") +local manif = require("luarocks.manif") + +local ext_url_target = ' target="_blank"' + +local index_header = [[ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> +<title>Available rocks</title> +<meta http-equiv="content-type" content="text/html; charset=iso-8859-1"> +<style> +body { + background-color: white; + font-family: "bitstream vera sans", "verdana", "sans"; + font-size: 14px; +} +a { + color: #0000c0; + text-decoration: none; +} +a:hover { + text-decoration: underline; +} +td.main { + border-style: none; +} +blockquote { + font-size: 12px; +} +td.package { + background-color: #f0f0f0; + vertical-align: top; +} +td.spacer { + height: 5px; +} +td.version { + background-color: #d0d0d0; + vertical-align: top; + text-align: left; + padding: 5px; + width: 100px; +} +p.manifest { + font-size: 8px; +} +</style> +</head> +<body> +<h1>Available rocks</h1> +<p> +Lua modules available from this location for use with <a href="http://www.luarocks.org">LuaRocks</a>: +</p> +<table class="main"> +]] + +local index_package_start = [[ +<td class="package"> +<p><a name="$anchor"></a><b>$package</b> - $summary<br/> +</p><blockquote><p>$detailed<br/> +$externaldependencies +<font size="-1"><a href="$original">latest sources</a> $homepage | License: $license</font></p> +</blockquote></a></td> +<td class="version"> +]] + +local index_package_end = [[ +</td></tr> +<tr><td colspan="2" class="spacer"></td></tr> +]] + +local index_footer = [[ +</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> +</p> +</body> +</html> +]] + +function format_external_dependencies(rockspec) + if rockspec.external_dependencies then + local deplist = {} + local listed_set = {} + local plats = nil + for name, desc in util.sortedpairs(rockspec.external_dependencies) do + if name ~= "platforms" then + table.insert(deplist, name:lower()) + listed_set[name] = true + else + plats = desc + end + end + if plats then + for plat, entries in util.sortedpairs(plats) do + for name, desc in util.sortedpairs(entries) do + if not listed_set[name] then + table.insert(deplist, name:lower() .. " (on "..plat..")") + end + end + end + end + return '<p><b>External dependencies:</b> ' .. table.concat(deplist, ', ').. '</p>' + else + return "" + end +end + +function make_index(repo) + if not fs.is_dir(repo) then + return nil, "Cannot access repository at "..repo + end + local manifest = manif.load_manifest(repo) + local out = io.open(dir.path(repo, "index.html"), "w") + + out:write(index_header) + for package, version_list in util.sortedpairs(manifest.repository) do + local latest_rockspec = nil + local output = index_package_start + 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>' + 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) + else + link = link:gsub("$url", ("%s-%s.%s.rock"):format(package, version, item.arch)) + end + table.insert(versions, link) + end + output = output .. table.concat(versions, ', ') .. '<br/>' + end + output = output .. index_package_end + if latest_rockspec then + local rockspec = persist.load_into_table(dir.path(repo, latest_rockspec)) + local descript = rockspec.description or {} + local vars = { + anchor = package, + package = rockspec.package, + original = rockspec.source.url, + summary = descript.summary or "", + 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) + } + 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>') + output = output:gsub("$(%w+)", vars) + else + output = output:gsub("$anchor", package) + output = output:gsub("$package", package) + output = output:gsub("$(%w+)", "") + end + out:write(output) + end + out:write(index_footer) + out:close() +end diff --git a/src/luarocks/install.lua b/src/luarocks/install.lua new file mode 100644 index 0000000..c181d61 --- /dev/null +++ b/src/luarocks/install.lua @@ -0,0 +1,164 @@ + +--- Module implementing the LuaRocks "install" command. +-- Installs binary rocks. +module("luarocks.install", package.seeall) + +local path = require("luarocks.path") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local util = require("luarocks.util") +local fs = require("luarocks.fs") +local deps = require("luarocks.deps") +local manif = require("luarocks.manif") +local remove = require("luarocks.remove") +local cfg = require("luarocks.cfg") + +help_summary = "Install a rock." + +help_arguments = "{<rock>|<name> [<version>]}" + +help = [[ +Argument may be the name of a rock to be fetched from a repository +or a filename of a locally available rock. + +--keep Do not remove previously installed versions of the + rock after installing a new one. This behavior can + be made permanent by setting keep_other_versions=true + in the configuration file. +]] + +--- Install a binary rock. +-- @param rock_file string: local or remote filename of a rock. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @return (string, string) or (nil, string, [string]): Name and version of +-- installed rock if succeeded or nil and an error message followed by an error code. +function install_binary_rock(rock_file, deps_mode) + assert(type(rock_file) == "string") + + local name, version, arch = path.parse_name(rock_file) + if not name then + return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." + end + + if arch ~= "all" and arch ~= cfg.arch then + return nil, "Incompatible architecture "..arch, "arch" + end + if repos.is_installed(name, version) then + repos.delete_version(name, version) + end + + local rollback = util.schedule_function(function() + fs.delete(path.install_dir(name, version)) + fs.remove_dir_if_empty(path.versions_dir(name)) + end) + + local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) + if not ok then return nil, err, errcode end + + local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version)) + if err then + return nil, "Failed loading rockspec for installed package: "..err, errcode + end + + if deps_mode == "none" then + util.printerr("Warning: skipping dependency checks.") + else + ok, err, errcode = deps.check_external_deps(rockspec, "install") + if err then return nil, err, errcode end + end + + -- For compatibility with .rock files built with LuaRocks 1 + if not fs.exists(path.rock_manifest_file(name, version)) then + ok, err = manif.make_rock_manifest(name, version) + if err then return nil, err end + end + + if deps_mode ~= "none" then + ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) + if err then return nil, err, errcode end + end + + 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 + + util.remove_scheduled_function(rollback) + rollback = util.schedule_function(function() + repos.delete_version(name, version) + end) + + ok, err = repos.run_hook(rockspec, "post_install") + if err then return nil, err end + + ok, err = manif.update_manifest(name, version, nil, deps_mode) + if err then return nil, err end + + local license = "" + if rockspec.description.license then + license = ("(license: "..rockspec.description.license..")") + end + + local root_dir = path.root_dir(cfg.rocks_dir) + util.printout() + util.printout(name.." "..version.." is now installed in "..root_dir.." "..license) + + util.remove_scheduled_function(rollback) + return name, version +end + +--- Driver function for the "install" command. +-- @param name string: name of a binary rock. If an URL or pathname +-- to a binary rock is given, fetches and installs it. If a rockspec or a +-- source rock is given, forwards the request to the "build" command. +-- If a package name is given, forwards the request to "search" and, +-- if returned a result, installs the matching rock. +-- @param version string: When passing a package name, a version number +-- may also be given. +-- @return boolean or (nil, string): True if installation was +-- successful, nil and an error message otherwise. +function 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 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)) + if not ok then return nil, err end + local name, version = ok, err + if (not flags["keep"]) and not cfg.keep_other_versions then + local ok, err = remove.remove_other_versions(name, version, flags["force"]) + if not ok then util.printerr(err) end + end + return name, version + else + local search = require("luarocks.search") + local results, err = search.find_suitable_rock(search.make_query(name:lower(), version)) + if err then + return nil, err + elseif type(results) == "string" then + local url = results + util.printout("Installing "..url.."...") + return run(url, util.forward_flags(flags)) + else + util.printout() + util.printerr("Could not determine which rock to install.") + util.title("Search results:") + search.print_results(results) + return nil, (next(results) and "Please narrow your query." or "No results found.") + end + end +end diff --git a/src/luarocks/lint.lua b/src/luarocks/lint.lua new file mode 100644 index 0000000..62ced64 --- /dev/null +++ b/src/luarocks/lint.lua @@ -0,0 +1,41 @@ + +--- Module implementing the LuaRocks "lint" command. +-- Utility function that checks syntax of the rockspec. +module("luarocks.lint", package.seeall) + +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 = [[ +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(...) + local flags, input = util.parse_flags(...) + + if not input then + return nil, "Argument missing. "..util.see_help("lint") + end + + local filename = input + if not input:match(".rockspec$") then + local err + filename, err = download.download("rockspec", input) + if not filename then + return nil, err + end + end + + local rs, err = fetch.load_local_rockspec(filename) + if not rs then + return nil, "Failed loading rockspec: "..err + end + + return true +end diff --git a/src/luarocks/list.lua b/src/luarocks/list.lua new file mode 100644 index 0000000..6081ed4 --- /dev/null +++ b/src/luarocks/list.lua @@ -0,0 +1,38 @@ + +--- Module implementing the LuaRocks "list" command. +-- Lists currently installed rocks. +module("luarocks.list", package.seeall) + +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 = [[ +<filter> is a substring of a rock name to filter by. + +--porcelain Produce machine-friendly output. +]] + +--- Driver function for "list" command. +-- @param filter string or nil: A substring of a rock name to filter by. +-- @param version string or nil: a version may also be passed. +-- @return boolean: True if succeeded, nil on errors. +function run(...) + local flags, filter, version = util.parse_flags(...) + local results = {} + local query = search.make_query(filter and filter:lower() or "", version) + query.exact_name = false + local trees = cfg.rocks_trees + if flags["tree"] then + trees = { flags["tree"] } + end + for _, tree in ipairs(trees) do + search.manifest_search(results, path.rocks_dir(tree), query) + end + util.title("Installed rocks:", flags["porcelain"]) + search.print_results(results, flags["porcelain"]) + return true +end diff --git a/src/luarocks/loader.lua b/src/luarocks/loader.lua new file mode 100644 index 0000000..a116766 --- /dev/null +++ b/src/luarocks/loader.lua @@ -0,0 +1,212 @@ + +--- A module which installs a Lua package loader that is LuaRocks-aware. +-- This loader uses dependency information from the LuaRocks tree to load +-- correct versions of modules. It does this by constructing a "context" +-- 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 + +module("luarocks.loader") + +local path = require("luarocks.path") +local manif_core = require("luarocks.manif_core") +local deps = require("luarocks.deps") +local cfg = require("luarocks.cfg") + +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 + +local function load_rocks_trees() + local any_ok = false + local trees = {} + for _, tree in ipairs(cfg.rocks_trees) do + local manifest, err = manif_core.load_local_manifest(path.rocks_dir(tree)) + if manifest then + any_ok = true + table.insert(trees, {tree=tree, manifest=manifest}) + end + end + if not any_ok then + rocks_trees = false + return false + end + rocks_trees = trees + return true +end + +--- Process the dependencies of a package to determine its dependency +-- 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) + -- assert(type(name) == "string") + -- assert(type(version) == "string") + + if context[name] then + return + end + context[name] = version + + if not rocks_trees and not load_rocks_trees() then + return nil + end + + local providers = {} + for _, tree in ipairs(rocks_trees) do + local manifest = tree.manifest + + local pkgdeps + if manifest.dependencies and manifest.dependencies[name] then + pkgdeps = manifest.dependencies[name][version] + end + if not pkgdeps then + return nil + end + for _, dep in ipairs(pkgdeps) do + local pkg, constraints = dep.name, dep.constraints + + for _, tree in ipairs(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) + end + end + end + end + end + end +end + +--- Internal sorting function. +-- @param a table: A provider table. +-- @param b table: Another provider table. +-- @return boolean: True if the version of a is greater than that of b. +local function sort_versions(a,b) + return a.version > b.version +end + +--- Request module to be loaded through other loaders, +-- once the proper name of the module has been determined. +-- For example, in case the module "socket.core" has been requested +-- to the LuaRocks loader and it determined based on context that +-- the version 2.0.2 needs to be loaded and it is not the current +-- version, the module requested for the other loaders will be +-- "socket.core_2_0_2". +-- @param module The module name requested by the user, such as "socket.core" +-- @param name The rock name, such as "luasocket" +-- @param version The rock version, such as "2.0.2-1" +-- @param module_name The actual module name, such as "socket.core" or "socket.core_2_0_2". +-- @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) } + if type(results[1]) == "function" then + return unpack(results) + end + end + end + return "Failed loading module "..module.." in LuaRocks rock "..name.." "..version +end + +--- Search for a module in the rocks trees +-- @param module string: module name (eg. "socket.core") +-- @param filter_module_name function(string, string, string, string, number): +-- a function that takes the module name (eg "socket.core"), the rock name +-- (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). +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 + return nil + end + + local providers = {} + for _, tree in ipairs(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)..")") + end + module_name = filter_module_name(module_name, name, version, tree.tree, i) + if 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}) + end + end + end + + if next(providers) then + table.sort(providers, sort_versions) + local first = providers[1] + return first.name, first.version.string, first.module_name + 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). +local function pick_module(module) + return + select_module(module, function(module_name, name, version, tree, i) + if i > 1 then + module_name = path.versioned_name(module_name, "", name, version) + end + module_name = path.path_to_module(module_name) + return module_name + end) +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) + local name, version, module_name = select_module(module, path.which_i) + return module_name +end + +--- Package loader for LuaRocks support. +-- A module is searched in installed rocks that match the +-- current LuaRocks context. If module is not part of the +-- context, or if a context has not yet been set, the module +-- in the package with the highest version is used. +-- @param module string: The module name, like in plain require(). +-- @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) + local name, version, module_name = pick_module(module) + if not name then + return "No LuaRocks module found for "..module + else + add_context(name, version) + return call_other_loaders(module, name, version, module_name) + end +end + +table.insert(global_env.package.loaders, 1, luarocks_loader) diff --git a/src/luarocks/make.lua b/src/luarocks/make.lua new file mode 100644 index 0000000..eef49d0 --- /dev/null +++ b/src/luarocks/make.lua @@ -0,0 +1,86 @@ + +--- Module implementing the LuaRocks "make" command. +-- Builds sources in the current directory, but unlike "build", +-- it does not fetch sources, etc., assuming everything is +-- available in the current directory. +module("luarocks.make", package.seeall) + +local build = require("luarocks.build") +local fs = require("luarocks.fs") +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") +local fetch = require("luarocks.fetch") +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 = [[ +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, +look for a rockspec in the current directory. If more than one +is found, you must specify which to use, through the command-line. + +This command is useful as a tool for debugging rockspecs. +To install rocks, you'll normally want to use the "install" and +"build" commands. See the help on those for details. + +--pack-binary-rock Do not install rock. Instead, produce a .rock file + with the contents of compilation in the current + directory. + +--keep Do not remove previously installed versions of the + rock after installing a new one. This behavior can + be made permanent by setting keep_other_versions=true + in the configuration file. + +]] + +--- 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(...) + 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 + if file:match("rockspec$") then + if rockspec then + return nil, "Please specify which rockspec file to use." + else + rockspec = file + end + end + end + if not rockspec then + return nil, "Argument missing: please specify a rockspec to use on current directory." + end + end + if not rockspec:match("rockspec$") then + return nil, "Invalid argument: 'make' takes a rockspec as a parameter. "..util.see_help("make") + end + + if flags["pack-binary-rock"] then + local rspec, err, errcode = fetch.load_rockspec(rockspec) + if not rspec then + return nil, err + end + return pack.pack_binary_rock(rspec.name, rspec.version, build.build_rockspec, rockspec, false, true, deps.get_deps_mode(flags)) + else + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err end + ok, err = build.build_rockspec(rockspec, false, true, deps.get_deps_mode(flags)) + if not ok then return nil, err end + local name, version = ok, err + if (not flags["keep"]) and not cfg.keep_other_versions then + local ok, err = remove.remove_other_versions(name, version, flags["force"]) + if not ok then util.printerr(err) end + end + return name, version + end +end diff --git a/src/luarocks/make_manifest.lua b/src/luarocks/make_manifest.lua new file mode 100644 index 0000000..a698f83 --- /dev/null +++ b/src/luarocks/make_manifest.lua @@ -0,0 +1,40 @@ + +--- Module implementing the luarocks-admin "make_manifest" command. +-- Compile a manifest file for a repository. +module("luarocks.make_manifest", package.seeall) + +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") + +help_summary = "Compile a manifest file for a repository." + +help = [[ +<argument>, if given, is a local repository pathname. + +--local-tree If given, do not write versioned versions of the manifest file. + Use this when rebuilding the manifest of a local rocks tree. +]] + +--- Driver function for "make_manifest" command. +-- @param repo string or nil: Pathname of a local repository. If not given, +-- the default local repository configured as cfg.rocks_dir is used. +-- @return boolean or (nil, string): True if manifest was generated, +-- or nil and an error message. +function run(...) + local flags, repo = util.parse_flags(...) + + assert(type(repo) == "string" or not repo) + repo = repo or cfg.rocks_dir + + util.printout("Making manifest for "..repo) + + 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 + return ok, err +end diff --git a/src/luarocks/manif.lua b/src/luarocks/manif.lua new file mode 100644 index 0000000..c17da71 --- /dev/null +++ b/src/luarocks/manif.lua @@ -0,0 +1,452 @@ + +--- 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) + +local manif_core = require("luarocks.manif_core") +local persist = require("luarocks.persist") +local fetch = require("luarocks.fetch") +local dir = require("luarocks.dir") +local fs = require("luarocks.fs") +local search = require("luarocks.search") +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") +local path = require("luarocks.path") +local repos = require("luarocks.repos") +local deps = require("luarocks.deps") + +rock_manifest_cache = {} + +--- Commit a table to disk in given local path. +-- @param where string: The directory where the table should be saved. +-- @param name string: The filename. +-- @param tbl table: The table to be saved. +-- @return boolean or (nil, string): true if successful, or nil and a +-- message in case of errors. +local function save_table(where, name, tbl) + assert(type(where) == "string") + assert(type(name) == "string") + assert(type(tbl) == "table") + + local filename = dir.path(where, name) + local ok, err = persist.save_from_table(filename..".tmp", tbl) + if ok then + ok, err = fs.replace_file(filename, filename..".tmp") + end + return ok, err +end + +function 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 + 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 + return rock_manifest.rock_manifest +end + +function make_rock_manifest(name, version) + local install_dir = path.install_dir(name, version) + local rock_manifest = path.rock_manifest_file(name, version) + local tree = {} + for _, file in ipairs(fs.find(install_dir)) do + local full_path = dir.path(install_dir, file) + local walk = tree + local last + local last_name + for name in file:gmatch("[^/]+") do + local next = walk[name] + if not next then + next = {} + walk[name] = next + end + last = walk + last_name = name + walk = next + end + if fs.is_file(full_path) then + last[last_name] = fs.get_md5(full_path) + end + end + local rock_manifest = { rock_manifest=tree } + 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) + if not file then + return nil, "Failed fetching manifest for "..repo_url..(err and " - "..err or ""), errcode + end + return file +end + +--- Load a local or remote manifest describing a repository. +-- All functions that use manifest tables assume they were obtained +-- through either this function or load_local_manifest. +-- @param repo_url string: URL or pathname for the repository. +-- @return table or (nil, string, [string]): A table representing the manifest, +-- or nil followed by an error message and an optional error code. +function 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) + if protocol == "file" then + local file = dir.path(pathname, vmanifest) + if fs.exists(file) then + pathname = file + else + pathname = dir.path(pathname, "manifest") + end + else + local file, err = fetch_manifest_from(repo_url, vmanifest) + if not file then + file, err = fetch_manifest_from(repo_url, "manifest") + end + if not file then + return nil, err + end + pathname = file + end + return manif_core.manifest_loader(pathname, repo_url) +end + +--- Output a table listing items of a package. +-- @param itemsfn function: a function for obtaining items of a package. +-- pkg and version will be passed to it; it should return a table with +-- items as keys. +-- @param pkg string: package name +-- @param version string: package version +-- @param tbl table: the package matching table: keys should be item names +-- and values arrays of strings with packages names in "name/version" format. +local function store_package_items(itemsfn, pkg, version, tbl) + assert(type(itemsfn) == "function") + assert(type(pkg) == "string") + assert(type(version) == "string") + assert(type(tbl) == "table") + + local pkg_version = pkg.."/"..version + local result = {} + + for item, path in pairs(itemsfn(pkg, version)) do + result[item] = path + if not tbl[item] then + tbl[item] = {} + end + table.insert(tbl[item], pkg_version) + end + return result +end + +--- Sort function for ordering rock identifiers in a manifest's +-- modules table. Rocks are ordered alphabetically by name, and then +-- by version which greater first. +-- @param a string: Version to compare. +-- @param b string: Version to compare. +-- @return boolean: The comparison result, according to the +-- rule outlined above. +local function sort_pkgs(a, b) + assert(type(a) == "string") + assert(type(b) == "string") + + local na, va = a:match("(.*)/(.*)$") + local nb, vb = b:match("(.*)/(.*)$") + + return (na == nb) and deps.compare_versions(va, vb) or na < nb +end + +--- Sort items of a package matching table by version number (higher versions first). +-- @param tbl table: the package matching table: keys should be strings +-- and values arrays of strings with packages names in "name/version" format. +local function sort_package_matching_table(tbl) + assert(type(tbl) == "table") + + if next(tbl) then + for item, pkgs in pairs(tbl) do + if #pkgs > 1 then + table.sort(pkgs, sort_pkgs) + -- Remove duplicates from the sorted array. + local prev = nil + local i = 1 + while pkgs[i] do + local curr = pkgs[i] + if curr == prev then + table.remove(pkgs, i) + else + prev = curr + i = i + 1 + end + end + end + end + end +end + +--- Process the dependencies of a manifest table to determine its dependency +-- chains for loading modules. The manifest dependencies information is filled +-- and any dependency inconsistencies or missing dependencies are reported to +-- standard error. +-- @param manifest table: a manifest table. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for no trees. +-- @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) + 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 + if repo.arch == "installed" then + local missing + repo.dependencies, missing = deps.scan_deps({}, {}, manifest, pkg, version, deps_mode) + repo.dependencies[pkg] = nil + if missing then + for miss, err in pairs(missing) do + if miss == current then + util.printerr("Tree inconsistency detected: "..current.." has no rockspec. "..err) + else + util.printerr("Missing dependency for "..pkg.." "..version..": "..miss) + end + end + end + elseif filter_lua and 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) + end + if rockspec then + cache[pathname] = rockspec + for _, dep in ipairs(rockspec.dependencies) do + if dep.name == "lua" then + if not deps.match_constraints(lua_version, dep.constraints) then + table.insert(to_remove, version) + end + break + end + end + else + util.printerr("Error loading rockspec for "..pkg.." "..version..": "..err) + end + end + end + end + if next(to_remove) then + for _, incompat in ipairs(to_remove) do + manifest.repository[pkg][incompat] = nil + end + if not next(manifest.repository[pkg]) then + manifest.repository[pkg] = nil + end + end + end +end + +--- Store search results in a manifest table. +-- @param results table: The search results as returned by search.disk_search. +-- @param manifest table: A manifest table (must contain repository, modules, commands tables). +-- It will be altered to include the search results. +-- @param 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 +-- @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) + assert(type(results) == "table") + assert(type(manifest) == "table") + assert(type(deps_mode) == "string") + + for name, versions in pairs(results) do + local pkgtable = manifest.repository[name] or {} + for version, entries in pairs(versions) do + local versiontable = {} + for _, entry in ipairs(entries) do + local entrytable = {} + entrytable.arch = entry.arch + if entry.arch == "installed" then + local rock_manifest = load_rock_manifest(name, version) + if not rock_manifest then + return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?" + end + entrytable.modules = store_package_items(repos.package_modules, name, version, manifest.modules) + entrytable.commands = store_package_items(repos.package_commands, name, version, manifest.commands) + end + table.insert(versiontable, entrytable) + end + pkgtable[version] = versiontable + end + manifest.repository[name] = pkgtable + end + update_dependencies(manifest, deps_mode, repo, filter_lua, cache) + sort_package_matching_table(manifest.modules) + sort_package_matching_table(manifest.commands) + return true +end + +--- Scan a LuaRocks repository and output a manifest file. +-- A file called 'manifest' will be written in the root of the given +-- repository directory. +-- @param repo A local repository directory. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for the default dependency mode from the configuration. +-- @param versioned boolean: if versioned versions of the manifest should be created. +-- @return boolean or (nil, string): True if manifest was generated, +-- or nil and an error message. +function make_manifest(repo, deps_mode, versioned) + assert(type(repo) == "string") + assert(type(deps_mode) == "string") + + if deps_mode == "none" then deps_mode = cfg.deps_mode end + + if not fs.is_dir(repo) then + return nil, "Cannot access repository at "..repo + end + + local query = search.make_query("") + query.exact_name = false + query.arch = "any" + local results = search.disk_search(repo, query) + local manifest = { repository = {}, modules = {}, commands = {} } + + manif_core.manifest_cache[repo] = manifest + + local cache = {} + local ok, err = store_results(results, manifest, deps_mode, repo, nil, cache) + if not ok then return nil, err end + + if versioned then + for _, luaver in ipairs({"5.1", "5.2"}) do + local vmanifest = { repository = {}, modules = {}, commands = {} } + local ok, err = store_results(results, vmanifest, deps_mode, repo, luaver, cache) + save_table(repo, "manifest-"..luaver, vmanifest) + end + end + + return save_table(repo, "manifest", manifest) +end + +--- Load a manifest file from a local repository and add to the repository +-- information with regard to the given name and version. +-- A file called 'manifest' will be written in the root of the given +-- repository directory. +-- @param name string: Name of a package from the repository. +-- @param version string: Version of a package from the repository. +-- @param repo string or nil: Pathname of a local repository. If not given, +-- the default local repository configured as cfg.rocks_dir 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) + assert(type(name) == "string") + assert(type(version) == "string") + repo = path.rocks_dir(repo or cfg.root_dir) + assert(type(deps_mode) == "string") + + if deps_mode == "none" then deps_mode = cfg.deps_mode end + + util.printout("Updating manifest for "..repo) + + local manifest, err = load_manifest(repo) + if not manifest then + util.printerr("No existing manifest. Attempting to rebuild...") + local ok, err = make_manifest(repo, deps_mode) + if not ok then + return nil, err + end + manifest, err = load_manifest(repo) + if not manifest then + return nil, err + end + end + + local results = {[name] = {[version] = {{arch = "installed", repo = repo}}}} + + local ok, err = store_results(results, manifest, deps_mode, repo) + if not ok then return nil, err end + + return save_table(repo, "manifest", manifest) +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?" + end + local deploy_bin = path.deploy_bin_dir(root) + local deploy_lua = path.deploy_lua_dir(root) + local deploy_lib = path.deploy_lib_dir(root) + local key, manifest_tbl + + if util.starts_with(file, deploy_lua) then + manifest_tbl = manifest.modules + key = path.path_to_module(file:sub(#deploy_lua+1):gsub("\\", "/")) + elseif util.starts_with(file, deploy_lib) then + manifest_tbl = manifest.modules + key = path.path_to_module(file:sub(#deploy_lib+1):gsub("\\", "/")) + elseif util.starts_with(file, deploy_bin) then + manifest_tbl = manifest.commands + key = file:sub(#deploy_bin+1):gsub("^[\\/]*", "") + else + assert(false, "Assertion failed: '"..file.."' is not a deployed file.") + end + + local providers = manifest_tbl[key] + if not providers then + return nil, "untracked" + end + return providers +end + +--- Given a path of a deployed file, figure out which rock name and version +-- correspond to it in the tree manifest. +-- @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) + 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) + local providers, err = find_providers(file, root) + if not providers then return nil, err end + if providers[2] then + return providers[2]:match("([^/]*)/([^/]*)") + else + return nil + end +end diff --git a/src/luarocks/manif_core.lua b/src/luarocks/manif_core.lua new file mode 100644 index 0000000..6424a1e --- /dev/null +++ b/src/luarocks/manif_core.lua @@ -0,0 +1,79 @@ + +--- Core functions for querying manifest files. +-- This module requires no specific 'fs' functionality. +module("luarocks.manif_core", package.seeall) + +local persist = require("luarocks.persist") +local type_check = require("luarocks.type_check") +local dir = require("luarocks.dir") +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") +local path = require("luarocks.path") + +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) + local manifest, err = persist.load_into_table(file) + if not manifest then + return nil, "Failed loading manifest for "..repo_url..": "..err + end + if not quick then + local ok, err = type_check.type_check_manifest(manifest) + if not ok then + return nil, "Error checking manifest: "..err + end + end + + manifest_cache[repo_url] = manifest + return manifest +end + +--- Load a local manifest describing a repository. +-- All functions that use manifest tables assume they were obtained +-- through either this function or load_manifest. +-- @param repo_url string: URL or pathname for the repository. +-- @return table or (nil, string): A table representing the manifest, +-- or nil followed by an error message. +function load_local_manifest(repo_url) + assert(type(repo_url) == "string") + + if manifest_cache[repo_url] then + return manifest_cache[repo_url] + end + + local pathname = dir.path(repo_url, "manifest") + + return manifest_loader(pathname, repo_url, true) +end + +--- Get all versions of a package listed in a manifest file. +-- @param name string: a package name. +-- @param deps_mode string: "one", to use only the currently +-- configured tree; "order" to select trees based on order +-- (use the current tree and all trees below it on the list) +-- or "all", to use all trees. +-- @return table: An array of strings listing installed +-- versions of a package. +function 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)) + if loaded then + util.deep_merge(manifest, loaded) + end + end) + + local item = next(manifest) and manifest.repository[name] + if item then + return util.keys(item) + end + return {} +end diff --git a/src/luarocks/new_version.lua b/src/luarocks/new_version.lua new file mode 100644 index 0000000..cb17032 --- /dev/null +++ b/src/luarocks/new_version.lua @@ -0,0 +1,178 @@ + +--- 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) + +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") + +help_summary = "Auto-write a rockspec for a new version of a rock." +help_arguments = "{<package>|<rockspec>} [<new_version>] [<new_url>]" +help = [[ +This is a utility function that writes a new rockspec, updating data +from a previous one. + +If a package name is given, it downloads the latest rockspec from the +default server. If a rockspec is given, it uses it instead. + +If the version number is not given, it only increments the revision +number of the given (or downloaded) rockspec. + +If a URL is given, it replaces the one from the old rockspec with the +given URL. If a URL is not given and a new version is given, it tries +to guess the new URL by replacing occurrences of the version number +in the URL or tag. It also tries to download the new URL to determine +the new MD5 checksum. + +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 + end + local old_field = tbl[field] + local new_field = tbl[field]:gsub(old, new) + if new_field ~= old_field then + util.printout("Guessing new '"..field.."' field as "..new_field) + tbl[field] = new_field + return true + end + return false +end + +local function check_url_and_update_md5(out_rs, out_name) + local old_md5 = out_rs.source.md5 + out_rs.source.md5 = nil + local file, temp_dir = fetch.fetch_url_at_temp_dir(out_rs.source.url, "luarocks-new-version-"..out_name) + if not file then + util.printerr("Warning: invalid URL - "..temp_dir) + return true + 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 + end + fs.pop_dir() + return out_rs.source.md5 ~= old_md5 +end + +local function update_source_section(out_rs, out_name, url, old_ver, new_ver) + if url then + out_rs.source.url = url + check_url_and_update_md5(out_rs, out_name) + return true + end + if new_ver == old_ver then + return true + end + if not out_rs.source then + return nil, "'source' table is missing. Invalid rockspec?" + end + if out_rs.source.dir then + try_replace(out_rs.source, "dir", old_ver, new_ver) + end + if out_rs.source.file then + try_replace(out_rs.source, "file", old_ver, new_ver) + end + local ok = try_replace(out_rs.source, "url", old_ver, new_ver) + if ok then + check_url_and_update_md5(out_rs, out_name) + return true + end + ok = try_replace(out_rs.source, "tag", old_ver, new_ver) + if not ok then + ok = check_url_and_update_md5(out_rs, out_name) + if ok then + util.printerr("Warning: URL is the same, but MD5 has changed. Old rockspec is broken.") + end + end + if not ok then + return nil, "Failed to determine the location of the new version." + end + return true +end + +function 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") + end + assert(type(input) == "string") + + local filename = input + if not input:match(".rockspec$") then + local err + filename, err = download.download("rockspec", input) + if not filename then + return nil, err + end + end + + local valid_rs, err = fetch.load_rockspec(filename) + if not valid_rs then + return nil, err + end + + local old_ver, old_rev = valid_rs.version:match("(.*)%-(%d+)$") + local new_ver, new_rev + + if version then + new_ver, new_rev = version:match("(.*)%-(%d+)$") + new_rev = tonumber(new_rev) + if not new_rev then + new_ver = version + new_rev = 1 + end + else + new_ver = old_ver + new_rev = tonumber(old_rev) + 1 + end + local new_rockver = new_ver:gsub("-", "") + + local out_rs = persist.load_into_table(filename) + local out_name = out_rs.package:lower() + out_rs.version = new_rockver.."-"..new_rev + + local ok, err = update_source_section(out_rs, out_name, url, old_ver, new_ver) + if not ok then return nil, err end + + if out_rs.build and out_rs.build.type == "module" then + out_rs.build.type = "builtin" + end + + local out_filename = out_name.."-"..new_rockver.."-"..new_rev..".rockspec" + + persist.save_from_table(out_filename, out_rs, order) + + util.printout("Wrote "..out_filename) + + local valid_out_rs, err = fetch.load_local_rockspec(out_filename) + if not valid_out_rs then + return nil, "Failed loading generated rockspec: "..err + end + + return true +end diff --git a/src/luarocks/pack.lua b/src/luarocks/pack.lua new file mode 100644 index 0000000..583932e --- /dev/null +++ b/src/luarocks/pack.lua @@ -0,0 +1,204 @@ + +--- Module implementing the LuaRocks "pack" command. +-- Creates a rock, packing sources or binaries. +module("luarocks.pack", package.seeall) + +local path = require("luarocks.path") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local cfg = require("luarocks.cfg") +local util = require("luarocks.util") +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 = [[ +Argument may be a rockspec file, for creating a source rock, +or the name of an installed package, for creating a binary rock. +In the latter case, the app version may be given as a second +argument. +]] + +--- Create a source rock. +-- Packages a rockspec and its required source files in a rock +-- file with the .src.rock extension, which can later be built and +-- installed with the "build" command. +-- @param rockspec_file string: An URL or pathname for a rockspec file. +-- @return string or (nil, string): The filename of the resulting +-- .src.rock file; or nil and an error message. +local function pack_source_rock(rockspec_file) + assert(type(rockspec_file) == "string") + + local rockspec, err = fetch.load_rockspec(rockspec_file) + if err then + return nil, "Error loading rockspec: "..err + end + rockspec_file = rockspec.local_filename + + local name_version = rockspec.name .. "-" .. rockspec.version + local rock_file = fs.absolute_name(name_version .. ".src.rock") + + local source_file, source_dir = fetch.fetch_sources(rockspec, false) + if not source_file then + return nil, source_dir + end + fs.change_dir(source_dir) + + fs.delete(rock_file) + fs.copy(rockspec_file, source_dir) + if not fs.zip(rock_file, dir.base_name(rockspec_file), dir.base_name(source_file)) then + return nil, "Failed packing "..rock_file + end + fs.pop_dir() + + return rock_file +end + +local function copy_back_files(name, version, file_tree, deploy_dir, pack_dir) + fs.make_dir(pack_dir) + 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) + else + local versioned = path.versioned_name(source, deploy_dir, name, version) + if fs.exists(versioned) then + fs.copy(versioned, target) + else + fs.copy(source, target) + end + end + end + return true +end + +-- @param name string: Name of package to pack. +-- @param version string or nil: A version number may also be passed. +-- @return string or (nil, string): The filename of the resulting +-- .src.rock file; or nil and an error message. +local function do_pack_binary_rock(name, version) + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local query = search.make_query(name, version) + query.exact_name = true + local results = {} + + search.manifest_search(results, cfg.rocks_dir, query) + + if not next(results) then + return nil, "'"..name.."' does not seem to be an installed rock." + end + + local versions = results[name] + + if not version then + local first = next(versions) + if next(versions, first) then + return nil, "Please specify which version of '"..name.."' to pack." + end + version = first + end + if not version:match("[^-]+%-%d+") then + return nil, "Expected version "..version.." in version-revision format." + end + + local info = versions[version][1] + + local root = path.root_dir(info.repo) + local prefix = path.install_dir(name, version, root) + if not fs.exists(prefix) then + return nil, "'"..name.." "..version.."' does not seem to be an installed rock." + end + + local rock_manifest = manif.load_rock_manifest(name, version, root) + if not rock_manifest then + return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?" + end + + local name_version = name .. "-" .. version + local rock_file = fs.absolute_name(name_version .. "."..cfg.arch..".rock") + + local temp_dir = fs.make_temp_dir("pack") + fs.copy_contents(prefix, temp_dir) + + local is_binary = false + if rock_manifest.lib then + copy_back_files(name, version, rock_manifest.lib, path.deploy_lib_dir(root), dir.path(temp_dir, "lib")) + 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")) + end + + fs.change_dir(temp_dir) + if not is_binary and not repos.has_binaries(name, version) then + rock_file = rock_file:gsub("%."..cfg.arch:gsub("%-","%%-").."%.", ".all.") + end + fs.delete(rock_file) + if not fs.zip(rock_file, unpack(fs.list_dir())) then + return nil, "Failed packing "..rock_file + end + fs.pop_dir() + fs.delete(temp_dir) + return rock_file +end + +function pack_binary_rock(name, version, cmd, ...) + + -- The --pack-binary-rock option for "luarocks build" basically performs + -- "luarocks build" on a temporary tree and then "luarocks pack". The + -- alternative would require refactoring parts of luarocks.build and + -- luarocks.pack, which would save a few file operations: the idea would be + -- to shave off the final deploy steps from the build phase and the initial + -- collect steps from the pack phase. + + local temp_dir = fs.make_temp_dir("luarocks-build-pack-"..dir.base_name(name)) + if not temp_dir then + return nil, "Failed creating temporary directory." + end + util.schedule_function(fs.delete, temp_dir) + + path.use_tree(temp_dir) + local ok, err = cmd(...) + if not ok then + return nil, err + end + local rname, rversion = path.parse_name(name) + if not rname then + rname, rversion = name, version + end + return do_pack_binary_rock(rname, rversion) +end + +--- Driver function for the "pack" command. +-- @param arg string: may be a rockspec file, for creating a source rock, +-- or the name of an installed package, for creating a binary rock. +-- @param version string or nil: if the name of a package is given, a +-- version may also be passed. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +function run(...) + local flags, arg, version = util.parse_flags(...) + assert(type(version) == "string" or not version) + if type(arg) ~= "string" then + return nil, "Argument missing. "..util.see_help("pack") + end + + local file, err + if arg:match(".*%.rockspec") then + file, err = pack_source_rock(arg) + else + file, err = do_pack_binary_rock(arg, version) + end + if err then + return nil, err + else + util.printout("Packed: "..file) + return true + end +end diff --git a/src/luarocks/path.lua b/src/luarocks/path.lua new file mode 100644 index 0000000..7af81c0 --- /dev/null +++ b/src/luarocks/path.lua @@ -0,0 +1,417 @@ + +--- 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) + +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) + assert(type(rock_name) == "string") + local base_name = dir.base_name(rock_name) + return base_name:match("(.*)%.[^.]*.rock") .. ".rockspec" +end + +function rocks_dir(tree) + if type(tree) == "string" then + return dir.path(tree, cfg.rocks_subdir) + else + assert(type(tree) == "table") + return tree.rocks_dir or dir.path(tree.root, cfg.rocks_subdir) + end +end + +function 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) + if type(tree) == "string" then + return tree + else + assert(type(tree) == "table") + return tree.root + end +end + +function deploy_bin_dir(tree) + if type(tree) == "string" then + return dir.path(tree, "bin") + else + assert(type(tree) == "table") + return tree.bin_dir or dir.path(tree.root, "bin") + end +end + +function deploy_lua_dir(tree) + if type(tree) == "string" then + return dir.path(tree, cfg.lua_modules_path) + else + assert(type(tree) == "table") + return tree.lua_dir or dir.path(tree.root, cfg.lua_modules_path) + end +end + +function deploy_lib_dir(tree) + if type(tree) == "string" then + return dir.path(tree, cfg.lib_modules_path) + else + assert(type(tree) == "table") + return tree.lib_dir or dir.path(tree.root, cfg.lib_modules_path) + end +end + +function manifest_file(tree) + if type(tree) == "string" then + return dir.path(tree, cfg.rocks_subdir, "manifest") + else + assert(type(tree) == "table") + return (tree.rocks_dir and dir.path(tree.rocks_dir, "manifest")) or dir.path(tree.root, cfg.rocks_subdir, "manifest") + end +end + +--- Get the directory for all versions of a package in a tree. +-- @param name string: The package name. +-- @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) + assert(type(name) == "string") + tree = tree or cfg.root_dir + return dir.path(rocks_dir(tree), name) +end + +--- Get the local installation directory (prefix) for a package. +-- @param name string: The package name. +-- @param version string: The package version. +-- @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) + assert(type(name) == "string") + assert(type(version) == "string") + tree = tree or cfg.root_dir + return dir.path(rocks_dir(tree), name, version) +end + +--- Get the local filename of the rockspec of an installed rock. +-- @param name string: The package name. +-- @param version string: The package version. +-- @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) + 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") +end + +--- Get the local filename of the rock_manifest file of an installed rock. +-- @param name string: The package name. +-- @param version string: The package version. +-- @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) + assert(type(name) == "string") + assert(type(version) == "string") + tree = tree or cfg.root_dir + return dir.path(rocks_dir(tree), name, version, "rock_manifest") +end + +--- Get the local installation directory for C libraries of a package. +-- @param name string: The package name. +-- @param version string: The package version. +-- @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) + assert(type(name) == "string") + assert(type(version) == "string") + tree = tree or cfg.root_dir + return dir.path(rocks_dir(tree), name, version, "lib") +end + +--- Get the local installation directory for Lua modules of a package. +-- @param name string: The package name. +-- @param version string: The package version. +-- @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) + assert(type(name) == "string") + assert(type(version) == "string") + tree = tree or cfg.root_dir + return dir.path(rocks_dir(tree), name, version, "lua") +end + +--- Get the local installation directory for documentation of a package. +-- @param name string: The package name. +-- @param version string: The package version. +-- @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) + assert(type(name) == "string") + assert(type(version) == "string") + tree = tree or cfg.root_dir + return dir.path(rocks_dir(tree), name, version, "doc") +end + +--- Get the local installation directory for configuration files of a package. +-- @param name string: The package name. +-- @param version string: The package version. +-- @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) + assert(type(name) == "string") + assert(type(version) == "string") + tree = tree or cfg.root_dir + return dir.path(rocks_dir(tree), name, version, "conf") +end + +--- Get the local installation directory for command-line scripts +-- of a package. +-- @param name string: The package name. +-- @param version string: The package version. +-- @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) + assert(type(name) == "string") + assert(type(version) == "string") + tree = tree or cfg.root_dir + return dir.path(rocks_dir(tree), name, version, "bin") +end + +--- Extract name, version and arch of a rock filename, +-- or name, version and "rockspec" from a rockspec name. +-- @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) + assert(type(file_name) == "string") + if file_name:match("%.rock$") then + return dir.base_name(file_name):match("(.*)-([^-]+-%d+)%.([^.]+)%.rock$") + else + return dir.base_name(file_name):match("(.*)-([^-]+-%d+)%.(rockspec)") + end +end + +--- Make a rockspec or rock URL. +-- @param pathname string: Base URL or pathname. +-- @param name string: Package name. +-- @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) + assert(type(pathname) == "string") + assert(type(name) == "string") + assert(type(version) == "string") + assert(type(arch) == "string") + + local filename = name.."-"..version + if arch == "installed" then + filename = dir.path(name, version, filename..".rockspec") + elseif arch == "rockspec" then + filename = filename..".rockspec" + else + filename = filename.."."..arch..".rock" + end + return dir.path(pathname, filename) +end + +--- Convert a pathname to a module identifier. +-- In Unix, for example, a path "foo/bar/baz.lua" is converted to +-- "foo.bar.baz"; "bla/init.lua" returns "bla"; "foo.so" returns "foo". +-- @param file string: Pathname of module +-- @return string: The module identifier, or nil if given path is +-- not a conformant module path (the function does not check if the +-- path actually exists). +function path_to_module(file) + assert(type(file) == "string") + + local name = file:match("(.*)%."..cfg.lua_extension.."$") + if name then + name = name:gsub(dir.separator, ".") + local init = name:match("(.*)%.init$") + if init then + name = init + end + else + name = file:match("(.*)%."..cfg.lib_extension.."$") + if name then + name = name:gsub(dir.separator, ".") + end + end + if not name then name = file end + name = name:gsub("^%.+", ""):gsub("%.+$", "") + return name +end + +--- Obtain the directory name where a module should be stored. +-- For example, on Unix, "foo.bar.baz" will return "foo/bar". +-- @param mod string: A module name in Lua dot-separated format. +-- @return string: A directory name using the platform's separator. +function module_to_path(mod) + assert(type(mod) == "string") + return (mod:gsub("[^.]*$", ""):gsub("%.", dir.separator)) +end + +--- Set up path-related variables for a given rock. +-- 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) + 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) + rockspec.variables = vars +end + +function versioned_name(file, prefix, name, version) + assert(type(file) == "string") + assert(type(name) == "string") + assert(type(version) == "string") + + local rest = file:sub(#prefix+1):gsub("^/*", "") + local name_version = (name.."_"..version):gsub("%-", "_"):gsub("%.", "_") + return dir.path(prefix, name_version.."-"..rest) +end + +function 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) +end + +--- Apply a given function to the active rocks trees based on chosen dependency mode. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for no trees (this function becomes a nop). +-- @param fn function: function to be applied, with the tree dir (string) as the first +-- argument and the remaining varargs of map_trees as the following arguments. +-- @return a table with all results of invocations of fn collected. +function map_trees(deps_mode, fn, ...) + local result = {} + if deps_mode == "one" then + table.insert(result, (fn(cfg.root_dir, ...)) or 0) + elseif deps_mode == "all" or deps_mode == "order" then + local use = false + if deps_mode == "all" then + use = true + end + for _, tree in ipairs(cfg.rocks_trees) do + if dir.normalize(tree) == dir.normalize(cfg.root_dir) then + use = true + end + if use then + table.insert(result, (fn(tree, ...)) or 0) + end + end + end + return result +end + +--- Return the pathname of the file that would be loaded for a module, indexed. +-- @param module_name string: module name (eg. "socket.core") +-- @param name string: name of the package (eg. "luasocket") +-- @param version string: version number (eg. "2.0.2-1") +-- @param tree string: repository path (eg. "/usr/local") +-- @param i number: the index, 1 if version is the current default, > 1 otherwise. +-- This is done this way for use by select_module in luarocks.loader. +-- @return string: filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so") +function which_i(module_name, name, version, tree, i) + local deploy_dir + if module_name:match("%.lua$") then + deploy_dir = deploy_lua_dir(tree) + module_name = dir.path(deploy_dir, module_name) + else + deploy_dir = 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) + end + return module_name +end + +--- Return the pathname of the file that would be loaded for a module, +-- returning the versioned pathname if given version is not the default version +-- in the given manifest. +-- @param module_name string: module name (eg. "socket.core") +-- @param name string: name of the package (eg. "luasocket") +-- @param version string: version number (eg. "2.0.2-1") +-- @param tree string: repository path (eg. "/usr/local") +-- @param 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) + 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("//", "/") + 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 + diff --git a/src/luarocks/persist.lua b/src/luarocks/persist.lua new file mode 100644 index 0000000..208aee0 --- /dev/null +++ b/src/luarocks/persist.lua @@ -0,0 +1,173 @@ + +--- Utility module for loading files into tables and +-- 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) + +local util = require("luarocks.util") + +--- Load a Lua file containing assignments, storing them in a table. +-- The global environment is not propagated to the loaded file. +-- @param filename string: the name of the file. +-- @param tbl table or nil: if given, this table is used to store +-- loaded values. +-- @return table 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) + 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 + chunk, err = loadfile(filename) + if chunk then + setfenv(chunk, result) + ran, err = pcall(chunk) + end + else -- Lua 5.2 + chunk, err = loadfile(filename, "t", result) + if chunk then + ran, err = pcall(chunk) + end + end + + if not chunk then + if err:sub(1,5) ~= filename:sub(1,5) then + return false, err + end + return nil, "Error loading file: "..err + end + if not ran then + return nil, "Error running file: "..err + end + return result +end + +local write_table + +--- Write a value as Lua code, invoking write_table. +-- This function handles only numbers, strings and tables +-- are keys (tables are handled recursively). +-- @param out userdata: a file object, open for writing. +-- @param v: the value to be written. +-- @param level number: the indentation level +-- @param sub_order table: optional prioritization table +-- @see write_table +local function write_value(out, v, level, sub_order) + if type(v) == "table" then + write_table(out, v, level + 1, sub_order) + elseif type(v) == "string" then + if v:match("\n") then + local open, close = "[[", "]]" + local equals = 0 + while v:find(open,1,true) or v:find(close,1,true) do + equals = equals + 1 + local eqs = ("="):rep(equals) + open, close = "["..eqs.."[", "]"..eqs.."]" + end + out:write(open.."\n"..v..close) + else + out:write("\""..v:gsub("\"", "\\\"").."\"") + end + else + out:write(tostring(v)) + end +end + +--- Write a table as Lua code representing a table to disk +-- (that is, in curly brackets notation). +-- This function handles only numbers, strings and tables +-- are keys (tables are handled recursively). +-- @param out userdata: a file object, open for writing. +-- @param tbl table: the table to be written. +-- @param level number: the indentation level +-- @param field_order table: optional prioritization table +write_table = function(out, tbl, level, field_order) + out:write("{") + local sep = "\n" + local indentation = " " + local indent = true + local i = 1 + for k, v, sub_order in util.sortedpairs(tbl, field_order) do + out:write(sep) + if indent then + for n = 1,level do out:write(indentation) end + end + sep = ",\n" + indent = true + if type(k) == "number" then + if k ~= i then + out:write("["..tostring(k).."]=") + else + i = i + 1 + end + indent = false + sep = ", " + elseif type(k) == "table" then + out:write("[") + write_table(out, k, level + 1) + out:write("] = ") + else + if k:match("^[a-zA-Z_][a-zA-Z0-9_]*$") then + out:write(k.." = ") + else + out:write("['"..k:gsub("'", "\\'").."'] = ") + end + end + write_value(out, v, level, sub_order) + end + if sep ~= "\n" then + out:write("\n") + for n = 1,level-1 do out:write(indentation) end + end + out:write("}") +end + +--- Writes a table to an io-like object. +-- @param out userdata: a file object, open for writing. +-- @param tbl table: the table to be written. +-- @param field_order table: optional prioritization table +-- @return userdata The file object originally passed in as the `out` parameter. +local function write_table(out, tbl, field_order) + for k, v, sub_order in util.sortedpairs(tbl, field_order) do + out:write(k.." = ") + write_value(out, v, 0, sub_order) + out:write("\n") + end + return out +end + +--- Save the contents of a table to a string. +-- Each element of the table is saved as a global assignment. +-- Only numbers, strings and tables (containing numbers, strings +-- or other recursively processed tables) are supported. +-- @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) + local out = {buffer = {}} + function out:write(data) table.insert(self.buffer, data) end + write_table(out, tbl, field_order) + return table.concat(out.buffer) +end + +--- Save the contents of a table in a file. +-- Each element of the table is saved as a global assignment. +-- Only numbers, strings and tables (containing numbers, strings +-- or other recursively processed tables) are supported. +-- @param filename string: the output filename +-- @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 boolean or (nil, string): true if successful, or nil and a +-- message in case of errors. +function save_from_table(filename, tbl, field_order) + local out = io.open(filename, "w") + if not out then + return nil, "Cannot create file at "..filename + end + write_table(out, tbl, field_order) + out:close() + return true +end diff --git a/src/luarocks/purge.lua b/src/luarocks/purge.lua new file mode 100644 index 0000000..e76a82e --- /dev/null +++ b/src/luarocks/purge.lua @@ -0,0 +1,70 @@ + +--- Module implementing the LuaRocks "purge" command. +-- Remove all rocks from a given tree. +module("luarocks.purge", package.seeall) + +local util = require("luarocks.util") +local fs = require("luarocks.fs") +local path = require("luarocks.path") +local search = require("luarocks.search") +local deps = require("luarocks.deps") +local repos = require("luarocks.repos") +local 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 = [[ +This command removes rocks en masse from a given tree. +By default, it removes all rocks from a tree. + +The --tree argument is mandatory: luarocks purge does not +assume a default tree. + +--old-versions Keep the highest-numbered version of each + rock and remove the other ones. By default + it only removes old versions if they are + not needed as dependencies. This can be + overridden with the flag --force. +]] + +function run(...) + local flags = util.parse_flags(...) + + local tree = flags["tree"] + + if type(tree) ~= "string" then + return nil, "The --tree argument is mandatory. "..util.see_help("purge") + end + + local results = {} + local query = search.make_query("") + query.exact_name = false + search.manifest_search(results, path.rocks_dir(tree), query) + + local sort = function(a,b) return deps.compare_versions(b,a) end + if flags["old-versions"] then + sort = deps.compare_versions + end + + for package, versions in util.sortedpairs(results) do + for version, repositories in util.sortedpairs(versions, sort) do + if flags["old-versions"] then + util.printout("Keeping "..package.." "..version.."...") + local ok, err = remove.remove_other_versions(package, version, flags["force"]) + if not ok then + util.printerr(err) + end + break + else + util.printout("Removing "..package.." "..version.."...") + local ok, err = repos.delete_version(package, version, true) + if not ok then + util.printerr(err) + end + end + end + end + return manif.make_manifest(cfg.rocks_dir, "one") +end diff --git a/src/luarocks/refresh_cache.lua b/src/luarocks/refresh_cache.lua new file mode 100644 index 0000000..8073037 --- /dev/null +++ b/src/luarocks/refresh_cache.lua @@ -0,0 +1,30 @@ + +--- Module implementing the luarocks-admin "refresh_cache" command. +module("luarocks.refresh_cache", package.seeall) + +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 = [[ +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(...) + local flags = util.parse_flags(...) + local server, upload_server = cache.get_upload_server(flags["server"]) + if not server then return nil, upload_server end + local download_url = cache.get_server_urls(server, upload_server) + + local ok, err = cache.refresh_local_cache(server, download_url, cfg.upload_user, cfg.upload_password) + if not ok then + return nil, err + else + return true + end +end + diff --git a/src/luarocks/remove.lua b/src/luarocks/remove.lua new file mode 100644 index 0000000..9681641 --- /dev/null +++ b/src/luarocks/remove.lua @@ -0,0 +1,157 @@ + +--- Module implementing the LuaRocks "remove" command. +-- Uninstalls rocks. +module("luarocks.remove", package.seeall) + +local search = require("luarocks.search") +local deps = require("luarocks.deps") +local fetch = require("luarocks.fetch") +local repos = require("luarocks.repos") +local path = require("luarocks.path") +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") +local manif = require("luarocks.manif") +local fs = require("luarocks.fs") + +help_summary = "Uninstall a rock." +help_arguments = "[--force] <name> [<version>]" +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. +]] + +--- Obtain a list of packages that depend on the given set of packages +-- (where all packages of the set are versions of one program). +-- @param name string: the name of a program +-- @param versions array of string: the versions to be deleted. +-- @return array of string: an empty table if no packages depend on any +-- of the given list, or an array of strings in "name/version" format. +local function check_dependents(name, versions, deps_mode) + local dependents = {} + local blacklist = {} + blacklist[name] = {} + for version, _ in pairs(versions) do + blacklist[name][version] = true + end + local local_rocks = {} + local query_all = search.make_query("") + query_all.exact_name = false + search.manifest_search(local_rocks, cfg.rocks_dir, query_all) + local_rocks[name] = nil + for rock_name, rock_versions in pairs(local_rocks) do + for rock_version, _ in pairs(rock_versions) do + local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version)) + if rockspec then + local _, missing = deps.match_deps(rockspec, blacklist, deps_mode) + if missing[name] then + table.insert(dependents, { name = rock_name, version = rock_version }) + end + end + end + end + return dependents +end + +--- Delete given versions of a program. +-- @param name string: the name of a program +-- @param versions array of string: the versions to be deleted. +-- @return boolean or (nil, string): true on success or nil and an error message. +local function delete_versions(name, versions) + + for version, _ in pairs(versions) do + util.printout("Removing "..name.." "..version.."...") + local ok, err = repos.delete_version(name, version) + if not ok then return nil, err end + end + + return true +end + +function 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) + + if #dependents == 0 or force then + if #dependents > 0 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) + end + util.printerr() + util.printerr("Use --force to force removal (warning: this may break modules).") + return nil, "Failed removing." + end + util.printout("Removal successful.") + return true +end + +function 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) + end + return true +end + +--- Driver function for the "remove" command. +-- @param name string: name of a rock. If a version is given, refer to +-- a specific version; otherwise, try to remove all versions. +-- @param version string: When passing a package name, a version number +-- may also be given. +-- @return boolean or (nil, string): True if removal was +-- successful, nil and an error message otherwise. +function run(...) + local flags, name, version = util.parse_flags(...) + + if type(name) ~= "string" then + return nil, "Argument missing, see help." + end + + local deps_mode = flags["deps-mode"] or cfg.deps_mode + + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err end + + local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$") + local filename = name + if rock_type then + name, version = path.parse_name(filename) + if not name then return nil, "Invalid "..rock_type.." filename: "..filename end + end + + local results = {} + 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." + end + + return remove_search_results(results, name, deps_mode, flags["force"]) +end diff --git a/src/luarocks/repos.lua b/src/luarocks/repos.lua new file mode 100644 index 0000000..b7e64d4 --- /dev/null +++ b/src/luarocks/repos.lua @@ -0,0 +1,309 @@ + +--- Functions for managing the repository on disk. +module("luarocks.repos", package.seeall) + +local fs = require("luarocks.fs") +local path = require("luarocks.path") +local cfg = require("luarocks.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") +local manif = require("luarocks.manif") +local deps = require("luarocks.deps") + +--- Get all installed versions of a package. +-- @param name string: a package name. +-- @return table or nil: An array of strings listing installed +-- versions of a package, or nil if none is available. +local function get_installed_versions(name) + assert(type(name) == "string") + + local dirs = fs.list_dir(path.versions_dir(name)) + return (dirs and #dirs > 0) and dirs or nil +end + +--- Check if a package exists in a local repository. +-- Version numbers are compared as exact string comparison. +-- @param name string: name of package +-- @param version string: package version in string format +-- @return boolean: true if a package is installed, +-- false otherwise. +function is_installed(name, version) + assert(type(name) == "string") + assert(type(version) == "string") + + return fs.is_dir(path.install_dir(name, version)) +end + +local function recurse_rock_manifest_tree(file_tree, action) + assert(type(file_tree) == "table") + assert(type(action) == "function") + local function do_recurse_rock_manifest_tree(tree, parent_path, parent_module) + + for file, sub in pairs(tree) do + if type(sub) == "table" then + local ok, err = do_recurse_rock_manifest_tree(sub, parent_path..file.."/", parent_module..file..".") + if not ok then return nil, err end + else + local ok, err = action(parent_path, parent_module, file) + if not ok then return nil, err end + end + end + return true + end + return do_recurse_rock_manifest_tree(file_tree, "", "") +end + +local function store_package_data(result, name, file_tree) + if not file_tree then return end + return recurse_rock_manifest_tree(file_tree, + function(parent_path, parent_module, file) + local pathname = parent_path..file + result[path.path_to_module(pathname)] = pathname + return true + end + ) +end + +--- Obtain a list of modules within an installed package. +-- @param package string: The package name; for example "luasocket" +-- @param version string: The exact version number including revision; +-- for example "2.0.1-1". +-- @return table: A table of modules where keys are module identifiers +-- 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) + assert(type(package) == "string") + assert(type(version) == "string") + + local result = {} + local rock_manifest = manif.load_rock_manifest(package, version) + store_package_data(result, package, rock_manifest.lib) + store_package_data(result, package, rock_manifest.lua) + return result +end + +--- Obtain a list of command-line scripts within an installed package. +-- @param package string: The package name; for example "luasocket" +-- @param version string: The exact version number including revision; +-- for example "2.0.1-1". +-- @return table: A table of items where keys are command names +-- 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) + assert(type(package) == "string") + assert(type(version) == "string") + + local result = {} + local rock_manifest = manif.load_rock_manifest(package, version) + store_package_data(result, package, rock_manifest.bin) + return result +end + + +--- Check if a rock contains binary executables. +-- @param name string: name of an installed rock +-- @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) + assert(type(name) == "string") + assert(type(version) == "string") + + local rock_manifest = manif.load_rock_manifest(name, version) + if rock_manifest.bin then + for name, md5 in pairs(rock_manifest.bin) do + -- TODO verify that it is the same file. If it isn't, find the actual command. + if fs.is_actual_binary(dir.path(cfg.deploy_bin_dir, name)) then + return true + end + end + end + return false +end + +function run_hook(rockspec, hook_name) + assert(type(rockspec) == "table") + assert(type(hook_name) == "string") + + local hooks = rockspec.hooks + if not hooks then + return true + end + + if cfg.hooks_enabled == false then + return nil, "This rockspec contains hooks, which are blocked by the 'hooks_enabled' setting in your LuaRocks configuration." + end + + if not hooks.substituted_variables then + util.variable_substitutions(hooks, rockspec.variables) + hooks.substituted_variables = true + end + local hook = hooks[hook_name] + if hook then + util.printout(hook) + if not fs.execute(hook) then + return nil, "Failed running "..hook_name.." hook." + end + end + return true +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) + else + ok, err = fs.copy_binary(source, target) + end + if file then file:close() end + return ok, err +end + +local function resolve_conflict(target, deploy_dir, name, version) + local cname, cversion = manif.find_current_provider(target) + if not cname then + return nil, cversion + 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)) + fs.move(target, versioned) + return target + else + return path.versioned_name(target, deploy_dir, name, version) + end +end + +function should_wrap_bin_scripts(rockspec) + assert(type(rockspec) == "table") + + if cfg.wrap_bin_scripts ~= nil then + return cfg.wrap_bin_scripts + end + if rockspec.deploy and rockspec.deploy.wrap_bin_scripts == false then + return false + end + return true +end + +function deploy_files(name, version, wrap_bin_scripts) + assert(type(name) == "string") + assert(type(version) == "string") + assert(type(wrap_bin_scripts) == "boolean") + + local function deploy_file_tree(file_tree, path_fn, deploy_dir, move_fn) + local source_dir = path_fn(name, version) + if not move_fn then + move_fn = fs.move + end + return recurse_rock_manifest_tree(file_tree, + function(parent_path, parent_module, file) + local source = dir.path(source_dir, parent_path, file) + local target = dir.path(deploy_dir, parent_path, file) + local ok, err + if fs.exists(target) then + local new_target, err = resolve_conflict(target, deploy_dir, name, version) + if err == "untracked" then + fs.delete(target) + 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 = move_fn(source, target, name, version) + fs.remove_dir_tree_if_empty(dir.dir_name(source)) + if not ok then return nil, err end + return true + end + ) + end + + local rock_manifest = manif.load_rock_manifest(name, version) + + local ok, err = true + if rock_manifest.bin then + local move_bin_fn = wrap_bin_scripts and install_binary or fs.copy_binary + ok, err = deploy_file_tree(rock_manifest.bin, path.bin_dir, cfg.deploy_bin_dir, move_bin_fn) + end + if ok and rock_manifest.lua then + ok, err = deploy_file_tree(rock_manifest.lua, path.lua_dir, cfg.deploy_lua_dir) + end + if ok and rock_manifest.lib then + ok, err = deploy_file_tree(rock_manifest.lib, path.lib_dir, cfg.deploy_lib_dir) + end + return ok, err +end + +--- Delete a package from the local repository. +-- Version numbers are compared as exact string comparison. +-- @param name string: name of package +-- @param version string: package version in string format +-- @param quick boolean: do not try to fix the versioned name +-- 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) + assert(type(name) == "string") + assert(type(version) == "string") + + local function delete_deployed_file_tree(file_tree, deploy_dir) + 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) + 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 + end + fs.remove_dir_tree_if_empty(dir.dir_name(target)) + if not ok then return nil, "Failed deleting "..target end + end + return true + end + ) + end + + local rock_manifest = manif.load_rock_manifest(name, version) + if not rock_manifest then + return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?" + end + + local ok, err = true + if rock_manifest.bin then + ok, err = delete_deployed_file_tree(rock_manifest.bin, cfg.deploy_bin_dir) + end + if ok and rock_manifest.lua then + ok, err = delete_deployed_file_tree(rock_manifest.lua, cfg.deploy_lua_dir) + end + if ok and rock_manifest.lib then + ok, err = delete_deployed_file_tree(rock_manifest.lib, cfg.deploy_lib_dir) + end + if err then return nil, err end + + fs.delete(path.install_dir(name, version)) + if not get_installed_versions(name) then + fs.delete(dir.path(cfg.rocks_dir, name)) + end + return true +end diff --git a/src/luarocks/require.lua b/src/luarocks/require.lua new file mode 100644 index 0000000..9917770 --- /dev/null +++ b/src/luarocks/require.lua @@ -0,0 +1,6 @@ +--- 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 diff --git a/src/luarocks/search.lua b/src/luarocks/search.lua new file mode 100644 index 0000000..1fc1f9a --- /dev/null +++ b/src/luarocks/search.lua @@ -0,0 +1,386 @@ + +--- Module implementing the LuaRocks "search" command. +-- Queries LuaRocks servers. +module("luarocks.search", package.seeall) + +local dir = require("luarocks.dir") +local path = require("luarocks.path") +local manif = require("luarocks.manif") +local deps = require("luarocks.deps") +local cfg = require("luarocks.cfg") +local util = require("luarocks.util") + +help_summary = "Query the LuaRocks servers." +help_arguments = "[--source] [--binary] { <name> [<version>] | --all }" +help = [[ +--source Return only rockspecs and source rocks, + to be used with the "build" command. +--binary Return only pure Lua and binary rocks (rocks that can be used + with the "install" command without requiring a C toolchain). +--all List all contents of the server that are suitable to + this platform, do not filter by name. +]] + +--- Convert the arch field of a query table to table format. +-- @param query table: A query table. +local function query_arch_as_table(query) + local format = type(query.arch) + if format == "table" then + return + elseif format == "nil" then + local accept = {} + accept["src"] = true + accept["all"] = true + accept["rockspec"] = true + accept["installed"] = true + accept[cfg.arch] = true + query.arch = accept + elseif format == "string" then + local accept = {} + for a in query.arch:gmatch("[%w_-]+") do + accept[a] = true + end + query.arch = accept + end +end + +--- Store a search result (a rock or rockspec) in the results table. +-- @param results table: The results table, where keys are package names and +-- versions are tables matching version strings to an array of servers. +-- @param name string: Package name. +-- @param version string: Package version. +-- @param arch string: Architecture of rock ("all", "src" or platform +-- identifier), "rockspec" or "installed" +-- @param repo string: Pathname of a local repository of URL of +-- rocks server. +local function store_result(results, name, version, arch, repo) + assert(type(results) == "table") + assert(type(name) == "string") + assert(type(version) == "string") + assert(type(arch) == "string") + assert(type(repo) == "string") + + if not results[name] then results[name] = {} end + if not results[name][version] then results[name][version] = {} end + table.insert(results[name][version], { + arch = arch, + repo = repo + }) +end + +--- Test the name field of a query. +-- If query has a boolean field exact_name set to false, +-- then substring match is performed; otherwise, exact string +-- comparison is done. +-- @param query table: A query in dependency table format. +-- @param name string: A package name. +-- @return boolean: True if names match, false otherwise. +local function match_name(query, name) + assert(type(query) == "table") + assert(type(name) == "string") + if query.exact_name == false then + return name:find(query.name, 0, true) and true or false + else + return name == query.name + end +end + +--- Store a match in a results table if version matches query. +-- Name, version, arch and repository path are stored in a given +-- table, optionally checking if version and arch (if given) match +-- a query. +-- @param results table: The results table, where keys are package names and +-- versions are tables matching version strings to an array of servers. +-- @param repo string: URL or pathname of the repository. +-- @param name string: The name of the package being tested. +-- @param version string: The version of the package being tested. +-- @param arch string: The arch of the package being tested. +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- If the arch field is omitted, the local architecture (cfg.arch) +-- is used. The special value "any" is also recognized, returning all +-- matches regardless of architecture. +local function store_if_match(results, repo, name, version, arch, query) + if match_name(query, name) then + if query.arch[arch] or query.arch["any"] then + if deps.match_constraints(deps.parse_version(version), query.constraints) then + store_result(results, name, version, arch, repo) + end + end + end +end + +--- Perform search on a local repository. +-- @param repo string: The pathname of the local repository. +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- If the arch field is omitted, the local architecture (cfg.arch) +-- is used. The special value "any" is also recognized, returning all +-- matches regardless of architecture. +-- @param results table or nil: If given, this table will store the +-- results; if not given, a new table will be created. +-- @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) + assert(type(repo) == "string") + assert(type(query) == "table") + assert(type(results) == "table" or not results) + + local fs = require("luarocks.fs") + + if not results then + results = {} + end + query_arch_as_table(query) + + for _, name in pairs(fs.list_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 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 +end + +--- Perform search on a rocks server. +-- @param results table: The results table, where keys are package names and +-- versions are tables matching version strings to an array of servers. +-- @param repo string: The URL of the rocks server. +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- If the arch field is omitted, the local architecture (cfg.arch) +-- is used. The special value "any" is also recognized, returning all +-- matches regardless of architecture. +-- @return true or, in case of errors, nil and an error message. +function 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) + if not manifest then + return nil, "Failed loading manifest: "..err + end + for name, versions in pairs(manifest.repository) do + for version, items in pairs(versions) do + for _, item in ipairs(items) do + store_if_match(results, repo, name, version, item.arch, query) + end + end + end + return true +end + +--- Search on all configured rocks servers. +-- @param query table: A dependency query. +-- @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) + 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 + end + local ok, err = manifest_search(results, mirror, query) + if ok then + break + else + util.warning("Failed searching manifest: "..err) + end + end + end + return results +end + +--- Prepare a query in dependency table format. +-- @param name string: The query name. +-- @param version string or nil: +-- @return table: A query in table format +function make_query(name, version) + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local query = { + name = name, + constraints = {} + } + if version then + table.insert(query.constraints, { op = "==", version = deps.parse_version(version)}) + end + return query +end + +--- Get the URL for the latest in a set of versions. +-- @param name string: The package name to be used in the URL. +-- @param versions table: An array of version informations, as stored +-- in search results tables. +-- @return string or nil: the URL for the latest version if one could +-- be picked, or nil. +local function pick_latest_version(name, versions) + assert(type(name) == "string") + assert(type(versions) == "table") + + local vtables = {} + for v, _ in pairs(versions) do + table.insert(vtables, deps.parse_version(v)) + end + table.sort(vtables) + local version = vtables[#vtables].string + local items = versions[version] + if items then + local pick = 1 + for i, item in ipairs(items) do + if (item.arch == 'src' and items[pick].arch == 'rockspec') + or (item.arch ~= 'src' and item.arch ~= 'rockspec') then + pick = i + end + end + return path.make_url(items[pick].repo, name, version, items[pick].arch) + end + return nil +end + +--- Attempt to get a single URL for a given search. +-- @param query table: A dependency query. +-- @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) + assert(type(query) == "table") + + local results = 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 + return pick_latest_version(query.name, results[first]) + else + return results + end +end + +--- Print a list of rocks/rockspecs on standard output. +-- @param results table: A table where keys are package names and versions +-- are tables matching version strings to an array of rocks servers. +-- @param porcelain boolean or nil: A flag to force machine-friendly output. +function print_results(results, porcelain) + assert(type(results) == "table") + assert(type(porcelain) == "boolean" or not porcelain) + + for package, versions in util.sortedpairs(results) do + if not porcelain then + util.printout(package) + end + for version, repos in util.sortedpairs(versions, deps.compare_versions) do + for _, repo in ipairs(repos) do + if porcelain then + util.printout(package, version, repo.arch, repo.repo) + else + util.printout(" "..version.." ("..repo.arch..") - "..repo.repo) + end + end + end + if not porcelain then + util.printout() + end + end +end + +--- Splits a list of search results into two lists, one for "source" results +-- to be used with the "build" command, and one for "binary" results to be +-- used with the "install" command. +-- @param results table: A search results table. +-- @return (table, table): Two tables, one for source and one for binary +-- results. +local function split_source_and_binary_results(results) + local sources, binaries = {}, {} + for name, versions in pairs(results) do + for version, repositories in pairs(versions) do + for _, repo in ipairs(repositories) do + local where = sources + if repo.arch == "all" or repo.arch == cfg.arch then + where = binaries + end + store_result(where, name, version, repo.arch, repo.repo) + end + end + end + return sources, binaries +end + +--- Given a name and optionally a version, try to find in the rocks +-- servers a single .src.rock or .rockspec file that satisfies +-- the request, and run the given function on it; or display to the +-- user possibilities if it couldn't narrow down a single match. +-- @param action function: A function that takes a .src.rock or +-- .rockspec URL as a parameter. +-- @param name string: A rock name +-- @param version string or nil: A version number may also be given. +-- @return The result of the action function, or nil and an error message. +function 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) + query.arch = "src|rockspec" + local results, err = find_suitable_rock(query) + if type(results) == "string" then + return action(results, ...) + else + return nil, "Could not find a result named "..name..(version and " "..version or "").."." + end +end + +--- Driver function for "search" command. +-- @param name string: A substring of a rock name to search. +-- @param version string or nil: a version may also be passed. +-- @return boolean or (nil, string): True if build was successful; nil and an +-- error message otherwise. +function run(...) + local flags, name, version = util.parse_flags(...) + + if flags["all"] then + name, version = "", nil + end + + if type(name) ~= "string" and not flags["all"] then + return nil, "Enter name and version or use --all. "..util.see_help("search") + end + + local query = make_query(name:lower(), version) + query.exact_name = false + local results, err = 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) + end + if next(binaries) and not flags["source"] then + util.title("Binary and pure-Lua rocks:", porcelain) + print_results(binaries, porcelain) + end + return true +end diff --git a/src/luarocks/show.lua b/src/luarocks/show.lua new file mode 100644 index 0000000..536085a --- /dev/null +++ b/src/luarocks/show.lua @@ -0,0 +1,156 @@ + +--- Module implementing the LuaRocks "show" command. +-- Shows information about an installed rock. +module("luarocks.show", package.seeall) + +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." + +help = [[ +<argument> is an existing package name. +Without any flags, show all module information. +With these flags, return only the desired information: + +--home home page of project +--modules all modules provided by this package as used by require() +--deps packages this package depends on +--rockspec the full path of the rockspec file +--mversion the package version +--rock-tree local tree where rock is installed +--rock-dir data directory of the installed rock +]] + +local function keys_as_string(t, sep) + return table.concat(util.keys(t), sep or " ") +end + +local function word_wrap(line) + local width = tonumber(os.getenv("COLUMNS")) or 80 + if width > 80 then width = 80 end + if #line > width then + local brk = width + while brk > 0 and line:sub(brk, brk) ~= " " do + brk = brk - 1 + end + if brk > 0 then + return line:sub(1, brk-1) .. "\n" .. word_wrap(line:sub(brk+1)) + end + end + return line +end + +local function format_text(text) + text = text:gsub("^%s*",""):gsub("%s$", ""):gsub("\n[ \t]+","\n"):gsub("([^\n])\n([^\n])","%1 %2") + local paragraphs = util.split_string(text, "\n\n") + for n, line in ipairs(paragraphs) do + paragraphs[n] = word_wrap(line) + end + return (table.concat(paragraphs, "\n\n"):gsub("%s$", "")) +end + +local function 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 + 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"] } + end + for _, tree in ipairs(trees) do + local rocks_dir = path.rocks_dir(tree) + tree_map[rocks_dir] = tree + search.manifest_search(results, rocks_dir, query) + end + + if not next(results) then -- + return nil,"cannot find package "..name.." "..(version or "").."\nUse 'list' to find installed rocks." + end + + version = nil + local repo_url + local package, versions = util.sortedpairs(results)() + --question: what do we do about multiple versions? This should + --give us the latest version on the last repo (which is usually the global one) + for vs, repositories in util.sortedpairs(versions, deps.compare_versions) do + if not version then version = vs end + for _, rp in ipairs(repositories) do repo_url = rp.repo end + end + + + local repo = tree_map[repo_url] + local directory = path.install_dir(name,version,repo) + local rockspec_file = path.rockspec_file(name, version, repo) + local rockspec, err = fetch.load_local_rockspec(rockspec_file) + if not rockspec then return nil,err end + + local descript = rockspec.description or {} + local manifest, err = manif.load_manifest(repo_url) + if not manifest then return nil,err end + local minfo = manifest.repository[name][version][1] + + if flags["rock-tree"] then util.printout(path.rocks_tree_to_string(repo)) + elseif flags["rock-dir"] then util.printout(directory) + elseif flags["home"] then util.printout(descript.homepage) + elseif flags["modules"] then util.printout(keys_as_string(minfo.modules)) + elseif flags["deps"] then util.printout(keys_as_string(minfo.dependencies)) + elseif flags["rockspec"] then util.printout(rockspec_file) + elseif flags["mversion"] then util.printout(version) + else + util.printout() + util.printout(rockspec.package.." "..rockspec.version.." - "..(descript.summary or "")) + util.printout() + if descript.detailed then + util.printout(format_text(descript.detailed)) + util.printout() + end + if descript.license then + util.printout("License: ", descript.license) + end + if descript.homepage then + util.printout("Homepage: ", descript.homepage) + end + util.printout("Installed in: ", path.rocks_tree_to_string(repo)) + if next(minfo.modules) then + util.printout() + util.printout("Modules:") + for mod, filename in util.sortedpairs(minfo.modules) do + util.printout("\t"..mod.." ("..path.which(mod, filename, name, version, repo, manifest)..")") + end + end + if next(minfo.dependencies) then + util.printout() + util.printout("Depends on:") + util.printout("\t"..keys_as_string(minfo.dependencies, "\n\t")) + end + util.printout() + end + return true +end + diff --git a/src/luarocks/tools/patch.lua b/src/luarocks/tools/patch.lua new file mode 100644 index 0000000..baaacfc --- /dev/null +++ b/src/luarocks/tools/patch.lua @@ -0,0 +1,712 @@ +--- Patch utility to apply unified diffs. +-- +-- http://lua-users.org/wiki/LuaPatch +-- +-- (c) 2008 David Manura, Licensed under the same terms as Lua (MIT license). +-- 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/ . + +module("luarocks.tools.patch", package.seeall) + +local fs = require("luarocks.fs") + +local version = '0.1' + +local io = io +local os = os +local string = string +local table = table +local format = string.format + +-- logging +local debugmode = false +local function debug(s) end +local function info(s) end +local function warning(s) io.stderr:write(s .. '\n') end + +-- Returns boolean whether string s2 starts with string s. +local function startswith(s, s2) + return s:sub(1, #s2) == s2 +end + +-- Returns boolean whether string s2 ends with string s. +local function endswith(s, s2) + return #s >= #s2 and s:sub(#s-#s2+1) == s2 +end + +-- Returns string s after filtering out any new-line characters from end. +local function endlstrip(s) + return s:gsub('[\r\n]+$', '') +end + +-- Returns shallow copy of table t. +local function table_copy(t) + local t2 = {} + for k,v in pairs(t) do t2[k] = v end + 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 + if fh then fh:close() end + return result +end +local function isfile() return true end --FIX? + +local function read_file(filename) + local fh, err, oserr = io.open(filename, 'rb') + if not fh then return fh, err, oserr end + local data, err, oserr = fh:read'*a' + fh:close() + if not data then return nil, err, oserr end + return data +end + +local function write_file(filename, data) + local fh, err, oserr = io.open(filename 'wb') + if not fh then return fh, err, oserr end + local status, err, oserr = fh:write(data) + fh:close() + if not status then return nil, err, oserr end + return true +end + +local function file_copy(src, dest) + local data, err, oserr = read_file(src) + if not data then return data, err, oserr end + local status, err, oserr = write_file(dest) + if not status then return status, err, oserr end + return true +end + +local function string_as_file(s) + return { + at = 0, + str = s, + len = #s, + eof = false, + read = function(self, n) + if self.eof then return nil end + local chunk = self.str:sub(self.at, self.at + n - 1) + self.at = self.at + n + if self.at > self.len then + self.eof = true + end + return chunk + end, + close = function(self) + self.eof = true + end, + } +end + +-- +-- file_lines(f) is similar to f:lines() for file f. +-- The main difference is that read_lines includes +-- new-line character sequences ("\n", "\r\n", "\r"), +-- if any, at the end of each line. Embedded "\0" are also handled. +-- Caution: The newline behavior can depend on whether f is opened +-- in binary or ASCII mode. +-- (file_lines - version 20080913) +-- +local function file_lines(f) + local CHUNK_SIZE = 1024 + local buffer = "" + local pos_beg = 1 + return function() + local pos, chars + while 1 do + pos, chars = buffer:match('()([\r\n].)', pos_beg) + if pos or not f then + break + elseif f then + local chunk = f:read(CHUNK_SIZE) + if chunk then + buffer = buffer:sub(pos_beg) .. chunk + pos_beg = 1 + else + f = nil + end + end + end + if not pos then + pos = #buffer + elseif chars == '\r\n' then + pos = pos + 1 + end + local line = buffer:sub(pos_beg, pos) + pos_beg = pos + 1 + if #line > 0 then + return line + end + end +end + +local function match_linerange(line) + local m1, m2, m3, m4 = line:match("^@@ %-(%d+),(%d+) %+(%d+),(%d+)") + if not m1 then m1, m3, m4 = line:match("^@@ %-(%d+) %+(%d+),(%d+)") end + if not m1 then m1, m2, m3 = line:match("^@@ %-(%d+),(%d+) %+(%d+)") end + if not m1 then m1, m3 = line:match("^@@ %-(%d+) %+(%d+)") end + return m1, m2, m3, m4 +end + +function read_patch(filename, data) + -- define possible file regions that will direct the parser flow + local state = 'header' + -- 'header' - comments before the patch body + -- 'filenames' - lines starting with --- and +++ + -- 'hunkhead' - @@ -R +R @@ sequence + -- 'hunkbody' + -- 'hunkskip' - skipping invalid hunk mode + + local all_ok = true + local lineends = {lf=0, crlf=0, cr=0} + local files = {source={}, target={}, hunks={}, fileends={}, hunkends={}} + local nextfileno = 0 + local nexthunkno = 0 --: even if index starts with 0 user messages + -- number hunks from 1 + + -- hunkinfo holds parsed values, hunkactual - calculated + local hunkinfo = { + startsrc=nil, linessrc=nil, starttgt=nil, linestgt=nil, + invalid=false, text={} + } + local hunkactual = {linessrc=nil, linestgt=nil} + + info(format("reading patch %s", filename)) + + local fp + if data then + fp = string_as_file(data) + else + fp = filename == '-' and io.stdin or assert(io.open(filename, "rb")) + end + local lineno = 0 + + for line in file_lines(fp) do + lineno = lineno + 1 + if state == 'header' then + if startswith(line, "--- ") then + state = 'filenames' + end + -- state is 'header' or 'filenames' + end + if state == 'hunkbody' then + -- skip hunkskip and hunkbody code until definition of hunkhead read + + -- process line first + if line:match"^[- +\\]" or line:match"^[\r\n]*$" then + -- gather stats about line endings + local he = files.hunkends[nextfileno] + if endswith(line, "\r\n") then + he.crlf = he.crlf + 1 + elseif endswith(line, "\n") then + he.lf = he.lf + 1 + elseif endswith(line, "\r") then + he.cr = he.cr + 1 + end + if startswith(line, "-") then + hunkactual.linessrc = hunkactual.linessrc + 1 + elseif startswith(line, "+") then + hunkactual.linestgt = hunkactual.linestgt + 1 + elseif startswith(line, "\\") then + -- nothing + else + hunkactual.linessrc = hunkactual.linessrc + 1 + hunkactual.linestgt = hunkactual.linestgt + 1 + end + table.insert(hunkinfo.text, line) + -- todo: handle \ No newline cases + else + warning(format("invalid hunk no.%d at %d for target file %s", + nexthunkno, lineno, files.target[nextfileno])) + -- add hunk status node + table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) + files.hunks[nextfileno][nexthunkno].invalid = true + all_ok = false + state = 'hunkskip' + end + + -- check exit conditions + if hunkactual.linessrc > hunkinfo.linessrc or + hunkactual.linestgt > hunkinfo.linestgt + then + warning(format("extra hunk no.%d lines at %d for target %s", + nexthunkno, lineno, files.target[nextfileno])) + -- add hunk status node + table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) + files.hunks[nextfileno][nexthunkno].invalid = true + state = 'hunkskip' + elseif hunkinfo.linessrc == hunkactual.linessrc and + hunkinfo.linestgt == hunkactual.linestgt + then + table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) + state = 'hunkskip' + + -- detect mixed window/unix line ends + local ends = files.hunkends[nextfileno] + if (ends.cr~=0 and 1 or 0) + (ends.crlf~=0 and 1 or 0) + + (ends.lf~=0 and 1 or 0) > 1 + then + warning(format("inconsistent line ends in patch hunks for %s", + files.source[nextfileno])) + end + if debugmode then + local debuglines = {crlf=ends.crlf, lf=ends.lf, cr=ends.cr, + file=files.target[nextfileno], hunk=nexthunkno} + debug(format("crlf: %(crlf)d lf: %(lf)d cr: %(cr)d\t " .. + "- file: %(file)s hunk: %(hunk)d", debuglines)) + end + end + -- state is 'hunkbody' or 'hunkskip' + end + + if state == 'hunkskip' then + if match_linerange(line) then + state = 'hunkhead' + elseif startswith(line, "--- ") then + state = 'filenames' + if debugmode and #files.source > 0 then + debug(format("- %2d hunks for %s", #files.hunks[nextfileno], + files.source[nextfileno])) + end + end + -- state is 'hunkskip', 'hunkhead', or 'filenames' + end + local advance + if state == 'filenames' then + if startswith(line, "--- ") then + if array_contains(files.source, nextfileno) then + all_ok = false + warning(format("skipping invalid patch for %s", + files.source[nextfileno+1])) + table.remove(files.source, nextfileno+1) + -- double source filename line is encountered + -- attempt to restart from this second line + end + -- Accept a space as a terminator, like GNU patch does. + -- Breaks patches containing filenames with spaces... + -- FIXME Figure out what does GNU patch do in those cases. + local match = line:match("^%-%-%- ([^ \t\r\n]+)") + if not match then + all_ok = false + warning(format("skipping invalid filename at line %d", lineno+1)) + state = 'header' + else + table.insert(files.source, match) + end + elseif not startswith(line, "+++ ") then + if array_contains(files.source, nextfileno) then + all_ok = false + warning(format("skipping invalid patch with no target for %s", + files.source[nextfileno+1])) + table.remove(files.source, nextfileno+1) + else + -- this should be unreachable + warning("skipping invalid target patch") + end + state = 'header' + else + if array_contains(files.target, nextfileno) then + all_ok = false + warning(format("skipping invalid patch - double target at line %d", + lineno+1)) + table.remove(files.source, nextfileno+1) + table.remove(files.target, nextfileno+1) + nextfileno = nextfileno - 1 + -- double target filename line is encountered + -- switch back to header state + state = 'header' + else + -- Accept a space as a terminator, like GNU patch does. + -- Breaks patches containing filenames with spaces... + -- FIXME Figure out what does GNU patch do in those cases. + local re_filename = "^%+%+%+ ([^ \t\r\n]+)" + local match = line:match(re_filename) + if not match then + all_ok = false + warning(format( + "skipping invalid patch - no target filename at line %d", + lineno+1)) + state = 'header' + else + table.insert(files.target, match) + nextfileno = nextfileno + 1 + nexthunkno = 0 + table.insert(files.hunks, {}) + table.insert(files.hunkends, table_copy(lineends)) + table.insert(files.fileends, table_copy(lineends)) + state = 'hunkhead' + advance = true + end + end + end + -- state is 'filenames', 'header', or ('hunkhead' with advance) + end + 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 + all_ok = false + warning(format("skipping invalid patch with no hunks for file %s", + files.target[nextfileno])) + end + state = 'header' + else + hunkinfo.startsrc = tonumber(m1) + hunkinfo.linessrc = tonumber(m2 or 1) + hunkinfo.starttgt = tonumber(m3) + hunkinfo.linestgt = tonumber(m4 or 1) + hunkinfo.invalid = false + hunkinfo.text = {} + + hunkactual.linessrc = 0 + hunkactual.linestgt = 0 + + state = 'hunkbody' + nexthunkno = nexthunkno + 1 + end + -- state is 'header' or 'hunkbody' + end + end + if state ~= 'hunkskip' then + warning(format("patch file incomplete - %s", filename)) + all_ok = false + -- os.exit(?) + else + -- duplicated message when an eof is reached + if debugmode and #files.source > 0 then + debug(format("- %2d hunks for %s", #files.hunks[nextfileno], + files.source[nextfileno])) + end + end + + local sum = 0; for _,hset in ipairs(files.hunks) do sum = sum + #hset end + info(format("total files: %d total hunks: %d", #files.source, sum)) + fp:close() + return files, all_ok +end + +local function find_hunk(file, h, hno) + for fuzz=0,2 do + local lineno = h.startsrc + 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 + if startswith(hline, " ") or startswith(hline, "-") then + local line = file[lineno] + lineno = lineno + 1 + if not line or #line == 0 then + found = false + break + end + if endlstrip(line) ~= endlstrip(hline:sub(2)) then + found = false + break + end + end + end + end + if found then + local offset = location - h.startsrc - fuzz + if offset ~= 0 then + warning(format("Hunk %d found at offset %d%s...", hno, offset, fuzz == 0 and "" or format(" (fuzz %d)", fuzz))) + end + h.startsrc = location + h.starttgt = h.starttgt + offset + for i=1,fuzz do + table.remove(h.text, 1) + table.remove(h.text, #h.text) + end + return true + end + lineno = i + end + end + return false +end + +local function load_file(filename) + local fp = assert(io.open(filename)) + local file = {} + local readline = file_lines(fp) + while true do + local line = readline() + if not line then break end + table.insert(file, line) + end + fp:close() + return file +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 +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' + end + for hno, h in ipairs(hunks) do + -- skip to line just before hunk starts + if #file < h.starttgt then + error 'nomatch' + end + lineno = h.starttgt + for _, hline in ipairs(h.text) do + -- todo: \ No newline at the end of file + if not startswith(hline, "-") and not startswith(hline, "\\") then + local line = file[lineno] + lineno = lineno + 1 + if #line == 0 then + error 'nomatch' + end + if endlstrip(line) ~= endlstrip(hline:sub(2)) then + warning(format("file is not patched - failed hunk: %d", hno)) + error 'nomatch' + end + end + end + end + end) + if err == 'nomatch' then + matched = false + end + -- todo: display failed hunk, i.e. expected/found + + return matched +end + +local function patch_hunks(srcname, tgtname, hunks) + local src = assert(io.open(srcname, "rb")) + local tgt = assert(io.open(tgtname, "wb")) + + local src_readline = file_lines(src) + + -- todo: detect linefeeds early - in apply_files routine + -- to handle cases when patch starts right from the first + -- line and no lines are processed. At the moment substituted + -- lineends may not be the same at the start and at the end + -- of patching. Also issue a warning about mixed lineends + + local srclineno = 1 + local lineends = {['\n']=0, ['\r\n']=0, ['\r']=0} + for hno, h in ipairs(hunks) do + debug(format("processing hunk %d for file %s", hno, tgtname)) + -- skip to line just before hunk starts + while srclineno < h.startsrc do + local line = src_readline() + -- Python 'U' mode works only with text files + if endswith(line, "\r\n") then + lineends["\r\n"] = lineends["\r\n"] + 1 + elseif endswith(line, "\n") then + lineends["\n"] = lineends["\n"] + 1 + elseif endswith(line, "\r") then + lineends["\r"] = lineends["\r"] + 1 + end + tgt:write(line) + srclineno = srclineno + 1 + end + + for _,hline in ipairs(h.text) do + -- todo: check \ No newline at the end of file + if startswith(hline, "-") or startswith(hline, "\\") then + src_readline() + srclineno = srclineno + 1 + else + if not startswith(hline, "+") then + src_readline() + srclineno = srclineno + 1 + end + local line2write = hline:sub(2) + -- detect if line ends are consistent in source file + local sum = 0 + for k,v in pairs(lineends) do if v > 0 then sum=sum+1 end end + if sum == 1 then + local newline + for k,v in pairs(lineends) do if v ~= 0 then newline = k end end + tgt:write(endlstrip(line2write) .. newline) + else -- newlines are mixed or unknown + tgt:write(line2write) + end + end + end + end + for line in src_readline do + tgt:write(line) + end + tgt:close() + src:close() + return true +end + +local function strip_dirs(filename, strip) + if strip == nil then return filename end + for i=1,strip do + filename=filename:gsub("^[^/]*/", "") + end + return filename +end + +function apply_patch(patch, strip) + local all_ok = true + local total = #patch.source + for fileno, filename in ipairs(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 = 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", + filename, f2patch)) + all_ok = false + continue = true + end + end + if not continue and not isfile(f2patch) then + warning(format("not a file - %s", f2patch)) + all_ok = false + continue = true + end + if not continue then + + filename = f2patch + + info(format("processing %d/%d:\t %s", fileno, total, filename)) + + -- validate before patching + local hunks = patch.hunks[fileno] + local file = load_file(filename) + local hunkno = 1 + local hunk = hunks[hunkno] + local hunkfind = {} + local hunkreplace = {} + local validhunks = 0 + local canpatch = false + local hunklineno + local isbreak + local lineno = 0 + + find_hunks(file, hunks) + + for _, line in ipairs(file) do + lineno = lineno + 1 + local continue + if not hunk or lineno < hunk.startsrc then + continue = true + elseif lineno == hunk.startsrc then + hunkfind = {} + for _,x in ipairs(hunk.text) do + if x:sub(1,1) == ' ' or x:sub(1,1) == '-' then + hunkfind[#hunkfind+1] = endlstrip(x:sub(2)) + end end + hunkreplace = {} + for _,x in ipairs(hunk.text) do + if x:sub(1,1) == ' ' or x:sub(1,1) == '+' then + hunkreplace[#hunkreplace+1] = endlstrip(x:sub(2)) + end end + --pprint(hunkreplace) + hunklineno = 1 + + -- todo \ No newline at end of file + end + -- check hunks in source file + if not continue and lineno < hunk.startsrc + #hunkfind - 1 then + if endlstrip(line) == hunkfind[hunklineno] then + hunklineno = hunklineno + 1 + else + debug(format("hunk no.%d doesn't match source file %s", + hunkno, filename)) + -- file may be already patched, but check other hunks anyway + hunkno = hunkno + 1 + if hunkno <= #hunks then + hunk = hunks[hunkno] + continue = true + else + isbreak = true; break + end + end + end + -- check if processed line is the last line + if not continue and lineno == hunk.startsrc + #hunkfind - 1 then + debug(format("file %s hunk no.%d -- is ready to be patched", + filename, hunkno)) + hunkno = hunkno + 1 + validhunks = validhunks + 1 + if hunkno <= #hunks then + hunk = hunks[hunkno] + else + if validhunks == #hunks then + -- patch file + canpatch = true + isbreak = true; break + end + end + end + end + if not isbreak then + if hunkno <= #hunks then + warning(format("premature end of source file %s at hunk %d", + filename, hunkno)) + all_ok = false + end + end + if validhunks < #hunks then + if check_patched(file, hunks) then + warning(format("already patched %s", filename)) + else + warning(format("source file is different - %s", filename)) + all_ok = false + end + end + if canpatch then + local backupname = filename .. ".orig" + if exists(backupname) then + warning(format("can't backup original file to %s - aborting", + backupname)) + all_ok = false + else + assert(os.rename(filename, backupname)) + if patch_hunks(backupname, filename, hunks) then + warning(format("successfully patched %s", filename)) + assert(os.remove(backupname)) + else + warning(format("error patching file %s", filename)) + assert(file_copy(filename, filename .. ".invalid")) + warning(format("invalid version is saved to %s", + filename .. ".invalid")) + -- todo: proper rejects + assert(os.rename(backupname, filename)) + all_ok = false + end + end + end + + end -- if not continue + end -- for + -- todo: check for premature eof + return all_ok +end diff --git a/src/luarocks/tools/tar.lua b/src/luarocks/tools/tar.lua new file mode 100644 index 0000000..ba01a41 --- /dev/null +++ b/src/luarocks/tools/tar.lua @@ -0,0 +1,144 @@ + +--- A pure-Lua implementation of untar (unpacking .tar archives) +module("luarocks.tools.tar", package.seeall) + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +local blocksize = 512 + +local function get_typeflag(flag) + if flag == "0" or flag == "\0" then return "file" + elseif flag == "1" then return "link" + elseif flag == "2" then return "symlink" -- "reserved" in POSIX, "symlink" in GNU + elseif flag == "3" then return "character" + elseif flag == "4" then return "block" + elseif flag == "5" then return "directory" + elseif flag == "6" then return "fifo" + elseif flag == "7" then return "contiguous" -- "reserved" in POSIX, "contiguous" in GNU + elseif flag == "x" then return "next file" + elseif flag == "g" then return "global extended header" + elseif flag == "L" then return "long name" + elseif flag == "K" then return "long link name" + end + return "unknown" +end + +local function octal_to_number(octal) + local exp = 0 + local number = 0 + for i = #octal,1,-1 do + local digit = tonumber(octal:sub(i,i)) + if not digit then break end + number = number + (digit * 8^exp) + exp = exp + 1 + end + return number +end + +local function checksum_header(block) + local sum = 256 + for i = 1,148 do + sum = sum + block:byte(i) + end + for i = 157,500 do + sum = sum + block:byte(i) + end + return sum +end + +local function nullterm(s) + return s:match("^[^%z]*") +end + +local function read_header_block(block) + local header = {} + header.name = nullterm(block:sub(1,100)) + header.mode = nullterm(block:sub(101,108)) + header.uid = octal_to_number(nullterm(block:sub(109,116))) + header.gid = octal_to_number(nullterm(block:sub(117,124))) + header.size = octal_to_number(nullterm(block:sub(125,136))) + header.mtime = octal_to_number(nullterm(block:sub(137,148))) + header.chksum = octal_to_number(nullterm(block:sub(149,156))) + header.typeflag = get_typeflag(block:sub(157,157)) + header.linkname = nullterm(block:sub(158,257)) + header.magic = block:sub(258,263) + header.version = block:sub(264,265) + header.uname = nullterm(block:sub(266,297)) + header.gname = nullterm(block:sub(298,329)) + header.devmajor = octal_to_number(nullterm(block:sub(330,337))) + header.devminor = octal_to_number(nullterm(block:sub(338,345))) + header.prefix = block:sub(346,500) + if header.magic ~= "ustar " and header.magic ~= "ustar\0" then + return false, "Invalid header magic "..header.magic + end + if header.version ~= "00" and header.version ~= " \0" then + return false, "Unknown version "..header.version + end + if not checksum_header(block) == header.chksum then + return false, "Failed header checksum" + end + return header +end + +function untar(filename, destdir) + assert(type(filename) == "string") + assert(type(destdir) == "string") + + local tar_handle = io.open(filename, "r") + if not tar_handle then return nil, "Error opening file "..filename end + + local long_name, long_link_name + while true do + local block + repeat + block = tar_handle:read(blocksize) + until (not block) or checksum_header(block) > 256 + if not block then break end + local header, err = read_header_block(block) + if not header then + util.printerr(err) + end + + local file_data = tar_handle:read(math.ceil(header.size / blocksize) * blocksize):sub(1,header.size) + + if header.typeflag == "long name" then + long_name = nullterm(file_data) + elseif header.typeflag == "long link name" then + long_link_name = nullterm(file_data) + else + if long_name then + header.name = long_name + long_name = nil + end + if long_link_name then + header.name = long_link_name + long_link_name = nil + end + end + local pathname = dir.path(destdir, header.name) + if header.typeflag == "directory" then + fs.make_dir(pathname) + elseif header.typeflag == "file" then + local dirname = dir.dir_name(pathname) + if dirname ~= "" then + fs.make_dir(dirname) + end + local file_handle = io.open(pathname, "wb") + file_handle:write(file_data) + file_handle:close() + fs.set_time(pathname, header.mtime) + if fs.chmod then + fs.chmod(pathname, header.mode) + end + end + --[[ + for k,v in pairs(header) do + util.printout("[\""..tostring(k).."\"] = "..(type(v)=="number" and v or "\""..v:gsub("%z", "\\0").."\"")) + end + util.printout() + --]] + end + return true +end diff --git a/src/luarocks/tools/zip.lua b/src/luarocks/tools/zip.lua new file mode 100644 index 0000000..35428d9 --- /dev/null +++ b/src/luarocks/tools/zip.lua @@ -0,0 +1,245 @@ + +--- A Lua implementation of .zip file archiving (used for creating .rock files), +-- using only lua-zlib. +module("luarocks.tools.zip", package.seeall) + +local zlib = require("zlib") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") + +local function number_to_bytestring(number, nbytes) + local out = {} + for i = 1, nbytes do + local byte = number % 256 + table.insert(out, string.char(byte)) + number = (number - byte) / 256 + end + return table.concat(out) +end + +--- Begin a new file to be stored inside the zipfile. +-- @param self handle of the zipfile being written. +-- @param filename filenome of the file to be added to the zipfile. +-- @return true if succeeded, nil in case of failure. +local function zipwriter_open_new_file_in_zip(self, filename) + if self.in_open_file then + self:close_file_in_zip() + return nil + end + local lfh = {} + self.local_file_header = lfh + lfh.last_mod_file_time = 0 -- TODO + lfh.last_mod_file_date = 0 -- TODO + lfh.crc32 = 0 -- initial value + lfh.compressed_size = 0 -- unknown yet + lfh.uncompressed_size = 0 -- unknown yet + lfh.file_name_length = #filename + lfh.extra_field_length = 0 + lfh.file_name = filename:gsub("\\", "/") + lfh.external_attr = 0 -- TODO properly store permissions + self.in_open_file = true + self.data = {} + return true +end + +--- Write data to the file currently being stored in the zipfile. +-- @param self handle of the zipfile being written. +-- @param buf string containing data to be written. +-- @return true if succeeded, nil in case of failure. +local function zipwriter_write_file_in_zip(self, buf) + if not self.in_open_file then + return nil + end + local lfh = self.local_file_header + local cbuf = zlib.compress(buf):sub(3, -5) + lfh.crc32 = zlib.crc32(lfh.crc32, buf) + lfh.compressed_size = lfh.compressed_size + #cbuf + lfh.uncompressed_size = lfh.uncompressed_size + #buf + table.insert(self.data, cbuf) + return true +end + +--- Complete the writing of a file stored in the zipfile. +-- @param self handle of the zipfile being written. +-- @return true if succeeded, nil in case of failure. +local function zipwriter_close_file_in_zip(self) + local zh = self.ziphandle + + if not self.in_open_file then + return nil + end + + -- Local file header + local lfh = self.local_file_header + lfh.offset = zh:seek() + zh:write(number_to_bytestring(0x04034b50, 4)) -- signature + zh:write(number_to_bytestring(20, 2)) -- version needed to extract: 2.0 + zh:write(number_to_bytestring(0, 2)) -- general purpose bit flag + zh:write(number_to_bytestring(8, 2)) -- compression method: deflate + zh:write(number_to_bytestring(lfh.last_mod_file_time, 2)) + zh:write(number_to_bytestring(lfh.last_mod_file_date, 2)) + zh:write(number_to_bytestring(lfh.crc32, 4)) + zh:write(number_to_bytestring(lfh.compressed_size, 4)) + zh:write(number_to_bytestring(lfh.uncompressed_size, 4)) + zh:write(number_to_bytestring(lfh.file_name_length, 2)) + zh:write(number_to_bytestring(lfh.extra_field_length, 2)) + zh:write(lfh.file_name) + + -- File data + for _, cbuf in ipairs(self.data) do + zh:write(cbuf) + end + + -- Data descriptor + zh:write(number_to_bytestring(lfh.crc32, 4)) + zh:write(number_to_bytestring(lfh.compressed_size, 4)) + zh:write(number_to_bytestring(lfh.uncompressed_size, 4)) + + table.insert(self.files, lfh) + self.in_open_file = false + + return true +end + +-- @return boolean or (boolean, string): true on success, +-- false and an error message on failure. +local function zipwriter_add(self, file) + local fin + local ok, err = self:open_new_file_in_zip(file) + if not ok then + err = "error in opening "..file.." in zipfile" + else + fin = io.open(fs.absolute_name(file), "rb") + if not fin then + ok = false + err = "error opening "..file.." for reading" + end + end + if ok then + local buf = fin:read("*a") + if not buf then + err = "error reading "..file + ok = false + else + ok = self:write_file_in_zip(buf) + if not ok then + err = "error in writing "..file.." in the zipfile" + end + end + end + if fin then + fin:close() + end + if ok then + ok = self:close_file_in_zip() + if not ok then + err = "error in writing "..file.." in the zipfile" + end + end + return ok == true, err +end + +--- Complete the writing of the zipfile. +-- @param self handle of the zipfile being written. +-- @return true if succeeded, nil in case of failure. +local function zipwriter_close(self) + local zh = self.ziphandle + + local central_directory_offset = zh:seek() + + local size_of_central_directory = 0 + -- Central directory structure + for _, lfh in ipairs(self.files) do + zh:write(number_to_bytestring(0x02014b50, 4)) -- signature + zh:write(number_to_bytestring(3, 2)) -- version made by: UNIX + zh:write(number_to_bytestring(20, 2)) -- version needed to extract: 2.0 + zh:write(number_to_bytestring(0, 2)) -- general purpose bit flag + zh:write(number_to_bytestring(8, 2)) -- compression method: deflate + zh:write(number_to_bytestring(lfh.last_mod_file_time, 2)) + zh:write(number_to_bytestring(lfh.last_mod_file_date, 2)) + zh:write(number_to_bytestring(lfh.crc32, 4)) + zh:write(number_to_bytestring(lfh.compressed_size, 4)) + zh:write(number_to_bytestring(lfh.uncompressed_size, 4)) + zh:write(number_to_bytestring(lfh.file_name_length, 2)) + zh:write(number_to_bytestring(lfh.extra_field_length, 2)) + zh:write(number_to_bytestring(0, 2)) -- file comment length + zh:write(number_to_bytestring(0, 2)) -- disk number start + zh:write(number_to_bytestring(0, 2)) -- internal file attributes + zh:write(number_to_bytestring(lfh.external_attr, 4)) -- external file attributes + zh:write(number_to_bytestring(lfh.offset, 4)) -- relative offset of local header + zh:write(lfh.file_name) + size_of_central_directory = size_of_central_directory + 46 + lfh.file_name_length + end + + -- End of central directory record + zh:write(number_to_bytestring(0x06054b50, 4)) -- signature + zh:write(number_to_bytestring(0, 2)) -- number of this disk + zh:write(number_to_bytestring(0, 2)) -- number of disk with start of central directory + zh:write(number_to_bytestring(#self.files, 2)) -- total number of entries in the central dir on this disk + zh:write(number_to_bytestring(#self.files, 2)) -- total number of entries in the central dir + zh:write(number_to_bytestring(size_of_central_directory, 4)) + zh:write(number_to_bytestring(central_directory_offset, 4)) + zh:write(number_to_bytestring(0, 2)) -- zip file comment length + zh:close() + + return true +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) + + local zw = {} + + zw.ziphandle = io.open(fs.absolute_name(name), "wb") + if not zw.ziphandle then + return nil + end + zw.files = {} + zw.in_open_file = false + + zw.add = zipwriter_add + zw.close = zipwriter_close + zw.open_new_file_in_zip = zipwriter_open_new_file_in_zip + zw.write_file_in_zip = zipwriter_write_file_in_zip + zw.close_file_in_zip = zipwriter_close_file_in_zip + + return zw +end + +--- Compress files in a .zip archive. +-- @param zipfile string: pathname of .zip archive to be created. +-- @param ... Filenames to be stored in the archive are given as +-- 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) + if not zw then + return nil, "error opening "..zipfile + end + + local ok, err + for _, file in pairs({...}) do + if fs.is_dir(file) then + for _, entry in pairs(fs.find(file)) do + local fullname = dir.path(file, entry) + if fs.is_file(fullname) then + ok, err = zw:add(fullname) + if not ok then break end + end + end + else + ok, err = zw:add(file) + if not ok then break end + end + end + + local ok = zw:close() + if not ok then + return false, "error closing "..zipfile + end + return ok, err +end + diff --git a/src/luarocks/type_check.lua b/src/luarocks/type_check.lua new file mode 100644 index 0000000..28e6e7b --- /dev/null +++ b/src/luarocks/type_check.lua @@ -0,0 +1,267 @@ + +--- Type-checking functions. +-- Functions and definitions for doing a basic lint check on files +-- loaded by LuaRocks. +module("luarocks.type_check", package.seeall) + +local cfg = require("luarocks.cfg") + +rockspec_format = "1.0" + +rockspec_types = { + rockspec_format = "string", + MUST_package = "string", + MUST_version = "[%w.]+-[%d]+", + description = { + summary = "string", + detailed = "string", + homepage = "string", + license = "string", + maintainer = "string" + }, + dependencies = { + platforms = {}, + ANY = "string" + }, + supported_platforms = { + ANY = "string" + }, + external_dependencies = { + platforms = {}, + ANY = { + program = "string", + header = "string", + library = "string" + } + }, + MUST_source = { + platforms = {}, + MUST_url = "string", + md5 = "string", + file = "string", + dir = "string", + tag = "string", + branch = "string", + module = "string", + cvs_tag = "string", + cvs_module = "string" + }, + build = { + platforms = {}, + type = "string", + install = { + lua = { + MORE = true + }, + lib = { + MORE = true + }, + conf = { + MORE = true + }, + bin = { + MORE = true + } + }, + copy_directories = { + ANY = "string" + }, + MORE = true + }, + hooks = { + platforms = {}, + post_install = "string" + } +} + +function load_extensions() + rockspec_format = "1.1" + rockspec_types.deploy = { + wrap_bin_scripts = true, + } +end + +if cfg.use_extensions then + load_extensions() +end + +rockspec_types.build.platforms.ANY = rockspec_types.build +rockspec_types.dependencies.platforms.ANY = rockspec_types.dependencies +rockspec_types.external_dependencies.platforms.ANY = rockspec_types.external_dependencies +rockspec_types.MUST_source.platforms.ANY = rockspec_types.MUST_source +rockspec_types.hooks.platforms.ANY = rockspec_types.hooks + +manifest_types = { + MUST_repository = { + -- packages + ANY = { + -- versions + ANY = { + -- items + ANY = { + MUST_arch = "string", + modules = { ANY = "string" }, + commands = { ANY = "string" }, + dependencies = { ANY = "string" }, + -- TODO: to be extended with more metadata. + } + } + } + }, + MUST_modules = { + -- modules + ANY = { + -- providers + ANY = "string" + } + }, + MUST_commands = { + -- modules + ANY = { + -- commands + ANY = "string" + } + }, + dependencies = { + -- each module + ANY = { + -- each version + ANY = { + -- each dependency + ANY = { + name = "string", + constraints = { + ANY = { + no_upgrade = "boolean", + op = "string", + version = { + string = "string", + ANY = 0, + } + } + } + } + } + } + } +} + +local type_check_table + +--- Type check an object. +-- The object is compared against an archetypical value +-- matching the expected type -- the actual values don't matter, +-- only their types. Tables are type checked recursively. +-- @param name any: The object name (for error messages). +-- @param item any: The object being checked. +-- @param expected any: The reference object. In case of a table, +-- its is structured as a type reference table. +-- @param context string: A string indicating the "context" where the +-- error occurred (such as the name of the table the item is a part of), +-- to be used by error messages. +-- @return boolean or (nil, string): true if type checking +-- succeeded, or nil and an error message if it failed. +-- @see type_check_table +local function type_check_item(name, item, expected, context) + name = tostring(name) + + local item_type = type(item) + local expected_type = type(expected) + if expected_type == "number" then + if not tonumber(item) then + return nil, "Type mismatch on field "..context..name..": expected a number" + end + elseif expected_type == "string" then + if type(item) ~= "string" then + 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 + end + end + elseif expected_type == "table" then + if item_type ~= expected_type then + return nil, "Type mismatch on field "..context..name..": expected a table" + else + return type_check_table(item, expected, context..name..".") + end + elseif item_type ~= expected_type then + return nil, "Type mismatch on field "..context..name..": expected a "..expected_type + end + return true +end + +--- Type check the contents of a table. +-- The table's contents are compared against a reference table, +-- which contains the recognized fields, with archetypical values +-- matching the expected types -- the actual values of items in the +-- reference table don't matter, only their types (ie, for field x +-- in tbl that is correctly typed, type(tbl.x) == type(types.x)). +-- If the reference table contains a field called MORE, then +-- unknown fields in the checked table are accepted. +-- If it contains a field called ANY, then its type will be +-- used to check any unknown fields. If a field is prefixed +-- with MUST_, it is mandatory; its absence from the table is +-- a type error. +-- Tables are type checked recursively. +-- @param tbl table: The table to be type checked. +-- @param types table: The reference table, containing +-- values for recognized fields in the checked table. +-- @param context string: A string indicating the "context" where the +-- error occurred (such as the name of the table the item is a part of), +-- to be used by error messages. +-- @return boolean or (nil, string): true if type checking +-- succeeded, or nil and an error message if it failed. +type_check_table = function(tbl, types, context) + assert(type(tbl) == "table") + assert(type(types) == "table") + for k, v in pairs(tbl) do + local t = types[k] or (type(k) == "string" and types["MUST_"..k]) or types.ANY + if t then + local ok, err = type_check_item(k, v, t, context) + if not ok then return nil, err end + elseif types.MORE then + -- Accept unknown field + else + if not cfg.accept_unknown_fields then + return nil, "Unknown field "..k + end + end + end + for k, v in pairs(types) do + local mandatory_key = k:match("^MUST_(.+)") + if mandatory_key then + if not tbl[mandatory_key] then + return nil, "Mandatory field "..context..mandatory_key.." is missing." + end + end + 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) + assert(type(rockspec) == "table") + if rockspec.rockspec_format then + -- relies on global state + load_extensions() + end + return type_check_table(rockspec, rockspec_types, "") +end + +--- Type check a manifest table. +-- Verify the correctness of elements from a +-- manifest table, reporting on unknown fields and type +-- mismatches. +-- @return boolean or (nil, string): true if type checking +-- succeeded, or nil and an error message if it failed. +function type_check_manifest(manifest) + assert(type(manifest) == "table") + return type_check_table(manifest, manifest_types, "") +end diff --git a/src/luarocks/unpack.lua b/src/luarocks/unpack.lua new file mode 100644 index 0000000..6715381 --- /dev/null +++ b/src/luarocks/unpack.lua @@ -0,0 +1,151 @@ + +--- Module implementing the LuaRocks "unpack" command. +-- Unpack the contents of a rock. +module("luarocks.unpack", package.seeall) + +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local util = require("luarocks.util") +local build = require("luarocks.build") +local dir = require("luarocks.dir") + +help_summary = "Unpack the contents of a rock." +help_arguments = "{<rock>|<name> [<version>]}" +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. +]] + +--- Load a rockspec file to the given directory, fetches the source +-- files specified in the rockspec, and unpack them inside the directory. +-- @param rockspec_file string: The URL for a rockspec file. +-- @param dir_name string: The directory where to store and unpack files. +-- @return table or (nil, string): the loaded rockspec table or +-- nil and an error message. +local function unpack_rockspec(rockspec_file, dir_name) + assert(type(rockspec_file) == "string") + assert(type(dir_name) == "string") + + local rockspec, err = fetch.load_rockspec(rockspec_file) + if not rockspec then + return nil, "Failed loading rockspec "..rockspec_file..": "..err + end + fs.change_dir(dir_name) + local ok, sources_dir = fetch.fetch_sources(rockspec, true, ".") + if not ok then + return nil, sources_dir + end + fs.change_dir(dir_name) + build.apply_patches(rockspec) + fs.pop_dir() + return rockspec +end + +--- Load a .rock file to the given directory and unpack it inside it. +-- @param rock_file string: The URL for a .rock file. +-- @param dir_name string: The directory where to unpack. +-- @param kind string: the kind of rock file, as in the second-level +-- extension in the rock filename (eg. "src", "all", "linux-x86") +-- @return table or (nil, string): the loaded rockspec table or +-- nil and an error message. +local function unpack_rock(rock_file, dir_name, kind) + assert(type(rock_file) == "string") + assert(type(dir_name) == "string") + + local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, dir_name) + if not ok then + return nil, "Failed unzipping rock "..rock_file, errcode + end + fs.change_dir(dir_name) + local rockspec_file = dir_name..".rockspec" + local rockspec, err = fetch.load_rockspec(rockspec_file) + if not rockspec then + return nil, "Failed loading rockspec "..rockspec_file..": "..err + end + if kind == "src" then + if rockspec.source.file then + local ok, err = fs.unpack_archive(rockspec.source.file) + if not ok then + return nil, err + end + fs.change_dir(rockspec.source.dir) + build.apply_patches(rockspec) + fs.pop_dir() + end + end + return rockspec +end + +--- Create a directory and perform the necessary actions so that +-- the sources for the rock and its rockspec are unpacked inside it, +-- laid out properly so that the 'make' command is able to build the module. +-- @param file string: A rockspec or .rock URL. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +local function run_unpacker(file) + assert(type(file) == "string") + + local base_name = dir.base_name(file) + local dir_name, kind, extension = base_name:match("(.*)%.([^.]+)%.(rock)$") + if not extension then + dir_name, extension = base_name:match("(.*)%.(rockspec)$") + kind = "rockspec" + end + if not extension then + return nil, file.." does not seem to be a valid filename." + end + + if (fs.exists(dir_name)) then + return nil, "Directory "..dir_name.." already exists." + end + fs.make_dir(dir_name) + local rollback = util.schedule_function(fs.delete, fs.absolute_name(dir_name)) + + local rockspec, err + if extension == "rock" then + rockspec, err = unpack_rock(file, dir_name, kind) + elseif extension == "rockspec" then + rockspec, err = unpack_rockspec(file, dir_name) + end + if not rockspec then + return nil, err + end + if kind == "src" or kind == "rockspec" then + if rockspec.source.dir ~= "." then + local ok = fs.copy(rockspec.local_filename, rockspec.source.dir) + if not ok then + return nil, "Failed copying unpacked rockspec into unpacked source directory." + end + end + util.printout() + util.printout("Done. You may now enter directory ") + util.printout(dir.path(dir_name, rockspec.source.dir)) + util.printout("and type 'luarocks make' to build.") + end + util.remove_scheduled_function(rollback) + return true +end + +--- Driver function for the "unpack" command. +-- @param name string: may be a rock filename, for unpacking a +-- rock file or the name of a rock to be fetched and unpacked. +-- @param version string or nil: if the name of a package is given, a +-- version may also be passed. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +function run(...) + local flags, name, version = util.parse_flags(...) + + assert(type(version) == "string" or not version) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("unpack") + end + + if name:match(".*%.rock") or name:match(".*%.rockspec") then + return run_unpacker(name) + else + local search = require("luarocks.search") + return search.act_on_src_or_rockspec(run_unpacker, name, version) + end +end diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua new file mode 100644 index 0000000..ba20acf --- /dev/null +++ b/src/luarocks/util.lua @@ -0,0 +1,504 @@ + +--- Assorted utilities for managing tables, plus a scheduler for rollback functions. +-- Does not requires modules directly (only as locals +-- inside specific functions) to avoid interdependencies, +-- as this is used in the bootstrapping stage of luarocks.cfg. + +local global_env = _G + +module("luarocks.util", package.seeall) + +local scheduled_functions = {} +local debug = require("debug") + +--- Schedule a function to be executed upon program termination. +-- This is useful for actions such as deleting temporary directories +-- or failure rollbacks. +-- @param f function: Function to be executed. +-- @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, ...) + assert(type(f) == "function") + + local item = { fn = f, args = {...} } + table.insert(scheduled_functions, item) + return item +end + +--- Unschedule a function. +-- 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) + for k, v in pairs(scheduled_functions) do + if v == item then + table.remove(scheduled_functions, k) + return + end + end +end + +--- Execute scheduled functions. +-- Some calls create temporary files and/or directories and register +-- 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() + local fs = require("luarocks.fs") + fs.change_dir_to_root() + for i = #scheduled_functions, 1, -1 do + local item = scheduled_functions[i] + item.fn(unpack(item.args)) + end +end + +--- Produce a Lua pattern that matches precisely the given string +-- (this is suitable to be concatenating to other patterns, +-- 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) + return (s:gsub("[?%-+*%[%].%%()$^]","%%%1")) +end + +--- Extract flags from an arguments list. +-- Given string arguments, extract flag arguments into a flags set. +-- 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(...) + local args = {...} + local flags = {} + for i = #args, 1, -1 do + local flag = args[i]:match("^%-%-(.*)") + if flag then + local var,val = flag:match("([a-z_%-]*)=(.*)") + if val then + flags[var] = val + else + flags[flag] = true + end + table.remove(args, i) + end + end + return flags, unpack(args) +end + +--- Build a sequence of flags for forwarding from one command to +-- another (for example, from "install" to "build"). +-- @param flags table: A table of parsed flags +-- @param ... string...: A variable number of flags to be checked +-- 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, ...) + assert(type(flags) == "table") + local out = {} + local filter = select('#', ...) + local function add_flag(flagname) + if flags[flagname] then + if flags[flagname] == true then + table.insert(out, "--"..flagname) + else + table.insert(out, "--"..flagname.."="..flags[flagname]) + end + end + end + if filter > 0 then + for i = 1, filter do + add_flag(select(i, ...)) + end + else + for flagname, _ in pairs(flags) do + add_flag(flagname) + end + end + return unpack(out) +end + +--- Merges contents of src on top of dst's contents. +-- @param dst Destination table, which will receive src's contents. +-- @param src Table which provides new contents to dst. +-- @see platform_overrides +function 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) + else + dst[k] = v + end + else + dst[k] = v + end + end +end + +--- Perform platform-specific overrides on a table. +-- Overrides values of table with the contents of the appropriate +-- subset of its "platforms" field. The "platforms" field should +-- be a table containing subtables keyed with strings representing +-- platform names. Names that match the contents of the global +-- cfg.platforms setting are used. For example, if +-- cfg.platforms= {"foo"}, then the fields of +-- tbl.platforms.foo will overwrite those of tbl with the same +-- names. For table values, the operation is performed recursively +-- (tbl.platforms.foo.x.y.z overrides tbl.x.y.z; other contents of +-- 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) + assert(type(tbl) == "table" or not tbl) + + local cfg = require("luarocks.cfg") + + if not tbl then return end + + if tbl.platforms then + for _, platform in ipairs(cfg.platforms) do + local platform_tbl = tbl.platforms[platform] + if platform_tbl then + deep_merge(tbl, platform_tbl) + end + end + end + tbl.platforms = nil +end + +local var_format_pattern = "%$%((%a[%a%d_]+)%)" + +--- Create a new shallow copy of a table: a new table with +-- the same keys and values. Keys point to the same objects as +-- the original table (ie, does not copy recursively). +-- @param tbl table: the input table +-- @return table: a new table with the same contents. +local function make_shallow_copy(tbl) + local copy = {} + for k,v in pairs(tbl) do + copy[k] = v + end + return copy +end + +-- Check if a set of needed variables are referenced +-- somewhere in a list of definitions, warning the user +-- about any unused ones. Each key in needed_set should +-- appear as a $(XYZ) variable at least once as a +-- substring of some value of var_defs. +-- @param var_defs: a table with string keys and string +-- values, containing variable definitions. +-- @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) + needed_set = make_shallow_copy(needed_set) + for var,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)) + end +end + +-- Output any entries that might remain in $(XYZ) format, +-- warning the user that substitutions have failed. +-- @param line string: the input string +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) + any_failed = true + end + end + return any_failed +end + +--- Perform make-style variable substitutions on string values of a table. +-- For every string value tbl.x which contains a substring of the format +-- "$(XYZ)" will have this substring replaced by vars["XYZ"], if that field +-- exists in vars. Only string values are processed; this function +-- does not scan subtables recursively. +-- @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) + assert(type(tbl) == "table") + assert(type(vars) == "table") + + local updated = {} + for k, v in pairs(tbl) do + if type(v) == "string" then + updated[k] = v:gsub(var_format_pattern, vars) + if warn_failed_matches(updated[k]) then + updated[k] = updated[k]:gsub(var_format_pattern, "") + end + end + end + for k, v in pairs(updated) do + tbl[k] = v + end +end + +--- Return an array of keys of a table. +-- @param tbl table: The input table. +-- @return table: The array of keys. +function keys(tbl) + local ks = {} + for k,_ in pairs(tbl) do + table.insert(ks, k) + end + return ks +end + +local function default_sort(a, b) + local ta = type(a) + local tb = type(b) + if ta == "number" and tb == "number" then + return a < b + elseif ta == "number" then + return true + elseif tb == "number" then + return false + else + return tostring(a) < tostring(b) + end +end + +-- The iterator function used internally by util.sortedpairs. +-- @param tbl table: The table to be iterated. +-- @param sort_function function or nil: An optional comparison function +-- to be used by table.sort when sorting keys. +-- @see sortedpairs +local function sortedpairs_iterator(tbl, sort_function) + local ks = keys(tbl) + if not sort_function or type(sort_function) == "function" then + table.sort(ks, sort_function or default_sort) + for _, k in ipairs(ks) do + coroutine.yield(k, tbl[k]) + end + else + local order = sort_function + local done = {} + for _, k in ipairs(order) do + local sub_order + if type(k) == "table" then + sub_order = k[2] + k = k[1] + end + if tbl[k] then + done[k] = true + coroutine.yield(k, tbl[k], sub_order) + end + end + table.sort(ks, default_sort) + for _, k in ipairs(ks) do + if not done[k] then + coroutine.yield(k, tbl[k]) + end + end + end +end + +--- A table iterator generator that returns elements sorted by key, +-- to be used in "for" loops. +-- @param tbl table: The table to be iterated. +-- @param sort_function function or table or nil: An optional comparison function +-- to be used by table.sort when sorting keys, or an array listing an explicit order +-- for keys. If a value itself is an array, it is taken so that the first element +-- is a string representing the field name, and the second element is a priority table +-- for that key. +-- @return function: the iterator function. +function sortedpairs(tbl, sort_function) + return coroutine.wrap(function() sortedpairs_iterator(tbl, sort_function) end) +end + +function starts_with(s, prefix) + return s:sub(1,#prefix) == prefix +end + +--- Print a line to standard output +function printout(...) + io.stdout:write(table.concat({...},"\t")) + io.stdout:write("\n") +end + +--- Print a line to standard error +function 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) +end + +function title(msg, porcelain, underline) + if porcelain then return end + printout() + printout(msg) + printout((underline or "-"):rep(#msg)) + printout() +end + +function this_program(default) + local i = 1 + local last, cur = default, default + while i do + local dbg = debug.getinfo(i,"S") + if not dbg then break end + last = cur + cur = dbg.source + i=i+1 + end + return last:sub(2) +end + +function see_help(command, program) + return "See '"..this_program(program or "luarocks")..' help '..command.."'." +end + +-- from http://lua-users.org/wiki/SplitJoin +-- by PhilippeLhoste +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 + +--- Remove repeated entries from a path-style string. +-- Example: given ("a;b;c;a;b;d", ";"), returns "a;b;c;d". +-- @param list string: A path string (from $PATH or package.path) +-- @param sep string: The separator +function remove_path_dupes(list, sep) + assert(type(list) == "string") + assert(type(sep) == "string") + local parts = split_string(list, sep) + local final, entries = {}, {} + for _, part in ipairs(parts) do + if not entries[part] then + table.insert(final, part) + entries[part] = true + end + end + return table.concat(final, sep) +end + +--- +-- Formats tables with cycles recursively to any depth. +-- References to other tables are shown as values. +-- Self references are indicated. +-- The string returned is "Lua code", which can be procesed +-- (in the case in which indent is composed by spaces or "--"). +-- Userdata and function keys and values are shown as strings, +-- which logically are exactly not equivalent to the original code. +-- This routine can serve for pretty formating tables with +-- proper indentations, apart from printing them: +-- io.write(table.show(t, "t")) -- a typical use +-- Written by Julio Manuel Fernandez-Diaz, +-- Heavily based on "Saving tables with cycles", PIL2, p. 113. +-- @param t table: is the table. +-- @param name string: is the name of the table (optional) +-- @param indent string: is a first indentation (optional). +-- @return string: the pretty-printed table +function show_table(t, name, indent) + local cart -- a container + local autoref -- for self references + + local function isemptytable(t) return next(t) == nil end + + local function basicSerialize (o) + local so = tostring(o) + if type(o) == "function" then + local info = debug.getinfo(o, "S") + -- info.name is nil because o is not a calling level + if info.what == "C" then + return ("%q"):format(so .. ", C function") + else + -- the information is defined through lines + return ("%q"):format(so .. ", defined in (" .. info.linedefined .. "-" .. info.lastlinedefined .. ")" .. info.source) + end + elseif type(o) == "number" then + return so + else + return ("%q"):format(so) + end + end + + local function addtocart (value, name, indent, saved, field) + indent = indent or "" + saved = saved or {} + field = field or name + + cart = cart .. indent .. field + + if type(value) ~= "table" then + cart = cart .. " = " .. basicSerialize(value) .. ";\n" + else + if saved[value] then + cart = cart .. " = {}; -- " .. saved[value] .. " (self reference)\n" + autoref = autoref .. name .. " = " .. saved[value] .. ";\n" + else + saved[value] = name + --if tablecount(value) == 0 then + if isemptytable(value) then + cart = cart .. " = {};\n" + else + cart = cart .. " = {\n" + for k, v in pairs(value) do + k = basicSerialize(k) + local fname = ("%s[%s]"):format(name, k) + field = ("[%s]"):format(k) + -- three spaces between levels + addtocart(v, fname, indent .. " ", saved, field) + end + cart = cart .. indent .. "};\n" + end + end + end + end + + name = name or "__unnamed__" + if type(t) ~= "table" then + return name .. " = " .. basicSerialize(t) + end + cart, autoref = "", "" + addtocart(t, name, indent) + return cart .. autoref +end + +function array_contains(tbl, value) + for _, v in ipairs(tbl) do + if v == value then + return true + end + end + return false +end diff --git a/src/luarocks/validate.lua b/src/luarocks/validate.lua new file mode 100644 index 0000000..1e1e69e --- /dev/null +++ b/src/luarocks/validate.lua @@ -0,0 +1,160 @@ + +--- Sandboxed test of build/install of all packages in a repository (unfinished and disabled). +module("luarocks.validate", package.seeall) + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local path = require("luarocks.path") +local cfg = require("luarocks.cfg") +local build = require("luarocks.build") +local install = require("luarocks.install") +local util = require("luarocks.util") + +help_summary = "Sandboxed test of build/install of all packages in a repository." + +help = [[ +<argument>, if given, is a local repository pathname. +]] + +local function save_settings(repo) + local protocol, path = dir.split_url(repo) + table.insert(cfg.rocks_servers, 1, protocol.."://"..path) + return { + root_dir = cfg.root_dir, + rocks_dir = cfg.rocks_dir, + deploy_bin_dir = cfg.deploy_bin_dir, + deploy_lua_dir = cfg.deploy_lua_dir, + deploy_lib_dir = cfg.deploy_lib_dir, + } +end + +local function restore_settings(settings) + cfg.root_dir = settings.root_dir + cfg.rocks_dir = settings.rocks_dir + cfg.deploy_bin_dir = settings.deploy_bin_dir + cfg.deploy_lua_dir = settings.deploy_lua_dir + cfg.deploy_lib_dir = settings.deploy_lib_dir + cfg.variables.ROCKS_TREE = settings.rocks_dir + cfg.variables.SCRIPTS_DIR = settings.deploy_bin_dir + table.remove(cfg.rocks_servers, 1) +end + +local function prepare_sandbox(file) + local root_dir = fs.make_temp_dir(file):gsub("/+$", "") + cfg.root_dir = root_dir + cfg.rocks_dir = path.rocks_dir(root_dir) + cfg.deploy_bin_dir = path.deploy_bin_dir(root_dir) + cfg.variables.ROCKS_TREE = cfg.rocks_dir + cfg.variables.SCRIPTS_DIR = cfg.deploy_bin_dir + return root_dir +end + +local function validate_rockspec(file) + local ok, err, errcode = build.build_rockspec(file, true, "one") + if not ok then + util.printerr(err) + end + return ok, err, errcode +end + +local function validate_src_rock(file) + local ok, err, errcode = build.build_rock(file, false, "one") + if not ok then + util.printerr(err) + end + return ok, err, errcode +end + +local function validate_rock(file) + local ok, err, errcode = install.install_binary_rock(file, "one") + if not ok then + util.printerr(err) + end + return ok, err, errcode +end + +local function validate(repo, flags) + local results = { + ok = {} + } + local settings = save_settings(repo) + local sandbox + if flags["quick"] then + sandbox = prepare_sandbox("luarocks_validate") + end + if not fs.exists(repo) then + return nil, repo.." is not a local repository." + end + for _, file in pairs(fs.list_dir(repo)) do for _=1,1 do + if file == "manifest" or file == "index.html" then + break -- continue for + end + local pathname = fs.absolute_name(dir.path(repo, file)) + if not flags["quick"] then + sandbox = prepare_sandbox(file) + end + local ok, err, errcode + util.printout() + util.printout("Verifying "..pathname) + if file:match("%.rockspec$") then + ok, err, errcode = validate_rockspec(pathname, "one") + elseif file:match("%.src%.rock$") then + ok, err, errcode = validate_src_rock(pathname) + elseif file:match("%.rock$") then + ok, err, errcode = validate_rock(pathname) + end + if ok then + table.insert(results.ok, {file=file} ) + else + if not errcode then + errcode = "misc" + end + if not results[errcode] then + results[errcode] = {} + end + table.insert(results[errcode], {file=file, err=err} ) + end + util.run_scheduled_functions() + if not flags["quick"] then + fs.delete(sandbox) + end + repeat until not fs.pop_dir() + end end + if flags["quick"] then + fs.delete(sandbox) + end + restore_settings(settings) + util.title("Results:") + util.printout("OK: "..tostring(#results.ok)) + for _, entry in ipairs(results.ok) do + util.printout(entry.file) + end + for errcode, errors in pairs(results) do + if errcode ~= "ok" then + util.printout() + util.printout(errcode.." errors: "..tostring(#errors)) + for _, entry in ipairs(errors) do + util.printout(entry.file, entry.err) + end + end + end + + util.title("Summary:") + local total = 0 + for errcode, errors in pairs(results) do + util.printout(errcode..": "..tostring(#errors)) + total = total + #errors + end + util.printout("Total: "..total) + return true +end + +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 + diff --git a/test/testing.sh b/test/testing.sh new file mode 100755 index 0000000..c06bd27 --- /dev/null +++ b/test/testing.sh @@ -0,0 +1,269 @@ +#!/bin/sh -e + +# Setup ######################################### + +[ -e ../configure ] || { + echo "Please run this from the test/ directory." + exit 1 +} + +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" + +[ "$1" ] || rm -f luacov.stats.out +rm -f luacov.report.out +rm -rf /tmp/luarocks_testing +mkdir /tmp/luarocks_testing +rm -rf "$testing_tree" +rm -rf "$testing_sys_tree" +rm -rf "$testing_tree_copy" +rm -rf "$testing_sys_tree_copy" +rm -rf "$testing_cache" + +cat <<EOF > $testing_dir/testing_config.lua +rocks_trees = { + "$testing_tree", + "$testing_sys_tree", +} +local_cache = "$testing_cache" +upload_server = "testing" +upload_user = "hisham" +upload_servers = { + testing = { + rsync = "localhost/tmp/luarocks_testing", + }, +} +EOF +cat <<EOF > $testing_dir/testing_config_sftp.lua +rocks_trees = { + "$testing_tree", + "$testing_sys_tree", +} +local_cache = "$testing_cache" +upload_server = "testing" +upload_user = "hisham" +upload_servers = { + testing = { + sftp = "localhost/tmp/luarocks_testing", + }, +} +EOF +cat <<EOF > $testing_dir/luacov.config +return { + ["configfile"] = ".luacov", + ["statsfile"] = "$testing_dir/luacov.stats.out", + ["reportfile"] = "$testing_dir/luacov.report.out", + runreport = false, + deletestats = false, + ["include"] = {}, + ["exclude"] = { + "luacov$", + "luacov%.reporter$", + "luacov%.defaults$", + "luacov%.runner$", + "luacov%.stats$", + "luacov%.tick$", + }, +} +EOF + +export LUAROCKS_CONFIG="$testing_dir/testing_config.lua" +export LUA_PATH= +export LUA_CPATH= + +luadir="/Programs/Lua/Current" +lua="$luadir/bin/lua" + +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 + +build_environment() { + rm -rf "$testing_tree" + rm -rf "$testing_sys_tree" + rm -rf "$testing_tree_copy" + rm -rf "$testing_sys_tree_copy" + mkdir -p "$testing_tree" + mkdir -p "$testing_sys_tree" + for package in "$@" + do + $luarocks_nocov build --tree="$testing_sys_tree" $package + done + eval `$luarocks_nocov path --bin` + cp -a "$testing_tree" "$testing_tree_copy" + cp -a "$testing_sys_tree" "$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" +} + +# Tests ######################################### + +test_version() { $luarocks --version; } + +fail_arg_server() { $luarocks --server; } +fail_arg_only_server() { $luarocks --only-server; } +fail_unknown_command() { $luarocks unknown_command; } + +test_empty_list() { $luarocks list; } + +fail_build_noarg() { $luarocks build; } +fail_download_noarg() { $luarocks download; } +fail_install_noarg() { $luarocks install; } +fail_lint_noarg() { $luarocks lint; } +fail_search_noarg() { $luarocks search; } +fail_show_noarg() { $luarocks show; } +fail_unpack_noarg() { $luarocks unpack; } +fail_new_version_noarg() { $luarocks new_version; } + +fail_build_invalid() { $luarocks build invalid; } +fail_download_invalid() { $luarocks download invalid; } +fail_install_invalid() { $luarocks install invalid; } +fail_lint_invalid() { $luarocks lint invalid; } +fail_show_invalid() { $luarocks show invalid; } +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_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_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_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_with_bin() { $luarocks install wsapi; } + +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_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_new_version() { $luarocks download --rockspec luacov 0.1 && $luarocks new_version ./luacov-0.1-1.rockspec 0.2 && rm ./luacov-0.*; } + +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_path() { $luarocks path --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_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_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_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"; } +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_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; } + +# Driver ######################################### + +run_tests() { + grep "^test_$1.*(" < $testing_dir/testing.sh | cut -d'(' -f1 | while read test + do + echo "-------------------------------------------" + echo "$test" + echo "-------------------------------------------" + reset_environment + if $test + then echo "OK: Expected success." + else echo "FAIL: Unexpected failure."; exit 1 + fi + done + + grep "^fail_$1.*(" < $testing_dir/testing.sh | cut -d'(' -f1 | while read test + do + echo "-------------------------------------------" + echo "$test" + echo "-------------------------------------------" + reset_environment + if $test + then echo "FAIL: Unexpected success."; exit 1 + else echo "OK: Expected failure." + fi + done +} + +run_with_minimal_environment() { + build_environment luacov + run_tests $1 +} + +run_with_full_environment() { + build_environment luacov luafilesystem luasocket luabitop luaposix md5 lzlib + run_tests $1 +} + +run_all_tests() { + run_with_minimal_environment $1 + run_with_full_environment $1 +} + +run_all_tests $1 +#run_with_minimal_environment $1 + +$testing_sys_tree/bin/luacov -c $testing_dir/luacov.config src/luarocks src/bin + +cat $testing_dir/luacov.report.out diff --git a/win32/bin/7z.dll b/win32/bin/7z.dll Binary files differnew file mode 100644 index 0000000..c0ff7fb --- /dev/null +++ b/win32/bin/7z.dll diff --git a/win32/bin/7z.exe b/win32/bin/7z.exe Binary files differnew file mode 100644 index 0000000..5e3d6f9 --- /dev/null +++ b/win32/bin/7z.exe diff --git a/win32/bin/LuaRocks.reg.template b/win32/bin/LuaRocks.reg.template new file mode 100644 index 0000000..82cc180 --- /dev/null +++ b/win32/bin/LuaRocks.reg.template @@ -0,0 +1,59 @@ +Windows Registry Editor Version 5.00 + +[HKEY_CLASSES_ROOT\.rockspec] +@="Lua.Rockspec" + +[HKEY_CLASSES_ROOT\.rockspec\Content Type] +@="text/plain" + +[HKEY_CLASSES_ROOT\.rockspec\PerceivedType] +@="text" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.rockspec] +@="Lua.Rockspec" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.rockspec\Content Type] +@="text/plain" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.rockspec\PerceivedType] +@="text" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Lua.Rockspec] +@="Lua Rockspec File" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Lua.Rockspec\DefaultIcon] +@="<LUAROCKSPATH>lua.ico" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Lua.Rockspec\Shell] +@="Edit" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Lua.Rockspec\Shell\Fetch] +@="Fetch and install" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Lua.Rockspec\Shell\Fetch\Command] +@="\"<LUAROCKSPATH>luarocksw.bat\" install \"%1\"" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Lua.Rockspec\Shell\Install] +@="Install Lua module" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Lua.Rockspec\Shell\Install\Command] +@="\"<LUAROCKSPATH>luarocksw.bat\" make \"%1\"" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Lua.Rockspec\Shell\Uninstall] +@="Uninstall Lua module (this version)" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Lua.Rockspec\Shell\Uninstall\Command] +@="\"<LUAROCKSPATH>luarocksw.bat\" remove \"%1\"" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Lua.Rockspec\Shell\UninstallAll] +@="Uninstall Lua module (all versions)" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Lua.Rockspec\Shell\UninstallAll\Command] +@="\"<LUAROCKSPATH>luarocksw.bat\" removeall \"%1\"" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Lua.Rockspec\Shell\Edit] +@="Edit" + +[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Lua.Rockspec\Shell\Edit\command] +@="notepad.exe \"%1\"" + diff --git a/win32/bin/chmod.exe b/win32/bin/chmod.exe Binary files differnew file mode 100644 index 0000000..9655cfd --- /dev/null +++ b/win32/bin/chmod.exe diff --git a/win32/bin/cp.exe b/win32/bin/cp.exe Binary files differnew file mode 100755 index 0000000..0ef4fe8 --- /dev/null +++ b/win32/bin/cp.exe diff --git a/win32/bin/create_reg_file.lua b/win32/bin/create_reg_file.lua new file mode 100644 index 0000000..2eb7583 --- /dev/null +++ b/win32/bin/create_reg_file.lua @@ -0,0 +1,51 @@ +-- Call this file using its full path and the template file as a parameter; +-- +-- C:\> lua.exe "create_reg_file.lua" "c:\luarocks\2.0\LuaRocks.reg.template" +-- +-- it will strip the ".template" extension and write to that file the +-- template contents, where "<LUAROCKSPATH>" will be replaced by the path +-- to LuaRocks (including the trailing backslash) + + + +-- Check argument +local f = (arg or {})[1] +if not f then + print("must provide template file on command line") + os.exit(1) +end + +-- cleanup filepath, remove all double backslashes +while f:match("\\\\") do + f = f:gsub("\\\\", "\\") +end + +-- extract path and name from argument +local p = "" +local ni = f +for i = #f, 1, -1 do + if f:sub(i,i) == "\\" then + ni = f:sub(i+1) + p = f:sub(1, i) + break + end +end + +-- create output name +local no = ni:gsub("%.template","") + +-- create path substitute; escape backslash by doubles +local ps = p:gsub("\\", "\\\\") + +-- read template +local fh = io.open(f) +local content = fh:read("*a") +fh:close() + +-- fill template +content = content:gsub("%<LUAROCKSPATH%>", ps) + +-- write destination file +fh = io.open(p..no, "w+") +fh:write(content) +fh:close() diff --git a/win32/bin/find.exe b/win32/bin/find.exe Binary files differnew file mode 100755 index 0000000..85192fb --- /dev/null +++ b/win32/bin/find.exe diff --git a/win32/bin/libeay32.dll b/win32/bin/libeay32.dll Binary files differnew file mode 100644 index 0000000..8d31f86 --- /dev/null +++ b/win32/bin/libeay32.dll diff --git a/win32/bin/libiconv2.dll b/win32/bin/libiconv2.dll Binary files differnew file mode 100644 index 0000000..544dd92 --- /dev/null +++ b/win32/bin/libiconv2.dll diff --git a/win32/bin/libintl3.dll b/win32/bin/libintl3.dll Binary files differnew file mode 100644 index 0000000..ec11e6b --- /dev/null +++ b/win32/bin/libintl3.dll diff --git a/win32/bin/libssl32.dll b/win32/bin/libssl32.dll Binary files differnew file mode 100644 index 0000000..a30ff0e --- /dev/null +++ b/win32/bin/libssl32.dll diff --git a/win32/bin/ls.exe b/win32/bin/ls.exe Binary files differnew file mode 100755 index 0000000..96ff2e5 --- /dev/null +++ b/win32/bin/ls.exe diff --git a/win32/bin/lua.ico b/win32/bin/lua.ico Binary files differnew file mode 100644 index 0000000..56dc4fe --- /dev/null +++ b/win32/bin/lua.ico diff --git a/win32/bin/luarocksw.bat b/win32/bin/luarocksw.bat new file mode 100644 index 0000000..313508d --- /dev/null +++ b/win32/bin/luarocksw.bat @@ -0,0 +1,49 @@ +@echo off +setlocal +SET MYPATH=%~dp0 + +IF NOT [%1]==[] GOTO LETSGO +ECHO Same as 'luarocks' command, except this +ECHO command will pause after completion, allowing for +ECHO examination of output. +ECHO. +ECHO For LuaRocks help use: +ECHO LUAROCKS HELP +ECHO. +ECHO OPTIONS specific for LUAROCKSW: +ECHO REMOVEALL is a command specific to this batch file +ECHO the option takes a FULL ROCKSPEC filename and then +ECHO it will strip path, version and extension info from +ECHO it before executing the LUAROCKS REMOVE command +ECHO Example: +ECHO luarocksw remove "c:\somedir\modulename-1.0-1.rockspec" +ECHO will execute: +ECHO luarocks remove "c:\somedir\modulename-1.0-1.rockspec" +ECHO and will only remove the specific version 1.0 from the +ECHO system. +ECHO luarocksw removeall "c:\somedir\modulename-1.0-1.rockspec" +ECHO will execute: +ECHO luarocks remove modulename +ECHO and will remove all versions of this package +ECHO. +GOTO END + +:LETSGO +REM if REMOVEALL command then info must be stripped from the parameter +if [%1]==[removeall] goto REMOVEALL + +REM execute LuaRocks and wait for results +echo executing: luarocks %* +call %MYPATH%luarocks %* +pause +goto END + +:REMOVEALL +for /f "delims=-" %%a in ("%~n2") do ( + echo executing: luarocks remove %%a + %MYPATH%luarocks remove "%%a" + pause + goto END +) + +:END
\ No newline at end of file diff --git a/win32/bin/md5sum.exe b/win32/bin/md5sum.exe Binary files differnew file mode 100644 index 0000000..4ae9f74 --- /dev/null +++ b/win32/bin/md5sum.exe diff --git a/win32/bin/mkdir.exe b/win32/bin/mkdir.exe Binary files differnew file mode 100755 index 0000000..83e57d9 --- /dev/null +++ b/win32/bin/mkdir.exe diff --git a/win32/bin/mv.exe b/win32/bin/mv.exe Binary files differnew file mode 100755 index 0000000..9fb65bb --- /dev/null +++ b/win32/bin/mv.exe diff --git a/win32/bin/objdump.exe b/win32/bin/objdump.exe Binary files differnew file mode 100644 index 0000000..4429d10 --- /dev/null +++ b/win32/bin/objdump.exe diff --git a/win32/bin/pwd.exe b/win32/bin/pwd.exe Binary files differnew file mode 100755 index 0000000..7dd114d --- /dev/null +++ b/win32/bin/pwd.exe diff --git a/win32/bin/rclauncher.c b/win32/bin/rclauncher.c new file mode 100644 index 0000000..77459f4 --- /dev/null +++ b/win32/bin/rclauncher.c @@ -0,0 +1,139 @@ + +/* +** Simple Lua interpreter. +** This program is used to run a Lua file embedded as a resource. +** It creates a Lua state, opens all its standard libraries, and run +** the Lua file in a protected environment just to redirect the error +** messages to stdout and stderr. +** +** $Id: rclauncher.c,v 1.1 2008/06/30 14:29:59 carregal Exp $ +*/ + +#include <string.h> +#include <stdlib.h> + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +#include <windows.h> +#include <io.h> +#include <fcntl.h> + +/* +** Report error message. +** Assumes that the error message is on top of the stack. +*/ +static int report (lua_State *L) { + fprintf (stderr, "lua: fatal error: `%s'\n", lua_tostring (L, -1)); + fflush (stderr); + printf ("Content-type: text/plain\n\nConfiguration fatal error: see error log!\n"); + printf ("%s", lua_tostring(L, -1)); + return 1; +} + +static int runlua (lua_State *L, const char *lua_string, int argc, char *argv[]) { + int err_func; + int err; + + lua_getglobal(L, "debug"); + lua_pushliteral(L, "traceback"); + lua_gettable(L, -2); + err_func = lua_gettop (L); + err = luaL_loadstring (L, lua_string); + if(!err) { + int i; + // fill global arg table + lua_getglobal(L, "arg"); + for(i = 1; i < argc; i++) + { + lua_pushstring(L, argv[i]); + lua_rawseti(L, -2, i); + } + lua_pop(L, 1); + // fill parameters (in vararg '...') + for(i = 1; i < argc; i++) + lua_pushstring(L, argv[i]); + return lua_pcall (L, argc - 1, LUA_MULTRET, err_func); + } else return err; +} + +static DWORD GetModulePath( HINSTANCE hInst, LPTSTR pszBuffer, DWORD dwSize ) +// +// Return the size of the path in bytes. +{ + DWORD dwLength = GetModuleFileName( hInst, pszBuffer, dwSize ); + if( dwLength ) + { + while( dwLength && pszBuffer[ dwLength ] != '.' ) + { + dwLength--; + } + + if( dwLength ) + pszBuffer[ dwLength ] = '\000'; + } + return dwLength; +} + + +/* +** MAIN +*/ +int main (int argc, char *argv[]) { + char name[ MAX_PATH ]; + DWORD dwLength; + int size; + luaL_Buffer b; + int i; +#ifdef UNICODE + TCHAR lua_wstring[4098]; +#endif + char lua_string[4098]; + lua_State *L = luaL_newstate(); + (void)argc; /* avoid "unused parameter" warning */ + luaL_openlibs(L); + lua_newtable(L); // create arg table + lua_pushstring(L, argv[0]); // add interpreter to arg table + lua_rawseti(L, -2, -1); + dwLength = GetModulePath( NULL, name, MAX_PATH ); + if(dwLength) { /* Optional bootstrap */ + strcat(name, ".lua"); + lua_pushstring(L, name); // add lua script to arg table + lua_rawseti(L, -2, 0); + lua_setglobal(L,"arg"); // set global arg table + if(!luaL_loadfile (L, name)) { + if(lua_pcall (L, 0, LUA_MULTRET, 0)) { + report (L); + lua_close (L); + return EXIT_FAILURE; + } + } + } + else + { + lua_pushstring(L, argv[0]); // no lua script, so add interpreter again, now as lua script + lua_rawseti(L, -2, 0); + lua_setglobal(L,"arg"); // set global arg table + } + + luaL_buffinit(L, &b); + for(i = 1; ; i++) { +#ifdef UNICODE + size = LoadString(GetModuleHandle(NULL), i, lua_wstring, + sizeof(lua_string)/sizeof(TCHAR)); + if(size > 0) wcstombs(lua_string, lua_wstring, size + 1); +#else + size = LoadString(GetModuleHandle(NULL), i, lua_string, + sizeof(lua_string)/sizeof(char)); +#endif + if(size) luaL_addlstring(&b, lua_string, size); else break; + } + luaL_pushresult(&b); + if (runlua (L, lua_tostring(L, -1), argc, argv)) { + report (L); + lua_close (L); + return EXIT_FAILURE; + } + lua_close (L); + return EXIT_SUCCESS; +} diff --git a/win32/bin/rm.exe b/win32/bin/rm.exe Binary files differnew file mode 100755 index 0000000..8e79306 --- /dev/null +++ b/win32/bin/rm.exe diff --git a/win32/bin/rmdir.exe b/win32/bin/rmdir.exe Binary files differnew file mode 100755 index 0000000..6a85c3c --- /dev/null +++ b/win32/bin/rmdir.exe diff --git a/win32/bin/test.exe b/win32/bin/test.exe Binary files differnew file mode 100755 index 0000000..94c95f9 --- /dev/null +++ b/win32/bin/test.exe diff --git a/win32/bin/uname.exe b/win32/bin/uname.exe Binary files differnew file mode 100755 index 0000000..3e2f4cf --- /dev/null +++ b/win32/bin/uname.exe diff --git a/win32/bin/wget.exe b/win32/bin/wget.exe Binary files differnew file mode 100644 index 0000000..54b372e --- /dev/null +++ b/win32/bin/wget.exe diff --git a/win32/lua5.1/bin/Microsoft.VC80.CRT.manifest b/win32/lua5.1/bin/Microsoft.VC80.CRT.manifest new file mode 100644 index 0000000..b89cafd --- /dev/null +++ b/win32/lua5.1/bin/Microsoft.VC80.CRT.manifest @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <noInheritable></noInheritable>
+ <assemblyIdentity type="win32" name="Microsoft.VC80.CRT" version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
+ <file name="msvcr80.dll" hash="10f4cb2831f1e9288a73387a8734a8b604e5beaa" hashalg="SHA1"><asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>n9On8FItNsK/DmT8UQxu6jYDtWQ=</dsig:DigestValue></asmv2:hash></file>
+ <file name="msvcp80.dll" hash="b2082dfd3009365c5b287448dcb3b4e2158a6d26" hashalg="SHA1"><asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>0KJ/VTwP4OUHx98HlIW2AdW1kuY=</dsig:DigestValue></asmv2:hash></file>
+ <file name="msvcm80.dll" hash="542490d0fcf8615c46d0ca487033ccaeb3941f0b" hashalg="SHA1"><asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>YJuB+9Os2oxW4mY+2oC/r8lICZE=</dsig:DigestValue></asmv2:hash></file>
+</assembly>
\ No newline at end of file diff --git a/win32/lua5.1/bin/bin2c5.1.exe b/win32/lua5.1/bin/bin2c5.1.exe Binary files differnew file mode 100644 index 0000000..ca81d4b --- /dev/null +++ b/win32/lua5.1/bin/bin2c5.1.exe diff --git a/win32/lua5.1/bin/lua5.1.dll b/win32/lua5.1/bin/lua5.1.dll Binary files differnew file mode 100644 index 0000000..b87f3b6 --- /dev/null +++ b/win32/lua5.1/bin/lua5.1.dll diff --git a/win32/lua5.1/bin/lua5.1.exe b/win32/lua5.1/bin/lua5.1.exe Binary files differnew file mode 100644 index 0000000..dc1c2c3 --- /dev/null +++ b/win32/lua5.1/bin/lua5.1.exe diff --git a/win32/lua5.1/bin/lua5.1.lib b/win32/lua5.1/bin/lua5.1.lib Binary files differnew file mode 100644 index 0000000..df98827 --- /dev/null +++ b/win32/lua5.1/bin/lua5.1.lib diff --git a/win32/lua5.1/bin/lua51.dll b/win32/lua5.1/bin/lua51.dll Binary files differnew file mode 100644 index 0000000..27ab265 --- /dev/null +++ b/win32/lua5.1/bin/lua51.dll diff --git a/win32/lua5.1/bin/lua51.lib b/win32/lua5.1/bin/lua51.lib Binary files differnew file mode 100644 index 0000000..74bcf3f --- /dev/null +++ b/win32/lua5.1/bin/lua51.lib diff --git a/win32/lua5.1/bin/luac5.1.exe b/win32/lua5.1/bin/luac5.1.exe Binary files differnew file mode 100644 index 0000000..6f87524 --- /dev/null +++ b/win32/lua5.1/bin/luac5.1.exe diff --git a/win32/lua5.1/bin/msvcm80.dll b/win32/lua5.1/bin/msvcm80.dll Binary files differnew file mode 100644 index 0000000..c751385 --- /dev/null +++ b/win32/lua5.1/bin/msvcm80.dll diff --git a/win32/lua5.1/bin/msvcp80.dll b/win32/lua5.1/bin/msvcp80.dll Binary files differnew file mode 100644 index 0000000..f0b52eb --- /dev/null +++ b/win32/lua5.1/bin/msvcp80.dll diff --git a/win32/lua5.1/bin/msvcr80.dll b/win32/lua5.1/bin/msvcr80.dll Binary files differnew file mode 100644 index 0000000..53c005e --- /dev/null +++ b/win32/lua5.1/bin/msvcr80.dll diff --git a/win32/lua5.1/bin/wlua5.1.exe b/win32/lua5.1/bin/wlua5.1.exe Binary files differnew file mode 100644 index 0000000..7be8c3a --- /dev/null +++ b/win32/lua5.1/bin/wlua5.1.exe diff --git a/win32/lua5.1/include/lauxlib.h b/win32/lua5.1/include/lauxlib.h new file mode 100644 index 0000000..3425823 --- /dev/null +++ b/win32/lua5.1/include/lauxlib.h @@ -0,0 +1,174 @@ +/* +** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lauxlib_h +#define lauxlib_h + + +#include <stddef.h> +#include <stdio.h> + +#include "lua.h" + + +#if defined(LUA_COMPAT_GETN) +LUALIB_API int (luaL_getn) (lua_State *L, int t); +LUALIB_API void (luaL_setn) (lua_State *L, int t, int n); +#else +#define luaL_getn(L,i) ((int)lua_objlen(L, i)) +#define luaL_setn(L,i,j) ((void)0) /* no op! */ +#endif + +#if defined(LUA_COMPAT_OPENLIB) +#define luaI_openlib luaL_openlib +#endif + + +/* extra error code for `luaL_load' */ +#define LUA_ERRFILE (LUA_ERRERR+1) + + +typedef struct luaL_Reg { + const char *name; + lua_CFunction func; +} luaL_Reg; + + + +LUALIB_API void (luaI_openlib) (lua_State *L, const char *libname, + const luaL_Reg *l, int nup); +LUALIB_API void (luaL_register) (lua_State *L, const char *libname, + const luaL_Reg *l); +LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname); +LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg); +LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg, + size_t *l); +LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg, + const char *def, size_t *l); +LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg); +LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def); + +LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg); +LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg, + lua_Integer def); + +LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); +LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t); +LUALIB_API void (luaL_checkany) (lua_State *L, int narg); + +LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); +LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); + +LUALIB_API void (luaL_where) (lua_State *L, int lvl); +LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); + +LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def, + const char *const lst[]); + +LUALIB_API int (luaL_ref) (lua_State *L, int t); +LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); + +LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename); +LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, + const char *name); +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); + +LUALIB_API lua_State *(luaL_newstate) (void); + + +LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, + const char *r); + +LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx, + const char *fname, int szhint); + + + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define luaL_argcheck(L, cond,numarg,extramsg) \ + ((void)((cond) || luaL_argerror(L, (numarg), (extramsg)))) +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) + +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) + +#define luaL_dofile(L, fn) \ + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_dostring(L, s) \ + (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) + +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + + +typedef struct luaL_Buffer { + char *p; /* current position in buffer */ + int lvl; /* number of strings in the stack (level) */ + lua_State *L; + char buffer[LUAL_BUFFERSIZE]; +} luaL_Buffer; + +#define luaL_addchar(B,c) \ + ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \ + (*(B)->p++ = (char)(c))) + +/* compatibility only */ +#define luaL_putchar(B,c) luaL_addchar(B,c) + +#define luaL_addsize(B,n) ((B)->p += (n)) + +LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); +LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B); +LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); +LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); +LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); + + +/* }====================================================== */ + + +/* compatibility with ref system */ + +/* pre-defined references */ +#define LUA_NOREF (-2) +#define LUA_REFNIL (-1) + +#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \ + (lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0)) + +#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref)) + +#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref)) + + +#define luaL_reg luaL_Reg + +#endif + + diff --git a/win32/lua5.1/include/lua.h b/win32/lua5.1/include/lua.h new file mode 100644 index 0000000..e4bdfd3 --- /dev/null +++ b/win32/lua5.1/include/lua.h @@ -0,0 +1,388 @@ +/* +** $Id: lua.h,v 1.218.1.5 2008/08/06 13:30:12 roberto Exp $ +** Lua - An Extensible Extension Language +** Lua.org, PUC-Rio, Brazil (http://www.lua.org) +** See Copyright Notice at the end of this file +*/ + + +#ifndef lua_h +#define lua_h + +#include <stdarg.h> +#include <stddef.h> + + +#include "luaconf.h" + + +#define LUA_VERSION "Lua 5.1" +#define LUA_RELEASE "Lua 5.1.4" +#define LUA_VERSION_NUM 501 +#define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes" + + +/* mark for precompiled code (`<esc>Lua') */ +#define LUA_SIGNATURE "\033Lua" + +/* option for multiple returns in `lua_pcall' and `lua_call' */ +#define LUA_MULTRET (-1) + + +/* +** pseudo-indices +*/ +#define LUA_REGISTRYINDEX (-10000) +#define LUA_ENVIRONINDEX (-10001) +#define LUA_GLOBALSINDEX (-10002) +#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i)) + + +/* thread status; 0 is OK */ +#define LUA_YIELD 1 +#define LUA_ERRRUN 2 +#define LUA_ERRSYNTAX 3 +#define LUA_ERRMEM 4 +#define LUA_ERRERR 5 + + +typedef struct lua_State lua_State; + +typedef int (*lua_CFunction) (lua_State *L); + + +/* +** functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud); + + +/* +** prototype for memory-allocation functions +*/ +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + + +/* +** basic types +*/ +#define LUA_TNONE (-1) + +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 + + + +/* minimum Lua stack available to a C function */ +#define LUA_MINSTACK 20 + + +/* +** generic extra include file +*/ +#if defined(LUA_USER_H) +#include LUA_USER_H +#endif + + +/* type of numbers in Lua */ +typedef LUA_NUMBER lua_Number; + + +/* type for integer functions */ +typedef LUA_INTEGER lua_Integer; + + + +/* +** state manipulation +*/ +LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); +LUA_API void (lua_close) (lua_State *L); +LUA_API lua_State *(lua_newthread) (lua_State *L); + +LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); + + +/* +** basic stack manipulation +*/ +LUA_API int (lua_gettop) (lua_State *L); +LUA_API void (lua_settop) (lua_State *L, int idx); +LUA_API void (lua_pushvalue) (lua_State *L, int idx); +LUA_API void (lua_remove) (lua_State *L, int idx); +LUA_API void (lua_insert) (lua_State *L, int idx); +LUA_API void (lua_replace) (lua_State *L, int idx); +LUA_API int (lua_checkstack) (lua_State *L, int sz); + +LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); + + +/* +** access functions (stack -> C) +*/ + +LUA_API int (lua_isnumber) (lua_State *L, int idx); +LUA_API int (lua_isstring) (lua_State *L, int idx); +LUA_API int (lua_iscfunction) (lua_State *L, int idx); +LUA_API int (lua_isuserdata) (lua_State *L, int idx); +LUA_API int (lua_type) (lua_State *L, int idx); +LUA_API const char *(lua_typename) (lua_State *L, int tp); + +LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2); + +LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx); +LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx); +LUA_API int (lua_toboolean) (lua_State *L, int idx); +LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); +LUA_API size_t (lua_objlen) (lua_State *L, int idx); +LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); +LUA_API void *(lua_touserdata) (lua_State *L, int idx); +LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); +LUA_API const void *(lua_topointer) (lua_State *L, int idx); + + +/* +** push functions (C -> stack) +*/ +LUA_API void (lua_pushnil) (lua_State *L); +LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); +LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); +LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l); +LUA_API void (lua_pushstring) (lua_State *L, const char *s); +LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, + va_list argp); +LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); +LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); +LUA_API void (lua_pushboolean) (lua_State *L, int b); +LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); +LUA_API int (lua_pushthread) (lua_State *L); + + +/* +** get functions (Lua -> stack) +*/ +LUA_API void (lua_gettable) (lua_State *L, int idx); +LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawget) (lua_State *L, int idx); +LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n); +LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); +LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); +LUA_API int (lua_getmetatable) (lua_State *L, int objindex); +LUA_API void (lua_getfenv) (lua_State *L, int idx); + + +/* +** set functions (stack -> Lua) +*/ +LUA_API void (lua_settable) (lua_State *L, int idx); +LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawset) (lua_State *L, int idx); +LUA_API void (lua_rawseti) (lua_State *L, int idx, int n); +LUA_API int (lua_setmetatable) (lua_State *L, int objindex); +LUA_API int (lua_setfenv) (lua_State *L, int idx); + + +/* +** `load' and `call' functions (load and run Lua code) +*/ +LUA_API void (lua_call) (lua_State *L, int nargs, int nresults); +LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc); +LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud); +LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname); + +LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data); + + +/* +** coroutine functions +*/ +LUA_API int (lua_yield) (lua_State *L, int nresults); +LUA_API int (lua_resume) (lua_State *L, int narg); +LUA_API int (lua_status) (lua_State *L); + +/* +** garbage-collection function and options +*/ + +#define LUA_GCSTOP 0 +#define LUA_GCRESTART 1 +#define LUA_GCCOLLECT 2 +#define LUA_GCCOUNT 3 +#define LUA_GCCOUNTB 4 +#define LUA_GCSTEP 5 +#define LUA_GCSETPAUSE 6 +#define LUA_GCSETSTEPMUL 7 + +LUA_API int (lua_gc) (lua_State *L, int what, int data); + + +/* +** miscellaneous functions +*/ + +LUA_API int (lua_error) (lua_State *L); + +LUA_API int (lua_next) (lua_State *L, int idx); + +LUA_API void (lua_concat) (lua_State *L, int n); + +LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); +LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud); + + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define lua_pop(L,n) lua_settop(L, -(n)-1) + +#define lua_newtable(L) lua_createtable(L, 0, 0) + +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) + +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) + +#define lua_strlen(L,i) lua_objlen(L, (i)) + +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) + +#define lua_pushliteral(L, s) \ + lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1) + +#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s)) +#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s)) + +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) + + + +/* +** compatibility macros and functions +*/ + +#define lua_open() luaL_newstate() + +#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX) + +#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0) + +#define lua_Chunkreader lua_Reader +#define lua_Chunkwriter lua_Writer + + +/* hack */ +LUA_API void lua_setlevel (lua_State *from, lua_State *to); + + +/* +** {====================================================================== +** Debug API +** ======================================================================= +*/ + + +/* +** Event codes +*/ +#define LUA_HOOKCALL 0 +#define LUA_HOOKRET 1 +#define LUA_HOOKLINE 2 +#define LUA_HOOKCOUNT 3 +#define LUA_HOOKTAILRET 4 + + +/* +** Event masks +*/ +#define LUA_MASKCALL (1 << LUA_HOOKCALL) +#define LUA_MASKRET (1 << LUA_HOOKRET) +#define LUA_MASKLINE (1 << LUA_HOOKLINE) +#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) + +typedef struct lua_Debug lua_Debug; /* activation record */ + + +/* Functions to be called by the debuger in specific events */ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar); +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar); +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n); +LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n); + +LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count); +LUA_API lua_Hook lua_gethook (lua_State *L); +LUA_API int lua_gethookmask (lua_State *L); +LUA_API int lua_gethookcount (lua_State *L); + + +struct lua_Debug { + int event; + const char *name; /* (n) */ + const char *namewhat; /* (n) `global', `local', `field', `method' */ + const char *what; /* (S) `Lua', `C', `main', `tail' */ + const char *source; /* (S) */ + int currentline; /* (l) */ + int nups; /* (u) number of upvalues */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + char short_src[LUA_IDSIZE]; /* (S) */ + /* private part */ + int i_ci; /* active function */ +}; + +/* }====================================================================== */ + + +/****************************************************************************** +* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + + +#endif diff --git a/win32/lua5.1/include/lua.hpp b/win32/lua5.1/include/lua.hpp new file mode 100644 index 0000000..ec417f5 --- /dev/null +++ b/win32/lua5.1/include/lua.hpp @@ -0,0 +1,9 @@ +// lua.hpp +// Lua header files for C++ +// <<extern "C">> not supplied automatically because Lua also compiles as C++ + +extern "C" { +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +} diff --git a/win32/lua5.1/include/luaconf.h b/win32/lua5.1/include/luaconf.h new file mode 100644 index 0000000..5e7b98b --- /dev/null +++ b/win32/lua5.1/include/luaconf.h @@ -0,0 +1,763 @@ +/* +** $Id: luaconf.h,v 1.82.1.7 2008/02/11 16:25:08 roberto Exp $ +** Configuration file for Lua +** See Copyright Notice in lua.h +*/ + + +#ifndef lconfig_h +#define lconfig_h + +#include <limits.h> +#include <stddef.h> + + +/* +** ================================================================== +** Search for "@@" to find all configurable definitions. +** =================================================================== +*/ + + +/* +@@ LUA_ANSI controls the use of non-ansi features. +** CHANGE it (define it) if you want Lua to avoid the use of any +** non-ansi feature or library. +*/ +#if defined(__STRICT_ANSI__) +#define LUA_ANSI +#endif + + +#if !defined(LUA_ANSI) && defined(_WIN32) +#define LUA_WIN +#endif + +#if defined(LUA_USE_LINUX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ +#define LUA_USE_READLINE /* needs some extra libraries */ +#endif + +#if defined(LUA_USE_MACOSX) +#define LUA_USE_POSIX +#define LUA_DL_DYLD /* does not need extra library */ +#endif + + + +/* +@@ LUA_USE_POSIX includes all functionallity listed as X/Open System +@* Interfaces Extension (XSI). +** CHANGE it (define it) if your system is XSI compatible. +*/ +#if defined(LUA_USE_POSIX) +#define LUA_USE_MKSTEMP +#define LUA_USE_ISATTY +#define LUA_USE_POPEN +#define LUA_USE_ULONGJMP +#endif + + +/* +@@ LUA_PATH and LUA_CPATH are the names of the environment variables that +@* Lua check to set its paths. +@@ LUA_INIT is the name of the environment variable that Lua +@* checks for initialization code. +** CHANGE them if you want different names. +*/ +#define LUA_PATH "LUA_PATH" +#define LUA_CPATH "LUA_CPATH" +#define LUA_INIT "LUA_INIT" + + +/* +@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for +@* Lua libraries. +@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for +@* C libraries. +** CHANGE them if your machine has a non-conventional directory +** hierarchy or if you want to install your libraries in +** non-conventional directories. +*/ +#if defined(_WIN32) +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" +#define LUA_PATH_DEFAULT \ + ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua" +#define LUA_CPATH_DEFAULT \ + ".\\?.dll;" ".\\?51.dll;" LUA_CDIR"?.dll;" LUA_CDIR"?51.dll;" LUA_CDIR"clibs\\?.dll;" LUA_CDIR"clibs\\?51.dll;" LUA_CDIR"loadall.dll;" LUA_CDIR"clibs\\loadall.dll" + +#else +#define LUA_ROOT "/usr/local/" +#define LUA_LDIR LUA_ROOT "share/lua/5.1/" +#define LUA_CDIR LUA_ROOT "lib/lua/5.1/" +#define LUA_PATH_DEFAULT \ + "./?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua" +#define LUA_CPATH_DEFAULT \ + "./?.so;" "./lib?51.so;" LUA_CDIR"?.so;" LUA_CDIR"lib?51.so;" LUA_CDIR"loadall.so" +#endif + + +/* +@@ LUA_DIRSEP is the directory separator (for submodules). +** CHANGE it if your machine does not use "/" as the directory separator +** and is not Windows. (On Windows Lua automatically uses "\".) +*/ +#if defined(_WIN32) +#define LUA_DIRSEP "\\" +#else +#define LUA_DIRSEP "/" +#endif + + +/* +@@ LUA_PATHSEP is the character that separates templates in a path. +@@ LUA_PATH_MARK is the string that marks the substitution points in a +@* template. +@@ LUA_EXECDIR in a Windows path is replaced by the executable's +@* directory. +@@ LUA_IGMARK is a mark to ignore all before it when bulding the +@* luaopen_ function name. +** CHANGE them if for some reason your system cannot use those +** characters. (E.g., if one of those characters is a common character +** in file/directory names.) Probably you do not need to change them. +*/ +#define LUA_PATHSEP ";" +#define LUA_PATH_MARK "?" +#define LUA_EXECDIR "!" +#define LUA_IGMARK "-" + + +/* +@@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger. +** CHANGE that if ptrdiff_t is not adequate on your machine. (On most +** machines, ptrdiff_t gives a good choice between int or long.) +*/ +#define LUA_INTEGER ptrdiff_t + + +/* +@@ LUA_API is a mark for all core API functions. +@@ LUALIB_API is a mark for all standard library functions. +** CHANGE them if you need to define those functions in some special way. +** For instance, if you want to create one Windows DLL with the core and +** the libraries, you may want to use the following definition (define +** LUA_BUILD_AS_DLL to get it). +*/ +#if defined(LUA_BUILD_AS_DLL) + +#if defined(LUA_CORE) || defined(LUA_LIB) +#define LUA_API __declspec(dllexport) +#else +#define LUA_API __declspec(dllimport) +#endif + +#else + +#define LUA_API extern + +#endif + +/* more often than not the libs go together with the core */ +#define LUALIB_API LUA_API + + +/* +@@ LUAI_FUNC is a mark for all extern functions that are not to be +@* exported to outside modules. +@@ LUAI_DATA is a mark for all extern (const) variables that are not to +@* be exported to outside modules. +** CHANGE them if you need to mark them in some special way. Elf/gcc +** (versions 3.2 and later) mark them as "hidden" to optimize access +** when Lua is compiled as a shared library. +*/ +#if defined(luaall_c) +#define LUAI_FUNC static +#define LUAI_DATA /* empty */ + +#elif defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ + defined(__ELF__) +#define LUAI_FUNC __attribute__((visibility("hidden"))) extern +#define LUAI_DATA LUAI_FUNC + +#else +#define LUAI_FUNC extern +#define LUAI_DATA extern +#endif + + + +/* +@@ LUA_QL describes how error messages quote program elements. +** CHANGE it if you want a different appearance. +*/ +#define LUA_QL(x) "'" x "'" +#define LUA_QS LUA_QL("%s") + + +/* +@@ LUA_IDSIZE gives the maximum size for the description of the source +@* of a function in debug information. +** CHANGE it if you want a different size. +*/ +#define LUA_IDSIZE 60 + + +/* +** {================================================================== +** Stand-alone configuration +** =================================================================== +*/ + +#if defined(lua_c) || defined(luaall_c) + +/* +@@ lua_stdin_is_tty detects whether the standard input is a 'tty' (that +@* is, whether we're running lua interactively). +** CHANGE it if you have a better definition for non-POSIX/non-Windows +** systems. +*/ +#if defined(LUA_USE_ISATTY) +#include <unistd.h> +#define lua_stdin_is_tty() isatty(0) +#elif defined(LUA_WIN) +#include <io.h> +#include <stdio.h> +#define lua_stdin_is_tty() _isatty(_fileno(stdin)) +#else +#define lua_stdin_is_tty() 1 /* assume stdin is a tty */ +#endif + + +/* +@@ LUA_PROMPT is the default prompt used by stand-alone Lua. +@@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua. +** CHANGE them if you want different prompts. (You can also change the +** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.) +*/ +#define LUA_PROMPT "> " +#define LUA_PROMPT2 ">> " + + +/* +@@ LUA_PROGNAME is the default name for the stand-alone Lua program. +** CHANGE it if your stand-alone interpreter has a different name and +** your system is not able to detect that name automatically. +*/ +#define LUA_PROGNAME "lua" + + +/* +@@ LUA_MAXINPUT is the maximum length for an input line in the +@* stand-alone interpreter. +** CHANGE it if you need longer lines. +*/ +#define LUA_MAXINPUT 512 + + +/* +@@ lua_readline defines how to show a prompt and then read a line from +@* the standard input. +@@ lua_saveline defines how to "save" a read line in a "history". +@@ lua_freeline defines how to free a line read by lua_readline. +** CHANGE them if you want to improve this functionality (e.g., by using +** GNU readline and history facilities). +*/ +#if defined(LUA_USE_READLINE) +#include <stdio.h> +#include <readline/readline.h> +#include <readline/history.h> +#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) +#define lua_saveline(L,idx) \ + if (lua_strlen(L,idx) > 0) /* non-empty line? */ \ + add_history(lua_tostring(L, idx)); /* add it to history */ +#define lua_freeline(L,b) ((void)L, free(b)) +#else +#define lua_readline(L,b,p) \ + ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ + fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ +#define lua_saveline(L,idx) { (void)L; (void)idx; } +#define lua_freeline(L,b) { (void)L; (void)b; } +#endif + +#endif + +/* }================================================================== */ + + +/* +@@ LUAI_GCPAUSE defines the default pause between garbage-collector cycles +@* as a percentage. +** CHANGE it if you want the GC to run faster or slower (higher values +** mean larger pauses which mean slower collection.) You can also change +** this value dynamically. +*/ +#define LUAI_GCPAUSE 200 /* 200% (wait memory to double before next GC) */ + + +/* +@@ LUAI_GCMUL defines the default speed of garbage collection relative to +@* memory allocation as a percentage. +** CHANGE it if you want to change the granularity of the garbage +** collection. (Higher values mean coarser collections. 0 represents +** infinity, where each step performs a full collection.) You can also +** change this value dynamically. +*/ +#define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */ + + + +/* +@@ LUA_COMPAT_GETN controls compatibility with old getn behavior. +** CHANGE it (define it) if you want exact compatibility with the +** behavior of setn/getn in Lua 5.0. +*/ +#undef LUA_COMPAT_GETN + +/* +@@ LUA_COMPAT_LOADLIB controls compatibility about global loadlib. +** CHANGE it to undefined as soon as you do not need a global 'loadlib' +** function (the function is still available as 'package.loadlib'). +*/ +#undef LUA_COMPAT_LOADLIB + +/* +@@ LUA_COMPAT_VARARG controls compatibility with old vararg feature. +** CHANGE it to undefined as soon as your programs use only '...' to +** access vararg parameters (instead of the old 'arg' table). +*/ +#define LUA_COMPAT_VARARG + +/* +@@ LUA_COMPAT_MOD controls compatibility with old math.mod function. +** CHANGE it to undefined as soon as your programs use 'math.fmod' or +** the new '%' operator instead of 'math.mod'. +*/ +#define LUA_COMPAT_MOD + +/* +@@ LUA_COMPAT_LSTR controls compatibility with old long string nesting +@* facility. +** CHANGE it to 2 if you want the old behaviour, or undefine it to turn +** off the advisory error when nesting [[...]]. +*/ +#define LUA_COMPAT_LSTR 1 + +/* +@@ LUA_COMPAT_GFIND controls compatibility with old 'string.gfind' name. +** CHANGE it to undefined as soon as you rename 'string.gfind' to +** 'string.gmatch'. +*/ +#define LUA_COMPAT_GFIND + +/* +@@ LUA_COMPAT_OPENLIB controls compatibility with old 'luaL_openlib' +@* behavior. +** CHANGE it to undefined as soon as you replace to 'luaL_register' +** your uses of 'luaL_openlib' +*/ +#define LUA_COMPAT_OPENLIB + + + +/* +@@ luai_apicheck is the assert macro used by the Lua-C API. +** CHANGE luai_apicheck if you want Lua to perform some checks in the +** parameters it gets from API calls. This may slow down the interpreter +** a bit, but may be quite useful when debugging C code that interfaces +** with Lua. A useful redefinition is to use assert.h. +*/ +#if defined(LUA_USE_APICHECK) +#include <assert.h> +#define luai_apicheck(L,o) { (void)L; assert(o); } +#else +#define luai_apicheck(L,o) { (void)L; } +#endif + + +/* +@@ LUAI_BITSINT defines the number of bits in an int. +** CHANGE here if Lua cannot automatically detect the number of bits of +** your machine. Probably you do not need to change this. +*/ +/* avoid overflows in comparison */ +#if INT_MAX-20 < 32760 +#define LUAI_BITSINT 16 +#elif INT_MAX > 2147483640L +/* int has at least 32 bits */ +#define LUAI_BITSINT 32 +#else +#error "you must define LUA_BITSINT with number of bits in an integer" +#endif + + +/* +@@ LUAI_UINT32 is an unsigned integer with at least 32 bits. +@@ LUAI_INT32 is an signed integer with at least 32 bits. +@@ LUAI_UMEM is an unsigned integer big enough to count the total +@* memory used by Lua. +@@ LUAI_MEM is a signed integer big enough to count the total memory +@* used by Lua. +** CHANGE here if for some weird reason the default definitions are not +** good enough for your machine. (The definitions in the 'else' +** part always works, but may waste space on machines with 64-bit +** longs.) Probably you do not need to change this. +*/ +#if LUAI_BITSINT >= 32 +#define LUAI_UINT32 unsigned int +#define LUAI_INT32 int +#define LUAI_MAXINT32 INT_MAX +#define LUAI_UMEM size_t +#define LUAI_MEM ptrdiff_t +#else +/* 16-bit ints */ +#define LUAI_UINT32 unsigned long +#define LUAI_INT32 long +#define LUAI_MAXINT32 LONG_MAX +#define LUAI_UMEM unsigned long +#define LUAI_MEM long +#endif + + +/* +@@ LUAI_MAXCALLS limits the number of nested calls. +** CHANGE it if you need really deep recursive calls. This limit is +** arbitrary; its only purpose is to stop infinite recursion before +** exhausting memory. +*/ +#define LUAI_MAXCALLS 20000 + + +/* +@@ LUAI_MAXCSTACK limits the number of Lua stack slots that a C function +@* can use. +** CHANGE it if you need lots of (Lua) stack space for your C +** functions. This limit is arbitrary; its only purpose is to stop C +** functions to consume unlimited stack space. (must be smaller than +** -LUA_REGISTRYINDEX) +*/ +#define LUAI_MAXCSTACK 8000 + + + +/* +** {================================================================== +** CHANGE (to smaller values) the following definitions if your system +** has a small C stack. (Or you may want to change them to larger +** values if your system has a large C stack and these limits are +** too rigid for you.) Some of these constants control the size of +** stack-allocated arrays used by the compiler or the interpreter, while +** others limit the maximum number of recursive calls that the compiler +** or the interpreter can perform. Values too large may cause a C stack +** overflow for some forms of deep constructs. +** =================================================================== +*/ + + +/* +@@ LUAI_MAXCCALLS is the maximum depth for nested C calls (short) and +@* syntactical nested non-terminals in a program. +*/ +#define LUAI_MAXCCALLS 200 + + +/* +@@ LUAI_MAXVARS is the maximum number of local variables per function +@* (must be smaller than 250). +*/ +#define LUAI_MAXVARS 200 + + +/* +@@ LUAI_MAXUPVALUES is the maximum number of upvalues per function +@* (must be smaller than 250). +*/ +#define LUAI_MAXUPVALUES 60 + + +/* +@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. +*/ +#define LUAL_BUFFERSIZE BUFSIZ + +/* }================================================================== */ + + + + +/* +** {================================================================== +@@ LUA_NUMBER is the type of numbers in Lua. +** CHANGE the following definitions only if you want to build Lua +** with a number type different from double. You may also need to +** change lua_number2int & lua_number2integer. +** =================================================================== +*/ + +#define LUA_NUMBER_DOUBLE +#define LUA_NUMBER double + +/* +@@ LUAI_UACNUMBER is the result of an 'usual argument conversion' +@* over a number. +*/ +#define LUAI_UACNUMBER double + + +/* +@@ LUA_NUMBER_SCAN is the format for reading numbers. +@@ LUA_NUMBER_FMT is the format for writing numbers. +@@ lua_number2str converts a number to a string. +@@ LUAI_MAXNUMBER2STR is maximum size of previous conversion. +@@ lua_str2number converts a string to a number. +*/ +#define LUA_NUMBER_SCAN "%lf" +#define LUA_NUMBER_FMT "%.14g" +#define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n)) +#define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ +#define lua_str2number(s,p) strtod((s), (p)) + + +/* +@@ The luai_num* macros define the primitive operations over numbers. +*/ +#if defined(LUA_CORE) +#include <math.h> +#define luai_numadd(a,b) ((a)+(b)) +#define luai_numsub(a,b) ((a)-(b)) +#define luai_nummul(a,b) ((a)*(b)) +#define luai_numdiv(a,b) ((a)/(b)) +#define luai_nummod(a,b) ((a) - floor((a)/(b))*(b)) +#define luai_numpow(a,b) (pow(a,b)) +#define luai_numunm(a) (-(a)) +#define luai_numeq(a,b) ((a)==(b)) +#define luai_numlt(a,b) ((a)<(b)) +#define luai_numle(a,b) ((a)<=(b)) +#define luai_numisnan(a) (!luai_numeq((a), (a))) +#endif + + +/* +@@ lua_number2int is a macro to convert lua_Number to int. +@@ lua_number2integer is a macro to convert lua_Number to lua_Integer. +** CHANGE them if you know a faster way to convert a lua_Number to +** int (with any rounding method and without throwing errors) in your +** system. In Pentium machines, a naive typecast from double to int +** in C is extremely slow, so any alternative is worth trying. +*/ + +/* On a Pentium, resort to a trick */ +#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \ + (defined(__i386) || defined (_M_IX86) || defined(__i386__)) + +/* On a Microsoft compiler, use assembler */ +#if defined(_MSC_VER) + +#define lua_number2int(i,d) __asm fld d __asm fistp i +#define lua_number2integer(i,n) lua_number2int(i, n) + +/* the next trick should work on any Pentium, but sometimes clashes + with a DirectX idiosyncrasy */ +#else + +union luai_Cast { double l_d; long l_l; }; +#define lua_number2int(i,d) \ + { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; } +#define lua_number2integer(i,n) lua_number2int(i, n) + +#endif + + +/* this option always works, but may be slow */ +#else +#define lua_number2int(i,d) ((i)=(int)(d)) +#define lua_number2integer(i,d) ((i)=(lua_Integer)(d)) + +#endif + +/* }================================================================== */ + + +/* +@@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment. +** CHANGE it if your system requires alignments larger than double. (For +** instance, if your system supports long doubles and they must be +** aligned in 16-byte boundaries, then you should add long double in the +** union.) Probably you do not need to change this. +*/ +#define LUAI_USER_ALIGNMENT_T union { double u; void *s; long l; } + + +/* +@@ LUAI_THROW/LUAI_TRY define how Lua does exception handling. +** CHANGE them if you prefer to use longjmp/setjmp even with C++ +** or if want/don't to use _longjmp/_setjmp instead of regular +** longjmp/setjmp. By default, Lua handles errors with exceptions when +** compiling as C++ code, with _longjmp/_setjmp when asked to use them, +** and with longjmp/setjmp otherwise. +*/ +#if defined(__cplusplus) +/* C++ exceptions */ +#define LUAI_THROW(L,c) throw(c) +#define LUAI_TRY(L,c,a) try { a } catch(...) \ + { if ((c)->status == 0) (c)->status = -1; } +#define luai_jmpbuf int /* dummy variable */ + +#elif defined(LUA_USE_ULONGJMP) +/* in Unix, try _longjmp/_setjmp (more efficient) */ +#define LUAI_THROW(L,c) _longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#else +/* default handling with long jumps */ +#define LUAI_THROW(L,c) longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#endif + + +/* +@@ LUA_MAXCAPTURES is the maximum number of captures that a pattern +@* can do during pattern-matching. +** CHANGE it if you need more captures. This limit is arbitrary. +*/ +#define LUA_MAXCAPTURES 32 + + +/* +@@ lua_tmpnam is the function that the OS library uses to create a +@* temporary name. +@@ LUA_TMPNAMBUFSIZE is the maximum size of a name created by lua_tmpnam. +** CHANGE them if you have an alternative to tmpnam (which is considered +** insecure) or if you want the original tmpnam anyway. By default, Lua +** uses tmpnam except when POSIX is available, where it uses mkstemp. +*/ +#if defined(loslib_c) || defined(luaall_c) + +#if defined(LUA_USE_MKSTEMP) +#include <unistd.h> +#define LUA_TMPNAMBUFSIZE 32 +#define lua_tmpnam(b,e) { \ + strcpy(b, "/tmp/lua_XXXXXX"); \ + e = mkstemp(b); \ + if (e != -1) close(e); \ + e = (e == -1); } + +#else +#define LUA_TMPNAMBUFSIZE L_tmpnam +#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } +#endif + +#endif + + +/* +@@ lua_popen spawns a new process connected to the current one through +@* the file streams. +** CHANGE it if you have a way to implement it in your system. +*/ +#if defined(LUA_USE_POPEN) + +#define lua_popen(L,c,m) ((void)L, fflush(NULL), popen(c,m)) +#define lua_pclose(L,file) ((void)L, (pclose(file) != -1)) + +#elif defined(LUA_WIN) + +#define lua_popen(L,c,m) ((void)L, _popen(c,m)) +#define lua_pclose(L,file) ((void)L, (_pclose(file) != -1)) + +#else + +#define lua_popen(L,c,m) ((void)((void)c, m), \ + luaL_error(L, LUA_QL("popen") " not supported"), (FILE*)0) +#define lua_pclose(L,file) ((void)((void)L, file), 0) + +#endif + +/* +@@ LUA_DL_* define which dynamic-library system Lua should use. +** CHANGE here if Lua has problems choosing the appropriate +** dynamic-library system for your platform (either Windows' DLL, Mac's +** dyld, or Unix's dlopen). If your system is some kind of Unix, there +** is a good chance that it has dlopen, so LUA_DL_DLOPEN will work for +** it. To use dlopen you also need to adapt the src/Makefile (probably +** adding -ldl to the linker options), so Lua does not select it +** automatically. (When you change the makefile to add -ldl, you must +** also add -DLUA_USE_DLOPEN.) +** If you do not want any kind of dynamic library, undefine all these +** options. +** By default, _WIN32 gets LUA_DL_DLL and MAC OS X gets LUA_DL_DYLD. +*/ +#if defined(LUA_USE_DLOPEN) +#define LUA_DL_DLOPEN +#endif + +#if defined(LUA_WIN) +#define LUA_DL_DLL +#endif + + +/* +@@ LUAI_EXTRASPACE allows you to add user-specific data in a lua_State +@* (the data goes just *before* the lua_State pointer). +** CHANGE (define) this if you really need that. This value must be +** a multiple of the maximum alignment required for your machine. +*/ +#define LUAI_EXTRASPACE 0 + + +/* +@@ luai_userstate* allow user-specific actions on threads. +** CHANGE them if you defined LUAI_EXTRASPACE and need to do something +** extra when a thread is created/deleted/resumed/yielded. +*/ +#define luai_userstateopen(L) ((void)L) +#define luai_userstateclose(L) ((void)L) +#define luai_userstatethread(L,L1) ((void)L) +#define luai_userstatefree(L) ((void)L) +#define luai_userstateresume(L,n) ((void)L) +#define luai_userstateyield(L,n) ((void)L) + + +/* +@@ LUA_INTFRMLEN is the length modifier for integer conversions +@* in 'string.format'. +@@ LUA_INTFRM_T is the integer type correspoding to the previous length +@* modifier. +** CHANGE them if your system supports long long or does not support long. +*/ + +#if defined(LUA_USELONGLONG) + +#define LUA_INTFRMLEN "ll" +#define LUA_INTFRM_T long long + +#else + +#define LUA_INTFRMLEN "l" +#define LUA_INTFRM_T long + +#endif + + + +/* =================================================================== */ + +/* +** Local configuration. You can use this space to add your redefinitions +** without modifying the main part of the file. +*/ + + + +#endif + diff --git a/win32/lua5.1/include/lualib.h b/win32/lua5.1/include/lualib.h new file mode 100644 index 0000000..469417f --- /dev/null +++ b/win32/lua5.1/include/lualib.h @@ -0,0 +1,53 @@ +/* +** $Id: lualib.h,v 1.36.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua standard libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lualib_h +#define lualib_h + +#include "lua.h" + + +/* Key to file-handle type */ +#define LUA_FILEHANDLE "FILE*" + + +#define LUA_COLIBNAME "coroutine" +LUALIB_API int (luaopen_base) (lua_State *L); + +#define LUA_TABLIBNAME "table" +LUALIB_API int (luaopen_table) (lua_State *L); + +#define LUA_IOLIBNAME "io" +LUALIB_API int (luaopen_io) (lua_State *L); + +#define LUA_OSLIBNAME "os" +LUALIB_API int (luaopen_os) (lua_State *L); + +#define LUA_STRLIBNAME "string" +LUALIB_API int (luaopen_string) (lua_State *L); + +#define LUA_MATHLIBNAME "math" +LUALIB_API int (luaopen_math) (lua_State *L); + +#define LUA_DBLIBNAME "debug" +LUALIB_API int (luaopen_debug) (lua_State *L); + +#define LUA_LOADLIBNAME "package" +LUALIB_API int (luaopen_package) (lua_State *L); + + +/* open all previous libraries */ +LUALIB_API void (luaL_openlibs) (lua_State *L); + + + +#ifndef lua_assert +#define lua_assert(x) ((void)0) +#endif + + +#endif |