diff options
| author | hisham <hisham@9ca3f7c1-7366-0410-b1a3-b5c78f85698c> | 2009-04-01 17:11:57 +0000 |
|---|---|---|
| committer | hisham <hisham@9ca3f7c1-7366-0410-b1a3-b5c78f85698c> | 2009-04-01 17:11:57 +0000 |
| commit | a88d6f2eeba2b3355c33fac6d736cf6086342f47 (patch) | |
| tree | 03ebfa0ced0186e091609cf337e290580fab8c11 | |
| download | luarocks-0.3.2.tar.gz luarocks-0.3.2.tar.bz2 luarocks-0.3.2.zip | |
Import latest revision from CVS at luaforge.netv1.0v0.5.2v0.5.1v0.5v0.4.3v0.4.2v0.4.1v0.4v0.3.2v0.3.1v0.3v0.2v0.1
git-svn-id: http://luarocks.org/svn/luarocks/trunk@1 9ca3f7c1-7366-0410-b1a3-b5c78f85698c
41 files changed, 6781 insertions, 0 deletions
diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..adec4655 --- /dev/null +++ b/COPYING | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | LuaRocks is free software: it can be used for both academic and commercial | ||
| 2 | purposes at absolutely no cost. There are no royalties or GNU-like "copyleft" | ||
| 3 | restrictions. LuaRocks qualifies as Open Source software. Its licenses are | ||
| 4 | compatible with the GPL. LuaRocks is not in the public domain and the Kepler | ||
| 5 | Project keeps its copyright. The legal details are below. | ||
| 6 | |||
| 7 | The spirit of the license is that you are free to use LuaRocks for any purpose | ||
| 8 | at no cost without having to ask us. The only requirement is that if you do | ||
| 9 | use LuaRocks, then you should give us credit by including the appropriate | ||
| 10 | copyright notice somewhere in your product or its documentation. | ||
| 11 | |||
| 12 | ------------------------------------------------------------------------------ | ||
| 13 | |||
| 14 | Copyright © 2007 Kepler Project. | ||
| 15 | |||
| 16 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 17 | of this software and associated documentation files (the "Software"), to deal | ||
| 18 | in the Software without restriction, including without limitation the rights | ||
| 19 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 20 | copies of the Software, and to permit persons to whom the Software is | ||
| 21 | furnished to do so, subject to the following conditions: | ||
| 22 | |||
| 23 | The above copyright notice and this permission notice shall be included in all | ||
| 24 | copies or substantial portions of the Software. | ||
| 25 | |||
| 26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 32 | SOFTWARE. | ||
diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..5241eb7a --- /dev/null +++ b/Makefile | |||
| @@ -0,0 +1,123 @@ | |||
| 1 | # $Id: Makefile,v 1.30 2008/08/18 14:07:35 hisham Exp $ | ||
| 2 | |||
| 3 | include config.unix | ||
| 4 | |||
| 5 | DESTDIR = | ||
| 6 | PREFIX ?= /usr/local | ||
| 7 | BINDIR ?= $(PREFIX)/bin | ||
| 8 | LUADIR ?= $(PREFIX)/share/lua/5.1/ | ||
| 9 | LUA_DIR ?= /usr/local | ||
| 10 | LUA_BINDIR ?= $(LUA_DIR)/bin | ||
| 11 | |||
| 12 | BIN_FILES = luarocks luarocks-admin | ||
| 13 | LUAROCKS_FILES = build/cmake.lua build/command.lua build.lua build/make.lua \ | ||
| 14 | command_line.lua cfg.lua deps.lua fetch.lua fs.lua fs/unix.lua \ | ||
| 15 | fs/win32.lua help.lua install.lua list.lua persist.lua \ | ||
| 16 | make_manifest.lua pack.lua path.lua rep.lua require.lua search.lua \ | ||
| 17 | type_check.lua util.lua remove.lua build/builtin.lua make.lua manif.lua unpack.lua \ | ||
| 18 | fetch/cvs.lua fetch/sscm.lua fetch/git.lua | ||
| 19 | |||
| 20 | CONFIG_FILE = $(SYSCONFDIR)/config.lua | ||
| 21 | |||
| 22 | all: | ||
| 23 | for f in $(BIN_FILES) ;\ | ||
| 24 | do \ | ||
| 25 | sed "1d" src/bin/$$f >> src/bin/$$f.bak ;\ | ||
| 26 | echo "#!$(LUA_BINDIR)/lua$(LUA_SUFFIX)" > src/bin/$$f ;\ | ||
| 27 | echo "package.path = [[$(LUADIR)/?.lua;$(LUADIR)/?/init.lua;]]..package.path" >> src/bin/$$f ;\ | ||
| 28 | cat src/bin/$$f.bak >> src/bin/$$f ;\ | ||
| 29 | rm src/bin/$$f.bak ;\ | ||
| 30 | done | ||
| 31 | cp src/luarocks/cfg.lua src/luarocks/cfg.lua.bak | ||
| 32 | rm src/luarocks/cfg.lua | ||
| 33 | if [ -n "$(PREFIX)" ] ;\ | ||
| 34 | then \ | ||
| 35 | echo "local LUAROCKS_PREFIX=[[$(PREFIX)]]" >> src/luarocks/cfg.lua ;\ | ||
| 36 | fi | ||
| 37 | if [ -n "$(LUA_INCDIR)" ] ;\ | ||
| 38 | then \ | ||
| 39 | echo "local LUA_INCDIR=[[$(LUA_INCDIR)]]" >> src/luarocks/cfg.lua ;\ | ||
| 40 | fi | ||
| 41 | if [ -n "$(LUA_LIBDIR)" ] ;\ | ||
| 42 | then \ | ||
| 43 | echo "local LUA_LIBDIR=[[$(LUA_LIBDIR)]]" >> src/luarocks/cfg.lua ;\ | ||
| 44 | fi | ||
| 45 | if [ -n "$(LUA_BINDIR)" ] ;\ | ||
| 46 | then \ | ||
| 47 | echo "local LUA_BINDIR=[[$(LUA_BINDIR)]]" >> src/luarocks/cfg.lua ;\ | ||
| 48 | fi | ||
| 49 | if [ -n "$(LUA_SUFFIX)" ] ;\ | ||
| 50 | then \ | ||
| 51 | echo "local LUA_INTERPRETER=[[lua$(LUA_SUFFIX)]]" >> src/luarocks/cfg.lua ;\ | ||
| 52 | fi | ||
| 53 | if [ -n "$(SYSCONFDIR)" ] ;\ | ||
| 54 | then \ | ||
| 55 | echo "local LUAROCKS_SYSCONFIG=[[$(SYSCONFDIR)/config.lua]]" >> src/luarocks/cfg.lua ;\ | ||
| 56 | fi | ||
| 57 | if [ -n "$(ROCKS_TREE)" ] ;\ | ||
| 58 | then \ | ||
| 59 | echo "local LUAROCKS_ROCKS_TREE=[[$(ROCKS_TREE)]]" >> src/luarocks/cfg.lua ;\ | ||
| 60 | fi | ||
| 61 | if [ -n "$(FORCE_CONFIG)" ] ;\ | ||
| 62 | then \ | ||
| 63 | echo "local LUAROCKS_FORCE_CONFIG=true" >> src/luarocks/cfg.lua ;\ | ||
| 64 | fi | ||
| 65 | echo "local LUAROCKS_UNAME_S=[[$(LUAROCKS_UNAME_S)]]" >> src/luarocks/cfg.lua | ||
| 66 | echo "local LUAROCKS_UNAME_M=[[$(LUAROCKS_UNAME_M)]]" >> src/luarocks/cfg.lua | ||
| 67 | echo "local LUAROCKS_DOWNLOADER=[[$(LUAROCKS_DOWNLOADER)]]" >> src/luarocks/cfg.lua | ||
| 68 | echo "local LUAROCKS_MD5CHECKER=[[$(LUAROCKS_MD5CHECKER)]]" >> src/luarocks/cfg.lua | ||
| 69 | cat src/luarocks/cfg.lua.bak >> src/luarocks/cfg.lua | ||
| 70 | rm src/luarocks/cfg.lua.bak | ||
| 71 | @echo | ||
| 72 | @echo "Done. Type 'make install' to install into $(PREFIX)." | ||
| 73 | @echo | ||
| 74 | |||
| 75 | luadoc: | ||
| 76 | rm -rf doc/luadoc | ||
| 77 | mkdir -p doc/luadoc | ||
| 78 | cd src && luadoc -d ../doc/luadoc --nofiles luarocks/*.lua | ||
| 79 | |||
| 80 | check_makefile: | ||
| 81 | echo $(BIN_FILES) | tr " " "\n" | sort > makefile_list.txt | ||
| 82 | ( cd src/bin && ls -d * ) | grep -v "CVS" | sort > luarocks_dir.txt | ||
| 83 | echo $(LUAROCKS_FILES) | tr " " "\n" | sort >> makefile_list.txt | ||
| 84 | ( cd src/luarocks && ls -d *.lua ) | sort >> luarocks_dir.txt | ||
| 85 | diff makefile_list.txt luarocks_dir.txt | ||
| 86 | rm makefile_list.txt luarocks_dir.txt | ||
| 87 | @echo | ||
| 88 | @echo "Makefile is sane." | ||
| 89 | @echo | ||
| 90 | |||
| 91 | clean: | ||
| 92 | for f in $(BIN_FILES) ;\ | ||
| 93 | do \ | ||
| 94 | sed -i.bak "s,^#!.*lua.*,#!/usr/bin/env lua,;/^package.path/d" src/bin/$$f ;\ | ||
| 95 | rm src/bin/$$f.bak ;\ | ||
| 96 | done | ||
| 97 | sed -i.bak "/^local LUA/d" src/luarocks/cfg.lua | ||
| 98 | rm src/luarocks/cfg.lua.bak | ||
| 99 | |||
| 100 | install: | ||
| 101 | mkdir -p "$(DESTDIR)$(BINDIR)" | ||
| 102 | cd src/bin && cp $(BIN_FILES) "$(DESTDIR)$(BINDIR)" | ||
| 103 | mkdir -p "$(DESTDIR)$(LUADIR)/luarocks" | ||
| 104 | cd src/luarocks && for f in $(LUAROCKS_FILES); do d="$(DESTDIR)$(LUADIR)/luarocks"/`dirname "$$f"`; mkdir -p "$$d"; cp "$$f" "$$d"; done | ||
| 105 | mkdir -p "$(DESTDIR)$(ROCKS_TREE)" | ||
| 106 | if [ ! -f "$(DESTDIR)$(CONFIG_FILE)" ] ;\ | ||
| 107 | then \ | ||
| 108 | mkdir -p `dirname "$(DESTDIR)$(CONFIG_FILE)"` ;\ | ||
| 109 | echo 'rocks_servers = {' >> "$(DESTDIR)$(CONFIG_FILE)" ;\ | ||
| 110 | echo ' [[http://luarocks.luaforge.net/rocks]]' >> "$(DESTDIR)$(CONFIG_FILE)" ;\ | ||
| 111 | echo '}' >> "$(DESTDIR)$(CONFIG_FILE)" ;\ | ||
| 112 | echo 'rocks_trees = {' >> "$(DESTDIR)$(CONFIG_FILE)" ;\ | ||
| 113 | if [ ! -n "$(FORCE_CONFIG)" ] ;\ | ||
| 114 | then \ | ||
| 115 | echo ' home..[[/.luarocks]],' >> "$(DESTDIR)$(CONFIG_FILE)" ;\ | ||
| 116 | fi ;\ | ||
| 117 | echo ' [[$(ROCKS_TREE)]]' >> "$(DESTDIR)$(CONFIG_FILE)" ;\ | ||
| 118 | echo '}' >> "$(DESTDIR)$(CONFIG_FILE)" ;\ | ||
| 119 | if [ -n "$(SCRIPTS_DIR)" ] ;\ | ||
| 120 | then \ | ||
| 121 | echo "scripts_dir = [[$(SCRIPTS_DIR)]]" >> "$(DESTDIR)$(CONFIG_FILE)" ;\ | ||
| 122 | fi ;\ | ||
| 123 | fi | ||
diff --git a/configure b/configure new file mode 100755 index 00000000..268b7241 --- /dev/null +++ b/configure | |||
| @@ -0,0 +1,362 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | # A basic configure script for LuaRocks. | ||
| 4 | # Not doing any fancy shell stuff here to keep good compatibility. | ||
| 5 | |||
| 6 | # Defaults | ||
| 7 | |||
| 8 | PREFIX="/usr/local" | ||
| 9 | SYSCONFDIR="$PREFIX/etc/luarocks" | ||
| 10 | ROCKS_TREE="$PREFIX/lib/luarocks" | ||
| 11 | SCRIPTS_DIR="" | ||
| 12 | LUA_SUFFIX="" | ||
| 13 | LUA_DIR="/usr" | ||
| 14 | LUA_BINDIR="/usr/bin" | ||
| 15 | LUA_INCDIR="/usr/include" | ||
| 16 | LUA_LIBDIR="/usr/lib" | ||
| 17 | |||
| 18 | # ---------------------------------------------------------------------------- | ||
| 19 | # FUNCTION DEFINITIONS | ||
| 20 | # ---------------------------------------------------------------------------- | ||
| 21 | |||
| 22 | # Help | ||
| 23 | |||
| 24 | show_help() { | ||
| 25 | cat <<EOF | ||
| 26 | Configure LuaRocks. | ||
| 27 | |||
| 28 | --help This help. | ||
| 29 | --prefix=DIR Prefix where LuaRocks should be installed. | ||
| 30 | Default is $PREFIX | ||
| 31 | --sysconfdir=DIR Location where the config file should be installed. | ||
| 32 | Default is \$PREFIX/etc/luarocks | ||
| 33 | --rocks-tree=FILE Root of the local tree of installed rocks. | ||
| 34 | Default is \$PREFIX/lib/luarocks | ||
| 35 | --scripts-dir=DIR Where to install scripts installed by rocks. | ||
| 36 | Default is \$ROCKS_TREE/bin. Use this if you | ||
| 37 | want to modify it so that rocks install scripts | ||
| 38 | somewhere else (possibly somewhere in your \$PATH), | ||
| 39 | but if you use a system path such as /usr/bin, | ||
| 40 | beware of clashes between files installed by | ||
| 41 | LuaRocks and by your system's package manager. | ||
| 42 | Example: /usr/local/bin | ||
| 43 | --lua-suffix=SUFFIX Versioning suffix to use in Lua filenames. | ||
| 44 | Default is "$LUA_SUFFIX" (lua$LUA_SUFFIX...) | ||
| 45 | --with-lua=PREFIX Use Lua from given prefix. | ||
| 46 | Default is $LUA_DIR | ||
| 47 | --with-lua-include=DIR You can also specify Lua's includes dir. | ||
| 48 | Default is \$LUA_DIR/include | ||
| 49 | --with-lua-lib=DIR You can also specify Lua's libraries dir. | ||
| 50 | Default is \$LUA_DIR/lib | ||
| 51 | --with-downloader=TOOL Which tool to use as a downloader. | ||
| 52 | Valid options are: wget, curl. | ||
| 53 | Default is to auto-detect. | ||
| 54 | --with-md5-checker=TOOL Which tool to use as a downloader. | ||
| 55 | Valid options are: md5sum, openssl | ||
| 56 | Default is to auto-detect. | ||
| 57 | --force-config Use a single config location. Do not use the | ||
| 58 | \$LUAROCKS_CONFIG variable or the user's home | ||
| 59 | directory. Useful to avoid conflicts when LuaRocks | ||
| 60 | is embedded within an application. | ||
| 61 | EOF | ||
| 62 | } | ||
| 63 | |||
| 64 | # Helper functions | ||
| 65 | |||
| 66 | find_program() { | ||
| 67 | path="$PATH" | ||
| 68 | item="`echo "$path" | sed 's/\([^:]*\):.*/\1/'`" | ||
| 69 | path="`echo "$path" | sed -n 's/[^:]*::*\(.*\)/\1/p'`" | ||
| 70 | found="no" | ||
| 71 | while [ -n "$item" ] | ||
| 72 | do | ||
| 73 | if [ -f "$item/$1" ] | ||
| 74 | then | ||
| 75 | found="yes" | ||
| 76 | break | ||
| 77 | fi | ||
| 78 | item="`echo "$path" | sed 's/\([^:]*\):.*/\1/'`" | ||
| 79 | path="`echo "$path" | sed -n 's/[^:]*::*\(.*\)/\1/p'`" | ||
| 80 | done | ||
| 81 | if [ "$found" = "yes" ] | ||
| 82 | then | ||
| 83 | echo "$item" | ||
| 84 | else | ||
| 85 | echo "" | ||
| 86 | fi | ||
| 87 | } | ||
| 88 | |||
| 89 | find_helper() { | ||
| 90 | explanation="$1" | ||
| 91 | shift | ||
| 92 | tried="$*" | ||
| 93 | while [ -n "$1" ] | ||
| 94 | do | ||
| 95 | found=`find_program "$1"` | ||
| 96 | if [ -n "$found" ] | ||
| 97 | then | ||
| 98 | echo "$1 found at $found" | ||
| 99 | HELPER=$1 | ||
| 100 | return | ||
| 101 | fi | ||
| 102 | shift | ||
| 103 | done | ||
| 104 | echo "Could not find a $explanation. Tried: $tried." | ||
| 105 | echo "Make sure one of them is installed and available in your PATH." | ||
| 106 | exit 1 | ||
| 107 | } | ||
| 108 | |||
| 109 | case `echo -n x` in | ||
| 110 | -n*) echo_n_flag='';; | ||
| 111 | *) echo_n_flag='-n';; | ||
| 112 | esac | ||
| 113 | |||
| 114 | echo_n() { | ||
| 115 | echo $echo_n_flag "$*" | ||
| 116 | } | ||
| 117 | |||
| 118 | die() { | ||
| 119 | echo "$*" | ||
| 120 | exit 1 | ||
| 121 | } | ||
| 122 | |||
| 123 | # ---------------------------------------------------------------------------- | ||
| 124 | # MAIN PROGRAM | ||
| 125 | # ---------------------------------------------------------------------------- | ||
| 126 | |||
| 127 | # Parse options | ||
| 128 | |||
| 129 | while [ -n "$1" ] | ||
| 130 | do | ||
| 131 | value="`echo $1 | sed 's/[^=]*.\(.*\)/\1/'`" | ||
| 132 | key="`echo $1 | sed 's/=.*//'`" | ||
| 133 | if `echo "$value" | grep "~" >/dev/null 2>/dev/null` | ||
| 134 | then | ||
| 135 | echo | ||
| 136 | echo '*WARNING*: the "~" sign is not expanded in flags.' | ||
| 137 | echo 'If you mean the home directory, use $HOME instead.' | ||
| 138 | echo | ||
| 139 | fi | ||
| 140 | case "$key" in | ||
| 141 | --help) | ||
| 142 | show_help | ||
| 143 | exit 0 | ||
| 144 | ;; | ||
| 145 | --prefix) | ||
| 146 | [ -n "$value" ] || die "Missing value in flag $key." | ||
| 147 | PREFIX="$value" | ||
| 148 | PREFIX_SET=yes | ||
| 149 | ;; | ||
| 150 | --sysconfdir) | ||
| 151 | [ -n "$value" ] || die "Missing value in flag $key." | ||
| 152 | SYSCONFDIR="$value" | ||
| 153 | SYSCONFDIR_SET=yes | ||
| 154 | ;; | ||
| 155 | --rocks-tree) | ||
| 156 | [ -n "$value" ] || die "Missing value in flag $key." | ||
| 157 | ROCKS_TREE="$value" | ||
| 158 | ROCKS_TREE_SET=yes | ||
| 159 | ;; | ||
| 160 | --scripts-dir) | ||
| 161 | [ -n "$value" ] || die "Missing value in flag $key." | ||
| 162 | SCRIPTS_DIR="$value" | ||
| 163 | SCRIPTS_DIR_SET=yes | ||
| 164 | ;; | ||
| 165 | --force-config) | ||
| 166 | FORCE_CONFIG=yes | ||
| 167 | ;; | ||
| 168 | --lua-suffix) | ||
| 169 | [ -n "$value" ] || die "Missing value in flag $key." | ||
| 170 | LUA_SUFFIX="$value" | ||
| 171 | LUA_SUFFIX_SET=yes | ||
| 172 | ;; | ||
| 173 | --with-lua) | ||
| 174 | [ -n "$value" ] || die "Missing value in flag $key." | ||
| 175 | LUA_DIR="$value" | ||
| 176 | LUA_DIR_SET=yes | ||
| 177 | ;; | ||
| 178 | --with-lua-include) | ||
| 179 | [ -n "$value" ] || die "Missing value in flag $key." | ||
| 180 | LUA_INCDIR="$value" | ||
| 181 | LUA_INCDIR_SET=yes | ||
| 182 | ;; | ||
| 183 | --with-lua-lib) | ||
| 184 | [ -n "$value" ] || die "Missing value in flag $key." | ||
| 185 | LUA_LIBDIR="$value" | ||
| 186 | LUA_LIBDIR_SET=yes | ||
| 187 | ;; | ||
| 188 | --with-downloader) | ||
| 189 | [ -n "$value" ] || die "Missing value in flag $key." | ||
| 190 | case "$value" in | ||
| 191 | wget|curl) LUAROCKS_DOWNLOADER="$value" ;; | ||
| 192 | *) echo "Invalid option: $value. See --help." ; exit 1 ;; | ||
| 193 | esac | ||
| 194 | LUAROCKS_DOWNLOADER_SET=yes | ||
| 195 | ;; | ||
| 196 | --with-md5-checker) | ||
| 197 | [ -n "$value" ] || die "Missing value in flag $key." | ||
| 198 | case "$value" in | ||
| 199 | md5sum|openssl|md5) LUAROCKS_MD5CHECKER="$value" ;; | ||
| 200 | *) echo "Invalid option: $value. See --help." ; exit 1 ;; | ||
| 201 | esac | ||
| 202 | LUAROCKS_MD5CHECKER_SET=yes | ||
| 203 | ;; | ||
| 204 | *) | ||
| 205 | echo "Error: Unknown flag: $1" | ||
| 206 | exit 1 | ||
| 207 | ;; | ||
| 208 | esac | ||
| 209 | shift | ||
| 210 | done | ||
| 211 | |||
| 212 | |||
| 213 | if [ "$PREFIX_SET" = "yes" -a ! "$SYSCONFDIR_SET" = "yes" ] | ||
| 214 | then | ||
| 215 | if [ "$PREFIX" = "/usr" ] | ||
| 216 | then SYSCONFDIR=/etc/luarocks | ||
| 217 | else SYSCONFDIR=$PREFIX/etc/luarocks | ||
| 218 | fi | ||
| 219 | fi | ||
| 220 | |||
| 221 | |||
| 222 | if [ "$PREFIX_SET" = "yes" -a ! "$ROCKS_TREE_SET" = "yes" ] | ||
| 223 | then | ||
| 224 | ROCKS_TREE=$PREFIX/lib/luarocks | ||
| 225 | fi | ||
| 226 | |||
| 227 | if [ "$LUA_SUFFIX_SET" != "yes" ] | ||
| 228 | then | ||
| 229 | for suffix in "" "5.1" "51" "" | ||
| 230 | do | ||
| 231 | LUA_SUFFIX="$suffix" | ||
| 232 | if [ "$LUA_DIR_SET" = "yes" ] | ||
| 233 | then | ||
| 234 | if [ -f "$LUA_DIR/bin/lua$suffix" ] | ||
| 235 | then | ||
| 236 | find_lua="$LUA_DIR" | ||
| 237 | fi | ||
| 238 | else | ||
| 239 | find_lua=`find_program lua$suffix` | ||
| 240 | fi | ||
| 241 | if [ -n "$find_lua" ] | ||
| 242 | then | ||
| 243 | echo "Lua interpreter found: $find_lua/lua$suffix..." | ||
| 244 | break | ||
| 245 | fi | ||
| 246 | done | ||
| 247 | fi | ||
| 248 | |||
| 249 | if [ "$LUA_DIR_SET" != "yes" ] | ||
| 250 | then | ||
| 251 | echo_n "Looking for Lua... " | ||
| 252 | if [ ! -n "$find_lua" ] | ||
| 253 | then | ||
| 254 | find_lua=`find_program lua$LUA_SUFFIX` | ||
| 255 | fi | ||
| 256 | |||
| 257 | if [ -n "$find_lua" ] | ||
| 258 | then | ||
| 259 | LUA_DIR=`dirname $find_lua` | ||
| 260 | LUA_BINDIR="$find_lua" | ||
| 261 | echo "lua$LUA_SUFFIX found in \$PATH: $find_lua" | ||
| 262 | else | ||
| 263 | echo "lua$LUA_SUFFIX not found in \$PATH." | ||
| 264 | echo "You may want to use the flags --with-lua and/or --lua-suffix. See --help." | ||
| 265 | exit 1 | ||
| 266 | fi | ||
| 267 | fi | ||
| 268 | |||
| 269 | if [ "$LUA_INCDIR_SET" != "yes" ] | ||
| 270 | then | ||
| 271 | LUA_INCDIR="$LUA_DIR/include" | ||
| 272 | fi | ||
| 273 | |||
| 274 | if [ "$LUA_LIBDIR_SET" != "yes" ] | ||
| 275 | then | ||
| 276 | LUA_LIBDIR="$LUA_DIR/lib" | ||
| 277 | fi | ||
| 278 | |||
| 279 | if [ "$LUA_DIR_SET" = "yes" ] | ||
| 280 | then | ||
| 281 | LUA_BINDIR="$LUA_DIR/bin" | ||
| 282 | fi | ||
| 283 | |||
| 284 | echo_n "Checking Lua includes... " | ||
| 285 | lua_h="$LUA_INCDIR/lua.h" | ||
| 286 | if [ -f "$lua_h" ] | ||
| 287 | then | ||
| 288 | echo "lua.h found in $lua_h" | ||
| 289 | else | ||
| 290 | echo "lua.h not found (looked in $lua_h)" | ||
| 291 | echo "You may want to use the flag --with-lua-include. See --help." | ||
| 292 | exit 1 | ||
| 293 | fi | ||
| 294 | |||
| 295 | if [ "$LUAROCKS_DOWNLOADER_SET" != "yes" ] | ||
| 296 | then | ||
| 297 | find_helper "downloader helper program" wget curl fetch | ||
| 298 | LUAROCKS_DOWNLOADER=$HELPER | ||
| 299 | fi | ||
| 300 | |||
| 301 | if [ "$LUAROCKS_MD5CHECKER_SET" != "yes" ] | ||
| 302 | then | ||
| 303 | find_helper "MD5 checksum calculator" md5sum openssl md5 | ||
| 304 | LUAROCKS_MD5CHECKER=$HELPER | ||
| 305 | fi | ||
| 306 | |||
| 307 | echo_n "Configuring for system... " | ||
| 308 | if uname -s | ||
| 309 | then | ||
| 310 | LUAROCKS_UNAME_S=`uname -s` | ||
| 311 | else | ||
| 312 | echo "Could not determine operating system. 'uname -s' failed." | ||
| 313 | exit 1 | ||
| 314 | fi | ||
| 315 | echo_n "Configuring for architecture... " | ||
| 316 | if uname -m | ||
| 317 | then | ||
| 318 | LUAROCKS_UNAME_M=`uname -m` | ||
| 319 | else | ||
| 320 | echo "Could not determine processor architecture. 'uname -m' failed." | ||
| 321 | exit 1 | ||
| 322 | fi | ||
| 323 | |||
| 324 | if [ -f config.unix ]; then | ||
| 325 | rm -f config.unix | ||
| 326 | fi | ||
| 327 | |||
| 328 | # Write config | ||
| 329 | |||
| 330 | echo "Writing configuration..." | ||
| 331 | echo | ||
| 332 | |||
| 333 | cat <<EOF > config.unix | ||
| 334 | # This file was automatically generated by the configure script. | ||
| 335 | # Run "./configure --help" for details. | ||
| 336 | |||
| 337 | PREFIX=$PREFIX | ||
| 338 | SYSCONFDIR=$SYSCONFDIR | ||
| 339 | ROCKS_TREE=$ROCKS_TREE | ||
| 340 | SCRIPTS_DIR=$SCRIPTS_DIR | ||
| 341 | LUA_SUFFIX=$LUA_SUFFIX | ||
| 342 | LUA_DIR=$LUA_DIR | ||
| 343 | LUA_INCDIR=$LUA_INCDIR | ||
| 344 | LUA_LIBDIR=$LUA_LIBDIR | ||
| 345 | LUA_BINDIR=$LUA_BINDIR | ||
| 346 | FORCE_CONFIG=$FORCE_CONFIG | ||
| 347 | LUAROCKS_UNAME_M=$LUAROCKS_UNAME_M | ||
| 348 | LUAROCKS_UNAME_S=$LUAROCKS_UNAME_S | ||
| 349 | LUAROCKS_DOWNLOADER=$LUAROCKS_DOWNLOADER | ||
| 350 | LUAROCKS_MD5CHECKER=$LUAROCKS_MD5CHECKER | ||
| 351 | |||
| 352 | EOF | ||
| 353 | |||
| 354 | echo "Installation prefix: $PREFIX" | ||
| 355 | echo "LuaRocks configuration directory: $SYSCONFDIR" | ||
| 356 | echo "Using Lua from: $LUA_DIR" | ||
| 357 | |||
| 358 | make clean > /dev/null 2> /dev/null | ||
| 359 | |||
| 360 | echo | ||
| 361 | echo "Done. You can now run 'make' to build." | ||
| 362 | echo | ||
diff --git a/install.bat b/install.bat new file mode 100644 index 00000000..141fe0f8 --- /dev/null +++ b/install.bat | |||
| @@ -0,0 +1,312 @@ | |||
| 1 | @ECHO OFF | ||
| 2 | |||
| 3 | REM Boy, it feels like 1994 all over again. | ||
| 4 | |||
| 5 | SETLOCAL | ||
| 6 | |||
| 7 | SET PREFIX=C:\LuaRocks | ||
| 8 | SET VERSION=1.0 | ||
| 9 | SET SYSCONFDIR=C:\LuaRocks | ||
| 10 | SET ROCKS_TREE=C:\LuaRocks | ||
| 11 | SET SCRIPTS_DIR= | ||
| 12 | SET FORCE=OFF | ||
| 13 | SET INSTALL_LUA=OFF | ||
| 14 | SET LUA_INTERPRETER= | ||
| 15 | SET LUA_PREFIX= | ||
| 16 | SET LUA_BINDIR= | ||
| 17 | SET LUA_INCDIR= | ||
| 18 | SET LUA_LIBDIR= | ||
| 19 | SET FORCE_CONFIG= | ||
| 20 | SET MKDIR=.\bin\mkdir -p | ||
| 21 | |||
| 22 | REM *********************************************************** | ||
| 23 | REM Option parser | ||
| 24 | REM *********************************************************** | ||
| 25 | |||
| 26 | :PARSE_LOOP | ||
| 27 | IF [%1]==[] GOTO DONE_PARSING | ||
| 28 | IF [%1]==[/?] ( | ||
| 29 | ECHO Installs LuaRocks. | ||
| 30 | ECHO. | ||
| 31 | ECHO /P [dir] Where to install. | ||
| 32 | ECHO Default is %PREFIX% | ||
| 33 | ECHO /CONFIG [dir] Location where the config file should be installed. | ||
| 34 | ECHO Default is %SYSCONFDIR% | ||
| 35 | ECHO /TREE [dir] Root of the local tree of installed rocks. | ||
| 36 | ECHO Default is %ROCKS_TREE% | ||
| 37 | ECHO /SCRIPTS [dir] Where to install scripts installed by rocks. | ||
| 38 | ECHO Default is TREE/bin. | ||
| 39 | ECHO. | ||
| 40 | ECHO /L Install LuaRocks' own copy of Lua even if detected. | ||
| 41 | ECHO /LUA [dir] Location where Lua is installed - e.g. c:\lua\5.1\ | ||
| 42 | ECHO /INC [dir] Location of Lua includes - e.g. c:\lua\5.1\include | ||
| 43 | ECHO /LIB [dir] Location of Lua libraries -e.g. c:\lua\5.1\lib | ||
| 44 | ECHO /BIN [dir] Location of Lua executables - e.g. c:\lua\5.1\bin | ||
| 45 | ECHO. | ||
| 46 | ECHO /FORCECONFIG Use a single config location. Do not use the | ||
| 47 | ECHO LUAROCKS_CONFIG variable or the user's home directory. | ||
| 48 | ECHO Useful to avoid conflicts when LuaRocks | ||
| 49 | ECHO is embedded within an application. | ||
| 50 | ECHO. | ||
| 51 | ECHO /F Remove installation directory if it already exists. | ||
| 52 | ECHO. | ||
| 53 | GOTO QUIT | ||
| 54 | ) | ||
| 55 | IF /I [%1]==[/P] ( | ||
| 56 | SET PREFIX=%2 | ||
| 57 | SHIFT /1 | ||
| 58 | SHIFT /1 | ||
| 59 | GOTO PARSE_LOOP | ||
| 60 | ) | ||
| 61 | IF /I [%1]==[/CONFIG] ( | ||
| 62 | SET SYSCONFDIR=%2 | ||
| 63 | SHIFT /1 | ||
| 64 | SHIFT /1 | ||
| 65 | GOTO PARSE_LOOP | ||
| 66 | ) | ||
| 67 | IF /I [%1]==[/TREE] ( | ||
| 68 | SET ROCKS_TREE=%2 | ||
| 69 | SHIFT /1 | ||
| 70 | SHIFT /1 | ||
| 71 | GOTO PARSE_LOOP | ||
| 72 | ) | ||
| 73 | IF /I [%1]==[/SCRIPTS] ( | ||
| 74 | SET SCRIPTS_DIR=%2 | ||
| 75 | SHIFT /1 | ||
| 76 | SHIFT /1 | ||
| 77 | GOTO PARSE_LOOP | ||
| 78 | ) | ||
| 79 | IF /I [%1]==[/L] ( | ||
| 80 | SET INSTALL_LUA=ON | ||
| 81 | SHIFT /1 | ||
| 82 | GOTO PARSE_LOOP | ||
| 83 | ) | ||
| 84 | IF /I [%1]==[/LUA] ( | ||
| 85 | SET LUA_PREFIX=%2 | ||
| 86 | SHIFT /1 | ||
| 87 | SHIFT /1 | ||
| 88 | GOTO PARSE_LOOP | ||
| 89 | ) | ||
| 90 | IF /I [%1]==[/LIB] ( | ||
| 91 | SET LUA_LIBDIR=%2 | ||
| 92 | SHIFT /1 | ||
| 93 | SHIFT /1 | ||
| 94 | GOTO PARSE_LOOP | ||
| 95 | ) | ||
| 96 | IF /I [%1]==[/INC] ( | ||
| 97 | SET LUA_INCDIR=%2 | ||
| 98 | SHIFT /1 | ||
| 99 | SHIFT /1 | ||
| 100 | GOTO PARSE_LOOP | ||
| 101 | ) | ||
| 102 | IF /I [%1]==[/BIN] ( | ||
| 103 | SET LUA_BINDIR=%2 | ||
| 104 | SHIFT /1 | ||
| 105 | SHIFT /1 | ||
| 106 | GOTO PARSE_LOOP | ||
| 107 | ) | ||
| 108 | IF /I [%1]==[/FORCECONFIG] ( | ||
| 109 | SET FORCE_CONFIG=ON | ||
| 110 | SHIFT /1 | ||
| 111 | GOTO PARSE_LOOP | ||
| 112 | ) | ||
| 113 | IF /I [%1]==[/F] ( | ||
| 114 | SET FORCE=ON | ||
| 115 | SHIFT /1 | ||
| 116 | GOTO PARSE_LOOP | ||
| 117 | ) | ||
| 118 | ECHO Unrecognized option: %1 | ||
| 119 | GOTO ERROR | ||
| 120 | :DONE_PARSING | ||
| 121 | |||
| 122 | SET FULL_PREFIX=%PREFIX%\%VERSION% | ||
| 123 | |||
| 124 | SET BINDIR=%FULL_PREFIX% | ||
| 125 | SET LIBDIR=%FULL_PREFIX% | ||
| 126 | SET LUADIR=%FULL_PREFIX%\lua | ||
| 127 | SET INCDIR=%FULL_PREFIX%\include | ||
| 128 | |||
| 129 | REM *********************************************************** | ||
| 130 | REM Detect Lua | ||
| 131 | REM *********************************************************** | ||
| 132 | |||
| 133 | IF [%INSTALL_LUA%]==[ON] GOTO USE_OWN_LUA | ||
| 134 | |||
| 135 | FOR %%L IN (%LUA_PREFIX% c:\lua\5.1.2 c:\lua c:\kepler\1.1) DO ( | ||
| 136 | SET CURR=%%L | ||
| 137 | IF EXIST "%%L" ( | ||
| 138 | IF NOT [%LUA_BINDIR%]==[] ( | ||
| 139 | IF EXIST %LUA_BINDIR%\lua5.1.exe ( | ||
| 140 | SET LUA_INTERPRETER=%LUA_BINDIR%\lua5.1.exe | ||
| 141 | GOTO INTERPRETER_IS_SET | ||
| 142 | ) | ||
| 143 | IF EXIST %LUA_BINDIR%\lua.exe ( | ||
| 144 | SET LUA_INTERPRETER=%LUA_BINDIR%\lua.exe | ||
| 145 | GOTO INTERPRETER_IS_SET | ||
| 146 | ) | ||
| 147 | ECHO Lua executable lua.exe or lua5.1.exe not found in %LUA_BINDIR% | ||
| 148 | GOTO ERROR | ||
| 149 | ) | ||
| 150 | SET CURR=%%L | ||
| 151 | FOR %%E IN (\ \bin\) DO ( | ||
| 152 | IF EXIST "%%L%%E\lua5.1.exe" ( | ||
| 153 | SET LUA_INTERPRETER=%%L%%E\lua5.1.exe | ||
| 154 | SET LUA_BINDIR=%%L%%E | ||
| 155 | GOTO INTERPRETER_IS_SET | ||
| 156 | ) | ||
| 157 | IF EXIST "%%L%%E\lua.exe" ( | ||
| 158 | SET LUA_INTERPRETER=%%L%%E\lua.exe | ||
| 159 | SET LUA_BINDIR=%%L%%E | ||
| 160 | GOTO INTERPRETER_IS_SET | ||
| 161 | ) | ||
| 162 | ) | ||
| 163 | GOTO TRY_NEXT_LUA_DIR | ||
| 164 | :INTERPRETER_IS_SET | ||
| 165 | IF NOT "%LUA_LIBDIR%"=="" ( | ||
| 166 | IF EXIST %LUA_LIBDIR%\lua5.1.lib GOTO LIBDIR_IS_SET | ||
| 167 | ECHO lua5.1.lib not found in %LUA_LIBDIR% | ||
| 168 | GOTO ERROR | ||
| 169 | ) | ||
| 170 | FOR %%E IN (\ \lib\ \bin\) DO ( | ||
| 171 | IF EXIST "%CURR%%%E\lua5.1.lib" ( | ||
| 172 | SET LUA_LIBDIR=%CURR%%%E | ||
| 173 | GOTO LIBDIR_IS_SET | ||
| 174 | ) | ||
| 175 | ) | ||
| 176 | GOTO TRY_NEXT_LUA_DIR | ||
| 177 | :LIBDIR_IS_SET | ||
| 178 | IF NOT [%LUA_INCDIR%]==[] ( | ||
| 179 | IF EXIST %LUA_INCDIR%\lua.h GOTO INCDIR_IS_SET | ||
| 180 | ECHO lua.h not found in %LUA_INCDIR% | ||
| 181 | GOTO ERROR | ||
| 182 | ) | ||
| 183 | FOR %%E IN (\ \include\) DO ( | ||
| 184 | IF EXIST "%CURR%%%E\lua.h" ( | ||
| 185 | SET LUA_INCDIR=%CURR%%%E | ||
| 186 | GOTO INCDIR_IS_SET | ||
| 187 | ) | ||
| 188 | ) | ||
| 189 | GOTO TRY_NEXT_LUA_DIR | ||
| 190 | :INCDIR_IS_SET | ||
| 191 | %LUA_INTERPRETER% -v 2>NUL | ||
| 192 | IF NOT ERRORLEVEL 1 ( | ||
| 193 | GOTO LUA_IS_SET | ||
| 194 | ) | ||
| 195 | ) | ||
| 196 | :TRY_NEXT_LUA_DIR | ||
| 197 | REM wtf | ||
| 198 | ) | ||
| 199 | ECHO Could not find Lua. Will install its own copy. | ||
| 200 | ECHO See /? for options for specifying the location of Lua. | ||
| 201 | :USE_OWN_LUA | ||
| 202 | SET INSTALL_LUA=ON | ||
| 203 | SET LUA_INTERPRETER=lua5.1 | ||
| 204 | SET LUA_BINDIR=%BINDIR% | ||
| 205 | SET LUA_LIBDIR=%LIBDIR% | ||
| 206 | SET LUA_INCDIR=%INCDIR% | ||
| 207 | :LUA_IS_SET | ||
| 208 | ECHO. | ||
| 209 | ECHO Will configure LuaRocks with the following paths: | ||
| 210 | ECHO Lua interpreter: %LUA_INTERPRETER% | ||
| 211 | ECHO Lua binaries: %LUA_BINDIR% | ||
| 212 | ECHO Lua libraries: %LUA_LIBDIR% | ||
| 213 | ECHO Lua includes: %LUA_INCDIR% | ||
| 214 | ECHO. | ||
| 215 | |||
| 216 | REM *********************************************************** | ||
| 217 | REM Install LuaRocks files | ||
| 218 | REM *********************************************************** | ||
| 219 | |||
| 220 | IF [%FORCE%]==[ON] ( | ||
| 221 | ECHO Removing %FULL_PREFIX%... | ||
| 222 | RD /S /Q "%FULL_PREFIX%" | ||
| 223 | ) | ||
| 224 | |||
| 225 | IF NOT EXIST "%FULL_PREFIX%" GOTO NOT_EXIST_PREFIX | ||
| 226 | ECHO %FULL_PREFIX% exists. Use /F to force removal and reinstallation. | ||
| 227 | GOTO ERROR | ||
| 228 | :NOT_EXIST_PREFIX | ||
| 229 | |||
| 230 | ECHO Installing LuaRocks in %FULL_PREFIX%... | ||
| 231 | IF NOT EXIST "%BINDIR%" %MKDIR% "%BINDIR%" | ||
| 232 | IF ERRORLEVEL 1 GOTO ERROR | ||
| 233 | IF [%INSTALL_LUA%]==[ON] ( | ||
| 234 | IF NOT EXIST "%LUA_BINDIR%" %MKDIR% "%LUA_BINDIR%" | ||
| 235 | IF NOT EXIST "%LUA_INCDIR%" %MKDIR% "%LUA_INCDIR%" | ||
| 236 | COPY lua5.1\bin\*.* "%LUA_BINDIR%" >NUL | ||
| 237 | COPY lua5.1\include\*.* "%LUA_INCDIR%" >NUL | ||
| 238 | ) | ||
| 239 | COPY bin\*.* "%BINDIR%" >NUL | ||
| 240 | IF ERRORLEVEL 1 GOTO ERROR | ||
| 241 | COPY src\bin\*.* "%BINDIR%" >NUL | ||
| 242 | IF ERRORLEVEL 1 GOTO ERROR | ||
| 243 | FOR %%C IN (luarocks luarocks-admin) DO ( | ||
| 244 | RENAME "%BINDIR%\%%C" %%C.lua | ||
| 245 | IF ERRORLEVEL 1 GOTO ERROR | ||
| 246 | DEL /F /Q "%BINDIR%\%%C.bat" 2>NUL | ||
| 247 | ECHO @ECHO OFF>> "%BINDIR%\%%C.bat" | ||
| 248 | ECHO SETLOCAL>> "%BINDIR%\%%C.bat" | ||
| 249 | ECHO SET LUA_PATH=%LUADIR%\?.lua;%LUADIR%\?\init.lua;%%LUA_PATH%%>> "%BINDIR%\%%C.bat" | ||
| 250 | ECHO SET PATH=%BINDIR%\;%%PATH%%>> "%BINDIR%\%%C.bat" | ||
| 251 | ECHO "%LUA_INTERPRETER%" "%BINDIR%\%%C.lua" %%*>> "%BINDIR%\%%C.bat" | ||
| 252 | ECHO ENDLOCAL>> "%BINDIR%\%%C.bat" | ||
| 253 | ) | ||
| 254 | IF NOT EXIST "%LUADIR%\luarocks" %MKDIR% "%LUADIR%\luarocks" | ||
| 255 | IF ERRORLEVEL 1 GOTO ERROR | ||
| 256 | XCOPY /S src\luarocks\*.* "%LUADIR%\luarocks" >NUL | ||
| 257 | IF ERRORLEVEL 1 GOTO ERROR | ||
| 258 | |||
| 259 | RENAME "%LUADIR%\luarocks\cfg.lua" "cfg.lua.bak" | ||
| 260 | ECHO local LUA_INCDIR=[[%LUA_INCDIR%]]>> "%LUADIR%\luarocks\cfg.lua" | ||
| 261 | ECHO local LUA_LIBDIR=[[%LUA_LIBDIR%]]>> "%LUADIR%\luarocks\cfg.lua" | ||
| 262 | ECHO local LUA_BINDIR=[[%LUA_BINDIR%]]>> "%LUADIR%\luarocks\cfg.lua" | ||
| 263 | ECHO local LUA_INTERPRETER=[[%LUA_INTERPRETER%]]>> "%LUADIR%\luarocks\cfg.lua" | ||
| 264 | ECHO local LUAROCKS_UNAME_S=[[WindowsNT]]>> "%LUADIR%\luarocks\cfg.lua" | ||
| 265 | ECHO local LUAROCKS_UNAME_M=[[x86]]>> "%LUADIR%\luarocks\cfg.lua" | ||
| 266 | ECHO local LUAROCKS_SYSCONFIG=[[%SYSCONFDIR%/config.lua]]>> "%LUADIR%\luarocks\cfg.lua" | ||
| 267 | ECHO local LUAROCKS_ROCKS_TREE=[[%ROCKS_TREE%]]>> "%LUADIR%\luarocks\cfg.lua" | ||
| 268 | ECHO local LUAROCKS_PREFIX=[[%PREFIX%]]>> "%LUADIR%\luarocks\cfg.lua" | ||
| 269 | IF NOT [%FORCE_CONFIG%]==[] ECHO local LUAROCKS_FORCE_CONFIG=true>> "%LUADIR%\luarocks\cfg.lua" | ||
| 270 | TYPE "%LUADIR%\luarocks\cfg.lua.bak">> "%LUADIR%\luarocks\cfg.lua" | ||
| 271 | |||
| 272 | DEL /F /Q "%LUADIR%\luarocks\cfg.lua.bak" | ||
| 273 | |||
| 274 | SET CONFIG_FILE=%SYSCONFDIR%\config.lua | ||
| 275 | |||
| 276 | IF NOT EXIST "%SYSCONFDIR%" %MKDIR% "%SYSCONFDIR%" | ||
| 277 | IF NOT EXIST "%CONFIG_FILE%" ( | ||
| 278 | ECHO rocks_servers = {>> "%CONFIG_FILE%" | ||
| 279 | ECHO [[http://luarocks.luaforge.net/rocks]]>> "%CONFIG_FILE%" | ||
| 280 | ECHO }>> "%CONFIG_FILE%" | ||
| 281 | ECHO rocks_trees = {>> "%CONFIG_FILE%" | ||
| 282 | IF [%FORCE_CONFIG%]==[] ECHO home..[[/luarocks]],>> "%CONFIG_FILE%" | ||
| 283 | ECHO [[%ROCKS_TREE%]]>> "%CONFIG_FILE%" | ||
| 284 | ECHO }>> "%CONFIG_FILE%" | ||
| 285 | IF NOT [%SCRIPTS_DIR%]==[] ECHO scripts_dir=[[%SCRIPTS_DIR%]]>> "%CONFIG_FILE%" | ||
| 286 | ) | ||
| 287 | |||
| 288 | IF [%SCRIPTS_DIR%]==[] ( | ||
| 289 | %MKDIR% "%ROCKS_TREE%"\bin >NUL | ||
| 290 | COPY lua5.1\bin\*.dll "%ROCKS_TREE%"\bin >NUL | ||
| 291 | ) | ||
| 292 | IF NOT [%SCRIPTS_DIR%]==[] ( | ||
| 293 | %MKDIR% "%SCRIPTS_DIR%" >NUL | ||
| 294 | COPY lua5.1\bin\*.dll "%SCRIPTS_DIR%" >NUL | ||
| 295 | ) | ||
| 296 | |||
| 297 | IF NOT EXIST "%ROCKS_TREE%" %MKDIR% "%ROCKS_TREE%" | ||
| 298 | IF NOT EXIST "%APPDATA%/luarocks" %MKDIR% "%APPDATA%/luarocks" | ||
| 299 | |||
| 300 | REM *********************************************************** | ||
| 301 | REM Exit handlers | ||
| 302 | REM *********************************************************** | ||
| 303 | |||
| 304 | ECHO LuaRocks is installed! | ||
| 305 | :QUIT | ||
| 306 | ENDLOCAL | ||
| 307 | EXIT /B 0 | ||
| 308 | |||
| 309 | :ERROR | ||
| 310 | ECHO Failed installing LuaRocks. | ||
| 311 | ENDLOCAL | ||
| 312 | EXIT /B 1 | ||
diff --git a/makedist b/makedist new file mode 100755 index 00000000..3e78040a --- /dev/null +++ b/makedist | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | cvslist() { | ||
| 4 | if [ "$1" ] | ||
| 5 | then local prefix="$1/" | ||
| 6 | fi | ||
| 7 | cvs list $1 | grep -v "^?" | while read line | ||
| 8 | do | ||
| 9 | local path="$prefix$line" | ||
| 10 | echo "$path" | ||
| 11 | if [ -d "$path" ] | ||
| 12 | then | ||
| 13 | cvslist "$path" | ||
| 14 | fi | ||
| 15 | done | ||
| 16 | } | ||
| 17 | |||
| 18 | if ! [ "$1" ] | ||
| 19 | then | ||
| 20 | echo "usage: $0 <version>" | ||
| 21 | exit 1 | ||
| 22 | fi | ||
| 23 | |||
| 24 | cvs list > /dev/null 2> /dev/null | ||
| 25 | if [ $? != 0 ] | ||
| 26 | then | ||
| 27 | echo "Your version of CVS may be too old. At least 1.12 is needed." | ||
| 28 | exit 1 | ||
| 29 | fi | ||
| 30 | |||
| 31 | make clean | ||
| 32 | |||
| 33 | out="luarocks-$1" | ||
| 34 | rm -rf "$out" | ||
| 35 | mkdir "$out" | ||
| 36 | list=`cvslist` | ||
| 37 | rm -f missing_ref | ||
| 38 | echo "$list" | while read i | ||
| 39 | do | ||
| 40 | if [ -f "$i" ] | ||
| 41 | then | ||
| 42 | dir=`dirname $i` | ||
| 43 | mkdir -p "$out/$dir" | ||
| 44 | cp "$i" "$out/$dir" | ||
| 45 | if echo "$i" | grep -q "^src/" | ||
| 46 | then | ||
| 47 | grep -qw `basename "$i"` Makefile || { | ||
| 48 | echo "Missing ref in makefile: $i" | ||
| 49 | touch missing_ref | ||
| 50 | exit 1 | ||
| 51 | } | ||
| 52 | fi | ||
| 53 | fi | ||
| 54 | done | ||
| 55 | if [ -e missing_ref ] | ||
| 56 | then | ||
| 57 | rm -f missing_ref | ||
| 58 | exit 1 | ||
| 59 | fi | ||
| 60 | rm -f "$out-win32.zip" "$out.tar.gz" | ||
| 61 | rm "$out/makedist" | ||
| 62 | rm "$out/install.bat" | ||
| 63 | tar czvpf "$out.tar.gz" "$out" | ||
| 64 | cp install.bat "$out" | ||
| 65 | cp -a win32/bin "$out" | ||
| 66 | cp -a win32/lua5.1 "$out" | ||
| 67 | zip -r "$out-win32.zip" "$out" | ||
| 68 | rm -rf "$out" | ||
diff --git a/src/bin/luarocks b/src/bin/luarocks new file mode 100755 index 00000000..8812ecc8 --- /dev/null +++ b/src/bin/luarocks | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | #!/usr/bin/env lua | ||
| 2 | |||
| 3 | local command_line = require("luarocks.command_line") | ||
| 4 | |||
| 5 | program_name = "luarocks" | ||
| 6 | program_description = "LuaRocks main command-line interface" | ||
| 7 | |||
| 8 | commands = {} | ||
| 9 | commands.help = require("luarocks.help") | ||
| 10 | commands.pack = require("luarocks.pack") | ||
| 11 | commands.unpack = require("luarocks.unpack") | ||
| 12 | commands.build = require("luarocks.build") | ||
| 13 | commands.install = require("luarocks.install") | ||
| 14 | commands.search = require("luarocks.search") | ||
| 15 | commands.list = require("luarocks.list") | ||
| 16 | commands.remove = require("luarocks.remove") | ||
| 17 | commands.make = require("luarocks.make") | ||
| 18 | |||
| 19 | command_line.run_command(...) | ||
diff --git a/src/bin/luarocks-admin b/src/bin/luarocks-admin new file mode 100755 index 00000000..826597f6 --- /dev/null +++ b/src/bin/luarocks-admin | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | #!/usr/bin/env lua | ||
| 2 | |||
| 3 | local command_line = require("luarocks.command_line") | ||
| 4 | |||
| 5 | program_name = "luarocks-admin" | ||
| 6 | program_description = "LuaRocks repository administration interface" | ||
| 7 | |||
| 8 | commands = { | ||
| 9 | } | ||
| 10 | |||
| 11 | commands.help = require("luarocks.help") | ||
| 12 | commands.make_manifest = require("luarocks.make_manifest") | ||
| 13 | |||
| 14 | command_line.run_command(...) | ||
diff --git a/src/luarocks/build.lua b/src/luarocks/build.lua new file mode 100644 index 00000000..f0f7225d --- /dev/null +++ b/src/luarocks/build.lua | |||
| @@ -0,0 +1,276 @@ | |||
| 1 | |||
| 2 | --- Module implementing the LuaRocks "build" command. | ||
| 3 | -- Builds a rock, compiling its C parts if any. | ||
| 4 | module("luarocks.build", package.seeall) | ||
| 5 | |||
| 6 | local path = require("luarocks.path") | ||
| 7 | local util = require("luarocks.util") | ||
| 8 | local rep = require("luarocks.rep") | ||
| 9 | local fetch = require("luarocks.fetch") | ||
| 10 | local fs = require("luarocks.fs") | ||
| 11 | local deps = require("luarocks.deps") | ||
| 12 | local manif = require("luarocks.manif") | ||
| 13 | |||
| 14 | help_summary = "Build/compile a rock." | ||
| 15 | help_arguments = "{<rockspec>|<rock>|<name> [<version>]}" | ||
| 16 | help = [[ | ||
| 17 | Build a rock, compiling its C parts if any. | ||
| 18 | Argument may be a rockspec file, a source rock file | ||
| 19 | or the name of a rock to be fetched from a repository. | ||
| 20 | ]] | ||
| 21 | |||
| 22 | --- Install files to a given location. | ||
| 23 | -- Takes a table where the array part is a list of filenames to be copied. | ||
| 24 | -- In the hash part, other keys are identifiers in Lua module format, | ||
| 25 | -- to indicate which subdirectory the file should be copied to. For example, | ||
| 26 | -- install_files({["foo.bar"] = "src/bar.lua"}, "boo") will copy src/bar.lua | ||
| 27 | -- to boo/foo. | ||
| 28 | -- @param files table or nil: A table containing a list of files to copy in | ||
| 29 | -- the format described above. If nil is passed, this function is a no-op. | ||
| 30 | -- Directories should be delimited by forward slashes as in internet URLs. | ||
| 31 | -- @param location string: The base directory files should be copied to. | ||
| 32 | -- @return boolean or (nil, string): True if succeeded or | ||
| 33 | -- nil and an error message. | ||
| 34 | local function install_files(files, location) | ||
| 35 | assert(type(files) == "table" or not files) | ||
| 36 | assert(type(location) == "string") | ||
| 37 | if files then | ||
| 38 | for k, file in pairs(files) do | ||
| 39 | local dest = location | ||
| 40 | if type(k) == "string" then | ||
| 41 | dest = fs.make_path(location, path.module_to_path(k)) | ||
| 42 | end | ||
| 43 | fs.make_dir(dest) | ||
| 44 | local ok = fs.copy(fs.make_path(file), dest) | ||
| 45 | if not ok then | ||
| 46 | return nil, "Failed copying "..file | ||
| 47 | end | ||
| 48 | end | ||
| 49 | end | ||
| 50 | return true | ||
| 51 | end | ||
| 52 | |||
| 53 | --- Write to the current directory the contents of a table, | ||
| 54 | -- where each key is a file name and its value is the file content. | ||
| 55 | -- @param files table: The table of files to be written. | ||
| 56 | local function extract_from_rockspec(files) | ||
| 57 | for name, content in pairs(files) do | ||
| 58 | local fd = io.open(fs.make_path(fs.current_dir(), name), "w+") | ||
| 59 | fd:write(content) | ||
| 60 | fd:close() | ||
| 61 | end | ||
| 62 | end | ||
| 63 | |||
| 64 | --- Applies patches inlined in the build.patches section | ||
| 65 | -- and extracts files inlined in the build.extra_files section | ||
| 66 | -- of a rockspec. | ||
| 67 | -- @param rockspec table: A rockspec table. | ||
| 68 | -- @return boolean or (nil, string): True if succeeded or | ||
| 69 | -- nil and an error message. | ||
| 70 | function apply_patches(rockspec) | ||
| 71 | assert(type(rockspec) == "table") | ||
| 72 | |||
| 73 | local build = rockspec.build | ||
| 74 | if build.extra_files then | ||
| 75 | extract_from_rockspec(build.extra_files) | ||
| 76 | end | ||
| 77 | if build.patches then | ||
| 78 | extract_from_rockspec(build.patches) | ||
| 79 | for patch, _ in util.sortedpairs(build.patches) do | ||
| 80 | print("Applying patch "..patch.."...") | ||
| 81 | local ok, err = fs.patch(tostring(patch)) | ||
| 82 | if not ok then | ||
| 83 | return nil, "Failed applying patch "..patch | ||
| 84 | end | ||
| 85 | end | ||
| 86 | end | ||
| 87 | return true | ||
| 88 | end | ||
| 89 | |||
| 90 | --- Build and install a rock given a rockspec. | ||
| 91 | -- @param rockspec_file string: local or remote filename of a rockspec. | ||
| 92 | -- @param need_to_fetch boolean: true if sources need to be fetched, | ||
| 93 | -- false if the rockspec was obtained from inside a source rock. | ||
| 94 | -- @return boolean or (nil, string): True if succeeded or | ||
| 95 | -- nil and an error message. | ||
| 96 | function build_rockspec(rockspec_file, need_to_fetch, minimal_mode) | ||
| 97 | assert(type(rockspec_file) == "string") | ||
| 98 | assert(type(need_to_fetch) == "boolean") | ||
| 99 | |||
| 100 | local rockspec, err = fetch.load_rockspec(rockspec_file) | ||
| 101 | if err then | ||
| 102 | return nil, err | ||
| 103 | elseif not rockspec.build then | ||
| 104 | return nil, "Rockspec error: build table not specified" | ||
| 105 | elseif not rockspec.build.type then | ||
| 106 | return nil, "Rockspec error: build type not specified" | ||
| 107 | end | ||
| 108 | |||
| 109 | local ok, err = deps.fulfill_dependencies(rockspec) | ||
| 110 | if err then | ||
| 111 | return nil, err | ||
| 112 | end | ||
| 113 | ok, err = deps.check_external_deps(rockspec, "build") | ||
| 114 | if err then | ||
| 115 | return nil, err | ||
| 116 | end | ||
| 117 | |||
| 118 | local name, version = rockspec.name, rockspec.version | ||
| 119 | if rep.is_installed(name, version) then | ||
| 120 | rep.delete_version(name, version) | ||
| 121 | end | ||
| 122 | |||
| 123 | if not minimal_mode then | ||
| 124 | local _, dir | ||
| 125 | if need_to_fetch then | ||
| 126 | ok, dir = fetch.fetch_sources(rockspec, true) | ||
| 127 | if not ok then | ||
| 128 | return nil, dir | ||
| 129 | end | ||
| 130 | fs.change_dir(dir) | ||
| 131 | elseif rockspec.source.file then | ||
| 132 | local ok, err = fs.unpack_archive(rockspec.source.file) | ||
| 133 | if not ok then | ||
| 134 | return nil, err | ||
| 135 | end | ||
| 136 | end | ||
| 137 | fs.change_dir(rockspec.source.dir) | ||
| 138 | end | ||
| 139 | |||
| 140 | local dirs = { | ||
| 141 | lua = path.lua_dir(name, version), | ||
| 142 | lib = path.lib_dir(name, version), | ||
| 143 | conf = path.conf_dir(name, version), | ||
| 144 | bin = path.bin_dir(name, version), | ||
| 145 | } | ||
| 146 | |||
| 147 | for _, dir in pairs(dirs) do | ||
| 148 | fs.make_dir(dir) | ||
| 149 | end | ||
| 150 | local rollback = util.schedule_function(function() | ||
| 151 | fs.delete(path.install_dir(name, version)) | ||
| 152 | fs.remove_dir_if_empty(path.versions_dir(name)) | ||
| 153 | end) | ||
| 154 | |||
| 155 | local build = rockspec.build | ||
| 156 | |||
| 157 | if not minimal_mode then | ||
| 158 | ok, err = apply_patches(rockspec) | ||
| 159 | if err then | ||
| 160 | return nil, err | ||
| 161 | end | ||
| 162 | end | ||
| 163 | |||
| 164 | if build.type ~= "none" then | ||
| 165 | |||
| 166 | -- Temporary compatibility | ||
| 167 | if build.type == "module" then build.type = "builtin" end | ||
| 168 | |||
| 169 | ok, build_type = pcall(require, "luarocks.build." .. build.type) | ||
| 170 | if not ok or not type(build_type) == "table" then | ||
| 171 | return nil, "Failed initializing build back-end for build type '"..build.type.."'" | ||
| 172 | end | ||
| 173 | |||
| 174 | ok, err = build_type.run(rockspec) | ||
| 175 | if not ok then | ||
| 176 | return nil, "Build error: " .. err | ||
| 177 | end | ||
| 178 | end | ||
| 179 | |||
| 180 | if build.install then | ||
| 181 | for id, dir in pairs(dirs) do | ||
| 182 | ok, err = install_files(build.install[id], dir) | ||
| 183 | if not ok then | ||
| 184 | return nil, err | ||
| 185 | end | ||
| 186 | end | ||
| 187 | end | ||
| 188 | |||
| 189 | local copy_directories = build.copy_directories or {"doc"} | ||
| 190 | |||
| 191 | for _, dir in pairs(copy_directories) do | ||
| 192 | if fs.is_dir(dir) then | ||
| 193 | local dest = fs.make_path(path.install_dir(name, version), dir) | ||
| 194 | fs.make_dir(dest) | ||
| 195 | fs.copy_contents(dir, dest) | ||
| 196 | end | ||
| 197 | end | ||
| 198 | |||
| 199 | for _, dir in pairs(dirs) do | ||
| 200 | fs.remove_dir_if_empty(dir) | ||
| 201 | end | ||
| 202 | |||
| 203 | fs.pop_dir() | ||
| 204 | |||
| 205 | fs.copy(rockspec.local_filename, path.rockspec_file(name, version)) | ||
| 206 | if need_to_fetch then | ||
| 207 | fs.pop_dir() | ||
| 208 | end | ||
| 209 | |||
| 210 | ok, err = rep.install_bins(name, version) | ||
| 211 | if not ok then return nil, err end | ||
| 212 | |||
| 213 | ok, err = rep.run_hook(rockspec, "post_install") | ||
| 214 | if err then | ||
| 215 | return nil, err | ||
| 216 | end | ||
| 217 | |||
| 218 | ok, err = manif.update_manifest(name, version) | ||
| 219 | if err then | ||
| 220 | return nil, err | ||
| 221 | end | ||
| 222 | util.remove_scheduled_function(rollback) | ||
| 223 | return true | ||
| 224 | end | ||
| 225 | |||
| 226 | --- Build and install a rock. | ||
| 227 | -- @param rock_file string: local or remote filename of a rock. | ||
| 228 | -- @param need_to_fetch boolean: true if sources need to be fetched, | ||
| 229 | -- false if the rockspec was obtained from inside a source rock. | ||
| 230 | -- @return boolean or (nil, string): True if build was successful, | ||
| 231 | -- or false and an error message. | ||
| 232 | local function build_rock(rock_file, need_to_fetch) | ||
| 233 | assert(type(rock_file) == "string") | ||
| 234 | assert(type(need_to_fetch) == "boolean") | ||
| 235 | |||
| 236 | local dir, err = fetch.fetch_and_unpack_rock(rock_file) | ||
| 237 | if not dir then | ||
| 238 | return nil, err | ||
| 239 | end | ||
| 240 | local rockspec_file = path.rockspec_name_from_rock(rock_file) | ||
| 241 | fs.change_dir(dir) | ||
| 242 | local ok, err = build_rockspec(rockspec_file, need_to_fetch) | ||
| 243 | fs.pop_dir() | ||
| 244 | return ok, err | ||
| 245 | end | ||
| 246 | |||
| 247 | --- Driver function for "build" command. | ||
| 248 | -- @param name string: A local or remote rockspec or rock file. | ||
| 249 | -- If a package name is given, forwards the request to "search" and, | ||
| 250 | -- if returned a result, installs the matching rock. | ||
| 251 | -- @param version string: When passing a package name, a version number may | ||
| 252 | -- also be given. | ||
| 253 | -- @return boolean or (nil, string): True if build was successful; nil and an | ||
| 254 | -- error message otherwise. | ||
| 255 | function run(...) | ||
| 256 | local flags, name, version = util.parse_flags(...) | ||
| 257 | if type(name) ~= "string" then | ||
| 258 | return nil, "Argument missing, see help." | ||
| 259 | end | ||
| 260 | assert(type(version) == "string" or not version) | ||
| 261 | |||
| 262 | if name:match("%.rockspec$") then | ||
| 263 | return build_rockspec(name, true) | ||
| 264 | elseif name:match("%.src%.rock$") then | ||
| 265 | return build_rock(name, false) | ||
| 266 | elseif name:match("%.all%.rock$") then | ||
| 267 | local install = require("luarocks.install") | ||
| 268 | return install.install_binary_rock(name) | ||
| 269 | elseif name:match("%.rock$") then | ||
| 270 | return build_rock(name, true) | ||
| 271 | elseif not name:match(fs.dir_separator) then | ||
| 272 | local search = require("luarocks.search") | ||
| 273 | return search.act_on_src_or_rockspec(run, name, version) | ||
| 274 | end | ||
| 275 | return nil, "Don't know what to do with "..name | ||
| 276 | end | ||
diff --git a/src/luarocks/build/builtin.lua b/src/luarocks/build/builtin.lua new file mode 100644 index 00000000..c93edaa2 --- /dev/null +++ b/src/luarocks/build/builtin.lua | |||
| @@ -0,0 +1,152 @@ | |||
| 1 | |||
| 2 | --- A builtin build system: back-end to provide a portable way of building C-based Lua modules. | ||
| 3 | module("luarocks.build.builtin", package.seeall) | ||
| 4 | |||
| 5 | local fs = require("luarocks.fs") | ||
| 6 | local path = require("luarocks.path") | ||
| 7 | local util = require("luarocks.util") | ||
| 8 | local cfg = require("luarocks.cfg") | ||
| 9 | |||
| 10 | --- Check if platform was detected | ||
| 11 | -- @param query string: The platform name to check. | ||
| 12 | -- @return boolean: true if LuaRocks is currently running on queried platform. | ||
| 13 | local function is_platform(query) | ||
| 14 | assert(type(query) == "string") | ||
| 15 | |||
| 16 | for _, platform in ipairs(cfg.platforms) do | ||
| 17 | if platform == query then | ||
| 18 | return true | ||
| 19 | end | ||
| 20 | end | ||
| 21 | end | ||
| 22 | |||
| 23 | --- Run a command displaying its execution on standard output. | ||
| 24 | -- @return boolean: true if command succeeds (status code 0), false | ||
| 25 | -- otherwise. | ||
| 26 | local function execute(...) | ||
| 27 | io.stdout:write(table.concat({...}, " ").."\n") | ||
| 28 | return fs.execute(...) | ||
| 29 | end | ||
| 30 | |||
| 31 | --- Driver function for the builtin build back-end. | ||
| 32 | -- @param rockspec table: the loaded rockspec. | ||
| 33 | -- @return boolean or (nil, string): true if no errors ocurred, | ||
| 34 | -- nil and an error message otherwise. | ||
| 35 | function run(rockspec) | ||
| 36 | assert(type(rockspec) == "table") | ||
| 37 | local compile_object, compile_library | ||
| 38 | |||
| 39 | local build = rockspec.build | ||
| 40 | local variables = rockspec.variables | ||
| 41 | |||
| 42 | local function add_flags(extras, flag, flags) | ||
| 43 | if flags then | ||
| 44 | if type(flags) ~= "table" then | ||
| 45 | flags = { tostring(flags) } | ||
| 46 | end | ||
| 47 | util.variable_substitutions(flags, variables) | ||
| 48 | for _, v in ipairs(flags) do | ||
| 49 | table.insert(extras, flag:format(v)) | ||
| 50 | end | ||
| 51 | end | ||
| 52 | end | ||
| 53 | |||
| 54 | if is_platform("win32") then | ||
| 55 | compile_object = function(object, source, defines, incdirs) | ||
| 56 | local extras = {} | ||
| 57 | add_flags(extras, "-D%s", defines) | ||
| 58 | add_flags(extras, "-I%s", incdirs) | ||
| 59 | return execute(variables.CC.." "..variables.CFLAGS, "-c", "-Fo"..object, "-I"..variables.LUA_INCDIR, source, unpack(extras)) | ||
| 60 | end | ||
| 61 | compile_library = function(library, objects, libraries, libdirs, name) | ||
| 62 | local extras = { unpack(objects) } | ||
| 63 | add_flags(extras, "-libpath:%s", libdirs) | ||
| 64 | add_flags(extras, "%s.lib", libraries) | ||
| 65 | local basename = fs.base_name(library):gsub(".[^.]*$", "") | ||
| 66 | local deffile = basename .. ".def" | ||
| 67 | local def = io.open(fs.make_path(fs.current_dir(), deffile), "w+") | ||
| 68 | def:write("EXPORTS\n") | ||
| 69 | def:write("luaopen_"..name:gsub("%.", "_").."\n") | ||
| 70 | def:close() | ||
| 71 | local ok = execute(variables.LD, "-dll", "-def:"..deffile, "-out:"..library, fs.make_path(variables.LUA_LIBDIR, "lua5.1.lib"), unpack(extras)) | ||
| 72 | local manifestfile = basename..".dll.manifest" | ||
| 73 | if ok and fs.exists(manifestfile) then | ||
| 74 | ok = execute(variables.MT, "-manifest", manifestfile, "-outputresource:"..basename..".dll;2") | ||
| 75 | end | ||
| 76 | return ok | ||
| 77 | end | ||
| 78 | else | ||
| 79 | compile_object = function(object, source, defines, incdirs) | ||
| 80 | local extras = {} | ||
| 81 | add_flags(extras, "-D%s", defines) | ||
| 82 | add_flags(extras, "-I%s", incdirs) | ||
| 83 | return execute(variables.CC.." "..variables.CFLAGS, "-I"..variables.LUA_INCDIR, "-c", source, "-o", object, unpack(extras)) | ||
| 84 | end | ||
| 85 | compile_library = function (library, objects, libraries, libdirs) | ||
| 86 | local extras = { unpack(objects) } | ||
| 87 | add_flags(extras, "-L%s", libdirs) | ||
| 88 | add_flags(extras, "-l%s", libraries) | ||
| 89 | if is_platform("cygwin") then | ||
| 90 | add_flags(extras, "-l%s", {"lua"}) | ||
| 91 | end | ||
| 92 | return execute(variables.LD.." "..variables.LIBFLAG, "-o", library, unpack(extras)) | ||
| 93 | end | ||
| 94 | end | ||
| 95 | |||
| 96 | local ok = true | ||
| 97 | local built_modules = {} | ||
| 98 | local luadir = path.lua_dir(rockspec.name, rockspec.version) | ||
| 99 | local libdir = path.lib_dir(rockspec.name, rockspec.version) | ||
| 100 | local docdir = path.doc_dir(rockspec.name, rockspec.version) | ||
| 101 | for name, info in pairs(build.modules) do | ||
| 102 | local moddir = path.module_to_path(name) | ||
| 103 | if type(info) == "string" then | ||
| 104 | local ext = info:match(".([^.]+)$") | ||
| 105 | if ext == "lua" then | ||
| 106 | local dest = fs.make_path(luadir, moddir) | ||
| 107 | built_modules[info] = dest | ||
| 108 | else | ||
| 109 | info = {info} | ||
| 110 | end | ||
| 111 | end | ||
| 112 | if type(info) == "table" then | ||
| 113 | local objects = {} | ||
| 114 | local sources = info.sources | ||
| 115 | if info[1] then sources = info end | ||
| 116 | if type(sources) == "string" then sources = {sources} end | ||
| 117 | for _, source in ipairs(sources) do | ||
| 118 | local object = source:gsub(".[^.]*$", "."..cfg.obj_extension) | ||
| 119 | if not object then | ||
| 120 | object = source.."."..cfg.obj_extension | ||
| 121 | end | ||
| 122 | ok = compile_object(object, source, info.defines, info.incdirs) | ||
| 123 | if not ok then break end | ||
| 124 | table.insert(objects, object) | ||
| 125 | end | ||
| 126 | if not ok then break end | ||
| 127 | local module_name = fs.make_path(moddir, name:match("([^.]*)$").."."..cfg.lib_extension):gsub("//", "/") | ||
| 128 | if moddir ~= "" then | ||
| 129 | fs.make_dir(moddir) | ||
| 130 | end | ||
| 131 | local dest = fs.make_path(libdir, moddir) | ||
| 132 | built_modules[module_name] = dest | ||
| 133 | ok = compile_library(module_name, objects, info.libraries, info.libdirs, name) | ||
| 134 | if not ok then break end | ||
| 135 | end | ||
| 136 | end | ||
| 137 | for name, dest in pairs(built_modules) do | ||
| 138 | fs.make_dir(dest) | ||
| 139 | ok = fs.copy(name, dest) | ||
| 140 | if not ok then break end | ||
| 141 | end | ||
| 142 | if ok then | ||
| 143 | if fs.is_dir("lua") then | ||
| 144 | fs.copy_contents("lua", luadir) | ||
| 145 | end | ||
| 146 | end | ||
| 147 | if ok then | ||
| 148 | return true | ||
| 149 | else | ||
| 150 | return nil, "Build error" | ||
| 151 | end | ||
| 152 | end | ||
diff --git a/src/luarocks/build/cmake.lua b/src/luarocks/build/cmake.lua new file mode 100644 index 00000000..7fd1fcc8 --- /dev/null +++ b/src/luarocks/build/cmake.lua | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | |||
| 2 | --- Build back-end for CMake-based modules. | ||
| 3 | module("luarocks.build.cmake", package.seeall) | ||
| 4 | |||
| 5 | local fs = require("luarocks.fs") | ||
| 6 | local util = require("luarocks.util") | ||
| 7 | local cfg = require("luarocks.cfg") | ||
| 8 | |||
| 9 | --- Driver function for the "cmake" build back-end. | ||
| 10 | -- @param rockspec table: the loaded rockspec. | ||
| 11 | -- @return boolean or (nil, string): true if no errors ocurred, | ||
| 12 | -- nil and an error message otherwise. | ||
| 13 | function run(rockspec) | ||
| 14 | assert(type(rockspec) == "table") | ||
| 15 | local build = rockspec.build | ||
| 16 | local variables = build.variables or {} | ||
| 17 | |||
| 18 | -- Pass Env variables | ||
| 19 | build.variables.CMAKE_MODULE_PATH=os.getenv("CMAKE_MODULE_PATH") | ||
| 20 | build.variables.CMAKE_LIBRARY_PATH=os.getenv("CMAKE_LIBRARY_PATH") | ||
| 21 | build.variables.CMAKE_INCLUDE_PATH=os.getenv("CMAKE_INCLUDE_PATH") | ||
| 22 | |||
| 23 | util.variable_substitutions(variables, rockspec.variables) | ||
| 24 | |||
| 25 | -- If inline cmake is present create CMakeLists.txt from it. | ||
| 26 | if type(build.cmake) == "string" then | ||
| 27 | local cmake = assert(io.open(fs.current_dir().."/CMakeLists.txt", "w")) | ||
| 28 | cmake:write(build.cmake) | ||
| 29 | cmake:close() | ||
| 30 | end | ||
| 31 | |||
| 32 | |||
| 33 | -- Execute cmake with variables. | ||
| 34 | local args = "" | ||
| 35 | if cfg.cmake_generator then | ||
| 36 | args = args .. ' -G"'..cfg.cmake_generator.. '"' | ||
| 37 | end | ||
| 38 | for k,v in pairs(variables) do | ||
| 39 | args = args .. ' -D' ..k.. '="' ..v.. '"' | ||
| 40 | end | ||
| 41 | |||
| 42 | if not fs.execute("cmake . " ..args) then | ||
| 43 | return nil, "Failed cmake." | ||
| 44 | end | ||
| 45 | |||
| 46 | if not fs.execute("make -fMakefile") then | ||
| 47 | return nil, "Failed building." | ||
| 48 | end | ||
| 49 | |||
| 50 | if not fs.execute("make -fMakefile install") then | ||
| 51 | return nil, "Failed installing." | ||
| 52 | end | ||
| 53 | return true | ||
| 54 | end | ||
diff --git a/src/luarocks/build/command.lua b/src/luarocks/build/command.lua new file mode 100644 index 00000000..72d3de7f --- /dev/null +++ b/src/luarocks/build/command.lua | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | |||
| 2 | --- Build back-end for raw listing of commands in rockspec files. | ||
| 3 | module("luarocks.build.command", package.seeall) | ||
| 4 | |||
| 5 | local fs = require("luarocks.fs") | ||
| 6 | local util = require("luarocks.util") | ||
| 7 | |||
| 8 | --- Driver function for the "command" build back-end. | ||
| 9 | -- @param rockspec table: the loaded rockspec. | ||
| 10 | -- @return boolean or (nil, string): true if no errors ocurred, | ||
| 11 | -- nil and an error message otherwise. | ||
| 12 | function run(rockspec) | ||
| 13 | assert(type(rockspec) == "table") | ||
| 14 | |||
| 15 | local build = rockspec.build | ||
| 16 | |||
| 17 | util.variable_substitutions(build, rockspec.variables) | ||
| 18 | |||
| 19 | if build.build_command then | ||
| 20 | if not fs.execute(build.build_command) then | ||
| 21 | return nil, "Failed building." | ||
| 22 | end | ||
| 23 | end | ||
| 24 | if build.install_command then | ||
| 25 | if not fs.execute(build.install_command) then | ||
| 26 | return nil, "Failed installing." | ||
| 27 | end | ||
| 28 | end | ||
| 29 | return true | ||
| 30 | end | ||
diff --git a/src/luarocks/build/make.lua b/src/luarocks/build/make.lua new file mode 100644 index 00000000..b3e553a9 --- /dev/null +++ b/src/luarocks/build/make.lua | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | |||
| 2 | --- Build back-end for using Makefile-based packages. | ||
| 3 | module("luarocks.build.make", package.seeall) | ||
| 4 | |||
| 5 | local fs = require("luarocks.fs") | ||
| 6 | local util = require("luarocks.util") | ||
| 7 | local cfg = require("luarocks.cfg") | ||
| 8 | |||
| 9 | --- Call "make" with given target and variables | ||
| 10 | -- @param pass boolean: If true, run make; if false, do nothing. | ||
| 11 | -- @param target string: The make target; an empty string indicates | ||
| 12 | -- the default target. | ||
| 13 | -- @param variables table: A table containing string-string key-value | ||
| 14 | -- pairs representing variable assignments to be passed to make. | ||
| 15 | -- @return boolean: false if any errors occurred, true otherwise. | ||
| 16 | local function make_pass(pass, target, variables) | ||
| 17 | assert(type(pass) == "boolean") | ||
| 18 | assert(type(target) == "string") | ||
| 19 | assert(type(variables) == "table") | ||
| 20 | |||
| 21 | local assignments = {} | ||
| 22 | for k,v in pairs(variables) do | ||
| 23 | table.insert(assignments, k.."="..v) | ||
| 24 | end | ||
| 25 | if pass then | ||
| 26 | return fs.execute(cfg.make.." "..target, unpack(assignments)) | ||
| 27 | else | ||
| 28 | return true | ||
| 29 | end | ||
| 30 | end | ||
| 31 | |||
| 32 | --- Driver function for the "make" build back-end. | ||
| 33 | -- @param rockspec table: the loaded rockspec. | ||
| 34 | -- @return boolean or (nil, string): true if no errors ocurred, | ||
| 35 | -- nil and an error message otherwise. | ||
| 36 | function run(rockspec) | ||
| 37 | assert(type(rockspec) == "table") | ||
| 38 | |||
| 39 | local build = rockspec.build | ||
| 40 | |||
| 41 | if build.build_pass == nil then build.build_pass = true end | ||
| 42 | if build.install_pass == nil then build.install_pass = true end | ||
| 43 | build.build_variables = build.build_variables or {} | ||
| 44 | build.install_variables = build.install_variables or {} | ||
| 45 | build.build_target = build.build_target or "" | ||
| 46 | build.install_target = build.install_target or "install" | ||
| 47 | local makefile = build.makefile or cfg.makefile | ||
| 48 | if makefile then | ||
| 49 | -- Assumes all make's accept -f. True for POSIX make, GNU make and Microsoft nmake. | ||
| 50 | build.build_target = "-f "..makefile.." "..build.build_target | ||
| 51 | build.install_target = "-f "..makefile.." "..build.install_target | ||
| 52 | end | ||
| 53 | |||
| 54 | if build.variables then | ||
| 55 | for var, val in pairs(build.variables) do | ||
| 56 | build.build_variables[var] = val | ||
| 57 | build.install_variables[var] = val | ||
| 58 | end | ||
| 59 | end | ||
| 60 | |||
| 61 | util.variable_substitutions(build.build_variables, rockspec.variables) | ||
| 62 | util.variable_substitutions(build.install_variables, rockspec.variables) | ||
| 63 | |||
| 64 | local auto_variables = { "CC" } | ||
| 65 | |||
| 66 | for _, variable in pairs(auto_variables) do | ||
| 67 | if not build.build_variables[variable] then | ||
| 68 | build.build_variables[variable] = rockspec.variables[variable] | ||
| 69 | end | ||
| 70 | if not build.install_variables[variable] then | ||
| 71 | build.install_variables[variable] = rockspec.variables[variable] | ||
| 72 | end | ||
| 73 | end | ||
| 74 | |||
| 75 | local ok = make_pass(build.build_pass, build.build_target, build.build_variables) | ||
| 76 | if not ok then | ||
| 77 | return nil, "Failed building." | ||
| 78 | end | ||
| 79 | ok = make_pass(build.install_pass, build.install_target, build.install_variables) | ||
| 80 | if not ok then | ||
| 81 | return nil, "Failed installing." | ||
| 82 | end | ||
| 83 | return true | ||
| 84 | end | ||
diff --git a/src/luarocks/cfg.lua b/src/luarocks/cfg.lua new file mode 100644 index 00000000..3c4c5aed --- /dev/null +++ b/src/luarocks/cfg.lua | |||
| @@ -0,0 +1,242 @@ | |||
| 1 | |||
| 2 | local rawset, next, table, pairs, print, require, io, os, setmetatable = | ||
| 3 | rawset, next, table, pairs, print, require, io, os, setmetatable | ||
| 4 | |||
| 5 | --- Configuration for LuaRocks. | ||
| 6 | -- Tries to load the user's configuration file and | ||
| 7 | -- defines defaults for unset values. See the | ||
| 8 | -- <a href="http://luarocks.org/en/Config_file_format">config | ||
| 9 | -- file format documentation</a> for details. | ||
| 10 | module("luarocks.cfg") | ||
| 11 | |||
| 12 | local persist = require("luarocks.persist") | ||
| 13 | |||
| 14 | local detected = {} | ||
| 15 | local system,proc | ||
| 16 | |||
| 17 | -- A proper installation of LuaRocks will hardcode the system | ||
| 18 | -- and proc values with LUAROCKS_UNAME_S and LUAROCKS_UNAME_M, | ||
| 19 | -- so that this detection does not run every time. When it is | ||
| 20 | -- performed, we use the Unix way to identify the system, | ||
| 21 | -- even on Windows (assuming UnxUtils or Cygwin). | ||
| 22 | system = LUAROCKS_UNAME_S or io.popen("uname -s"):read("*l") | ||
| 23 | proc = LUAROCKS_UNAME_M or io.popen("uname -m"):read("*l") | ||
| 24 | if proc:match("i[%d]86") then | ||
| 25 | proc = "x86" | ||
| 26 | elseif proc:match("amd64") or proc:match("x86_64") then | ||
| 27 | proc = "x86_64" | ||
| 28 | elseif proc:match("Power Macintosh") then | ||
| 29 | proc = "powerpc" | ||
| 30 | end | ||
| 31 | |||
| 32 | if system == "FreeBSD" then | ||
| 33 | detected.unix = true | ||
| 34 | detected.freebsd = true | ||
| 35 | elseif system == "Darwin" then | ||
| 36 | detected.unix = true | ||
| 37 | detected.macosx = true | ||
| 38 | elseif system == "Linux" then | ||
| 39 | detected.unix = true | ||
| 40 | detected.linux = true | ||
| 41 | elseif system and system:match("^CYGWIN") then | ||
| 42 | detected.unix = true | ||
| 43 | detected.cygwin = true | ||
| 44 | elseif system and system:match("^Windows") then | ||
| 45 | detected.windows = true | ||
| 46 | else | ||
| 47 | detected.unix = true | ||
| 48 | -- Fall back to Unix in unknown systems. | ||
| 49 | end | ||
| 50 | |||
| 51 | local sys_config_file, home_config_file, home_tree | ||
| 52 | if detected.windows then | ||
| 53 | home = os.getenv("APPDATA") or "c:" | ||
| 54 | sys_config_file = "c:/luarocks/config.lua" | ||
| 55 | home_config_file = home.."/luarocks/config.lua" | ||
| 56 | home_tree = home.."/luarocks/" | ||
| 57 | else | ||
| 58 | home = os.getenv("HOME") or "" | ||
| 59 | sys_config_file = "/etc/luarocks/config.lua" | ||
| 60 | home_config_file = home.."/.luarocks/config.lua" | ||
| 61 | home_tree = home.."/.luarocks/" | ||
| 62 | end | ||
| 63 | |||
| 64 | variables = {} | ||
| 65 | rocks_trees = {} | ||
| 66 | |||
| 67 | persist.load_into_table(LUAROCKS_SYSCONFIG or sys_config_file, _M) | ||
| 68 | |||
| 69 | if not LUAROCKS_FORCE_CONFIG then | ||
| 70 | home_config_file = os.getenv("LUAROCKS_CONFIG") or home_config_file | ||
| 71 | local home_overrides = persist.load_into_table(home_config_file, { home = home }) | ||
| 72 | if home_overrides then | ||
| 73 | local util = require("luarocks.util") | ||
| 74 | util.deep_merge(_M, home_overrides) | ||
| 75 | end | ||
| 76 | end | ||
| 77 | |||
| 78 | local defaults = { | ||
| 79 | arch = "unknown", | ||
| 80 | lib_extension = "unknown", | ||
| 81 | obj_extension = "unknown", | ||
| 82 | rocks_servers = { | ||
| 83 | "http://luarocks.luaforge.net/rocks" | ||
| 84 | }, | ||
| 85 | lua_extension = "lua", | ||
| 86 | lua_interpreter = LUA_INTERPRETER or "lua", | ||
| 87 | downloader = LUAROCKS_DOWNLOADER or "wget", | ||
| 88 | md5checker = LUAROCKS_MD5CHECKER or "md5sum", | ||
| 89 | variables = {} | ||
| 90 | } | ||
| 91 | |||
| 92 | defaults.external_deps_subdirs = { | ||
| 93 | bin = "bin", | ||
| 94 | lib = "lib", | ||
| 95 | include = "include" | ||
| 96 | } | ||
| 97 | defaults.runtime_external_deps_subdirs = defaults.external_deps_subdirs | ||
| 98 | |||
| 99 | if detected.windows then | ||
| 100 | home_config_file = home_config_file:gsub("\\","/") | ||
| 101 | defaults.lib_extension = "dll" | ||
| 102 | defaults.obj_extension = "obj" | ||
| 103 | local rootdir = LUAROCKS_ROCKS_TREE or home_tree | ||
| 104 | defaults.root_dir = rootdir | ||
| 105 | defaults.rocks_dir = rootdir.."/rocks/" | ||
| 106 | defaults.scripts_dir = rootdir.."/bin/" | ||
| 107 | defaults.external_deps_dirs = { "c:/external/" } | ||
| 108 | defaults.variables.LUA_BINDIR = LUA_BINDIR and LUA_BINDIR:gsub("\\", "/") or "c:/lua5.1/bin" | ||
| 109 | defaults.variables.LUA_INCDIR = LUA_INCDIR and LUA_INCDIR:gsub("\\", "/") or "c:/lua5.1/include" | ||
| 110 | defaults.variables.LUA_LIBDIR = LUA_LIBDIR and LUA_LIBDIR:gsub("\\", "/") or "c:/lua5.1/lib" | ||
| 111 | defaults.arch = "win32-"..proc | ||
| 112 | defaults.platforms = {"win32", "windows" } | ||
| 113 | defaults.cmake_generator = "MinGW Makefiles" | ||
| 114 | -- TODO: Split Windows flavors between mingw and msvc | ||
| 115 | -- defaults.make = "make" | ||
| 116 | -- defaults.makefile = "Makefile" | ||
| 117 | defaults.make = "nmake" | ||
| 118 | defaults.makefile = "Makefile.win" | ||
| 119 | defaults.variables.CC = "cl" | ||
| 120 | defaults.variables.LD = "link" | ||
| 121 | defaults.variables.MT = "mt" | ||
| 122 | defaults.variables.CFLAGS = "/MD /O2" | ||
| 123 | defaults.variables.LIBFLAG = "/dll" | ||
| 124 | defaults.external_deps_patterns = { | ||
| 125 | bin = { "?.exe", "?.bat" }, | ||
| 126 | lib = { "?.lib", "?.dll" }, | ||
| 127 | include = { "?.h" } | ||
| 128 | } | ||
| 129 | defaults.runtime_external_deps_patterns = { | ||
| 130 | bin = { "?.exe", "?.bat" }, | ||
| 131 | lib = { "?.dll" }, | ||
| 132 | include = { "?.h" } | ||
| 133 | } | ||
| 134 | end | ||
| 135 | |||
| 136 | if detected.unix then | ||
| 137 | defaults.lib_extension = "so" | ||
| 138 | defaults.obj_extension = "o" | ||
| 139 | local rootdir = LUAROCKS_ROCKS_TREE or home_tree | ||
| 140 | defaults.root_dir = rootdir | ||
| 141 | defaults.rocks_dir = rootdir.."/rocks/" | ||
| 142 | defaults.scripts_dir = rootdir.."/bin/" | ||
| 143 | defaults.external_deps_dirs = { "/usr/local", "/usr" } | ||
| 144 | defaults.variables.LUA_BINDIR = LUA_BINDIR or "/usr/local/bin" | ||
| 145 | defaults.variables.LUA_INCDIR = LUA_INCDIR or "/usr/local/include" | ||
| 146 | defaults.variables.LUA_LIBDIR = LUA_LIBDIR or "/usr/local/lib" | ||
| 147 | defaults.variables.CFLAGS = "-O2" | ||
| 148 | defaults.cmake_generator = "Unix Makefiles" | ||
| 149 | defaults.make = "make" | ||
| 150 | defaults.platforms = { "unix" } | ||
| 151 | defaults.variables.CC = "cc" | ||
| 152 | defaults.variables.LD = "ld" | ||
| 153 | defaults.variables.LIBFLAG = "-shared" | ||
| 154 | defaults.external_deps_patterns = { | ||
| 155 | bin = { "?" }, | ||
| 156 | lib = { "lib?.a", "lib?.so" }, | ||
| 157 | include = { "?.h" } | ||
| 158 | } | ||
| 159 | defaults.runtime_external_deps_patterns = { | ||
| 160 | bin = { "?" }, | ||
| 161 | lib = { "lib?.so" }, | ||
| 162 | include = { "?.h" } | ||
| 163 | } | ||
| 164 | end | ||
| 165 | |||
| 166 | if detected.cygwin then | ||
| 167 | defaults.lib_extension = "so" -- can be overridden in the config file for mingw builds | ||
| 168 | defaults.arch = "cygwin-"..proc | ||
| 169 | defaults.platforms = {"unix", "cygwin"} | ||
| 170 | defaults.cmake_generator = "Unix Makefiles" | ||
| 171 | defaults.variables.CC = "echo -llua | xargs gcc" | ||
| 172 | defaults.variables.LD = "echo -llua | xargs gcc" | ||
| 173 | defaults.variables.LIBFLAG = "-shared" | ||
| 174 | end | ||
| 175 | |||
| 176 | defaults.external_lib_extension = defaults.lib_extension | ||
| 177 | |||
| 178 | if detected.macosx then | ||
| 179 | defaults.external_lib_extension = "dylib" | ||
| 180 | defaults.arch = "macosx-"..proc | ||
| 181 | defaults.platforms = {"unix", "macosx"} | ||
| 182 | defaults.variables.CC = "export MACOSX_DEPLOYMENT_TARGET=10.3; gcc" | ||
| 183 | defaults.variables.LD = "export MACOSX_DEPLOYMENT_TARGET=10.3; gcc" | ||
| 184 | defaults.variables.LIBFLAG = "-bundle -undefined dynamic_lookup -all_load" | ||
| 185 | end | ||
| 186 | |||
| 187 | if detected.linux then | ||
| 188 | defaults.arch = "linux-"..proc | ||
| 189 | defaults.platforms = {"unix", "linux"} | ||
| 190 | defaults.variables.CC = "gcc" | ||
| 191 | defaults.variables.LD = "gcc" | ||
| 192 | defaults.variables.LIBFLAG = "-shared" | ||
| 193 | end | ||
| 194 | |||
| 195 | if detected.freebsd then | ||
| 196 | defaults.arch = "freebsd-"..proc | ||
| 197 | defaults.make = "gmake" | ||
| 198 | defaults.platforms = {"unix", "freebsd"} | ||
| 199 | defaults.variables.CC = "gcc" | ||
| 200 | defaults.variables.LD = "gcc" | ||
| 201 | defaults.variables.LIBFLAG = "-shared" | ||
| 202 | end | ||
| 203 | |||
| 204 | if proc == "x86_64" and not defaults.variables.CFLAGS:match("-fPIC") then | ||
| 205 | defaults.variables.CFLAGS = defaults.variables.CFLAGS.." -fPIC" | ||
| 206 | end | ||
| 207 | |||
| 208 | -- Expose some more values detected by LuaRocks for use by rockspec authors. | ||
| 209 | defaults.variables.LUA = defaults.lua_interpreter | ||
| 210 | defaults.variables.LIB_EXTENSION = defaults.lib_extension | ||
| 211 | defaults.variables.OBJ_EXTENSION = defaults.obj_extension | ||
| 212 | defaults.variables.LUAROCKS_PREFIX = LUAROCKS_PREFIX | ||
| 213 | |||
| 214 | local cfg_mt = { | ||
| 215 | __index = function(t, k) | ||
| 216 | local default = defaults[k] | ||
| 217 | if default then | ||
| 218 | rawset(t, k, default) | ||
| 219 | end | ||
| 220 | return default | ||
| 221 | end | ||
| 222 | } | ||
| 223 | |||
| 224 | if not _M.variables then | ||
| 225 | _M.variables = {} | ||
| 226 | end | ||
| 227 | for k,v in pairs(defaults.variables) do | ||
| 228 | if not _M.variables[k] then | ||
| 229 | _M.variables[k] = v | ||
| 230 | end | ||
| 231 | end | ||
| 232 | |||
| 233 | setmetatable(_M, cfg_mt) | ||
| 234 | |||
| 235 | if not next(rocks_trees) then | ||
| 236 | if home_tree then | ||
| 237 | table.insert(rocks_trees, home_tree) | ||
| 238 | end | ||
| 239 | if LUAROCKS_ROCKS_TREE then | ||
| 240 | table.insert(rocks_trees, LUAROCKS_ROCKS_TREE) | ||
| 241 | end | ||
| 242 | end | ||
diff --git a/src/luarocks/command_line.lua b/src/luarocks/command_line.lua new file mode 100644 index 00000000..b3020284 --- /dev/null +++ b/src/luarocks/command_line.lua | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | |||
| 2 | program_version = "1.0" | ||
| 3 | |||
| 4 | --- Functions for command-line scripts. | ||
| 5 | module("luarocks.command_line", package.seeall) | ||
| 6 | |||
| 7 | local util = require("luarocks.util") | ||
| 8 | local cfg = require("luarocks.cfg") | ||
| 9 | local fs = require("luarocks.fs") | ||
| 10 | |||
| 11 | --- Display an error message and exit. | ||
| 12 | -- @param message string: The error message. | ||
| 13 | local function die(message) | ||
| 14 | assert(type(message) == "string") | ||
| 15 | |||
| 16 | local ok, err = pcall(util.run_scheduled_functions) | ||
| 17 | if not ok then | ||
| 18 | print("\nLuaRocks "..program_version.." internal bug (please report at luarocks-developers@lists.luaforge.net):\n"..err) | ||
| 19 | end | ||
| 20 | print("\nError: "..message) | ||
| 21 | os.exit(1) | ||
| 22 | end | ||
| 23 | |||
| 24 | --- Main command-line processor. | ||
| 25 | -- Parses input arguments and calls the appropriate driver function | ||
| 26 | -- to execute the action requested on the command-line, forwarding | ||
| 27 | -- to it any additional arguments passed by the user. | ||
| 28 | -- Uses the global table "commands", which contains | ||
| 29 | -- the loaded modules representing commands. | ||
| 30 | -- @param ... string: Arguments given on the command-line. | ||
| 31 | function run_command(...) | ||
| 32 | local args = {...} | ||
| 33 | local cmdline_vars = {} | ||
| 34 | for i = #args, 1, -1 do | ||
| 35 | local arg = args[i] | ||
| 36 | if arg:match("^[^-][^=]*=") then | ||
| 37 | local var, val = arg:match("^([A-Z_][A-Z0-9_]*)=(.*)") | ||
| 38 | if val then | ||
| 39 | cmdline_vars[var] = val | ||
| 40 | table.remove(args, i) | ||
| 41 | else | ||
| 42 | die("Invalid assignment: "..arg) | ||
| 43 | end | ||
| 44 | end | ||
| 45 | end | ||
| 46 | local nonflags = { util.parse_flags(unpack(args)) } | ||
| 47 | local flags = table.remove(nonflags, 1) | ||
| 48 | |||
| 49 | if flags["to"] then | ||
| 50 | if flags["to"] == true then | ||
| 51 | die("Argument error: use --to=<path>") | ||
| 52 | end | ||
| 53 | local root_dir = fs.absolute_name(flags["to"]) | ||
| 54 | cfg.root_dir = root_dir | ||
| 55 | cfg.rocks_dir = root_dir.."/rocks" | ||
| 56 | cfg.scripts_dir = root_dir.."/bin" | ||
| 57 | else | ||
| 58 | local trees = cfg.rocks_trees | ||
| 59 | for i = #trees, 1, -1 do | ||
| 60 | local tree = trees[i] | ||
| 61 | if fs.make_dir(tree) and fs.is_writable(tree) then | ||
| 62 | cfg.root_dir = tree | ||
| 63 | cfg.rocks_dir = tree.."/rocks" | ||
| 64 | cfg.scripts_dir = rawget(cfg, "scripts_dir") or tree.."/bin" | ||
| 65 | break | ||
| 66 | end | ||
| 67 | end | ||
| 68 | end | ||
| 69 | |||
| 70 | cfg.root_dir = cfg.root_dir:gsub("/+$", "") | ||
| 71 | cfg.rocks_dir = cfg.rocks_dir:gsub("/+$", "") | ||
| 72 | cfg.scripts_dir = cfg.scripts_dir:gsub("/+$", "") | ||
| 73 | |||
| 74 | cfg.variables.ROCKS_TREE = cfg.root_dir | ||
| 75 | cfg.variables.SCRIPTS_DIR = cfg.scripts_dir | ||
| 76 | |||
| 77 | if flags["from"] then | ||
| 78 | if flags["from"] == true then | ||
| 79 | die("Argument error: use --from=<url>") | ||
| 80 | end | ||
| 81 | local protocol, path = fs.split_url(flags["from"]) | ||
| 82 | table.insert(cfg.rocks_servers, 1, protocol.."://"..path) | ||
| 83 | end | ||
| 84 | |||
| 85 | if flags["only-from"] then | ||
| 86 | if flags["only-from"] == true then | ||
| 87 | die("Argument error: use --only-from=<url>") | ||
| 88 | end | ||
| 89 | cfg.rocks_servers = { flags["only-from"] } | ||
| 90 | end | ||
| 91 | |||
| 92 | local command | ||
| 93 | |||
| 94 | if flags["version"] then | ||
| 95 | print(program_name.." "..program_version) | ||
| 96 | print(program_description) | ||
| 97 | print() | ||
| 98 | os.exit(0) | ||
| 99 | elseif flags["help"] or #nonflags == 0 then | ||
| 100 | command = "help" | ||
| 101 | args = nonflags | ||
| 102 | else | ||
| 103 | command = nonflags[1] | ||
| 104 | for i, arg in ipairs(args) do | ||
| 105 | if arg == command then | ||
| 106 | table.remove(args, i) | ||
| 107 | break | ||
| 108 | end | ||
| 109 | end | ||
| 110 | end | ||
| 111 | |||
| 112 | if command ~= "help" then | ||
| 113 | for k, v in pairs(cmdline_vars) do | ||
| 114 | cfg.variables[k] = v | ||
| 115 | end | ||
| 116 | end | ||
| 117 | |||
| 118 | command = command:gsub("-", "_") | ||
| 119 | if commands[command] then | ||
| 120 | local xp, ok, err = xpcall(function() return commands[command].run(unpack(args)) end, function(err) | ||
| 121 | die(debug.traceback("LuaRocks "..program_version | ||
| 122 | .." bug (please report at luarocks-developers@lists.luaforge.net).\n" | ||
| 123 | ..err, 2)) | ||
| 124 | end) | ||
| 125 | if xp and (not ok) then | ||
| 126 | die(err) | ||
| 127 | end | ||
| 128 | else | ||
| 129 | die("Unknown command: "..command) | ||
| 130 | end | ||
| 131 | |||
| 132 | util.run_scheduled_functions() | ||
| 133 | end | ||
diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua new file mode 100644 index 00000000..d5a64e52 --- /dev/null +++ b/src/luarocks/deps.lua | |||
| @@ -0,0 +1,618 @@ | |||
| 1 | |||
| 2 | --- Dependency handling functions. | ||
| 3 | -- Dependencies are represented in LuaRocks through strings with | ||
| 4 | -- a package name followed by a comma-separated list of constraints. | ||
| 5 | -- Each constraint consists of an operator and a version number. | ||
| 6 | -- In this string format, version numbers are represented as | ||
| 7 | -- naturally as possible, like they are used by upstream projects | ||
| 8 | -- (e.g. "2.0beta3"). Internally, LuaRocks converts them to a purely | ||
| 9 | -- numeric representation, allowing comparison following some | ||
| 10 | -- "common sense" heuristics. The precise specification of the | ||
| 11 | -- comparison criteria is the source code of this module, but the | ||
| 12 | -- test/test_deps.lua file included with LuaRocks provides some | ||
| 13 | -- insights on what these criteria are. | ||
| 14 | module("luarocks.deps", package.seeall) | ||
| 15 | |||
| 16 | local rep = require("luarocks.rep") | ||
| 17 | local search = require("luarocks.search") | ||
| 18 | local install = require("luarocks.install") | ||
| 19 | local cfg = require("luarocks.cfg") | ||
| 20 | local manif = require("luarocks.manif") | ||
| 21 | local fs = require("luarocks.fs") | ||
| 22 | local fetch = require("luarocks.fetch") | ||
| 23 | local path = require("luarocks.path") | ||
| 24 | |||
| 25 | local operators = { | ||
| 26 | ["=="] = "==", | ||
| 27 | ["~="] = "~=", | ||
| 28 | [">"] = ">", | ||
| 29 | ["<"] = "<", | ||
| 30 | [">="] = ">=", | ||
| 31 | ["<="] = "<=", | ||
| 32 | ["~>"] = "~>", | ||
| 33 | -- plus some convenience translations | ||
| 34 | [""] = "==", | ||
| 35 | ["="] = "==", | ||
| 36 | ["!="] = "~=" | ||
| 37 | } | ||
| 38 | |||
| 39 | local deltas = { | ||
| 40 | scm = 1000, | ||
| 41 | cvs = 1000, | ||
| 42 | rc = -1000, | ||
| 43 | pre = -10000, | ||
| 44 | beta = -100000, | ||
| 45 | alpha = -1000000 | ||
| 46 | } | ||
| 47 | |||
| 48 | local version_mt = { | ||
| 49 | --- Equality comparison for versions. | ||
| 50 | -- All version numbers must be equal. | ||
| 51 | -- If both versions have revision numbers, they must be equal; | ||
| 52 | -- otherwise the revision number is ignored. | ||
| 53 | -- @param v1 table: version table to compare. | ||
| 54 | -- @param v2 table: version table to compare. | ||
| 55 | -- @return boolean: true if they are considered equivalent. | ||
| 56 | __eq = function(v1, v2) | ||
| 57 | if #v1 ~= #v2 then | ||
| 58 | return false | ||
| 59 | end | ||
| 60 | for i = 1, #v1 do | ||
| 61 | if v1[i] ~= v2[i] then | ||
| 62 | return false | ||
| 63 | end | ||
| 64 | end | ||
| 65 | if v1.revision and v2.revision then | ||
| 66 | return (v1.revision == v2.revision) | ||
| 67 | end | ||
| 68 | return true | ||
| 69 | end, | ||
| 70 | --- Size comparison for versions. | ||
| 71 | -- All version numbers are compared. | ||
| 72 | -- If both versions have revision numbers, they are compared; | ||
| 73 | -- otherwise the revision number is ignored. | ||
| 74 | -- @param v1 table: version table to compare. | ||
| 75 | -- @param v2 table: version table to compare. | ||
| 76 | -- @return boolean: true if v1 is considered lower than v2. | ||
| 77 | __lt = function(v1, v2) | ||
| 78 | for i = 1, math.max(#v1, #v2) do | ||
| 79 | local v1i, v2i = v1[i] or 0, v2[i] or 0 | ||
| 80 | if v1i ~= v2i then | ||
| 81 | return (v1i < v2i) | ||
| 82 | end | ||
| 83 | end | ||
| 84 | if v1.revision and v2.revision then | ||
| 85 | return (v1.revision < v2.revision) | ||
| 86 | end | ||
| 87 | return false | ||
| 88 | end | ||
| 89 | } | ||
| 90 | |||
| 91 | local version_cache = {} | ||
| 92 | setmetatable(version_cache, { | ||
| 93 | __mode = "kv" | ||
| 94 | }) | ||
| 95 | |||
| 96 | --- Parse a version string, converting to table format. | ||
| 97 | -- A version table contains all components of the version string | ||
| 98 | -- converted to numeric format, stored in the array part of the table. | ||
| 99 | -- If the version contains a revision, it is stored numerically | ||
| 100 | -- in the 'revision' field. The original string representation of | ||
| 101 | -- the string is preserved in the 'string' field. | ||
| 102 | -- Returned version tables use a metatable | ||
| 103 | -- allowing later comparison through relational operators. | ||
| 104 | -- @param vstring string: A version number in string format. | ||
| 105 | -- @return table or nil: A version table or nil | ||
| 106 | -- if the input string contains invalid characters. | ||
| 107 | function parse_version(vstring) | ||
| 108 | if not vstring then return nil end | ||
| 109 | assert(type(vstring) == "string") | ||
| 110 | |||
| 111 | local cached = version_cache[vstring] | ||
| 112 | if cached then | ||
| 113 | return cached | ||
| 114 | end | ||
| 115 | |||
| 116 | local version = {} | ||
| 117 | local i = 1 | ||
| 118 | |||
| 119 | local function add_token(number) | ||
| 120 | version[i] = version[i] and version[i] + number/100000 or number | ||
| 121 | i = i + 1 | ||
| 122 | end | ||
| 123 | |||
| 124 | -- trim leading and trailing spaces | ||
| 125 | vstring = vstring:match("^%s*(.*)%s*$") | ||
| 126 | version.string = vstring | ||
| 127 | -- store revision separately if any | ||
| 128 | local main, revision = vstring:match("(.*)%-(%d+)$") | ||
| 129 | if revision then | ||
| 130 | vstring = main | ||
| 131 | version.revision = tonumber(revision) | ||
| 132 | end | ||
| 133 | while #vstring > 0 do | ||
| 134 | -- extract a number | ||
| 135 | local token, rest = vstring:match("^(%d+)[%.%-%_]*(.*)") | ||
| 136 | if token then | ||
| 137 | add_token(tonumber(token)) | ||
| 138 | else | ||
| 139 | -- extract a word | ||
| 140 | token, rest = vstring:match("^(%a+)[%.%-%_]*(.*)") | ||
| 141 | if not token then | ||
| 142 | return nil | ||
| 143 | end | ||
| 144 | local last = #version | ||
| 145 | version[i] = deltas[token] or (token:byte() / 1000) | ||
| 146 | end | ||
| 147 | vstring = rest | ||
| 148 | end | ||
| 149 | setmetatable(version, version_mt) | ||
| 150 | version_cache[vstring] = version | ||
| 151 | return version | ||
| 152 | end | ||
| 153 | |||
| 154 | --- Utility function to compare version numbers given as strings. | ||
| 155 | -- @param a string: one version. | ||
| 156 | -- @param b string: another version. | ||
| 157 | -- @return boolean: True if a > b. | ||
| 158 | function compare_versions(a, b) | ||
| 159 | return parse_version(a) > parse_version(b) | ||
| 160 | end | ||
| 161 | |||
| 162 | --- Consumes a constraint from a string, converting it to table format. | ||
| 163 | -- For example, a string ">= 1.0, > 2.0" is converted to a table in the | ||
| 164 | -- format {op = ">=", version={1,0}} and the rest, "> 2.0", is returned | ||
| 165 | -- back to the caller. | ||
| 166 | -- @param input string: A list of constraints in string format. | ||
| 167 | -- @return (table, string) or nil: A table representing the same | ||
| 168 | -- constraints and the string with the unused input, or nil if the | ||
| 169 | -- input string is invalid. | ||
| 170 | local function parse_constraint(input) | ||
| 171 | assert(type(input) == "string") | ||
| 172 | |||
| 173 | local no_upgrade, op, version, rest = input:match("^(@?)([<>=~!]*)%s*([%w%.%_%-]+)[%s,]*(.*)") | ||
| 174 | op = operators[op] | ||
| 175 | version = parse_version(version) | ||
| 176 | if not op or not version then return nil end | ||
| 177 | return { op = op, version = version, no_upgrade = no_upgrade=="@" and true or nil }, rest | ||
| 178 | end | ||
| 179 | |||
| 180 | --- Convert a list of constraints from string to table format. | ||
| 181 | -- For example, a string ">= 1.0, < 2.0" is converted to a table in the format | ||
| 182 | -- {{op = ">=", version={1,0}}, {op = "<", version={2,0}}}. | ||
| 183 | -- Version tables use a metatable allowing later comparison through | ||
| 184 | -- relational operators. | ||
| 185 | -- @param input string: A list of constraints in string format. | ||
| 186 | -- @return table or nil: A table representing the same constraints, | ||
| 187 | -- or nil if the input string is invalid. | ||
| 188 | function parse_constraints(input) | ||
| 189 | assert(type(input) == "string") | ||
| 190 | |||
| 191 | local constraints, constraint = {}, nil | ||
| 192 | while #input > 0 do | ||
| 193 | constraint, input = parse_constraint(input) | ||
| 194 | if constraint then | ||
| 195 | table.insert(constraints, constraint) | ||
| 196 | else | ||
| 197 | return nil | ||
| 198 | end | ||
| 199 | end | ||
| 200 | return constraints | ||
| 201 | end | ||
| 202 | |||
| 203 | --- Convert a dependency from string to table format. | ||
| 204 | -- For example, a string "foo >= 1.0, < 2.0" | ||
| 205 | -- is converted to a table in the format | ||
| 206 | -- {name = "foo", constraints = {{op = ">=", version={1,0}}, | ||
| 207 | -- {op = "<", version={2,0}}}}. Version tables use a metatable | ||
| 208 | -- allowing later comparison through relational operators. | ||
| 209 | -- @param dep string: A dependency in string format | ||
| 210 | -- as entered in rockspec files. | ||
| 211 | -- @return table or nil: A table representing the same dependency relation, | ||
| 212 | -- or nil if the input string is invalid. | ||
| 213 | function parse_dep(dep) | ||
| 214 | assert(type(dep) == "string") | ||
| 215 | |||
| 216 | local name, rest = dep:match("^%s*(%a[%w%-]*%w)%s*(.*)") | ||
| 217 | if not name then return nil end | ||
| 218 | local constraints = parse_constraints(rest) | ||
| 219 | if not constraints then return nil end | ||
| 220 | return { name = name, constraints = constraints } | ||
| 221 | end | ||
| 222 | |||
| 223 | --- Convert a version table to a string. | ||
| 224 | -- @param v table: The version table | ||
| 225 | -- @param internal boolean or nil: Whether to display versions in their | ||
| 226 | -- internal representation format or how they were specified. | ||
| 227 | -- @return string: The dependency information pretty-printed as a string. | ||
| 228 | function show_version(v, internal) | ||
| 229 | assert(type(v) == "table") | ||
| 230 | assert(type(internal) == "boolean" or not internal) | ||
| 231 | |||
| 232 | return (internal | ||
| 233 | and table.concat(v, ":")..(v.revision and tostring(v.revision) or "") | ||
| 234 | or v.string) | ||
| 235 | end | ||
| 236 | |||
| 237 | --- Convert a dependency in table format to a string. | ||
| 238 | -- @param dep table: The dependency in table format | ||
| 239 | -- @param internal boolean or nil: Whether to display versions in their | ||
| 240 | -- internal representation format or how they were specified. | ||
| 241 | -- @return string: The dependency information pretty-printed as a string. | ||
| 242 | function show_dep(dep, internal) | ||
| 243 | assert(type(dep) == "table") | ||
| 244 | assert(type(internal) == "boolean" or not internal) | ||
| 245 | |||
| 246 | local pretty = {} | ||
| 247 | for _, c in ipairs(dep.constraints) do | ||
| 248 | table.insert(pretty, c.op .. " " .. show_version(c.version, internal)) | ||
| 249 | end | ||
| 250 | return dep.name.." "..table.concat(pretty, ", ") | ||
| 251 | end | ||
| 252 | |||
| 253 | --- A more lenient check for equivalence between versions. | ||
| 254 | -- This returns true if the requested components of a version | ||
| 255 | -- match and ignore the ones that were not given. For example, | ||
| 256 | -- when requesting "2", then "2", "2.1", "2.3.5-9"... all match. | ||
| 257 | -- When requesting "2.1", then "2.1", "2.1.3" match, but "2.2" | ||
| 258 | -- doesn't. | ||
| 259 | -- @param version string or table: Version to be tested; may be | ||
| 260 | -- in string format or already parsed into a table. | ||
| 261 | -- @param requested string or table: Version requested; may be | ||
| 262 | -- in string format or already parsed into a table. | ||
| 263 | -- @return boolean: True if the tested version matches the requested | ||
| 264 | -- version, false otherwise. | ||
| 265 | local function partial_match(version, requested) | ||
| 266 | assert(type(version) == "string" or type(version) == "table") | ||
| 267 | assert(type(requested) == "string" or type(version) == "table") | ||
| 268 | |||
| 269 | if type(version) ~= "table" then version = parse_version(version) end | ||
| 270 | if type(requested) ~= "table" then requested = parse_version(requested) end | ||
| 271 | if not version or not requested then return false end | ||
| 272 | |||
| 273 | for i, ri in ipairs(requested) do | ||
| 274 | local vi = version[i] or 0 | ||
| 275 | if ri ~= vi then return false end | ||
| 276 | end | ||
| 277 | if requested.revision then | ||
| 278 | return requested.revision == version.revision | ||
| 279 | end | ||
| 280 | return true | ||
| 281 | end | ||
| 282 | |||
| 283 | --- Check if a version satisfies a set of constraints. | ||
| 284 | -- @param version table: A version in table format | ||
| 285 | -- @param constraints table: An array of constraints in table format. | ||
| 286 | -- @return boolean: True if version satisfies all constraints, | ||
| 287 | -- false otherwise. | ||
| 288 | function match_constraints(version, constraints) | ||
| 289 | assert(type(version) == "table") | ||
| 290 | assert(type(constraints) == "table") | ||
| 291 | local ok = true | ||
| 292 | setmetatable(version, version_mt) | ||
| 293 | for _, constr in pairs(constraints) do | ||
| 294 | local constr_version = constr.version | ||
| 295 | setmetatable(constr.version, version_mt) | ||
| 296 | if constr.op == "==" then ok = version == constr_version | ||
| 297 | elseif constr.op == "~=" then ok = version ~= constr_version | ||
| 298 | elseif constr.op == ">" then ok = version > constr_version | ||
| 299 | elseif constr.op == "<" then ok = version < constr_version | ||
| 300 | elseif constr.op == ">=" then ok = version >= constr_version | ||
| 301 | elseif constr.op == "<=" then ok = version <= constr_version | ||
| 302 | elseif constr.op == "~>" then ok = partial_match(version, constr_version) | ||
| 303 | end | ||
| 304 | if not ok then break end | ||
| 305 | end | ||
| 306 | return ok | ||
| 307 | end | ||
| 308 | |||
| 309 | --- Attempt to match a dependency to an installed rock. | ||
| 310 | -- @param dep table: A dependency parsed in table format. | ||
| 311 | -- @param blacklist table: Versions that can't be accepted. Table where keys | ||
| 312 | -- are program versions and values are 'true'. | ||
| 313 | -- @return table or nil: A table containing fields 'name' and 'version' | ||
| 314 | -- representing an installed rock which matches the given dependency, | ||
| 315 | -- or nil if it could not be matched. | ||
| 316 | local function match_dep(dep, blacklist) | ||
| 317 | assert(type(dep) == "table") | ||
| 318 | |||
| 319 | local versions | ||
| 320 | if dep.name == "lua" then | ||
| 321 | versions = { "5.1" } | ||
| 322 | else | ||
| 323 | versions = manif.get_versions(dep.name) | ||
| 324 | end | ||
| 325 | if not versions then | ||
| 326 | return nil | ||
| 327 | end | ||
| 328 | if blacklist then | ||
| 329 | local i = 1 | ||
| 330 | while versions[i] do | ||
| 331 | if blacklist[versions[i]] then | ||
| 332 | table.remove(versions, i) | ||
| 333 | else | ||
| 334 | i = i + 1 | ||
| 335 | end | ||
| 336 | end | ||
| 337 | end | ||
| 338 | local candidates = {} | ||
| 339 | for _, vstring in ipairs(versions) do | ||
| 340 | local version = parse_version(vstring) | ||
| 341 | if match_constraints(version, dep.constraints) then | ||
| 342 | table.insert(candidates, version) | ||
| 343 | end | ||
| 344 | end | ||
| 345 | if #candidates == 0 then | ||
| 346 | return nil | ||
| 347 | else | ||
| 348 | table.sort(candidates) | ||
| 349 | return { | ||
| 350 | name = dep.name, | ||
| 351 | version = candidates[#candidates].string | ||
| 352 | } | ||
| 353 | end | ||
| 354 | end | ||
| 355 | |||
| 356 | --- Attempt to match dependencies of a rockspec to installed rocks. | ||
| 357 | -- @param rockspec table: The rockspec loaded as a table. | ||
| 358 | -- @param blacklist table or nil: Program versions to not use as valid matches. | ||
| 359 | -- Table where keys are program names and values are tables where keys | ||
| 360 | -- are program versions and values are 'true'. | ||
| 361 | -- @return table, table: A table where keys are dependencies parsed | ||
| 362 | -- in table format and values are tables containing fields 'name' and | ||
| 363 | -- version' representing matches, and a table of missing dependencies | ||
| 364 | -- parsed as tables. | ||
| 365 | function match_deps(rockspec, blacklist) | ||
| 366 | assert(type(rockspec) == "table") | ||
| 367 | assert(type(blacklist) == "table" or not blacklist) | ||
| 368 | local matched, missing, no_upgrade = {}, {}, {} | ||
| 369 | |||
| 370 | for _, dep in ipairs(rockspec.dependencies) do | ||
| 371 | local found = match_dep(dep, blacklist and blacklist[dep.name] or nil) | ||
| 372 | if found then | ||
| 373 | if dep.name ~= "lua" then | ||
| 374 | matched[dep] = found | ||
| 375 | end | ||
| 376 | else | ||
| 377 | if dep.constraints[1] and dep.constraints[1].no_upgrade then | ||
| 378 | no_upgrade[dep.name] = dep | ||
| 379 | else | ||
| 380 | missing[dep.name] = dep | ||
| 381 | end | ||
| 382 | end | ||
| 383 | end | ||
| 384 | return matched, missing, no_upgrade | ||
| 385 | end | ||
| 386 | |||
| 387 | --- Return a set of values of a table. | ||
| 388 | -- @param tbl table: The input table. | ||
| 389 | -- @return table: The array of keys. | ||
| 390 | local function values_set(tbl) | ||
| 391 | local set = {} | ||
| 392 | for _, v in pairs(tbl) do | ||
| 393 | set[v] = true | ||
| 394 | end | ||
| 395 | return set | ||
| 396 | end | ||
| 397 | |||
| 398 | --- Check dependencies of a rock and attempt to install any missing ones. | ||
| 399 | -- Packages are installed using the LuaRocks "install" command. | ||
| 400 | -- Aborts the program if a dependency could not be fulfilled. | ||
| 401 | -- @param rockspec table: A rockspec in table format. | ||
| 402 | -- @return boolean or (nil, string): True if no errors occurred, or | ||
| 403 | -- nil and an error message if any test failed. | ||
| 404 | function fulfill_dependencies(rockspec) | ||
| 405 | |||
| 406 | if rockspec.supported_platforms then | ||
| 407 | if not platforms_set then | ||
| 408 | platforms_set = values_set(cfg.platforms) | ||
| 409 | end | ||
| 410 | local supported = nil | ||
| 411 | for _, plat in pairs(rockspec.supported_platforms) do | ||
| 412 | local neg, plat = plat:match("^(!?)(.*)") | ||
| 413 | if neg == "!" then | ||
| 414 | if platforms_set[plat] then | ||
| 415 | return nil, "This rockspec for "..rockspec.package.." does not support "..plat.." platforms." | ||
| 416 | end | ||
| 417 | else | ||
| 418 | if platforms_set[plat] then | ||
| 419 | supported = true | ||
| 420 | else | ||
| 421 | if supported == nil then | ||
| 422 | supported = false | ||
| 423 | end | ||
| 424 | end | ||
| 425 | end | ||
| 426 | end | ||
| 427 | if supported == false then | ||
| 428 | local plats = table.concat(cfg.platforms, ", ") | ||
| 429 | return nil, "This rockspec for "..rockspec.package.." does not support "..plats.." platforms." | ||
| 430 | end | ||
| 431 | end | ||
| 432 | |||
| 433 | local matched, missing, no_upgrade = match_deps(rockspec) | ||
| 434 | |||
| 435 | if next(no_upgrade) then | ||
| 436 | print("Missing dependencies for "..rockspec.name.." "..rockspec.version..":") | ||
| 437 | for _, dep in pairs(no_upgrade) do | ||
| 438 | print(show_dep(dep)) | ||
| 439 | end | ||
| 440 | if next(missing) then | ||
| 441 | for _, dep in pairs(missing) do | ||
| 442 | print(show_dep(dep)) | ||
| 443 | end | ||
| 444 | end | ||
| 445 | print() | ||
| 446 | for _, dep in pairs(no_upgrade) do | ||
| 447 | print("This version of "..rockspec.name.." is designed for use with") | ||
| 448 | print(show_dep(dep)..", but is configured to avoid upgrading it") | ||
| 449 | print("automatically. Please upgrade "..dep.name.." with") | ||
| 450 | print(" luarocks install "..dep.name) | ||
| 451 | print("or choose an older version of "..rockspec.name.." with") | ||
| 452 | print(" luarocks search "..rockspec.name) | ||
| 453 | end | ||
| 454 | return nil, "Failed matching dependencies." | ||
| 455 | end | ||
| 456 | |||
| 457 | if next(missing) then | ||
| 458 | print() | ||
| 459 | print("Missing dependencies for "..rockspec.name..":") | ||
| 460 | for _, dep in pairs(missing) do | ||
| 461 | print(show_dep(dep)) | ||
| 462 | end | ||
| 463 | print() | ||
| 464 | |||
| 465 | for _, dep in pairs(missing) do | ||
| 466 | -- Double-check in case dependency was filled during recursion. | ||
| 467 | if not match_dep(dep) then | ||
| 468 | local rock = search.find_suitable_rock(dep) | ||
| 469 | if not rock then | ||
| 470 | return nil, "Could not find a rock to satisfy dependency: "..show_dep(dep) | ||
| 471 | end | ||
| 472 | local ok, err = install.run(rock) | ||
| 473 | if not ok then | ||
| 474 | return nil, "Failed installing dependency: "..rock.." - "..err | ||
| 475 | end | ||
| 476 | end | ||
| 477 | end | ||
| 478 | end | ||
| 479 | return true | ||
| 480 | end | ||
| 481 | |||
| 482 | --- Set up path-related variables for external dependencies. | ||
| 483 | -- For each key in the external_dependencies table in the | ||
| 484 | -- rockspec file, four variables are created: <key>_DIR, <key>_BINDIR, | ||
| 485 | -- <key>_INCDIR and <key>_LIBDIR. These are not overwritten | ||
| 486 | -- if already set (e.g. by the LuaRocks config file or through the | ||
| 487 | -- command-line). Values in the external_dependencies table | ||
| 488 | -- are tables that may contain a "header" or a "library" field, | ||
| 489 | -- with filenames to be tested for existence. | ||
| 490 | -- @param rockspec table: The rockspec table. | ||
| 491 | -- @param mode string: if "build" is given, checks all files; | ||
| 492 | -- if "install" is given, do not scan for headers. | ||
| 493 | -- @return boolean or (nil, string): True if no errors occurred, or | ||
| 494 | -- nil and an error message if any test failed. | ||
| 495 | function check_external_deps(rockspec, mode) | ||
| 496 | assert(type(rockspec) == "table") | ||
| 497 | |||
| 498 | local vars = rockspec.variables | ||
| 499 | local patterns = cfg.external_deps_patterns | ||
| 500 | local subdirs = cfg.external_deps_subdirs | ||
| 501 | if mode == "install" then | ||
| 502 | patterns = cfg.runtime_external_deps_patterns | ||
| 503 | subdirs = cfg.runtime_external_deps_subdirs | ||
| 504 | end | ||
| 505 | local dirs = { | ||
| 506 | BINDIR = { subdir = subdirs.bin, testfile = "program", pattern = patterns.bin }, | ||
| 507 | INCDIR = { subdir = subdirs.include, testfile = "header", pattern = patterns.include }, | ||
| 508 | LIBDIR = { subdir = subdirs.lib, testfile = "library", pattern = patterns.lib } | ||
| 509 | } | ||
| 510 | if mode == "install" then | ||
| 511 | dirs.INCDIR = nil | ||
| 512 | end | ||
| 513 | if rockspec.external_dependencies then | ||
| 514 | for name, files in pairs(rockspec.external_dependencies) do | ||
| 515 | local ok = true | ||
| 516 | local failed_file = nil | ||
| 517 | for _, extdir in ipairs(cfg.external_deps_dirs) do | ||
| 518 | ok = true | ||
| 519 | local prefix = vars[name.."_DIR"] | ||
| 520 | if not prefix then | ||
| 521 | prefix = extdir | ||
| 522 | end | ||
| 523 | for dirname, dirdata in pairs(dirs) do | ||
| 524 | dirdata.dir = vars[name.."_"..dirname] or fs.make_path(prefix, dirdata.subdir) | ||
| 525 | local file = files[dirdata.testfile] | ||
| 526 | if file then | ||
| 527 | local files = {} | ||
| 528 | if not file:match("%.") then | ||
| 529 | for _, pattern in ipairs(dirdata.pattern) do | ||
| 530 | table.insert(files, pattern:gsub("?", file)) | ||
| 531 | end | ||
| 532 | else | ||
| 533 | table.insert(files, file) | ||
| 534 | end | ||
| 535 | local found = false | ||
| 536 | failed_file = nil | ||
| 537 | for _, f in pairs(files) do | ||
| 538 | if f:match("%.so$") or f:match("%.dylib$") or f:match("%.dll$") then | ||
| 539 | f = f:gsub("%.[^.]+$", "."..cfg.external_lib_extension) | ||
| 540 | end | ||
| 541 | local testfile = fs.make_path(dirdata.dir, f) | ||
| 542 | if fs.exists(testfile) then | ||
| 543 | found = true | ||
| 544 | break | ||
| 545 | else | ||
| 546 | if failed_file then | ||
| 547 | failed_file = failed_file .. ", or " .. f | ||
| 548 | else | ||
| 549 | failed_file = f | ||
| 550 | end | ||
| 551 | end | ||
| 552 | end | ||
| 553 | if not found then | ||
| 554 | ok = false | ||
| 555 | break | ||
| 556 | end | ||
| 557 | end | ||
| 558 | end | ||
| 559 | if ok then | ||
| 560 | for dirname, dirdata in pairs(dirs) do | ||
| 561 | vars[name.."_"..dirname] = dirdata.dir | ||
| 562 | end | ||
| 563 | vars[name.."_DIR"] = prefix | ||
| 564 | break | ||
| 565 | end | ||
| 566 | end | ||
| 567 | if not ok then | ||
| 568 | return nil, "Could not find expected file "..failed_file.." for "..name.." -- you may have to install "..name.." in your system and/or set the "..name.."_DIR variable" | ||
| 569 | end | ||
| 570 | end | ||
| 571 | end | ||
| 572 | return true | ||
| 573 | end | ||
| 574 | |||
| 575 | --- Recursively scan dependencies, to build a transitive closure of all | ||
| 576 | -- dependent packages. | ||
| 577 | -- @param results table: The results table being built. | ||
| 578 | -- @param name string: Package name. | ||
| 579 | -- @param version string: Package version. | ||
| 580 | -- @return (table, table): The results and a table of missing dependencies. | ||
| 581 | function scan_deps(results, missing, manifest, name, version) | ||
| 582 | assert(type(results) == "table") | ||
| 583 | assert(type(missing) == "table") | ||
| 584 | assert(type(name) == "string") | ||
| 585 | assert(type(version) == "string") | ||
| 586 | |||
| 587 | local err | ||
| 588 | if results[name] then | ||
| 589 | return results, missing | ||
| 590 | end | ||
| 591 | if not manifest.dependencies then manifest.dependencies = {} end | ||
| 592 | local dependencies = manifest.dependencies | ||
| 593 | if not dependencies[name] then dependencies[name] = {} end | ||
| 594 | local dependencies_name = dependencies[name] | ||
| 595 | local deplist = dependencies_name[version] | ||
| 596 | local rockspec, err | ||
| 597 | if not deplist then | ||
| 598 | rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version)) | ||
| 599 | if err then | ||
| 600 | missing[name.." "..version] = true | ||
| 601 | return results, missing | ||
| 602 | end | ||
| 603 | dependencies_name[version] = rockspec.dependencies | ||
| 604 | else | ||
| 605 | rockspec = { dependencies = deplist } | ||
| 606 | end | ||
| 607 | local matched, failures = match_deps(rockspec) | ||
| 608 | for _, match in pairs(matched) do | ||
| 609 | results, missing = scan_deps(results, missing, manifest, match.name, match.version) | ||
| 610 | end | ||
| 611 | if next(failures) then | ||
| 612 | for _, failure in pairs(failures) do | ||
| 613 | missing[show_dep(failure)] = true | ||
| 614 | end | ||
| 615 | end | ||
| 616 | results[name] = version | ||
| 617 | return results, missing | ||
| 618 | end | ||
diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua new file mode 100644 index 00000000..b61fab28 --- /dev/null +++ b/src/luarocks/fetch.lua | |||
| @@ -0,0 +1,284 @@ | |||
| 1 | |||
| 2 | --- Functions related to fetching and loading local and remote files. | ||
| 3 | module("luarocks.fetch", package.seeall) | ||
| 4 | |||
| 5 | local fs = require("luarocks.fs") | ||
| 6 | local type_check = require("luarocks.type_check") | ||
| 7 | local path = require("luarocks.path") | ||
| 8 | local deps = require("luarocks.deps") | ||
| 9 | local persist = require("luarocks.persist") | ||
| 10 | local util = require("luarocks.util") | ||
| 11 | |||
| 12 | --- Fetch a local or remote file. | ||
| 13 | -- Make a remote or local URL/pathname local, fetching the file if necessary. | ||
| 14 | -- Other "fetch" and "load" functions use this function to obtain files. | ||
| 15 | -- If a local pathname is given, it is returned as a result. | ||
| 16 | -- @param url string: a local pathname or a remote URL. | ||
| 17 | -- @param filename string or nil: this function attempts to detect the | ||
| 18 | -- resulting local filename of the remote file as the basename of the URL; | ||
| 19 | -- if that is not correct (due to a redirection, for example), the local | ||
| 20 | -- filename can be given explicitly as this second argument. | ||
| 21 | -- @return string or (nil, string): the absolute local pathname for the | ||
| 22 | -- fetched file, or nil and a message in case of errors. | ||
| 23 | function fetch_url(url, filename) | ||
| 24 | assert(type(url) == "string") | ||
| 25 | assert(type(filename) == "string" or not filename) | ||
| 26 | |||
| 27 | local protocol, pathname = fs.split_url(url) | ||
| 28 | if protocol == "file" then | ||
| 29 | return fs.absolute_name(pathname) | ||
| 30 | elseif protocol == "http" or protocol == "ftp" or protocol == "https" then | ||
| 31 | local ok = fs.download(url) | ||
| 32 | if not ok then | ||
| 33 | return nil, "Failed downloading "..url | ||
| 34 | end | ||
| 35 | return fs.make_path(fs.current_dir(), filename or fs.base_name(url)) | ||
| 36 | else | ||
| 37 | return nil, "Unsupported protocol "..protocol | ||
| 38 | end | ||
| 39 | end | ||
| 40 | |||
| 41 | --- For remote URLs, create a temporary directory and download URL inside it. | ||
| 42 | -- This temporary directory will be deleted on program termination. | ||
| 43 | -- For local URLs, just return the local pathname and its directory. | ||
| 44 | -- @param url string: URL to be downloaded | ||
| 45 | -- @param tmpname string: name pattern to use for avoiding conflicts | ||
| 46 | -- when creating temporary directory. | ||
| 47 | -- @param filename string or nil: local filename of URL to be downloaded, | ||
| 48 | -- in case it can't be inferred from the URL. | ||
| 49 | -- @return (string, string) or (nil, string): absolute local pathname of | ||
| 50 | -- the fetched file and temporary directory name; or nil and an error message. | ||
| 51 | function fetch_url_at_temp_dir(url, tmpname, filename) | ||
| 52 | assert(type(url) == "string") | ||
| 53 | assert(type(tmpname) == "string") | ||
| 54 | assert(type(filename) == "string" or not filename) | ||
| 55 | filename = filename or fs.base_name(url) | ||
| 56 | |||
| 57 | local protocol, pathname = fs.split_url(url) | ||
| 58 | if protocol == "file" then | ||
| 59 | return pathname, fs.dir_name(pathname) | ||
| 60 | else | ||
| 61 | local dir = fs.make_temp_dir(tmpname) | ||
| 62 | if not dir then | ||
| 63 | return nil, "Failed creating temporary directory." | ||
| 64 | end | ||
| 65 | util.schedule_function(fs.delete, dir) | ||
| 66 | fs.change_dir(dir) | ||
| 67 | local file, err = fetch_url(url, filename) | ||
| 68 | if not file then | ||
| 69 | return nil, "Error fetching file: "..err | ||
| 70 | end | ||
| 71 | fs.pop_dir() | ||
| 72 | return file, dir | ||
| 73 | end | ||
| 74 | end | ||
| 75 | |||
| 76 | --- Obtain a rock and unpack it. | ||
| 77 | -- If a directory is not given, a temporary directory will be created, | ||
| 78 | -- which will be deleted on program termination. | ||
| 79 | -- @param rock_file string: URL or filename of the rock. | ||
| 80 | -- @param dest string or nil: if given, directory will be used as | ||
| 81 | -- a permanent destination. | ||
| 82 | -- @return string or (nil, string): the directory containing the contents | ||
| 83 | -- of the unpacked rock. | ||
| 84 | function fetch_and_unpack_rock(rock_file, dest) | ||
| 85 | assert(type(rock_file) == "string") | ||
| 86 | assert(type(dest) == "string" or not dest) | ||
| 87 | |||
| 88 | local name = fs.base_name(rock_file):match("(.*)%.[^.]*%.rock") | ||
| 89 | |||
| 90 | local rock_file, err = fetch_url_at_temp_dir(rock_file,"luarocks-rock-"..name) | ||
| 91 | if not rock_file then | ||
| 92 | return nil, "Could not fetch rock file: " .. err | ||
| 93 | end | ||
| 94 | |||
| 95 | rock_file = fs.absolute_name(rock_file) | ||
| 96 | local dir | ||
| 97 | if dest then | ||
| 98 | dir = dest | ||
| 99 | fs.make_dir(dir) | ||
| 100 | else | ||
| 101 | dir = fs.make_temp_dir(name) | ||
| 102 | end | ||
| 103 | if not dest then | ||
| 104 | util.schedule_function(fs.delete, dir) | ||
| 105 | end | ||
| 106 | fs.change_dir(dir) | ||
| 107 | local ok = fs.unzip(rock_file) | ||
| 108 | if not ok then | ||
| 109 | return nil, "Failed unpacking rock file: " .. rock_file | ||
| 110 | end | ||
| 111 | fs.pop_dir() | ||
| 112 | return dir | ||
| 113 | end | ||
| 114 | |||
| 115 | --- Back-end function that actually loads the local rockspec. | ||
| 116 | -- Performs some validation and postprocessing of the rockspec contents. | ||
| 117 | -- @param file string: The local filename of the rockspec file. | ||
| 118 | -- @return table or (nil, string): A table representing the rockspec | ||
| 119 | -- or nil followed by an error message. | ||
| 120 | function load_local_rockspec(filename) | ||
| 121 | assert(type(filename) == "string") | ||
| 122 | |||
| 123 | local rockspec, err = persist.load_into_table(filename) | ||
| 124 | if not rockspec then | ||
| 125 | return nil, "Could not load rockspec file "..filename.." ("..err..")" | ||
| 126 | end | ||
| 127 | |||
| 128 | local ok, err = type_check.type_check_rockspec(rockspec) | ||
| 129 | if not ok then | ||
| 130 | return nil, filename..": "..err | ||
| 131 | end | ||
| 132 | |||
| 133 | if rockspec.rockspec_format then | ||
| 134 | if deps.compare_versions(rockspec.rockspec_format, type_check.rockspec_format) then | ||
| 135 | return nil, "Rockspec format "..rockspec.rockspec_format.." is not supported, please upgrade LuaRocks." | ||
| 136 | end | ||
| 137 | end | ||
| 138 | |||
| 139 | util.platform_overrides(rockspec.build) | ||
| 140 | util.platform_overrides(rockspec.dependencies) | ||
| 141 | util.platform_overrides(rockspec.external_dependencies) | ||
| 142 | util.platform_overrides(rockspec.source) | ||
| 143 | util.platform_overrides(rockspec.hooks) | ||
| 144 | |||
| 145 | local basename = fs.base_name(filename) | ||
| 146 | rockspec.name = basename:match("(.*)-[^-]*-[0-9]*") | ||
| 147 | if not rockspec.name then | ||
| 148 | return nil, "Expected filename in format 'name-version-revision.rockspec'." | ||
| 149 | end | ||
| 150 | |||
| 151 | local protocol, pathname = fs.split_url(rockspec.source.url) | ||
| 152 | if protocol == "http" or protocol == "https" or protocol == "ftp" or protocol == "file" then | ||
| 153 | rockspec.source.file = rockspec.source.file or fs.base_name(rockspec.source.url) | ||
| 154 | end | ||
| 155 | rockspec.source.protocol, rockspec.source.pathname = protocol, pathname | ||
| 156 | |||
| 157 | -- Temporary compatibility | ||
| 158 | if not rockspec.source.module then | ||
| 159 | rockspec.source.module = rockspec.source.cvs_module | ||
| 160 | rockspec.source.tag = rockspec.source.cvs_tag | ||
| 161 | end | ||
| 162 | |||
| 163 | local name_version = rockspec.package:lower() .. "-" .. rockspec.version | ||
| 164 | if basename ~= name_version .. ".rockspec" then | ||
| 165 | return nil, "Inconsistency between rockspec filename ("..basename..") and its contents ("..name_version..".rockspec)." | ||
| 166 | end | ||
| 167 | |||
| 168 | rockspec.local_filename = filename | ||
| 169 | local filebase = rockspec.source.file or rockspec.source.url | ||
| 170 | local base = fs.base_name(filebase) | ||
| 171 | base = base:gsub("%.[^.]*$", ""):gsub("%.tar$", "") | ||
| 172 | rockspec.source.dir = rockspec.source.dir | ||
| 173 | or rockspec.source.module | ||
| 174 | or ((filebase:match(".lua$") or filebase:match(".c$")) and ".") | ||
| 175 | or base | ||
| 176 | if rockspec.dependencies then | ||
| 177 | for i = 1, #rockspec.dependencies do | ||
| 178 | local parsed = deps.parse_dep(rockspec.dependencies[i]) | ||
| 179 | if not parsed then | ||
| 180 | return nil, "Parse error processing dependency '"..rockspec.dependencies[i].."'" | ||
| 181 | end | ||
| 182 | rockspec.dependencies[i] = parsed | ||
| 183 | end | ||
| 184 | else | ||
| 185 | rockspec.dependencies = {} | ||
| 186 | end | ||
| 187 | local ok, err = path.configure_paths(rockspec) | ||
| 188 | if err then | ||
| 189 | return nil, "Error verifying paths: "..err | ||
| 190 | end | ||
| 191 | |||
| 192 | return rockspec | ||
| 193 | end | ||
| 194 | |||
| 195 | --- Load a local or remote rockspec into a table. | ||
| 196 | -- This is the entry point for the LuaRocks tools. | ||
| 197 | -- Only the LuaRocks runtime loader should use | ||
| 198 | -- load_local_rockspec directly. | ||
| 199 | -- @param filename string: Local or remote filename of a rockspec. | ||
| 200 | -- @return table or (nil, string): A table representing the rockspec | ||
| 201 | -- or nil followed by an error message. | ||
| 202 | function load_rockspec(filename) | ||
| 203 | assert(type(filename) == "string") | ||
| 204 | |||
| 205 | local name = fs.base_name(filename):match("(.*)%.rockspec") | ||
| 206 | if not name then | ||
| 207 | return nil, "Filename '"..filename.."' does not look like a rockspec." | ||
| 208 | end | ||
| 209 | |||
| 210 | local filename, err = fetch_url_at_temp_dir(filename,"luarocks-rockspec-"..name) | ||
| 211 | if not filename then | ||
| 212 | return nil, err | ||
| 213 | end | ||
| 214 | |||
| 215 | return load_local_rockspec(filename) | ||
| 216 | end | ||
| 217 | |||
| 218 | --- Download sources for building a rock using the basic URL downloader. | ||
| 219 | -- @param rockspec table: The rockspec table | ||
| 220 | -- @param extract boolean: Whether to extract the sources from | ||
| 221 | -- the fetched source tarball or not. | ||
| 222 | -- @param dest_dir string or nil: If set, will extract to the given directory. | ||
| 223 | -- @return (string, string) or (nil, string): The absolute pathname of | ||
| 224 | -- the fetched source tarball and the temporary directory created to | ||
| 225 | -- store it; or nil and an error message. | ||
| 226 | function get_sources(rockspec, extract, dest_dir) | ||
| 227 | assert(type(rockspec) == "table") | ||
| 228 | assert(type(extract) == "boolean") | ||
| 229 | assert(type(dest_dir) == "string" or not dest_dir) | ||
| 230 | |||
| 231 | local url = rockspec.source.url | ||
| 232 | local name = rockspec.name.."-"..rockspec.version | ||
| 233 | local filename = rockspec.source.file | ||
| 234 | local source_file, dir, err | ||
| 235 | if dest_dir then | ||
| 236 | fs.change_dir(dest_dir) | ||
| 237 | source_file, err = fetch_url(url, filename) | ||
| 238 | fs.pop_dir() | ||
| 239 | dir = dest_dir | ||
| 240 | else | ||
| 241 | source_file, dir = fetch_url_at_temp_dir(url, "luarocks-source-"..name, filename) | ||
| 242 | end | ||
| 243 | if not source_file then | ||
| 244 | return nil, err or dir | ||
| 245 | end | ||
| 246 | if rockspec.source.md5 then | ||
| 247 | if not fs.check_md5(source_file, rockspec.source.md5) then | ||
| 248 | return nil, "MD5 check for "..filename.." has failed." | ||
| 249 | end | ||
| 250 | end | ||
| 251 | if extract then | ||
| 252 | fs.change_dir(dir) | ||
| 253 | fs.unpack_archive(rockspec.source.file) | ||
| 254 | fs.pop_dir() | ||
| 255 | end | ||
| 256 | return source_file, dir | ||
| 257 | end | ||
| 258 | |||
| 259 | --- Download sources for building a rock, calling the appropriate protocol method. | ||
| 260 | -- @param rockspec table: The rockspec table | ||
| 261 | -- @param extract boolean: When downloading compressed formats, whether to extract | ||
| 262 | -- the sources from the fetched archive or not. | ||
| 263 | -- @param dest_dir string or nil: If set, will extract to the given directory. | ||
| 264 | -- @return (string, string) or (nil, string): The absolute pathname of | ||
| 265 | -- the fetched source tarball and the temporary directory created to | ||
| 266 | -- store it; or nil and an error message. | ||
| 267 | function fetch_sources(rockspec, extract, dest_dir) | ||
| 268 | assert(type(rockspec) == "table") | ||
| 269 | assert(type(extract) == "boolean") | ||
| 270 | assert(type(dest_dir) == "string" or not dest_dir) | ||
| 271 | |||
| 272 | local protocol = rockspec.source.protocol | ||
| 273 | local proto | ||
| 274 | if protocol == "http" or protocol == "https" or protocol == "ftp" or protocol == "file" then | ||
| 275 | proto = require("luarocks.fetch") | ||
| 276 | else | ||
| 277 | ok, proto = pcall(require, "luarocks.fetch."..protocol) | ||
| 278 | if not ok then | ||
| 279 | return nil, "Unknown protocol "..protocol | ||
| 280 | end | ||
| 281 | end | ||
| 282 | |||
| 283 | return proto.get_sources(rockspec, extract, dest_dir) | ||
| 284 | end | ||
diff --git a/src/luarocks/fetch/cvs.lua b/src/luarocks/fetch/cvs.lua new file mode 100644 index 00000000..291388cb --- /dev/null +++ b/src/luarocks/fetch/cvs.lua | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | |||
| 2 | --- Fetch back-end for retrieving sources from CVS. | ||
| 3 | module("luarocks.fetch.cvs", package.seeall) | ||
| 4 | |||
| 5 | local fs = require("luarocks.fs") | ||
| 6 | local util = require("luarocks.util") | ||
| 7 | |||
| 8 | --- Download sources for building a rock, using CVS. | ||
| 9 | -- @param rockspec table: The rockspec table | ||
| 10 | -- @param extract boolean: Unused in this module (required for API purposes.) | ||
| 11 | -- @param dest_dir string or nil: If set, will extract to the given directory. | ||
| 12 | -- @return (string, string) or (nil, string): The absolute pathname of | ||
| 13 | -- the fetched source tarball and the temporary directory created to | ||
| 14 | -- store it; or nil and an error message. | ||
| 15 | function get_sources(rockspec, extract, dest_dir) | ||
| 16 | assert(type(rockspec) == "table") | ||
| 17 | assert(type(dest_dir) == "string" or not dest_dir) | ||
| 18 | |||
| 19 | local name_version = rockspec.name .. "-" .. rockspec.version | ||
| 20 | local module = rockspec.source.module or fs.base_name(rockspec.source.url) | ||
| 21 | local command = {"cvs", "-d"..rockspec.source.pathname, "export", module} | ||
| 22 | if rockspec.source.tag then | ||
| 23 | table.insert(command, 4, "-r") | ||
| 24 | table.insert(command, 5, rockspec.source.tag) | ||
| 25 | end | ||
| 26 | local dir | ||
| 27 | if not dest_dir then | ||
| 28 | dir = fs.make_temp_dir(name_version) | ||
| 29 | if not dir then | ||
| 30 | return nil, "Failed creating temporary directory." | ||
| 31 | end | ||
| 32 | util.schedule_function(fs.delete, dir) | ||
| 33 | else | ||
| 34 | dir = dest_dir | ||
| 35 | end | ||
| 36 | fs.change_dir(dir) | ||
| 37 | if not fs.execute(unpack(command)) then | ||
| 38 | return nil, "Failed fetching files from CVS." | ||
| 39 | end | ||
| 40 | fs.pop_dir() | ||
| 41 | return module, dir | ||
| 42 | end | ||
| 43 | |||
diff --git a/src/luarocks/fetch/git.lua b/src/luarocks/fetch/git.lua new file mode 100644 index 00000000..d0d204a9 --- /dev/null +++ b/src/luarocks/fetch/git.lua | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | |||
| 2 | --- Fetch back-end for retrieving sources from GIT. | ||
| 3 | module("luarocks.fetch.git", package.seeall) | ||
| 4 | |||
| 5 | local fs = require("luarocks.fs") | ||
| 6 | local util = require("luarocks.util") | ||
| 7 | |||
| 8 | --- Download sources for building a rock, using git. | ||
| 9 | -- @param rockspec table: The rockspec table | ||
| 10 | -- @param extract boolean: Unused in this module (required for API purposes.) | ||
| 11 | -- @param dest_dir string or nil: If set, will extract to the given directory. | ||
| 12 | -- @return (string, string) or (nil, string): The absolute pathname of | ||
| 13 | -- the fetched source tarball and the temporary directory created to | ||
| 14 | -- store it; or nil and an error message. | ||
| 15 | function get_sources(rockspec, extract, dest_dir) | ||
| 16 | assert(type(rockspec) == "table") | ||
| 17 | assert(type(dest_dir) == "string" or not dest_dir) | ||
| 18 | |||
| 19 | local name_version = rockspec.name .. "-" .. rockspec.version | ||
| 20 | local module = fs.base_name(rockspec.source.url) | ||
| 21 | -- Strip off .git from base name if present | ||
| 22 | module = module:gsub("%.git$", "") | ||
| 23 | local command = {"git", "clone", rockspec.source.url, module} | ||
| 24 | local checkout_command | ||
| 25 | local tag_or_branch = rockspec.source.tag or rockspec.source.branch | ||
| 26 | if tag_or_branch then | ||
| 27 | checkout_command = {"git", "checkout", tag_or_branch} | ||
| 28 | end | ||
| 29 | local dir | ||
| 30 | if not dest_dir then | ||
| 31 | dir = fs.make_temp_dir(name_version) | ||
| 32 | if not dir then | ||
| 33 | return nil, "Failed creating temporary directory." | ||
| 34 | end | ||
| 35 | util.schedule_function(fs.delete, dir) | ||
| 36 | else | ||
| 37 | dir = dest_dir | ||
| 38 | end | ||
| 39 | fs.change_dir(dir) | ||
| 40 | if not fs.execute(unpack(command)) then | ||
| 41 | return nil, "Failed fetching files from GIT while cloning." | ||
| 42 | end | ||
| 43 | if checkout_command then | ||
| 44 | fs.change_dir(module) | ||
| 45 | if not fs.execute(unpack(checkout_command)) then | ||
| 46 | return nil, "Failed fetching files from GIT while getting tag/branch." | ||
| 47 | end | ||
| 48 | fs.pop_dir() | ||
| 49 | end | ||
| 50 | fs.pop_dir() | ||
| 51 | return module, dir | ||
| 52 | end | ||
| 53 | |||
diff --git a/src/luarocks/fetch/sscm.lua b/src/luarocks/fetch/sscm.lua new file mode 100644 index 00000000..a2c70b96 --- /dev/null +++ b/src/luarocks/fetch/sscm.lua | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | |||
| 2 | --- Fetch back-end for retrieving sources from Surround SCM Server | ||
| 3 | module("luarocks.fetch.sscm", package.seeall) | ||
| 4 | |||
| 5 | local fs = require("luarocks.fs") | ||
| 6 | |||
| 7 | --- Download sources via Surround SCM Server for building a rock. | ||
| 8 | -- @param rockspec table: The rockspec table | ||
| 9 | -- @param extract boolean: Unused in this module (required for API purposes.) | ||
| 10 | -- @param dest_dir string or nil: If set, will extract to the given directory. | ||
| 11 | -- @return (string, string) or (nil, string): The absolute pathname of | ||
| 12 | -- the fetched source tarball and the temporary directory created to | ||
| 13 | -- store it; or nil and an error message. | ||
| 14 | function get_sources(rockspec, extract, dest_dir) | ||
| 15 | assert(type(rockspec) == "table") | ||
| 16 | assert(type(dest_dir) == "string" or not dest_dir) | ||
| 17 | |||
| 18 | local module = rockspec.source.module or fs.base_name(rockspec.source.url) | ||
| 19 | local branch, repository = string.match(rockspec.source.pathname, "^([^/]*)/(.*)") | ||
| 20 | if not branch or not repository then | ||
| 21 | return nil, "Error retrieving branch and repository from rockspec." | ||
| 22 | end | ||
| 23 | -- Search for working directory. | ||
| 24 | local working_dir | ||
| 25 | local tmp = io.popen(string.format([[sscm property "/" -d -b%s -p%s]], branch, repository)) | ||
| 26 | for line in tmp:lines() do | ||
| 27 | --%c because a chr(13) comes in the end. | ||
| 28 | working_dir = string.match(line, "Working directory:[%s]*(.*)%c$") | ||
| 29 | if working_dir then break end | ||
| 30 | end | ||
| 31 | tmp:close() | ||
| 32 | if not working_dir then | ||
| 33 | return nil, "Error retrieving working directory from SSCM." | ||
| 34 | end | ||
| 35 | if not fs.execute([["sscm"]], "get", "*", "-e" , "-r", "-b"..branch, "-p"..repository, "-tmodify", "-wreplace") then | ||
| 36 | return nil, "Failed fetching files from SSCM." | ||
| 37 | end | ||
| 38 | -- FIXME: This function does not honor the dest_dir parameter. | ||
| 39 | return module, working_dir | ||
| 40 | end | ||
diff --git a/src/luarocks/fs.lua b/src/luarocks/fs.lua new file mode 100644 index 00000000..7027167a --- /dev/null +++ b/src/luarocks/fs.lua | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | |||
| 2 | local rawset = rawset | ||
| 3 | |||
| 4 | --- Proxy module for filesystem and platform abstractions. | ||
| 5 | -- All code using "fs" code should require "luarocks.fs", | ||
| 6 | -- and not the various platform-specific implementations. | ||
| 7 | -- However, see the documentation of the implementation | ||
| 8 | -- for the API reference. | ||
| 9 | module("luarocks.fs", package.seeall) | ||
| 10 | |||
| 11 | local cfg = require("luarocks.cfg") | ||
| 12 | |||
| 13 | local fs_impl = nil | ||
| 14 | for _, platform in ipairs(cfg.platforms) do | ||
| 15 | local ok, result = pcall(require, "luarocks.fs."..platform) | ||
| 16 | if ok then | ||
| 17 | fs_impl = result | ||
| 18 | if fs_impl then | ||
| 19 | break | ||
| 20 | end | ||
| 21 | end | ||
| 22 | end | ||
| 23 | |||
| 24 | local fs_unix = require("luarocks.fs.unix") | ||
| 25 | |||
| 26 | local fs_mt = { | ||
| 27 | __index = function(t, k) | ||
| 28 | local impl = fs_impl and fs_impl[k] | ||
| 29 | if not impl then | ||
| 30 | impl = fs_unix[k] | ||
| 31 | end | ||
| 32 | rawset(t, k, impl) | ||
| 33 | return impl | ||
| 34 | end | ||
| 35 | } | ||
| 36 | |||
| 37 | setmetatable(luarocks.fs, fs_mt) | ||
| 38 | |||
| 39 | fs_unix.init_fs_functions(luarocks.fs) | ||
| 40 | if fs_impl then | ||
| 41 | fs_impl.init_fs_functions(luarocks.fs) | ||
| 42 | end | ||
| 43 | |||
diff --git a/src/luarocks/fs/unix.lua b/src/luarocks/fs/unix.lua new file mode 100644 index 00000000..2a849329 --- /dev/null +++ b/src/luarocks/fs/unix.lua | |||
| @@ -0,0 +1,519 @@ | |||
| 1 | |||
| 2 | local assert, type, table, io, package, math, os, ipairs = | ||
| 3 | assert, type, table, io, package, math, os, ipairs | ||
| 4 | |||
| 5 | --- Unix implementation of filesystem and platform abstractions. | ||
| 6 | module("luarocks.fs.unix", package.seeall) | ||
| 7 | |||
| 8 | local cfg = require("luarocks.cfg") | ||
| 9 | |||
| 10 | math.randomseed(os.time()) | ||
| 11 | |||
| 12 | dir_stack = {} | ||
| 13 | |||
| 14 | local fs_absolute_name, | ||
| 15 | fs_base_name, | ||
| 16 | fs_copy, | ||
| 17 | fs_current_dir, | ||
| 18 | fs_dir_stack, | ||
| 19 | fs_execute, | ||
| 20 | fs_execute_string, | ||
| 21 | fs_is_dir, | ||
| 22 | fs_make_dir, | ||
| 23 | fs_make_path, | ||
| 24 | fs_exists, | ||
| 25 | fs_find, | ||
| 26 | fs_Q | ||
| 27 | |||
| 28 | function init_fs_functions(impl) | ||
| 29 | fs_absolute_name = impl.absolute_name | ||
| 30 | fs_base_name = impl.base_name | ||
| 31 | fs_copy = impl.copy | ||
| 32 | fs_current_dir = impl.current_dir | ||
| 33 | fs_dir_stack = impl.dir_stack | ||
| 34 | fs_execute = impl.execute | ||
| 35 | fs_execute_string = impl.execute_string | ||
| 36 | fs_is_dir = impl.is_dir | ||
| 37 | fs_make_dir = impl.make_dir | ||
| 38 | fs_make_path = impl.make_path | ||
| 39 | fs_exists = impl.exists | ||
| 40 | fs_find = impl.find | ||
| 41 | fs_Q = impl.Q | ||
| 42 | end | ||
| 43 | |||
| 44 | dir_separator = "/" | ||
| 45 | |||
| 46 | --- Quote argument for shell processing. | ||
| 47 | -- Adds single quotes and escapes. | ||
| 48 | -- @param arg string: Unquoted argument. | ||
| 49 | -- @return string: Quoted argument. | ||
| 50 | function Q(arg) | ||
| 51 | assert(type(arg) == "string") | ||
| 52 | |||
| 53 | return "'" .. arg:gsub("\\", "\\\\"):gsub("'", "'\\''") .. "'" | ||
| 54 | end | ||
| 55 | |||
| 56 | --- Obtain current directory. | ||
| 57 | -- Uses the module's internal dir stack. | ||
| 58 | -- @return string: the absolute pathname of the current directory. | ||
| 59 | function current_dir() | ||
| 60 | local current = os.getenv("PWD") | ||
| 61 | if not current then | ||
| 62 | local pipe = io.popen("pwd") | ||
| 63 | current = pipe:read("*l") | ||
| 64 | pipe:close() | ||
| 65 | end | ||
| 66 | for _, dir in ipairs(fs_dir_stack) do | ||
| 67 | current = fs_absolute_name(dir, current) | ||
| 68 | end | ||
| 69 | return current | ||
| 70 | end | ||
| 71 | |||
| 72 | --- Run the given command. | ||
| 73 | -- The command is executed in the current directory in the dir stack. | ||
| 74 | -- @param cmd string: No quoting/escaping is applied to the command. | ||
| 75 | -- @return boolean: true if command succeeds (status code 0), false | ||
| 76 | -- otherwise. | ||
| 77 | function execute_string(cmd) | ||
| 78 | if os.execute("cd " .. fs_Q(fs_current_dir()) .. " && " .. cmd) == 0 then | ||
| 79 | return true | ||
| 80 | else | ||
| 81 | return false | ||
| 82 | end | ||
| 83 | end | ||
| 84 | |||
| 85 | --- Run the given command, quoting its arguments. | ||
| 86 | -- The command is executed in the current directory in the dir stack. | ||
| 87 | -- @param command string: The command to be executed. No quoting/escaping | ||
| 88 | -- is applied. | ||
| 89 | -- @param ... Strings containing additional arguments, which are quoted. | ||
| 90 | -- @return boolean: true if command succeeds (status code 0), false | ||
| 91 | -- otherwise. | ||
| 92 | function execute(command, ...) | ||
| 93 | assert(type(command) == "string") | ||
| 94 | |||
| 95 | for _, arg in ipairs({...}) do | ||
| 96 | assert(type(arg) == "string") | ||
| 97 | command = command .. " " .. fs_Q(arg) | ||
| 98 | end | ||
| 99 | return fs_execute_string(command) | ||
| 100 | end | ||
| 101 | |||
| 102 | --- Change the current directory. | ||
| 103 | -- Uses the module's internal dir stack. This does not have exact | ||
| 104 | -- semantics of chdir, as it does not handle errors the same way, | ||
| 105 | -- but works well for our purposes for now. | ||
| 106 | -- @param dir string: The directory to switch to. | ||
| 107 | function change_dir(dir) | ||
| 108 | assert(type(dir) == "string") | ||
| 109 | table.insert(fs_dir_stack, dir) | ||
| 110 | end | ||
| 111 | |||
| 112 | --- Change directory to root. | ||
| 113 | -- Allows leaving a directory (e.g. for deleting it) in | ||
| 114 | -- a crossplatform way. | ||
| 115 | function change_dir_to_root() | ||
| 116 | table.insert(fs_dir_stack, "/") | ||
| 117 | end | ||
| 118 | |||
| 119 | --- Change working directory to the previous in the dir stack. | ||
| 120 | function pop_dir() | ||
| 121 | table.remove(fs_dir_stack) | ||
| 122 | end | ||
| 123 | |||
| 124 | --- Create a directory if it does not already exist. | ||
| 125 | -- If any of the higher levels in the path name does not exist | ||
| 126 | -- too, they are created as well. | ||
| 127 | -- @param dir string: pathname of directory to create. | ||
| 128 | -- @return boolean: true on success, false on failure. | ||
| 129 | function make_dir(dir) | ||
| 130 | assert(dir) | ||
| 131 | return fs_execute("mkdir -p", dir) | ||
| 132 | end | ||
| 133 | |||
| 134 | --- Remove a directory if it is empty. | ||
| 135 | -- Does not return errors (for example, if directory is not empty or | ||
| 136 | -- if already does not exist) | ||
| 137 | -- @param dir string: pathname of directory to remove. | ||
| 138 | function remove_dir_if_empty(dir) | ||
| 139 | assert(dir) | ||
| 140 | fs_execute_string("rmdir "..fs_Q(dir).." 1> /dev/null 2> /dev/null") | ||
| 141 | end | ||
| 142 | |||
| 143 | --- Copy a file. | ||
| 144 | -- @param src string: Pathname of source | ||
| 145 | -- @param dest string: Pathname of destination | ||
| 146 | -- @return boolean or (boolean, string): true on success, false on failure, | ||
| 147 | -- plus an error message. | ||
| 148 | function copy(src, dest) | ||
| 149 | assert(src and dest) | ||
| 150 | if fs_execute("cp", src, dest) then | ||
| 151 | return true | ||
| 152 | else | ||
| 153 | return false, "Failed copying "..src.." to "..dest | ||
| 154 | end | ||
| 155 | end | ||
| 156 | |||
| 157 | --- Recursively copy the contents of a directory. | ||
| 158 | -- @param src string: Pathname of source | ||
| 159 | -- @param dest string: Pathname of destination | ||
| 160 | -- @return boolean or (boolean, string): true on success, false on failure, | ||
| 161 | -- plus an error message. | ||
| 162 | function copy_contents(src, dest) | ||
| 163 | assert(src and dest) | ||
| 164 | if fs_execute_string("cp -a "..fs_Q(src).."/* "..fs_Q(dest).." 1> /dev/null 2>/dev/null") then | ||
| 165 | return true | ||
| 166 | else | ||
| 167 | return false, "Failed copying "..src.." to "..dest | ||
| 168 | end | ||
| 169 | end | ||
| 170 | |||
| 171 | --- Delete a file or a directory and all its contents. | ||
| 172 | -- For safety, this only accepts absolute paths. | ||
| 173 | -- @param arg string: Pathname of source | ||
| 174 | -- @return boolean: true on success, false on failure. | ||
| 175 | function delete(arg) | ||
| 176 | assert(arg) | ||
| 177 | assert(arg:sub(1,1) == "/") | ||
| 178 | return fs_execute_string("rm -rf " .. fs_Q(arg) .. " 1> /dev/null 2>/dev/null") | ||
| 179 | end | ||
| 180 | |||
| 181 | --- List the contents of a directory. | ||
| 182 | -- @param at string or nil: directory to list (will be the current | ||
| 183 | -- directory if none is given). | ||
| 184 | -- @return table: an array of strings with the filenames representing | ||
| 185 | -- the contents of a directory. | ||
| 186 | function dir(at) | ||
| 187 | assert(type(at) == "string" or not at) | ||
| 188 | if not at then | ||
| 189 | at = fs_current_dir() | ||
| 190 | end | ||
| 191 | if not fs_is_dir(at) then | ||
| 192 | return {} | ||
| 193 | end | ||
| 194 | local result = {} | ||
| 195 | local pipe = io.popen("cd "..fs_Q(at).." && ls") | ||
| 196 | for file in pipe:lines() do | ||
| 197 | table.insert(result, file) | ||
| 198 | end | ||
| 199 | pipe:close() | ||
| 200 | return result | ||
| 201 | end | ||
| 202 | |||
| 203 | --- Recursively scan the contents of a directory. | ||
| 204 | -- @param at string or nil: directory to scan (will be the current | ||
| 205 | -- directory if none is given). | ||
| 206 | -- @return table: an array of strings with the filenames representing | ||
| 207 | -- the contents of a directory. | ||
| 208 | function find(at) | ||
| 209 | assert(type(at) == "string" or not at) | ||
| 210 | if not at then | ||
| 211 | at = fs_current_dir() | ||
| 212 | end | ||
| 213 | if not fs_is_dir(at) then | ||
| 214 | return {} | ||
| 215 | end | ||
| 216 | local result = {} | ||
| 217 | local pipe = io.popen("cd "..fs_Q(at).." && find * 2>/dev/null") | ||
| 218 | for file in pipe:lines() do | ||
| 219 | table.insert(result, file) | ||
| 220 | end | ||
| 221 | pipe:close() | ||
| 222 | return result | ||
| 223 | end | ||
| 224 | |||
| 225 | --- Compress files in a .zip archive. | ||
| 226 | -- @param zipfile string: pathname of .zip archive to be created. | ||
| 227 | -- @param ... Filenames to be stored in the archive are given as | ||
| 228 | -- additional arguments. | ||
| 229 | -- @return boolean: true on success, false on failure. | ||
| 230 | function zip(zipfile, ...) | ||
| 231 | return fs_execute("zip -r", zipfile, ...) | ||
| 232 | end | ||
| 233 | |||
| 234 | --- Uncompress files from a .zip archive. | ||
| 235 | -- @param zipfile string: pathname of .zip archive to be extracted. | ||
| 236 | -- @return boolean: true on success, false on failure. | ||
| 237 | function unzip(zipfile) | ||
| 238 | assert(zipfile) | ||
| 239 | return fs_execute("unzip", zipfile) | ||
| 240 | end | ||
| 241 | |||
| 242 | --- Test for existance of a file. | ||
| 243 | -- @param file string: filename to test | ||
| 244 | -- @return boolean: true if file exists, false otherwise. | ||
| 245 | function exists(file) | ||
| 246 | assert(file) | ||
| 247 | return fs_execute("test -r", file) | ||
| 248 | end | ||
| 249 | |||
| 250 | --- Test is file/dir is writable. | ||
| 251 | -- @param file string: filename to test | ||
| 252 | -- @return boolean: true if file exists, false otherwise. | ||
| 253 | function is_writable(file) | ||
| 254 | assert(file) | ||
| 255 | return fs_execute("test -w", file) | ||
| 256 | end | ||
| 257 | |||
| 258 | --- Test is pathname is a directory. | ||
| 259 | -- @param file string: pathname to test | ||
| 260 | -- @return boolean: true if it is a directory, false otherwise. | ||
| 261 | function is_dir(file) | ||
| 262 | assert(file) | ||
| 263 | return fs_execute("test -d", file) | ||
| 264 | end | ||
| 265 | |||
| 266 | --- Download a remote file. | ||
| 267 | -- @param url string: URL to be fetched. | ||
| 268 | -- @param filename string or nil: this function attempts to detect the | ||
| 269 | -- resulting local filename of the remote file as the basename of the URL; | ||
| 270 | -- if that is not correct (due to a redirection, for example), the local | ||
| 271 | -- filename can be given explicitly as this second argument. | ||
| 272 | -- @return boolean: true on success, false on failure. | ||
| 273 | function download(url, filename) | ||
| 274 | assert(type(url) == "string") | ||
| 275 | assert(type(filename) == "string" or not filename) | ||
| 276 | |||
| 277 | if cfg.downloader == "wget" then | ||
| 278 | if filename then | ||
| 279 | return fs_execute("wget --quiet --continue --output-document ", filename, url) | ||
| 280 | else | ||
| 281 | return fs_execute("wget --quiet --continue ", url) | ||
| 282 | end | ||
| 283 | elseif cfg.downloader == "curl" then | ||
| 284 | filename = filename or fs_base_name(url) | ||
| 285 | return fs_execute_string("curl "..fs_Q(url).." 2> /dev/null 1> "..fs_Q(filename)) | ||
| 286 | end | ||
| 287 | end | ||
| 288 | |||
| 289 | --- Strip the path off a path+filename. | ||
| 290 | -- @param pathname string: A path+name, such as "/a/b/c". | ||
| 291 | -- @return string: The filename without its path, such as "c". | ||
| 292 | function base_name(pathname) | ||
| 293 | assert(type(pathname) == "string") | ||
| 294 | |||
| 295 | local base = pathname:match(".*/([^/]*)") | ||
| 296 | return base or pathname | ||
| 297 | end | ||
| 298 | |||
| 299 | --- Strip the name off a path+filename. | ||
| 300 | -- @param pathname string: A path+name, such as "/a/b/c". | ||
| 301 | -- @return string: The filename without its path, such as "/a/b/". | ||
| 302 | -- For entries such as "/a/b/", "/a/" is returned. If there are | ||
| 303 | -- no directory separators in input, "" is returned. | ||
| 304 | function dir_name(pathname) | ||
| 305 | assert(type(pathname) == "string") | ||
| 306 | |||
| 307 | local dir = pathname:gsub("/*$", ""):match("(.*/)[^/]*") | ||
| 308 | return dir or "" | ||
| 309 | end | ||
| 310 | |||
| 311 | --- Create a temporary directory. | ||
| 312 | -- @param name string: name pattern to use for avoiding conflicts | ||
| 313 | -- when creating temporary directory. | ||
| 314 | -- @return string or nil: name of temporary directory or nil on failure. | ||
| 315 | function make_temp_dir(name) | ||
| 316 | assert(type(name) == "string") | ||
| 317 | |||
| 318 | local temp_dir = (os.getenv("TMP") or "/tmp") .. "/" .. name .. "-" .. tostring(math.floor(math.random() * 10000)) | ||
| 319 | if fs_make_dir(temp_dir) then | ||
| 320 | return temp_dir | ||
| 321 | else | ||
| 322 | return nil | ||
| 323 | end | ||
| 324 | end | ||
| 325 | |||
| 326 | --- Apply a patch. | ||
| 327 | -- @param patchname string: The filename of the patch. | ||
| 328 | function patch(patchname) | ||
| 329 | return fs_execute("patch -p1 -f -i ", patchname) | ||
| 330 | end | ||
| 331 | |||
| 332 | --- Unpack an archive. | ||
| 333 | -- Extract the contents of an archive, detecting its format by | ||
| 334 | -- filename extension. | ||
| 335 | -- @param archive string: Filename of archive. | ||
| 336 | -- @return boolean or (boolean, string): true on success, false and an error message on failure. | ||
| 337 | function unpack_archive(archive) | ||
| 338 | assert(type(archive) == "string") | ||
| 339 | |||
| 340 | local ok | ||
| 341 | if archive:match("%.tar%.gz$") or archive:match("%.tgz$") then | ||
| 342 | -- ok = fs_execute("tar zxvpf ", archive) | ||
| 343 | ok = fs_execute_string("gunzip -c "..archive.."|tar -xf -") | ||
| 344 | elseif archive:match("%.tar%.bz2$") then | ||
| 345 | -- ok = fs_execute("tar jxvpf ", archive) | ||
| 346 | ok = fs_execute_string("bunzip2 -c "..archive.."|tar -xf -") | ||
| 347 | elseif archive:match("%.zip$") then | ||
| 348 | ok = fs_execute("unzip ", archive) | ||
| 349 | elseif archive:match("%.lua$") or archive:match("%.c$") then | ||
| 350 | -- Ignore .lua and .c files; they don't need to be extracted. | ||
| 351 | return true | ||
| 352 | else | ||
| 353 | local ext = archive:match(".*(%..*)") | ||
| 354 | return false, "Unrecognized filename extension "..(ext or "") | ||
| 355 | end | ||
| 356 | if not ok then | ||
| 357 | return false, "Failed extracting "..archive | ||
| 358 | end | ||
| 359 | return true | ||
| 360 | end | ||
| 361 | |||
| 362 | --- Check the MD5 checksum for a file. | ||
| 363 | -- @param file string: The file to be checked. | ||
| 364 | -- @param md5sum string: The string with the expected MD5 checksum. | ||
| 365 | -- @return boolean: true if the MD5 checksum for 'file' equals 'md5sum', false if not | ||
| 366 | -- or if it could not perform the check for any reason. | ||
| 367 | function check_md5(file, md5sum) | ||
| 368 | file = fs_absolute_name(file) | ||
| 369 | local computed | ||
| 370 | if cfg.md5checker == "md5sum" then | ||
| 371 | local pipe = io.popen("md5sum "..file) | ||
| 372 | computed = pipe:read("*l"):gsub("[^%x]+", "") | ||
| 373 | pipe:close() | ||
| 374 | if computed then | ||
| 375 | computed = computed:sub(1,32) | ||
| 376 | end | ||
| 377 | elseif cfg.md5checker == "openssl" then | ||
| 378 | local pipe = io.popen("openssl md5 "..file) | ||
| 379 | computed = pipe:read("*l") | ||
| 380 | pipe:close() | ||
| 381 | if computed then | ||
| 382 | computed = computed:sub(-32) | ||
| 383 | end | ||
| 384 | elseif cfg.md5checker == "md5" then | ||
| 385 | local pipe = io.popen("md5 "..file) | ||
| 386 | computed = pipe:read("*l") | ||
| 387 | pipe:close() | ||
| 388 | if computed then | ||
| 389 | computed = computed:sub(-32) | ||
| 390 | end | ||
| 391 | end | ||
| 392 | if not computed then | ||
| 393 | return false | ||
| 394 | end | ||
| 395 | if computed:match("^"..md5sum) then | ||
| 396 | return true | ||
| 397 | else | ||
| 398 | return false | ||
| 399 | end | ||
| 400 | end | ||
| 401 | |||
| 402 | --- Return an absolute pathname from a potentially relative one. | ||
| 403 | -- @param pathname string: pathname to convert. | ||
| 404 | -- @param relative_to string or nil: path to prepend when making | ||
| 405 | -- pathname absolute, or the current dir in the dir stack if | ||
| 406 | -- not given. | ||
| 407 | -- @return string: The pathname converted to absolute. | ||
| 408 | function absolute_name(pathname, relative_to) | ||
| 409 | assert(type(pathname) == "string") | ||
| 410 | assert(type(relative_to) == "string" or not relative_to) | ||
| 411 | |||
| 412 | relative_to = relative_to or fs_current_dir() | ||
| 413 | if pathname:sub(1,1) == "/" then | ||
| 414 | return pathname | ||
| 415 | else | ||
| 416 | return relative_to .. "/" .. pathname | ||
| 417 | end | ||
| 418 | end | ||
| 419 | |||
| 420 | --- Describe a path in a cross-platform way. | ||
| 421 | -- Use this function to avoid platform-specific directory | ||
| 422 | -- separators in other modules. If the first item contains a | ||
| 423 | -- protocol descriptor (e.g. "http:"), paths are always constituted | ||
| 424 | -- with forward slashes. | ||
| 425 | -- @param ... strings representing directories | ||
| 426 | -- @return string: a string with a platform-specific representation | ||
| 427 | -- of the path. | ||
| 428 | function make_path(...) | ||
| 429 | local items = {...} | ||
| 430 | local i = 1 | ||
| 431 | while items[i] do | ||
| 432 | if items[i] == "" then | ||
| 433 | table.remove(items, i) | ||
| 434 | else | ||
| 435 | i = i + 1 | ||
| 436 | end | ||
| 437 | end | ||
| 438 | return table.concat(items, "/") | ||
| 439 | end | ||
| 440 | |||
| 441 | --- Split protocol and path from an URL or local pathname. | ||
| 442 | -- URLs should be in the "protocol://path" format. | ||
| 443 | -- For local pathnames, "file" is returned as the protocol. | ||
| 444 | -- @param url string: an URL or a local pathname. | ||
| 445 | -- @return string, string: the protocol, and the absolute pathname without the protocol. | ||
| 446 | function split_url(url) | ||
| 447 | assert(type(url) == "string") | ||
| 448 | |||
| 449 | local protocol, pathname = url:match("^([^:]*)://(.*)") | ||
| 450 | if not protocol then | ||
| 451 | protocol = "file" | ||
| 452 | pathname = url | ||
| 453 | end | ||
| 454 | if protocol == "file" then | ||
| 455 | pathname = fs_absolute_name(pathname) | ||
| 456 | end | ||
| 457 | return protocol, pathname | ||
| 458 | end | ||
| 459 | |||
| 460 | --- Create a wrapper to make a script executable from the command-line. | ||
| 461 | -- @param file string: Pathname of script to be made executable. | ||
| 462 | -- @param dest string: Directory where to put the wrapper. | ||
| 463 | -- @return boolean or (nil, string): True if succeeded, or nil and | ||
| 464 | -- an error message. | ||
| 465 | function wrap_script(file, dest) | ||
| 466 | assert(type(file) == "string") | ||
| 467 | assert(type(dest) == "string") | ||
| 468 | |||
| 469 | local base = fs_base_name(file) | ||
| 470 | local wrapname = dest.."/"..base | ||
| 471 | local wrapper = io.open(wrapname, "w") | ||
| 472 | if not wrapper then | ||
| 473 | return nil, "Could not open "..wrapname.." for writing." | ||
| 474 | end | ||
| 475 | wrapper:write("#!/bin/sh\n\n") | ||
| 476 | wrapper:write('LUA_PATH="'..package.path..';$LUA_PATH"\n') | ||
| 477 | wrapper:write('LUA_CPATH="'..package.cpath..';$LUA_CPATH"\n') | ||
| 478 | wrapper:write('export LUA_PATH LUA_CPATH\n') | ||
| 479 | wrapper:write('exec "'..fs_make_path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)..'" -lluarocks.require "'..file..'" "$@"\n') | ||
| 480 | wrapper:close() | ||
| 481 | if fs_execute("chmod +x",wrapname) then | ||
| 482 | return true | ||
| 483 | else | ||
| 484 | return nil, "Could not make "..wrapname.." executable." | ||
| 485 | end | ||
| 486 | end | ||
| 487 | |||
| 488 | --- Check if a file (typically inside path.bin_dir) is an actual binary | ||
| 489 | -- or a Lua wrapper. | ||
| 490 | -- @param filename string: the file name with full path. | ||
| 491 | -- @return boolean: returns true if file is an actual binary | ||
| 492 | -- (or if it couldn't check) or false if it is a Lua wrapper. | ||
| 493 | local function is_actual_binary(filename) | ||
| 494 | local file = io.open(filename) | ||
| 495 | if file then | ||
| 496 | local found = false | ||
| 497 | if file:read():match("#!/bin/sh") then | ||
| 498 | local line = file:read() | ||
| 499 | line = file:read() | ||
| 500 | if not(line and line:match("LUA_PATH")) then | ||
| 501 | found = true | ||
| 502 | end | ||
| 503 | end | ||
| 504 | file:close() | ||
| 505 | if found then | ||
| 506 | return false | ||
| 507 | else | ||
| 508 | return true | ||
| 509 | end | ||
| 510 | else | ||
| 511 | return true | ||
| 512 | end | ||
| 513 | return false | ||
| 514 | end | ||
| 515 | |||
| 516 | function copy_binary(filename, dest) | ||
| 517 | return fs_copy(filename, dest) | ||
| 518 | end | ||
| 519 | |||
diff --git a/src/luarocks/fs/win32.lua b/src/luarocks/fs/win32.lua new file mode 100644 index 00000000..5702a3aa --- /dev/null +++ b/src/luarocks/fs/win32.lua | |||
| @@ -0,0 +1,369 @@ | |||
| 1 | --- Windows implementation of filesystem and platform abstractions. | ||
| 2 | -- Download http://unxutils.sourceforge.net/ for Windows GNU utilities | ||
| 3 | -- used by this module. | ||
| 4 | module("luarocks.fs.win32", package.seeall) | ||
| 5 | |||
| 6 | local cfg = require("luarocks.cfg") | ||
| 7 | |||
| 8 | local fs_base_name, | ||
| 9 | fs_copy, | ||
| 10 | fs_current_dir, | ||
| 11 | fs_execute, | ||
| 12 | fs_execute_string, | ||
| 13 | fs_is_dir, | ||
| 14 | fs_make_path, | ||
| 15 | fs_Q | ||
| 16 | |||
| 17 | function init_fs_functions(impl) | ||
| 18 | fs_base_name = impl.base_name | ||
| 19 | fs_copy = impl.copy | ||
| 20 | fs_current_dir = impl.current_dir | ||
| 21 | fs_execute = impl.execute | ||
| 22 | fs_execute_string = impl.execute_string | ||
| 23 | fs_is_dir = impl.is_dir | ||
| 24 | fs_make_path = impl.make_path | ||
| 25 | fs_Q = impl.Q | ||
| 26 | end | ||
| 27 | |||
| 28 | --- Quote argument for shell processing. Fixes paths on Windows. | ||
| 29 | -- Adds single quotes and escapes. | ||
| 30 | -- @param arg string: Unquoted argument. | ||
| 31 | -- @return string: Quoted argument. | ||
| 32 | function Q(arg) | ||
| 33 | assert(type(arg) == "string") | ||
| 34 | -- Quote DIR for Windows | ||
| 35 | if arg:match("^[\.a-zA-Z]?:?[\\/]") then | ||
| 36 | return '"' .. arg:gsub("/", "\\"):gsub('"', '\\"') .. '"' | ||
| 37 | end | ||
| 38 | -- URLs and anything else | ||
| 39 | return '"' .. arg:gsub('"', '\\"') .. '"' | ||
| 40 | end | ||
| 41 | |||
| 42 | local function command_at(dir, cmd) | ||
| 43 | local drive = dir:match("^([A-Za-z]:)") | ||
| 44 | cmd = "cd " .. fs_Q(dir) .. " & " .. cmd | ||
| 45 | if drive then | ||
| 46 | cmd = drive .. " & " .. cmd | ||
| 47 | end | ||
| 48 | return cmd | ||
| 49 | end | ||
| 50 | |||
| 51 | --- Run the given command. | ||
| 52 | -- The command is executed in the current directory in the dir stack. | ||
| 53 | -- @param cmd string: No quoting/escaping is applied to the command. | ||
| 54 | -- @return boolean: true if command succeeds (status code 0), false | ||
| 55 | -- otherwise. | ||
| 56 | function execute_string(cmd) | ||
| 57 | if os.execute(command_at(fs_current_dir(), cmd)) == 0 then | ||
| 58 | return true | ||
| 59 | else | ||
| 60 | return false | ||
| 61 | end | ||
| 62 | end | ||
| 63 | |||
| 64 | --- Test for existance of a file. | ||
| 65 | -- @param file string: filename to test | ||
| 66 | -- @return boolean: true if file exists, false otherwise. | ||
| 67 | function exists(file) | ||
| 68 | assert(file) | ||
| 69 | return fs_execute("if not exist " .. fs_Q(file) .. | ||
| 70 | " invalidcommandname 2>NUL 1>NUL") | ||
| 71 | end | ||
| 72 | |||
| 73 | --- Test is pathname is a directory. | ||
| 74 | -- @param file string: pathname to test | ||
| 75 | -- @return boolean: true if it is a directory, false otherwise. | ||
| 76 | function is_dir(file) | ||
| 77 | assert(file) | ||
| 78 | return fs_execute("chdir /D " .. fs_Q(file) .. " 2>NUL 1>NUL") | ||
| 79 | end | ||
| 80 | |||
| 81 | --- Test is file/dir is writable. | ||
| 82 | -- @param file string: filename to test | ||
| 83 | -- @return boolean: true if file exists, false otherwise. | ||
| 84 | function is_writable(file) | ||
| 85 | assert(file) | ||
| 86 | local result | ||
| 87 | if is_dir(file) then | ||
| 88 | local file2 = file .. '/.tmpluarockstestwritable' | ||
| 89 | local fh = io.open(file2, 'w') | ||
| 90 | result = fh ~= nil | ||
| 91 | if fh then fh:close() end | ||
| 92 | os.remove(file2) | ||
| 93 | else | ||
| 94 | local fh = io.open(file, 'r+') | ||
| 95 | result = fh ~= nil | ||
| 96 | if fh then fh:close() end | ||
| 97 | end | ||
| 98 | return result | ||
| 99 | end | ||
| 100 | |||
| 101 | |||
| 102 | --- Create a directory if it does not already exist. | ||
| 103 | -- If any of the higher levels in the path name does not exist | ||
| 104 | -- too, they are created as well. | ||
| 105 | -- @param dir string: pathname of directory to create. | ||
| 106 | -- @return boolean: true on success, false on failure. | ||
| 107 | function make_dir(dir) | ||
| 108 | assert(dir) | ||
| 109 | fs_execute("mkdir "..fs_Q(dir).." 1> NUL 2> NUL") | ||
| 110 | return 1 | ||
| 111 | end | ||
| 112 | |||
| 113 | --- Remove a directory if it is empty. | ||
| 114 | -- Does not return errors (for example, if directory is not empty or | ||
| 115 | -- if already does not exist) | ||
| 116 | -- @param dir string: pathname of directory to remove. | ||
| 117 | function remove_dir_if_empty(dir) | ||
| 118 | assert(dir) | ||
| 119 | fs_execute_string("rmdir "..fs_Q(dir).." 1> NUL 2> NUL") | ||
| 120 | end | ||
| 121 | |||
| 122 | --- Copy a file. | ||
| 123 | -- @param src string: Pathname of source | ||
| 124 | -- @param dest string: Pathname of destination | ||
| 125 | -- @return boolean or (boolean, string): true on success, false on failure, | ||
| 126 | -- plus an error message. | ||
| 127 | function copy(src, dest) | ||
| 128 | assert(src and dest) | ||
| 129 | if dest:match("[/\\]$") then dest = dest:sub(1, -2) end | ||
| 130 | if fs_execute("cp", src, dest) then | ||
| 131 | return true | ||
| 132 | else | ||
| 133 | return false, "Failed copying "..src.." to "..dest | ||
| 134 | end | ||
| 135 | end | ||
| 136 | |||
| 137 | --- Recursively copy the contents of a directory. | ||
| 138 | -- @param src string: Pathname of source | ||
| 139 | -- @param dest string: Pathname of destination | ||
| 140 | -- @return boolean or (boolean, string): true on success, false on failure, | ||
| 141 | -- plus an error message. | ||
| 142 | function copy_contents(src, dest) | ||
| 143 | assert(src and dest) | ||
| 144 | if fs_execute_string("cp -a "..src.."\\*.* "..fs_Q(dest).." 1> NUL 2> NUL") then | ||
| 145 | return true | ||
| 146 | else | ||
| 147 | return false, "Failed copying "..src.." to "..dest | ||
| 148 | end | ||
| 149 | end | ||
| 150 | |||
| 151 | --- Delete a file or a directory and all its contents. | ||
| 152 | -- For safety, this only accepts absolute paths. | ||
| 153 | -- @param arg string: Pathname of source | ||
| 154 | -- @return boolean: true on success, false on failure. | ||
| 155 | function delete(arg) | ||
| 156 | assert(arg) | ||
| 157 | assert(arg:match("^[\a-zA-Z]?:?[\\/]")) | ||
| 158 | fs_execute("chmod a+rw -R ", arg) | ||
| 159 | return fs_execute_string("rm -rf " .. fs_Q(arg) .. " 1> NUL 2> NUL") | ||
| 160 | end | ||
| 161 | |||
| 162 | --- List the contents of a directory. | ||
| 163 | -- @param at string or nil: directory to list (will be the current | ||
| 164 | -- directory if none is given). | ||
| 165 | -- @return table: an array of strings with the filenames representing | ||
| 166 | -- the contents of a directory. | ||
| 167 | function dir(at) | ||
| 168 | assert(type(at) == "string" or not at) | ||
| 169 | if not at then | ||
| 170 | at = fs_current_dir() | ||
| 171 | end | ||
| 172 | if not fs_is_dir(at) then | ||
| 173 | return {} | ||
| 174 | end | ||
| 175 | local result = {} | ||
| 176 | local pipe = io.popen(command_at(at, "ls")) | ||
| 177 | for file in pipe:lines() do | ||
| 178 | table.insert(result, file) | ||
| 179 | end | ||
| 180 | pipe:close() | ||
| 181 | |||
| 182 | return result | ||
| 183 | end | ||
| 184 | |||
| 185 | --- Recursively scan the contents of a directory. | ||
| 186 | -- @param at string or nil: directory to scan (will be the current | ||
| 187 | -- directory if none is given). | ||
| 188 | -- @return table: an array of strings with the filenames representing | ||
| 189 | -- the contents of a directory. Paths are returned with forward slashes. | ||
| 190 | function find(at) | ||
| 191 | assert(type(at) == "string" or not at) | ||
| 192 | if not at then | ||
| 193 | at = fs_current_dir() | ||
| 194 | end | ||
| 195 | if not fs_is_dir(at) then | ||
| 196 | return {} | ||
| 197 | end | ||
| 198 | local result = {} | ||
| 199 | local pipe = io.popen(command_at(at, "find 2> NUL")) | ||
| 200 | for file in pipe:lines() do | ||
| 201 | -- Windows find is a bit different | ||
| 202 | if file:sub(1,2)==".\\" then file=file:sub(3) end | ||
| 203 | if file ~= "." then | ||
| 204 | table.insert(result, (file:gsub("\\", "/"))) | ||
| 205 | end | ||
| 206 | end | ||
| 207 | return result | ||
| 208 | end | ||
| 209 | |||
| 210 | |||
| 211 | --- Download a remote file. | ||
| 212 | -- @param url string: URL to be fetched. | ||
| 213 | -- @param filename string or nil: this function attempts to detect the | ||
| 214 | -- resulting local filename of the remote file as the basename of the URL; | ||
| 215 | -- if that is not correct (due to a redirection, for example), the local | ||
| 216 | -- filename can be given explicitly as this second argument. | ||
| 217 | -- @return boolean: true on success, false on failure. | ||
| 218 | function download(url, filename) | ||
| 219 | assert(type(url) == "string") | ||
| 220 | assert(type(filename) == "string" or not filename) | ||
| 221 | |||
| 222 | if filename then | ||
| 223 | return fs_execute("wget --quiet --continue --output-document ", filename, url) | ||
| 224 | else | ||
| 225 | return fs_execute("wget --quiet --continue ", url) | ||
| 226 | end | ||
| 227 | end | ||
| 228 | |||
| 229 | --- Strip the path off a path+filename. | ||
| 230 | -- @param pathname string: A path+name, such as "/a/b/c". | ||
| 231 | -- @return string: The filename without its path, such as "c". | ||
| 232 | function base_name(pathname) | ||
| 233 | assert(type(pathname) == "string") | ||
| 234 | |||
| 235 | local base = pathname:match(".*[/\\]([^/\\]*)") | ||
| 236 | return base or pathname | ||
| 237 | end | ||
| 238 | |||
| 239 | --- Strip the last extension of a filename. | ||
| 240 | -- Example: "foo.tar.gz" becomes "foo.tar". | ||
| 241 | -- If filename has no dots, returns it unchanged. | ||
| 242 | -- @param filename string: The file name to strip. | ||
| 243 | -- @return string: The stripped name. | ||
| 244 | local function strip_extension(filename) | ||
| 245 | assert(type(filename) == "string") | ||
| 246 | |||
| 247 | return (filename:gsub("%.[^.]+$", "")) or filename | ||
| 248 | end | ||
| 249 | |||
| 250 | --- Uncompress gzip file. | ||
| 251 | -- @param archive string: Filename of archive. | ||
| 252 | -- @return boolean : success status | ||
| 253 | local function gunzip(archive) | ||
| 254 | local cmd = fs_execute("gunzip -h 1>NUL 2>NUL") and 'gunzip' or | ||
| 255 | fs_execute("gzip -h 1>NUL 2>NUL") and 'gzip -d' | ||
| 256 | local ok = fs_execute(cmd, archive) | ||
| 257 | return ok | ||
| 258 | end | ||
| 259 | |||
| 260 | --- Unpack an archive. | ||
| 261 | -- Extract the contents of an archive, detecting its format by | ||
| 262 | -- filename extension. | ||
| 263 | -- @param archive string: Filename of archive. | ||
| 264 | -- @return boolean or (boolean, string): true on success, false and an error message on failure. | ||
| 265 | function unpack_archive(archive) | ||
| 266 | assert(type(archive) == "string") | ||
| 267 | |||
| 268 | local ok | ||
| 269 | if archive:match("%.tar%.gz$") then | ||
| 270 | ok = gunzip(archive) | ||
| 271 | if ok then | ||
| 272 | ok = fs_execute("tar -xf ", strip_extension(archive)) | ||
| 273 | end | ||
| 274 | elseif archive:match("%.tgz$") then | ||
| 275 | ok = gunzip(archive) | ||
| 276 | if ok then | ||
| 277 | ok = fs_execute("tar -xf ", strip_extension(archive)..".tar") | ||
| 278 | end | ||
| 279 | elseif archive:match("%.tar%.bz2$") then | ||
| 280 | ok = fs_execute("bunzip2 ", archive) | ||
| 281 | if ok then | ||
| 282 | ok = fs_execute("tar -xf ", strip_extension(archive)) | ||
| 283 | end | ||
| 284 | elseif archive:match("%.zip$") then | ||
| 285 | ok = fs_execute("unzip ", archive) | ||
| 286 | elseif archive:match("%.lua$") or archive:match("%.c$") then | ||
| 287 | -- Ignore .lua and .c files; they don't need to be extracted. | ||
| 288 | return true | ||
| 289 | else | ||
| 290 | local ext = archive:match(".*(%..*)") | ||
| 291 | return false, "Unrecognized filename extension "..(ext or "") | ||
| 292 | end | ||
| 293 | if not ok then | ||
| 294 | return false, "Failed extracting "..archive | ||
| 295 | end | ||
| 296 | return true | ||
| 297 | end | ||
| 298 | |||
| 299 | --- Return an absolute pathname from a potentially relative one. | ||
| 300 | -- @param pathname string: pathname to convert. | ||
| 301 | -- @param relative_to string or nil: path to prepend when making | ||
| 302 | -- pathname absolute, or the current dir in the dir stack if | ||
| 303 | -- not given. | ||
| 304 | -- @return string: The pathname converted to absolute. | ||
| 305 | function absolute_name(pathname, relative_to) | ||
| 306 | assert(type(pathname) == "string") | ||
| 307 | assert(type(relative_to) == "string" or not relative_to) | ||
| 308 | |||
| 309 | relative_to = relative_to or fs_current_dir() | ||
| 310 | if pathname:match("^[\.a-zA-Z]?:?[\\/]") then | ||
| 311 | return pathname | ||
| 312 | else | ||
| 313 | return relative_to .. "/" .. pathname | ||
| 314 | end | ||
| 315 | end | ||
| 316 | |||
| 317 | --- Create a wrapper to make a script executable from the command-line. | ||
| 318 | -- @param file string: Pathname of script to be made executable. | ||
| 319 | -- @param dest string: Directory where to put the wrapper. | ||
| 320 | -- @return boolean or (nil, string): True if succeeded, or nil and | ||
| 321 | -- an error message. | ||
| 322 | function wrap_script(file, dest) | ||
| 323 | assert(type(file) == "string") | ||
| 324 | assert(type(dest) == "string") | ||
| 325 | |||
| 326 | local base = fs_base_name(file) | ||
| 327 | local wrapname = dest.."/"..base..".bat" | ||
| 328 | local wrapper = io.open(wrapname, "w") | ||
| 329 | if not wrapper then | ||
| 330 | return nil, "Could not open "..wrapname.." for writing." | ||
| 331 | end | ||
| 332 | wrapper:write("@echo off\n") | ||
| 333 | wrapper:write("setlocal\n") | ||
| 334 | wrapper:write('set LUA_PATH='..package.path..";%LUA_PATH%\n") | ||
| 335 | wrapper:write('set LUA_CPATH='..package.cpath..";%LUA_CPATH%\n") | ||
| 336 | wrapper:write('"'..fs_make_path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)..'" -lluarocks.require "'..file..'" %*\n') | ||
| 337 | wrapper:write("endlocal\n") | ||
| 338 | wrapper:close() | ||
| 339 | return true | ||
| 340 | end | ||
| 341 | |||
| 342 | function is_actual_binary(name) | ||
| 343 | name = name:lower() | ||
| 344 | if name:match("%.bat$") or name:match("%.exe$") then | ||
| 345 | return true | ||
| 346 | end | ||
| 347 | return false | ||
| 348 | end | ||
| 349 | |||
| 350 | function copy_binary(filename, dest) | ||
| 351 | local ok, err = fs_copy(filename, dest) | ||
| 352 | if not ok then | ||
| 353 | return nil, err | ||
| 354 | end | ||
| 355 | local exe_pattern = "%.[Ee][Xx][Ee]$" | ||
| 356 | local base = fs_base_name(filename) | ||
| 357 | if base:match(exe_pattern) then | ||
| 358 | base = base:gsub(exe_pattern, ".lua") | ||
| 359 | local helpname = dest.."/"..base | ||
| 360 | local helper = io.open(helpname, "w") | ||
| 361 | if not helper then | ||
| 362 | return nil, "Could not open "..helpname.." for writing." | ||
| 363 | end | ||
| 364 | helper:write('package.path=\"'..package.path:gsub("\\","\\\\")..';\"..package.path\n') | ||
| 365 | helper:write('package.cpath=\"'..package.path:gsub("\\","\\\\")..';\"..package.cpath\n') | ||
| 366 | helper:close() | ||
| 367 | end | ||
| 368 | return true | ||
| 369 | end | ||
diff --git a/src/luarocks/help.lua b/src/luarocks/help.lua new file mode 100644 index 00000000..76e77a33 --- /dev/null +++ b/src/luarocks/help.lua | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | |||
| 2 | --- Module implementing the LuaRocks "help" command. | ||
| 3 | -- This is a generic help display module, which | ||
| 4 | -- uses a global table called "commands" to find commands | ||
| 5 | -- to show help for; each command should be represented by a | ||
| 6 | -- table containing "help" and "help_summary" fields. | ||
| 7 | module("luarocks.help", package.seeall) | ||
| 8 | |||
| 9 | local util = require("luarocks.util") | ||
| 10 | |||
| 11 | help_summary = "Help on commands." | ||
| 12 | |||
| 13 | help_arguments = "[<command>]" | ||
| 14 | help = [[ | ||
| 15 | <command> is the command to show help for. | ||
| 16 | ]] | ||
| 17 | |||
| 18 | --- Driver function for the "help" command. | ||
| 19 | -- @param command string or nil: command to show help for; if not | ||
| 20 | -- given, help summaries for all commands are shown. | ||
| 21 | -- @return boolean or (nil, string): true if there were no errors | ||
| 22 | -- or nil and an error message if an invalid command was requested. | ||
| 23 | function run(...) | ||
| 24 | local flags, command = util.parse_flags(...) | ||
| 25 | |||
| 26 | if not command then | ||
| 27 | print([[ | ||
| 28 | LuaRocks ]]..program_version..[[, a module deployment system for Lua | ||
| 29 | |||
| 30 | ]]..program_name..[[ - ]]..program_description..[[ | ||
| 31 | |||
| 32 | usage: ]]..program_name..[[ [--from=<server> | --only-from=<server>] [--to=<tree>] [VAR=VALUE]... <command> [<argument>] | ||
| 33 | |||
| 34 | Variables from the "variables" table of the configuration file | ||
| 35 | can be overriden with VAR=VALUE assignments. | ||
| 36 | |||
| 37 | --from=<server> Fetch rocks/rockspecs from this server | ||
| 38 | (takes priority over config file) | ||
| 39 | --only-from=<server> Fetch rocks/rockspecs from this server only | ||
| 40 | (overrides any entries in the config file) | ||
| 41 | --to=<tree> Which tree to operate on. | ||
| 42 | |||
| 43 | Supported commands: | ||
| 44 | ]]) | ||
| 45 | local names = {} | ||
| 46 | for name, command in pairs(commands) do | ||
| 47 | table.insert(names, name) | ||
| 48 | end | ||
| 49 | table.sort(names) | ||
| 50 | for _, name in ipairs(names) do | ||
| 51 | local command = commands[name] | ||
| 52 | print(name, command.help_summary) | ||
| 53 | end | ||
| 54 | else | ||
| 55 | command = command:gsub("-", "_") | ||
| 56 | if commands[command] then | ||
| 57 | local arguments = commands[command].help_arguments or "<argument>" | ||
| 58 | print() | ||
| 59 | print(program_name.." "..command.." "..arguments) | ||
| 60 | print() | ||
| 61 | print(command.." - "..commands[command].help_summary) | ||
| 62 | print() | ||
| 63 | print(commands[command].help) | ||
| 64 | else | ||
| 65 | return nil, "Unknown command '"..command.."'" | ||
| 66 | end | ||
| 67 | end | ||
| 68 | return true | ||
| 69 | end | ||
diff --git a/src/luarocks/install.lua b/src/luarocks/install.lua new file mode 100644 index 00000000..b115a8a8 --- /dev/null +++ b/src/luarocks/install.lua | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | |||
| 2 | --- Module implementing the LuaRocks "install" command. | ||
| 3 | -- Installs binary rocks. | ||
| 4 | module("luarocks.install", package.seeall) | ||
| 5 | |||
| 6 | local path = require("luarocks.path") | ||
| 7 | local rep = require("luarocks.rep") | ||
| 8 | local fetch = require("luarocks.fetch") | ||
| 9 | local util = require("luarocks.util") | ||
| 10 | local fs = require("luarocks.fs") | ||
| 11 | local deps = require("luarocks.deps") | ||
| 12 | local manif = require("luarocks.manif") | ||
| 13 | local cfg = require("luarocks.cfg") | ||
| 14 | |||
| 15 | help_summary = "Install a rock." | ||
| 16 | |||
| 17 | help_arguments = "{<rock>|<name> [<version>]}" | ||
| 18 | |||
| 19 | help = [[ | ||
| 20 | Argument may be the name of a rock to be fetched from a repository | ||
| 21 | or a filename of a locally available rock. | ||
| 22 | ]] | ||
| 23 | |||
| 24 | --- Install a binary rock. | ||
| 25 | -- @param rock_file string: local or remote filename of a rock. | ||
| 26 | -- @return boolean or (nil, string): True if succeeded or | ||
| 27 | -- nil and an error message. | ||
| 28 | function install_binary_rock(rock_file) | ||
| 29 | local name, version, arch = path.parse_rock_name(rock_file) | ||
| 30 | if not name then | ||
| 31 | return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." | ||
| 32 | end | ||
| 33 | if arch ~= "all" and arch ~= cfg.arch then | ||
| 34 | return nil, "Incompatible architecture "..arch | ||
| 35 | end | ||
| 36 | if rep.is_installed(name, version) then | ||
| 37 | rep.delete_version(name, version) | ||
| 38 | end | ||
| 39 | local rollback = util.schedule_function(function() | ||
| 40 | fs.delete(path.install_dir(name, version)) | ||
| 41 | fs.remove_dir_if_empty(path.versions_dir(name)) | ||
| 42 | end) | ||
| 43 | local ok, err = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) | ||
| 44 | if not ok then return nil, err end | ||
| 45 | ok, err = rep.install_bins(name, version) | ||
| 46 | |||
| 47 | local rockspec, err = fetch.load_rockspec(path.rockspec_file(name, version)) | ||
| 48 | if err then | ||
| 49 | return nil, "Failed loading rockspec for installed package: "..err | ||
| 50 | end | ||
| 51 | |||
| 52 | ok, err = deps.check_external_deps(rockspec, "install") | ||
| 53 | if err then | ||
| 54 | return nil, err | ||
| 55 | end | ||
| 56 | |||
| 57 | ok, err = deps.fulfill_dependencies(rockspec) | ||
| 58 | if err then | ||
| 59 | return nil, err | ||
| 60 | end | ||
| 61 | |||
| 62 | ok, err = rep.run_hook(rockspec, "post_install") | ||
| 63 | if err then | ||
| 64 | return nil, err | ||
| 65 | end | ||
| 66 | |||
| 67 | ok, err = manif.update_manifest(name, version) | ||
| 68 | if err then | ||
| 69 | return nil, err | ||
| 70 | end | ||
| 71 | util.remove_scheduled_function(rollback) | ||
| 72 | return true | ||
| 73 | end | ||
| 74 | |||
| 75 | --- Driver function for the "install" command. | ||
| 76 | -- @param name string: name of a binary rock. If an URL or pathname | ||
| 77 | -- to a binary rock is given, fetches and installs it. If a rockspec or a | ||
| 78 | -- source rock is given, forwards the request to the "build" command. | ||
| 79 | -- If a package name is given, forwards the request to "search" and, | ||
| 80 | -- if returned a result, installs the matching rock. | ||
| 81 | -- @param version string: When passing a package name, a version number | ||
| 82 | -- may also be given. | ||
| 83 | -- @return boolean or (nil, string): True if installation was | ||
| 84 | -- successful, nil and an error message otherwise. | ||
| 85 | function run(...) | ||
| 86 | local flags, name, version = util.parse_flags(...) | ||
| 87 | if type(name) ~= "string" then | ||
| 88 | return nil, "Argument missing, see help." | ||
| 89 | end | ||
| 90 | |||
| 91 | if name:match("%.rockspec$") or name:match("%.src%.rock$") then | ||
| 92 | local build = require("luarocks.build") | ||
| 93 | return build.run(name) | ||
| 94 | elseif name:match("%.rock$") then | ||
| 95 | return install_binary_rock(name) | ||
| 96 | else | ||
| 97 | local search = require("luarocks.search") | ||
| 98 | local results, err = search.find_suitable_rock(search.make_query(name, version)) | ||
| 99 | if err then | ||
| 100 | return nil, err | ||
| 101 | elseif type(results) == "string" then | ||
| 102 | local url = results | ||
| 103 | print("Installing "..url.."...") | ||
| 104 | return run(url) | ||
| 105 | else | ||
| 106 | print() | ||
| 107 | print("Could not determine which rock to install.") | ||
| 108 | print() | ||
| 109 | print("Search results:") | ||
| 110 | print("---------------") | ||
| 111 | search.print_results(results) | ||
| 112 | return nil, (next(results) and "Please narrow your query." or "No results found.") | ||
| 113 | end | ||
| 114 | end | ||
| 115 | end | ||
diff --git a/src/luarocks/list.lua b/src/luarocks/list.lua new file mode 100644 index 00000000..1251653c --- /dev/null +++ b/src/luarocks/list.lua | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | |||
| 2 | --- Module implementing the LuaRocks "list" command. | ||
| 3 | -- Lists currently installed rocks. | ||
| 4 | module("luarocks.list", package.seeall) | ||
| 5 | |||
| 6 | local search = require("luarocks.search") | ||
| 7 | local cfg = require("luarocks.cfg") | ||
| 8 | local util = require("luarocks.util") | ||
| 9 | local fs = require("luarocks.fs") | ||
| 10 | |||
| 11 | help_summary = "Lists currently installed rocks." | ||
| 12 | |||
| 13 | help = [[ | ||
| 14 | <argument> is a substring of a rock name to filter by. | ||
| 15 | ]] | ||
| 16 | |||
| 17 | --- Driver function for "list" command. | ||
| 18 | -- @param filter string or nil: A substring of a rock name to filter by. | ||
| 19 | -- @param version string or nil: a version may also be passed. | ||
| 20 | -- @return boolean: True if succeeded, nil on errors. | ||
| 21 | function run(...) | ||
| 22 | local flags, filter, version = util.parse_flags(...) | ||
| 23 | local results = {} | ||
| 24 | local query = search.make_query(filter or "", version) | ||
| 25 | query.exact_name = false | ||
| 26 | for _, tree in ipairs(cfg.rocks_trees) do | ||
| 27 | search.manifest_search(results, fs.make_path(tree, "rocks"), query) | ||
| 28 | end | ||
| 29 | print() | ||
| 30 | print("Installed rocks:") | ||
| 31 | print("----------------") | ||
| 32 | print() | ||
| 33 | search.print_results(results, false) | ||
| 34 | return true | ||
| 35 | end | ||
diff --git a/src/luarocks/make.lua b/src/luarocks/make.lua new file mode 100644 index 00000000..1c116cdb --- /dev/null +++ b/src/luarocks/make.lua | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | |||
| 2 | --- Module implementing the LuaRocks "make" command. | ||
| 3 | -- Builds sources in the current directory, but unlike "build", | ||
| 4 | -- it does not fetch sources, etc., assuming everything is | ||
| 5 | -- available in the current directory. | ||
| 6 | module("luarocks.make", package.seeall) | ||
| 7 | |||
| 8 | local build = require("luarocks.build") | ||
| 9 | local fs = require("luarocks.fs") | ||
| 10 | local util = require("luarocks.util") | ||
| 11 | |||
| 12 | help_summary = "Compile package in current directory using a rockspec." | ||
| 13 | help_arguments = "[<rockspec>]" | ||
| 14 | help = [[ | ||
| 15 | Builds sources in the current directory, but unlike "build", | ||
| 16 | it does not fetch sources, etc., assuming everything is | ||
| 17 | available in the current directory. If no argument is given, | ||
| 18 | look for a rockspec in the current directory. If more than one | ||
| 19 | is found, you must specify which to use, through the command-line. | ||
| 20 | |||
| 21 | This command is useful as a tool for debugging rockspecs. | ||
| 22 | To install rocks, you'll normally want to use the "install" and | ||
| 23 | "build" commands. See the help on those for details. | ||
| 24 | ]] | ||
| 25 | |||
| 26 | --- Driver function for "make" command. | ||
| 27 | -- @param name string: A local rockspec. | ||
| 28 | -- @return boolean or (nil, string): True if build was successful; nil and an | ||
| 29 | -- error message otherwise. | ||
| 30 | function run(...) | ||
| 31 | local flags, rockspec = util.parse_flags(...) | ||
| 32 | assert(type(rockspec) == "string" or not rockspec) | ||
| 33 | |||
| 34 | if not rockspec then | ||
| 35 | local files = fs.dir(fs.current_dir()) | ||
| 36 | for _, file in pairs(files) do | ||
| 37 | if file:match(".rockspec$") then | ||
| 38 | if rockspec then | ||
| 39 | return nil, "Please specify which rockspec file to use." | ||
| 40 | else | ||
| 41 | rockspec = file | ||
| 42 | end | ||
| 43 | end | ||
| 44 | end | ||
| 45 | if not rockspec then | ||
| 46 | return nil, "Argument missing: please specify a rockspec to use on current directory." | ||
| 47 | end | ||
| 48 | end | ||
| 49 | if not rockspec:match("%.rockspec$") then | ||
| 50 | return nil, "Invalid argument: 'make' takes a rockspec as a parameter. See help." | ||
| 51 | end | ||
| 52 | |||
| 53 | return build.build_rockspec(rockspec, false, true) | ||
| 54 | end | ||
diff --git a/src/luarocks/make_manifest.lua b/src/luarocks/make_manifest.lua new file mode 100644 index 00000000..6edbd14b --- /dev/null +++ b/src/luarocks/make_manifest.lua | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | |||
| 2 | --- Module implementing the luarocks-admin "make_manifest" command. | ||
| 3 | -- Compile a manifest file for a repository. | ||
| 4 | module("luarocks.make_manifest", package.seeall) | ||
| 5 | |||
| 6 | local manif = require("luarocks.manif") | ||
| 7 | local cfg = require("luarocks.cfg") | ||
| 8 | |||
| 9 | help_summary = "Compile a manifest file for a repository." | ||
| 10 | |||
| 11 | help = [[ | ||
| 12 | <argument>, if given, is a local repository pathname. | ||
| 13 | ]] | ||
| 14 | |||
| 15 | --- Driver function for "make_manifest" command. | ||
| 16 | -- @param repo string or nil: Pathname of a local repository. If not given, | ||
| 17 | -- the default local repository configured as cfg.rocks_dir is used. | ||
| 18 | -- @return boolean or (nil, string): True if manifest was generated, | ||
| 19 | -- or nil and an error message. | ||
| 20 | function run(repo) | ||
| 21 | assert(type(repo) == "string" or not repo) | ||
| 22 | repo = repo or cfg.rocks_dir | ||
| 23 | |||
| 24 | print("Making manifest for "..repo) | ||
| 25 | |||
| 26 | ok = manif.make_manifest(repo) | ||
| 27 | if ok then | ||
| 28 | print("Generating index.html for "..repo) | ||
| 29 | manif.make_index(repo) | ||
| 30 | end | ||
| 31 | return ok | ||
| 32 | end | ||
diff --git a/src/luarocks/manif.lua b/src/luarocks/manif.lua new file mode 100644 index 00000000..98772319 --- /dev/null +++ b/src/luarocks/manif.lua | |||
| @@ -0,0 +1,439 @@ | |||
| 1 | |||
| 2 | --- Functions for querying and manipulating manifest files. | ||
| 3 | module("luarocks.manif", package.seeall) | ||
| 4 | |||
| 5 | local util = require("luarocks.util") | ||
| 6 | local fs = require("luarocks.fs") | ||
| 7 | local search = require("luarocks.search") | ||
| 8 | local rep = require("luarocks.rep") | ||
| 9 | local deps = require("luarocks.deps") | ||
| 10 | local cfg = require("luarocks.cfg") | ||
| 11 | local persist = require("luarocks.persist") | ||
| 12 | local fetch = require("luarocks.fetch") | ||
| 13 | local type_check = require("luarocks.type_check") | ||
| 14 | |||
| 15 | manifest_cache = {} | ||
| 16 | |||
| 17 | --- Get all versions of a package listed in a manifest file. | ||
| 18 | -- @param name string: a package name. | ||
| 19 | -- @param manifest table or nil: a manifest table; if not given, the | ||
| 20 | -- default local manifest table is used. | ||
| 21 | -- @return table: An array of strings listing installed | ||
| 22 | -- versions of a package. | ||
| 23 | function get_versions(name, manifest) | ||
| 24 | assert(type(name) == "string") | ||
| 25 | assert(type(manifest) == "table" or not manifest) | ||
| 26 | |||
| 27 | if not manifest then | ||
| 28 | manifest = load_local_manifest(cfg.rocks_dir) | ||
| 29 | if not manifest then | ||
| 30 | return {} | ||
| 31 | end | ||
| 32 | end | ||
| 33 | |||
| 34 | local item = manifest.repository[name] | ||
| 35 | if item then | ||
| 36 | return util.keys(item) | ||
| 37 | end | ||
| 38 | return {} | ||
| 39 | end | ||
| 40 | |||
| 41 | --- Back-end function that actually loads the manifest | ||
| 42 | -- and stores it in the manifest cache. | ||
| 43 | -- @param file string: The local filename of the manifest file. | ||
| 44 | -- @param repo_url string: The repository identifier. | ||
| 45 | local function manifest_loader(file, repo_url, quick) | ||
| 46 | local manifest = persist.load_into_table(file) | ||
| 47 | if not manifest then | ||
| 48 | return nil, "Failed loading manifest for "..repo_url | ||
| 49 | end | ||
| 50 | if not quick then | ||
| 51 | local ok, err = type_check.type_check_manifest(manifest) | ||
| 52 | if not ok then | ||
| 53 | return nil, "Error checking manifest: "..err | ||
| 54 | end | ||
| 55 | end | ||
| 56 | |||
| 57 | manifest_cache[repo_url] = manifest | ||
| 58 | return manifest | ||
| 59 | end | ||
| 60 | |||
| 61 | --- Load a local or remote manifest describing a repository. | ||
| 62 | -- All functions that use manifest tables assume they were obtained | ||
| 63 | -- through either this function or load_local_manifest. | ||
| 64 | -- @param repo_url string: URL or pathname for the repository. | ||
| 65 | -- @return table or (nil, string): A table representing the manifest, | ||
| 66 | -- or nil followed by an error message. | ||
| 67 | function load_manifest(repo_url) | ||
| 68 | assert(type(repo_url) == "string") | ||
| 69 | |||
| 70 | if manifest_cache[repo_url] then | ||
| 71 | return manifest_cache[repo_url] | ||
| 72 | end | ||
| 73 | |||
| 74 | local protocol, pathname = fs.split_url(repo_url) | ||
| 75 | if protocol == "file" then | ||
| 76 | pathname = fs.make_path(pathname, "manifest") | ||
| 77 | else | ||
| 78 | local url = fs.make_path(repo_url, "manifest") | ||
| 79 | local name = repo_url:gsub("[/:]","_") | ||
| 80 | local file, dir = fetch.fetch_url_at_temp_dir(url, "luarocks-manifest-"..name) | ||
| 81 | if not file then | ||
| 82 | return nil, "Failed fetching manifest for "..repo_url | ||
| 83 | end | ||
| 84 | pathname = file | ||
| 85 | end | ||
| 86 | return manifest_loader(pathname, repo_url) | ||
| 87 | end | ||
| 88 | |||
| 89 | --- Load a local manifest describing a repository. | ||
| 90 | -- All functions that use manifest tables assume they were obtained | ||
| 91 | -- through either this function or load_manifest. | ||
| 92 | -- @param repo_url string: URL or pathname for the repository. | ||
| 93 | -- @return table or (nil, string): A table representing the manifest, | ||
| 94 | -- or nil followed by an error message. | ||
| 95 | function load_local_manifest(repo_url) | ||
| 96 | assert(type(repo_url) == "string") | ||
| 97 | |||
| 98 | if manifest_cache[repo_url] then | ||
| 99 | return manifest_cache[repo_url] | ||
| 100 | end | ||
| 101 | |||
| 102 | local pathname = fs.make_path(repo_url, "manifest") | ||
| 103 | |||
| 104 | return manifest_loader(pathname, repo_url, true) | ||
| 105 | end | ||
| 106 | |||
| 107 | --- Sort function for ordering rock identifiers in a manifest's | ||
| 108 | -- modules table. Rocks are ordered alphabetically by name, and then | ||
| 109 | -- by version which greater first. | ||
| 110 | -- @param a string: Version to compare. | ||
| 111 | -- @param b string: Version to compare. | ||
| 112 | -- @return boolean: The comparison result, according to the | ||
| 113 | -- rule outlined above. | ||
| 114 | local function sort_pkgs(a, b) | ||
| 115 | assert(type(a) == "string") | ||
| 116 | assert(type(b) == "string") | ||
| 117 | |||
| 118 | local na, va = a:match("(.*)/(.*)$") | ||
| 119 | local nb, vb = b:match("(.*)/(.*)$") | ||
| 120 | |||
| 121 | return (na == nb) and deps.compare_versions(va, vb) or na < nb | ||
| 122 | end | ||
| 123 | |||
| 124 | --- Output a table listing items of a package. | ||
| 125 | -- @param itemsfn function: a function for obtaining items of a package. | ||
| 126 | -- pkg and version will be passed to it; it should return a table with | ||
| 127 | -- items as keys. | ||
| 128 | -- @param pkg string: package name | ||
| 129 | -- @param version string: package version | ||
| 130 | -- @param tbl table: the package matching table: keys should be item names | ||
| 131 | -- and values arrays of strings with packages names in "name/version" format. | ||
| 132 | local function store_package_items(itemsfn, pkg, version, tbl) | ||
| 133 | assert(type(itemsfn) == "function") | ||
| 134 | assert(type(pkg) == "string") | ||
| 135 | assert(type(version) == "string") | ||
| 136 | assert(type(tbl) == "table") | ||
| 137 | |||
| 138 | local path = pkg.."/"..version | ||
| 139 | local result = {} | ||
| 140 | for item, _ in pairs(itemsfn(pkg, version)) do | ||
| 141 | table.insert(result, item) | ||
| 142 | if not tbl[item] then | ||
| 143 | tbl[item] = {} | ||
| 144 | end | ||
| 145 | table.insert(tbl[item], path) | ||
| 146 | end | ||
| 147 | return result | ||
| 148 | end | ||
| 149 | |||
| 150 | --- Sort items of a package matching table by version number (higher versions first). | ||
| 151 | -- @param tbl table: the package matching table: keys should be strings | ||
| 152 | -- and values arrays of strings with packages names in "name/version" format. | ||
| 153 | local function sort_package_matching_table(tbl) | ||
| 154 | assert(type(tbl) == "table") | ||
| 155 | |||
| 156 | if next(tbl) then | ||
| 157 | for item, pkgs in pairs(tbl) do | ||
| 158 | if #pkgs > 1 then | ||
| 159 | table.sort(pkgs, sort_pkgs) | ||
| 160 | -- Remove duplicates from the sorted array. | ||
| 161 | local prev = nil | ||
| 162 | local i = 1 | ||
| 163 | while pkgs[i] do | ||
| 164 | local curr = pkgs[i] | ||
| 165 | if curr == prev then | ||
| 166 | table.remove(pkgs, i) | ||
| 167 | else | ||
| 168 | prev = curr | ||
| 169 | i = i + 1 | ||
| 170 | end | ||
| 171 | end | ||
| 172 | end | ||
| 173 | end | ||
| 174 | end | ||
| 175 | end | ||
| 176 | |||
| 177 | --- Commit manifest to disk in given local repository. | ||
| 178 | -- @param repo string: The directory of the local repository. | ||
| 179 | -- @param manifest table: The manifest table | ||
| 180 | -- @return boolean or (nil, string): true if successful, or nil and a | ||
| 181 | -- message in case of errors. | ||
| 182 | local function save_manifest(repo, manifest) | ||
| 183 | assert(type(repo) == "string") | ||
| 184 | assert(type(manifest) == "table") | ||
| 185 | |||
| 186 | local filename = fs.make_path(repo, "manifest") | ||
| 187 | return persist.save_from_table(filename, manifest) | ||
| 188 | end | ||
| 189 | |||
| 190 | --- Process the dependencies of a package to determine its dependency | ||
| 191 | -- chain for loading modules. | ||
| 192 | -- @param name string: Package name. | ||
| 193 | -- @param version string: Package version. | ||
| 194 | -- @return (table, table): A table listing dependencies as string-string pairs | ||
| 195 | -- of names and versions, and a similar table of missing dependencies. | ||
| 196 | local function update_dependencies(manifest) | ||
| 197 | for pkg, versions in pairs(manifest.repository) do | ||
| 198 | for version, repos in pairs(versions) do | ||
| 199 | local current = pkg.." "..version | ||
| 200 | for _, repo in ipairs(repos) do | ||
| 201 | if repo.arch == "installed" then | ||
| 202 | local missing | ||
| 203 | repo.dependencies, missing = deps.scan_deps({}, {}, manifest, pkg, version) | ||
| 204 | repo.dependencies[pkg] = nil | ||
| 205 | if missing then | ||
| 206 | for miss, _ in pairs(missing) do | ||
| 207 | if miss == current then | ||
| 208 | print("Tree inconsistency detected: "..current.." has no rockspec.") | ||
| 209 | else | ||
| 210 | print("Missing dependency for "..pkg.." "..version..": "..miss) | ||
| 211 | end | ||
| 212 | end | ||
| 213 | end | ||
| 214 | end | ||
| 215 | end | ||
| 216 | end | ||
| 217 | end | ||
| 218 | end | ||
| 219 | |||
| 220 | --- Store search results in a manifest table. | ||
| 221 | -- @param results table: The search results as returned by search.disk_search. | ||
| 222 | -- @param manifest table: A manifest table (must contain repository, modules, commands tables). | ||
| 223 | local function store_results(results, manifest) | ||
| 224 | assert(type(results) == "table") | ||
| 225 | assert(type(manifest) == "table") | ||
| 226 | |||
| 227 | for pkg, versions in pairs(results) do | ||
| 228 | local pkgtable = manifest.repository[pkg] or {} | ||
| 229 | for version, repos in pairs(versions) do | ||
| 230 | local versiontable = {} | ||
| 231 | for _, repo in ipairs(repos) do | ||
| 232 | local repotable = {} | ||
| 233 | repotable.arch = repo.arch | ||
| 234 | if repo.arch == "installed" then | ||
| 235 | repotable.modules = store_package_items(rep.package_modules, pkg, version, manifest.modules) | ||
| 236 | repotable.commands = store_package_items(rep.package_commands, pkg, version, manifest.commands) | ||
| 237 | end | ||
| 238 | table.insert(versiontable, repotable) | ||
| 239 | end | ||
| 240 | pkgtable[version] = versiontable | ||
| 241 | end | ||
| 242 | manifest.repository[pkg] = pkgtable | ||
| 243 | end | ||
| 244 | update_dependencies(manifest) | ||
| 245 | sort_package_matching_table(manifest.modules) | ||
| 246 | sort_package_matching_table(manifest.commands) | ||
| 247 | end | ||
| 248 | |||
| 249 | --- Load a manifest file from a local repository and add to the repository | ||
| 250 | -- information with regard to the given name and version. | ||
| 251 | -- A file called 'manifest' will be written in the root of the given | ||
| 252 | -- repository directory. | ||
| 253 | -- @param name string: Name of a package from the repository. | ||
| 254 | -- @param version string: Version of a package from the repository. | ||
| 255 | -- @param repo string or nil: Pathname of a local repository. If not given, | ||
| 256 | -- the default local repository configured as cfg.rocks_dir is used. | ||
| 257 | -- @return boolean or (nil, string): True if manifest was generated, | ||
| 258 | -- or nil and an error message. | ||
| 259 | function update_manifest(name, version, repo) | ||
| 260 | assert(type(name) == "string") | ||
| 261 | assert(type(version) == "string") | ||
| 262 | assert(type(repo) == "string" or not repo) | ||
| 263 | repo = repo or cfg.rocks_dir | ||
| 264 | |||
| 265 | print("Updating manifest for "..repo) | ||
| 266 | |||
| 267 | local manifest, err = load_manifest(repo) | ||
| 268 | if not manifest then | ||
| 269 | print("No existing manifest. Attempting to rebuild...") | ||
| 270 | local ok, err = make_manifest(repo) | ||
| 271 | if not ok then | ||
| 272 | return nil, err | ||
| 273 | end | ||
| 274 | manifest, err = load_manifest(repo) | ||
| 275 | if not manifest then | ||
| 276 | return nil, err | ||
| 277 | end | ||
| 278 | end | ||
| 279 | |||
| 280 | local results = {[name] = {[version] = {{arch = "installed", repo = repo}}}} | ||
| 281 | |||
| 282 | store_results(results, manifest) | ||
| 283 | return save_manifest(repo, manifest) | ||
| 284 | end | ||
| 285 | |||
| 286 | --- Scan a LuaRocks repository and output a manifest file. | ||
| 287 | -- A file called 'manifest' will be written in the root of the given | ||
| 288 | -- repository directory. | ||
| 289 | -- @param repo A local repository directory. | ||
| 290 | -- @return boolean or (nil, string): True if manifest was generated, | ||
| 291 | -- or nil and an error message. | ||
| 292 | function make_manifest(repo) | ||
| 293 | assert(type(repo) == "string") | ||
| 294 | |||
| 295 | if not fs.is_dir(repo) then | ||
| 296 | return nil, "Cannot access repository at "..repo | ||
| 297 | end | ||
| 298 | |||
| 299 | local query = search.make_query("") | ||
| 300 | query.exact_name = false | ||
| 301 | query.arch = "any" | ||
| 302 | local results = search.disk_search(repo, query) | ||
| 303 | |||
| 304 | local manifest = { repository = {}, modules = {}, commands = {} } | ||
| 305 | manifest_cache[repo] = manifest | ||
| 306 | store_results(results, manifest) | ||
| 307 | return save_manifest(repo, manifest) | ||
| 308 | end | ||
| 309 | |||
| 310 | local index_header = [[ | ||
| 311 | <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> | ||
| 312 | <html> | ||
| 313 | <head> | ||
| 314 | <title>Available rocks</title> | ||
| 315 | <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"> | ||
| 316 | <style> | ||
| 317 | body { | ||
| 318 | background-color: white; | ||
| 319 | font-family: "bitstream vera sans", "verdana", "sans"; | ||
| 320 | font-size: 14px; | ||
| 321 | } | ||
| 322 | a { | ||
| 323 | color: #0000c0; | ||
| 324 | text-decoration: none; | ||
| 325 | } | ||
| 326 | a:hover { | ||
| 327 | text-decoration: underline; | ||
| 328 | } | ||
| 329 | td.main { | ||
| 330 | border-style: none; | ||
| 331 | } | ||
| 332 | blockquote { | ||
| 333 | font-size: 12px; | ||
| 334 | } | ||
| 335 | td.package { | ||
| 336 | background-color: #f0f0f0; | ||
| 337 | vertical-align: top; | ||
| 338 | } | ||
| 339 | td.spacer { | ||
| 340 | height: 5px; | ||
| 341 | } | ||
| 342 | td.version { | ||
| 343 | background-color: #d0d0d0; | ||
| 344 | vertical-align: top; | ||
| 345 | text-align: left; | ||
| 346 | padding: 5px; | ||
| 347 | width: 100px; | ||
| 348 | } | ||
| 349 | p.manifest { | ||
| 350 | font-size: 8px; | ||
| 351 | } | ||
| 352 | </style> | ||
| 353 | </head> | ||
| 354 | <body> | ||
| 355 | <h1>Available rocks</h1> | ||
| 356 | <p> | ||
| 357 | Lua modules avaliable from this location for use with <a href="http://www.luarocks.org">LuaRocks</a>: | ||
| 358 | </p> | ||
| 359 | <table class="main"> | ||
| 360 | ]] | ||
| 361 | |||
| 362 | local index_package_start = [[ | ||
| 363 | <td class="package"> | ||
| 364 | <p><a name="$anchor"></a><b>$package</b> - $summary<br/> | ||
| 365 | </p><blockquote><p>$detailed<br/> | ||
| 366 | <font size="-1"><a href="$original">latest sources</a> | <a href="$homepage">project homepage</a> | License: $license</font></p> | ||
| 367 | </blockquote></a></td> | ||
| 368 | <td class="version"> | ||
| 369 | ]] | ||
| 370 | |||
| 371 | local index_package_end = [[ | ||
| 372 | </td></tr> | ||
| 373 | <tr><td colspan="2" class="spacer"></td></tr> | ||
| 374 | ]] | ||
| 375 | |||
| 376 | local index_footer = [[ | ||
| 377 | </table> | ||
| 378 | <p class="manifest"> | ||
| 379 | <a href="manifest">manifest file</a> | ||
| 380 | </p> | ||
| 381 | </body> | ||
| 382 | </html> | ||
| 383 | ]] | ||
| 384 | |||
| 385 | function make_index(repo) | ||
| 386 | if not fs.is_dir(repo) then | ||
| 387 | return nil, "Cannot access repository at "..repo | ||
| 388 | end | ||
| 389 | local manifest = load_manifest(repo) | ||
| 390 | files = fs.find(repo) | ||
| 391 | local out = io.open(fs.make_path(repo, "index.html"), "w") | ||
| 392 | out:write(index_header) | ||
| 393 | for package, version_list in util.sortedpairs(manifest.repository) do | ||
| 394 | local latest_rockspec = nil | ||
| 395 | local output = index_package_start | ||
| 396 | for version, data in util.sortedpairs(version_list, deps.compare_versions) do | ||
| 397 | local out_versions = {} | ||
| 398 | local arches = 0 | ||
| 399 | output = output..version | ||
| 400 | local sep = ': ' | ||
| 401 | for _, item in ipairs(data) do | ||
| 402 | output = output .. sep .. '<a href="$url">'..item.arch..'</a>' | ||
| 403 | sep = ', ' | ||
| 404 | if item.arch == 'rockspec' then | ||
| 405 | local rs = ("%s-%s.rockspec"):format(package, version) | ||
| 406 | if not latest_rockspec then latest_rockspec = rs end | ||
| 407 | output = output:gsub("$url", rs) | ||
| 408 | else | ||
| 409 | output = output:gsub("$url", ("%s-%s.%s.rock"):format(package, version, item.arch)) | ||
| 410 | end | ||
| 411 | end | ||
| 412 | output = output .. '<br/>' | ||
| 413 | output = output:gsub("$na", arches) | ||
| 414 | end | ||
| 415 | output = output .. index_package_end | ||
| 416 | if latest_rockspec then | ||
| 417 | local rockspec = persist.load_into_table(fs.make_path(repo, latest_rockspec)) | ||
| 418 | local vars = { | ||
| 419 | anchor = package, | ||
| 420 | package = rockspec.package, | ||
| 421 | original = rockspec.source.url, | ||
| 422 | summary = rockspec.description.summary or "", | ||
| 423 | detailed = rockspec.description.detailed or "", | ||
| 424 | license = rockspec.description.license or "N/A", | ||
| 425 | homepage = rockspec.description.homepage or "" | ||
| 426 | } | ||
| 427 | vars.detailed = vars.detailed:gsub("\n\n", "</p><p>"):gsub("%s+", " ") | ||
| 428 | output = output:gsub("$(%w+)", vars) | ||
| 429 | else | ||
| 430 | output = output:gsub("$anchor", package) | ||
| 431 | output = output:gsub("$package", package) | ||
| 432 | output = output:gsub("$(%w+)", "") | ||
| 433 | end | ||
| 434 | out:write(output) | ||
| 435 | end | ||
| 436 | out:write(index_footer) | ||
| 437 | out:close() | ||
| 438 | end | ||
| 439 | |||
diff --git a/src/luarocks/pack.lua b/src/luarocks/pack.lua new file mode 100644 index 00000000..3f64b1c0 --- /dev/null +++ b/src/luarocks/pack.lua | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | |||
| 2 | --- Module implementing the LuaRocks "pack" command. | ||
| 3 | -- Creates a rock, packing sources or binaries. | ||
| 4 | module("luarocks.pack", package.seeall) | ||
| 5 | |||
| 6 | local path = require("luarocks.path") | ||
| 7 | local rep = require("luarocks.rep") | ||
| 8 | local fetch = require("luarocks.fetch") | ||
| 9 | local fs = require("luarocks.fs") | ||
| 10 | local cfg = require("luarocks.cfg") | ||
| 11 | local util = require("luarocks.util") | ||
| 12 | |||
| 13 | help_summary = "Create a rock, packing sources or binaries." | ||
| 14 | help_arguments = "{<rockspec>|<name> [<version>]}" | ||
| 15 | help = [[ | ||
| 16 | Argument may be a rockspec file, for creating a source rock, | ||
| 17 | or the name of an installed package, for creating a binary rock. | ||
| 18 | In the latter case, the app version may be given as a second | ||
| 19 | argument. | ||
| 20 | ]] | ||
| 21 | |||
| 22 | --- Create a source rock. | ||
| 23 | -- Packages a rockspec and its required source files in a rock | ||
| 24 | -- file with the .src.rock extension, which can later be built and | ||
| 25 | -- installed with the "build" command. | ||
| 26 | -- @param rockspec_file string: An URL or pathname for a rockspec file. | ||
| 27 | -- @return string or (nil, string): The filename of the resulting | ||
| 28 | -- .src.rock file; or nil and an error message. | ||
| 29 | local function pack_source_rock(rockspec_file) | ||
| 30 | assert(type(rockspec_file) == "string") | ||
| 31 | |||
| 32 | rockspec_file = fs.absolute_name(rockspec_file) | ||
| 33 | local rockspec, err = fetch.load_rockspec(rockspec_file) | ||
| 34 | if err then | ||
| 35 | return nil, "Error loading rockspec: "..err | ||
| 36 | end | ||
| 37 | |||
| 38 | local name_version = rockspec.name .. "-" .. rockspec.version | ||
| 39 | local rock_file = fs.absolute_name(name_version .. ".src.rock") | ||
| 40 | |||
| 41 | local source_file, dir = fetch.fetch_sources(rockspec, false) | ||
| 42 | if not source_file then | ||
| 43 | return nil, dir | ||
| 44 | end | ||
| 45 | fs.change_dir(dir) | ||
| 46 | |||
| 47 | fs.delete(rock_file) | ||
| 48 | fs.copy(rockspec_file, dir) | ||
| 49 | if not fs.zip(rock_file, fs.base_name(rockspec_file), fs.base_name(source_file)) then | ||
| 50 | return nil, "Failed packing "..rock_file | ||
| 51 | end | ||
| 52 | fs.pop_dir() | ||
| 53 | |||
| 54 | return rock_file | ||
| 55 | end | ||
| 56 | |||
| 57 | -- @param name string: Name of package to pack. | ||
| 58 | -- @param version string or nil: A version number may also be passed. | ||
| 59 | -- @return string or (nil, string): The filename of the resulting | ||
| 60 | -- .src.rock file; or nil and an error message. | ||
| 61 | local function pack_binary_rock(name, version) | ||
| 62 | assert(type(name) == "string") | ||
| 63 | assert(type(version) == "string" or not version) | ||
| 64 | |||
| 65 | local versions = rep.get_versions(name) | ||
| 66 | |||
| 67 | if not versions then | ||
| 68 | return nil, "'"..name.."' does not seem to be an installed rock." | ||
| 69 | end | ||
| 70 | if not version then | ||
| 71 | if #versions > 1 then | ||
| 72 | return nil, "Please specify which version of '"..name.."' to pack." | ||
| 73 | end | ||
| 74 | version = versions[1] | ||
| 75 | end | ||
| 76 | if not version:match("[^-]+%-%d+") then | ||
| 77 | return nil, "Expected version "..version.." in version-revision format." | ||
| 78 | end | ||
| 79 | local prefix = path.install_dir(name, version) | ||
| 80 | if not fs.exists(prefix) then | ||
| 81 | return nil, "'"..name.." "..version.."' does not seem to be an installed rock." | ||
| 82 | end | ||
| 83 | local name_version = name .. "-" .. version | ||
| 84 | local rock_file = fs.absolute_name(name_version .. "."..cfg.arch..".rock") | ||
| 85 | fs.change_dir(prefix) | ||
| 86 | if not rep.is_binary_rock(name, version) then | ||
| 87 | rock_file = rock_file:gsub("%."..cfg.arch:gsub("%-","%%-").."%.", ".all.") | ||
| 88 | end | ||
| 89 | fs.delete(rock_file) | ||
| 90 | if not fs.zip(rock_file, unpack(fs.dir())) then | ||
| 91 | return nil, "Failed packing "..rock_file | ||
| 92 | end | ||
| 93 | fs.pop_dir() | ||
| 94 | return rock_file | ||
| 95 | end | ||
| 96 | |||
| 97 | --- Driver function for the "pack" command. | ||
| 98 | -- @param arg string: may be a rockspec file, for creating a source rock, | ||
| 99 | -- or the name of an installed package, for creating a binary rock. | ||
| 100 | -- @param version string or nil: if the name of a package is given, a | ||
| 101 | -- version may also be passed. | ||
| 102 | -- @return boolean or (nil, string): true if successful or nil followed | ||
| 103 | -- by an error message. | ||
| 104 | function run(...) | ||
| 105 | local flags, arg, version = util.parse_flags(...) | ||
| 106 | assert(type(version) == "string" or not version) | ||
| 107 | if type(arg) ~= "string" then | ||
| 108 | return nil, "Argument missing, see help." | ||
| 109 | end | ||
| 110 | |||
| 111 | if arg:match(".*%.rockspec") then | ||
| 112 | return pack_source_rock(arg) | ||
| 113 | else | ||
| 114 | return pack_binary_rock(arg, version) | ||
| 115 | end | ||
| 116 | end | ||
diff --git a/src/luarocks/path.lua b/src/luarocks/path.lua new file mode 100644 index 00000000..e236db01 --- /dev/null +++ b/src/luarocks/path.lua | |||
| @@ -0,0 +1,224 @@ | |||
| 1 | |||
| 2 | --- Path and filename handling functions. | ||
| 3 | -- All paths are configured in this module, making it a single | ||
| 4 | -- point where the layout of the local installation is defined in LuaRocks. | ||
| 5 | module("luarocks.path", package.seeall) | ||
| 6 | |||
| 7 | local fs = require("luarocks.fs") | ||
| 8 | local cfg = require("luarocks.cfg") | ||
| 9 | |||
| 10 | --- Infer rockspec filename from a rock filename. | ||
| 11 | -- @param rock_name string: Pathname of a rock file. | ||
| 12 | -- @return string: Filename of the rockspec, without path. | ||
| 13 | function rockspec_name_from_rock(rock_name) | ||
| 14 | assert(type(rock_name) == "string") | ||
| 15 | local base_name = fs.base_name(rock_name) | ||
| 16 | return base_name:match("(.*)%.[^.]*.rock") .. ".rockspec" | ||
| 17 | end | ||
| 18 | |||
| 19 | --- Get the repository directory for all versions of a package. | ||
| 20 | -- @param name string: The package name. | ||
| 21 | -- @return string: The resulting path -- does not guarantee that | ||
| 22 | -- @param repo string or nil: If given, specifies the local repository to use. | ||
| 23 | -- the package (and by extension, the path) exists. | ||
| 24 | function versions_dir(name, repo) | ||
| 25 | assert(type(name) == "string") | ||
| 26 | assert(not repo or type(repo) == "string") | ||
| 27 | |||
| 28 | return fs.make_path(repo or cfg.rocks_dir, name) | ||
| 29 | end | ||
| 30 | |||
| 31 | --- Get the local installation directory (prefix) for a package. | ||
| 32 | -- @param name string: The package name. | ||
| 33 | -- @param version string: The package version. | ||
| 34 | -- @param repo string or nil: If given, specifies the local repository to use. | ||
| 35 | -- @return string: The resulting path -- does not guarantee that | ||
| 36 | -- the package (and by extension, the path) exists. | ||
| 37 | function install_dir(name, version, repo) | ||
| 38 | assert(type(name) == "string") | ||
| 39 | assert(type(version) == "string") | ||
| 40 | assert(not repo or type(repo) == "string") | ||
| 41 | |||
| 42 | return fs.make_path(repo or cfg.rocks_dir, name, version) | ||
| 43 | end | ||
| 44 | |||
| 45 | --- Get the local filename of the rockspec of an installed rock. | ||
| 46 | -- @param name string: The package name. | ||
| 47 | -- @param version string: The package version. | ||
| 48 | -- @param repo string or nil: If given, specifies the local repository to use. | ||
| 49 | -- @return string: The resulting path -- does not guarantee that | ||
| 50 | -- the package (and by extension, the file) exists. | ||
| 51 | function rockspec_file(name, version, repo) | ||
| 52 | assert(type(name) == "string") | ||
| 53 | assert(type(version) == "string") | ||
| 54 | assert(not repo or type(repo) == "string") | ||
| 55 | |||
| 56 | return fs.make_path(repo or cfg.rocks_dir, name, version, name.."-"..version..".rockspec") | ||
| 57 | end | ||
| 58 | |||
| 59 | --- Get the local installation directory for C libraries of a package. | ||
| 60 | -- @param name string: The package name. | ||
| 61 | -- @param version string: The package version. | ||
| 62 | -- @param repo string or nil: If given, specifies the local repository to use. | ||
| 63 | -- @return string: The resulting path -- does not guarantee that | ||
| 64 | -- the package (and by extension, the path) exists. | ||
| 65 | function lib_dir(name, version, repo) | ||
| 66 | assert(type(name) == "string") | ||
| 67 | assert(type(version) == "string") | ||
| 68 | assert(not repo or type(repo) == "string") | ||
| 69 | |||
| 70 | return fs.make_path(repo or cfg.rocks_dir, name, version, "lib") | ||
| 71 | end | ||
| 72 | |||
| 73 | --- Get the local installation directory for Lua modules of a package. | ||
| 74 | -- @param name string: The package name. | ||
| 75 | -- @param version string: The package version. | ||
| 76 | -- @param repo string or nil: If given, specifies the local repository to use. | ||
| 77 | -- @return string: The resulting path -- does not guarantee that | ||
| 78 | -- the package (and by extension, the path) exists. | ||
| 79 | function lua_dir(name, version, repo) | ||
| 80 | assert(type(name) == "string") | ||
| 81 | assert(type(version) == "string") | ||
| 82 | assert(not repo or type(repo) == "string") | ||
| 83 | |||
| 84 | return fs.make_path(repo or cfg.rocks_dir, name, version, "lua") | ||
| 85 | end | ||
| 86 | |||
| 87 | --- Get the local installation directory for documentation of a package. | ||
| 88 | -- @param name string: The package name. | ||
| 89 | -- @param version string: The package version. | ||
| 90 | -- @param repo string or nil: If given, specifies the local repository to use. | ||
| 91 | -- @return string: The resulting path -- does not guarantee that | ||
| 92 | -- the package (and by extension, the path) exists. | ||
| 93 | function doc_dir(name, version, repo) | ||
| 94 | assert(type(name) == "string") | ||
| 95 | assert(type(version) == "string") | ||
| 96 | assert(not repo or type(repo) == "string") | ||
| 97 | |||
| 98 | return fs.make_path(repo or cfg.rocks_dir, name, version, "doc") | ||
| 99 | end | ||
| 100 | |||
| 101 | --- Get the local installation directory for configuration files of a package. | ||
| 102 | -- @param name string: The package name. | ||
| 103 | -- @param version string: The package version. | ||
| 104 | -- @param repo string or nil: If given, specifies the local repository to use. | ||
| 105 | -- @return string: The resulting path -- does not guarantee that | ||
| 106 | -- the package (and by extension, the path) exists. | ||
| 107 | function conf_dir(name, version, repo) | ||
| 108 | assert(type(name) == "string") | ||
| 109 | assert(type(version) == "string") | ||
| 110 | assert(not repo or type(repo) == "string") | ||
| 111 | |||
| 112 | return fs.make_path(repo or cfg.rocks_dir, name, version, "conf") | ||
| 113 | end | ||
| 114 | |||
| 115 | --- Get the local installation directory for command-line scripts | ||
| 116 | -- of a package. | ||
| 117 | -- @param name string: The package name. | ||
| 118 | -- @param version string: The package version. | ||
| 119 | -- @param repo string or nil: If given, specifies the local repository to use. | ||
| 120 | -- @return string: The resulting path -- does not guarantee that | ||
| 121 | -- the package (and by extension, the path) exists. | ||
| 122 | function bin_dir(name, version, repo) | ||
| 123 | assert(type(name) == "string") | ||
| 124 | assert(type(version) == "string") | ||
| 125 | assert(not repo or type(repo) == "string") | ||
| 126 | |||
| 127 | return fs.make_path(repo or cfg.rocks_dir, name, version, "bin") | ||
| 128 | end | ||
| 129 | |||
| 130 | --- Extract name, version and arch of a rock filename. | ||
| 131 | -- @param rock_file string: pathname of a rock | ||
| 132 | -- @return (string, string, string) or nil: name, version and arch | ||
| 133 | -- of rock, or nil if name could not be parsed | ||
| 134 | function parse_rock_name(rock_file) | ||
| 135 | assert(type(rock_file) == "string") | ||
| 136 | return fs.base_name(rock_file):match("(.*)-([^-]+-%d+)%.([^.]+)%.rock$") | ||
| 137 | end | ||
| 138 | |||
| 139 | --- Extract name and version of a rockspec filename. | ||
| 140 | -- @param rockspec_file string: pathname of a rockspec | ||
| 141 | -- @return (string, string) or nil: name and version | ||
| 142 | -- of rockspec, or nil if name could not be parsed | ||
| 143 | function parse_rockspec_name(rockspec_file) | ||
| 144 | assert(type(rockspec_file) == "string") | ||
| 145 | return fs.base_name(rockspec_file):match("(.*)-([^-]+-%d+)%.(rockspec)") | ||
| 146 | end | ||
| 147 | |||
| 148 | --- Make a rockspec or rock URL. | ||
| 149 | -- @param pathname string: Base URL or pathname. | ||
| 150 | -- @param name string: Package name. | ||
| 151 | -- @param version string: Package version. | ||
| 152 | -- @param arch string: Architecture identifier, or "rockspec" or "installed". | ||
| 153 | -- @return string: A URL or pathname following LuaRocks naming conventions. | ||
| 154 | function make_url(pathname, name, version, arch) | ||
| 155 | assert(type(pathname) == "string") | ||
| 156 | assert(type(name) == "string") | ||
| 157 | assert(type(version) == "string") | ||
| 158 | assert(type(arch) == "string") | ||
| 159 | |||
| 160 | local filename = name.."-"..version | ||
| 161 | if arch == "installed" then | ||
| 162 | filename = fs.make_path(name, version, filename..".rockspec") | ||
| 163 | elseif arch == "rockspec" then | ||
| 164 | filename = filename..".rockspec" | ||
| 165 | else | ||
| 166 | filename = filename.."."..arch..".rock" | ||
| 167 | end | ||
| 168 | return fs.make_path(pathname, filename) | ||
| 169 | end | ||
| 170 | |||
| 171 | --- Convert a pathname to a module identifier. | ||
| 172 | -- In Unix, for example, a path "foo/bar/baz.lua" is converted to | ||
| 173 | -- "foo.bar.baz"; "bla/init.lua" returns "bla"; "foo.so" returns "foo". | ||
| 174 | -- @param file string: Pathname of module | ||
| 175 | -- @return string: The module identifier, or nil if given path is | ||
| 176 | -- not a conformant module path (the function does not check if the | ||
| 177 | -- path actually exists). | ||
| 178 | function path_to_module(file) | ||
| 179 | assert(type(file) == "string") | ||
| 180 | |||
| 181 | local name = file:match("(.*)%."..cfg.lua_extension.."$") | ||
| 182 | if name then | ||
| 183 | name = name:gsub(fs.dir_separator, ".") | ||
| 184 | local init = name:match("(.*)%.init$") | ||
| 185 | if init then | ||
| 186 | name = init | ||
| 187 | end | ||
| 188 | else | ||
| 189 | name = file:match("(.*)%."..cfg.lib_extension.."$") | ||
| 190 | if name then | ||
| 191 | name = name:gsub(fs.dir_separator, ".") | ||
| 192 | end | ||
| 193 | end | ||
| 194 | return name | ||
| 195 | end | ||
| 196 | |||
| 197 | --- Obtain the directory name where a module should be stored. | ||
| 198 | -- For example, on Unix, "foo.bar.baz" will return "foo/bar". | ||
| 199 | -- @param mod string: A module name in Lua dot-separated format. | ||
| 200 | -- @return string: A directory name using the platform's separator. | ||
| 201 | function module_to_path(mod) | ||
| 202 | assert(type(mod) == "string") | ||
| 203 | return (mod:gsub("[^.]*$", ""):gsub("%.", fs.dir_separator)) | ||
| 204 | end | ||
| 205 | |||
| 206 | --- Set up path-related variables for a given rock. | ||
| 207 | -- Create a "variables" table in the rockspec table, containing | ||
| 208 | -- adjusted variables according to the configuration file. | ||
| 209 | -- @param rockspec table: The rockspec table. | ||
| 210 | function configure_paths(rockspec) | ||
| 211 | assert(type(rockspec) == "table") | ||
| 212 | local vars = {} | ||
| 213 | for k,v in pairs(cfg.variables) do | ||
| 214 | vars[k] = v | ||
| 215 | end | ||
| 216 | local name, version = rockspec.name, rockspec.version | ||
| 217 | vars.PREFIX = install_dir(name, version) | ||
| 218 | vars.LUADIR = lua_dir(name, version) | ||
| 219 | vars.LIBDIR = lib_dir(name, version) | ||
| 220 | vars.CONFDIR = conf_dir(name, version) | ||
| 221 | vars.BINDIR = bin_dir(name, version) | ||
| 222 | vars.DOCDIR = doc_dir(name, version) | ||
| 223 | rockspec.variables = vars | ||
| 224 | end | ||
diff --git a/src/luarocks/persist.lua b/src/luarocks/persist.lua new file mode 100644 index 00000000..4f69184c --- /dev/null +++ b/src/luarocks/persist.lua | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | |||
| 2 | --- Utility module for loading files into tables and | ||
| 3 | -- saving tables into files. | ||
| 4 | -- Implemented separately to avoid interdependencies, | ||
| 5 | -- as it is used in the bootstrapping stage of the cfg module. | ||
| 6 | module("luarocks.persist", package.seeall) | ||
| 7 | |||
| 8 | --- Load a Lua file containing assignments, storing them in a table. | ||
| 9 | -- The global environment is not propagated to the loaded file. | ||
| 10 | -- @param filename string: the name of the file. | ||
| 11 | -- @param tbl table or nil: if given, this table is used to store | ||
| 12 | -- loaded values. | ||
| 13 | -- @return table or (nil, string): a table with the file's assignments | ||
| 14 | -- as fields, or nil and a message in case of errors. | ||
| 15 | function load_into_table(filename, tbl) | ||
| 16 | assert(type(filename) == "string") | ||
| 17 | assert(type(tbl) == "table" or not tbl) | ||
| 18 | |||
| 19 | local chunk, err = loadfile(filename) | ||
| 20 | if not chunk then | ||
| 21 | return nil, err | ||
| 22 | end | ||
| 23 | local result = {} | ||
| 24 | if tbl then result = tbl end | ||
| 25 | setfenv(chunk, result) | ||
| 26 | chunk() | ||
| 27 | return result | ||
| 28 | end | ||
| 29 | |||
| 30 | --- Write a table as Lua code representing a table to disk | ||
| 31 | -- (that is, in curly brackets notation). | ||
| 32 | -- This function handles only numbers, strings and tables | ||
| 33 | -- are keys (tables are handled recursively). | ||
| 34 | -- @param out userdata: a file object, open for writing. | ||
| 35 | -- @param tbl table: the table to be written. | ||
| 36 | local function write_table(out, tbl) | ||
| 37 | out:write("{") | ||
| 38 | local size = table.getn(tbl) | ||
| 39 | local sep = "" | ||
| 40 | local i = 1 | ||
| 41 | for k, v in pairs(tbl) do | ||
| 42 | out:write(sep) | ||
| 43 | if type(k) == "number" then | ||
| 44 | if k ~= i then | ||
| 45 | out:write(tostring(k).."=") | ||
| 46 | else | ||
| 47 | i = i + 1 | ||
| 48 | end | ||
| 49 | elseif type(k) == "table" then | ||
| 50 | out:write("[") | ||
| 51 | write_table(out, k) | ||
| 52 | out:write("]=") | ||
| 53 | else | ||
| 54 | if k:match("^[a-z_]+$") then | ||
| 55 | out:write(k.."=") | ||
| 56 | else | ||
| 57 | out:write("['"..k:gsub("'", "\\'").."']=") | ||
| 58 | end | ||
| 59 | end | ||
| 60 | local typ = type(v) | ||
| 61 | if typ == "table" then | ||
| 62 | write_table(out, v) | ||
| 63 | elseif typ == "string" then | ||
| 64 | out:write("'"..v:gsub("'", "\\'").."'") | ||
| 65 | else | ||
| 66 | out:write(tostring(v)) | ||
| 67 | end | ||
| 68 | sep = ", " | ||
| 69 | end | ||
| 70 | out:write("}\n") | ||
| 71 | end | ||
| 72 | |||
| 73 | --- Save the contents of a table in a file. | ||
| 74 | -- Each element of the table is saved as a global assignment. | ||
| 75 | -- Only numbers, strings and tables (containing numbers, strings | ||
| 76 | -- or other recursively processed tables) are supported. | ||
| 77 | -- @return boolean or (nil, string): true if successful, or nil and a | ||
| 78 | -- message in case of errors. | ||
| 79 | function save_from_table(filename, tbl) | ||
| 80 | local out = io.open(filename, "w") | ||
| 81 | if not out then | ||
| 82 | return nil, "Cannot create file at "..filename | ||
| 83 | end | ||
| 84 | for k, v in pairs(tbl) do | ||
| 85 | out:write(k.." = ") | ||
| 86 | write_table(out, v) | ||
| 87 | out:write("\n") | ||
| 88 | end | ||
| 89 | out:close() | ||
| 90 | return true | ||
| 91 | end | ||
diff --git a/src/luarocks/remove.lua b/src/luarocks/remove.lua new file mode 100644 index 00000000..d7162336 --- /dev/null +++ b/src/luarocks/remove.lua | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | |||
| 2 | --- Module implementing the LuaRocks "remove" command. | ||
| 3 | -- Uninstalls rocks. | ||
| 4 | module("luarocks.remove", package.seeall) | ||
| 5 | |||
| 6 | local search = require("luarocks.search") | ||
| 7 | local deps = require("luarocks.deps") | ||
| 8 | local fetch = require("luarocks.fetch") | ||
| 9 | local rep = require("luarocks.rep") | ||
| 10 | local path = require("luarocks.path") | ||
| 11 | local util = require("luarocks.util") | ||
| 12 | local cfg = require("luarocks.cfg") | ||
| 13 | local manif = require("luarocks.manif") | ||
| 14 | |||
| 15 | help_summary = "Uninstall a rock." | ||
| 16 | help_arguments = "[--force] <name> [<version>]" | ||
| 17 | help = [[ | ||
| 18 | Argument is the name of a rock to be uninstalled. | ||
| 19 | If a version is not given, try to remove all versions at once. | ||
| 20 | Will only perform the removal if it does not break dependencies. | ||
| 21 | To override this check and force the removal, use --force. | ||
| 22 | ]] | ||
| 23 | |||
| 24 | --- Obtain a list of packages that depend on the given set of packages | ||
| 25 | -- (where all packages of the set are versions of one program). | ||
| 26 | -- @param name string: the name of a program | ||
| 27 | -- @param versions array of string: the versions to be deleted. | ||
| 28 | -- @return array of string: an empty table if no packages depend on any | ||
| 29 | -- of the given list, or an array of strings in "name/version" format. | ||
| 30 | local function check_dependents(name, versions) | ||
| 31 | local dependents = {} | ||
| 32 | local blacklist = {} | ||
| 33 | blacklist[name] = {} | ||
| 34 | for version, _ in pairs(versions) do | ||
| 35 | blacklist[name][version] = true | ||
| 36 | end | ||
| 37 | local local_rocks = {} | ||
| 38 | local query_all = search.make_query("") | ||
| 39 | query_all.exact_name = false | ||
| 40 | search.manifest_search(local_rocks, cfg.rocks_dir, query_all) | ||
| 41 | local_rocks[name] = nil | ||
| 42 | for rock_name, rock_versions in pairs(local_rocks) do | ||
| 43 | for rock_version, _ in pairs(rock_versions) do | ||
| 44 | local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version)) | ||
| 45 | if rockspec then | ||
| 46 | local _, missing = deps.match_deps(rockspec, blacklist) | ||
| 47 | if missing[name] then | ||
| 48 | table.insert(dependents, { name = rock_name, version = rock_version }) | ||
| 49 | end | ||
| 50 | end | ||
| 51 | end | ||
| 52 | end | ||
| 53 | return dependents | ||
| 54 | end | ||
| 55 | |||
| 56 | --- Delete given versions of a program. | ||
| 57 | -- @param name string: the name of a program | ||
| 58 | -- @param versions array of string: the versions to be deleted. | ||
| 59 | -- @return boolean or (nil, string): true on success or nil and an error message. | ||
| 60 | local function delete_versions(name, versions) | ||
| 61 | |||
| 62 | local manifest, err = manif.load_manifest(cfg.rocks_dir) | ||
| 63 | if not manifest then | ||
| 64 | return nil, err | ||
| 65 | end | ||
| 66 | |||
| 67 | local commands = {} | ||
| 68 | for version, data in pairs(versions) do | ||
| 69 | for _, command in pairs(manifest.repository[name][version][1].commands) do | ||
| 70 | if not commands[command] then commands[command] = {} end | ||
| 71 | table.insert(commands[command], name.."/"..version) | ||
| 72 | end | ||
| 73 | end | ||
| 74 | |||
| 75 | if next(commands) then | ||
| 76 | for command, users in pairs(commands) do | ||
| 77 | local providers_of_command = manifest.commands[command] | ||
| 78 | for _, user in ipairs(users) do | ||
| 79 | for i, value in ipairs(providers_of_command) do | ||
| 80 | if providers_of_command[i] == user then | ||
| 81 | table.remove(providers_of_command, i) | ||
| 82 | break | ||
| 83 | end | ||
| 84 | end | ||
| 85 | end | ||
| 86 | local remaining = next(providers_of_command) | ||
| 87 | if remaining then | ||
| 88 | local name, version = remaining:match("^([^/]*)/(.*)$") | ||
| 89 | rep.install_bins(name, version, command) | ||
| 90 | else | ||
| 91 | rep.delete_bin(command) | ||
| 92 | end | ||
| 93 | end | ||
| 94 | end | ||
| 95 | |||
| 96 | for version, _ in pairs(versions) do | ||
| 97 | print("Removing "..name.." "..version.."...") | ||
| 98 | rep.delete_version(name, version) | ||
| 99 | end | ||
| 100 | |||
| 101 | return true | ||
| 102 | end | ||
| 103 | |||
| 104 | --- Driver function for the "install" command. | ||
| 105 | -- @param name string: name of a rock. If a version is given, refer to | ||
| 106 | -- a specific version; otherwise, try to remove all versions. | ||
| 107 | -- @param version string: When passing a package name, a version number | ||
| 108 | -- may also be given. | ||
| 109 | -- @return boolean or (nil, string): True if removal was | ||
| 110 | -- successful, nil and an error message otherwise. | ||
| 111 | function run(...) | ||
| 112 | local flags, name, version = util.parse_flags(...) | ||
| 113 | |||
| 114 | if type(name) ~= "string" then | ||
| 115 | return nil, "Argument missing, see help." | ||
| 116 | end | ||
| 117 | local results = {} | ||
| 118 | search.manifest_search(results, cfg.rocks_dir, search.make_query(name, version)) | ||
| 119 | |||
| 120 | local versions = results[name] | ||
| 121 | if not versions then | ||
| 122 | return nil, "Could not find rock '"..name..(version and " "..version or "").."' in local tree." | ||
| 123 | else | ||
| 124 | local version = next(versions) | ||
| 125 | local second = next(versions, version) | ||
| 126 | |||
| 127 | print("Checking stability of dependencies on the absence of") | ||
| 128 | print(name.." "..table.concat(util.keys(versions), ", ").."...") | ||
| 129 | print() | ||
| 130 | |||
| 131 | local dependents = check_dependents(name, versions) | ||
| 132 | |||
| 133 | if #dependents == 0 or flags["force"] then | ||
| 134 | if #dependents > 0 then | ||
| 135 | print("The following packages may be broken by this forced removal:") | ||
| 136 | for _, dependent in ipairs(dependents) do | ||
| 137 | print(dependent.name.." "..dependent.version) | ||
| 138 | end | ||
| 139 | print() | ||
| 140 | end | ||
| 141 | local ok, err1 = delete_versions(name, versions) | ||
| 142 | local ok, err2 = manif.make_manifest(cfg.rocks_dir) | ||
| 143 | if err1 or err2 then | ||
| 144 | return nil, err1 or err2 | ||
| 145 | end | ||
| 146 | else | ||
| 147 | if not second then | ||
| 148 | print("Will not remove "..name.." "..version..".") | ||
| 149 | print("Removing it would break dependencies for: ") | ||
| 150 | else | ||
| 151 | print("Will not remove all versions of "..name..".") | ||
| 152 | print("Removing them would break dependencies for: ") | ||
| 153 | end | ||
| 154 | for _, dependent in ipairs(dependents) do | ||
| 155 | print(dependent.name.." "..dependent.version) | ||
| 156 | end | ||
| 157 | print() | ||
| 158 | print("Use --force to force removal (warning: this may break modules).") | ||
| 159 | return nil, "Failed removing." | ||
| 160 | end | ||
| 161 | end | ||
| 162 | return true | ||
| 163 | end | ||
diff --git a/src/luarocks/rep.lua b/src/luarocks/rep.lua new file mode 100644 index 00000000..b5797803 --- /dev/null +++ b/src/luarocks/rep.lua | |||
| @@ -0,0 +1,187 @@ | |||
| 1 | |||
| 2 | --- Functions for managing the repository on disk. | ||
| 3 | module("luarocks.rep", package.seeall) | ||
| 4 | |||
| 5 | local fs = require("luarocks.fs") | ||
| 6 | local path = require("luarocks.path") | ||
| 7 | local cfg = require("luarocks.cfg") | ||
| 8 | local util = require("luarocks.util") | ||
| 9 | |||
| 10 | --- Get all installed versions of a package. | ||
| 11 | -- @param name string: a package name. | ||
| 12 | -- @return table or nil: An array of strings listing installed | ||
| 13 | -- versions of a package, or nil if none is available. | ||
| 14 | function get_versions(name) | ||
| 15 | assert(type(name) == "string") | ||
| 16 | |||
| 17 | local dirs = fs.dir(path.versions_dir(name)) | ||
| 18 | return (dirs and #dirs > 0) and dirs or nil | ||
| 19 | end | ||
| 20 | |||
| 21 | --- Check if a package exists in a local repository. | ||
| 22 | -- Version numbers are compared as exact string comparison. | ||
| 23 | -- @param name string: name of package | ||
| 24 | -- @param version string: package version in string format | ||
| 25 | -- @return boolean: true if a package is installed, | ||
| 26 | -- false otherwise. | ||
| 27 | function is_installed(name, version) | ||
| 28 | assert(type(name) == "string") | ||
| 29 | assert(type(version) == "string") | ||
| 30 | |||
| 31 | return fs.is_dir(path.install_dir(name, version)) | ||
| 32 | end | ||
| 33 | |||
| 34 | --- Delete a package from the local repository. | ||
| 35 | -- Version numbers are compared as exact string comparison. | ||
| 36 | -- @param name string: name of package | ||
| 37 | -- @param version string: package version in string format | ||
| 38 | function delete_version(name, version) | ||
| 39 | assert(type(name) == "string") | ||
| 40 | assert(type(version) == "string") | ||
| 41 | |||
| 42 | fs.delete(path.install_dir(name, version)) | ||
| 43 | if not get_versions(name) then | ||
| 44 | fs.delete(fs.make_path(cfg.rocks_dir, name)) | ||
| 45 | end | ||
| 46 | end | ||
| 47 | |||
| 48 | --- Delete a command-line item from the bin directory. | ||
| 49 | -- @param command string: name of script | ||
| 50 | function delete_bin(command) | ||
| 51 | assert(type(command) == "string") | ||
| 52 | |||
| 53 | fs.delete(fs.make_path(cfg.scripts_dir, command)) | ||
| 54 | end | ||
| 55 | |||
| 56 | --- Install bin entries in the repository bin dir. | ||
| 57 | -- @param name string: name of package | ||
| 58 | -- @param version string: package version in string format | ||
| 59 | -- @param single_file string or nil: optional parameter, indicating the name | ||
| 60 | -- of a single file to install; if not given, all bin files from the package | ||
| 61 | -- are installed. | ||
| 62 | -- @return boolean or (nil, string): True if succeeded or nil and | ||
| 63 | -- and error message. | ||
| 64 | function install_bins(name, version, single_file) | ||
| 65 | assert(type(name) == "string") | ||
| 66 | assert(type(version) == "string") | ||
| 67 | |||
| 68 | local bindir = path.bin_dir(name, version) | ||
| 69 | if fs.exists(bindir) then | ||
| 70 | local ok, err = fs.make_dir(cfg.scripts_dir) | ||
| 71 | if not ok then | ||
| 72 | return nil, "Could not create "..cfg.scripts_dir | ||
| 73 | end | ||
| 74 | local files = single_file and {single_file} or fs.dir(bindir) | ||
| 75 | for _, file in pairs(files) do | ||
| 76 | local fullname = fs.make_path(bindir, file) | ||
| 77 | local match = file:match("%.lua$") | ||
| 78 | local file | ||
| 79 | if not match then | ||
| 80 | file = io.open(fullname) | ||
| 81 | end | ||
| 82 | if match or (file and file:read():match("#!.*lua.*")) then | ||
| 83 | ok, err = fs.wrap_script(fullname, cfg.scripts_dir) | ||
| 84 | else | ||
| 85 | ok, err = fs.copy_binary(fullname, cfg.scripts_dir) | ||
| 86 | end | ||
| 87 | if file then file:close() end | ||
| 88 | if not ok then | ||
| 89 | return nil, err | ||
| 90 | end | ||
| 91 | end | ||
| 92 | end | ||
| 93 | return true | ||
| 94 | end | ||
| 95 | |||
| 96 | --- Obtain a list of modules within an installed package. | ||
| 97 | -- @param package string: The package name; for example "luasocket" | ||
| 98 | -- @param version string: The exact version number including revision; | ||
| 99 | -- for example "2.0.1-1". | ||
| 100 | -- @return table: A table of modules where keys are module identifiers | ||
| 101 | -- in "foo.bar" format and values are pathnames in architecture-dependent | ||
| 102 | -- "foo/bar.so" format. If no modules are found or if package or version | ||
| 103 | -- are invalid, an empty table is returned. | ||
| 104 | function package_modules(package, version) | ||
| 105 | assert(type(package) == "string") | ||
| 106 | assert(type(version) == "string") | ||
| 107 | |||
| 108 | local result = {} | ||
| 109 | local luas = fs.find(path.lua_dir(package, version)) | ||
| 110 | local libs = fs.find(path.lib_dir(package, version)) | ||
| 111 | for _, file in ipairs(luas) do | ||
| 112 | local name = path.path_to_module(file) | ||
| 113 | if name then | ||
| 114 | result[name] = file | ||
| 115 | end | ||
| 116 | end | ||
| 117 | for _, file in ipairs(libs) do | ||
| 118 | local name = path.path_to_module(file) | ||
| 119 | if name then | ||
| 120 | result[name] = file | ||
| 121 | end | ||
| 122 | end | ||
| 123 | return result | ||
| 124 | end | ||
| 125 | |||
| 126 | --- Obtain a list of command-line scripts within an installed package. | ||
| 127 | -- @param package string: The package name; for example "luasocket" | ||
| 128 | -- @param version string: The exact version number including revision; | ||
| 129 | -- for example "2.0.1-1". | ||
| 130 | -- @return table: A table of items where keys are command names | ||
| 131 | -- as strings and values are pathnames in architecture-dependent | ||
| 132 | -- ".../bin/foo" format. If no modules are found or if package or version | ||
| 133 | -- are invalid, an empty table is returned. | ||
| 134 | function package_commands(package, version) | ||
| 135 | assert(type(package) == "string") | ||
| 136 | assert(type(version) == "string") | ||
| 137 | |||
| 138 | local result = {} | ||
| 139 | local bindir = path.bin_dir(package, version) | ||
| 140 | local bins = fs.find(bindir) | ||
| 141 | for _, file in ipairs(bins) do | ||
| 142 | if file then | ||
| 143 | result[file] = fs.make_path(bindir, file) | ||
| 144 | end | ||
| 145 | end | ||
| 146 | return result | ||
| 147 | end | ||
| 148 | |||
| 149 | --- Check if a rock contains binary parts or if it is pure Lua. | ||
| 150 | -- @param name string: name of an installed rock | ||
| 151 | -- @param version string: version of an installed rock | ||
| 152 | -- @return boolean: returns true if rock contains platform-specific | ||
| 153 | -- binary code, or false if it is a pure-Lua rock. | ||
| 154 | function is_binary_rock(name, version) | ||
| 155 | local bin_dir = path.bin_dir(name, version) | ||
| 156 | local lib_dir = path.lib_dir(name, version) | ||
| 157 | if fs.exists(lib_dir) then | ||
| 158 | return true | ||
| 159 | end | ||
| 160 | if fs.exists(bin_dir) then | ||
| 161 | for _, name in pairs(fs.find(bin_dir)) do | ||
| 162 | if fs.is_actual_binary(fs.make_path(bin_dir, name)) then | ||
| 163 | return true | ||
| 164 | end | ||
| 165 | end | ||
| 166 | end | ||
| 167 | return false | ||
| 168 | end | ||
| 169 | |||
| 170 | function run_hook(rockspec, hook_name) | ||
| 171 | local hooks = rockspec.hooks | ||
| 172 | if not hooks then | ||
| 173 | return true | ||
| 174 | end | ||
| 175 | if not hooks.substituted_variables then | ||
| 176 | util.variable_substitutions(hooks, rockspec.variables) | ||
| 177 | hooks.substituted_variables = true | ||
| 178 | end | ||
| 179 | local hook = hooks[hook_name] | ||
| 180 | if hook then | ||
| 181 | print(hook) | ||
| 182 | if not fs.execute(hook) then | ||
| 183 | return nil, "Failed running "..hook_name.." hook." | ||
| 184 | end | ||
| 185 | end | ||
| 186 | return true | ||
| 187 | end | ||
diff --git a/src/luarocks/require.lua b/src/luarocks/require.lua new file mode 100644 index 00000000..f5d26078 --- /dev/null +++ b/src/luarocks/require.lua | |||
| @@ -0,0 +1,263 @@ | |||
| 1 | |||
| 2 | local global_env = _G | ||
| 3 | local plain_require = require | ||
| 4 | local plain_package_path = package.path | ||
| 5 | local plain_package_cpath = package.cpath | ||
| 6 | local package, assert, ipairs, pairs, os, print, table, type, next = | ||
| 7 | package, assert, ipairs, pairs, os, print, table, type, next | ||
| 8 | |||
| 9 | --- Application interface with LuaRocks. | ||
| 10 | -- Load this module to LuaRocks-enable an application: | ||
| 11 | -- this overrides the require() function, making it able to | ||
| 12 | -- load modules installed as rocks. | ||
| 13 | module("luarocks.require") | ||
| 14 | |||
| 15 | local path = plain_require("luarocks.path") | ||
| 16 | local manif = plain_require("luarocks.manif") | ||
| 17 | local deps = plain_require("luarocks.deps") | ||
| 18 | local cfg = plain_require("luarocks.cfg") | ||
| 19 | |||
| 20 | context = {} | ||
| 21 | |||
| 22 | -- Contains a table when rocks trees are loaded, | ||
| 23 | -- or 'false' to indicate rocks trees failed to load. | ||
| 24 | -- 'nil' indicates rocks trees were not attempted to be loaded yet. | ||
| 25 | rocks_trees = nil | ||
| 26 | |||
| 27 | local function load_rocks_trees() | ||
| 28 | local any_ok = false | ||
| 29 | local trees = {} | ||
| 30 | for _, tree in pairs(cfg.rocks_trees) do | ||
| 31 | local rocks_dir = tree .. "/rocks/" | ||
| 32 | local manifest, err = manif.load_local_manifest(rocks_dir) | ||
| 33 | if manifest then | ||
| 34 | any_ok = true | ||
| 35 | table.insert(trees, {rocks_dir=rocks_dir, manifest=manifest}) | ||
| 36 | end | ||
| 37 | end | ||
| 38 | if not any_ok then | ||
| 39 | rocks_trees = false | ||
| 40 | return false | ||
| 41 | end | ||
| 42 | rocks_trees = trees | ||
| 43 | return true | ||
| 44 | end | ||
| 45 | |||
| 46 | --- Process the dependencies of a package to determine its dependency | ||
| 47 | -- chain for loading modules. | ||
| 48 | -- @parse name string: The name of an installed rock. | ||
| 49 | -- @parse version string: The version of the rock, in string format | ||
| 50 | -- @parse manifest table: The local manifest table where this rock | ||
| 51 | -- is installed. | ||
| 52 | local function add_context(name, version, manifest) | ||
| 53 | -- assert(type(name) == "string") | ||
| 54 | -- assert(type(version) == "string") | ||
| 55 | -- assert(type(manifest) == "table") | ||
| 56 | |||
| 57 | if context[name] then | ||
| 58 | return | ||
| 59 | end | ||
| 60 | context[name] = version | ||
| 61 | |||
| 62 | local pkgdeps = manifest.dependencies and manifest.dependencies[name][version] | ||
| 63 | if pkgdeps then | ||
| 64 | for _, dep in ipairs(pkgdeps) do | ||
| 65 | local package, constraints = dep.name, dep.constraints | ||
| 66 | |||
| 67 | for _, tree in pairs(rocks_trees) do | ||
| 68 | local entries = tree.manifest.repository[package] | ||
| 69 | if entries then | ||
| 70 | for version, packages in pairs(entries) do | ||
| 71 | if (not constraints) or deps.match_constraints(deps.parse_version(version), constraints) then | ||
| 72 | add_context(package, version, tree.manifest) | ||
| 73 | end | ||
| 74 | end | ||
| 75 | end | ||
| 76 | end | ||
| 77 | end | ||
| 78 | end | ||
| 79 | end | ||
| 80 | |||
| 81 | --- Internal sorting function. | ||
| 82 | -- @param a table: A provider table. | ||
| 83 | -- @param b table: Another provider table. | ||
| 84 | -- @return boolean: True if the version of a is greater than that of b. | ||
| 85 | local function sort_versions(a,b) | ||
| 86 | return a.version > b.version | ||
| 87 | end | ||
| 88 | |||
| 89 | --- Specify a dependency chain for LuaRocks. | ||
| 90 | -- In the presence of multiple versions of packages, it is necessary to, | ||
| 91 | -- at some point, indicate which dependency chain we're following. | ||
| 92 | -- set_context does this by allowing one to pick a package to be the | ||
| 93 | -- root of this dependency chain. Once a dependency chain is picked it's | ||
| 94 | -- easy to know which modules to load ("I want to use *this* version of | ||
| 95 | -- A, which requires *that* version of B, which requires etc etc etc"). | ||
| 96 | -- @param name string: The package name of an installed rock. | ||
| 97 | -- @param version string or nil: Optionally, a version number | ||
| 98 | -- When a version is not given, it picks the highest version installed. | ||
| 99 | -- @return boolean: true if succeeded, false otherwise. | ||
| 100 | function set_context(name, version) | ||
| 101 | --assert(type(name) == "string") | ||
| 102 | --assert(type(version) == "string" or not version) | ||
| 103 | |||
| 104 | if rocks_trees == false or (not rocks_trees and not load_rocks_trees()) then | ||
| 105 | return false | ||
| 106 | end | ||
| 107 | |||
| 108 | local manifest | ||
| 109 | local vtables = {} | ||
| 110 | for _, tree in ipairs(rocks_trees) do | ||
| 111 | if version then | ||
| 112 | local manif_repo = tree.manifest.repository | ||
| 113 | if manif_repo[name] and manif_repo[name][version] then | ||
| 114 | manifest = tree.manifest | ||
| 115 | break | ||
| 116 | end | ||
| 117 | else | ||
| 118 | local versions = manif.get_versions(name, tree.manifest) | ||
| 119 | for _, version in ipairs(versions) do | ||
| 120 | table.insert(vtables, {version = deps.parse_version(version), manifest = tree.manifest}) | ||
| 121 | end | ||
| 122 | end | ||
| 123 | end | ||
| 124 | if not version then | ||
| 125 | if not next(vtables) then | ||
| 126 | table.sort(vtables, sort_versions) | ||
| 127 | local highest = vtables[#vtables] | ||
| 128 | version = highest.version.string | ||
| 129 | manifest = highest.manifest | ||
| 130 | end | ||
| 131 | end | ||
| 132 | if not manifest then | ||
| 133 | return false | ||
| 134 | end | ||
| 135 | |||
| 136 | add_context(name, version, manifest) | ||
| 137 | -- TODO: platform independence | ||
| 138 | local lpath, cpath = "", "" | ||
| 139 | for name, version in pairs(context) do | ||
| 140 | lpath = lpath .. path.lua_dir(name, version) .. "/?.lua;" | ||
| 141 | lpath = lpath .. path.lua_dir(name, version) .. "/?/init.lua;" | ||
| 142 | cpath = cpath .. path.lib_dir(name, version) .."/?."..cfg.lib_extension..";" | ||
| 143 | end | ||
| 144 | global_env.package.path = lpath .. plain_package_path | ||
| 145 | global_env.package.cpath = cpath .. plain_package_cpath | ||
| 146 | end | ||
| 147 | |||
| 148 | --- Call the vanilla require() function using specially constructed | ||
| 149 | -- package paths so that it finds exactly the version we want it to find. | ||
| 150 | -- @param name string: The rock name. | ||
| 151 | -- @param version string: The rock version. | ||
| 152 | -- @param module string: The module name, in require() notation. | ||
| 153 | -- @return The result returned by require(). | ||
| 154 | local function plain_require_on(module, name, version, rocks_dir, ...) | ||
| 155 | --assert(type(module) == "string") | ||
| 156 | --assert(type(name) == "string") | ||
| 157 | --assert(type(version) == "string") | ||
| 158 | |||
| 159 | local global_package = global_env.package | ||
| 160 | local save_path = global_package.path | ||
| 161 | local save_cpath = global_package.cpath | ||
| 162 | global_package.path = path.lua_dir(name, version, rocks_dir) .. "/?.lua;" | ||
| 163 | .. path.lua_dir(name, version, rocks_dir) .. "/?/init.lua;" .. save_path | ||
| 164 | global_package.cpath = path.lib_dir(name, version, rocks_dir) .. "/?."..cfg.lib_extension..";" .. save_cpath | ||
| 165 | local result = plain_require(module, ...) | ||
| 166 | global_package.path = save_path | ||
| 167 | global_package.cpath = save_cpath | ||
| 168 | return result | ||
| 169 | end | ||
| 170 | |||
| 171 | local function pick_module(module, constraints) | ||
| 172 | --assert(type(module) == "string") | ||
| 173 | --assert(not constraints or type(constraints) == "string") | ||
| 174 | |||
| 175 | if not rocks_trees and not load_rocks_trees() then | ||
| 176 | return nil | ||
| 177 | end | ||
| 178 | |||
| 179 | if constraints then | ||
| 180 | if type(constraints) == "string" then | ||
| 181 | constraints = deps.parse_constraints(constraints) | ||
| 182 | else | ||
| 183 | constraints = nil | ||
| 184 | end | ||
| 185 | end | ||
| 186 | |||
| 187 | local providers = {} | ||
| 188 | for _, tree in pairs(rocks_trees) do | ||
| 189 | local entries = tree.manifest.modules[module] | ||
| 190 | if entries then | ||
| 191 | for _, entry in pairs(entries) do | ||
| 192 | local name, version = entry:match("^([^/]*)/(.*)$") | ||
| 193 | if context[name] == version then | ||
| 194 | return name, version, tree | ||
| 195 | end | ||
| 196 | version = deps.parse_version(version) | ||
| 197 | if (not constraints) or deps.match_constraints(version, constraints) then | ||
| 198 | table.insert(providers, {name = name, version = version, repo = tree}) | ||
| 199 | end | ||
| 200 | end | ||
| 201 | end | ||
| 202 | end | ||
| 203 | |||
| 204 | if next(providers) then | ||
| 205 | table.sort(providers, sort_versions) | ||
| 206 | local first = providers[1] | ||
| 207 | return first.name, first.version.string, first.repo | ||
| 208 | end | ||
| 209 | end | ||
| 210 | |||
| 211 | --- Inform which rock LuaRocks would use if require() is called | ||
| 212 | -- with the given arguments. | ||
| 213 | -- @param module string: The module name, like in plain require(). | ||
| 214 | -- @param constraints string or nil: An optional comma-separated | ||
| 215 | -- list of version constraints. | ||
| 216 | -- @return (string, string) or nil: Rock name and version if the | ||
| 217 | -- requested module can be supplied by LuaRocks, or nil if it can't. | ||
| 218 | function get_rock_from_module(module, constraints) | ||
| 219 | --assert(type(module) == "string") | ||
| 220 | --assert(not constraints or type(constraints) == "string") | ||
| 221 | local name, version = pick_module(module, constraints) | ||
| 222 | return name, version | ||
| 223 | end | ||
| 224 | |||
| 225 | --- Function that overloads require(), adding LuaRocks support. | ||
| 226 | -- This function wraps around Lua's standard require() call, | ||
| 227 | -- allowing it to find modules installed by LuaRocks. | ||
| 228 | -- A module is searched in installed rocks that match the | ||
| 229 | -- current LuaRocks context. If module is not part of the | ||
| 230 | -- context, or if a context has not yet been set, the module | ||
| 231 | -- in the package with the highest version is used. | ||
| 232 | -- If a module is not available in any installed rock, plain | ||
| 233 | -- require() is called, using the original package.path and | ||
| 234 | -- package.cpath lookup locations. | ||
| 235 | -- Additionally, version constraints for the matching rock may | ||
| 236 | -- be given as a second parameter. If version constraints could | ||
| 237 | -- not be fulfilled, this equates to the rock not being | ||
| 238 | -- available: as such, plain require() with the default paths | ||
| 239 | -- is called as a fallback. | ||
| 240 | -- @param module string: The module name, like in plain require(). | ||
| 241 | -- @param constraints string or nil: An optional comma-separated | ||
| 242 | -- list of version constraints. | ||
| 243 | -- @return table: The module table (typically), like in plain | ||
| 244 | -- require(). See <a href="http://www.lua.org/manual/5.1/manual.html#pdf-require">require()</a> | ||
| 245 | -- in the Lua reference manual for details. | ||
| 246 | function require(module, ...) | ||
| 247 | --assert(type(module) == "string") | ||
| 248 | |||
| 249 | if package.loaded[module] or rocks_trees == false or (not rocks_trees and not load_rocks_trees()) then | ||
| 250 | return plain_require(module, ...) | ||
| 251 | end | ||
| 252 | |||
| 253 | local name, version, repo = pick_module(module, ...) | ||
| 254 | |||
| 255 | if not name then | ||
| 256 | return plain_require(module, ...) | ||
| 257 | else | ||
| 258 | add_context(name, version, repo.manifest) | ||
| 259 | return plain_require_on(module, name, version, repo.rocks_dir, ...) | ||
| 260 | end | ||
| 261 | end | ||
| 262 | |||
| 263 | global_env.require = require | ||
diff --git a/src/luarocks/search.lua b/src/luarocks/search.lua new file mode 100644 index 00000000..a3ac68f3 --- /dev/null +++ b/src/luarocks/search.lua | |||
| @@ -0,0 +1,394 @@ | |||
| 1 | |||
| 2 | --- Module implementing the LuaRocks "search" command. | ||
| 3 | -- Queries LuaRocks servers. | ||
| 4 | module("luarocks.search", package.seeall) | ||
| 5 | |||
| 6 | local fs = require("luarocks.fs") | ||
| 7 | local path = require("luarocks.path") | ||
| 8 | local manif = require("luarocks.manif") | ||
| 9 | local deps = require("luarocks.deps") | ||
| 10 | local cfg = require("luarocks.cfg") | ||
| 11 | local util = require("luarocks.util") | ||
| 12 | |||
| 13 | help_summary = "Query the LuaRocks servers." | ||
| 14 | help_arguments = "[--source] [--binary] { <name> [<version>] | --all }" | ||
| 15 | help = [[ | ||
| 16 | --source Return only rockspecs and source rocks, | ||
| 17 | to be used with the "build" command. | ||
| 18 | --binary Return only pure Lua and binary rocks (rocks that can be used | ||
| 19 | with the "install" command without requiring a C toolchain). | ||
| 20 | --all List all contents of the server that are suitable to | ||
| 21 | this platform, do not filter by name. | ||
| 22 | ]] | ||
| 23 | |||
| 24 | --- Convert the arch field of a query table to table format. | ||
| 25 | -- @param query table: A query table. | ||
| 26 | local function query_arch_as_table(query) | ||
| 27 | local format = type(query.arch) | ||
| 28 | if format == "table" then | ||
| 29 | return | ||
| 30 | elseif format == "nil" then | ||
| 31 | local accept = {} | ||
| 32 | accept["src"] = true | ||
| 33 | accept["all"] = true | ||
| 34 | accept["rockspec"] = true | ||
| 35 | accept["installed"] = true | ||
| 36 | accept[cfg.arch] = true | ||
| 37 | query.arch = accept | ||
| 38 | elseif format == "string" then | ||
| 39 | local accept = {} | ||
| 40 | for a in string.gmatch(query.arch, "[%w_]+") do | ||
| 41 | accept[a] = true | ||
| 42 | end | ||
| 43 | query.arch = accept | ||
| 44 | end | ||
| 45 | end | ||
| 46 | |||
| 47 | --- Store a search result (a rock or rockspec) in the results table. | ||
| 48 | -- @param results table: The results table, where keys are package names and | ||
| 49 | -- versions are tables matching version strings to an array of servers. | ||
| 50 | -- @param name string: Package name. | ||
| 51 | -- @param version string: Package version. | ||
| 52 | -- @param arch string: Architecture of rock ("all", "src" or platform | ||
| 53 | -- identifier), "rockspec" or "installed" | ||
| 54 | -- @param repo string: Pathname of a local repository of URL of | ||
| 55 | -- rocks server. | ||
| 56 | local function store_result(results, name, version, arch, repo) | ||
| 57 | assert(type(results) == "table") | ||
| 58 | assert(type(name) == "string") | ||
| 59 | assert(type(version) == "string") | ||
| 60 | assert(type(arch) == "string") | ||
| 61 | assert(type(repo) == "string") | ||
| 62 | |||
| 63 | if not results[name] then results[name] = {} end | ||
| 64 | if not results[name][version] then results[name][version] = {} end | ||
| 65 | table.insert(results[name][version], { | ||
| 66 | arch = arch, | ||
| 67 | repo = repo | ||
| 68 | }) | ||
| 69 | end | ||
| 70 | |||
| 71 | --- Test the name field of a query. | ||
| 72 | -- If query has a boolean field exact_name set to false, | ||
| 73 | -- then substring match is performed; otherwise, exact string | ||
| 74 | -- comparison is done. | ||
| 75 | -- @param query table: A query in dependency table format. | ||
| 76 | -- @param name string: A package name. | ||
| 77 | -- @return boolean: True if names match, false otherwise. | ||
| 78 | local function match_name(query, name) | ||
| 79 | assert(type(query) == "table") | ||
| 80 | assert(type(name) == "string") | ||
| 81 | if query.exact_name == false then | ||
| 82 | return name:find(query.name, 0, true) and true or false | ||
| 83 | else | ||
| 84 | return name == query.name | ||
| 85 | end | ||
| 86 | end | ||
| 87 | |||
| 88 | --- Store a match in a results table if version matches query. | ||
| 89 | -- Name, version, arch and repository path are stored in a given | ||
| 90 | -- table, optionally checking if version and arch (if given) match | ||
| 91 | -- a query. | ||
| 92 | -- @param results table: The results table, where keys are package names and | ||
| 93 | -- versions are tables matching version strings to an array of servers. | ||
| 94 | -- @param repo string: URL or pathname of the repository. | ||
| 95 | -- @param name string: The name of the package being tested. | ||
| 96 | -- @param version string: The version of the package being tested. | ||
| 97 | -- @param arch string: The arch of the package being tested. | ||
| 98 | -- @param query table: A table describing the query in dependency | ||
| 99 | -- format (for example, {name = "filesystem", exact_name = false, | ||
| 100 | -- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). | ||
| 101 | -- If the arch field is omitted, the local architecture (cfg.arch) | ||
| 102 | -- is used. The special value "any" is also recognized, returning all | ||
| 103 | -- matches regardless of architecture. | ||
| 104 | local function store_if_match(results, repo, name, version, arch, query) | ||
| 105 | if match_name(query, name) then | ||
| 106 | if query.arch[arch] or query.arch["any"] then | ||
| 107 | if deps.match_constraints(deps.parse_version(version), query.constraints) then | ||
| 108 | store_result(results, name, version, arch, repo) | ||
| 109 | end | ||
| 110 | end | ||
| 111 | end | ||
| 112 | end | ||
| 113 | |||
| 114 | --- Perform search on a local repository. | ||
| 115 | -- @param repo string: The pathname of the local repository. | ||
| 116 | -- @param query table: A table describing the query in dependency | ||
| 117 | -- format (for example, {name = "filesystem", exact_name = false, | ||
| 118 | -- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). | ||
| 119 | -- If the arch field is omitted, the local architecture (cfg.arch) | ||
| 120 | -- is used. The special value "any" is also recognized, returning all | ||
| 121 | -- matches regardless of architecture. | ||
| 122 | -- @param results table or nil: If given, this table will store the | ||
| 123 | -- results; if not given, a new table will be created. | ||
| 124 | -- @param table: The results table, where keys are package names and | ||
| 125 | -- versions are tables matching version strings to an array of servers. | ||
| 126 | -- If a table was given in the "results" parameter, that is the result value. | ||
| 127 | function disk_search(repo, query, results) | ||
| 128 | assert(type(repo) == "string") | ||
| 129 | assert(type(query) == "table") | ||
| 130 | assert(type(results) == "table" or not results) | ||
| 131 | if not results then | ||
| 132 | results = {} | ||
| 133 | end | ||
| 134 | query_arch_as_table(query) | ||
| 135 | |||
| 136 | for _, name in pairs(fs.dir(repo)) do | ||
| 137 | local pathname = fs.make_path(repo, name) | ||
| 138 | local rname, rversion, rarch = path.parse_rock_name(name) | ||
| 139 | if not rname then | ||
| 140 | rname, rversion, rarch = path.parse_rockspec_name(name) | ||
| 141 | end | ||
| 142 | if fs.is_dir(pathname) then | ||
| 143 | for _, version in pairs(fs.dir(pathname)) do | ||
| 144 | if version:match("-%d+$") then | ||
| 145 | store_if_match(results, repo, name, version, "installed", query) | ||
| 146 | end | ||
| 147 | end | ||
| 148 | elseif rname then | ||
| 149 | store_if_match(results, repo, rname, rversion, rarch, query) | ||
| 150 | end | ||
| 151 | end | ||
| 152 | return results | ||
| 153 | end | ||
| 154 | |||
| 155 | --- Perform search on a rocks server. | ||
| 156 | -- @param results table: The results table, where keys are package names and | ||
| 157 | -- versions are tables matching version strings to an array of servers. | ||
| 158 | -- @param repo string: The URL of the rocks server. | ||
| 159 | -- @param query table: A table describing the query in dependency | ||
| 160 | -- format (for example, {name = "filesystem", exact_name = false, | ||
| 161 | -- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). | ||
| 162 | -- If the arch field is omitted, the local architecture (cfg.arch) | ||
| 163 | -- is used. The special value "any" is also recognized, returning all | ||
| 164 | -- matches regardless of architecture. | ||
| 165 | function manifest_search(results, repo, query) | ||
| 166 | assert(type(results) == "table") | ||
| 167 | assert(type(repo) == "string") | ||
| 168 | assert(type(query) == "table") | ||
| 169 | |||
| 170 | query_arch_as_table(query) | ||
| 171 | local manifest, err = manif.load_manifest(repo) | ||
| 172 | if not manifest then | ||
| 173 | print(err) | ||
| 174 | return | ||
| 175 | end | ||
| 176 | for name, versions in pairs(manifest.repository) do | ||
| 177 | for version, items in pairs(versions) do | ||
| 178 | for _, item in ipairs(items) do | ||
| 179 | store_if_match(results, repo, name, version, item.arch, query) | ||
| 180 | end | ||
| 181 | end | ||
| 182 | end | ||
| 183 | end | ||
| 184 | |||
| 185 | --- Search on all configured rocks servers. | ||
| 186 | -- @param query table: A dependency query. | ||
| 187 | -- @return table or (nil, string): A table where keys are package names | ||
| 188 | -- and values are tables matching version strings to an array of | ||
| 189 | -- rocks servers; if no results are found, an empty table is returned. | ||
| 190 | -- In case of errors, nil and and error message are returned. | ||
| 191 | local function search_repos(query) | ||
| 192 | assert(type(query) == "table") | ||
| 193 | |||
| 194 | local results = {} | ||
| 195 | for _, repo in ipairs(cfg.rocks_servers) do | ||
| 196 | local protocol, pathname = fs.split_url(repo) | ||
| 197 | if protocol == "file" then | ||
| 198 | repo = pathname | ||
| 199 | end | ||
| 200 | manifest_search(results, repo, query) | ||
| 201 | end | ||
| 202 | return results | ||
| 203 | end | ||
| 204 | |||
| 205 | --- Prepare a query in dependency table format. | ||
| 206 | -- @param name string: The query name. | ||
| 207 | -- @param version string or nil: | ||
| 208 | -- @return table: A query in table format | ||
| 209 | function make_query(name, version) | ||
| 210 | assert(type(name) == "string") | ||
| 211 | assert(type(version) == "string" or not version) | ||
| 212 | |||
| 213 | local query = { | ||
| 214 | name = name, | ||
| 215 | constraints = {} | ||
| 216 | } | ||
| 217 | if version then | ||
| 218 | table.insert(query.constraints, { op = "~>", version = deps.parse_version(version)}) | ||
| 219 | end | ||
| 220 | return query | ||
| 221 | end | ||
| 222 | |||
| 223 | --- Get the URL for the latest in a set of versions. | ||
| 224 | -- @param name string: The package name to be used in the URL. | ||
| 225 | -- @param versions table: An array of version informations, as stored | ||
| 226 | -- in search results tables. | ||
| 227 | -- @return string or nil: the URL for the latest version if one could | ||
| 228 | -- be picked, or nil. | ||
| 229 | local function pick_latest_version(name, versions) | ||
| 230 | assert(type(name) == "string") | ||
| 231 | assert(type(versions) == "table") | ||
| 232 | |||
| 233 | local vtables = {} | ||
| 234 | for v, _ in pairs(versions) do | ||
| 235 | table.insert(vtables, deps.parse_version(v)) | ||
| 236 | end | ||
| 237 | table.sort(vtables) | ||
| 238 | local version = vtables[#vtables].string | ||
| 239 | local items = versions[version] | ||
| 240 | if items then | ||
| 241 | local pick = 1 | ||
| 242 | for i, item in ipairs(items) do | ||
| 243 | if (item.arch == 'src' and items[pick].arch == 'rockspec') | ||
| 244 | or (item.arch ~= 'src' and item.arch ~= 'rockspec') then | ||
| 245 | pick = i | ||
| 246 | end | ||
| 247 | end | ||
| 248 | return path.make_url(items[pick].repo, name, version, items[pick].arch) | ||
| 249 | end | ||
| 250 | return nil | ||
| 251 | end | ||
| 252 | |||
| 253 | --- Attempt to get a single URL for a given search. | ||
| 254 | -- @param query table: A dependency query. | ||
| 255 | -- @return string or table or (nil, string): URL for matching rock if | ||
| 256 | -- a single one was found, a table of candidates if it could not narrow to | ||
| 257 | -- a single result, or nil followed by an error message. | ||
| 258 | function find_suitable_rock(query) | ||
| 259 | assert(type(query) == "table") | ||
| 260 | |||
| 261 | local results, err = search_repos(query) | ||
| 262 | if not results then | ||
| 263 | return nil, err | ||
| 264 | end | ||
| 265 | local first = results and next(results) | ||
| 266 | if first and next(results, first) == nil then | ||
| 267 | return pick_latest_version(query.name, results[first]) | ||
| 268 | elseif not first then | ||
| 269 | return nil, "No results matching query were found." | ||
| 270 | else | ||
| 271 | return results | ||
| 272 | end | ||
| 273 | end | ||
| 274 | |||
| 275 | --- Print a list of rocks/rockspecs on standard output. | ||
| 276 | -- @param results table: A table where keys are package names and versions | ||
| 277 | -- are tables matching version strings to an array of rocks servers. | ||
| 278 | -- @param show_repo boolean or nil: Whether to show repository | ||
| 279 | -- information or not. Default is true. | ||
| 280 | function print_results(results, show_repo) | ||
| 281 | assert(type(results) == "table") | ||
| 282 | assert(type(show_repo) == "boolean" or not show_repo) | ||
| 283 | -- Force display of repo location for the time being | ||
| 284 | show_repo = true -- show_repo == nil and true or show_repo | ||
| 285 | |||
| 286 | for package, versions in util.sortedpairs(results) do | ||
| 287 | print(package) | ||
| 288 | for version, repos in util.sortedpairs(versions, deps.compare_versions) do | ||
| 289 | if show_repo then | ||
| 290 | for _, repo in ipairs(repos) do | ||
| 291 | print(" "..version.." ("..repo.arch..") - "..repo.repo) | ||
| 292 | end | ||
| 293 | else | ||
| 294 | print(" "..version) | ||
| 295 | end | ||
| 296 | end | ||
| 297 | print() | ||
| 298 | end | ||
| 299 | end | ||
| 300 | |||
| 301 | --- Splits a list of search results into two lists, one for "source" results | ||
| 302 | -- to be used with the "build" command, and one for "binary" results to be | ||
| 303 | -- used with the "install" command. | ||
| 304 | -- @param results table: A search results table. | ||
| 305 | -- @return (table, table): Two tables, one for source and one for binary | ||
| 306 | -- results. | ||
| 307 | local function split_source_and_binary_results(results) | ||
| 308 | local sources, binaries = {}, {} | ||
| 309 | for name, versions in pairs(results) do | ||
| 310 | for version, repos in pairs(versions) do | ||
| 311 | for _, repo in ipairs(repos) do | ||
| 312 | local where = sources | ||
| 313 | if repo.arch == "all" or repo.arch == cfg.arch then | ||
| 314 | where = binaries | ||
| 315 | end | ||
| 316 | store_result(where, name, version, repo.arch, repo.repo) | ||
| 317 | end | ||
| 318 | end | ||
| 319 | end | ||
| 320 | return sources, binaries | ||
| 321 | end | ||
| 322 | |||
| 323 | --- Given a name and optionally a version, try to find in the rocks | ||
| 324 | -- servers a single .src.rock or .rockspec file that satisfies | ||
| 325 | -- the request, and run the given function on it; or display to the | ||
| 326 | -- user possibilities if it couldn't narrow down a single match. | ||
| 327 | -- @param action function: A function that takes a .src.rock or | ||
| 328 | -- .rockspec URL as a parameter. | ||
| 329 | -- @string name string: A rock name | ||
| 330 | -- @string version string or nil: A version number may also be given. | ||
| 331 | -- @return The result of the action function, or nil and an error message. | ||
| 332 | function act_on_src_or_rockspec(action, name, version) | ||
| 333 | assert(type(action) == "function") | ||
| 334 | assert(type(name) == "string") | ||
| 335 | assert(type(version) == "string" or not version) | ||
| 336 | |||
| 337 | local query = make_query(name, version) | ||
| 338 | query.arch = "src|rockspec" | ||
| 339 | local results, err = find_suitable_rock(query) | ||
| 340 | if type(results) == "string" then | ||
| 341 | return action(results) | ||
| 342 | elseif type(results) == "table" and next(results) then | ||
| 343 | print("Multiple search results were returned.") | ||
| 344 | print() | ||
| 345 | print("Search results:") | ||
| 346 | print("---------------") | ||
| 347 | print_results(results) | ||
| 348 | return nil, "Please narrow your query." | ||
| 349 | else | ||
| 350 | return nil, "Could not find a result named "..name.."." | ||
| 351 | end | ||
| 352 | end | ||
| 353 | |||
| 354 | --- Driver function for "search" command. | ||
| 355 | -- @param name string: A substring of a rock name to search. | ||
| 356 | -- @param version string or nil: a version may also be passed. | ||
| 357 | -- @return boolean or (nil, string): True if build was successful; nil and an | ||
| 358 | -- error message otherwise. | ||
| 359 | function run(...) | ||
| 360 | local flags, name, version = util.parse_flags(...) | ||
| 361 | |||
| 362 | if flags["all"] then | ||
| 363 | name, version = "", "" | ||
| 364 | end | ||
| 365 | |||
| 366 | if type(name) ~= "string" and not flags["all"] then | ||
| 367 | return nil, "Enter name and version or use --all; see help." | ||
| 368 | end | ||
| 369 | |||
| 370 | local query = make_query(name, version) | ||
| 371 | query.exact_name = false | ||
| 372 | local results, err = search_repos(query) | ||
| 373 | if not results then | ||
| 374 | return nil, err | ||
| 375 | end | ||
| 376 | print() | ||
| 377 | print("Search results:") | ||
| 378 | print("===============") | ||
| 379 | print() | ||
| 380 | local sources, binaries = split_source_and_binary_results(results) | ||
| 381 | if next(sources) and not flags["binary"] then | ||
| 382 | print("Rockspecs and source rocks:") | ||
| 383 | print("---------------------------") | ||
| 384 | print() | ||
| 385 | print_results(sources, true) | ||
| 386 | end | ||
| 387 | if next(binaries) and not flags["source"] then | ||
| 388 | print("Binary and pure-Lua rocks:") | ||
| 389 | print("--------------------------") | ||
| 390 | print() | ||
| 391 | print_results(binaries, true) | ||
| 392 | end | ||
| 393 | return true | ||
| 394 | end | ||
diff --git a/src/luarocks/type_check.lua b/src/luarocks/type_check.lua new file mode 100644 index 00000000..ec9d6c5c --- /dev/null +++ b/src/luarocks/type_check.lua | |||
| @@ -0,0 +1,233 @@ | |||
| 1 | |||
| 2 | --- Type-checking functions. | ||
| 3 | -- Functions and definitions for doing a basic lint check on files | ||
| 4 | -- loaded by LuaRocks. | ||
| 5 | module("luarocks.type_check", package.seeall) | ||
| 6 | |||
| 7 | rockspec_format = "1.0" | ||
| 8 | |||
| 9 | rockspec_types = { | ||
| 10 | rockspec_format = "string", | ||
| 11 | MUST_package = "string", | ||
| 12 | MUST_version = "string", | ||
| 13 | description = { | ||
| 14 | summary = "string", | ||
| 15 | detailed = "string", | ||
| 16 | homepage = "string", | ||
| 17 | license = "string", | ||
| 18 | maintainer = "string" | ||
| 19 | }, | ||
| 20 | dependencies = { | ||
| 21 | platforms = {}, | ||
| 22 | ANY = "string" | ||
| 23 | }, | ||
| 24 | supported_platforms = { | ||
| 25 | ANY = "string" | ||
| 26 | }, | ||
| 27 | external_dependencies = { | ||
| 28 | platforms = {}, | ||
| 29 | ANY = { | ||
| 30 | program = "string", | ||
| 31 | header = "string", | ||
| 32 | library = "string" | ||
| 33 | } | ||
| 34 | }, | ||
| 35 | MUST_source = { | ||
| 36 | platforms = {}, | ||
| 37 | MUST_url = "string", | ||
| 38 | md5 = "string", | ||
| 39 | file = "string", | ||
| 40 | dir = "string", | ||
| 41 | tag = "string", | ||
| 42 | branch = "string", | ||
| 43 | cvs_tag = "string", | ||
| 44 | cvs_module = "string" | ||
| 45 | }, | ||
| 46 | build = { | ||
| 47 | platforms = {}, | ||
| 48 | type = "string", | ||
| 49 | install = { | ||
| 50 | lua = { | ||
| 51 | MORE = true | ||
| 52 | }, | ||
| 53 | lib = { | ||
| 54 | MORE = true | ||
| 55 | }, | ||
| 56 | conf = { | ||
| 57 | MORE = true | ||
| 58 | }, | ||
| 59 | bin = { | ||
| 60 | MORE = true | ||
| 61 | } | ||
| 62 | }, | ||
| 63 | copy_directories = { | ||
| 64 | ANY = "string" | ||
| 65 | }, | ||
| 66 | MORE = true | ||
| 67 | }, | ||
| 68 | hooks = { | ||
| 69 | platforms = {}, | ||
| 70 | post_install = "string" | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | rockspec_types.build.platforms.ANY = rockspec_types.build | ||
| 75 | rockspec_types.dependencies.platforms.ANY = rockspec_types.dependencies | ||
| 76 | rockspec_types.external_dependencies.platforms.ANY = rockspec_types.external_dependencies | ||
| 77 | rockspec_types.MUST_source.platforms.ANY = rockspec_types.MUST_source | ||
| 78 | rockspec_types.hooks.platforms.ANY = rockspec_types.hooks | ||
| 79 | |||
| 80 | manifest_types = { | ||
| 81 | MUST_repository = { | ||
| 82 | -- packages | ||
| 83 | ANY = { | ||
| 84 | -- versions | ||
| 85 | ANY = { | ||
| 86 | -- items | ||
| 87 | ANY = { | ||
| 88 | MUST_arch = "string", | ||
| 89 | modules = { ANY = "string" }, | ||
| 90 | commands = { ANY = "string" }, | ||
| 91 | dependencies = { ANY = "string" }, | ||
| 92 | -- TODO: to be extended with more metadata. | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | }, | ||
| 97 | MUST_modules = { | ||
| 98 | -- modules | ||
| 99 | ANY = { | ||
| 100 | -- providers | ||
| 101 | ANY = "string" | ||
| 102 | } | ||
| 103 | }, | ||
| 104 | MUST_commands = { | ||
| 105 | -- modules | ||
| 106 | ANY = { | ||
| 107 | -- commands | ||
| 108 | ANY = "string" | ||
| 109 | } | ||
| 110 | }, | ||
| 111 | dependencies = { | ||
| 112 | -- each module | ||
| 113 | ANY = { | ||
| 114 | -- each version | ||
| 115 | ANY = { | ||
| 116 | -- each dependency | ||
| 117 | ANY = { | ||
| 118 | name = "string", | ||
| 119 | constraints = { | ||
| 120 | ANY = { | ||
| 121 | no_upgrade = "boolean", | ||
| 122 | op = "string", | ||
| 123 | version = { | ||
| 124 | string = "string", | ||
| 125 | ANY = 0, | ||
| 126 | } | ||
| 127 | } | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | local type_check_table | ||
| 136 | |||
| 137 | --- Type check an object. | ||
| 138 | -- The object is compared against an archetypical value | ||
| 139 | -- matching the expected type -- the actual values don't matter, | ||
| 140 | -- only their types. Tables are type checked recursively. | ||
| 141 | -- @param name any: The object name (for error messages). | ||
| 142 | -- @param item any: The object being checked. | ||
| 143 | -- @param expected any: The reference object. In case of a table, | ||
| 144 | -- its is structured as a type reference table. | ||
| 145 | -- @return boolean or (nil, string): true if type checking | ||
| 146 | -- succeeded, or nil and an error message if it failed. | ||
| 147 | -- @see type_check_table | ||
| 148 | local function type_check_item(name, item, expected, context) | ||
| 149 | name = tostring(name) | ||
| 150 | |||
| 151 | local item_type = type(item) | ||
| 152 | local expected_type = type(expected) | ||
| 153 | if expected_type == "number" then | ||
| 154 | if not tonumber(item) then | ||
| 155 | return nil, "Type mismatch on field "..context..name..": expected a number" | ||
| 156 | end | ||
| 157 | elseif expected_type == "table" then | ||
| 158 | if item_type ~= expected_type then | ||
| 159 | return nil, "Type mismatch on field "..context..name..": expected a table" | ||
| 160 | else | ||
| 161 | return type_check_table(item, expected, context..name..".") | ||
| 162 | end | ||
| 163 | elseif item_type ~= expected_type then | ||
| 164 | return nil, "Type mismatch on field "..context..name..": expected a "..expected_type | ||
| 165 | end | ||
| 166 | return true | ||
| 167 | end | ||
| 168 | |||
| 169 | --- Type check the contents of a table. | ||
| 170 | -- The table's contents are compared against a reference table, | ||
| 171 | -- which contains the recognized fields, with archetypical values | ||
| 172 | -- matching the expected types -- the actual values of items in the | ||
| 173 | -- reference table don't matter, only their types (ie, for field x | ||
| 174 | -- in tbl that is correctly typed, type(tbl.x) == type(types.x)). | ||
| 175 | -- If the reference table contains a field called MORE, then | ||
| 176 | -- unknown fields in the checked table are accepted. | ||
| 177 | -- If it contains a field called ANY, then its type will be | ||
| 178 | -- used to check any unknown fields. If a field is prefixed | ||
| 179 | -- with MUST_, it is mandatory; its absence from the table is | ||
| 180 | -- a type error. | ||
| 181 | -- Tables are type checked recursively. | ||
| 182 | -- @param tbl table: The table to be type checked. | ||
| 183 | -- @param types table: The reference table, containing | ||
| 184 | -- values for recognized fields in the checked table. | ||
| 185 | -- @return boolean or (nil, string): true if type checking | ||
| 186 | -- succeeded, or nil and an error message if it failed. | ||
| 187 | type_check_table = function(tbl, types, context) | ||
| 188 | assert(type(tbl) == "table") | ||
| 189 | assert(type(types) == "table") | ||
| 190 | |||
| 191 | for k, v in pairs(tbl) do | ||
| 192 | local t = types[k] or (type(k) == "string" and types["MUST_"..k]) or types.ANY | ||
| 193 | if t then | ||
| 194 | local ok, err = type_check_item(k, v, t, context) | ||
| 195 | if not ok then return nil, err end | ||
| 196 | elseif types.MORE then | ||
| 197 | -- Accept unknown field | ||
| 198 | else | ||
| 199 | return nil, "Unknown field "..k | ||
| 200 | end | ||
| 201 | end | ||
| 202 | for k, v in pairs(types) do | ||
| 203 | local mandatory_key = k:match("^MUST_(.+)") | ||
| 204 | if mandatory_key then | ||
| 205 | if not tbl[mandatory_key] then | ||
| 206 | return nil, "Mandatory field "..context..mandatory_key.." is missing." | ||
| 207 | end | ||
| 208 | end | ||
| 209 | end | ||
| 210 | return true | ||
| 211 | end | ||
| 212 | |||
| 213 | --- Type check a rockspec table. | ||
| 214 | -- Verify the correctness of elements from a | ||
| 215 | -- rockspec table, reporting on unknown fields and type | ||
| 216 | -- mismatches. | ||
| 217 | -- @return boolean or (nil, string): true if type checking | ||
| 218 | -- succeeded, or nil and an error message if it failed. | ||
| 219 | function type_check_rockspec(rockspec) | ||
| 220 | assert(type(rockspec) == "table") | ||
| 221 | return type_check_table(rockspec, rockspec_types, "") | ||
| 222 | end | ||
| 223 | |||
| 224 | --- Type check a manifest table. | ||
| 225 | -- Verify the correctness of elements from a | ||
| 226 | -- manifest table, reporting on unknown fields and type | ||
| 227 | -- mismatches. | ||
| 228 | -- @return boolean or (nil, string): true if type checking | ||
| 229 | -- succeeded, or nil and an error message if it failed. | ||
| 230 | function type_check_manifest(manifest) | ||
| 231 | assert(type(manifest) == "table") | ||
| 232 | return type_check_table(manifest, manifest_types, "") | ||
| 233 | end | ||
diff --git a/src/luarocks/unpack.lua b/src/luarocks/unpack.lua new file mode 100644 index 00000000..60bc1295 --- /dev/null +++ b/src/luarocks/unpack.lua | |||
| @@ -0,0 +1,148 @@ | |||
| 1 | |||
| 2 | --- Module implementing the LuaRocks "unpack" command. | ||
| 3 | -- Unpack the contents of a rock. | ||
| 4 | module("luarocks.unpack", package.seeall) | ||
| 5 | |||
| 6 | local fetch = require("luarocks.fetch") | ||
| 7 | local fs = require("luarocks.fs") | ||
| 8 | local util = require("luarocks.util") | ||
| 9 | local build = require("luarocks.build") | ||
| 10 | |||
| 11 | help_summary = "Unpack the contents of a rock." | ||
| 12 | help_arguments = "{<rock>|<name> [<version>]}" | ||
| 13 | help = [[ | ||
| 14 | Unpacks the contents of a rock in a newly created directory. | ||
| 15 | Argument may be a rock file, or the name of a rock in a rocks server. | ||
| 16 | In the latter case, the app version may be given as a second argument. | ||
| 17 | ]] | ||
| 18 | |||
| 19 | --- Load a rockspec file to the given directory, fetches the source | ||
| 20 | -- files specified in the rockspec, and unpack them inside the directory. | ||
| 21 | -- @param rockspec_file string: The URL for a rockspec file. | ||
| 22 | -- @param dir_name string: The directory where to store and unpack files. | ||
| 23 | -- @return table or (nil, string): the loaded rockspec table or | ||
| 24 | -- nil and an error message. | ||
| 25 | local function unpack_rockspec(rockspec_file, dir_name) | ||
| 26 | assert(type(rockspec_file) == "string") | ||
| 27 | assert(type(dir_name) == "string") | ||
| 28 | |||
| 29 | local rockspec = fetch.load_rockspec(rockspec_file) | ||
| 30 | if not rockspec then | ||
| 31 | return nil, "Failed loading rockspec "..rockspec_file | ||
| 32 | end | ||
| 33 | fs.change_dir(dir_name) | ||
| 34 | local ok, sources_dir = fetch.fetch_sources(rockspec, true, ".") | ||
| 35 | if not ok then | ||
| 36 | return nil, sources_dir | ||
| 37 | end | ||
| 38 | fs.change_dir(dir_name) | ||
| 39 | build.apply_patches(rockspec) | ||
| 40 | fs.pop_dir() | ||
| 41 | return rockspec | ||
| 42 | end | ||
| 43 | |||
| 44 | --- Load a .rock file to the given directory and unpack it inside it. | ||
| 45 | -- @param rock_file string: The URL for a .rock file. | ||
| 46 | -- @param dir_name string: The directory where to unpack. | ||
| 47 | -- @return table or (nil, string): the loaded rockspec table or | ||
| 48 | -- nil and an error message. | ||
| 49 | local function unpack_rock(rock_file, dir_name, kind) | ||
| 50 | assert(type(rock_file) == "string") | ||
| 51 | assert(type(dir_name) == "string") | ||
| 52 | |||
| 53 | local ok, err = fetch.fetch_and_unpack_rock(rock_file, dir_name) | ||
| 54 | if not ok then | ||
| 55 | return nil, "Failed unzipping rock "..rock_file | ||
| 56 | end | ||
| 57 | fs.change_dir(dir_name) | ||
| 58 | local rockspec_file = dir_name..".rockspec" | ||
| 59 | local rockspec, err = fetch.load_rockspec(rockspec_file) | ||
| 60 | if not rockspec then | ||
| 61 | return nil, "Failed loading rockspec "..rockspec_file..": "..err | ||
| 62 | end | ||
| 63 | if kind == "src" then | ||
| 64 | if rockspec.source.file then | ||
| 65 | local ok, err = fs.unpack_archive(rockspec.source.file) | ||
| 66 | if not ok then | ||
| 67 | return nil, err | ||
| 68 | end | ||
| 69 | fs.change_dir(rockspec.source.dir) | ||
| 70 | build.apply_patches(rockspec) | ||
| 71 | fs.pop_dir() | ||
| 72 | end | ||
| 73 | end | ||
| 74 | return rockspec | ||
| 75 | end | ||
| 76 | |||
| 77 | --- Create a directory and perform the necessary actions so that | ||
| 78 | -- the sources for the rock and its rockspec are unpacked inside it, | ||
| 79 | -- laid out properly so that the 'make' command is able to build the module. | ||
| 80 | -- @param file string: A rockspec or .rock URL. | ||
| 81 | -- @return boolean or (nil, string): true if successful or nil followed | ||
| 82 | -- by an error message. | ||
| 83 | local function run_unpacker(file) | ||
| 84 | assert(type(file) == "string") | ||
| 85 | |||
| 86 | local base_name = fs.base_name(file) | ||
| 87 | local dir_name, kind, extension = base_name:match("(.*)%.([^.]+)%.(rock)$") | ||
| 88 | if not extension then | ||
| 89 | dir_name, extension = base_name:match("(.*)%.(rockspec)$") | ||
| 90 | kind = "rockspec" | ||
| 91 | end | ||
| 92 | if not extension then | ||
| 93 | return nil, file.." does not seem to be a valid filename." | ||
| 94 | end | ||
| 95 | |||
| 96 | if (fs.exists(dir_name)) then | ||
| 97 | return nil, "Directory "..dir_name.." already exists." | ||
| 98 | end | ||
| 99 | fs.make_dir(dir_name) | ||
| 100 | local rollback = util.schedule_function(fs.delete, fs.absolute_name(dir_name)) | ||
| 101 | |||
| 102 | local rockspec, err | ||
| 103 | if extension == "rock" then | ||
| 104 | rockspec, err = unpack_rock(file, dir_name, kind) | ||
| 105 | elseif extension == "rockspec" then | ||
| 106 | rockspec, err = unpack_rockspec(file, dir_name) | ||
| 107 | end | ||
| 108 | if not rockspec then | ||
| 109 | return nil, err | ||
| 110 | end | ||
| 111 | if kind == "src" or kind == "rockspec" then | ||
| 112 | if rockspec.source.dir ~= "." then | ||
| 113 | local ok = fs.copy(rockspec.local_filename, rockspec.source.dir) | ||
| 114 | if not ok then | ||
| 115 | return nil, "Failed copying unpacked rockspec into unpacked source directory." | ||
| 116 | end | ||
| 117 | end | ||
| 118 | print() | ||
| 119 | print("Done. You may now enter directory ") | ||
| 120 | print(fs.make_path(dir_name, rockspec.source.dir)) | ||
| 121 | print("and type 'luarocks make' to build.") | ||
| 122 | end | ||
| 123 | util.remove_scheduled_function(rollback) | ||
| 124 | return true | ||
| 125 | end | ||
| 126 | |||
| 127 | --- Driver function for the "unpack" command. | ||
| 128 | -- @param name string: may be a rock filename, for unpacking a | ||
| 129 | -- rock file or the name of a rock to be fetched and unpacked. | ||
| 130 | -- @param version string or nil: if the name of a package is given, a | ||
| 131 | -- version may also be passed. | ||
| 132 | -- @return boolean or (nil, string): true if successful or nil followed | ||
| 133 | -- by an error message. | ||
| 134 | function run(...) | ||
| 135 | local flags, name, version = util.parse_flags(...) | ||
| 136 | |||
| 137 | assert(type(version) == "string" or not version) | ||
| 138 | if type(name) ~= "string" then | ||
| 139 | return nil, "Argument missing, see help." | ||
| 140 | end | ||
| 141 | |||
| 142 | if name:match(".*%.rock") or name:match(".*%.rockspec") then | ||
| 143 | return run_unpacker(name) | ||
| 144 | else | ||
| 145 | local search = require("luarocks.search") | ||
| 146 | return search.act_on_src_or_rockspec(run_unpacker, name, version) | ||
| 147 | end | ||
| 148 | end | ||
diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua new file mode 100644 index 00000000..50efaa9d --- /dev/null +++ b/src/luarocks/util.lua | |||
| @@ -0,0 +1,180 @@ | |||
| 1 | |||
| 2 | local global_env = _G | ||
| 3 | |||
| 4 | --- Utility functions shared by other modules. | ||
| 5 | -- Does not requires modules directly (only as locals | ||
| 6 | -- inside specific functions) to avoid interdependencies, | ||
| 7 | -- as this is used in the bootstrapping stage of luarocks.cfg. | ||
| 8 | module("luarocks.util", package.seeall) | ||
| 9 | |||
| 10 | local scheduled_functions = {} | ||
| 11 | |||
| 12 | --- Schedule a function to be executed upon program termination. | ||
| 13 | -- This is useful for actions such as deleting temporary directories | ||
| 14 | -- or failure rollbacks. | ||
| 15 | -- @param f function: Function to be executed. | ||
| 16 | -- @param ... arguments to be passed to function. | ||
| 17 | -- @return table: A token representing the scheduled execution, | ||
| 18 | -- which can be used to remove the item later from the list. | ||
| 19 | function schedule_function(f, ...) | ||
| 20 | assert(type(f) == "function") | ||
| 21 | |||
| 22 | local item = { fn = f, args = {...} } | ||
| 23 | table.insert(scheduled_functions, item) | ||
| 24 | return item | ||
| 25 | end | ||
| 26 | |||
| 27 | --- Unschedule a function. | ||
| 28 | -- This is useful for cancelling a rollback of a completed operation. | ||
| 29 | -- @param table: The token representing the scheduled function that was | ||
| 30 | -- returned from the schedule_function call. | ||
| 31 | function remove_scheduled_function(item) | ||
| 32 | for k, v in pairs(scheduled_functions) do | ||
| 33 | if v == item then | ||
| 34 | table.remove(scheduled_functions, k) | ||
| 35 | return | ||
| 36 | end | ||
| 37 | end | ||
| 38 | end | ||
| 39 | |||
| 40 | --- Execute scheduled functions. | ||
| 41 | -- Some calls create temporary files and/or directories and register | ||
| 42 | -- corresponding cleanup functions. Calling this function will run | ||
| 43 | -- these function, erasing temporaries. | ||
| 44 | -- Functions are executed in the inverse order they were scheduled. | ||
| 45 | function run_scheduled_functions() | ||
| 46 | local fs = require("luarocks.fs") | ||
| 47 | fs.change_dir_to_root() | ||
| 48 | for i = #scheduled_functions, 1, -1 do | ||
| 49 | local item = scheduled_functions[i] | ||
| 50 | item.fn(unpack(item.args)) | ||
| 51 | end | ||
| 52 | end | ||
| 53 | |||
| 54 | --- Extract flags from an arguments list. | ||
| 55 | -- Given string arguments, extract flag arguments into a flags set. | ||
| 56 | -- For example, given "foo", "--tux=beep", "--bla", "bar", "--baz", | ||
| 57 | -- it would return the following: | ||
| 58 | -- {["bla"] = true, ["tux"] = "beep", ["baz"] = true}, "foo", "bar". | ||
| 59 | function parse_flags(...) | ||
| 60 | local args = {...} | ||
| 61 | local flags = {} | ||
| 62 | for i = #args, 1, -1 do | ||
| 63 | local flag = args[i]:match("^%-%-(.*)") | ||
| 64 | if flag then | ||
| 65 | local var,val = flag:match("([a-z_%-]*)=(.*)") | ||
| 66 | if val then | ||
| 67 | flags[var] = val | ||
| 68 | else | ||
| 69 | flags[flag] = true | ||
| 70 | end | ||
| 71 | table.remove(args, i) | ||
| 72 | end | ||
| 73 | end | ||
| 74 | return flags, unpack(args) | ||
| 75 | end | ||
| 76 | |||
| 77 | --- Merges contents of src on top of dst's contents. | ||
| 78 | -- @param dst Destination table, which will receive src's contents. | ||
| 79 | -- @param src Table which provides new contents to dst. | ||
| 80 | -- @see platform_overrides | ||
| 81 | function deep_merge(dst, src) | ||
| 82 | for k, v in pairs(src) do | ||
| 83 | if type(v) == "table" then | ||
| 84 | if not dst[k] then | ||
| 85 | dst[k] = {} | ||
| 86 | end | ||
| 87 | deep_merge(dst[k], v) | ||
| 88 | else | ||
| 89 | dst[k] = v | ||
| 90 | end | ||
| 91 | end | ||
| 92 | end | ||
| 93 | |||
| 94 | --- Perform platform-specific overrides on a table. | ||
| 95 | -- Overrides values of table with the contents of the appropriate | ||
| 96 | -- subset of its "platforms" field. The "platforms" field should | ||
| 97 | -- be a table containing subtables keyed with strings representing | ||
| 98 | -- platform names. Names that match the contents of the global | ||
| 99 | -- cfg.platforms setting are used. For example, if | ||
| 100 | -- cfg.platforms= {"foo"}, then the fields of | ||
| 101 | -- tbl.platforms.foo will overwrite those of tbl with the same | ||
| 102 | -- names. For table values, the operation is performed recursively | ||
| 103 | -- (tbl.platforms.foo.x.y.z overrides tbl.x.y.z; other contents of | ||
| 104 | -- tbl.x are preserved). | ||
| 105 | -- @param tbl table or nil: Table which may contain a "platforms" field; | ||
| 106 | -- if it doesn't (or if nil is passed), this function does nothing. | ||
| 107 | function platform_overrides(tbl) | ||
| 108 | assert(type(tbl) == "table" or not tbl) | ||
| 109 | |||
| 110 | local cfg = require("luarocks.cfg") | ||
| 111 | |||
| 112 | if not tbl then return end | ||
| 113 | |||
| 114 | if tbl.platforms then | ||
| 115 | for _, platform in ipairs(cfg.platforms) do | ||
| 116 | local platform_tbl = tbl.platforms[platform] | ||
| 117 | if platform_tbl then | ||
| 118 | deep_merge(tbl, platform_tbl) | ||
| 119 | end | ||
| 120 | end | ||
| 121 | end | ||
| 122 | tbl.platforms = nil | ||
| 123 | end | ||
| 124 | |||
| 125 | --- Perform make-style variable substitutions on string values of a table. | ||
| 126 | -- For every string value tbl.x which contains a substring of the format | ||
| 127 | -- "$(XYZ)" will have this substring replaced by vars["XYZ"], if that field | ||
| 128 | -- exists in vars. Only string values are processed; this function | ||
| 129 | -- does not scan subtables recursively. | ||
| 130 | -- @param tbl table: Table to have its string values modified. | ||
| 131 | -- @param vars table: Table containing string-string key-value pairs | ||
| 132 | -- representing variables to replace in the strings values of tbl. | ||
| 133 | function variable_substitutions(tbl, vars) | ||
| 134 | assert(type(tbl) == "table") | ||
| 135 | assert(type(vars) == "table") | ||
| 136 | |||
| 137 | local updated = {} | ||
| 138 | for k, v in pairs(tbl) do | ||
| 139 | if type(v) == "string" then | ||
| 140 | updated[k] = v:gsub("%$%((%a[%a%d_]+)%)", vars) | ||
| 141 | end | ||
| 142 | end | ||
| 143 | for k, v in pairs(updated) do | ||
| 144 | tbl[k] = v | ||
| 145 | end | ||
| 146 | end | ||
| 147 | |||
| 148 | --- Return an array of keys of a table. | ||
| 149 | -- @param tbl table: The input table. | ||
| 150 | -- @return table: The array of keys. | ||
| 151 | function keys(tbl) | ||
| 152 | local ks = {} | ||
| 153 | for k,_ in pairs(tbl) do | ||
| 154 | table.insert(ks, k) | ||
| 155 | end | ||
| 156 | return ks | ||
| 157 | end | ||
| 158 | |||
| 159 | -- The iterator function used internally by util.sortedpairs. | ||
| 160 | -- @param tbl table: The table to be iterated. | ||
| 161 | -- @param sort_function function or nil: An optional comparison function | ||
| 162 | -- to be used by table.sort when sorting keys. | ||
| 163 | -- @see sortedpairs | ||
| 164 | local function sortedpairs_iterator(tbl, sort_function) | ||
| 165 | local ks = keys(tbl) | ||
| 166 | table.sort(ks, sort_function) | ||
| 167 | for _, k in ipairs(ks) do | ||
| 168 | coroutine.yield(k, tbl[k]) | ||
| 169 | end | ||
| 170 | end | ||
| 171 | |||
| 172 | --- A table iterator generator that returns elements sorted by key, | ||
| 173 | -- to be used in "for" loops. | ||
| 174 | -- @param tbl table: The table to be iterated. | ||
| 175 | -- @param sort_function function or nil: An optional comparison function | ||
| 176 | -- to be used by table.sort when sorting keys. | ||
| 177 | -- @return function: the iterator function. | ||
| 178 | function sortedpairs(tbl, sort_function) | ||
| 179 | return coroutine.wrap(function() sortedpairs_iterator(tbl, sort_function) end) | ||
| 180 | end | ||
diff --git a/test/run_tests.sh b/test/run_tests.sh new file mode 100755 index 00000000..aa06854f --- /dev/null +++ b/test/run_tests.sh | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | |||
| 3 | if [ -e ./run_tests.sh ] | ||
| 4 | then | ||
| 5 | cd ../src | ||
| 6 | elif [ -d src ] | ||
| 7 | then | ||
| 8 | cd src | ||
| 9 | elif ! [ -d luarocks ] | ||
| 10 | then | ||
| 11 | echo "Go to the src directory and run this." | ||
| 12 | exit 1 | ||
| 13 | fi | ||
| 14 | |||
| 15 | if [ ! -d ../rocks ] | ||
| 16 | then | ||
| 17 | echo "Downloading entire rocks repository for tests" | ||
| 18 | cd .. | ||
| 19 | wget -r -nH -np -R"index.*" http://luarocks.luaforge.net/rocks/ | ||
| 20 | cd src | ||
| 21 | fi | ||
| 22 | |||
| 23 | rocks=( | ||
| 24 | `ls ../rocks/*.rockspec | grep -v luacom` | ||
| 25 | `ls ../rocks/*.src.rock | grep -v luacom` | ||
| 26 | ) | ||
| 27 | |||
| 28 | bin/luarocks-admin make-manifest ../rocks || exit 1 | ||
| 29 | |||
| 30 | [ "$1" ] && rocks=("$1") | ||
| 31 | |||
| 32 | TRY() { | ||
| 33 | "$@" || { | ||
| 34 | echo "Failed running: $@" | ||
| 35 | exit 1 | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | list_search() { | ||
| 40 | bin/luarocks list $name | grep $version | ||
| 41 | } | ||
| 42 | |||
| 43 | for rock in "${rocks[@]}" | ||
| 44 | do | ||
| 45 | base=`basename $rock` | ||
| 46 | baserockspec=`basename $rock .rockspec` | ||
| 47 | basesrcrock=`basename $rock .src.rock` | ||
| 48 | if [ "$base" != "$baserockspec" ] | ||
| 49 | then | ||
| 50 | base=$baserockspec | ||
| 51 | name=`echo $base | sed 's/\(.*\)-[^-]*-[^-]*$/\1/'` | ||
| 52 | version=`echo $base | sed 's/.*-\([^-]*-[^-]*\)$/\1/'` | ||
| 53 | TRY bin/luarocks pack $rock | ||
| 54 | TRY bin/luarocks build $base.src.rock | ||
| 55 | TRY rm $base.src.rock | ||
| 56 | else | ||
| 57 | base=$basesrcrock | ||
| 58 | name=`echo $base | sed 's/\(.*\)-[^-]*-[^-]*$/\1/'` | ||
| 59 | version=`echo $base | sed 's/.*-\([^-]*-[^-]*\)$/\1/'` | ||
| 60 | TRY bin/luarocks build $rock | ||
| 61 | fi | ||
| 62 | TRY bin/luarocks pack $name $version | ||
| 63 | TRY bin/luarocks install $base.*.rock | ||
| 64 | TRY rm $base.*.rock | ||
| 65 | TRY list_search $name $version | ||
| 66 | bin/luarocks remove $name $version | ||
| 67 | # TODO: differentiate between error and dependency block. | ||
| 68 | done | ||
| 69 | |||
| 70 | if bin/luarocks install nonexistant | grep "No results" | ||
| 71 | then echo "OK, got expected error." | ||
| 72 | else exit 1 | ||
| 73 | fi | ||
| 74 | |||
| 75 | TRY ../test/test_deps.lua | ||
| 76 | TRY ../test/test_require.lua | ||
diff --git a/test/test_deps.lua b/test/test_deps.lua new file mode 100644 index 00000000..7236273c --- /dev/null +++ b/test/test_deps.lua | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | #!/usr/bin/env lua | ||
| 2 | |||
| 3 | deps = require "luarocks.deps" | ||
| 4 | |||
| 5 | print(deps.show_dep(deps.parse_dep("lfs 2.1.9pre5"), true)) | ||
| 6 | print(deps.show_dep(deps.parse_dep("cgilua cvs-2"), true)) | ||
| 7 | print(deps.show_dep(deps.parse_dep("foobar 0.0.1beta"), true)) | ||
| 8 | print(deps.show_dep(deps.parse_dep("foobar 0.0.1a"), true)) | ||
| 9 | |||
| 10 | print(deps.show_dep(deps.parse_dep("foobar 1"), true)) | ||
| 11 | print(deps.show_dep(deps.parse_dep("foobar 2.0"), true)) | ||
| 12 | print(deps.show_dep(deps.parse_dep("foobar 3.5a4"), true)) | ||
| 13 | print(deps.show_dep(deps.parse_dep("foobar 1.1pre2"), true)) | ||
| 14 | print(deps.show_dep(deps.parse_dep("foobar 2.0-beta3"), true)) | ||
| 15 | print(deps.show_dep(deps.parse_dep("foobar 5.3"), true)) | ||
| 16 | print(deps.show_dep(deps.parse_dep("foobar 3.5rc2"), true)) | ||
| 17 | print(deps.show_dep(deps.parse_dep("foobar 4.19p"), true)) | ||
| 18 | |||
| 19 | print() | ||
| 20 | comparisons = { | ||
| 21 | -- first second eq le | ||
| 22 | {"Vista", "XP", false, true}, | ||
| 23 | {"XP", "3.1", false, true}, | ||
| 24 | {"1.0", "1.0", true, false}, | ||
| 25 | {"2.2.10", "2.2-10", false, false}, | ||
| 26 | {"2.2", "2.2-10", true, false}, | ||
| 27 | {"1.0beta1", "1.0rc3", false, true}, | ||
| 28 | {"2.0beta3", "2.0", false, true}, | ||
| 29 | {"2.0beta", "2.0beta2", false, true}, | ||
| 30 | {"2.0beta4", "2.0beta3", false, false}, | ||
| 31 | {"2.1alpha1", "2.0beta1", false, false}, | ||
| 32 | {"1.5p3", "1.5.1", false, true}, | ||
| 33 | {"1.1.3", "1.1.3a", false, true}, | ||
| 34 | {"1.5a100", "1.5b1", false, true}, | ||
| 35 | {"2.0alpha100", "2.0beta1", false, true}, | ||
| 36 | {"2.0.0beta3", "2.0beta2", false, false}, | ||
| 37 | {"2.0-1", "2.0-2", false, true}, | ||
| 38 | {"2.0-2", "2.0-1", false, false}, | ||
| 39 | --[[ | ||
| 40 | -- Corner cases I don't wish to handle by now. | ||
| 41 | {"2.0.0beta2", "2.0beta2", true, true}, | ||
| 42 | {"2.0.0beta2", "2.0beta3", false, true}, | ||
| 43 | ]] | ||
| 44 | } | ||
| 45 | |||
| 46 | local v1, v2 | ||
| 47 | |||
| 48 | err = false | ||
| 49 | |||
| 50 | function result(test, expected) | ||
| 51 | if test == expected then | ||
| 52 | print(test, "OK") | ||
| 53 | else | ||
| 54 | print(test, "ERROR", deps.show_version(v1, true), deps.show_version(v2, true)) | ||
| 55 | err = true | ||
| 56 | end | ||
| 57 | end | ||
| 58 | |||
| 59 | for _, c in ipairs(comparisons) do | ||
| 60 | v1, v2 = deps.parse_version(c[1]), deps.parse_version(c[2]) | ||
| 61 | print(c[1].." == "..c[2].." ?") | ||
| 62 | result(v1 == v2, c[3]) | ||
| 63 | print(c[1].." < "..c[2].." ?") | ||
| 64 | result(v1 < v2, c[4]) | ||
| 65 | end | ||
| 66 | |||
| 67 | if err then os.exit(1) end | ||
diff --git a/test/test_require.lua b/test/test_require.lua new file mode 100755 index 00000000..2a652d29 --- /dev/null +++ b/test/test_require.lua | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #!/usr/bin/env lua | ||
| 2 | |||
| 3 | local luarocks = require("luarocks.require") | ||
| 4 | |||
| 5 | luarocks.set_context("cgilua", "cvs-2") | ||
| 6 | |||
| 7 | print(package.path) | ||
| 8 | |||
| 9 | print(package.cpath) | ||
| 10 | |||
| 11 | local socket = require("socket") | ||
| 12 | if not socket then os.exit(1) end | ||
| 13 | print(socket, socket._VERSION) | ||
| 14 | |||
| 15 | local socket2 = require("socket") | ||
| 16 | if not socket2 then os.exit(1) end | ||
| 17 | print(socket2, socket2._VERSION) | ||
| 18 | |||
| 19 | local mime = require("mime") | ||
| 20 | if not mime then os.exit(1) end | ||
| 21 | print(mime, mime._VERSION) | ||
| 22 | |||
| 23 | local socket = require("lfs") | ||
| 24 | if not lfs then os.exit(1) end | ||
| 25 | print(lfs, lfs._VERSION) | ||
