aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHisham Muhammad <hisham@gobolinux.org>2017-09-13 12:46:22 -0300
committerHisham Muhammad <hisham@gobolinux.org>2017-09-13 12:46:22 -0300
commitf1473f71235bb89555dea7041b536144d32bac8c (patch)
tree5c0b9215801ea1711c7919c1b42085d1b5687075 /src
parent1eb598706f9000361f0059a303c3cf99be5a8ceb (diff)
parent9c853b1e7ec0f30e0e539fb56d7c81edbe5c8d7d (diff)
downloadluarocks-f1473f71235bb89555dea7041b536144d32bac8c.tar.gz
luarocks-f1473f71235bb89555dea7041b536144d32bac8c.tar.bz2
luarocks-f1473f71235bb89555dea7041b536144d32bac8c.zip
Merge branch 'luarocks-3'
Diffstat (limited to 'src')
-rwxr-xr-xsrc/bin/luarocks40
-rwxr-xr-xsrc/bin/luarocks-admin12
-rw-r--r--src/luarocks/admin/cache.lua (renamed from src/luarocks/cache.lua)3
-rw-r--r--src/luarocks/admin/cmd/add.lua (renamed from src/luarocks/add.lua)21
-rw-r--r--src/luarocks/admin/cmd/make_manifest.lua (renamed from src/luarocks/make_manifest.lua)8
-rw-r--r--src/luarocks/admin/cmd/refresh_cache.lua (renamed from src/luarocks/refresh_cache.lua)7
-rw-r--r--src/luarocks/admin/cmd/remove.lua (renamed from src/luarocks/admin_remove.lua)10
-rw-r--r--src/luarocks/build.lua147
-rw-r--r--src/luarocks/build/builtin.lua37
-rw-r--r--src/luarocks/build/cmake.lua23
-rw-r--r--src/luarocks/build/make.lua2
-rw-r--r--src/luarocks/cmd/build.lua119
-rw-r--r--src/luarocks/cmd/config.lua (renamed from src/luarocks/config_cmd.lua)3
-rw-r--r--src/luarocks/cmd/doc.lua (renamed from src/luarocks/doc.lua)2
-rw-r--r--src/luarocks/cmd/download.lua45
-rw-r--r--src/luarocks/cmd/help.lua (renamed from src/luarocks/help.lua)5
-rw-r--r--src/luarocks/cmd/install.lua (renamed from src/luarocks/install.lua)14
-rw-r--r--src/luarocks/cmd/lint.lua (renamed from src/luarocks/lint.lua)2
-rw-r--r--src/luarocks/cmd/list.lua (renamed from src/luarocks/list.lua)12
-rw-r--r--src/luarocks/cmd/make.lua (renamed from src/luarocks/make.lua)8
-rw-r--r--src/luarocks/cmd/new_version.lua (renamed from src/luarocks/new_version.lua)1
-rw-r--r--src/luarocks/cmd/pack.lua45
-rw-r--r--src/luarocks/cmd/path.lua (renamed from src/luarocks/path_cmd.lua)3
-rw-r--r--src/luarocks/cmd/purge.lua (renamed from src/luarocks/purge.lua)16
-rw-r--r--src/luarocks/cmd/remove.lua67
-rw-r--r--src/luarocks/cmd/search.lua74
-rw-r--r--src/luarocks/cmd/show.lua (renamed from src/luarocks/show.lua)16
-rw-r--r--src/luarocks/cmd/unpack.lua (renamed from src/luarocks/unpack.lua)4
-rw-r--r--src/luarocks/cmd/upload.lua (renamed from src/luarocks/upload.lua)3
-rw-r--r--src/luarocks/cmd/write_rockspec.lua (renamed from src/luarocks/write_rockspec.lua)7
-rw-r--r--src/luarocks/command_line.lua2
-rw-r--r--src/luarocks/core/cfg.lua (renamed from src/luarocks/cfg.lua)34
-rw-r--r--src/luarocks/core/dir.lua43
-rw-r--r--src/luarocks/core/manif.lua (renamed from src/luarocks/manif_core.lua)58
-rw-r--r--src/luarocks/core/path.lua114
-rw-r--r--src/luarocks/core/persist.lua81
-rw-r--r--src/luarocks/core/type_check.lua229
-rw-r--r--src/luarocks/core/util.lua272
-rw-r--r--src/luarocks/core/vers.lua191
-rw-r--r--src/luarocks/deps.lua378
-rw-r--r--src/luarocks/dir.lua37
-rw-r--r--src/luarocks/download.lua45
-rw-r--r--src/luarocks/fetch.lua46
-rw-r--r--src/luarocks/fetch/git.lua57
-rw-r--r--src/luarocks/fs.lua3
-rw-r--r--src/luarocks/fs/lua.lua8
-rw-r--r--src/luarocks/fs/tools.lua2
-rw-r--r--src/luarocks/fs/unix.lua2
-rw-r--r--src/luarocks/fs/unix/tools.lua2
-rw-r--r--src/luarocks/fs/win32.lua2
-rw-r--r--src/luarocks/fs/win32/tools.lua2
-rw-r--r--src/luarocks/index.lua5
-rw-r--r--src/luarocks/loader.lua39
-rw-r--r--src/luarocks/manif.lua476
-rw-r--r--src/luarocks/manif/writer.lua437
-rw-r--r--src/luarocks/pack.lua53
-rw-r--r--src/luarocks/path.lua139
-rw-r--r--src/luarocks/persist.lua75
-rw-r--r--src/luarocks/remove.lua61
-rw-r--r--src/luarocks/repos.lua126
-rw-r--r--src/luarocks/search.lua87
-rw-r--r--src/luarocks/tools/tar.lua6
-rw-r--r--src/luarocks/type_check.lua232
-rw-r--r--src/luarocks/upload/api.lua2
-rw-r--r--src/luarocks/util.lua271
-rw-r--r--src/luarocks/validate.lua159
-rw-r--r--src/luarocks/vers.lua138
67 files changed, 2403 insertions, 2267 deletions
diff --git a/src/bin/luarocks b/src/bin/luarocks
index be6c2b81..88a1d1ca 100755
--- a/src/bin/luarocks
+++ b/src/bin/luarocks
@@ -1,7 +1,7 @@
1#!/usr/bin/env lua 1#!/usr/bin/env lua
2 2
3-- this should be loaded first. 3-- this should be loaded first.
4local cfg = require("luarocks.cfg") 4local cfg = require("luarocks.core.cfg")
5 5
6local loader = require("luarocks.loader") 6local loader = require("luarocks.loader")
7local command_line = require("luarocks.command_line") 7local command_line = require("luarocks.command_line")
@@ -9,25 +9,25 @@ local command_line = require("luarocks.command_line")
9program_description = "LuaRocks main command-line interface" 9program_description = "LuaRocks main command-line interface"
10 10
11commands = { 11commands = {
12 help = "luarocks.help", 12 help = "luarocks.cmd.help",
13 pack = "luarocks.pack", 13 pack = "luarocks.cmd.pack",
14 unpack = "luarocks.unpack", 14 unpack = "luarocks.cmd.unpack",
15 build = "luarocks.build", 15 build = "luarocks.cmd.build",
16 install = "luarocks.install", 16 install = "luarocks.cmd.install",
17 search = "luarocks.search", 17 search = "luarocks.cmd.search",
18 list = "luarocks.list", 18 list = "luarocks.cmd.list",
19 remove = "luarocks.remove", 19 remove = "luarocks.cmd.remove",
20 make = "luarocks.make", 20 make = "luarocks.cmd.make",
21 download = "luarocks.download", 21 download = "luarocks.cmd.download",
22 path = "luarocks.path_cmd", 22 path = "luarocks.cmd.path",
23 show = "luarocks.show", 23 show = "luarocks.cmd.show",
24 new_version = "luarocks.new_version", 24 new_version = "luarocks.cmd.new_version",
25 lint = "luarocks.lint", 25 lint = "luarocks.cmd.lint",
26 write_rockspec = "luarocks.write_rockspec", 26 write_rockspec = "luarocks.cmd.write_rockspec",
27 purge = "luarocks.purge", 27 purge = "luarocks.cmd.purge",
28 doc = "luarocks.doc", 28 doc = "luarocks.cmd.doc",
29 upload = "luarocks.upload", 29 upload = "luarocks.cmd.upload",
30 config = "luarocks.config_cmd", 30 config = "luarocks.cmd.config",
31} 31}
32 32
33command_line.run_command(...) 33command_line.run_command(...)
diff --git a/src/bin/luarocks-admin b/src/bin/luarocks-admin
index 2890d1f1..5db24640 100755
--- a/src/bin/luarocks-admin
+++ b/src/bin/luarocks-admin
@@ -1,7 +1,7 @@
1#!/usr/bin/env lua 1#!/usr/bin/env lua
2 2
3-- this should be loaded first. 3-- this should be loaded first.
4local cfg = require("luarocks.cfg") 4local cfg = require("luarocks.core.cfg")
5 5
6local loader = require("luarocks.loader") 6local loader = require("luarocks.loader")
7local command_line = require("luarocks.command_line") 7local command_line = require("luarocks.command_line")
@@ -9,11 +9,11 @@ local command_line = require("luarocks.command_line")
9program_description = "LuaRocks repository administration interface" 9program_description = "LuaRocks repository administration interface"
10 10
11commands = { 11commands = {
12 help = "luarocks.help", 12 help = "luarocks.cmd.help",
13 make_manifest = "luarocks.make_manifest", 13 make_manifest = "luarocks.admin.cmd.make_manifest",
14 add = "luarocks.add", 14 add = "luarocks.admin.cmd.add",
15 remove = "luarocks.admin_remove", 15 remove = "luarocks.admin.cmd.remove",
16 refresh_cache = "luarocks.refresh_cache", 16 refresh_cache = "luarocks.admin.cmd.refresh_cache",
17} 17}
18 18
19command_line.run_command(...) 19command_line.run_command(...)
diff --git a/src/luarocks/cache.lua b/src/luarocks/admin/cache.lua
index 4a95f70e..0daa0fc0 100644
--- a/src/luarocks/cache.lua
+++ b/src/luarocks/admin/cache.lua
@@ -2,10 +2,9 @@
2--- Module handling the LuaRocks local cache. 2--- Module handling the LuaRocks local cache.
3-- Adds a rock or rockspec to a rocks server. 3-- Adds a rock or rockspec to a rocks server.
4local cache = {} 4local cache = {}
5package.loaded["luarocks.cache"] = cache
6 5
7local fs = require("luarocks.fs") 6local fs = require("luarocks.fs")
8local cfg = require("luarocks.cfg") 7local cfg = require("luarocks.core.cfg")
9local dir = require("luarocks.dir") 8local dir = require("luarocks.dir")
10local util = require("luarocks.util") 9local util = require("luarocks.util")
11 10
diff --git a/src/luarocks/add.lua b/src/luarocks/admin/cmd/add.lua
index f37d334d..daf46c1d 100644
--- a/src/luarocks/add.lua
+++ b/src/luarocks/admin/cmd/add.lua
@@ -2,17 +2,15 @@
2--- Module implementing the luarocks-admin "add" command. 2--- Module implementing the luarocks-admin "add" command.
3-- Adds a rock or rockspec to a rocks server. 3-- Adds a rock or rockspec to a rocks server.
4local add = {} 4local add = {}
5package.loaded["luarocks.add"] = add
6 5
7local cfg = require("luarocks.cfg") 6local cfg = require("luarocks.core.cfg")
8local util = require("luarocks.util") 7local util = require("luarocks.util")
9local dir = require("luarocks.dir") 8local dir = require("luarocks.dir")
10local manif = require("luarocks.manif") 9local writer = require("luarocks.manif.writer")
11local index = require("luarocks.index") 10local index = require("luarocks.index")
12local fs = require("luarocks.fs") 11local fs = require("luarocks.fs")
13local cache = require("luarocks.cache") 12local cache = require("luarocks.admin.cache")
14 13
15util.add_run_function(add)
16add.help_summary = "Add a rock or rockspec to a rocks server." 14add.help_summary = "Add a rock or rockspec to a rocks server."
17add.help_arguments = "[--server=<server>] [--no-refresh] {<rockspec>|<rock>...}" 15add.help_arguments = "[--server=<server>] [--no-refresh] {<rockspec>|<rock>...}"
18add.help = [[ 16add.help = [[
@@ -24,6 +22,15 @@ The flag --no-refresh indicates the local cache should not be refreshed
24prior to generation of the updated manifest. 22prior to generation of the updated manifest.
25]] 23]]
26 24
25local function zip_manifests()
26 for ver in util.lua_versions() do
27 local file = "manifest-"..ver
28 local zip = file..".zip"
29 fs.delete(dir.path(fs.current_dir(), zip))
30 fs.zip(zip, file)
31 end
32end
33
27local function add_files_to_server(refresh, rockfiles, server, upload_server) 34local function add_files_to_server(refresh, rockfiles, server, upload_server)
28 assert(type(refresh) == "boolean" or not refresh) 35 assert(type(refresh) == "boolean" or not refresh)
29 assert(type(rockfiles) == "table") 36 assert(type(rockfiles) == "table")
@@ -68,9 +75,9 @@ local function add_files_to_server(refresh, rockfiles, server, upload_server)
68 if not ok then return nil, err end 75 if not ok then return nil, err end
69 76
70 util.printout("Updating manifest...") 77 util.printout("Updating manifest...")
71 manif.make_manifest(local_cache, "one", true) 78 writer.make_manifest(local_cache, "one", true)
72 79
73 manif.zip_manifests() 80 zip_manifests()
74 81
75 util.printout("Updating index.html...") 82 util.printout("Updating index.html...")
76 index.make_index(local_cache) 83 index.make_index(local_cache)
diff --git a/src/luarocks/make_manifest.lua b/src/luarocks/admin/cmd/make_manifest.lua
index c39c2939..57851942 100644
--- a/src/luarocks/make_manifest.lua
+++ b/src/luarocks/admin/cmd/make_manifest.lua
@@ -2,17 +2,15 @@
2--- Module implementing the luarocks-admin "make_manifest" command. 2--- Module implementing the luarocks-admin "make_manifest" command.
3-- Compile a manifest file for a repository. 3-- Compile a manifest file for a repository.
4local make_manifest = {} 4local make_manifest = {}
5package.loaded["luarocks.make_manifest"] = make_manifest
6 5
7local manif = require("luarocks.manif") 6local writer = require("luarocks.manif.writer")
8local index = require("luarocks.index") 7local index = require("luarocks.index")
9local cfg = require("luarocks.cfg") 8local cfg = require("luarocks.core.cfg")
10local util = require("luarocks.util") 9local util = require("luarocks.util")
11local deps = require("luarocks.deps") 10local deps = require("luarocks.deps")
12local fs = require("luarocks.fs") 11local fs = require("luarocks.fs")
13local dir = require("luarocks.dir") 12local dir = require("luarocks.dir")
14 13
15util.add_run_function(make_manifest)
16make_manifest.help_summary = "Compile a manifest file for a repository." 14make_manifest.help_summary = "Compile a manifest file for a repository."
17 15
18make_manifest.help = [[ 16make_manifest.help = [[
@@ -37,7 +35,7 @@ function make_manifest.command(flags, repo)
37 util.warning("This looks like a local rocks tree, but you did not pass --local-tree.") 35 util.warning("This looks like a local rocks tree, but you did not pass --local-tree.")
38 end 36 end
39 37
40 local ok, err = manif.make_manifest(repo, deps.get_deps_mode(flags), not flags["local-tree"]) 38 local ok, err = writer.make_manifest(repo, deps.get_deps_mode(flags), not flags["local-tree"])
41 if ok and not flags["local-tree"] then 39 if ok and not flags["local-tree"] then
42 util.printout("Generating index.html for "..repo) 40 util.printout("Generating index.html for "..repo)
43 index.make_index(repo) 41 index.make_index(repo)
diff --git a/src/luarocks/refresh_cache.lua b/src/luarocks/admin/cmd/refresh_cache.lua
index bbfd1f4d..947dbfb0 100644
--- a/src/luarocks/refresh_cache.lua
+++ b/src/luarocks/admin/cmd/refresh_cache.lua
@@ -1,13 +1,10 @@
1 1
2--- Module implementing the luarocks-admin "refresh_cache" command. 2--- Module implementing the luarocks-admin "refresh_cache" command.
3local refresh_cache = {} 3local refresh_cache = {}
4package.loaded["luarocks.refresh_cache"] = refresh_cache
5 4
6local util = require("luarocks.util") 5local cfg = require("luarocks.core.cfg")
7local cfg = require("luarocks.cfg") 6local cache = require("luarocks.admin.cache")
8local cache = require("luarocks.cache")
9 7
10util.add_run_function(refresh_cache)
11refresh_cache.help_summary = "Refresh local cache of a remote rocks server." 8refresh_cache.help_summary = "Refresh local cache of a remote rocks server."
12refresh_cache.help_arguments = "[--from=<server>]" 9refresh_cache.help_arguments = "[--from=<server>]"
13refresh_cache.help = [[ 10refresh_cache.help = [[
diff --git a/src/luarocks/admin_remove.lua b/src/luarocks/admin/cmd/remove.lua
index 621f1317..763a166f 100644
--- a/src/luarocks/admin_remove.lua
+++ b/src/luarocks/admin/cmd/remove.lua
@@ -2,17 +2,15 @@
2--- Module implementing the luarocks-admin "remove" command. 2--- Module implementing the luarocks-admin "remove" command.
3-- Removes a rock or rockspec from a rocks server. 3-- Removes a rock or rockspec from a rocks server.
4local admin_remove = {} 4local admin_remove = {}
5package.loaded["luarocks.admin_remove"] = admin_remove
6 5
7local cfg = require("luarocks.cfg") 6local cfg = require("luarocks.core.cfg")
8local util = require("luarocks.util") 7local util = require("luarocks.util")
9local dir = require("luarocks.dir") 8local dir = require("luarocks.dir")
10local manif = require("luarocks.manif") 9local writer = require("luarocks.manif.writer")
11local index = require("luarocks.index") 10local index = require("luarocks.index")
12local fs = require("luarocks.fs") 11local fs = require("luarocks.fs")
13local cache = require("luarocks.cache") 12local cache = require("luarocks.admin.cache")
14 13
15util.add_run_function(admin_remove)
16admin_remove.help_summary = "Remove a rock or rockspec from a rocks server." 14admin_remove.help_summary = "Remove a rock or rockspec from a rocks server."
17admin_remove.help_arguments = "[--server=<server>] [--no-refresh] {<rockspec>|<rock>...}" 15admin_remove.help_arguments = "[--server=<server>] [--no-refresh] {<rockspec>|<rock>...}"
18admin_remove.help = [[ 16admin_remove.help = [[
@@ -65,7 +63,7 @@ local function remove_files_from_server(refresh, rockfiles, server, upload_serve
65 if not ok then return nil, err end 63 if not ok then return nil, err end
66 64
67 util.printout("Updating manifest...") 65 util.printout("Updating manifest...")
68 manif.make_manifest(local_cache, "one", true) 66 writer.make_manifest(local_cache, "one", true)
69 util.printout("Updating index.html...") 67 util.printout("Updating index.html...")
70 index.make_index(local_cache) 68 index.make_index(local_cache)
71 69
diff --git a/src/luarocks/build.lua b/src/luarocks/build.lua
index 96b232ff..ceaa20dd 100644
--- a/src/luarocks/build.lua
+++ b/src/luarocks/build.lua
@@ -1,45 +1,15 @@
1 1
2--- Module implementing the LuaRocks "build" command.
3-- Builds a rock, compiling its C parts if any.
4local build = {} 2local build = {}
5package.loaded["luarocks.build"] = build
6 3
7local pack = require("luarocks.pack")
8local path = require("luarocks.path") 4local path = require("luarocks.path")
9local util = require("luarocks.util") 5local util = require("luarocks.util")
10local repos = require("luarocks.repos")
11local fetch = require("luarocks.fetch") 6local fetch = require("luarocks.fetch")
12local fs = require("luarocks.fs") 7local fs = require("luarocks.fs")
13local dir = require("luarocks.dir") 8local dir = require("luarocks.dir")
14local deps = require("luarocks.deps") 9local deps = require("luarocks.deps")
15local manif = require("luarocks.manif") 10local cfg = require("luarocks.core.cfg")
16local remove = require("luarocks.remove") 11local repos = require("luarocks.repos")
17local cfg = require("luarocks.cfg") 12local writer = require("luarocks.manif.writer")
18
19util.add_run_function(build)
20build.help_summary = "Build/compile a rock."
21build.help_arguments = "[--pack-binary-rock] [--keep] {<rockspec>|<rock>|<name> [<version>]}"
22build.help = [[
23Build and install a rock, compiling its C parts if any.
24Argument may be a rockspec file, a source rock file
25or the name of a rock to be fetched from a repository.
26
27--pack-binary-rock Do not install rock. Instead, produce a .rock file
28 with the contents of compilation in the current
29 directory.
30
31--keep Do not remove previously installed versions of the
32 rock after building a new one. This behavior can
33 be made permanent by setting keep_other_versions=true
34 in the configuration file.
35
36--branch=<name> Override the `source.branch` field in the loaded
37 rockspec. Allows to specify a different branch to
38 fetch. Particularly for SCM rocks.
39
40--only-deps Installs only the dependencies of the rock.
41
42]]..util.deps_mode_help()
43 13
44--- Install files to a given location. 14--- Install files to a given location.
45-- Takes a table where the array part is a list of filenames to be copied. 15-- Takes a table where the array part is a list of filenames to be copied.
@@ -148,6 +118,31 @@ local function install_default_docs(name, version)
148 end 118 end
149end 119end
150 120
121local function check_macosx_deployment_target(rockspec)
122 local target = rockspec.build.macosx_deployment_target
123 local function minor(version)
124 return tonumber(version and version:match("^[^.]+%.([^.]+)"))
125 end
126 local function patch_variable(var, target)
127 if rockspec.variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then
128 rockspec.variables[var] = (rockspec.variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET="..target)
129 else
130 rockspec.variables[var] = "env MACOSX_DEPLOYMENT_TARGET="..target.." "..rockspec.variables[var]
131 end
132 end
133 if cfg.platforms.macosx and rockspec:format_is_at_least("3.0") and target then
134 local version = util.popen_read("sw_vers -productVersion")
135 local versionminor = minor(version)
136 local targetminor = minor(target)
137 if targetminor > versionminor then
138 return nil, ("This rock requires Mac OSX 10.%d, and you are running 10.%d."):format(targetminor, versionminor)
139 end
140 patch_variable("CC", target)
141 patch_variable("LD", target)
142 end
143 return true
144end
145
151--- Build and install a rock given a rockspec. 146--- Build and install a rock given a rockspec.
152-- @param rockspec_file string: local or remote filename of a rockspec. 147-- @param rockspec_file string: local or remote filename of a rockspec.
153-- @param need_to_fetch boolean: true if sources need to be fetched, 148-- @param need_to_fetch boolean: true if sources need to be fetched,
@@ -245,6 +240,11 @@ function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_m
245 end 240 end
246 end 241 end
247 242
243 ok, err = check_macosx_deployment_target(rockspec)
244 if not ok then
245 return nil, err
246 end
247
248 if build_spec.type ~= "none" then 248 if build_spec.type ~= "none" then
249 249
250 -- Temporary compatibility 250 -- Temporary compatibility
@@ -314,7 +314,7 @@ function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_m
314 fs.pop_dir() 314 fs.pop_dir()
315 end 315 end
316 316
317 ok, err = manif.make_rock_manifest(name, version) 317 ok, err = writer.make_rock_manifest(name, version)
318 if err then return nil, err end 318 if err then return nil, err end
319 319
320 ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) 320 ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode)
@@ -333,83 +333,4 @@ function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_m
333 return name, version 333 return name, version
334end 334end
335 335
336--- Build and install a rock.
337-- @param rock_file string: local or remote filename of a rock.
338-- @param need_to_fetch boolean: true if sources need to be fetched,
339-- false if the rockspec was obtained from inside a source rock.
340-- @param deps_mode: string: Which trees to check dependencies for:
341-- "one" for the current default tree, "all" for all trees,
342-- "order" for all trees with priority >= the current default, "none" for no trees.
343-- @param build_only_deps boolean: true to build the listed dependencies only.
344-- @return boolean or (nil, string, [string]): True if build was successful,
345-- or false and an error message and an optional error code.
346function build.build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps)
347 assert(type(rock_file) == "string")
348 assert(type(need_to_fetch) == "boolean")
349
350 local ok, err, errcode
351 local unpack_dir
352 unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file)
353 if not unpack_dir then
354 return nil, err, errcode
355 end
356 local rockspec_file = path.rockspec_name_from_rock(rock_file)
357 ok, err = fs.change_dir(unpack_dir)
358 if not ok then return nil, err end
359 ok, err, errcode = build.build_rockspec(rockspec_file, need_to_fetch, false, deps_mode, build_only_deps)
360 fs.pop_dir()
361 return ok, err, errcode
362end
363
364local function do_build(name, version, deps_mode, build_only_deps)
365 if name:match("%.rockspec$") then
366 return build.build_rockspec(name, true, false, deps_mode, build_only_deps)
367 elseif name:match("%.src%.rock$") then
368 return build.build_rock(name, false, deps_mode, build_only_deps)
369 elseif name:match("%.all%.rock$") then
370 local install = require("luarocks.install")
371 local install_fun = build_only_deps and install.install_binary_rock_deps or install.install_binary_rock
372 return install_fun(name, deps_mode)
373 elseif name:match("%.rock$") then
374 return build.build_rock(name, true, deps_mode, build_only_deps)
375 elseif not name:match(dir.separator) then
376 local search = require("luarocks.search")
377 return search.act_on_src_or_rockspec(do_build, name:lower(), version, nil, deps_mode, build_only_deps)
378 end
379 return nil, "Don't know what to do with "..name
380end
381
382--- Driver function for "build" command.
383-- @param name string: A local or remote rockspec or rock file.
384-- If a package name is given, forwards the request to "search" and,
385-- if returned a result, installs the matching rock.
386-- @param version string: When passing a package name, a version number may
387-- also be given.
388-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an
389-- error message otherwise. exitcode is optionally returned.
390function build.command(flags, name, version)
391 if type(name) ~= "string" then
392 return nil, "Argument missing. "..util.see_help("build")
393 end
394 assert(type(version) == "string" or not version)
395
396 if flags["pack-binary-rock"] then
397 return pack.pack_binary_rock(name, version, do_build, name, version, deps.get_deps_mode(flags))
398 else
399 local ok, err = fs.check_command_permissions(flags)
400 if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end
401 ok, err = do_build(name, version, deps.get_deps_mode(flags), flags["only-deps"])
402 if not ok then return nil, err end
403 name, version = ok, err
404
405 if (not flags["only-deps"]) and (not flags["keep"]) and not cfg.keep_other_versions then
406 local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"])
407 if not ok then util.printerr(err) end
408 end
409
410 manif.check_dependencies(nil, deps.get_deps_mode(flags))
411 return name, version
412 end
413end
414
415return build 336return build
diff --git a/src/luarocks/build/builtin.lua b/src/luarocks/build/builtin.lua
index ccefaa3b..403a8aaf 100644
--- a/src/luarocks/build/builtin.lua
+++ b/src/luarocks/build/builtin.lua
@@ -7,7 +7,7 @@ local unpack = unpack or table.unpack
7local fs = require("luarocks.fs") 7local fs = require("luarocks.fs")
8local path = require("luarocks.path") 8local path = require("luarocks.path")
9local util = require("luarocks.util") 9local util = require("luarocks.util")
10local cfg = require("luarocks.cfg") 10local cfg = require("luarocks.core.cfg")
11local dir = require("luarocks.dir") 11local dir = require("luarocks.dir")
12 12
13--- Run a command displaying its execution on standard output. 13--- Run a command displaying its execution on standard output.
@@ -49,7 +49,7 @@ end
49-- nil and an error message otherwise. 49-- nil and an error message otherwise.
50function builtin.run(rockspec) 50function builtin.run(rockspec)
51 assert(type(rockspec) == "table") 51 assert(type(rockspec) == "table")
52 local compile_object, compile_library, compile_wrapper_binary --TODO EXEWRAPPER 52 local compile_object, compile_library, compile_static_library, compile_wrapper_binary --TODO EXEWRAPPER
53 53
54 local build = rockspec.build 54 local build = rockspec.build
55 local variables = rockspec.variables 55 local variables = rockspec.variables
@@ -83,6 +83,15 @@ function builtin.run(rockspec)
83 local ok = execute(variables.LD.." "..variables.LIBFLAG, "-o", library, unpack(extras)) 83 local ok = execute(variables.LD.." "..variables.LIBFLAG, "-o", library, unpack(extras))
84 return ok 84 return ok
85 end 85 end
86 --[[ TODO disable static libs until we fix the conflict in the manifest, which will take extending the manifest format.
87 compile_static_library = function(library, objects, libraries, libdirs, name)
88 local ok = execute(variables.AR, "rc", library, unpack(objects))
89 if ok then
90 ok = execute(variables.RANLIB, library)
91 end
92 return ok
93 end
94 ]]
86 compile_wrapper_binary = function(fullname, name) 95 compile_wrapper_binary = function(fullname, name)
87 --TODO EXEWRAPPER 96 --TODO EXEWRAPPER
88 local fullbasename = fullname:gsub("%.lua$", ""):gsub("/", "\\") 97 local fullbasename = fullname:gsub("%.lua$", ""):gsub("/", "\\")
@@ -129,6 +138,12 @@ function builtin.run(rockspec)
129 end 138 end
130 return ok 139 return ok
131 end 140 end
141 --[[ TODO disable static libs until we fix the conflict in the manifest, which will take extending the manifest format.
142 compile_static_library = function(library, objects, libraries, libdirs, name)
143 local ok = execute(variables.AR, "-out:"..library, unpack(objects))
144 return ok
145 end
146 ]]
132 compile_wrapper_binary = function(fullname, name) 147 compile_wrapper_binary = function(fullname, name)
133 --TODO EXEWRAPPER 148 --TODO EXEWRAPPER
134 local fullbasename = fullname:gsub("%.lua$", ""):gsub("/", "\\") 149 local fullbasename = fullname:gsub("%.lua$", ""):gsub("/", "\\")
@@ -170,6 +185,13 @@ function builtin.run(rockspec)
170 end 185 end
171 return execute(variables.LD.." "..variables.LIBFLAG, "-o", library, "-L"..variables.LUA_LIBDIR, unpack(extras)) 186 return execute(variables.LD.." "..variables.LIBFLAG, "-o", library, "-L"..variables.LUA_LIBDIR, unpack(extras))
172 end 187 end
188 compile_static_library = function(library, objects, libraries, libdirs, name)
189 local ok = execute(variables.AR, "rc", library, unpack(objects))
190 if ok then
191 ok = execute(variables.RANLIB, library)
192 end
193 return ok
194 end
173 compile_wrapper_binary = function(_, name) return true, name end 195 compile_wrapper_binary = function(_, name) return true, name end
174 --TODO EXEWRAPPER 196 --TODO EXEWRAPPER
175 end 197 end
@@ -253,6 +275,17 @@ function builtin.run(rockspec)
253 if not ok then 275 if not ok then
254 return nil, "Failed compiling module "..module_name 276 return nil, "Failed compiling module "..module_name
255 end 277 end
278 --[[ TODO disable static libs until we fix the conflict in the manifest, which will take extending the manifest format.
279 module_name = name:match("([^.]*)$").."."..util.matchquote(cfg.static_lib_extension)
280 if moddir ~= "" then
281 module_name = dir.path(moddir, module_name)
282 end
283 lib_modules[module_name] = dir.path(libdir, module_name)
284 ok = compile_static_library(module_name, objects, info.libraries, info.libdirs, name)
285 if not ok then
286 return nil, "Failed compiling static library "..module_name
287 end
288 ]]
256 end 289 end
257 end 290 end
258 for _, mods in ipairs({{ tbl = lua_modules, perms = cfg.perm_read }, { tbl = lib_modules, perms = cfg.perm_exec }}) do 291 for _, mods in ipairs({{ tbl = lua_modules, perms = cfg.perm_read }, { tbl = lib_modules, perms = cfg.perm_exec }}) do
diff --git a/src/luarocks/build/cmake.lua b/src/luarocks/build/cmake.lua
index da5a31f1..8ee6b6b2 100644
--- a/src/luarocks/build/cmake.lua
+++ b/src/luarocks/build/cmake.lua
@@ -4,7 +4,7 @@ local cmake = {}
4 4
5local fs = require("luarocks.fs") 5local fs = require("luarocks.fs")
6local util = require("luarocks.util") 6local util = require("luarocks.util")
7local cfg = require("luarocks.cfg") 7local cfg = require("luarocks.core.cfg")
8 8
9--- Driver function for the "cmake" build back-end. 9--- Driver function for the "cmake" build back-end.
10-- @param rockspec table: the loaded rockspec. 10-- @param rockspec table: the loaded rockspec.
@@ -52,13 +52,26 @@ function cmake.run(rockspec)
52 return nil, "Failed cmake." 52 return nil, "Failed cmake."
53 end 53 end
54 54
55 if not fs.execute_string(rockspec.variables.CMAKE.." --build build.luarocks --config Release") then 55 local do_build, do_install
56 return nil, "Failed building." 56 if rockspec:format_is_at_least("3.0") then
57 do_build = (build.build_pass == nil) and true or build.build_pass
58 do_install = (build.install_pass == nil) and true or build.install_pass
59 else
60 do_build = true
61 do_install = true
57 end 62 end
58 63
59 if not fs.execute_string(rockspec.variables.CMAKE.." --build build.luarocks --target install --config Release") then 64 if do_build then
60 return nil, "Failed installing." 65 if not fs.execute_string(rockspec.variables.CMAKE.." --build build.luarocks --config Release") then
66 return nil, "Failed building."
67 end
61 end 68 end
69 if do_install then
70 if not fs.execute_string(rockspec.variables.CMAKE.." --build build.luarocks --target install --config Release") then
71 return nil, "Failed installing."
72 end
73 end
74
62 return true 75 return true
63end 76end
64 77
diff --git a/src/luarocks/build/make.lua b/src/luarocks/build/make.lua
index 69e73c2e..ded015b7 100644
--- a/src/luarocks/build/make.lua
+++ b/src/luarocks/build/make.lua
@@ -6,7 +6,7 @@ local unpack = unpack or table.unpack
6 6
7local fs = require("luarocks.fs") 7local fs = require("luarocks.fs")
8local util = require("luarocks.util") 8local util = require("luarocks.util")
9local cfg = require("luarocks.cfg") 9local cfg = require("luarocks.core.cfg")
10 10
11--- Call "make" with given target and variables 11--- Call "make" with given target and variables
12-- @param make_cmd string: the make command to be used (typically 12-- @param make_cmd string: the make command to be used (typically
diff --git a/src/luarocks/cmd/build.lua b/src/luarocks/cmd/build.lua
new file mode 100644
index 00000000..a6b67b92
--- /dev/null
+++ b/src/luarocks/cmd/build.lua
@@ -0,0 +1,119 @@
1
2--- Module implementing the LuaRocks "build" command.
3-- Builds a rock, compiling its C parts if any.
4local cmd_build = {}
5
6local pack = require("luarocks.pack")
7local path = require("luarocks.path")
8local util = require("luarocks.util")
9local fetch = require("luarocks.fetch")
10local fs = require("luarocks.fs")
11local dir = require("luarocks.dir")
12local deps = require("luarocks.deps")
13local remove = require("luarocks.remove")
14local cfg = require("luarocks.core.cfg")
15local build = require("luarocks.build")
16local writer = require("luarocks.manif.writer")
17
18cmd_build.help_summary = "build/compile a rock."
19cmd_build.help_arguments = "[--pack-binary-rock] [--keep] {<rockspec>|<rock>|<name> [<version>]}"
20cmd_build.help = [[
21Build and install a rock, compiling its C parts if any.
22Argument may be a rockspec file, a source rock file
23or the name of a rock to be fetched from a repository.
24
25--pack-binary-rock Do not install rock. Instead, produce a .rock file
26 with the contents of compilation in the current
27 directory.
28
29--keep Do not remove previously installed versions of the
30 rock after building a new one. This behavior can
31 be made permanent by setting keep_other_versions=true
32 in the configuration file.
33
34--branch=<name> Override the `source.branch` field in the loaded
35 rockspec. Allows to specify a different branch to
36 fetch. Particularly for SCM rocks.
37
38--only-deps Installs only the dependencies of the rock.
39
40]]..util.deps_mode_help()
41
42--- Build and install a rock.
43-- @param rock_file string: local or remote filename of a rock.
44-- @param need_to_fetch boolean: true if sources need to be fetched,
45-- false if the rockspec was obtained from inside a source rock.
46-- @param deps_mode: string: Which trees to check dependencies for:
47-- "one" for the current default tree, "all" for all trees,
48-- "order" for all trees with priority >= the current default, "none" for no trees.
49-- @param build_only_deps boolean: true to build the listed dependencies only.
50-- @return boolean or (nil, string, [string]): True if build was successful,
51-- or false and an error message and an optional error code.
52local function build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps)
53 assert(type(rock_file) == "string")
54 assert(type(need_to_fetch) == "boolean")
55
56 local ok, err, errcode
57 local unpack_dir
58 unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file)
59 if not unpack_dir then
60 return nil, err, errcode
61 end
62 local rockspec_file = path.rockspec_name_from_rock(rock_file)
63 ok, err = fs.change_dir(unpack_dir)
64 if not ok then return nil, err end
65 ok, err, errcode = build.build_rockspec(rockspec_file, need_to_fetch, false, deps_mode, build_only_deps)
66 fs.pop_dir()
67 return ok, err, errcode
68end
69
70local function do_build(name, version, deps_mode, build_only_deps)
71 if name:match("%.rockspec$") then
72 return build.build_rockspec(name, true, false, deps_mode, build_only_deps)
73 elseif name:match("%.src%.rock$") then
74 return build_rock(name, false, deps_mode, build_only_deps)
75 elseif name:match("%.all%.rock$") then
76 return build_rock(name, true, deps_mode, build_only_deps)
77 elseif name:match("%.rock$") then
78 return build_rock(name, true, deps_mode, build_only_deps)
79 elseif not name:match(dir.separator) then
80 local search = require("luarocks.search")
81 return search.act_on_src_or_rockspec(do_build, name:lower(), version, nil, deps_mode, build_only_deps)
82 end
83 return nil, "Don't know what to do with "..name
84end
85
86--- Driver function for "build" command.
87-- @param name string: A local or remote rockspec or rock file.
88-- If a package name is given, forwards the request to "search" and,
89-- if returned a result, installs the matching rock.
90-- @param version string: When passing a package name, a version number may
91-- also be given.
92-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an
93-- error message otherwise. exitcode is optionally returned.
94function cmd_build.command(flags, name, version)
95 if type(name) ~= "string" then
96 return nil, "Argument missing. "..util.see_help("build")
97 end
98 assert(type(version) == "string" or not version)
99
100 if flags["pack-binary-rock"] then
101 return pack.pack_binary_rock(name, version, do_build, name, version, deps.get_deps_mode(flags))
102 else
103 local ok, err = fs.check_command_permissions(flags)
104 if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end
105 ok, err = do_build(name, version, deps.get_deps_mode(flags), flags["only-deps"])
106 if not ok then return nil, err end
107 name, version = ok, err
108
109 if (not flags["only-deps"]) and (not flags["keep"]) and not cfg.keep_other_versions then
110 local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"])
111 if not ok then util.printerr(err) end
112 end
113
114 writer.check_dependencies(nil, deps.get_deps_mode(flags))
115 return name, version
116 end
117end
118
119return cmd_build
diff --git a/src/luarocks/config_cmd.lua b/src/luarocks/cmd/config.lua
index fe3cc637..b68f7898 100644
--- a/src/luarocks/config_cmd.lua
+++ b/src/luarocks/cmd/config.lua
@@ -2,11 +2,10 @@
2-- Queries information about the LuaRocks configuration. 2-- Queries information about the LuaRocks configuration.
3local config_cmd = {} 3local config_cmd = {}
4 4
5local cfg = require("luarocks.cfg") 5local cfg = require("luarocks.core.cfg")
6local util = require("luarocks.util") 6local util = require("luarocks.util")
7local dir = require("luarocks.dir") 7local dir = require("luarocks.dir")
8 8
9util.add_run_function(config_cmd)
10config_cmd.help_summary = "Query information about the LuaRocks configuration." 9config_cmd.help_summary = "Query information about the LuaRocks configuration."
11config_cmd.help_arguments = "<flag>" 10config_cmd.help_arguments = "<flag>"
12config_cmd.help = [[ 11config_cmd.help = [[
diff --git a/src/luarocks/doc.lua b/src/luarocks/cmd/doc.lua
index 423ebe02..5d521276 100644
--- a/src/luarocks/doc.lua
+++ b/src/luarocks/cmd/doc.lua
@@ -2,7 +2,6 @@
2--- Module implementing the LuaRocks "doc" command. 2--- Module implementing the LuaRocks "doc" command.
3-- Shows documentation for an installed rock. 3-- Shows documentation for an installed rock.
4local doc = {} 4local doc = {}
5package.loaded["luarocks.doc"] = doc
6 5
7local util = require("luarocks.util") 6local util = require("luarocks.util")
8local search = require("luarocks.search") 7local search = require("luarocks.search")
@@ -12,7 +11,6 @@ local fetch = require("luarocks.fetch")
12local fs = require("luarocks.fs") 11local fs = require("luarocks.fs")
13local download = require("luarocks.download") 12local download = require("luarocks.download")
14 13
15util.add_run_function(doc)
16doc.help_summary = "Show documentation for an installed rock." 14doc.help_summary = "Show documentation for an installed rock."
17 15
18doc.help = [[ 16doc.help = [[
diff --git a/src/luarocks/cmd/download.lua b/src/luarocks/cmd/download.lua
new file mode 100644
index 00000000..9c119f6e
--- /dev/null
+++ b/src/luarocks/cmd/download.lua
@@ -0,0 +1,45 @@
1
2--- Module implementing the luarocks "download" command.
3-- Download a rock from the repository.
4local cmd_download = {}
5
6local util = require("luarocks.util")
7local download = require("luarocks.download")
8
9cmd_download.help_summary = "Download a specific rock file from a rocks server."
10cmd_download.help_arguments = "[--all] [--arch=<arch> | --source | --rockspec] [<name> [<version>]]"
11cmd_download.help = [[
12--all Download all files if there are multiple matches.
13--source Download .src.rock if available.
14--rockspec Download .rockspec if available.
15--arch=<arch> Download rock for a specific architecture.
16]]
17
18--- Driver function for the "download" command.
19-- @param name string: a rock name.
20-- @param version string or nil: if the name of a package is given, a
21-- version may also be passed.
22-- @return boolean or (nil, string): true if successful or nil followed
23-- by an error message.
24function cmd_download.command(flags, name, version)
25 assert(type(version) == "string" or not version)
26 if type(name) ~= "string" and not flags["all"] then
27 return nil, "Argument missing. "..util.see_help("download")
28 end
29 if not name then name, version = "", "" end
30
31 local arch
32
33 if flags["source"] then
34 arch = "src"
35 elseif flags["rockspec"] then
36 arch = "rockspec"
37 elseif flags["arch"] then
38 arch = flags["arch"]
39 end
40
41 local dl, err = download.download(arch, name:lower(), version, flags["all"])
42 return dl and true, err
43end
44
45return cmd_download
diff --git a/src/luarocks/help.lua b/src/luarocks/cmd/help.lua
index 10eb2287..b986534c 100644
--- a/src/luarocks/help.lua
+++ b/src/luarocks/cmd/help.lua
@@ -7,12 +7,11 @@
7local help = {} 7local help = {}
8 8
9local util = require("luarocks.util") 9local util = require("luarocks.util")
10local cfg = require("luarocks.cfg") 10local cfg = require("luarocks.core.cfg")
11local dir = require("luarocks.dir") 11local dir = require("luarocks.dir")
12 12
13local program = util.this_program("luarocks") 13local program = util.this_program("luarocks")
14 14
15util.add_run_function(help)
16help.help_summary = "Help on commands. Type '"..program.." help <command>' for more." 15help.help_summary = "Help on commands. Type '"..program.." help <command>' for more."
17 16
18help.help_arguments = "[<command>]" 17help.help_arguments = "[<command>]"
@@ -48,7 +47,7 @@ function help.command(flags, command)
48 print_section("NAME") 47 print_section("NAME")
49 util.printout("\t"..program..[[ - ]]..program_description) 48 util.printout("\t"..program..[[ - ]]..program_description)
50 print_section("SYNOPSIS") 49 print_section("SYNOPSIS")
51 util.printout("\t"..program..[[ [--server=<server> | --only-server=<server>] [--tree=<tree>] [VAR=VALUE]... <command> [<argument>] ]]) 50 util.printout("\t"..program..[[ [<flags...>] [VAR=VALUE]... <command> [<argument>] ]])
52 print_section("GENERAL OPTIONS") 51 print_section("GENERAL OPTIONS")
53 util.printout([[ 52 util.printout([[
54 These apply to all commands, as appropriate: 53 These apply to all commands, as appropriate:
diff --git a/src/luarocks/install.lua b/src/luarocks/cmd/install.lua
index e28c24f0..8b4a154b 100644
--- a/src/luarocks/install.lua
+++ b/src/luarocks/cmd/install.lua
@@ -1,7 +1,6 @@
1--- Module implementing the LuaRocks "install" command. 1--- Module implementing the LuaRocks "install" command.
2-- Installs binary rocks. 2-- Installs binary rocks.
3local install = {} 3local install = {}
4package.loaded["luarocks.install"] = install
5 4
6local path = require("luarocks.path") 5local path = require("luarocks.path")
7local repos = require("luarocks.repos") 6local repos = require("luarocks.repos")
@@ -9,11 +8,11 @@ local fetch = require("luarocks.fetch")
9local util = require("luarocks.util") 8local util = require("luarocks.util")
10local fs = require("luarocks.fs") 9local fs = require("luarocks.fs")
11local deps = require("luarocks.deps") 10local deps = require("luarocks.deps")
12local manif = require("luarocks.manif") 11local writer = require("luarocks.manif.writer")
13local remove = require("luarocks.remove") 12local remove = require("luarocks.remove")
14local cfg = require("luarocks.cfg") 13local search = require("luarocks.search")
14local cfg = require("luarocks.core.cfg")
15 15
16util.add_run_function(install)
17install.help_summary = "Install a rock." 16install.help_summary = "Install a rock."
18 17
19install.help_arguments = "{<rock>|<name> [<version>]}" 18install.help_arguments = "{<rock>|<name> [<version>]}"
@@ -75,7 +74,7 @@ function install.install_binary_rock(rock_file, deps_mode)
75 74
76 -- For compatibility with .rock files built with LuaRocks 1 75 -- For compatibility with .rock files built with LuaRocks 1
77 if not fs.exists(path.rock_manifest_file(name, version)) then 76 if not fs.exists(path.rock_manifest_file(name, version)) then
78 ok, err = manif.make_rock_manifest(name, version) 77 ok, err = writer.make_rock_manifest(name, version)
79 if err then return nil, err end 78 if err then return nil, err end
80 end 79 end
81 80
@@ -156,7 +155,7 @@ function install.command(flags, name, version)
156 if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end 155 if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end
157 156
158 if name:match("%.rockspec$") or name:match("%.src%.rock$") then 157 if name:match("%.rockspec$") or name:match("%.src%.rock$") then
159 local build = require("luarocks.build") 158 local build = require("luarocks.cmd.build")
160 return build.command(flags, name) 159 return build.command(flags, name)
161 elseif name:match("%.rock$") then 160 elseif name:match("%.rock$") then
162 if flags["only-deps"] then 161 if flags["only-deps"] then
@@ -172,10 +171,9 @@ function install.command(flags, name, version)
172 if not ok then util.printerr(err) end 171 if not ok then util.printerr(err) end
173 end 172 end
174 173
175 manif.check_dependencies(nil, deps.get_deps_mode(flags)) 174 writer.check_dependencies(nil, deps.get_deps_mode(flags))
176 return name, version 175 return name, version
177 else 176 else
178 local search = require("luarocks.search")
179 local url, err = search.find_suitable_rock(search.make_query(name:lower(), version)) 177 local url, err = search.find_suitable_rock(search.make_query(name:lower(), version))
180 if not url then 178 if not url then
181 return nil, err 179 return nil, err
diff --git a/src/luarocks/lint.lua b/src/luarocks/cmd/lint.lua
index 0fd81a20..c9ea45ea 100644
--- a/src/luarocks/lint.lua
+++ b/src/luarocks/cmd/lint.lua
@@ -2,13 +2,11 @@
2--- Module implementing the LuaRocks "lint" command. 2--- Module implementing the LuaRocks "lint" command.
3-- Utility function that checks syntax of the rockspec. 3-- Utility function that checks syntax of the rockspec.
4local lint = {} 4local lint = {}
5package.loaded["luarocks.lint"] = lint
6 5
7local util = require("luarocks.util") 6local util = require("luarocks.util")
8local download = require("luarocks.download") 7local download = require("luarocks.download")
9local fetch = require("luarocks.fetch") 8local fetch = require("luarocks.fetch")
10 9
11util.add_run_function(lint)
12lint.help_summary = "Check syntax of a rockspec." 10lint.help_summary = "Check syntax of a rockspec."
13lint.help_arguments = "<rockspec>" 11lint.help_arguments = "<rockspec>"
14lint.help = [[ 12lint.help = [[
diff --git a/src/luarocks/list.lua b/src/luarocks/cmd/list.lua
index c65e058f..9dff22ac 100644
--- a/src/luarocks/list.lua
+++ b/src/luarocks/cmd/list.lua
@@ -2,15 +2,13 @@
2--- Module implementing the LuaRocks "list" command. 2--- Module implementing the LuaRocks "list" command.
3-- Lists currently installed rocks. 3-- Lists currently installed rocks.
4local list = {} 4local list = {}
5package.loaded["luarocks.list"] = list
6 5
7local search = require("luarocks.search") 6local search = require("luarocks.search")
8local deps = require("luarocks.deps") 7local vers = require("luarocks.vers")
9local cfg = require("luarocks.cfg") 8local cfg = require("luarocks.core.cfg")
10local util = require("luarocks.util") 9local util = require("luarocks.util")
11local path = require("luarocks.path") 10local path = require("luarocks.path")
12 11
13util.add_run_function(list)
14list.help_summary = "List currently installed rocks." 12list.help_summary = "List currently installed rocks."
15list.help_arguments = "[--porcelain] <filter>" 13list.help_arguments = "[--porcelain] <filter>"
16list.help = [[ 14list.help = [[
@@ -30,7 +28,7 @@ local function check_outdated(trees, query)
30 local outdated = {} 28 local outdated = {}
31 for name, versions in util.sortedpairs(results_installed) do 29 for name, versions in util.sortedpairs(results_installed) do
32 versions = util.keys(versions) 30 versions = util.keys(versions)
33 table.sort(versions, deps.compare_versions) 31 table.sort(versions, vers.compare_versions)
34 local latest_installed = versions[1] 32 local latest_installed = versions[1]
35 33
36 local query_available = search.make_query(name:lower()) 34 local query_available = search.make_query(name:lower())
@@ -39,11 +37,11 @@ local function check_outdated(trees, query)
39 37
40 if results_available[name] then 38 if results_available[name] then
41 local available_versions = util.keys(results_available[name]) 39 local available_versions = util.keys(results_available[name])
42 table.sort(available_versions, deps.compare_versions) 40 table.sort(available_versions, vers.compare_versions)
43 local latest_available = available_versions[1] 41 local latest_available = available_versions[1]
44 local latest_available_repo = results_available[name][latest_available][1].repo 42 local latest_available_repo = results_available[name][latest_available][1].repo
45 43
46 if deps.compare_versions(latest_available, latest_installed) then 44 if vers.compare_versions(latest_available, latest_installed) then
47 table.insert(outdated, { name = name, installed = latest_installed, available = latest_available, repo = latest_available_repo }) 45 table.insert(outdated, { name = name, installed = latest_installed, available = latest_available, repo = latest_available_repo })
48 end 46 end
49 end 47 end
diff --git a/src/luarocks/make.lua b/src/luarocks/cmd/make.lua
index e59dee37..bfdddb37 100644
--- a/src/luarocks/make.lua
+++ b/src/luarocks/cmd/make.lua
@@ -4,19 +4,17 @@
4-- it does not fetch sources, etc., assuming everything is 4-- it does not fetch sources, etc., assuming everything is
5-- available in the current directory. 5-- available in the current directory.
6local make = {} 6local make = {}
7package.loaded["luarocks.make"] = make
8 7
9local build = require("luarocks.build") 8local build = require("luarocks.build")
10local fs = require("luarocks.fs") 9local fs = require("luarocks.fs")
11local util = require("luarocks.util") 10local util = require("luarocks.util")
12local cfg = require("luarocks.cfg") 11local cfg = require("luarocks.core.cfg")
13local fetch = require("luarocks.fetch") 12local fetch = require("luarocks.fetch")
14local pack = require("luarocks.pack") 13local pack = require("luarocks.pack")
15local remove = require("luarocks.remove") 14local remove = require("luarocks.remove")
16local deps = require("luarocks.deps") 15local deps = require("luarocks.deps")
17local manif = require("luarocks.manif") 16local writer = require("luarocks.manif.writer")
18 17
19util.add_run_function(make)
20make.help_summary = "Compile package in current directory using a rockspec." 18make.help_summary = "Compile package in current directory using a rockspec."
21make.help_arguments = "[--pack-binary-rock] [<rockspec>]" 19make.help_arguments = "[--pack-binary-rock] [<rockspec>]"
22make.help = [[ 20make.help = [[
@@ -87,7 +85,7 @@ function make.command(flags, rockspec)
87 if not ok then util.printerr(err) end 85 if not ok then util.printerr(err) end
88 end 86 end
89 87
90 manif.check_dependencies(nil, deps.get_deps_mode(flags)) 88 writer.check_dependencies(nil, deps.get_deps_mode(flags))
91 return name, version 89 return name, version
92 end 90 end
93end 91end
diff --git a/src/luarocks/new_version.lua b/src/luarocks/cmd/new_version.lua
index 91f7607c..b13dbb97 100644
--- a/src/luarocks/new_version.lua
+++ b/src/luarocks/cmd/new_version.lua
@@ -10,7 +10,6 @@ local persist = require("luarocks.persist")
10local fs = require("luarocks.fs") 10local fs = require("luarocks.fs")
11local type_check = require("luarocks.type_check") 11local type_check = require("luarocks.type_check")
12 12
13util.add_run_function(new_version)
14new_version.help_summary = "Auto-write a rockspec for a new version of a rock." 13new_version.help_summary = "Auto-write a rockspec for a new version of a rock."
15new_version.help_arguments = "[--tag=<tag>] [<package>|<rockspec>] [<new_version>] [<new_url>]" 14new_version.help_arguments = "[--tag=<tag>] [<package>|<rockspec>] [<new_version>] [<new_url>]"
16new_version.help = [[ 15new_version.help = [[
diff --git a/src/luarocks/cmd/pack.lua b/src/luarocks/cmd/pack.lua
new file mode 100644
index 00000000..e43e5b3f
--- /dev/null
+++ b/src/luarocks/cmd/pack.lua
@@ -0,0 +1,45 @@
1
2--- Module implementing the LuaRocks "pack" command.
3-- Creates a rock, packing sources or binaries.
4local cmd_pack = {}
5
6local util = require("luarocks.util")
7local pack = require("luarocks.pack")
8
9cmd_pack.help_summary = "Create a rock, packing sources or binaries."
10cmd_pack.help_arguments = "{<rockspec>|<name> [<version>]}"
11cmd_pack.help = [[
12Argument may be a rockspec file, for creating a source rock,
13or the name of an installed package, for creating a binary rock.
14In the latter case, the app version may be given as a second
15argument.
16]]
17
18--- Driver function for the "pack" command.
19-- @param arg string: may be a rockspec file, for creating a source rock,
20-- or the name of an installed package, for creating a binary rock.
21-- @param version string or nil: if the name of a package is given, a
22-- version may also be passed.
23-- @return boolean or (nil, string): true if successful or nil followed
24-- by an error message.
25function cmd_pack.command(flags, arg, version)
26 assert(type(version) == "string" or not version)
27 if type(arg) ~= "string" then
28 return nil, "Argument missing. "..util.see_help("pack")
29 end
30
31 local file, err
32 if arg:match(".*%.rockspec") then
33 file, err = pack.pack_source_rock(arg)
34 else
35 file, err = pack.pack_installed_rock(arg:lower(), version, flags["tree"])
36 end
37 if err then
38 return nil, err
39 else
40 util.printout("Packed: "..file)
41 return true
42 end
43end
44
45return cmd_pack
diff --git a/src/luarocks/path_cmd.lua b/src/luarocks/cmd/path.lua
index eba85d46..516a0c47 100644
--- a/src/luarocks/path_cmd.lua
+++ b/src/luarocks/cmd/path.lua
@@ -4,9 +4,8 @@
4local path_cmd = {} 4local path_cmd = {}
5 5
6local util = require("luarocks.util") 6local util = require("luarocks.util")
7local cfg = require("luarocks.cfg") 7local cfg = require("luarocks.core.cfg")
8 8
9util.add_run_function(path_cmd)
10path_cmd.help_summary = "Return the currently configured package path." 9path_cmd.help_summary = "Return the currently configured package path."
11path_cmd.help_arguments = "" 10path_cmd.help_arguments = ""
12path_cmd.help = [[ 11path_cmd.help = [[
diff --git a/src/luarocks/purge.lua b/src/luarocks/cmd/purge.lua
index 17724e84..bbce9f8c 100644
--- a/src/luarocks/purge.lua
+++ b/src/luarocks/cmd/purge.lua
@@ -2,19 +2,17 @@
2--- Module implementing the LuaRocks "purge" command. 2--- Module implementing the LuaRocks "purge" command.
3-- Remove all rocks from a given tree. 3-- Remove all rocks from a given tree.
4local purge = {} 4local purge = {}
5package.loaded["luarocks.purge"] = purge
6 5
7local util = require("luarocks.util") 6local util = require("luarocks.util")
8local fs = require("luarocks.fs") 7local fs = require("luarocks.fs")
9local path = require("luarocks.path") 8local path = require("luarocks.path")
10local search = require("luarocks.search") 9local search = require("luarocks.search")
11local deps = require("luarocks.deps") 10local vers = require("luarocks.vers")
12local repos = require("luarocks.repos") 11local repos = require("luarocks.repos")
13local manif = require("luarocks.manif") 12local writer = require("luarocks.manif.writer")
14local cfg = require("luarocks.cfg") 13local cfg = require("luarocks.core.cfg")
15local remove = require("luarocks.remove") 14local remove = require("luarocks.remove")
16 15
17util.add_run_function(purge)
18purge.help_summary = "Remove all installed rocks from a tree." 16purge.help_summary = "Remove all installed rocks from a tree."
19purge.help_arguments = "--tree=<tree> [--old-versions]" 17purge.help_arguments = "--tree=<tree> [--old-versions]"
20purge.help = [[ 18purge.help = [[
@@ -50,13 +48,13 @@ function purge.command(flags)
50 48
51 search.manifest_search(results, path.rocks_dir(tree), query) 49 search.manifest_search(results, path.rocks_dir(tree), query)
52 50
53 local sort = function(a,b) return deps.compare_versions(b,a) end 51 local sort = function(a,b) return vers.compare_versions(b,a) end
54 if flags["old-versions"] then 52 if flags["old-versions"] then
55 sort = deps.compare_versions 53 sort = vers.compare_versions
56 end 54 end
57 55
58 for package, versions in util.sortedpairs(results) do 56 for package, versions in util.sortedpairs(results) do
59 for version, repositories in util.sortedpairs(versions, sort) do 57 for version, _ in util.sortedpairs(versions, sort) do
60 if flags["old-versions"] then 58 if flags["old-versions"] then
61 util.printout("Keeping "..package.." "..version.."...") 59 util.printout("Keeping "..package.." "..version.."...")
62 local ok, err = remove.remove_other_versions(package, version, flags["force"], flags["force-fast"]) 60 local ok, err = remove.remove_other_versions(package, version, flags["force"], flags["force-fast"])
@@ -73,7 +71,7 @@ function purge.command(flags)
73 end 71 end
74 end 72 end
75 end 73 end
76 return manif.make_manifest(cfg.rocks_dir, "one") 74 return writer.make_manifest(cfg.rocks_dir, "one")
77end 75end
78 76
79return purge 77return purge
diff --git a/src/luarocks/cmd/remove.lua b/src/luarocks/cmd/remove.lua
new file mode 100644
index 00000000..b8148ba7
--- /dev/null
+++ b/src/luarocks/cmd/remove.lua
@@ -0,0 +1,67 @@
1
2--- Module implementing the LuaRocks "remove" command.
3-- Uninstalls rocks.
4local cmd_remove = {}
5
6local remove = require("luarocks.remove")
7local util = require("luarocks.util")
8local cfg = require("luarocks.core.cfg")
9local fs = require("luarocks.fs")
10local search = require("luarocks.search")
11local path = require("luarocks.path")
12local deps = require("luarocks.deps")
13local writer = require("luarocks.manif.writer")
14
15cmd_remove.help_summary = "Uninstall a rock."
16cmd_remove.help_arguments = "[--force|--force-fast] <name> [<version>]"
17cmd_remove.help = [[
18Argument is the name of a rock to be uninstalled.
19If a version is not given, try to remove all versions at once.
20Will only perform the removal if it does not break dependencies.
21To override this check and force the removal, use --force.
22To perform a forced removal without reporting dependency issues,
23use --force-fast.
24
25]]..util.deps_mode_help()
26
27--- Driver function for the "remove" command.
28-- @param name string: name of a rock. If a version is given, refer to
29-- a specific version; otherwise, try to remove all versions.
30-- @param version string: When passing a package name, a version number
31-- may also be given.
32-- @return boolean or (nil, string, exitcode): True if removal was
33-- successful, nil and an error message otherwise. exitcode is optionally returned.
34function cmd_remove.command(flags, name, version)
35 if type(name) ~= "string" then
36 return nil, "Argument missing. "..util.see_help("remove")
37 end
38
39 local deps_mode = flags["deps-mode"] or cfg.deps_mode
40
41 local ok, err = fs.check_command_permissions(flags)
42 if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end
43
44 local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$")
45 local filename = name
46 if rock_type then
47 name, version = path.parse_name(filename)
48 if not name then return nil, "Invalid "..rock_type.." filename: "..filename end
49 end
50
51 local results = {}
52 name = name:lower()
53 search.manifest_search(results, cfg.rocks_dir, search.make_query(name, version))
54 if not results[name] then
55 return nil, "Could not find rock '"..name..(version and " "..version or "").."' in "..path.rocks_tree_to_string(cfg.root_dir)
56 end
57
58 local ok, err = remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"])
59 if not ok then
60 return nil, err
61 end
62
63 writer.check_dependencies(nil, deps.get_deps_mode(flags))
64 return true
65end
66
67return cmd_remove
diff --git a/src/luarocks/cmd/search.lua b/src/luarocks/cmd/search.lua
new file mode 100644
index 00000000..c4e4058d
--- /dev/null
+++ b/src/luarocks/cmd/search.lua
@@ -0,0 +1,74 @@
1
2--- Module implementing the LuaRocks "search" command.
3-- Queries LuaRocks servers.
4local cmd_search = {}
5
6local cfg = require("luarocks.core.cfg")
7local util = require("luarocks.util")
8local search = require("luarocks.search")
9
10cmd_search.help_summary = "Query the LuaRocks servers."
11cmd_search.help_arguments = "[--source] [--binary] { <name> [<version>] | --all }"
12cmd_search.help = [[
13--source Return only rockspecs and source rocks,
14 to be used with the "build" command.
15--binary Return only pure Lua and binary rocks (rocks that can be used
16 with the "install" command without requiring a C toolchain).
17--all List all contents of the server that are suitable to
18 this platform, do not filter by name.
19]]
20
21--- Splits a list of search results into two lists, one for "source" results
22-- to be used with the "build" command, and one for "binary" results to be
23-- used with the "install" command.
24-- @param results table: A search results table.
25-- @return (table, table): Two tables, one for source and one for binary
26-- results.
27local function split_source_and_binary_results(results)
28 local sources, binaries = {}, {}
29 for name, versions in pairs(results) do
30 for version, repositories in pairs(versions) do
31 for _, repo in ipairs(repositories) do
32 local where = sources
33 if repo.arch == "all" or repo.arch == cfg.arch then
34 where = binaries
35 end
36 search.store_result(where, name, version, repo.arch, repo.repo)
37 end
38 end
39 end
40 return sources, binaries
41end
42
43--- Driver function for "search" command.
44-- @param name string: A substring of a rock name to search.
45-- @param version string or nil: a version may also be passed.
46-- @return boolean or (nil, string): True if build was successful; nil and an
47-- error message otherwise.
48function cmd_search.command(flags, name, version)
49 if flags["all"] then
50 name, version = "", nil
51 end
52
53 if type(name) ~= "string" and not flags["all"] then
54 return nil, "Enter name and version or use --all. "..util.see_help("search")
55 end
56
57 local query = search.make_query(name:lower(), version)
58 query.exact_name = false
59 local results, err = search.search_repos(query)
60 local porcelain = flags["porcelain"]
61 util.title("Search results:", porcelain, "=")
62 local sources, binaries = split_source_and_binary_results(results)
63 if next(sources) and not flags["binary"] then
64 util.title("Rockspecs and source rocks:", porcelain)
65 search.print_results(sources, porcelain)
66 end
67 if next(binaries) and not flags["source"] then
68 util.title("Binary and pure-Lua rocks:", porcelain)
69 search.print_results(binaries, porcelain)
70 end
71 return true
72end
73
74return cmd_search
diff --git a/src/luarocks/show.lua b/src/luarocks/cmd/show.lua
index ac6273e0..16c8d34e 100644
--- a/src/luarocks/show.lua
+++ b/src/luarocks/cmd/show.lua
@@ -1,18 +1,16 @@
1--- Module implementing the LuaRocks "show" command. 1--- Module implementing the LuaRocks "show" command.
2-- Shows information about an installed rock. 2-- Shows information about an installed rock.
3local show = {} 3local show = {}
4package.loaded["luarocks.show"] = show
5 4
6local search = require("luarocks.search") 5local search = require("luarocks.search")
7local cfg = require("luarocks.cfg") 6local cfg = require("luarocks.core.cfg")
8local util = require("luarocks.util") 7local util = require("luarocks.util")
9local path = require("luarocks.path") 8local path = require("luarocks.path")
10local deps = require("luarocks.deps") 9local vers = require("luarocks.vers")
11local fetch = require("luarocks.fetch") 10local fetch = require("luarocks.fetch")
12local manif = require("luarocks.manif") 11local manif = require("luarocks.manif")
13local repos = require("luarocks.repos") 12local repos = require("luarocks.repos")
14 13
15util.add_run_function(show)
16show.help_summary = "Show information about an installed rock." 14show.help_summary = "Show information about an installed rock."
17 15
18show.help = [[ 16show.help = [[
@@ -103,6 +101,8 @@ function show.command(flags, name, version)
103 if flags["rock-tree"] then util.printout(path.rocks_tree_to_string(repo)) 101 if flags["rock-tree"] then util.printout(path.rocks_tree_to_string(repo))
104 elseif flags["rock-dir"] then util.printout(directory) 102 elseif flags["rock-dir"] then util.printout(directory)
105 elseif flags["home"] then util.printout(descript.homepage) 103 elseif flags["home"] then util.printout(descript.homepage)
104 elseif flags["issues"] then util.printout(descript.issues_url)
105 elseif flags["labels"] then util.printout(descript.labels and table.concat(descript.labels, "\n"))
106 elseif flags["modules"] then util.printout(keys_as_string(minfo.modules, "\n")) 106 elseif flags["modules"] then util.printout(keys_as_string(minfo.modules, "\n"))
107 elseif flags["deps"] then util.printout(keys_as_string(minfo.dependencies)) 107 elseif flags["deps"] then util.printout(keys_as_string(minfo.dependencies))
108 elseif flags["rockspec"] then util.printout(rockspec_file) 108 elseif flags["rockspec"] then util.printout(rockspec_file)
@@ -121,6 +121,12 @@ function show.command(flags, name, version)
121 if descript.homepage then 121 if descript.homepage then
122 util.printout("Homepage: ", descript.homepage) 122 util.printout("Homepage: ", descript.homepage)
123 end 123 end
124 if descript.issues_url then
125 util.printout("Issues: ", descript.issues_url)
126 end
127 if descript.labels then
128 util.printout("Labels: ", table.concat(descript.labels, ", "))
129 end
124 util.printout("Installed in: ", path.rocks_tree_to_string(repo)) 130 util.printout("Installed in: ", path.rocks_tree_to_string(repo))
125 131
126 if next(minfo.commands) then 132 if next(minfo.commands) then
@@ -141,7 +147,7 @@ function show.command(flags, name, version)
141 util.printout("Depends on:") 147 util.printout("Depends on:")
142 for _, dep in ipairs(rockspec.dependencies) do 148 for _, dep in ipairs(rockspec.dependencies) do
143 direct_deps[dep.name] = true 149 direct_deps[dep.name] = true
144 util.printout("\t"..deps.show_dep(dep).." "..installed_rock_label(dep.name, flags["tree"])) 150 util.printout("\t"..vers.show_dep(dep).." "..installed_rock_label(dep.name, flags["tree"]))
145 end 151 end
146 end 152 end
147 local has_indirect_deps 153 local has_indirect_deps
diff --git a/src/luarocks/unpack.lua b/src/luarocks/cmd/unpack.lua
index 0922f9b9..c50701b0 100644
--- a/src/luarocks/unpack.lua
+++ b/src/luarocks/cmd/unpack.lua
@@ -2,16 +2,14 @@
2--- Module implementing the LuaRocks "unpack" command. 2--- Module implementing the LuaRocks "unpack" command.
3-- Unpack the contents of a rock. 3-- Unpack the contents of a rock.
4local unpack = {} 4local unpack = {}
5package.loaded["luarocks.unpack"] = unpack
6 5
7local fetch = require("luarocks.fetch") 6local fetch = require("luarocks.fetch")
8local fs = require("luarocks.fs") 7local fs = require("luarocks.fs")
9local util = require("luarocks.util") 8local util = require("luarocks.util")
10local build = require("luarocks.build") 9local build = require("luarocks.build")
11local dir = require("luarocks.dir") 10local dir = require("luarocks.dir")
12local cfg = require("luarocks.cfg") 11local cfg = require("luarocks.core.cfg")
13 12
14util.add_run_function(unpack)
15unpack.help_summary = "Unpack the contents of a rock." 13unpack.help_summary = "Unpack the contents of a rock."
16unpack.help_arguments = "[--force] {<rock>|<name> [<version>]}" 14unpack.help_arguments = "[--force] {<rock>|<name> [<version>]}"
17unpack.help = [[ 15unpack.help = [[
diff --git a/src/luarocks/upload.lua b/src/luarocks/cmd/upload.lua
index 7c0c416c..baee47ab 100644
--- a/src/luarocks/upload.lua
+++ b/src/luarocks/cmd/upload.lua
@@ -4,10 +4,9 @@ local upload = {}
4local util = require("luarocks.util") 4local util = require("luarocks.util")
5local fetch = require("luarocks.fetch") 5local fetch = require("luarocks.fetch")
6local pack = require("luarocks.pack") 6local pack = require("luarocks.pack")
7local cfg = require("luarocks.cfg") 7local cfg = require("luarocks.core.cfg")
8local Api = require("luarocks.upload.api") 8local Api = require("luarocks.upload.api")
9 9
10util.add_run_function(upload)
11upload.help_summary = "Upload a rockspec to the public rocks repository." 10upload.help_summary = "Upload a rockspec to the public rocks repository."
12upload.help_arguments = "[--skip-pack] [--api-key=<key>] [--force] <rockspec>" 11upload.help_arguments = "[--skip-pack] [--api-key=<key>] [--force] <rockspec>"
13upload.help = [[ 12upload.help = [[
diff --git a/src/luarocks/write_rockspec.lua b/src/luarocks/cmd/write_rockspec.lua
index 33edeb1b..73010f08 100644
--- a/src/luarocks/write_rockspec.lua
+++ b/src/luarocks/cmd/write_rockspec.lua
@@ -1,8 +1,7 @@
1 1
2local write_rockspec = {} 2local write_rockspec = {}
3package.loaded["luarocks.write_rockspec"] = write_rockspec
4 3
5local cfg = require("luarocks.cfg") 4local cfg = require("luarocks.core.cfg")
6local dir = require("luarocks.dir") 5local dir = require("luarocks.dir")
7local fetch = require("luarocks.fetch") 6local fetch = require("luarocks.fetch")
8local fs = require("luarocks.fs") 7local fs = require("luarocks.fs")
@@ -10,8 +9,8 @@ local path = require("luarocks.path")
10local persist = require("luarocks.persist") 9local persist = require("luarocks.persist")
11local type_check = require("luarocks.type_check") 10local type_check = require("luarocks.type_check")
12local util = require("luarocks.util") 11local util = require("luarocks.util")
12local vers = require("luarocks.vers")
13 13
14util.add_run_function(write_rockspec)
15write_rockspec.help_summary = "Write a template for a rockspec file." 14write_rockspec.help_summary = "Write a template for a rockspec file."
16write_rockspec.help_arguments = "[--output=<file> ...] [<name>] [<version>] [<url>|<path>]" 15write_rockspec.help_arguments = "[--output=<file> ...] [<name>] [<version>] [<url>|<path>]"
17write_rockspec.help = [[ 16write_rockspec.help = [[
@@ -223,6 +222,7 @@ local function rockspec_cleanup(rockspec)
223 rockspec.source.protocol = nil 222 rockspec.source.protocol = nil
224 rockspec.variables = nil 223 rockspec.variables = nil
225 rockspec.name = nil 224 rockspec.name = nil
225 rockspec.format_is_at_least = nil
226end 226end
227 227
228function write_rockspec.command(flags, name, version, url_or_dir) 228function write_rockspec.command(flags, name, version, url_or_dir)
@@ -285,6 +285,7 @@ function write_rockspec.command(flags, name, version, url_or_dir)
285 } 285 }
286 path.configure_paths(rockspec) 286 path.configure_paths(rockspec)
287 rockspec.source.protocol = protocol 287 rockspec.source.protocol = protocol
288 rockspec.format_is_at_least = vers.format_is_at_least
288 289
289 configure_lua_version(rockspec, flags["lua-version"]) 290 configure_lua_version(rockspec, flags["lua-version"])
290 291
diff --git a/src/luarocks/command_line.lua b/src/luarocks/command_line.lua
index ac5ccd35..6a1cc519 100644
--- a/src/luarocks/command_line.lua
+++ b/src/luarocks/command_line.lua
@@ -5,7 +5,7 @@ local command_line = {}
5local unpack = unpack or table.unpack 5local unpack = unpack or table.unpack
6 6
7local util = require("luarocks.util") 7local util = require("luarocks.util")
8local cfg = require("luarocks.cfg") 8local cfg = require("luarocks.core.cfg")
9local path = require("luarocks.path") 9local path = require("luarocks.path")
10local dir = require("luarocks.dir") 10local dir = require("luarocks.dir")
11local deps = require("luarocks.deps") 11local deps = require("luarocks.deps")
diff --git a/src/luarocks/cfg.lua b/src/luarocks/core/cfg.lua
index 858a87fe..250f503e 100644
--- a/src/luarocks/cfg.lua
+++ b/src/luarocks/core/cfg.lua
@@ -1,3 +1,4 @@
1
1--- Configuration for LuaRocks. 2--- Configuration for LuaRocks.
2-- Tries to load the user's configuration file and 3-- Tries to load the user's configuration file and
3-- defines defaults for unset values. See the 4-- defines defaults for unset values. See the
@@ -12,11 +13,7 @@
12local rawset, next, table, pairs, require, io, os, setmetatable, pcall, ipairs, package, tonumber, type, assert, _VERSION = 13local rawset, next, table, pairs, require, io, os, setmetatable, pcall, ipairs, package, tonumber, type, assert, _VERSION =
13 rawset, next, table, pairs, require, io, os, setmetatable, pcall, ipairs, package, tonumber, type, assert, _VERSION 14 rawset, next, table, pairs, require, io, os, setmetatable, pcall, ipairs, package, tonumber, type, assert, _VERSION
14 15
15--module("luarocks.cfg")
16local cfg = {} 16local cfg = {}
17package.loaded["luarocks.cfg"] = cfg
18
19local util = require("luarocks.util")
20 17
21cfg.lua_version = _VERSION:match(" (5%.[123])$") or "5.1" 18cfg.lua_version = _VERSION:match(" (5%.[123])$") or "5.1"
22local version_suffix = cfg.lua_version:gsub("%.", "_") 19local version_suffix = cfg.lua_version:gsub("%.", "_")
@@ -31,15 +28,18 @@ if not ok then
31 site_config = {} 28 site_config = {}
32end 29end
33 30
31local util = require("luarocks.core.util")
32local persist = require("luarocks.core.persist")
33local require = nil
34--------------------------------------------------------------------------------
35
34cfg.program_version = "scm" 36cfg.program_version = "scm"
35cfg.program_series = "2.2" 37cfg.program_series = "3.0"
36cfg.major_version = (cfg.program_version:match("([^.]%.[^.])")) or cfg.program_series 38cfg.major_version = (cfg.program_version:match("([^.]%.[^.])")) or cfg.program_series
37cfg.variables = {} 39cfg.variables = {}
38cfg.rocks_trees = {} 40cfg.rocks_trees = {}
39cfg.platforms = {} 41cfg.platforms = {}
40 42
41local persist = require("luarocks.persist")
42
43cfg.errorcodes = setmetatable({ 43cfg.errorcodes = setmetatable({
44 OK = 0, 44 OK = 0,
45 UNSPECIFIED = 1, 45 UNSPECIFIED = 1,
@@ -75,8 +75,8 @@ end
75-- so that this detection does not run every time. When it is 75-- so that this detection does not run every time. When it is
76-- performed, we use the Unix way to identify the system, 76-- performed, we use the Unix way to identify the system,
77-- even on Windows (assuming UnxUtils or Cygwin). 77-- even on Windows (assuming UnxUtils or Cygwin).
78local system = site_config.LUAROCKS_UNAME_S or io.popen("uname -s"):read("*l") 78local system = site_config.LUAROCKS_UNAME_S or util.popen_read("uname -s")
79local proc = site_config.LUAROCKS_UNAME_M or io.popen("uname -m"):read("*l") 79local proc = site_config.LUAROCKS_UNAME_M or util.popen_read("uname -m")
80if proc:match("i[%d]86") then 80if proc:match("i[%d]86") then
81 cfg.target_cpu = "x86" 81 cfg.target_cpu = "x86"
82elseif proc:match("amd64") or proc:match("x86_64") then 82elseif proc:match("amd64") or proc:match("x86_64") then
@@ -370,6 +370,8 @@ local defaults = {
370 MAKE = "make", 370 MAKE = "make",
371 CC = "cc", 371 CC = "cc",
372 LD = "ld", 372 LD = "ld",
373 AR = "ar",
374 RANLIB = "ranlib",
373 375
374 CVS = "cvs", 376 CVS = "cvs",
375 GIT = "git", 377 GIT = "git",
@@ -426,7 +428,8 @@ local defaults = {
426 include = "include" 428 include = "include"
427 }, 429 },
428 430
429 rocks_provided = {} 431 rocks_provided = {},
432 rocks_provided_3_0 = {},
430} 433}
431 434
432if cfg.platforms.windows then 435if cfg.platforms.windows then
@@ -438,6 +441,7 @@ if cfg.platforms.windows then
438 defaults.arch = "win32-"..cfg.target_cpu 441 defaults.arch = "win32-"..cfg.target_cpu
439 defaults.lib_extension = "dll" 442 defaults.lib_extension = "dll"
440 defaults.external_lib_extension = "dll" 443 defaults.external_lib_extension = "dll"
444 defaults.static_lib_extension = "lib"
441 defaults.obj_extension = "obj" 445 defaults.obj_extension = "obj"
442 defaults.external_deps_dirs = { "c:/external/" } 446 defaults.external_deps_dirs = { "c:/external/" }
443 defaults.variables.LUA_BINDIR = site_config.LUA_BINDIR and site_config.LUA_BINDIR:gsub("\\", "/") or "c:/lua"..cfg.lua_version.."/bin" 447 defaults.variables.LUA_BINDIR = site_config.LUA_BINDIR and site_config.LUA_BINDIR:gsub("\\", "/") or "c:/lua"..cfg.lua_version.."/bin"
@@ -451,6 +455,7 @@ if cfg.platforms.windows then
451 defaults.variables.WRAPPER = full_prefix.."\\rclauncher.c" 455 defaults.variables.WRAPPER = full_prefix.."\\rclauncher.c"
452 defaults.variables.LD = "link" 456 defaults.variables.LD = "link"
453 defaults.variables.MT = "mt" 457 defaults.variables.MT = "mt"
458 defaults.variables.AR = "lib"
454 defaults.variables.LUALIB = "lua"..cfg.lua_version..".lib" 459 defaults.variables.LUALIB = "lua"..cfg.lua_version..".lib"
455 defaults.variables.CFLAGS = "/nologo /MD /O2" 460 defaults.variables.CFLAGS = "/nologo /MD /O2"
456 defaults.variables.LIBFLAG = "/nologo /dll" 461 defaults.variables.LIBFLAG = "/nologo /dll"
@@ -490,11 +495,14 @@ end
490 495
491if cfg.platforms.mingw32 then 496if cfg.platforms.mingw32 then
492 defaults.obj_extension = "o" 497 defaults.obj_extension = "o"
498 defaults.static_lib_extension = "a"
493 defaults.cmake_generator = "MinGW Makefiles" 499 defaults.cmake_generator = "MinGW Makefiles"
494 defaults.variables.MAKE = "mingw32-make" 500 defaults.variables.MAKE = "mingw32-make"
495 defaults.variables.CC = "mingw32-gcc" 501 defaults.variables.CC = "mingw32-gcc"
496 defaults.variables.RC = "windres" 502 defaults.variables.RC = "windres"
497 defaults.variables.LD = "mingw32-gcc" 503 defaults.variables.LD = "mingw32-gcc"
504 defaults.variables.AR = "ar"
505 defaults.variables.RANLIB = "ranlib"
498 defaults.variables.CFLAGS = "-O2" 506 defaults.variables.CFLAGS = "-O2"
499 defaults.variables.LIBFLAG = "-shared" 507 defaults.variables.LIBFLAG = "-shared"
500 defaults.makefile = "Makefile" 508 defaults.makefile = "Makefile"
@@ -515,6 +523,7 @@ end
515 523
516if cfg.platforms.unix then 524if cfg.platforms.unix then
517 defaults.lib_extension = "so" 525 defaults.lib_extension = "so"
526 defaults.static_lib_extension = "a"
518 defaults.external_lib_extension = "so" 527 defaults.external_lib_extension = "so"
519 defaults.obj_extension = "o" 528 defaults.obj_extension = "o"
520 defaults.external_deps_dirs = { "/usr/local", "/usr" } 529 defaults.external_deps_dirs = { "/usr/local", "/usr" }
@@ -589,7 +598,7 @@ if cfg.platforms.macosx then
589 defaults.variables.LIBFLAG = "-bundle -undefined dynamic_lookup -all_load" 598 defaults.variables.LIBFLAG = "-bundle -undefined dynamic_lookup -all_load"
590 defaults.variables.STAT = "/usr/bin/stat" 599 defaults.variables.STAT = "/usr/bin/stat"
591 defaults.variables.STATFLAG = "-f '%A'" 600 defaults.variables.STATFLAG = "-f '%A'"
592 local version = io.popen("sw_vers -productVersion"):read("*l") 601 local version = util.popen_read("sw_vers -productVersion")
593 version = tonumber(version and version:match("^[^.]+%.([^.]+)")) or 3 602 version = tonumber(version and version:match("^[^.]+%.([^.]+)")) or 3
594 if version >= 10 then 603 if version >= 10 then
595 version = 8 604 version = 8
@@ -648,8 +657,8 @@ end
648if package.loaded.jit then 657if package.loaded.jit then
649 -- LuaJIT 658 -- LuaJIT
650 local lj_version = package.loaded.jit.version:match("LuaJIT (.*)"):gsub("%-","") 659 local lj_version = package.loaded.jit.version:match("LuaJIT (.*)"):gsub("%-","")
651 --defaults.rocks_provided["luajit"] = lj_version.."-1"
652 defaults.rocks_provided["luabitop"] = lj_version.."-1" 660 defaults.rocks_provided["luabitop"] = lj_version.."-1"
661 defaults.rocks_provided_3_0["luajit"] = lj_version.."-1"
653end 662end
654 663
655-- Use defaults: 664-- Use defaults:
@@ -666,6 +675,7 @@ for _, entry in ipairs({"variables", "rocks_provided"}) do
666 end 675 end
667 end 676 end
668end 677end
678setmetatable(defaults.rocks_provided_3_0, { __index = cfg.rocks_provided })
669 679
670-- For values not set in the config file, use values from the 'defaults' table. 680-- For values not set in the config file, use values from the 'defaults' table.
671local cfg_mt = { 681local cfg_mt = {
diff --git a/src/luarocks/core/dir.lua b/src/luarocks/core/dir.lua
new file mode 100644
index 00000000..05b2c72d
--- /dev/null
+++ b/src/luarocks/core/dir.lua
@@ -0,0 +1,43 @@
1
2local dir = {}
3
4local require = nil
5--------------------------------------------------------------------------------
6
7dir.separator = "/"
8
9--- Describe a path in a cross-platform way.
10-- Use this function to avoid platform-specific directory
11-- separators in other modules. Removes trailing slashes from
12-- each component given, to avoid repeated separators.
13-- Separators inside strings are kept, to handle URLs containing
14-- protocols.
15-- @param ... strings representing directories
16-- @return string: a string with a platform-specific representation
17-- of the path.
18function dir.path(...)
19 local t = {...}
20 while t[1] == "" do
21 table.remove(t, 1)
22 end
23 return (table.concat(t, "/"):gsub("([^:])/+", "%1/"):gsub("^/+", "/"):gsub("/*$", ""))
24end
25
26--- Split protocol and path from an URL or local pathname.
27-- URLs should be in the "protocol://path" format.
28-- For local pathnames, "file" is returned as the protocol.
29-- @param url string: an URL or a local pathname.
30-- @return string, string: the protocol, and the pathname without the protocol.
31function dir.split_url(url)
32 assert(type(url) == "string")
33
34 local protocol, pathname = url:match("^([^:]*)://(.*)")
35 if not protocol then
36 protocol = "file"
37 pathname = url
38 end
39 return protocol, pathname
40end
41
42return dir
43
diff --git a/src/luarocks/manif_core.lua b/src/luarocks/core/manif.lua
index 82e7ea4d..549cfb4c 100644
--- a/src/luarocks/manif_core.lua
+++ b/src/luarocks/core/manif.lua
@@ -1,15 +1,13 @@
1 1
2--- Core functions for querying manifest files. 2--- Core functions for querying manifest files.
3-- This module requires no specific 'fs' functionality. 3local manif = {}
4local manif_core = {}
5package.loaded["luarocks.manif_core"] = manif_core
6 4
7local persist = require("luarocks.persist") 5local persist = require("luarocks.core.persist")
8local type_check = require("luarocks.type_check") 6local type_check = require("luarocks.core.type_check")
9local cfg = require("luarocks.cfg") 7local cfg = require("luarocks.core.cfg")
10local dir = require("luarocks.dir") 8local dir = require("luarocks.core.dir")
11local util = require("luarocks.util") 9local require = nil
12local path = require("luarocks.path") 10--------------------------------------------------------------------------------
13 11
14-- Table with repository identifiers as keys and tables mapping 12-- Table with repository identifiers as keys and tables mapping
15-- Lua versions to cached loaded manifests as values. 13-- Lua versions to cached loaded manifests as values.
@@ -19,7 +17,7 @@ local manifest_cache = {}
19-- @param repo_url string: The repository identifier. 17-- @param repo_url string: The repository identifier.
20-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. 18-- @param lua_version string: Lua version in "5.x" format, defaults to installed version.
21-- @param manifest table: the manifest to be cached. 19-- @param manifest table: the manifest to be cached.
22function manif_core.cache_manifest(repo_url, lua_version, manifest) 20function manif.cache_manifest(repo_url, lua_version, manifest)
23 lua_version = lua_version or cfg.lua_version 21 lua_version = lua_version or cfg.lua_version
24 manifest_cache[repo_url] = manifest_cache[repo_url] or {} 22 manifest_cache[repo_url] = manifest_cache[repo_url] or {}
25 manifest_cache[repo_url][lua_version] = manifest 23 manifest_cache[repo_url][lua_version] = manifest
@@ -29,7 +27,7 @@ end
29-- @param repo_url string: The repository identifier. 27-- @param repo_url string: The repository identifier.
30-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. 28-- @param lua_version string: Lua version in "5.x" format, defaults to installed version.
31-- @return table or nil: loaded manifest or nil if cache is empty. 29-- @return table or nil: loaded manifest or nil if cache is empty.
32function manif_core.get_cached_manifest(repo_url, lua_version) 30function manif.get_cached_manifest(repo_url, lua_version)
33 lua_version = lua_version or cfg.lua_version 31 lua_version = lua_version or cfg.lua_version
34 return manifest_cache[repo_url] and manifest_cache[repo_url][lua_version] 32 return manifest_cache[repo_url] and manifest_cache[repo_url][lua_version]
35end 33end
@@ -42,7 +40,7 @@ end
42-- @param quick boolean: If given, skips type checking. 40-- @param quick boolean: If given, skips type checking.
43-- @return table or (nil, string, string): the manifest or nil, 41-- @return table or (nil, string, string): the manifest or nil,
44-- error message and error code ("open", "load", "run" or "type"). 42-- error message and error code ("open", "load", "run" or "type").
45function manif_core.manifest_loader(file, repo_url, lua_version, quick) 43function manif.manifest_loader(file, repo_url, lua_version, quick)
46 local manifest, err, errcode = persist.load_into_table(file) 44 local manifest, err, errcode = persist.load_into_table(file)
47 if not manifest then 45 if not manifest then
48 return nil, "Failed loading manifest for "..repo_url..": "..err, errcode 46 return nil, "Failed loading manifest for "..repo_url..": "..err, errcode
@@ -55,7 +53,7 @@ function manif_core.manifest_loader(file, repo_url, lua_version, quick)
55 end 53 end
56 end 54 end
57 55
58 manif_core.cache_manifest(repo_url, lua_version, manifest) 56 manif.cache_manifest(repo_url, lua_version, manifest)
59 return manifest 57 return manifest
60end 58end
61 59
@@ -65,42 +63,16 @@ end
65-- @param repo_url string: URL or pathname for the repository. 63-- @param repo_url string: URL or pathname for the repository.
66-- @return table or (nil, string, string): A table representing the manifest, 64-- @return table or (nil, string, string): A table representing the manifest,
67-- or nil followed by an error message and an error code, see manifest_loader. 65-- or nil followed by an error message and an error code, see manifest_loader.
68function manif_core.load_local_manifest(repo_url) 66function manif.load_local_manifest(repo_url)
69 assert(type(repo_url) == "string") 67 assert(type(repo_url) == "string")
70 68
71 local cached_manifest = manif_core.get_cached_manifest(repo_url) 69 local cached_manifest = manif.get_cached_manifest(repo_url)
72 if cached_manifest then 70 if cached_manifest then
73 return cached_manifest 71 return cached_manifest
74 end 72 end
75 73
76 local pathname = dir.path(repo_url, "manifest") 74 local pathname = dir.path(repo_url, "manifest")
77 return manif_core.manifest_loader(pathname, repo_url, nil, true) 75 return manif.manifest_loader(pathname, repo_url, nil, true)
78end 76end
79 77
80--- Get all versions of a package listed in a manifest file. 78return manif
81-- @param name string: a package name.
82-- @param deps_mode string: "one", to use only the currently
83-- configured tree; "order" to select trees based on order
84-- (use the current tree and all trees below it on the list)
85-- or "all", to use all trees.
86-- @return table: An array of strings listing installed
87-- versions of a package.
88function manif_core.get_versions(name, deps_mode)
89 assert(type(name) == "string")
90 assert(type(deps_mode) == "string")
91
92 local version_set = {}
93 path.map_trees(deps_mode, function(tree)
94 local manifest = manif_core.load_local_manifest(path.rocks_dir(tree))
95
96 if manifest and manifest.repository[name] then
97 for version in pairs(manifest.repository[name]) do
98 version_set[version] = true
99 end
100 end
101 end)
102
103 return util.keys(version_set)
104end
105
106return manif_core
diff --git a/src/luarocks/core/path.lua b/src/luarocks/core/path.lua
new file mode 100644
index 00000000..ed85aeac
--- /dev/null
+++ b/src/luarocks/core/path.lua
@@ -0,0 +1,114 @@
1
2--- Core LuaRocks-specific path handling functions.
3local path = {}
4
5local cfg = require("luarocks.core.cfg")
6local dir = require("luarocks.core.dir")
7local require = nil
8--------------------------------------------------------------------------------
9
10function path.rocks_dir(tree)
11 if type(tree) == "string" then
12 return dir.path(tree, cfg.rocks_subdir)
13 else
14 assert(type(tree) == "table")
15 return tree.rocks_dir or dir.path(tree.root, cfg.rocks_subdir)
16 end
17end
18
19--- Produce a versioned version of a filename.
20-- @param file string: filename (must start with prefix)
21-- @param prefix string: Path prefix for file
22-- @param name string: Rock name
23-- @param version string: Rock version
24-- @return string: a pathname with the same directory parts and a versioned basename.
25function path.versioned_name(file, prefix, name, version)
26 assert(type(file) == "string")
27 assert(type(name) == "string")
28 assert(type(version) == "string")
29
30 local rest = file:sub(#prefix+1):gsub("^/*", "")
31 local name_version = (name.."_"..version):gsub("%-", "_"):gsub("%.", "_")
32 return dir.path(prefix, name_version.."-"..rest)
33end
34
35--- Convert a pathname to a module identifier.
36-- In Unix, for example, a path "foo/bar/baz.lua" is converted to
37-- "foo.bar.baz"; "bla/init.lua" returns "bla"; "foo.so" returns "foo".
38-- @param file string: Pathname of module
39-- @return string: The module identifier, or nil if given path is
40-- not a conformant module path (the function does not check if the
41-- path actually exists).
42function path.path_to_module(file)
43 assert(type(file) == "string")
44
45 local name = file:match("(.*)%."..cfg.lua_extension.."$")
46 if name then
47 name = name:gsub(dir.separator, ".")
48 local init = name:match("(.*)%.init$")
49 if init then
50 name = init
51 end
52 else
53 name = file:match("(.*)%."..cfg.lib_extension.."$")
54 if name then
55 name = name:gsub(dir.separator, ".")
56 --[[ TODO disable static libs until we fix the conflict in the manifest, which will take extending the manifest format.
57 else
58 name = file:match("(.*)%."..cfg.static_lib_extension.."$")
59 if name then
60 name = name:gsub(dir.separator, ".")
61 end
62 ]]
63 end
64 end
65 if not name then name = file end
66 name = name:gsub("^%.+", ""):gsub("%.+$", "")
67 return name
68end
69
70function path.deploy_lua_dir(tree)
71 if type(tree) == "string" then
72 return dir.path(tree, cfg.lua_modules_path)
73 else
74 assert(type(tree) == "table")
75 return tree.lua_dir or dir.path(tree.root, cfg.lua_modules_path)
76 end
77end
78
79function path.deploy_lib_dir(tree)
80 if type(tree) == "string" then
81 return dir.path(tree, cfg.lib_modules_path)
82 else
83 assert(type(tree) == "table")
84 return tree.lib_dir or dir.path(tree.root, cfg.lib_modules_path)
85 end
86end
87
88local is_src_extension = { [".lua"] = true, [".tl"] = true, [".tld"] = true, [".moon"] = true }
89
90--- Return the pathname of the file that would be loaded for a module, indexed.
91-- @param file_name string: module file name as in manifest (eg. "socket/core.so")
92-- @param name string: name of the package (eg. "luasocket")
93-- @param version string: version number (eg. "2.0.2-1")
94-- @param tree string: repository path (eg. "/usr/local")
95-- @param i number: the index, 1 if version is the current default, > 1 otherwise.
96-- This is done this way for use by select_module in luarocks.loader.
97-- @return string: filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so")
98function path.which_i(file_name, name, version, tree, i)
99 local deploy_dir
100 local extension = file_name:match("%.[a-z]+$")
101 if is_src_extension[extension] then
102 deploy_dir = path.deploy_lua_dir(tree)
103 file_name = dir.path(deploy_dir, file_name)
104 else
105 deploy_dir = path.deploy_lib_dir(tree)
106 file_name = dir.path(deploy_dir, file_name)
107 end
108 if i > 1 then
109 file_name = path.versioned_name(file_name, deploy_dir, name, version)
110 end
111 return file_name
112end
113
114return path
diff --git a/src/luarocks/core/persist.lua b/src/luarocks/core/persist.lua
new file mode 100644
index 00000000..48979184
--- /dev/null
+++ b/src/luarocks/core/persist.lua
@@ -0,0 +1,81 @@
1
2local persist = {}
3
4local require = nil
5--------------------------------------------------------------------------------
6
7--- Load and run a Lua file in an environment.
8-- @param filename string: the name of the file.
9-- @param env table: the environment table.
10-- @return (true, any) or (nil, string, string): true and the return value
11-- of the file, or nil, an error message and an error code ("open", "load"
12-- or "run") in case of errors.
13local function run_file(filename, env)
14 local fd, err = io.open(filename)
15 if not fd then
16 return nil, err, "open"
17 end
18 local str, err = fd:read("*a")
19 fd:close()
20 if not str then
21 return nil, err, "open"
22 end
23 str = str:gsub("^#![^\n]*\n", "")
24 local chunk, ran
25 if _VERSION == "Lua 5.1" then -- Lua 5.1
26 chunk, err = loadstring(str, filename)
27 if chunk then
28 setfenv(chunk, env)
29 ran, err = pcall(chunk)
30 end
31 else -- Lua 5.2
32 chunk, err = load(str, filename, "t", env)
33 if chunk then
34 ran, err = pcall(chunk)
35 end
36 end
37 if not chunk then
38 return nil, "Error loading file: "..err, "load"
39 end
40 if not ran then
41 return nil, "Error running file: "..err, "run"
42 end
43 return true, err
44end
45
46--- Load a Lua file containing assignments, storing them in a table.
47-- The global environment is not propagated to the loaded file.
48-- @param filename string: the name of the file.
49-- @param tbl table or nil: if given, this table is used to store
50-- loaded values.
51-- @return (table, table) or (nil, string, string): a table with the file's assignments
52-- as fields and set of undefined globals accessed in file,
53-- or nil, an error message and an error code ("open"; couldn't open the file,
54-- "load"; compile-time error, or "run"; run-time error)
55-- in case of errors.
56function persist.load_into_table(filename, tbl)
57 assert(type(filename) == "string")
58 assert(type(tbl) == "table" or not tbl)
59
60 local result = tbl or {}
61 local globals = {}
62 local globals_mt = {
63 __index = function(t, k)
64 globals[k] = true
65 end
66 }
67 local save_mt = getmetatable(result)
68 setmetatable(result, globals_mt)
69
70 local ok, err, errcode = run_file(filename, result)
71
72 setmetatable(result, save_mt)
73
74 if not ok then
75 return nil, err, errcode
76 end
77 return result, globals
78end
79
80return persist
81
diff --git a/src/luarocks/core/type_check.lua b/src/luarocks/core/type_check.lua
new file mode 100644
index 00000000..343b7154
--- /dev/null
+++ b/src/luarocks/core/type_check.lua
@@ -0,0 +1,229 @@
1
2local type_check = {}
3
4local cfg = require("luarocks.core.cfg")
5local vers = require("luarocks.core.vers")
6local require = nil
7--------------------------------------------------------------------------------
8
9type_check.string_1 = { _type = "string" }
10type_check.number_1 = { _type = "number" }
11type_check.mandatory_string_1 = { _type = "string", _mandatory = true }
12
13local number_1 = type_check.number_1
14local string_1 = type_check.string_1
15local mandatory_string_1 = type_check.mandatory_string_1
16
17local manifest_types = {
18 repository = {
19 _mandatory = true,
20 -- packages
21 _any = {
22 -- versions
23 _any = {
24 -- items
25 _any = {
26 arch = mandatory_string_1,
27 modules = { _any = string_1 },
28 commands = { _any = string_1 },
29 dependencies = { _any = string_1 },
30 -- TODO: to be extended with more metadata.
31 }
32 }
33 }
34 },
35 modules = {
36 _mandatory = true,
37 -- modules
38 _any = {
39 -- providers
40 _any = string_1
41 }
42 },
43 commands = {
44 _mandatory = true,
45 -- modules
46 _any = {
47 -- commands
48 _any = string_1
49 }
50 },
51 dependencies = {
52 -- each module
53 _any = {
54 -- each version
55 _any = {
56 -- each dependency
57 _any = {
58 name = string_1,
59 constraints = {
60 _any = {
61 no_upgrade = { _type = "boolean" },
62 op = string_1,
63 version = {
64 string = string_1,
65 _any = number_1,
66 }
67 }
68 }
69 }
70 }
71 }
72 }
73}
74
75local function check_version(version, typetbl, context)
76 local typetbl_version = typetbl._version or "1.0"
77 if vers.compare_versions(typetbl_version, version) then
78 if context == "" then
79 return nil, "Invalid rockspec_format version number in rockspec? Please fix rockspec accordingly."
80 else
81 return nil, context.." is not supported in rockspec format "..version.." (requires version "..typetbl_version.."), please fix the rockspec_format field accordingly."
82 end
83 end
84 return true
85end
86
87--- Type check an object.
88-- The object is compared against an archetypical value
89-- matching the expected type -- the actual values don't matter,
90-- only their types. Tables are type checked recursively.
91-- @param version string: The version of the item.
92-- @param item any: The object being checked.
93-- @param typetbl any: The type-checking table for the object.
94-- @param context string: A string indicating the "context" where the
95-- error occurred (the full table path), for error messages.
96-- @return boolean or (nil, string): true if type checking
97-- succeeded, or nil and an error message if it failed.
98-- @see type_check_table
99local function type_check_item(version, item, typetbl, context)
100 assert(type(version) == "string")
101
102 local ok, err = check_version(version, typetbl, context)
103 if not ok then
104 return nil, err
105 end
106
107 local item_type = type(item) or "nil"
108 local expected_type = typetbl._type or "table"
109
110 if expected_type == "number" then
111 if not tonumber(item) then
112 return nil, "Type mismatch on field "..context..": expected a number"
113 end
114 elseif expected_type == "string" then
115 if item_type ~= "string" then
116 return nil, "Type mismatch on field "..context..": expected a string, got "..item_type
117 end
118 if typetbl._pattern then
119 if not item:match("^"..typetbl._pattern.."$") then
120 return nil, "Type mismatch on field "..context..": invalid value "..item.." does not match '"..typetbl._pattern.."'"
121 end
122 end
123 elseif expected_type == "table" then
124 if item_type ~= expected_type then
125 return nil, "Type mismatch on field "..context..": expected a table"
126 else
127 return type_check.type_check_table(version, item, typetbl, context)
128 end
129 elseif item_type ~= expected_type then
130 return nil, "Type mismatch on field "..context..": expected "..expected_type
131 end
132 return true
133end
134
135local function mkfield(context, field)
136 if context == "" then
137 return tostring(field)
138 elseif type(field) == "string" then
139 return context.."."..field
140 else
141 return context.."["..tostring(field).."]"
142 end
143end
144
145--- Type check the contents of a table.
146-- The table's contents are compared against a reference table,
147-- which contains the recognized fields, with archetypical values
148-- matching the expected types -- the actual values of items in the
149-- reference table don't matter, only their types (ie, for field x
150-- in tbl that is correctly typed, type(tbl.x) == type(types.x)).
151-- If the reference table contains a field called MORE, then
152-- unknown fields in the checked table are accepted.
153-- If it contains a field called ANY, then its type will be
154-- used to check any unknown fields. If a field is prefixed
155-- with MUST_, it is mandatory; its absence from the table is
156-- a type error.
157-- Tables are type checked recursively.
158-- @param version string: The version of tbl.
159-- @param tbl table: The table to be type checked.
160-- @param typetbl table: The type-checking table, containing
161-- values for recognized fields in the checked table.
162-- @param context string: A string indicating the "context" where the
163-- error occurred (such as the name of the table the item is a part of),
164-- to be used by error messages.
165-- @return boolean or (nil, string): true if type checking
166-- succeeded, or nil and an error message if it failed.
167function type_check.type_check_table(version, tbl, typetbl, context)
168 assert(type(version) == "string")
169 assert(type(tbl) == "table")
170 assert(type(typetbl) == "table")
171
172 local ok, err = check_version(version, typetbl, context)
173 if not ok then
174 return nil, err
175 end
176
177 for k, v in pairs(tbl) do
178 local t = typetbl[k] or typetbl._any
179 if t then
180 local ok, err = type_check_item(version, v, t, mkfield(context, k))
181 if not ok then return nil, err end
182 elseif typetbl._more then
183 -- Accept unknown field
184 else
185 if not cfg.accept_unknown_fields then
186 return nil, "Unknown field "..k
187 end
188 end
189 end
190 for k, v in pairs(typetbl) do
191 if k:sub(1,1) ~= "_" and v._mandatory then
192 if not tbl[k] then
193 return nil, "Mandatory field "..mkfield(context, k).." is missing."
194 end
195 end
196 end
197 return true
198end
199
200function type_check.check_undeclared_globals(globals, typetbl)
201 local undeclared = {}
202 for glob, _ in pairs(globals) do
203 if not (typetbl[glob] or typetbl["MUST_"..glob]) then
204 table.insert(undeclared, glob)
205 end
206 end
207 if #undeclared == 1 then
208 return nil, "Unknown variable: "..undeclared[1]
209 elseif #undeclared > 1 then
210 return nil, "Unknown variables: "..table.concat(undeclared, ", ")
211 end
212 return true
213end
214
215--- Type check a manifest table.
216-- Verify the correctness of elements from a
217-- manifest table, reporting on unknown fields and type
218-- mismatches.
219-- @return boolean or (nil, string): true if type checking
220-- succeeded, or nil and an error message if it failed.
221function type_check.type_check_manifest(manifest, globals)
222 assert(type(manifest) == "table")
223 local ok, err = type_check.check_undeclared_globals(globals, manifest_types)
224 if not ok then return nil, err end
225 return type_check.type_check_table("1.0", manifest, manifest_types, "")
226end
227
228return type_check
229
diff --git a/src/luarocks/core/util.lua b/src/luarocks/core/util.lua
new file mode 100644
index 00000000..85b59af6
--- /dev/null
+++ b/src/luarocks/core/util.lua
@@ -0,0 +1,272 @@
1
2local util = {}
3
4local require = nil
5--------------------------------------------------------------------------------
6
7--- Run a process and read a its output.
8-- Equivalent to io.popen(cmd):read("*l"), except that it
9-- closes the fd right away.
10-- @param cmd string: The command to execute
11-- @param spec string: "*l" by default, to read a single line.
12-- May be used to read more, passing, for instance, "*a".
13-- @return string: the output of the program.
14function util.popen_read(cmd, spec)
15 local fd = io.popen(cmd)
16 local out = fd:read(spec or "*l")
17 fd:close()
18 return out
19end
20
21--- Create a new shallow copy of a table: a new table with
22-- the same keys and values. Keys point to the same objects as
23-- the original table (ie, does not copy recursively).
24-- @param tbl table: the input table
25-- @return table: a new table with the same contents.
26function util.make_shallow_copy(tbl)
27 local copy = {}
28 for k,v in pairs(tbl) do
29 copy[k] = v
30 end
31 return copy
32end
33
34---
35-- Formats tables with cycles recursively to any depth.
36-- References to other tables are shown as values.
37-- Self references are indicated.
38-- The string returned is "Lua code", which can be procesed
39-- (in the case in which indent is composed by spaces or "--").
40-- Userdata and function keys and values are shown as strings,
41-- which logically are exactly not equivalent to the original code.
42-- This routine can serve for pretty formating tables with
43-- proper indentations, apart from printing them:
44-- io.write(table.show(t, "t")) -- a typical use
45-- Written by Julio Manuel Fernandez-Diaz,
46-- Heavily based on "Saving tables with cycles", PIL2, p. 113.
47-- @param t table: is the table.
48-- @param name string: is the name of the table (optional)
49-- @param indent string: is a first indentation (optional).
50-- @return string: the pretty-printed table
51function util.show_table(t, name, indent)
52 local cart -- a container
53 local autoref -- for self references
54
55 local function is_empty_table(t) return next(t) == nil end
56
57 local function basic_serialize (o)
58 local so = tostring(o)
59 if type(o) == "function" then
60 local info = debug.getinfo(o, "S")
61 -- info.name is nil because o is not a calling level
62 if info.what == "C" then
63 return ("%q"):format(so .. ", C function")
64 else
65 -- the information is defined through lines
66 return ("%q"):format(so .. ", defined in (" .. info.linedefined .. "-" .. info.lastlinedefined .. ")" .. info.source)
67 end
68 elseif type(o) == "number" then
69 return so
70 else
71 return ("%q"):format(so)
72 end
73 end
74
75 local function add_to_cart (value, name, indent, saved, field)
76 indent = indent or ""
77 saved = saved or {}
78 field = field or name
79
80 cart = cart .. indent .. field
81
82 if type(value) ~= "table" then
83 cart = cart .. " = " .. basic_serialize(value) .. ";\n"
84 else
85 if saved[value] then
86 cart = cart .. " = {}; -- " .. saved[value] .. " (self reference)\n"
87 autoref = autoref .. name .. " = " .. saved[value] .. ";\n"
88 else
89 saved[value] = name
90 --if tablecount(value) == 0 then
91 if is_empty_table(value) then
92 cart = cart .. " = {};\n"
93 else
94 cart = cart .. " = {\n"
95 for k, v in pairs(value) do
96 k = basic_serialize(k)
97 local fname = ("%s[%s]"):format(name, k)
98 field = ("[%s]"):format(k)
99 -- three spaces between levels
100 add_to_cart(v, fname, indent .. " ", saved, field)
101 end
102 cart = cart .. indent .. "};\n"
103 end
104 end
105 end
106 end
107
108 name = name or "__unnamed__"
109 if type(t) ~= "table" then
110 return name .. " = " .. basic_serialize(t)
111 end
112 cart, autoref = "", ""
113 add_to_cart(t, name, indent)
114 return cart .. autoref
115end
116
117--- Merges contents of src on top of dst's contents.
118-- @param dst Destination table, which will receive src's contents.
119-- @param src Table which provides new contents to dst.
120-- @see platform_overrides
121function util.deep_merge(dst, src)
122 for k, v in pairs(src) do
123 if type(v) == "table" then
124 if not dst[k] then
125 dst[k] = {}
126 end
127 if type(dst[k]) == "table" then
128 util.deep_merge(dst[k], v)
129 else
130 dst[k] = v
131 end
132 else
133 dst[k] = v
134 end
135 end
136end
137
138--- Remove repeated entries from a path-style string.
139-- Example: given ("a;b;c;a;b;d", ";"), returns "a;b;c;d".
140-- @param list string: A path string (from $PATH or package.path)
141-- @param sep string: The separator
142function util.remove_path_dupes(list, sep)
143 assert(type(list) == "string")
144 assert(type(sep) == "string")
145 local parts = util.split_string(list, sep)
146 local final, entries = {}, {}
147 for _, part in ipairs(parts) do
148 part = part:gsub("//", "/")
149 if not entries[part] then
150 table.insert(final, part)
151 entries[part] = true
152 end
153 end
154 return table.concat(final, sep)
155end
156
157-- from http://lua-users.org/wiki/SplitJoin
158-- by Philippe Lhoste
159function util.split_string(str, delim, maxNb)
160 -- Eliminate bad cases...
161 if string.find(str, delim) == nil then
162 return { str }
163 end
164 if maxNb == nil or maxNb < 1 then
165 maxNb = 0 -- No limit
166 end
167 local result = {}
168 local pat = "(.-)" .. delim .. "()"
169 local nb = 0
170 local lastPos
171 for part, pos in string.gmatch(str, pat) do
172 nb = nb + 1
173 result[nb] = part
174 lastPos = pos
175 if nb == maxNb then break end
176 end
177 -- Handle the last field
178 if nb ~= maxNb then
179 result[nb + 1] = string.sub(str, lastPos)
180 end
181 return result
182end
183
184--- Return an array of keys of a table.
185-- @param tbl table: The input table.
186-- @return table: The array of keys.
187function util.keys(tbl)
188 local ks = {}
189 for k,_ in pairs(tbl) do
190 table.insert(ks, k)
191 end
192 return ks
193end
194
195--- Print a line to standard error
196function util.printerr(...)
197 io.stderr:write(table.concat({...},"\t"))
198 io.stderr:write("\n")
199end
200
201--- Simple sort function used as a default for util.sortedpairs.
202local function default_sort(a, b)
203 local ta = type(a)
204 local tb = type(b)
205 if ta == "number" and tb == "number" then
206 return a < b
207 elseif ta == "number" then
208 return true
209 elseif tb == "number" then
210 return false
211 else
212 return tostring(a) < tostring(b)
213 end
214end
215
216--- A table iterator generator that returns elements sorted by key,
217-- to be used in "for" loops.
218-- @param tbl table: The table to be iterated.
219-- @param sort_function function or table or nil: An optional comparison function
220-- to be used by table.sort when sorting keys, or an array listing an explicit order
221-- for keys. If a value itself is an array, it is taken so that the first element
222-- is a string representing the field name, and the second element is a priority table
223-- for that key, which is returned by the iterator as the third value after the key
224-- and the value.
225-- @return function: the iterator function.
226function util.sortedpairs(tbl, sort_function)
227 sort_function = sort_function or default_sort
228 local keys = util.keys(tbl)
229 local sub_orders = {}
230
231 if type(sort_function) == "function" then
232 table.sort(keys, sort_function)
233 else
234 local order = sort_function
235 local ordered_keys = {}
236 local all_keys = keys
237 keys = {}
238
239 for _, order_entry in ipairs(order) do
240 local key, sub_order
241 if type(order_entry) == "table" then
242 key = order_entry[1]
243 sub_order = order_entry[2]
244 else
245 key = order_entry
246 end
247
248 if tbl[key] then
249 ordered_keys[key] = true
250 sub_orders[key] = sub_order
251 table.insert(keys, key)
252 end
253 end
254
255 table.sort(all_keys, default_sort)
256 for _, key in ipairs(all_keys) do
257 if not ordered_keys[key] then
258 table.insert(keys, key)
259 end
260 end
261 end
262
263 local i = 1
264 return function()
265 local key = keys[i]
266 i = i + 1
267 return key, tbl[key], sub_orders[key]
268 end
269end
270
271return util
272
diff --git a/src/luarocks/core/vers.lua b/src/luarocks/core/vers.lua
new file mode 100644
index 00000000..2a765b9b
--- /dev/null
+++ b/src/luarocks/core/vers.lua
@@ -0,0 +1,191 @@
1
2local vers = {}
3
4local util = require("luarocks.core.util")
5local require = nil
6--------------------------------------------------------------------------------
7
8local deltas = {
9 scm = 1100,
10 cvs = 1000,
11 rc = -1000,
12 pre = -10000,
13 beta = -100000,
14 alpha = -1000000
15}
16
17local version_mt = {
18 --- Equality comparison for versions.
19 -- All version numbers must be equal.
20 -- If both versions have revision numbers, they must be equal;
21 -- otherwise the revision number is ignored.
22 -- @param v1 table: version table to compare.
23 -- @param v2 table: version table to compare.
24 -- @return boolean: true if they are considered equivalent.
25 __eq = function(v1, v2)
26 if #v1 ~= #v2 then
27 return false
28 end
29 for i = 1, #v1 do
30 if v1[i] ~= v2[i] then
31 return false
32 end
33 end
34 if v1.revision and v2.revision then
35 return (v1.revision == v2.revision)
36 end
37 return true
38 end,
39 --- Size comparison for versions.
40 -- All version numbers are compared.
41 -- If both versions have revision numbers, they are compared;
42 -- otherwise the revision number is ignored.
43 -- @param v1 table: version table to compare.
44 -- @param v2 table: version table to compare.
45 -- @return boolean: true if v1 is considered lower than v2.
46 __lt = function(v1, v2)
47 for i = 1, math.max(#v1, #v2) do
48 local v1i, v2i = v1[i] or 0, v2[i] or 0
49 if v1i ~= v2i then
50 return (v1i < v2i)
51 end
52 end
53 if v1.revision and v2.revision then
54 return (v1.revision < v2.revision)
55 end
56 return false
57 end
58}
59
60local version_cache = {}
61setmetatable(version_cache, {
62 __mode = "kv"
63})
64
65--- Parse a version string, converting to table format.
66-- A version table contains all components of the version string
67-- converted to numeric format, stored in the array part of the table.
68-- If the version contains a revision, it is stored numerically
69-- in the 'revision' field. The original string representation of
70-- the string is preserved in the 'string' field.
71-- Returned version tables use a metatable
72-- allowing later comparison through relational operators.
73-- @param vstring string: A version number in string format.
74-- @return table or nil: A version table or nil
75-- if the input string contains invalid characters.
76function vers.parse_version(vstring)
77 if not vstring then return nil end
78 assert(type(vstring) == "string")
79
80 local cached = version_cache[vstring]
81 if cached then
82 return cached
83 end
84
85 local version = {}
86 local i = 1
87
88 local function add_token(number)
89 version[i] = version[i] and version[i] + number/100000 or number
90 i = i + 1
91 end
92
93 -- trim leading and trailing spaces
94 vstring = vstring:match("^%s*(.*)%s*$")
95 version.string = vstring
96 -- store revision separately if any
97 local main, revision = vstring:match("(.*)%-(%d+)$")
98 if revision then
99 vstring = main
100 version.revision = tonumber(revision)
101 end
102 while #vstring > 0 do
103 -- extract a number
104 local token, rest = vstring:match("^(%d+)[%.%-%_]*(.*)")
105 if token then
106 add_token(tonumber(token))
107 else
108 -- extract a word
109 token, rest = vstring:match("^(%a+)[%.%-%_]*(.*)")
110 if not token then
111 util.printerr("Warning: version number '"..vstring.."' could not be parsed.")
112 version[i] = 0
113 break
114 end
115 version[i] = deltas[token] or (token:byte() / 1000)
116 end
117 vstring = rest
118 end
119 setmetatable(version, version_mt)
120 version_cache[vstring] = version
121 return version
122end
123
124--- Utility function to compare version numbers given as strings.
125-- @param a string: one version.
126-- @param b string: another version.
127-- @return boolean: True if a > b.
128function vers.compare_versions(a, b)
129 return vers.parse_version(a) > vers.parse_version(b)
130end
131
132--- A more lenient check for equivalence between versions.
133-- This returns true if the requested components of a version
134-- match and ignore the ones that were not given. For example,
135-- when requesting "2", then "2", "2.1", "2.3.5-9"... all match.
136-- When requesting "2.1", then "2.1", "2.1.3" match, but "2.2"
137-- doesn't.
138-- @param version string or table: Version to be tested; may be
139-- in string format or already parsed into a table.
140-- @param requested string or table: Version requested; may be
141-- in string format or already parsed into a table.
142-- @return boolean: True if the tested version matches the requested
143-- version, false otherwise.
144local function partial_match(version, requested)
145 assert(type(version) == "string" or type(version) == "table")
146 assert(type(requested) == "string" or type(version) == "table")
147
148 if type(version) ~= "table" then version = vers.parse_version(version) end
149 if type(requested) ~= "table" then requested = vers.parse_version(requested) end
150 if not version or not requested then return false end
151
152 for i, ri in ipairs(requested) do
153 local vi = version[i] or 0
154 if ri ~= vi then return false end
155 end
156 if requested.revision then
157 return requested.revision == version.revision
158 end
159 return true
160end
161
162--- Check if a version satisfies a set of constraints.
163-- @param version table: A version in table format
164-- @param constraints table: An array of constraints in table format.
165-- @return boolean: True if version satisfies all constraints,
166-- false otherwise.
167function vers.match_constraints(version, constraints)
168 assert(type(version) == "table")
169 assert(type(constraints) == "table")
170 local ok = true
171 setmetatable(version, version_mt)
172 for _, constr in pairs(constraints) do
173 if type(constr.version) == "string" then
174 constr.version = vers.parse_version(constr.version)
175 end
176 local constr_version, constr_op = constr.version, constr.op
177 setmetatable(constr_version, version_mt)
178 if constr_op == "==" then ok = version == constr_version
179 elseif constr_op == "~=" then ok = version ~= constr_version
180 elseif constr_op == ">" then ok = version > constr_version
181 elseif constr_op == "<" then ok = version < constr_version
182 elseif constr_op == ">=" then ok = version >= constr_version
183 elseif constr_op == "<=" then ok = version <= constr_version
184 elseif constr_op == "~>" then ok = partial_match(version, constr_version)
185 end
186 if not ok then break end
187 end
188 return ok
189end
190
191return vers
diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua
index dcebec9b..015e2527 100644
--- a/src/luarocks/deps.lua
+++ b/src/luarocks/deps.lua
@@ -1,344 +1,40 @@
1 1
2--- Dependency handling functions. 2--- High-level dependency related 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.
14local deps = {} 3local deps = {}
15package.loaded["luarocks.deps"] = deps
16 4
17local cfg = require("luarocks.cfg") 5local cfg = require("luarocks.core.cfg")
18local manif_core = require("luarocks.manif_core") 6local manif = require("luarocks.manif")
19local path = require("luarocks.path") 7local path = require("luarocks.path")
20local dir = require("luarocks.dir") 8local dir = require("luarocks.dir")
21local util = require("luarocks.util") 9local util = require("luarocks.util")
22 10local vers = require("luarocks.vers")
23local operators = {
24 ["=="] = "==",
25 ["~="] = "~=",
26 [">"] = ">",
27 ["<"] = "<",
28 [">="] = ">=",
29 ["<="] = "<=",
30 ["~>"] = "~>",
31 -- plus some convenience translations
32 [""] = "==",
33 ["="] = "==",
34 ["!="] = "~="
35}
36
37local deltas = {
38 scm = 1100,
39 cvs = 1000,
40 rc = -1000,
41 pre = -10000,
42 beta = -100000,
43 alpha = -1000000
44}
45
46local version_mt = {
47 --- Equality comparison for versions.
48 -- All version numbers must be equal.
49 -- If both versions have revision numbers, they must be equal;
50 -- otherwise the revision number is ignored.
51 -- @param v1 table: version table to compare.
52 -- @param v2 table: version table to compare.
53 -- @return boolean: true if they are considered equivalent.
54 __eq = function(v1, v2)
55 if #v1 ~= #v2 then
56 return false
57 end
58 for i = 1, #v1 do
59 if v1[i] ~= v2[i] then
60 return false
61 end
62 end
63 if v1.revision and v2.revision then
64 return (v1.revision == v2.revision)
65 end
66 return true
67 end,
68 --- Size comparison for versions.
69 -- All version numbers are compared.
70 -- If both versions have revision numbers, they are compared;
71 -- otherwise the revision number is ignored.
72 -- @param v1 table: version table to compare.
73 -- @param v2 table: version table to compare.
74 -- @return boolean: true if v1 is considered lower than v2.
75 __lt = function(v1, v2)
76 for i = 1, math.max(#v1, #v2) do
77 local v1i, v2i = v1[i] or 0, v2[i] or 0
78 if v1i ~= v2i then
79 return (v1i < v2i)
80 end
81 end
82 if v1.revision and v2.revision then
83 return (v1.revision < v2.revision)
84 end
85 return false
86 end
87}
88
89local version_cache = {}
90setmetatable(version_cache, {
91 __mode = "kv"
92})
93
94--- Parse a version string, converting to table format.
95-- A version table contains all components of the version string
96-- converted to numeric format, stored in the array part of the table.
97-- If the version contains a revision, it is stored numerically
98-- in the 'revision' field. The original string representation of
99-- the string is preserved in the 'string' field.
100-- Returned version tables use a metatable
101-- allowing later comparison through relational operators.
102-- @param vstring string: A version number in string format.
103-- @return table or nil: A version table or nil
104-- if the input string contains invalid characters.
105function deps.parse_version(vstring)
106 if not vstring then return nil end
107 assert(type(vstring) == "string")
108
109 local cached = version_cache[vstring]
110 if cached then
111 return cached
112 end
113
114 local version = {}
115 local i = 1
116
117 local function add_token(number)
118 version[i] = version[i] and version[i] + number/100000 or number
119 i = i + 1
120 end
121
122 -- trim leading and trailing spaces
123 vstring = vstring:match("^%s*(.*)%s*$")
124 version.string = vstring
125 -- store revision separately if any
126 local main, revision = vstring:match("(.*)%-(%d+)$")
127 if revision then
128 vstring = main
129 version.revision = tonumber(revision)
130 end
131 while #vstring > 0 do
132 -- extract a number
133 local token, rest = vstring:match("^(%d+)[%.%-%_]*(.*)")
134 if token then
135 add_token(tonumber(token))
136 else
137 -- extract a word
138 token, rest = vstring:match("^(%a+)[%.%-%_]*(.*)")
139 if not token then
140 util.printerr("Warning: version number '"..vstring.."' could not be parsed.")
141 version[i] = 0
142 break
143 end
144 version[i] = deltas[token] or (token:byte() / 1000)
145 end
146 vstring = rest
147 end
148 setmetatable(version, version_mt)
149 version_cache[vstring] = version
150 return version
151end
152
153--- Utility function to compare version numbers given as strings.
154-- @param a string: one version.
155-- @param b string: another version.
156-- @return boolean: True if a > b.
157function deps.compare_versions(a, b)
158 return deps.parse_version(a) > deps.parse_version(b)
159end
160
161--- Consumes a constraint from a string, converting it to table format.
162-- For example, a string ">= 1.0, > 2.0" is converted to a table in the
163-- format {op = ">=", version={1,0}} and the rest, "> 2.0", is returned
164-- back to the caller.
165-- @param input string: A list of constraints in string format.
166-- @return (table, string) or nil: A table representing the same
167-- constraints and the string with the unused input, or nil if the
168-- input string is invalid.
169local function parse_constraint(input)
170 assert(type(input) == "string")
171
172 local no_upgrade, op, version, rest = input:match("^(@?)([<>=~!]*)%s*([%w%.%_%-]+)[%s,]*(.*)")
173 local _op = operators[op]
174 version = deps.parse_version(version)
175 if not _op then
176 return nil, "Encountered bad constraint operator: '"..tostring(op).."' in '"..input.."'"
177 end
178 if not version then
179 return nil, "Could not parse version from constraint: '"..input.."'"
180 end
181 return { op = _op, version = version, no_upgrade = no_upgrade=="@" and true or nil }, rest
182end
183
184--- Convert a list of constraints from string to table format.
185-- For example, a string ">= 1.0, < 2.0" is converted to a table in the format
186-- {{op = ">=", version={1,0}}, {op = "<", version={2,0}}}.
187-- Version tables use a metatable allowing later comparison through
188-- relational operators.
189-- @param input string: A list of constraints in string format.
190-- @return table or nil: A table representing the same constraints,
191-- or nil if the input string is invalid.
192function deps.parse_constraints(input)
193 assert(type(input) == "string")
194
195 local constraints, constraint, oinput = {}, nil, input
196 while #input > 0 do
197 constraint, input = parse_constraint(input)
198 if constraint then
199 table.insert(constraints, constraint)
200 else
201 return nil, "Failed to parse constraint '"..tostring(oinput).."' with error: ".. input
202 end
203 end
204 return constraints
205end
206
207--- Convert a dependency from string to table format.
208-- For example, a string "foo >= 1.0, < 2.0"
209-- is converted to a table in the format
210-- {name = "foo", constraints = {{op = ">=", version={1,0}},
211-- {op = "<", version={2,0}}}}. Version tables use a metatable
212-- allowing later comparison through relational operators.
213-- @param dep string: A dependency in string format
214-- as entered in rockspec files.
215-- @return table or nil: A table representing the same dependency relation,
216-- or nil if the input string is invalid.
217function deps.parse_dep(dep)
218 assert(type(dep) == "string")
219
220 local name, rest = dep:match("^%s*([a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*(.*)")
221 if not name then return nil, "failed to extract dependency name from '"..tostring(dep).."'" end
222 local constraints, err = deps.parse_constraints(rest)
223 if not constraints then return nil, err end
224 return { name = name, constraints = constraints }
225end
226
227--- Convert a version table to a string.
228-- @param v table: The version table
229-- @param internal boolean or nil: Whether to display versions in their
230-- internal representation format or how they were specified.
231-- @return string: The dependency information pretty-printed as a string.
232function deps.show_version(v, internal)
233 assert(type(v) == "table")
234 assert(type(internal) == "boolean" or not internal)
235
236 return (internal
237 and table.concat(v, ":")..(v.revision and tostring(v.revision) or "")
238 or v.string)
239end
240
241--- Convert a dependency in table format to a string.
242-- @param dep table: The dependency in table format
243-- @param internal boolean or nil: Whether to display versions in their
244-- internal representation format or how they were specified.
245-- @return string: The dependency information pretty-printed as a string.
246function deps.show_dep(dep, internal)
247 assert(type(dep) == "table")
248 assert(type(internal) == "boolean" or not internal)
249
250 if #dep.constraints > 0 then
251 local pretty = {}
252 for _, c in ipairs(dep.constraints) do
253 table.insert(pretty, c.op .. " " .. deps.show_version(c.version, internal))
254 end
255 return dep.name.." "..table.concat(pretty, ", ")
256 else
257 return dep.name
258 end
259end
260
261--- A more lenient check for equivalence between versions.
262-- This returns true if the requested components of a version
263-- match and ignore the ones that were not given. For example,
264-- when requesting "2", then "2", "2.1", "2.3.5-9"... all match.
265-- When requesting "2.1", then "2.1", "2.1.3" match, but "2.2"
266-- doesn't.
267-- @param version string or table: Version to be tested; may be
268-- in string format or already parsed into a table.
269-- @param requested string or table: Version requested; may be
270-- in string format or already parsed into a table.
271-- @return boolean: True if the tested version matches the requested
272-- version, false otherwise.
273local function partial_match(version, requested)
274 assert(type(version) == "string" or type(version) == "table")
275 assert(type(requested) == "string" or type(version) == "table")
276
277 if type(version) ~= "table" then version = deps.parse_version(version) end
278 if type(requested) ~= "table" then requested = deps.parse_version(requested) end
279 if not version or not requested then return false end
280
281 for i, ri in ipairs(requested) do
282 local vi = version[i] or 0
283 if ri ~= vi then return false end
284 end
285 if requested.revision then
286 return requested.revision == version.revision
287 end
288 return true
289end
290
291--- Check if a version satisfies a set of constraints.
292-- @param version table: A version in table format
293-- @param constraints table: An array of constraints in table format.
294-- @return boolean: True if version satisfies all constraints,
295-- false otherwise.
296function deps.match_constraints(version, constraints)
297 assert(type(version) == "table")
298 assert(type(constraints) == "table")
299 local ok = true
300 setmetatable(version, version_mt)
301 for _, constr in pairs(constraints) do
302 if type(constr.version) == "string" then
303 constr.version = deps.parse_version(constr.version)
304 end
305 local constr_version, constr_op = constr.version, constr.op
306 setmetatable(constr_version, version_mt)
307 if constr_op == "==" then ok = version == constr_version
308 elseif constr_op == "~=" then ok = version ~= constr_version
309 elseif constr_op == ">" then ok = version > constr_version
310 elseif constr_op == "<" then ok = version < constr_version
311 elseif constr_op == ">=" then ok = version >= constr_version
312 elseif constr_op == "<=" then ok = version <= constr_version
313 elseif constr_op == "~>" then ok = partial_match(version, constr_version)
314 end
315 if not ok then break end
316 end
317 return ok
318end
319 11
320--- Attempt to match a dependency to an installed rock. 12--- Attempt to match a dependency to an installed rock.
321-- @param dep table: A dependency parsed in table format. 13-- @param dep table: A dependency parsed in table format.
322-- @param blacklist table: Versions that can't be accepted. Table where keys 14-- @param blacklist table: Versions that can't be accepted. Table where keys
323-- are program versions and values are 'true'. 15-- are program versions and values are 'true'.
16-- @param rocks_provided table: A table of auto-dependencies provided
17-- by this Lua implementation for the given dependency.
324-- @return string or nil: latest installed version of the rock matching the dependency 18-- @return string or nil: latest installed version of the rock matching the dependency
325-- or nil if it could not be matched. 19-- or nil if it could not be matched.
326local function match_dep(dep, blacklist, deps_mode) 20local function match_dep(dep, blacklist, deps_mode, rocks_provided)
327 assert(type(dep) == "table") 21 assert(type(dep) == "table")
328 22 assert(type(rocks_provided) == "table")
23
329 local versions 24 local versions
330 if cfg.rocks_provided[dep.name] then 25 local provided = rocks_provided[dep.name]
331 -- provided rocks have higher priority than manifest's rocks 26 if provided then
332 versions = { cfg.rocks_provided[dep.name] } 27 -- Provided rocks have higher priority than manifest's rocks.
28 versions = { provided }
333 else 29 else
334 versions = manif_core.get_versions(dep.name, deps_mode) 30 versions = manif.get_versions(dep.name, deps_mode)
335 end 31 end
336 32
337 local latest_version 33 local latest_version
338 for _, vstring in ipairs(versions) do 34 for _, vstring in ipairs(versions) do
339 if not blacklist or not blacklist[vstring] then 35 if not blacklist or not blacklist[vstring] then
340 local version = deps.parse_version(vstring) 36 local version = vers.parse_version(vstring)
341 if deps.match_constraints(version, dep.constraints) then 37 if vers.match_constraints(version, dep.constraints) then
342 if not latest_version or version > latest_version then 38 if not latest_version or version > latest_version then
343 latest_version = version 39 latest_version = version
344 end 40 end
@@ -366,9 +62,9 @@ function deps.match_deps(rockspec, blacklist, deps_mode)
366 local matched, missing, no_upgrade = {}, {}, {} 62 local matched, missing, no_upgrade = {}, {}, {}
367 63
368 for _, dep in ipairs(rockspec.dependencies) do 64 for _, dep in ipairs(rockspec.dependencies) do
369 local found = match_dep(dep, blacklist and blacklist[dep.name] or nil, deps_mode) 65 local found = match_dep(dep, blacklist and blacklist[dep.name] or nil, deps_mode, rockspec.rocks_provided)
370 if found then 66 if found then
371 if not cfg.rocks_provided[dep.name] then 67 if not rockspec.rocks_provided[dep.name] then
372 matched[dep] = {name = dep.name, version = found} 68 matched[dep] = {name = dep.name, version = found}
373 end 69 end
374 else 70 else
@@ -393,10 +89,10 @@ local function values_set(tbl)
393 return set 89 return set
394end 90end
395 91
396local function rock_status(name, deps_mode) 92local function rock_status(name, deps_mode, rocks_provided)
397 local search = require("luarocks.search") 93 local search = require("luarocks.search")
398 local installed = match_dep(search.make_query(name), nil, deps_mode) 94 local installed = match_dep(search.make_query(name), nil, deps_mode, rocks_provided)
399 local installation_type = cfg.rocks_provided[name] and "provided by VM" or "installed" 95 local installation_type = rocks_provided[name] and "provided by VM" or "installed"
400 return installed and installed.." "..installation_type or "not installed" 96 return installed and installed.." "..installation_type or "not installed"
401end 97end
402 98
@@ -405,19 +101,21 @@ end
405-- @param version string: package version. 101-- @param version string: package version.
406-- @param dependencies table: array of dependencies. 102-- @param dependencies table: array of dependencies.
407-- @param deps_mode string: Which trees to check dependencies for: 103-- @param deps_mode string: Which trees to check dependencies for:
104-- @param rocks_provided table: A table of auto-dependencies provided
105-- by this Lua implementation for the given dependency.
408-- "one" for the current default tree, "all" for all trees, 106-- "one" for the current default tree, "all" for all trees,
409-- "order" for all trees with priority >= the current default, "none" for no trees. 107-- "order" for all trees with priority >= the current default, "none" for no trees.
410function deps.report_missing_dependencies(name, version, dependencies, deps_mode) 108function deps.report_missing_dependencies(name, version, dependencies, deps_mode, rocks_provided)
411 local first_missing_dep = true 109 local first_missing_dep = true
412 110
413 for _, dep in ipairs(dependencies) do 111 for _, dep in ipairs(dependencies) do
414 if not match_dep(dep, nil, deps_mode) then 112 if not match_dep(dep, nil, deps_mode, rocks_provided) then
415 if first_missing_dep then 113 if first_missing_dep then
416 util.printout(("Missing dependencies for %s %s:"):format(name, version)) 114 util.printout(("Missing dependencies for %s %s:"):format(name, version))
417 first_missing_dep = false 115 first_missing_dep = false
418 end 116 end
419 117
420 util.printout((" %s (%s)"):format(deps.show_dep(dep), rock_status(dep.name, deps_mode))) 118 util.printout((" %s (%s)"):format(vers.show_dep(dep), rock_status(dep.name, deps_mode, rocks_provided)))
421 end 119 end
422 end 120 end
423end 121end
@@ -432,7 +130,7 @@ end
432function deps.fulfill_dependencies(rockspec, deps_mode) 130function deps.fulfill_dependencies(rockspec, deps_mode)
433 131
434 local search = require("luarocks.search") 132 local search = require("luarocks.search")
435 local install = require("luarocks.install") 133 local install = require("luarocks.cmd.install")
436 134
437 if rockspec.supported_platforms then 135 if rockspec.supported_platforms then
438 if not deps.platforms_set then 136 if not deps.platforms_set then
@@ -440,7 +138,8 @@ function deps.fulfill_dependencies(rockspec, deps_mode)
440 end 138 end
441 local supported = nil 139 local supported = nil
442 for _, plat in pairs(rockspec.supported_platforms) do 140 for _, plat in pairs(rockspec.supported_platforms) do
443 local neg, plat = plat:match("^(!?)(.*)") 141 local neg
142 neg, plat = plat:match("^(!?)(.*)")
444 if neg == "!" then 143 if neg == "!" then
445 if deps.platforms_set[plat] then 144 if deps.platforms_set[plat] then
446 return nil, "This rockspec for "..rockspec.package.." does not support "..plat.." platforms." 145 return nil, "This rockspec for "..rockspec.package.." does not support "..plat.." platforms."
@@ -461,23 +160,23 @@ function deps.fulfill_dependencies(rockspec, deps_mode)
461 end 160 end
462 end 161 end
463 162
464 deps.report_missing_dependencies(rockspec.name, rockspec.version, rockspec.dependencies, deps_mode) 163 deps.report_missing_dependencies(rockspec.name, rockspec.version, rockspec.dependencies, deps_mode, rockspec.rocks_provided)
465 164
466 local first_missing_dep = true 165 local first_missing_dep = true
467 166
468 for _, dep in ipairs(rockspec.dependencies) do 167 for _, dep in ipairs(rockspec.dependencies) do
469 if not match_dep(dep, nil, deps_mode) then 168 if not match_dep(dep, nil, deps_mode, rockspec.rocks_provided) then
470 if first_missing_dep then 169 if first_missing_dep then
471 util.printout() 170 util.printout()
472 first_missing_dep = false 171 first_missing_dep = false
473 end 172 end
474 173
475 util.printout(("%s %s depends on %s (%s)"):format( 174 util.printout(("%s %s depends on %s (%s)"):format(
476 rockspec.name, rockspec.version, deps.show_dep(dep), rock_status(dep.name, deps_mode))) 175 rockspec.name, rockspec.version, vers.show_dep(dep), rock_status(dep.name, deps_mode, rockspec.rocks_provided)))
477 176
478 if dep.constraints[1] and dep.constraints[1].no_upgrade then 177 if dep.constraints[1] and dep.constraints[1].no_upgrade then
479 util.printerr("This version of "..rockspec.name.." is designed for use with") 178 util.printerr("This version of "..rockspec.name.." is designed for use with")
480 util.printerr(deps.show_dep(dep)..", but is configured to avoid upgrading it") 179 util.printerr(vers.show_dep(dep)..", but is configured to avoid upgrading it")
481 util.printerr("automatically. Please upgrade "..dep.name.." with") 180 util.printerr("automatically. Please upgrade "..dep.name.." with")
482 util.printerr(" luarocks install "..dep.name) 181 util.printerr(" luarocks install "..dep.name)
483 util.printerr("or choose an older version of "..rockspec.name.." with") 182 util.printerr("or choose an older version of "..rockspec.name.." with")
@@ -487,7 +186,7 @@ function deps.fulfill_dependencies(rockspec, deps_mode)
487 186
488 local url, search_err = search.find_suitable_rock(dep) 187 local url, search_err = search.find_suitable_rock(dep)
489 if not url then 188 if not url then
490 return nil, "Could not satisfy dependency "..deps.show_dep(dep)..": "..search_err 189 return nil, "Could not satisfy dependency "..vers.show_dep(dep)..": "..search_err
491 end 190 end
492 util.printout("Installing "..url) 191 util.printout("Installing "..url)
493 local ok, install_err, errcode = install.command({deps_mode = deps_mode}, url) 192 local ok, install_err, errcode = install.command({deps_mode = deps_mode}, url)
@@ -717,7 +416,10 @@ function deps.scan_deps(results, manifest, name, version, deps_mode)
717 end 416 end
718 dependencies_name[version] = rockspec.dependencies 417 dependencies_name[version] = rockspec.dependencies
719 else 418 else
720 rockspec = { dependencies = deplist } 419 rockspec = {
420 dependencies = deplist,
421 rocks_provided = setmetatable({}, { __index = cfg.rocks_provided_3_0 })
422 }
721 end 423 end
722 local matched = deps.match_deps(rockspec, nil, deps_mode) 424 local matched = deps.match_deps(rockspec, nil, deps_mode)
723 results[name] = version 425 results[name] = version
@@ -745,8 +447,4 @@ function deps.get_deps_mode(flags)
745 end 447 end
746end 448end
747 449
748function deps.deps_mode_to_flag(deps_mode)
749 return "--deps-mode="..deps_mode
750end
751
752return deps 450return deps
diff --git a/src/luarocks/dir.lua b/src/luarocks/dir.lua
index f72ebd6c..71477804 100644
--- a/src/luarocks/dir.lua
+++ b/src/luarocks/dir.lua
@@ -1,9 +1,7 @@
1 1
2--- Generic utilities for handling pathnames. 2--- Generic utilities for handling pathnames.
3local dir = {} 3local dir = {}
4package.loaded["luarocks.dir"] = dir 4setmetatable(dir, { __index = require("luarocks.core.dir") })
5
6dir.separator = "/"
7 5
8--- Strip the path off a path+filename. 6--- Strip the path off a path+filename.
9-- @param pathname string: A path+name, such as "/a/b/c" 7-- @param pathname string: A path+name, such as "/a/b/c"
@@ -26,39 +24,6 @@ function dir.dir_name(pathname)
26 return (pathname:gsub("/*$", ""):match("(.*)[/]+[^/]*")) or "" 24 return (pathname:gsub("/*$", ""):match("(.*)[/]+[^/]*")) or ""
27end 25end
28 26
29--- Describe a path in a cross-platform way.
30-- Use this function to avoid platform-specific directory
31-- separators in other modules. Removes trailing slashes from
32-- each component given, to avoid repeated separators.
33-- Separators inside strings are kept, to handle URLs containing
34-- protocols.
35-- @param ... strings representing directories
36-- @return string: a string with a platform-specific representation
37-- of the path.
38function dir.path(...)
39 local t = {...}
40 while t[1] == "" do
41 table.remove(t, 1)
42 end
43 return (table.concat(t, "/"):gsub("([^:])/+", "%1/"):gsub("^/+", "/"):gsub("/*$", ""))
44end
45
46--- Split protocol and path from an URL or local pathname.
47-- URLs should be in the "protocol://path" format.
48-- For local pathnames, "file" is returned as the protocol.
49-- @param url string: an URL or a local pathname.
50-- @return string, string: the protocol, and the pathname without the protocol.
51function dir.split_url(url)
52 assert(type(url) == "string")
53
54 local protocol, pathname = url:match("^([^:]*)://(.*)")
55 if not protocol then
56 protocol = "file"
57 pathname = url
58 end
59 return protocol, pathname
60end
61
62--- Normalize a url or local path. 27--- Normalize a url or local path.
63-- URLs should be in the "protocol://path" format. System independent 28-- URLs should be in the "protocol://path" format. System independent
64-- forward slashes are used, removing trailing and double slashes 29-- forward slashes are used, removing trailing and double slashes
diff --git a/src/luarocks/download.lua b/src/luarocks/download.lua
index e434cdbb..ec9996b2 100644
--- a/src/luarocks/download.lua
+++ b/src/luarocks/download.lua
@@ -1,27 +1,11 @@
1
2--- Module implementing the luarocks "download" command.
3-- Download a rock from the repository.
4local download = {} 1local download = {}
5package.loaded["luarocks.download"] = download
6 2
7local util = require("luarocks.util")
8local path = require("luarocks.path") 3local path = require("luarocks.path")
9local fetch = require("luarocks.fetch") 4local fetch = require("luarocks.fetch")
10local search = require("luarocks.search") 5local search = require("luarocks.search")
11local fs = require("luarocks.fs") 6local fs = require("luarocks.fs")
12local dir = require("luarocks.dir") 7local dir = require("luarocks.dir")
13local cfg = require("luarocks.cfg") 8local cfg = require("luarocks.core.cfg")
14
15util.add_run_function(download)
16download.help_summary = "Download a specific rock file from a rocks server."
17download.help_arguments = "[--all] [--arch=<arch> | --source | --rockspec] [<name> [<version>]]"
18
19download.help = [[
20--all Download all files if there are multiple matches.
21--source Download .src.rock if available.
22--rockspec Download .rockspec if available.
23--arch=<arch> Download rock for a specific architecture.
24]]
25 9
26local function get_file(filename) 10local function get_file(filename)
27 local protocol, pathname = dir.split_url(filename) 11 local protocol, pathname = dir.split_url(filename)
@@ -79,31 +63,4 @@ function download.download(arch, name, version, all)
79 (search_err and ": "..search_err or ".") 63 (search_err and ": "..search_err or ".")
80end 64end
81 65
82--- Driver function for the "download" command.
83-- @param name string: a rock name.
84-- @param version string or nil: if the name of a package is given, a
85-- version may also be passed.
86-- @return boolean or (nil, string): true if successful or nil followed
87-- by an error message.
88function download.command(flags, name, version)
89 assert(type(version) == "string" or not version)
90 if type(name) ~= "string" and not flags["all"] then
91 return nil, "Argument missing. "..util.see_help("download")
92 end
93 if not name then name, version = "", "" end
94
95 local arch
96
97 if flags["source"] then
98 arch = "src"
99 elseif flags["rockspec"] then
100 arch = "rockspec"
101 elseif flags["arch"] then
102 arch = flags["arch"]
103 end
104
105 local dl, err = download.download(arch, name:lower(), version, flags["all"])
106 return dl and true, err
107end
108
109return download 66return download
diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua
index 76f366cd..2cad3df2 100644
--- a/src/luarocks/fetch.lua
+++ b/src/luarocks/fetch.lua
@@ -1,16 +1,15 @@
1 1
2--- Functions related to fetching and loading local and remote files. 2--- Functions related to fetching and loading local and remote files.
3local fetch = {} 3local fetch = {}
4package.loaded["luarocks.fetch"] = fetch
5 4
6local fs = require("luarocks.fs") 5local fs = require("luarocks.fs")
7local dir = require("luarocks.dir") 6local dir = require("luarocks.dir")
8local type_check = require("luarocks.type_check") 7local type_check = require("luarocks.type_check")
9local path = require("luarocks.path") 8local path = require("luarocks.path")
10local deps = require("luarocks.deps") 9local vers = require("luarocks.vers")
11local persist = require("luarocks.persist") 10local persist = require("luarocks.persist")
12local util = require("luarocks.util") 11local util = require("luarocks.util")
13local cfg = require("luarocks.cfg") 12local cfg = require("luarocks.core.cfg")
14 13
15function fetch.is_basic_protocol(protocol, remote) 14function fetch.is_basic_protocol(protocol, remote)
16 return protocol == "http" or protocol == "https" or protocol == "ftp" or (not remote and protocol == "file") 15 return protocol == "http" or protocol == "https" or protocol == "ftp" or (not remote and protocol == "file")
@@ -198,7 +197,7 @@ function fetch.load_local_rockspec(filename, quick)
198 local globals = err 197 local globals = err
199 198
200 if rockspec.rockspec_format then 199 if rockspec.rockspec_format then
201 if deps.compare_versions(rockspec.rockspec_format, type_check.rockspec_format) then 200 if vers.compare_versions(rockspec.rockspec_format, type_check.rockspec_format) then
202 return nil, "Rockspec format "..rockspec.rockspec_format.." is not supported, please upgrade LuaRocks." 201 return nil, "Rockspec format "..rockspec.rockspec_format.." is not supported, please upgrade LuaRocks."
203 end 202 end
204 end 203 end
@@ -209,6 +208,8 @@ function fetch.load_local_rockspec(filename, quick)
209 return nil, filename..": "..err 208 return nil, filename..": "..err
210 end 209 end
211 end 210 end
211
212 rockspec.format_is_at_least = vers.format_is_at_least
212 213
213 util.platform_overrides(rockspec.build) 214 util.platform_overrides(rockspec.build)
214 util.platform_overrides(rockspec.dependencies) 215 util.platform_overrides(rockspec.dependencies)
@@ -244,13 +245,22 @@ function fetch.load_local_rockspec(filename, quick)
244 rockspec.local_filename = filename 245 rockspec.local_filename = filename
245 local filebase = rockspec.source.file or rockspec.source.url 246 local filebase = rockspec.source.file or rockspec.source.url
246 local base = fetch.url_to_base_dir(filebase) 247 local base = fetch.url_to_base_dir(filebase)
248 rockspec.source.dir_set = rockspec.source.dir ~= nil
247 rockspec.source.dir = rockspec.source.dir 249 rockspec.source.dir = rockspec.source.dir
248 or rockspec.source.module 250 or rockspec.source.module
249 or ((filebase:match("%.lua$") or filebase:match("%.c$")) and ".") 251 or ( (filebase:match("%.lua$") or filebase:match("%.c$"))
252 and (rockspec:format_is_at_least("3.0")
253 and (fetch.is_basic_protocol(protocol) and "." or base)
254 or ".") )
250 or base 255 or base
256
257 rockspec.rocks_provided = (rockspec:format_is_at_least("3.0")
258 and cfg.rocks_provided_3_0
259 or cfg.rocks_provided)
260
251 if rockspec.dependencies then 261 if rockspec.dependencies then
252 for i = 1, #rockspec.dependencies do 262 for i = 1, #rockspec.dependencies do
253 local parsed, err = deps.parse_dep(rockspec.dependencies[i]) 263 local parsed, err = vers.parse_dep(rockspec.dependencies[i])
254 if not parsed then 264 if not parsed then
255 return nil, "Parse error processing dependency '"..rockspec.dependencies[i].."': "..tostring(err) 265 return nil, "Parse error processing dependency '"..rockspec.dependencies[i].."': "..tostring(err)
256 end 266 end
@@ -347,7 +357,29 @@ function fetch.get_sources(rockspec, extract, dest_dir)
347 ok, err = fs.unpack_archive(rockspec.source.file) 357 ok, err = fs.unpack_archive(rockspec.source.file)
348 if not ok then return nil, err end 358 if not ok then return nil, err end
349 if not fs.exists(rockspec.source.dir) then 359 if not fs.exists(rockspec.source.dir) then
350 return nil, "Directory "..rockspec.source.dir.." not found inside archive "..rockspec.source.file, "source.dir", source_file, store_dir 360
361 -- If rockspec.source.dir can't be found, see if we only have one
362 -- directory in store_dir. If that's the case, assume it's what
363 -- we're looking for.
364 -- We only do this if the rockspec source.dir was not set, and only
365 -- with rockspecs newer than 3.0.
366 local dir_count, found_dir = 0
367
368 if not rockspec.source.dir_set and rockspec:format_is_at_least("3.0") then
369 local files = fs.list_dir()
370 for _, f in ipairs(files) do
371 if fs.is_dir(f) then
372 dir_count = dir_count + 1
373 found_dir = f
374 end
375 end
376 end
377
378 if dir_count == 1 then
379 rockspec.source.dir = found_dir
380 else
381 return nil, "Directory "..rockspec.source.dir.." not found inside archive "..rockspec.source.file, "source.dir", source_file, store_dir
382 end
351 end 383 end
352 fs.pop_dir() 384 fs.pop_dir()
353 end 385 end
diff --git a/src/luarocks/fetch/git.lua b/src/luarocks/fetch/git.lua
index f61d89e9..edc11762 100644
--- a/src/luarocks/fetch/git.lua
+++ b/src/luarocks/fetch/git.lua
@@ -6,20 +6,48 @@ local unpack = unpack or table.unpack
6 6
7local fs = require("luarocks.fs") 7local fs = require("luarocks.fs")
8local dir = require("luarocks.dir") 8local dir = require("luarocks.dir")
9local vers = require("luarocks.vers")
9local util = require("luarocks.util") 10local util = require("luarocks.util")
10 11
12local cached_git_version
13
14--- Get git version.
15-- @param git_cmd string: name of git command.
16-- @return table: git version as returned by luarocks.vers.parse_version.
17local function git_version(git_cmd)
18 if not cached_git_version then
19 local version_line = io.popen(fs.Q(git_cmd)..' --version'):read()
20 local version_string = version_line:match('%d-%.%d+%.?%d*')
21 cached_git_version = vers.parse_version(version_string)
22 end
23
24 return cached_git_version
25end
26
27--- Check if git satisfies version requirement.
28-- @param git_cmd string: name of git command.
29-- @param version string: required version.
30-- @return boolean: true if git matches version or is newer, false otherwise.
31local function git_is_at_least(git_cmd, version)
32 return git_version(git_cmd) >= vers.parse_version(version)
33end
34
11--- Git >= 1.7.10 can clone a branch **or tag**, < 1.7.10 by branch only. We 35--- Git >= 1.7.10 can clone a branch **or tag**, < 1.7.10 by branch only. We
12-- need to know this in order to build the appropriate command; if we can't 36-- need to know this in order to build the appropriate command; if we can't
13-- clone by tag then we'll have to issue a subsequent command to check out the 37-- clone by tag then we'll have to issue a subsequent command to check out the
14-- given tag. 38-- given tag.
39-- @param git_cmd string: name of git command.
15-- @return boolean: Whether Git can clone by tag. 40-- @return boolean: Whether Git can clone by tag.
16local function git_can_clone_by_tag(git_cmd) 41local function git_can_clone_by_tag(git_cmd)
17 local version_string = io.popen(fs.Q(git_cmd)..' --version'):read() 42 return git_is_at_least(git_cmd, "1.7.10")
18 local major, minor, tiny = version_string:match('(%d-)%.(%d+)%.?(%d*)') 43end
19 major, minor, tiny = tonumber(major), tonumber(minor), tonumber(tiny) or 0 44
20 local value = major > 1 or (major == 1 and (minor > 7 or (minor == 7 and tiny >= 10))) 45--- Git >= 1.8.4 can fetch submodules shallowly, saving bandwidth and time for
21 git_can_clone_by_tag = function() return value end 46-- submodules with large history.
22 return value 47-- @param git_cmd string: name of git command.
48-- @return boolean: Whether Git can fetch submodules shallowly.
49local function git_supports_shallow_submodules(git_cmd)
50 return git_is_at_least(git_cmd, "1.8.4")
23end 51end
24 52
25--- Download sources for building a rock, using git. 53--- Download sources for building a rock, using git.
@@ -76,12 +104,25 @@ function git.get_sources(rockspec, extract, dest_dir, depth)
76 ok, err = fs.change_dir(module) 104 ok, err = fs.change_dir(module)
77 if not ok then return nil, err end 105 if not ok then return nil, err end
78 if tag_or_branch and not git_can_clone_by_tag() then 106 if tag_or_branch and not git_can_clone_by_tag() then
79 local checkout_command = {fs.Q(git_cmd), "checkout", tag_or_branch} 107 if not fs.execute(fs.Q(git_cmd), "checkout", tag_or_branch) then
80 if not fs.execute(unpack(checkout_command)) then
81 return nil, 'Failed to check out the "' .. tag_or_branch ..'" tag or branch.' 108 return nil, 'Failed to check out the "' .. tag_or_branch ..'" tag or branch.'
82 end 109 end
83 end 110 end
84 111
112 -- Fetching git submodules is supported only when rockspec format is >= 3.0.
113 if rockspec:format_is_at_least("3.0") then
114 command = {fs.Q(git_cmd), "submodule", "update", "--init", "--recursive"}
115
116 if git_supports_shallow_submodules(git_cmd) then
117 -- Fetch only the last commit of each submodule.
118 table.insert(command, 5, "--depth=1")
119 end
120
121 if not fs.execute(unpack(command)) then
122 return nil, 'Failed to fetch submodules.'
123 end
124 end
125
85 fs.delete(dir.path(store_dir, module, ".git")) 126 fs.delete(dir.path(store_dir, module, ".git"))
86 fs.delete(dir.path(store_dir, module, ".gitignore")) 127 fs.delete(dir.path(store_dir, module, ".gitignore"))
87 fs.pop_dir() 128 fs.pop_dir()
diff --git a/src/luarocks/fs.lua b/src/luarocks/fs.lua
index 54cc7d73..7322e552 100644
--- a/src/luarocks/fs.lua
+++ b/src/luarocks/fs.lua
@@ -8,9 +8,10 @@
8local pairs = pairs 8local pairs = pairs
9 9
10local fs = {} 10local fs = {}
11-- To avoid a loop when loading the other fs modules.
11package.loaded["luarocks.fs"] = fs 12package.loaded["luarocks.fs"] = fs
12 13
13local cfg = require("luarocks.cfg") 14local cfg = require("luarocks.core.cfg")
14 15
15local pack = table.pack or function(...) return { n = select("#", ...), ... } end 16local pack = table.pack or function(...) return { n = select("#", ...), ... } end
16local unpack = table.unpack or unpack 17local unpack = table.unpack or unpack
diff --git a/src/luarocks/fs/lua.lua b/src/luarocks/fs/lua.lua
index 841b7243..54da4afd 100644
--- a/src/luarocks/fs/lua.lua
+++ b/src/luarocks/fs/lua.lua
@@ -6,7 +6,7 @@ local fs_lua = {}
6 6
7local fs = require("luarocks.fs") 7local fs = require("luarocks.fs")
8 8
9local cfg = require("luarocks.cfg") 9local cfg = require("luarocks.core.cfg")
10local dir = require("luarocks.dir") 10local dir = require("luarocks.dir")
11local util = require("luarocks.util") 11local util = require("luarocks.util")
12local path = require("luarocks.path") 12local path = require("luarocks.path")
@@ -489,10 +489,10 @@ end
489 489
490if unzip_ok then 490if unzip_ok then
491--- Uncompress files from a .zip archive. 491--- Uncompress files from a .zip archive.
492-- @param zipfile string: pathname of .zip archive to be extracted. 492-- @param filename string: pathname of .zip archive to be extracted.
493-- @return boolean: true on success, false on failure. 493-- @return boolean: true on success, false on failure.
494function fs_lua.unzip(zipfile) 494function fs_lua.unzip(filename)
495 local zipfile, err = luazip.open(zipfile) 495 local zipfile, err = luazip.open(filename)
496 if not zipfile then return nil, err end 496 if not zipfile then return nil, err end
497 local files = zipfile:files() 497 local files = zipfile:files()
498 local file = files() 498 local file = files()
diff --git a/src/luarocks/fs/tools.lua b/src/luarocks/fs/tools.lua
index 60d1d9ff..16dc9cf2 100644
--- a/src/luarocks/fs/tools.lua
+++ b/src/luarocks/fs/tools.lua
@@ -4,7 +4,7 @@ local tools = {}
4 4
5local fs = require("luarocks.fs") 5local fs = require("luarocks.fs")
6local dir = require("luarocks.dir") 6local dir = require("luarocks.dir")
7local cfg = require("luarocks.cfg") 7local cfg = require("luarocks.core.cfg")
8 8
9local vars = cfg.variables 9local vars = cfg.variables
10 10
diff --git a/src/luarocks/fs/unix.lua b/src/luarocks/fs/unix.lua
index 5c51bad5..89e3ec73 100644
--- a/src/luarocks/fs/unix.lua
+++ b/src/luarocks/fs/unix.lua
@@ -4,7 +4,7 @@ local unix = {}
4 4
5local fs = require("luarocks.fs") 5local fs = require("luarocks.fs")
6 6
7local cfg = require("luarocks.cfg") 7local cfg = require("luarocks.core.cfg")
8local dir = require("luarocks.dir") 8local dir = require("luarocks.dir")
9local util = require("luarocks.util") 9local util = require("luarocks.util")
10 10
diff --git a/src/luarocks/fs/unix/tools.lua b/src/luarocks/fs/unix/tools.lua
index f72001cb..b2ace3b1 100644
--- a/src/luarocks/fs/unix/tools.lua
+++ b/src/luarocks/fs/unix/tools.lua
@@ -4,7 +4,7 @@ local tools = {}
4 4
5local fs = require("luarocks.fs") 5local fs = require("luarocks.fs")
6local dir = require("luarocks.dir") 6local dir = require("luarocks.dir")
7local cfg = require("luarocks.cfg") 7local cfg = require("luarocks.core.cfg")
8 8
9local vars = cfg.variables 9local vars = cfg.variables
10 10
diff --git a/src/luarocks/fs/win32.lua b/src/luarocks/fs/win32.lua
index 55d69f9f..19c603dc 100644
--- a/src/luarocks/fs/win32.lua
+++ b/src/luarocks/fs/win32.lua
@@ -5,7 +5,7 @@ local win32 = {}
5 5
6local fs = require("luarocks.fs") 6local fs = require("luarocks.fs")
7 7
8local cfg = require("luarocks.cfg") 8local cfg = require("luarocks.core.cfg")
9local dir = require("luarocks.dir") 9local dir = require("luarocks.dir")
10local util = require("luarocks.util") 10local util = require("luarocks.util")
11 11
diff --git a/src/luarocks/fs/win32/tools.lua b/src/luarocks/fs/win32/tools.lua
index 4adc78d1..7f6b853e 100644
--- a/src/luarocks/fs/win32/tools.lua
+++ b/src/luarocks/fs/win32/tools.lua
@@ -6,7 +6,7 @@ local tools = {}
6 6
7local fs = require("luarocks.fs") 7local fs = require("luarocks.fs")
8local dir = require("luarocks.dir") 8local dir = require("luarocks.dir")
9local cfg = require("luarocks.cfg") 9local cfg = require("luarocks.core.cfg")
10 10
11local vars = cfg.variables 11local vars = cfg.variables
12 12
diff --git a/src/luarocks/index.lua b/src/luarocks/index.lua
index e1f563ef..80371151 100644
--- a/src/luarocks/index.lua
+++ b/src/luarocks/index.lua
@@ -1,11 +1,10 @@
1 1
2--- Module which builds the index.html page to be used in rocks servers. 2--- Module which builds the index.html page to be used in rocks servers.
3local index = {} 3local index = {}
4package.loaded["luarocks.index"] = index
5 4
6local util = require("luarocks.util") 5local util = require("luarocks.util")
7local fs = require("luarocks.fs") 6local fs = require("luarocks.fs")
8local deps = require("luarocks.deps") 7local vers = require("luarocks.vers")
9local persist = require("luarocks.persist") 8local persist = require("luarocks.persist")
10local dir = require("luarocks.dir") 9local dir = require("luarocks.dir")
11local manif = require("luarocks.manif") 10local manif = require("luarocks.manif")
@@ -135,7 +134,7 @@ function index.make_index(repo)
135 for package, version_list in util.sortedpairs(manifest.repository) do 134 for package, version_list in util.sortedpairs(manifest.repository) do
136 local latest_rockspec = nil 135 local latest_rockspec = nil
137 local output = index_package_begin 136 local output = index_package_begin
138 for version, data in util.sortedpairs(version_list, deps.compare_versions) do 137 for version, data in util.sortedpairs(version_list, vers.compare_versions) do
139 local versions = {} 138 local versions = {}
140 output = output..version..':&nbsp;' 139 output = output..version..':&nbsp;'
141 table.sort(data, function(a,b) return a.arch < b.arch end) 140 table.sort(data, function(a,b) return a.arch < b.arch end)
diff --git a/src/luarocks/loader.lua b/src/luarocks/loader.lua
index 874bc10c..c8259689 100644
--- a/src/luarocks/loader.lua
+++ b/src/luarocks/loader.lua
@@ -6,21 +6,24 @@
6-- used to load previous modules, so that the loader chooses versions 6-- used to load previous modules, so that the loader chooses versions
7-- that are declared to be compatible with the ones loaded earlier. 7-- that are declared to be compatible with the ones loaded earlier.
8local loaders = package.loaders or package.searchers 8local loaders = package.loaders or package.searchers
9local package, require, ipairs, table, type, next, tostring, error = 9local require, ipairs, table, type, next, tostring, error =
10 package, require, ipairs, table, type, next, tostring, error 10 require, ipairs, table, type, next, tostring, error
11local unpack = unpack or table.unpack 11local unpack = unpack or table.unpack
12 12
13--module("luarocks.loader")
14local loader = {} 13local loader = {}
15package.loaded["luarocks.loader"] = loader
16 14
17local cfg = require("luarocks.cfg") 15local is_clean = not package.loaded["luarocks.core.cfg"]
16
17-- This loader module depends only on core modules.
18local cfg = require("luarocks.core.cfg")
18cfg.init_package_paths() 19cfg.init_package_paths()
19 20
20local path = require("luarocks.path") 21local path = require("luarocks.core.path")
21local manif_core = require("luarocks.manif_core") 22local manif = require("luarocks.core.manif")
22local deps = require("luarocks.deps") 23local vers = require("luarocks.core.vers")
23local util = require("luarocks.util") 24local util = require("luarocks.core.util")
25local require = nil
26--------------------------------------------------------------------------------
24 27
25-- Workaround for wrappers produced by older versions of LuaRocks 28-- Workaround for wrappers produced by older versions of LuaRocks
26local temporary_global = false 29local temporary_global = false
@@ -55,7 +58,7 @@ local function load_rocks_trees()
55 local any_ok = false 58 local any_ok = false
56 local trees = {} 59 local trees = {}
57 for _, tree in ipairs(cfg.rocks_trees) do 60 for _, tree in ipairs(cfg.rocks_trees) do
58 local manifest, err = manif_core.load_local_manifest(path.rocks_dir(tree)) 61 local manifest, err = manif.load_local_manifest(path.rocks_dir(tree))
59 if manifest then 62 if manifest then
60 any_ok = true 63 any_ok = true
61 table.insert(trees, {tree=tree, manifest=manifest}) 64 table.insert(trees, {tree=tree, manifest=manifest})
@@ -109,8 +112,8 @@ function loader.add_context(name, version)
109 for _, tree in ipairs(loader.rocks_trees) do 112 for _, tree in ipairs(loader.rocks_trees) do
110 local entries = tree.manifest.repository[pkg] 113 local entries = tree.manifest.repository[pkg]
111 if entries then 114 if entries then
112 for version, pkgs in util.sortedpairs(entries, deps.compare_versions) do 115 for ver, pkgs in util.sortedpairs(entries, vers.compare_versions) do
113 if (not constraints) or deps.match_constraints(deps.parse_version(version), constraints) then 116 if (not constraints) or vers.match_constraints(vers.parse_version(ver), constraints) then
114 loader.add_context(pkg, version) 117 loader.add_context(pkg, version)
115 end 118 end
116 end 119 end
@@ -142,7 +145,7 @@ end
142-- @return table or (nil, string): The module table as returned by some other loader, 145-- @return table or (nil, string): The module table as returned by some other loader,
143-- or nil followed by an error message if no other loader managed to load the module. 146-- or nil followed by an error message if no other loader managed to load the module.
144local function call_other_loaders(module, name, version, module_name) 147local function call_other_loaders(module, name, version, module_name)
145 for i, a_loader in ipairs(loaders) do 148 for _, a_loader in ipairs(loaders) do
146 if a_loader ~= loader.luarocks_loader then 149 if a_loader ~= loader.luarocks_loader then
147 local results = { a_loader(module_name) } 150 local results = { a_loader(module_name) }
148 if type(results[1]) == "function" then 151 if type(results[1]) == "function" then
@@ -187,7 +190,7 @@ local function select_module(module, filter_file_name)
187 if loader.context[name] == version then 190 if loader.context[name] == version then
188 return name, version, file_name 191 return name, version, file_name
189 end 192 end
190 version = deps.parse_version(version) 193 version = vers.parse_version(version)
191 table.insert(providers, {name = name, version = version, module_name = file_name, tree = tree}) 194 table.insert(providers, {name = name, version = version, module_name = file_name, tree = tree})
192 end 195 end
193 end 196 end
@@ -246,4 +249,12 @@ end
246 249
247table.insert(loaders, 1, loader.luarocks_loader) 250table.insert(loaders, 1, loader.luarocks_loader)
248 251
252if is_clean then
253 for modname, _ in pairs(package.loaded) do
254 if modname:match("^luarocks%.") then
255 package.loaded[modname] = nil
256 end
257 end
258end
259
249return loader 260return loader
diff --git a/src/luarocks/manif.lua b/src/luarocks/manif.lua
index 88a13f10..7f3085db 100644
--- a/src/luarocks/manif.lua
+++ b/src/luarocks/manif.lua
@@ -3,41 +3,18 @@
3-- They are loaded into manifest tables, which are then used for 3-- They are loaded into manifest tables, which are then used for
4-- performing searches, matching dependencies, etc. 4-- performing searches, matching dependencies, etc.
5local manif = {} 5local manif = {}
6package.loaded["luarocks.manif"] = manif 6setmetatable(manif, { __index = require("luarocks.core.manif") })
7 7
8local manif_core = require("luarocks.manif_core")
9local persist = require("luarocks.persist") 8local persist = require("luarocks.persist")
10local fetch = require("luarocks.fetch") 9local fetch = require("luarocks.fetch")
11local dir = require("luarocks.dir") 10local dir = require("luarocks.dir")
12local fs = require("luarocks.fs") 11local fs = require("luarocks.fs")
13local search = require("luarocks.search") 12local cfg = require("luarocks.core.cfg")
14local util = require("luarocks.util")
15local cfg = require("luarocks.cfg")
16local path = require("luarocks.path") 13local path = require("luarocks.path")
17local repos = require("luarocks.repos") 14local util = require("luarocks.util")
18local deps = require("luarocks.deps")
19 15
20manif.rock_manifest_cache = {} 16manif.rock_manifest_cache = {}
21 17
22--- Commit a table to disk in given local path.
23-- @param where string: The directory where the table should be saved.
24-- @param name string: The filename.
25-- @param tbl table: The table to be saved.
26-- @return boolean or (nil, string): true if successful, or nil and a
27-- message in case of errors.
28local function save_table(where, name, tbl)
29 assert(type(where) == "string")
30 assert(type(name) == "string")
31 assert(type(tbl) == "table")
32
33 local filename = dir.path(where, name)
34 local ok, err = persist.save_from_table(filename..".tmp", tbl)
35 if ok then
36 ok, err = fs.replace_file(filename, filename..".tmp")
37 end
38 return ok, err
39end
40
41function manif.load_rock_manifest(name, version, root) 18function manif.load_rock_manifest(name, version, root)
42 assert(type(name) == "string") 19 assert(type(name) == "string")
43 assert(type(version) == "string") 20 assert(type(version) == "string")
@@ -48,41 +25,13 @@ function manif.load_rock_manifest(name, version, root)
48 end 25 end
49 local pathname = path.rock_manifest_file(name, version, root) 26 local pathname = path.rock_manifest_file(name, version, root)
50 local rock_manifest = persist.load_into_table(pathname) 27 local rock_manifest = persist.load_into_table(pathname)
51 if not rock_manifest then return nil end 28 if not rock_manifest then
29 return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks tree?"
30 end
52 manif.rock_manifest_cache[name_version] = rock_manifest 31 manif.rock_manifest_cache[name_version] = rock_manifest
53 return rock_manifest.rock_manifest 32 return rock_manifest.rock_manifest
54end 33end
55 34
56function manif.make_rock_manifest(name, version)
57 local install_dir = path.install_dir(name, version)
58 local tree = {}
59 for _, file in ipairs(fs.find(install_dir)) do
60 local full_path = dir.path(install_dir, file)
61 local walk = tree
62 local last
63 local last_name
64 for name in file:gmatch("[^/]+") do
65 local next = walk[name]
66 if not next then
67 next = {}
68 walk[name] = next
69 end
70 last = walk
71 last_name = name
72 walk = next
73 end
74 if fs.is_file(full_path) then
75 local sum, err = fs.get_md5(full_path)
76 if not sum then
77 return nil, "Failed producing checksum: "..tostring(err)
78 end
79 last[last_name] = sum
80 end
81 end
82 local rock_manifest = { rock_manifest=tree }
83 manif.rock_manifest_cache[name.."/"..version] = rock_manifest
84 save_table(install_dir, "rock_manifest", rock_manifest )
85end
86 35
87local function fetch_manifest_from(repo_url, filename) 36local function fetch_manifest_from(repo_url, filename)
88 local url = dir.path(repo_url, filename) 37 local url = dir.path(repo_url, filename)
@@ -111,7 +60,7 @@ function manif.load_manifest(repo_url, lua_version)
111 assert(type(lua_version) == "string" or not lua_version) 60 assert(type(lua_version) == "string" or not lua_version)
112 lua_version = lua_version or cfg.lua_version 61 lua_version = lua_version or cfg.lua_version
113 62
114 local cached_manifest = manif_core.get_cached_manifest(repo_url, lua_version) 63 local cached_manifest = manif.get_cached_manifest(repo_url, lua_version)
115 if cached_manifest then 64 if cached_manifest then
116 return cached_manifest 65 return cached_manifest
117 end 66 end
@@ -158,386 +107,7 @@ function manif.load_manifest(repo_url, lua_version)
158 end 107 end
159 pathname = nozip 108 pathname = nozip
160 end 109 end
161 return manif_core.manifest_loader(pathname, repo_url, lua_version) 110 return manif.manifest_loader(pathname, repo_url, lua_version)
162end
163
164--- Update storage table to account for items provided by a package.
165-- @param storage table: a table storing items in the following format:
166-- keys are item names and values are arrays of packages providing each item,
167-- where a package is specified as string `name/version`.
168-- @param items table: a table mapping item names to paths.
169-- @param name string: package name.
170-- @param version string: package version.
171local function store_package_items(storage, name, version, items)
172 assert(type(storage) == "table")
173 assert(type(items) == "table")
174 assert(type(name) == "string")
175 assert(type(version) == "string")
176
177 local package_identifier = name.."/"..version
178
179 for item_name, path in pairs(items) do
180 if not storage[item_name] then
181 storage[item_name] = {}
182 end
183
184 table.insert(storage[item_name], package_identifier)
185 end
186end
187
188--- Update storage table removing items provided by a package.
189-- @param storage table: a table storing items in the following format:
190-- keys are item names and values are arrays of packages providing each item,
191-- where a package is specified as string `name/version`.
192-- @param items table: a table mapping item names to paths.
193-- @param name string: package name.
194-- @param version string: package version.
195local function remove_package_items(storage, name, version, items)
196 assert(type(storage) == "table")
197 assert(type(items) == "table")
198 assert(type(name) == "string")
199 assert(type(version) == "string")
200
201 local package_identifier = name.."/"..version
202
203 for item_name, path in pairs(items) do
204 local all_identifiers = storage[item_name]
205
206 for i, identifier in ipairs(all_identifiers) do
207 if identifier == package_identifier then
208 table.remove(all_identifiers, i)
209 break
210 end
211 end
212
213 if #all_identifiers == 0 then
214 storage[item_name] = nil
215 end
216 end
217end
218
219--- Sort function for ordering rock identifiers in a manifest's
220-- modules table. Rocks are ordered alphabetically by name, and then
221-- by version which greater first.
222-- @param a string: Version to compare.
223-- @param b string: Version to compare.
224-- @return boolean: The comparison result, according to the
225-- rule outlined above.
226local function sort_pkgs(a, b)
227 assert(type(a) == "string")
228 assert(type(b) == "string")
229
230 local na, va = a:match("(.*)/(.*)$")
231 local nb, vb = b:match("(.*)/(.*)$")
232
233 return (na == nb) and deps.compare_versions(va, vb) or na < nb
234end
235
236--- Sort items of a package matching table by version number (higher versions first).
237-- @param tbl table: the package matching table: keys should be strings
238-- and values arrays of strings with packages names in "name/version" format.
239local function sort_package_matching_table(tbl)
240 assert(type(tbl) == "table")
241
242 if next(tbl) then
243 for item, pkgs in pairs(tbl) do
244 if #pkgs > 1 then
245 table.sort(pkgs, sort_pkgs)
246 -- Remove duplicates from the sorted array.
247 local prev = nil
248 local i = 1
249 while pkgs[i] do
250 local curr = pkgs[i]
251 if curr == prev then
252 table.remove(pkgs, i)
253 else
254 prev = curr
255 i = i + 1
256 end
257 end
258 end
259 end
260 end
261end
262
263--- Process the dependencies of a manifest table to determine its dependency
264-- chains for loading modules. The manifest dependencies information is filled
265-- and any dependency inconsistencies or missing dependencies are reported to
266-- standard error.
267-- @param manifest table: a manifest table.
268-- @param deps_mode string: Dependency mode: "one" for the current default tree,
269-- "all" for all trees, "order" for all trees with priority >= the current default,
270-- "none" for no trees.
271local function update_dependencies(manifest, deps_mode)
272 assert(type(manifest) == "table")
273 assert(type(deps_mode) == "string")
274
275 for pkg, versions in pairs(manifest.repository) do
276 for version, repositories in pairs(versions) do
277 for _, repo in ipairs(repositories) do
278 if repo.arch == "installed" then
279 repo.dependencies = {}
280 deps.scan_deps(repo.dependencies, manifest, pkg, version, deps_mode)
281 repo.dependencies[pkg] = nil
282 end
283 end
284 end
285 end
286end
287
288--- Filter manifest table by Lua version, removing rockspecs whose Lua version
289-- does not match.
290-- @param manifest table: a manifest table.
291-- @param lua_version string or nil: filter by Lua version
292-- @param repodir string: directory of repository being scanned
293-- @param cache table: temporary rockspec cache table
294local function filter_by_lua_version(manifest, lua_version, repodir, cache)
295 assert(type(manifest) == "table")
296 assert(type(repodir) == "string")
297 assert((not cache) or type(cache) == "table")
298
299 cache = cache or {}
300 lua_version = deps.parse_version(lua_version)
301 for pkg, versions in pairs(manifest.repository) do
302 local to_remove = {}
303 for version, repositories in pairs(versions) do
304 for _, repo in ipairs(repositories) do
305 if repo.arch == "rockspec" then
306 local pathname = dir.path(repodir, pkg.."-"..version..".rockspec")
307 local rockspec, err = cache[pathname]
308 if not rockspec then
309 rockspec, err = fetch.load_local_rockspec(pathname, true)
310 end
311 if rockspec then
312 cache[pathname] = rockspec
313 for _, dep in ipairs(rockspec.dependencies) do
314 if dep.name == "lua" then
315 if not deps.match_constraints(lua_version, dep.constraints) then
316 table.insert(to_remove, version)
317 end
318 break
319 end
320 end
321 else
322 util.printerr("Error loading rockspec for "..pkg.." "..version..": "..err)
323 end
324 end
325 end
326 end
327 if next(to_remove) then
328 for _, incompat in ipairs(to_remove) do
329 versions[incompat] = nil
330 end
331 if not next(versions) then
332 manifest.repository[pkg] = nil
333 end
334 end
335 end
336end
337
338--- Store search results in a manifest table.
339-- @param results table: The search results as returned by search.disk_search.
340-- @param manifest table: A manifest table (must contain repository, modules, commands tables).
341-- It will be altered to include the search results.
342-- @return boolean or (nil, string): true in case of success, or nil followed by an error message.
343local function store_results(results, manifest)
344 assert(type(results) == "table")
345 assert(type(manifest) == "table")
346
347 for name, versions in pairs(results) do
348 local pkgtable = manifest.repository[name] or {}
349 for version, entries in pairs(versions) do
350 local versiontable = {}
351 for _, entry in ipairs(entries) do
352 local entrytable = {}
353 entrytable.arch = entry.arch
354 if entry.arch == "installed" then
355 local rock_manifest = manif.load_rock_manifest(name, version)
356 if not rock_manifest then
357 return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?"
358 end
359
360 entrytable.modules = repos.package_modules(name, version)
361 store_package_items(manifest.modules, name, version, entrytable.modules)
362 entrytable.commands = repos.package_commands(name, version)
363 store_package_items(manifest.commands, name, version, entrytable.commands)
364 end
365 table.insert(versiontable, entrytable)
366 end
367 pkgtable[version] = versiontable
368 end
369 manifest.repository[name] = pkgtable
370 end
371 sort_package_matching_table(manifest.modules)
372 sort_package_matching_table(manifest.commands)
373 return true
374end
375
376--- Scan a LuaRocks repository and output a manifest file.
377-- A file called 'manifest' will be written in the root of the given
378-- repository directory.
379-- @param repo A local repository directory.
380-- @param deps_mode string: Dependency mode: "one" for the current default tree,
381-- "all" for all trees, "order" for all trees with priority >= the current default,
382-- "none" for the default dependency mode from the configuration.
383-- @param remote boolean: 'true' if making a manifest for a rocks server.
384-- @return boolean or (nil, string): True if manifest was generated,
385-- or nil and an error message.
386function manif.make_manifest(repo, deps_mode, remote)
387 assert(type(repo) == "string")
388 assert(type(deps_mode) == "string")
389
390 if deps_mode == "none" then deps_mode = cfg.deps_mode end
391
392 if not fs.is_dir(repo) then
393 return nil, "Cannot access repository at "..repo
394 end
395
396 local query = search.make_query("")
397 query.exact_name = false
398 query.arch = "any"
399 local results = search.disk_search(repo, query)
400 local manifest = { repository = {}, modules = {}, commands = {} }
401
402 manif_core.cache_manifest(repo, nil, manifest)
403
404 local ok, err = store_results(results, manifest)
405 if not ok then return nil, err end
406
407 if remote then
408 local cache = {}
409 for luaver in util.lua_versions() do
410 local vmanifest = { repository = {}, modules = {}, commands = {} }
411 local ok, err = store_results(results, vmanifest)
412 filter_by_lua_version(vmanifest, luaver, repo, cache)
413 save_table(repo, "manifest-"..luaver, vmanifest)
414 end
415 else
416 update_dependencies(manifest, deps_mode)
417 end
418
419 return save_table(repo, "manifest", manifest)
420end
421
422--- Update manifest file for a local repository
423-- adding information about a version of a package installed in that repository.
424-- @param name string: Name of a package from the repository.
425-- @param version string: Version of a package from the repository.
426-- @param repo string or nil: Pathname of a local repository. If not given,
427-- the default local repository is used.
428-- @param deps_mode string: Dependency mode: "one" for the current default tree,
429-- "all" for all trees, "order" for all trees with priority >= the current default,
430-- "none" for using the default dependency mode from the configuration.
431-- @return boolean or (nil, string): True if manifest was updated successfully,
432-- or nil and an error message.
433function manif.add_to_manifest(name, version, repo, deps_mode)
434 assert(type(name) == "string")
435 assert(type(version) == "string")
436 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
437 assert(type(deps_mode) == "string")
438
439 if deps_mode == "none" then deps_mode = cfg.deps_mode end
440
441 local manifest, err = manif_core.load_local_manifest(rocks_dir)
442 if not manifest then
443 util.printerr("No existing manifest. Attempting to rebuild...")
444 -- Manifest built by `manif.make_manifest` should already
445 -- include information about given name and version,
446 -- no need to update it.
447 return manif.make_manifest(rocks_dir, deps_mode)
448 end
449
450 local results = {[name] = {[version] = {{arch = "installed", repo = rocks_dir}}}}
451
452 local ok, err = store_results(results, manifest)
453 if not ok then return nil, err end
454
455 update_dependencies(manifest, deps_mode)
456 return save_table(rocks_dir, "manifest", manifest)
457end
458
459--- Update manifest file for a local repository
460-- removing information about a version of a package.
461-- @param name string: Name of a package removed from the repository.
462-- @param version string: Version of a package removed from the repository.
463-- @param repo string or nil: Pathname of a local repository. If not given,
464-- the default local repository is used.
465-- @param deps_mode string: Dependency mode: "one" for the current default tree,
466-- "all" for all trees, "order" for all trees with priority >= the current default,
467-- "none" for using the default dependency mode from the configuration.
468-- @return boolean or (nil, string): True if manifest was updated successfully,
469-- or nil and an error message.
470function manif.remove_from_manifest(name, version, repo, deps_mode)
471 assert(type(name) == "string")
472 assert(type(version) == "string")
473 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
474 assert(type(deps_mode) == "string")
475
476 if deps_mode == "none" then deps_mode = cfg.deps_mode end
477
478 local manifest, err = manif_core.load_local_manifest(rocks_dir)
479 if not manifest then
480 util.printerr("No existing manifest. Attempting to rebuild...")
481 -- Manifest built by `manif.make_manifest` should already
482 -- include up-to-date information, no need to update it.
483 return manif.make_manifest(rocks_dir, deps_mode)
484 end
485
486 local package_entry = manifest.repository[name]
487
488 local version_entry = package_entry[version][1]
489 remove_package_items(manifest.modules, name, version, version_entry.modules)
490 remove_package_items(manifest.commands, name, version, version_entry.commands)
491
492 package_entry[version] = nil
493 manifest.dependencies[name][version] = nil
494
495 if not next(package_entry) then
496 -- No more versions of this package.
497 manifest.repository[name] = nil
498 manifest.dependencies[name] = nil
499 end
500
501 update_dependencies(manifest, deps_mode)
502 return save_table(rocks_dir, "manifest", manifest)
503end
504
505--- Report missing dependencies for all rocks installed in a repository.
506-- @param repo string or nil: Pathname of a local repository. If not given,
507-- the default local repository is used.
508-- @param deps_mode string: Dependency mode: "one" for the current default tree,
509-- "all" for all trees, "order" for all trees with priority >= the current default,
510-- "none" for using the default dependency mode from the configuration.
511function manif.check_dependencies(repo, deps_mode)
512 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
513 assert(type(deps_mode) == "string")
514 if deps_mode == "none" then deps_mode = cfg.deps_mode end
515
516 local manifest = manif_core.load_local_manifest(rocks_dir)
517 if not manifest then
518 return
519 end
520
521 for name, versions in util.sortedpairs(manifest.repository) do
522 for version, version_entries in util.sortedpairs(versions, deps.compare_versions) do
523 for _, entry in ipairs(version_entries) do
524 if entry.arch == "installed" then
525 if manifest.dependencies[name] and manifest.dependencies[name][version] then
526 deps.report_missing_dependencies(name, version, manifest.dependencies[name][version], deps_mode)
527 end
528 end
529 end
530 end
531 end
532end
533
534function manif.zip_manifests()
535 for ver in util.lua_versions() do
536 local file = "manifest-"..ver
537 local zip = file..".zip"
538 fs.delete(dir.path(fs.current_dir(), zip))
539 fs.zip(zip, file)
540 end
541end 111end
542 112
543--- Get type and name of an item (a module or a command) provided by a file. 113--- Get type and name of an item (a module or a command) provided by a file.
@@ -556,7 +126,7 @@ local function get_providers(item_type, item_name, repo)
556 assert(type(item_type) == "string") 126 assert(type(item_type) == "string")
557 assert(type(item_name) == "string") 127 assert(type(item_name) == "string")
558 local rocks_dir = path.rocks_dir(repo or cfg.root_dir) 128 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
559 local manifest = manif_core.load_local_manifest(rocks_dir) 129 local manifest = manif.load_local_manifest(rocks_dir)
560 return manifest and manifest[item_type .. "s"][item_name] 130 return manifest and manifest[item_type .. "s"][item_name]
561end 131end
562 132
@@ -592,7 +162,7 @@ end
592-- and path to the providing file relatively to that subtree. 162-- and path to the providing file relatively to that subtree.
593function manif.get_providing_file(name, version, item_type, item_name, repo) 163function manif.get_providing_file(name, version, item_type, item_name, repo)
594 local rocks_dir = path.rocks_dir(repo or cfg.root_dir) 164 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
595 local manifest = manif_core.load_local_manifest(rocks_dir) 165 local manifest = manif.load_local_manifest(rocks_dir)
596 166
597 local entry_table = manifest.repository[name][version][1] 167 local entry_table = manifest.repository[name][version][1]
598 local file_path = entry_table[item_type .. "s"][item_name] 168 local file_path = entry_table[item_type .. "s"][item_name]
@@ -625,4 +195,30 @@ function manif.get_providing_file(name, version, item_type, item_name, repo)
625 return type(subtree) == "string" and "lib" or "lua", file_path 195 return type(subtree) == "string" and "lib" or "lua", file_path
626end 196end
627 197
198--- Get all versions of a package listed in a manifest file.
199-- @param name string: a package name.
200-- @param deps_mode string: "one", to use only the currently
201-- configured tree; "order" to select trees based on order
202-- (use the current tree and all trees below it on the list)
203-- or "all", to use all trees.
204-- @return table: An array of strings listing installed
205-- versions of a package.
206function manif.get_versions(name, deps_mode)
207 assert(type(name) == "string")
208 assert(type(deps_mode) == "string")
209
210 local version_set = {}
211 path.map_trees(deps_mode, function(tree)
212 local manifest = manif.load_local_manifest(path.rocks_dir(tree))
213
214 if manifest and manifest.repository[name] then
215 for version in pairs(manifest.repository[name]) do
216 version_set[version] = true
217 end
218 end
219 end)
220
221 return util.keys(version_set)
222end
223
628return manif 224return manif
diff --git a/src/luarocks/manif/writer.lua b/src/luarocks/manif/writer.lua
new file mode 100644
index 00000000..2d17d328
--- /dev/null
+++ b/src/luarocks/manif/writer.lua
@@ -0,0 +1,437 @@
1
2local writer = {}
3
4local cfg = require("luarocks.core.cfg")
5local search = require("luarocks.search")
6local repos = require("luarocks.repos")
7local deps = require("luarocks.deps")
8local vers = require("luarocks.vers")
9local fs = require("luarocks.fs")
10local util = require("luarocks.util")
11local dir = require("luarocks.dir")
12local fetch = require("luarocks.fetch")
13local path = require("luarocks.path")
14local persist = require("luarocks.persist")
15local manif = require("luarocks.manif")
16
17--- Update storage table to account for items provided by a package.
18-- @param storage table: a table storing items in the following format:
19-- keys are item names and values are arrays of packages providing each item,
20-- where a package is specified as string `name/version`.
21-- @param items table: a table mapping item names to paths.
22-- @param name string: package name.
23-- @param version string: package version.
24local function store_package_items(storage, name, version, items)
25 assert(type(storage) == "table")
26 assert(type(items) == "table")
27 assert(type(name) == "string")
28 assert(type(version) == "string")
29
30 local package_identifier = name.."/"..version
31
32 for item_name, path in pairs(items) do
33 if not storage[item_name] then
34 storage[item_name] = {}
35 end
36
37 table.insert(storage[item_name], package_identifier)
38 end
39end
40
41--- Update storage table removing items provided by a package.
42-- @param storage table: a table storing items in the following format:
43-- keys are item names and values are arrays of packages providing each item,
44-- where a package is specified as string `name/version`.
45-- @param items table: a table mapping item names to paths.
46-- @param name string: package name.
47-- @param version string: package version.
48local function remove_package_items(storage, name, version, items)
49 assert(type(storage) == "table")
50 assert(type(items) == "table")
51 assert(type(name) == "string")
52 assert(type(version) == "string")
53
54 local package_identifier = name.."/"..version
55
56 for item_name, path in pairs(items) do
57 local all_identifiers = storage[item_name]
58
59 for i, identifier in ipairs(all_identifiers) do
60 if identifier == package_identifier then
61 table.remove(all_identifiers, i)
62 break
63 end
64 end
65
66 if #all_identifiers == 0 then
67 storage[item_name] = nil
68 end
69 end
70end
71
72--- Process the dependencies of a manifest table to determine its dependency
73-- chains for loading modules. The manifest dependencies information is filled
74-- and any dependency inconsistencies or missing dependencies are reported to
75-- standard error.
76-- @param manifest table: a manifest table.
77-- @param deps_mode string: Dependency mode: "one" for the current default tree,
78-- "all" for all trees, "order" for all trees with priority >= the current default,
79-- "none" for no trees.
80local function update_dependencies(manifest, deps_mode)
81 assert(type(manifest) == "table")
82 assert(type(deps_mode) == "string")
83
84 for pkg, versions in pairs(manifest.repository) do
85 for version, repositories in pairs(versions) do
86 for _, repo in ipairs(repositories) do
87 if repo.arch == "installed" then
88 repo.dependencies = {}
89 deps.scan_deps(repo.dependencies, manifest, pkg, version, deps_mode)
90 repo.dependencies[pkg] = nil
91 end
92 end
93 end
94 end
95end
96
97
98
99--- Sort function for ordering rock identifiers in a manifest's
100-- modules table. Rocks are ordered alphabetically by name, and then
101-- by version which greater first.
102-- @param a string: Version to compare.
103-- @param b string: Version to compare.
104-- @return boolean: The comparison result, according to the
105-- rule outlined above.
106local function sort_pkgs(a, b)
107 assert(type(a) == "string")
108 assert(type(b) == "string")
109
110 local na, va = a:match("(.*)/(.*)$")
111 local nb, vb = b:match("(.*)/(.*)$")
112
113 return (na == nb) and vers.compare_versions(va, vb) or na < nb
114end
115
116--- Sort items of a package matching table by version number (higher versions first).
117-- @param tbl table: the package matching table: keys should be strings
118-- and values arrays of strings with packages names in "name/version" format.
119local function sort_package_matching_table(tbl)
120 assert(type(tbl) == "table")
121
122 if next(tbl) then
123 for item, pkgs in pairs(tbl) do
124 if #pkgs > 1 then
125 table.sort(pkgs, sort_pkgs)
126 -- Remove duplicates from the sorted array.
127 local prev = nil
128 local i = 1
129 while pkgs[i] do
130 local curr = pkgs[i]
131 if curr == prev then
132 table.remove(pkgs, i)
133 else
134 prev = curr
135 i = i + 1
136 end
137 end
138 end
139 end
140 end
141end
142
143--- Filter manifest table by Lua version, removing rockspecs whose Lua version
144-- does not match.
145-- @param manifest table: a manifest table.
146-- @param lua_version string or nil: filter by Lua version
147-- @param repodir string: directory of repository being scanned
148-- @param cache table: temporary rockspec cache table
149local function filter_by_lua_version(manifest, lua_version, repodir, cache)
150 assert(type(manifest) == "table")
151 assert(type(repodir) == "string")
152 assert((not cache) or type(cache) == "table")
153
154 cache = cache or {}
155 lua_version = vers.parse_version(lua_version)
156 for pkg, versions in pairs(manifest.repository) do
157 local to_remove = {}
158 for version, repositories in pairs(versions) do
159 for _, repo in ipairs(repositories) do
160 if repo.arch == "rockspec" then
161 local pathname = dir.path(repodir, pkg.."-"..version..".rockspec")
162 local rockspec, err = cache[pathname]
163 if not rockspec then
164 rockspec, err = fetch.load_local_rockspec(pathname, true)
165 end
166 if rockspec then
167 cache[pathname] = rockspec
168 for _, dep in ipairs(rockspec.dependencies) do
169 if dep.name == "lua" then
170 if not vers.match_constraints(lua_version, dep.constraints) then
171 table.insert(to_remove, version)
172 end
173 break
174 end
175 end
176 else
177 util.printerr("Error loading rockspec for "..pkg.." "..version..": "..err)
178 end
179 end
180 end
181 end
182 if next(to_remove) then
183 for _, incompat in ipairs(to_remove) do
184 versions[incompat] = nil
185 end
186 if not next(versions) then
187 manifest.repository[pkg] = nil
188 end
189 end
190 end
191end
192
193--- Store search results in a manifest table.
194-- @param results table: The search results as returned by search.disk_search.
195-- @param manifest table: A manifest table (must contain repository, modules, commands tables).
196-- It will be altered to include the search results.
197-- @return boolean or (nil, string): true in case of success, or nil followed by an error message.
198local function store_results(results, manifest)
199 assert(type(results) == "table")
200 assert(type(manifest) == "table")
201
202 for name, versions in pairs(results) do
203 local pkgtable = manifest.repository[name] or {}
204 for version, entries in pairs(versions) do
205 local versiontable = {}
206 for _, entry in ipairs(entries) do
207 local entrytable = {}
208 entrytable.arch = entry.arch
209 if entry.arch == "installed" then
210 local rock_manifest, err = manif.load_rock_manifest(name, version)
211 if not rock_manifest then return nil, err end
212
213 entrytable.modules = repos.package_modules(name, version)
214 store_package_items(manifest.modules, name, version, entrytable.modules)
215 entrytable.commands = repos.package_commands(name, version)
216 store_package_items(manifest.commands, name, version, entrytable.commands)
217 end
218 table.insert(versiontable, entrytable)
219 end
220 pkgtable[version] = versiontable
221 end
222 manifest.repository[name] = pkgtable
223 end
224 sort_package_matching_table(manifest.modules)
225 sort_package_matching_table(manifest.commands)
226 return true
227end
228
229--- Commit a table to disk in given local path.
230-- @param where string: The directory where the table should be saved.
231-- @param name string: The filename.
232-- @param tbl table: The table to be saved.
233-- @return boolean or (nil, string): true if successful, or nil and a
234-- message in case of errors.
235local function save_table(where, name, tbl)
236 assert(type(where) == "string")
237 assert(type(name) == "string")
238 assert(type(tbl) == "table")
239
240 local filename = dir.path(where, name)
241 local ok, err = persist.save_from_table(filename..".tmp", tbl)
242 if ok then
243 ok, err = fs.replace_file(filename, filename..".tmp")
244 end
245 return ok, err
246end
247
248function writer.make_rock_manifest(name, version)
249 local install_dir = path.install_dir(name, version)
250 local tree = {}
251 for _, file in ipairs(fs.find(install_dir)) do
252 local full_path = dir.path(install_dir, file)
253 local walk = tree
254 local last
255 local last_name
256 for filename in file:gmatch("[^/]+") do
257 local next = walk[filename]
258 if not next then
259 next = {}
260 walk[filename] = next
261 end
262 last = walk
263 last_name = filename
264 walk = next
265 end
266 if fs.is_file(full_path) then
267 local sum, err = fs.get_md5(full_path)
268 if not sum then
269 return nil, "Failed producing checksum: "..tostring(err)
270 end
271 last[last_name] = sum
272 end
273 end
274 local rock_manifest = { rock_manifest=tree }
275 manif.rock_manifest_cache[name.."/"..version] = rock_manifest
276 save_table(install_dir, "rock_manifest", rock_manifest )
277end
278
279--- Scan a LuaRocks repository and output a manifest file.
280-- A file called 'manifest' will be written in the root of the given
281-- repository directory.
282-- @param repo A local repository directory.
283-- @param deps_mode string: Dependency mode: "one" for the current default tree,
284-- "all" for all trees, "order" for all trees with priority >= the current default,
285-- "none" for the default dependency mode from the configuration.
286-- @param remote boolean: 'true' if making a manifest for a rocks server.
287-- @return boolean or (nil, string): True if manifest was generated,
288-- or nil and an error message.
289function writer.make_manifest(repo, deps_mode, remote)
290 assert(type(repo) == "string")
291 assert(type(deps_mode) == "string")
292
293 if deps_mode == "none" then deps_mode = cfg.deps_mode end
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 local manifest = { repository = {}, modules = {}, commands = {} }
304
305 manif.cache_manifest(repo, nil, manifest)
306
307 local ok, err = store_results(results, manifest)
308 if not ok then return nil, err end
309
310 if remote then
311 local cache = {}
312 for luaver in util.lua_versions() do
313 local vmanifest = { repository = {}, modules = {}, commands = {} }
314 local ok, err = store_results(results, vmanifest)
315 filter_by_lua_version(vmanifest, luaver, repo, cache)
316 save_table(repo, "manifest-"..luaver, vmanifest)
317 end
318 else
319 update_dependencies(manifest, deps_mode)
320 end
321
322 return save_table(repo, "manifest", manifest)
323end
324
325--- Update manifest file for a local repository
326-- adding information about a version of a package installed in that repository.
327-- @param name string: Name of a package from the repository.
328-- @param version string: Version of a package from the repository.
329-- @param repo string or nil: Pathname of a local repository. If not given,
330-- the default local repository is used.
331-- @param deps_mode string: Dependency mode: "one" for the current default tree,
332-- "all" for all trees, "order" for all trees with priority >= the current default,
333-- "none" for using the default dependency mode from the configuration.
334-- @return boolean or (nil, string): True if manifest was updated successfully,
335-- or nil and an error message.
336function writer.add_to_manifest(name, version, repo, deps_mode)
337 assert(type(name) == "string")
338 assert(type(version) == "string")
339 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
340 assert(type(deps_mode) == "string")
341
342 if deps_mode == "none" then deps_mode = cfg.deps_mode end
343
344 local manifest, err = manif.load_local_manifest(rocks_dir)
345 if not manifest then
346 util.printerr("No existing manifest. Attempting to rebuild...")
347 -- Manifest built by `writer.make_manifest` should already
348 -- include information about given name and version,
349 -- no need to update it.
350 return writer.make_manifest(rocks_dir, deps_mode)
351 end
352
353 local results = {[name] = {[version] = {{arch = "installed", repo = rocks_dir}}}}
354
355 local ok, err = store_results(results, manifest)
356 if not ok then return nil, err end
357
358 update_dependencies(manifest, deps_mode)
359 return save_table(rocks_dir, "manifest", manifest)
360end
361
362--- Update manifest file for a local repository
363-- removing information about a version of a package.
364-- @param name string: Name of a package removed from the repository.
365-- @param version string: Version of a package removed from the repository.
366-- @param repo string or nil: Pathname of a local repository. If not given,
367-- the default local repository is used.
368-- @param deps_mode string: Dependency mode: "one" for the current default tree,
369-- "all" for all trees, "order" for all trees with priority >= the current default,
370-- "none" for using the default dependency mode from the configuration.
371-- @return boolean or (nil, string): True if manifest was updated successfully,
372-- or nil and an error message.
373function writer.remove_from_manifest(name, version, repo, deps_mode)
374 assert(type(name) == "string")
375 assert(type(version) == "string")
376 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
377 assert(type(deps_mode) == "string")
378
379 if deps_mode == "none" then deps_mode = cfg.deps_mode end
380
381 local manifest, err = manif.load_local_manifest(rocks_dir)
382 if not manifest then
383 util.printerr("No existing manifest. Attempting to rebuild...")
384 -- Manifest built by `writer.make_manifest` should already
385 -- include up-to-date information, no need to update it.
386 return writer.make_manifest(rocks_dir, deps_mode)
387 end
388
389 local package_entry = manifest.repository[name]
390
391 local version_entry = package_entry[version][1]
392 remove_package_items(manifest.modules, name, version, version_entry.modules)
393 remove_package_items(manifest.commands, name, version, version_entry.commands)
394
395 package_entry[version] = nil
396 manifest.dependencies[name][version] = nil
397
398 if not next(package_entry) then
399 -- No more versions of this package.
400 manifest.repository[name] = nil
401 manifest.dependencies[name] = nil
402 end
403
404 update_dependencies(manifest, deps_mode)
405 return save_table(rocks_dir, "manifest", manifest)
406end
407
408--- Report missing dependencies for all rocks installed in a repository.
409-- @param repo string or nil: Pathname of a local repository. If not given,
410-- the default local repository is used.
411-- @param deps_mode string: Dependency mode: "one" for the current default tree,
412-- "all" for all trees, "order" for all trees with priority >= the current default,
413-- "none" for using the default dependency mode from the configuration.
414function writer.check_dependencies(repo, deps_mode)
415 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
416 assert(type(deps_mode) == "string")
417 if deps_mode == "none" then deps_mode = cfg.deps_mode end
418
419 local manifest = manif.load_local_manifest(rocks_dir)
420 if not manifest then
421 return
422 end
423
424 for name, versions in util.sortedpairs(manifest.repository) do
425 for version, version_entries in util.sortedpairs(versions, vers.compare_versions) do
426 for _, entry in ipairs(version_entries) do
427 if entry.arch == "installed" then
428 if manifest.dependencies[name] and manifest.dependencies[name][version] then
429 deps.report_missing_dependencies(name, version, manifest.dependencies[name][version], deps_mode, cfg.rocks_provided_3_0)
430 end
431 end
432 end
433 end
434 end
435end
436
437return writer
diff --git a/src/luarocks/pack.lua b/src/luarocks/pack.lua
index 35bbe838..b721a209 100644
--- a/src/luarocks/pack.lua
+++ b/src/luarocks/pack.lua
@@ -1,8 +1,6 @@
1 1
2--- Module implementing the LuaRocks "pack" command. 2-- Create rock files, packing sources or binaries.
3-- Creates a rock, packing sources or binaries.
4local pack = {} 3local pack = {}
5package.loaded["luarocks.pack"] = pack
6 4
7local unpack = unpack or table.unpack 5local unpack = unpack or table.unpack
8 6
@@ -10,22 +8,12 @@ local path = require("luarocks.path")
10local repos = require("luarocks.repos") 8local repos = require("luarocks.repos")
11local fetch = require("luarocks.fetch") 9local fetch = require("luarocks.fetch")
12local fs = require("luarocks.fs") 10local fs = require("luarocks.fs")
13local cfg = require("luarocks.cfg") 11local cfg = require("luarocks.core.cfg")
14local util = require("luarocks.util") 12local util = require("luarocks.util")
15local dir = require("luarocks.dir") 13local dir = require("luarocks.dir")
16local manif = require("luarocks.manif") 14local manif = require("luarocks.manif")
17local search = require("luarocks.search") 15local search = require("luarocks.search")
18 16
19util.add_run_function(pack)
20pack.help_summary = "Create a rock, packing sources or binaries."
21pack.help_arguments = "{<rockspec>|<name> [<version>]}"
22pack.help = [[
23Argument may be a rockspec file, for creating a source rock,
24or the name of an installed package, for creating a binary rock.
25In the latter case, the app version may be given as a second
26argument.
27]]
28
29--- Create a source rock. 17--- Create a source rock.
30-- Packages a rockspec and its required source files in a rock 18-- Packages a rockspec and its required source files in a rock
31-- file with the .src.rock extension, which can later be built and 19-- file with the .src.rock extension, which can later be built and
@@ -88,7 +76,7 @@ end
88-- @param tree string or nil: An optional tree to pick the package from. 76-- @param tree string or nil: An optional tree to pick the package from.
89-- @return string or (nil, string): The filename of the resulting 77-- @return string or (nil, string): The filename of the resulting
90-- .src.rock file; or nil and an error message. 78-- .src.rock file; or nil and an error message.
91local function do_pack_binary_rock(name, version, tree) 79function pack.pack_installed_rock(name, version, tree)
92 assert(type(name) == "string") 80 assert(type(name) == "string")
93 assert(type(version) == "string" or not version) 81 assert(type(version) == "string" or not version)
94 82
@@ -104,10 +92,8 @@ local function do_pack_binary_rock(name, version, tree)
104 return nil, "'"..name.." "..version.."' does not seem to be an installed rock." 92 return nil, "'"..name.." "..version.."' does not seem to be an installed rock."
105 end 93 end
106 94
107 local rock_manifest = manif.load_rock_manifest(name, version, root) 95 local rock_manifest, err = manif.load_rock_manifest(name, version, root)
108 if not rock_manifest then 96 if not rock_manifest then return nil, err end
109 return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?"
110 end
111 97
112 local name_version = name .. "-" .. version 98 local name_version = name .. "-" .. version
113 local rock_file = fs.absolute_name(name_version .. "."..cfg.arch..".rock") 99 local rock_file = fs.absolute_name(name_version .. "."..cfg.arch..".rock")
@@ -164,34 +150,7 @@ function pack.pack_binary_rock(name, version, cmd, ...)
164 if not rname then 150 if not rname then
165 rname, rversion = name, version 151 rname, rversion = name, version
166 end 152 end
167 return do_pack_binary_rock(rname, rversion, temp_dir) 153 return pack.pack_installed_rock(rname, rversion, temp_dir)
168end
169
170--- Driver function for the "pack" command.
171-- @param arg string: may be a rockspec file, for creating a source rock,
172-- or the name of an installed package, for creating a binary rock.
173-- @param version string or nil: if the name of a package is given, a
174-- version may also be passed.
175-- @return boolean or (nil, string): true if successful or nil followed
176-- by an error message.
177function pack.command(flags, arg, version)
178 assert(type(version) == "string" or not version)
179 if type(arg) ~= "string" then
180 return nil, "Argument missing. "..util.see_help("pack")
181 end
182
183 local file, err
184 if arg:match(".*%.rockspec") then
185 file, err = pack.pack_source_rock(arg)
186 else
187 file, err = do_pack_binary_rock(arg:lower(), version, flags["tree"])
188 end
189 if err then
190 return nil, err
191 else
192 util.printout("Packed: "..file)
193 return true
194 end
195end 154end
196 155
197return pack 156return pack
diff --git a/src/luarocks/path.lua b/src/luarocks/path.lua
index dafc64e7..37898435 100644
--- a/src/luarocks/path.lua
+++ b/src/luarocks/path.lua
@@ -3,9 +3,10 @@
3-- All paths are configured in this module, making it a single 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. 4-- point where the layout of the local installation is defined in LuaRocks.
5local path = {} 5local path = {}
6setmetatable(path, { __index = require("luarocks.core.path") })
6 7
7local dir = require("luarocks.dir") 8local dir = require("luarocks.dir")
8local cfg = require("luarocks.cfg") 9local cfg = require("luarocks.core.cfg")
9local util = require("luarocks.util") 10local util = require("luarocks.util")
10 11
11--- Infer rockspec filename from a rock filename. 12--- Infer rockspec filename from a rock filename.
@@ -17,29 +18,11 @@ function path.rockspec_name_from_rock(rock_name)
17 return base_name:match("(.*)%.[^.]*.rock") .. ".rockspec" 18 return base_name:match("(.*)%.[^.]*.rock") .. ".rockspec"
18end 19end
19 20
20function path.rocks_dir(tree)
21 if type(tree) == "string" then
22 return dir.path(tree, cfg.rocks_subdir)
23 else
24 assert(type(tree) == "table")
25 return tree.rocks_dir or dir.path(tree.root, cfg.rocks_subdir)
26 end
27end
28
29function path.root_dir(rocks_dir) 21function path.root_dir(rocks_dir)
30 assert(type(rocks_dir) == "string") 22 assert(type(rocks_dir) == "string")
31 return rocks_dir:match("(.*)" .. util.matchquote(cfg.rocks_subdir) .. ".*$") 23 return rocks_dir:match("(.*)" .. util.matchquote(cfg.rocks_subdir) .. ".*$")
32end 24end
33 25
34function path.rocks_tree_to_string(tree)
35 if type(tree) == "string" then
36 return tree
37 else
38 assert(type(tree) == "table")
39 return tree.root
40 end
41end
42
43function path.deploy_bin_dir(tree) 26function path.deploy_bin_dir(tree)
44 if type(tree) == "string" then 27 if type(tree) == "string" then
45 return dir.path(tree, "bin") 28 return dir.path(tree, "bin")
@@ -49,24 +32,6 @@ function path.deploy_bin_dir(tree)
49 end 32 end
50end 33end
51 34
52function path.deploy_lua_dir(tree)
53 if type(tree) == "string" then
54 return dir.path(tree, cfg.lua_modules_path)
55 else
56 assert(type(tree) == "table")
57 return tree.lua_dir or dir.path(tree.root, cfg.lua_modules_path)
58 end
59end
60
61function path.deploy_lib_dir(tree)
62 if type(tree) == "string" then
63 return dir.path(tree, cfg.lib_modules_path)
64 else
65 assert(type(tree) == "table")
66 return tree.lib_dir or dir.path(tree.root, cfg.lib_modules_path)
67 end
68end
69
70function path.manifest_file(tree) 35function path.manifest_file(tree)
71 if type(tree) == "string" then 36 if type(tree) == "string" then
72 return dir.path(tree, cfg.rocks_subdir, "manifest") 37 return dir.path(tree, cfg.rocks_subdir, "manifest")
@@ -229,34 +194,6 @@ function path.make_url(pathname, name, version, arch)
229 return dir.path(pathname, filename) 194 return dir.path(pathname, filename)
230end 195end
231 196
232--- Convert a pathname to a module identifier.
233-- In Unix, for example, a path "foo/bar/baz.lua" is converted to
234-- "foo.bar.baz"; "bla/init.lua" returns "bla"; "foo.so" returns "foo".
235-- @param file string: Pathname of module
236-- @return string: The module identifier, or nil if given path is
237-- not a conformant module path (the function does not check if the
238-- path actually exists).
239function path.path_to_module(file)
240 assert(type(file) == "string")
241
242 local name = file:match("(.*)%."..cfg.lua_extension.."$")
243 if name then
244 name = name:gsub(dir.separator, ".")
245 local init = name:match("(.*)%.init$")
246 if init then
247 name = init
248 end
249 else
250 name = file:match("(.*)%."..cfg.lib_extension.."$")
251 if name then
252 name = name:gsub(dir.separator, ".")
253 end
254 end
255 if not name then name = file end
256 name = name:gsub("^%.+", ""):gsub("%.+$", "")
257 return name
258end
259
260--- Obtain the directory name where a module should be stored. 197--- Obtain the directory name where a module should be stored.
261-- For example, on Unix, "foo.bar.baz" will return "foo/bar". 198-- For example, on Unix, "foo.bar.baz" will return "foo/bar".
262-- @param mod string: A module name in Lua dot-separated format. 199-- @param mod string: A module name in Lua dot-separated format.
@@ -286,22 +223,6 @@ function path.configure_paths(rockspec)
286 rockspec.variables = vars 223 rockspec.variables = vars
287end 224end
288 225
289--- Produce a versioned version of a filename.
290-- @param file string: filename (must start with prefix)
291-- @param prefix string: Path prefix for file
292-- @param name string: Rock name
293-- @param version string: Rock version
294-- @return string: a pathname with the same directory parts and a versioned basename.
295function path.versioned_name(file, prefix, name, version)
296 assert(type(file) == "string")
297 assert(type(name) == "string")
298 assert(type(version) == "string")
299
300 local rest = file:sub(#prefix+1):gsub("^/*", "")
301 local name_version = (name.."_"..version):gsub("%-", "_"):gsub("%.", "_")
302 return dir.path(prefix, name_version.."-"..rest)
303end
304
305function path.use_tree(tree) 226function path.use_tree(tree)
306 cfg.root_dir = tree 227 cfg.root_dir = tree
307 cfg.rocks_dir = path.rocks_dir(tree) 228 cfg.rocks_dir = path.rocks_dir(tree)
@@ -310,6 +231,15 @@ function path.use_tree(tree)
310 cfg.deploy_lib_dir = path.deploy_lib_dir(tree) 231 cfg.deploy_lib_dir = path.deploy_lib_dir(tree)
311end 232end
312 233
234function path.rocks_tree_to_string(tree)
235 if type(tree) == "string" then
236 return tree
237 else
238 assert(type(tree) == "table")
239 return tree.root
240 end
241end
242
313--- Apply a given function to the active rocks trees based on chosen dependency mode. 243--- Apply a given function to the active rocks trees based on chosen dependency mode.
314-- @param deps_mode string: Dependency mode: "one" for the current default tree, 244-- @param deps_mode string: Dependency mode: "one" for the current default tree,
315-- "all" for all trees, "order" for all trees with priority >= the current default, 245-- "all" for all trees, "order" for all trees with priority >= the current default,
@@ -338,51 +268,4 @@ function path.map_trees(deps_mode, fn, ...)
338 return result 268 return result
339end 269end
340 270
341local is_src_extension = { [".lua"] = true, [".tl"] = true, [".tld"] = true, [".moon"] = true }
342
343--- Return the pathname of the file that would be loaded for a module, indexed.
344-- @param file_name string: module file name as in manifest (eg. "socket/core.so")
345-- @param name string: name of the package (eg. "luasocket")
346-- @param version string: version number (eg. "2.0.2-1")
347-- @param tree string: repository path (eg. "/usr/local")
348-- @param i number: the index, 1 if version is the current default, > 1 otherwise.
349-- This is done this way for use by select_module in luarocks.loader.
350-- @return string: filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so")
351function path.which_i(file_name, name, version, tree, i)
352 local deploy_dir
353 local extension = file_name:match("%.[a-z]+$")
354 if is_src_extension[extension] then
355 deploy_dir = path.deploy_lua_dir(tree)
356 file_name = dir.path(deploy_dir, file_name)
357 else
358 deploy_dir = path.deploy_lib_dir(tree)
359 file_name = dir.path(deploy_dir, file_name)
360 end
361 if i > 1 then
362 file_name = path.versioned_name(file_name, deploy_dir, name, version)
363 end
364 return file_name
365end
366
367--- Return the pathname of the file that would be loaded for a module,
368-- returning the versioned pathname if given version is not the default version
369-- in the given manifest.
370-- @param module_name string: module name (eg. "socket.core")
371-- @param file_name string: module file name as in manifest (eg. "socket/core.so")
372-- @param name string: name of the package (eg. "luasocket")
373-- @param version string: version number (eg. "2.0.2-1")
374-- @param tree string: repository path (eg. "/usr/local")
375-- @param manifest table: the manifest table for the tree.
376-- @return string: filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so")
377function path.which(module_name, file_name, name, version, tree, manifest)
378 local versions = manifest.modules[module_name]
379 assert(versions)
380 for i, name_version in ipairs(versions) do
381 if name_version == name.."/"..version then
382 return path.which_i(file_name, name, version, tree, i):gsub("//", "/")
383 end
384 end
385 assert(false)
386end
387
388return path 271return path
diff --git a/src/luarocks/persist.lua b/src/luarocks/persist.lua
index 708f07be..6d5e917b 100644
--- a/src/luarocks/persist.lua
+++ b/src/luarocks/persist.lua
@@ -4,83 +4,10 @@
4-- Implemented separately to avoid interdependencies, 4-- Implemented separately to avoid interdependencies,
5-- as it is used in the bootstrapping stage of the cfg module. 5-- as it is used in the bootstrapping stage of the cfg module.
6local persist = {} 6local persist = {}
7package.loaded["luarocks.persist"] = persist 7setmetatable(persist, { __index = require("luarocks.core.persist") })
8 8
9local util = require("luarocks.util") 9local util = require("luarocks.util")
10 10
11--- Load and run a Lua file in an environment.
12-- @param filename string: the name of the file.
13-- @param env table: the environment table.
14-- @return (true, any) or (nil, string, string): true and the return value
15-- of the file, or nil, an error message and an error code ("open", "load"
16-- or "run") in case of errors.
17local function run_file(filename, env)
18 local fd, err = io.open(filename)
19 if not fd then
20 return nil, err, "open"
21 end
22 local str, err = fd:read("*a")
23 fd:close()
24 if not str then
25 return nil, err, "open"
26 end
27 str = str:gsub("^#![^\n]*\n", "")
28 local chunk, ran
29 if _VERSION == "Lua 5.1" then -- Lua 5.1
30 chunk, err = loadstring(str, filename)
31 if chunk then
32 setfenv(chunk, env)
33 ran, err = pcall(chunk)
34 end
35 else -- Lua 5.2
36 chunk, err = load(str, filename, "t", env)
37 if chunk then
38 ran, err = pcall(chunk)
39 end
40 end
41 if not chunk then
42 return nil, "Error loading file: "..err, "load"
43 end
44 if not ran then
45 return nil, "Error running file: "..err, "run"
46 end
47 return true, err
48end
49
50--- Load a Lua file containing assignments, storing them in a table.
51-- The global environment is not propagated to the loaded file.
52-- @param filename string: the name of the file.
53-- @param tbl table or nil: if given, this table is used to store
54-- loaded values.
55-- @return (table, table) or (nil, string, string): a table with the file's assignments
56-- as fields and set of undefined globals accessed in file,
57-- or nil, an error message and an error code ("open"; couldn't open the file,
58-- "load"; compile-time error, or "run"; run-time error)
59-- in case of errors.
60function persist.load_into_table(filename, tbl)
61 assert(type(filename) == "string")
62 assert(type(tbl) == "table" or not tbl)
63
64 local result = tbl or {}
65 local globals = {}
66 local globals_mt = {
67 __index = function(t, k)
68 globals[k] = true
69 end
70 }
71 local save_mt = getmetatable(result)
72 setmetatable(result, globals_mt)
73
74 local ok, err, errcode = run_file(filename, result)
75
76 setmetatable(result, save_mt)
77
78 if not ok then
79 return nil, err, errcode
80 end
81 return result, globals
82end
83
84local write_table 11local write_table
85 12
86--- Write a value as Lua code. 13--- Write a value as Lua code.
diff --git a/src/luarocks/remove.lua b/src/luarocks/remove.lua
index 514c6dfa..6cc8334f 100644
--- a/src/luarocks/remove.lua
+++ b/src/luarocks/remove.lua
@@ -1,8 +1,4 @@
1
2--- Module implementing the LuaRocks "remove" command.
3-- Uninstalls rocks.
4local remove = {} 1local remove = {}
5package.loaded["luarocks.remove"] = remove
6 2
7local search = require("luarocks.search") 3local search = require("luarocks.search")
8local deps = require("luarocks.deps") 4local deps = require("luarocks.deps")
@@ -10,22 +6,7 @@ local fetch = require("luarocks.fetch")
10local repos = require("luarocks.repos") 6local repos = require("luarocks.repos")
11local path = require("luarocks.path") 7local path = require("luarocks.path")
12local util = require("luarocks.util") 8local util = require("luarocks.util")
13local cfg = require("luarocks.cfg") 9local cfg = require("luarocks.core.cfg")
14local fs = require("luarocks.fs")
15local manif = require("luarocks.manif")
16
17util.add_run_function(remove)
18remove.help_summary = "Uninstall a rock."
19remove.help_arguments = "[--force|--force-fast] <name> [<version>]"
20remove.help = [[
21Argument is the name of a rock to be uninstalled.
22If a version is not given, try to remove all versions at once.
23Will only perform the removal if it does not break dependencies.
24To override this check and force the removal, use --force.
25To perform a forced removal without reporting dependency issues,
26use --force-fast.
27
28]]..util.deps_mode_help()
29 10
30--- Obtain a list of packages that depend on the given set of packages 11--- Obtain a list of packages that depend on the given set of packages
31-- (where all packages of the set are versions of one program). 12-- (where all packages of the set are versions of one program).
@@ -131,44 +112,4 @@ function remove.remove_other_versions(name, version, force, fast)
131 return true 112 return true
132end 113end
133 114
134--- Driver function for the "remove" command.
135-- @param name string: name of a rock. If a version is given, refer to
136-- a specific version; otherwise, try to remove all versions.
137-- @param version string: When passing a package name, a version number
138-- may also be given.
139-- @return boolean or (nil, string, exitcode): True if removal was
140-- successful, nil and an error message otherwise. exitcode is optionally returned.
141function remove.command(flags, name, version)
142 if type(name) ~= "string" then
143 return nil, "Argument missing. "..util.see_help("remove")
144 end
145
146 local deps_mode = flags["deps-mode"] or cfg.deps_mode
147
148 local ok, err = fs.check_command_permissions(flags)
149 if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end
150
151 local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$")
152 local filename = name
153 if rock_type then
154 name, version = path.parse_name(filename)
155 if not name then return nil, "Invalid "..rock_type.." filename: "..filename end
156 end
157
158 local results = {}
159 name = name:lower()
160 search.manifest_search(results, cfg.rocks_dir, search.make_query(name, version))
161 if not results[name] then
162 return nil, "Could not find rock '"..name..(version and " "..version or "").."' in "..path.rocks_tree_to_string(cfg.root_dir)
163 end
164
165 local ok, err = remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"])
166 if not ok then
167 return nil, err
168 end
169
170 manif.check_dependencies(nil, deps.get_deps_mode(flags))
171 return true
172end
173
174return remove 115return remove
diff --git a/src/luarocks/repos.lua b/src/luarocks/repos.lua
index abc62b0d..1abb5628 100644
--- a/src/luarocks/repos.lua
+++ b/src/luarocks/repos.lua
@@ -1,15 +1,14 @@
1 1
2--- Functions for managing the repository on disk. 2--- Functions for managing the repository on disk.
3local repos = {} 3local repos = {}
4package.loaded["luarocks.repos"] = repos
5 4
6local fs = require("luarocks.fs") 5local fs = require("luarocks.fs")
7local path = require("luarocks.path") 6local path = require("luarocks.path")
8local cfg = require("luarocks.cfg") 7local cfg = require("luarocks.core.cfg")
9local util = require("luarocks.util") 8local util = require("luarocks.util")
10local dir = require("luarocks.dir") 9local dir = require("luarocks.dir")
11local manif = require("luarocks.manif") 10local manif = require("luarocks.manif")
12local deps = require("luarocks.deps") 11local vers = require("luarocks.vers")
13 12
14-- Tree of files installed by a package are stored 13-- Tree of files installed by a package are stored
15-- in its rock manifest. Some of these files have to 14-- in its rock manifest. Some of these files have to
@@ -55,67 +54,73 @@ end
55local function recurse_rock_manifest_tree(file_tree, action) 54local function recurse_rock_manifest_tree(file_tree, action)
56 assert(type(file_tree) == "table") 55 assert(type(file_tree) == "table")
57 assert(type(action) == "function") 56 assert(type(action) == "function")
58 local function do_recurse_rock_manifest_tree(tree, parent_path, parent_module) 57
59 58 local function do_recurse_rock_manifest_tree(tree, parent_path)
60 for file, sub in pairs(tree) do 59 for file, sub in pairs(tree) do
60 local sub_path = (parent_path and (parent_path .. "/") or "") .. file
61 local ok, err
62
61 if type(sub) == "table" then 63 if type(sub) == "table" then
62 local ok, err = do_recurse_rock_manifest_tree(sub, parent_path..file.."/", parent_module..file..".") 64 ok, err = do_recurse_rock_manifest_tree(sub, sub_path)
63 if not ok then return nil, err end
64 else 65 else
65 local ok, err = action(parent_path, parent_module, file) 66 ok, err = action(sub_path)
66 if not ok then return nil, err end
67 end 67 end
68
69 if not ok then return nil, err end
68 end 70 end
69 return true 71 return true
70 end 72 end
71 return do_recurse_rock_manifest_tree(file_tree, "", "") 73 return do_recurse_rock_manifest_tree(file_tree)
72end 74end
73 75
74local function store_package_data(result, name, file_tree) 76local function store_package_data(result, rock_manifest, deploy_type)
75 if not file_tree then return end 77 if rock_manifest[deploy_type] then
76 return recurse_rock_manifest_tree(file_tree, 78 recurse_rock_manifest_tree(rock_manifest[deploy_type], function(file_path)
77 function(parent_path, parent_module, file) 79 local _, item_name = manif.get_provided_item(deploy_type, file_path)
78 local pathname = parent_path..file 80 result[item_name] = file_path
79 result[path.path_to_module(pathname)] = pathname
80 return true 81 return true
81 end 82 end)
82 ) 83 end
83end 84end
84 85
85--- Obtain a list of modules within an installed package. 86--- Obtain a table of modules within an installed package.
86-- @param package string: The package name; for example "luasocket" 87-- @param name string: The package name; for example "luasocket"
87-- @param version string: The exact version number including revision; 88-- @param version string: The exact version number including revision;
88-- for example "2.0.1-1". 89-- for example "2.0.1-1".
89-- @return table: A table of modules where keys are module identifiers 90-- @return table: A table of modules where keys are module names
90-- in "foo.bar" format and values are pathnames in architecture-dependent 91-- and values are file paths of files providing modules
91-- "foo/bar.so" format. If no modules are found or if package or version 92-- relative to "lib" or "lua" rock manifest subtree.
93-- If no modules are found or if package name or version
92-- are invalid, an empty table is returned. 94-- are invalid, an empty table is returned.
93function repos.package_modules(package, version) 95function repos.package_modules(name, version)
94 assert(type(package) == "string") 96 assert(type(name) == "string")
95 assert(type(version) == "string") 97 assert(type(version) == "string")
96 98
97 local result = {} 99 local result = {}
98 local rock_manifest = manif.load_rock_manifest(package, version) 100 local rock_manifest = manif.load_rock_manifest(name, version)
99 store_package_data(result, package, rock_manifest.lib) 101 if not rock_manifest then return result end
100 store_package_data(result, package, rock_manifest.lua) 102 store_package_data(result, rock_manifest, "lib")
103 store_package_data(result, rock_manifest, "lua")
101 return result 104 return result
102end 105end
103 106
104--- Obtain a list of command-line scripts within an installed package. 107--- Obtain a table of command-line scripts within an installed package.
105-- @param package string: The package name; for example "luasocket" 108-- @param name string: The package name; for example "luasocket"
106-- @param version string: The exact version number including revision; 109-- @param version string: The exact version number including revision;
107-- for example "2.0.1-1". 110-- for example "2.0.1-1".
108-- @return table: A table of items where keys are command names 111-- @return table: A table of commands where keys and values are command names
109-- as strings and values are pathnames in architecture-dependent 112-- as strings - file paths of files providing commands
110-- ".../bin/foo" format. If no modules are found or if package or version 113-- relative to "bin" rock manifest subtree.
114-- If no commands are found or if package name or version
111-- are invalid, an empty table is returned. 115-- are invalid, an empty table is returned.
112function repos.package_commands(package, version) 116function repos.package_commands(name, version)
113 assert(type(package) == "string") 117 assert(type(name) == "string")
114 assert(type(version) == "string") 118 assert(type(version) == "string")
115 119
116 local result = {} 120 local result = {}
117 local rock_manifest = manif.load_rock_manifest(package, version) 121 local rock_manifest = manif.load_rock_manifest(name, version)
118 store_package_data(result, package, rock_manifest.bin) 122 if not rock_manifest then return result end
123 store_package_data(result, rock_manifest, "bin")
119 return result 124 return result
120end 125end
121 126
@@ -130,7 +135,7 @@ function repos.has_binaries(name, version)
130 assert(type(version) == "string") 135 assert(type(version) == "string")
131 136
132 local rock_manifest = manif.load_rock_manifest(name, version) 137 local rock_manifest = manif.load_rock_manifest(name, version)
133 if rock_manifest.bin then 138 if rock_manifest and rock_manifest.bin then
134 for name, md5 in pairs(rock_manifest.bin) do 139 for name, md5 in pairs(rock_manifest.bin) do
135 -- TODO verify that it is the same file. If it isn't, find the actual command. 140 -- TODO verify that it is the same file. If it isn't, find the actual command.
136 if fs.is_actual_binary(dir.path(cfg.deploy_bin_dir, name)) then 141 if fs.is_actual_binary(dir.path(cfg.deploy_bin_dir, name)) then
@@ -241,7 +246,7 @@ local function prepare_target(name, version, deploy_type, file_path, suffix)
241 246
242 if not cur_name then 247 if not cur_name then
243 return non_versioned 248 return non_versioned
244 elseif name < cur_name or (name == cur_name and deps.compare_versions(version, cur_version)) then 249 elseif name < cur_name or (name == cur_name and vers.compare_versions(version, cur_version)) then
245 -- New version has priority. Move currently provided version back using versioned name. 250 -- New version has priority. Move currently provided version back using versioned name.
246 local cur_deploy_type, cur_file_path = manif.get_providing_file(cur_name, cur_version, item_type, item_name) 251 local cur_deploy_type, cur_file_path = manif.get_providing_file(cur_name, cur_version, item_type, item_name)
247 local cur_non_versioned, cur_versioned = get_deploy_paths(cur_name, cur_version, cur_deploy_type, cur_file_path) 252 local cur_non_versioned, cur_versioned = get_deploy_paths(cur_name, cur_version, cur_deploy_type, cur_file_path)
@@ -271,15 +276,15 @@ function repos.deploy_files(name, version, wrap_bin_scripts, deps_mode)
271 assert(type(version) == "string") 276 assert(type(version) == "string")
272 assert(type(wrap_bin_scripts) == "boolean") 277 assert(type(wrap_bin_scripts) == "boolean")
273 278
274 local rock_manifest = manif.load_rock_manifest(name, version) 279 local rock_manifest, load_err = manif.load_rock_manifest(name, version)
280 if not rock_manifest then return nil, load_err end
275 281
276 local function deploy_file_tree(deploy_type, source_dir, move_fn, suffix) 282 local function deploy_file_tree(deploy_type, source_dir, move_fn, suffix)
277 if not rock_manifest[deploy_type] then 283 if not rock_manifest[deploy_type] then
278 return true 284 return true
279 end 285 end
280 286
281 return recurse_rock_manifest_tree(rock_manifest[deploy_type], function(parent_path, parent_module, file) 287 return recurse_rock_manifest_tree(rock_manifest[deploy_type], function(file_path)
282 local file_path = parent_path .. file
283 local source = dir.path(source_dir, file_path) 288 local source = dir.path(source_dir, file_path)
284 289
285 local target, prepare_err = prepare_target(name, version, deploy_type, file_path, suffix) 290 local target, prepare_err = prepare_target(name, version, deploy_type, file_path, suffix)
@@ -331,7 +336,8 @@ function repos.deploy_files(name, version, wrap_bin_scripts, deps_mode)
331 ok, err = deploy_file_tree("lib", path.lib_dir(name, version), make_mover(cfg.perm_exec)) 336 ok, err = deploy_file_tree("lib", path.lib_dir(name, version), make_mover(cfg.perm_exec))
332 if not ok then return nil, err end 337 if not ok then return nil, err end
333 338
334 return manif.add_to_manifest(name, version, nil, deps_mode) 339 local writer = require("luarocks.manif.writer")
340 return writer.add_to_manifest(name, version, nil, deps_mode)
335end 341end
336 342
337--- Delete a package from the local repository. 343--- Delete a package from the local repository.
@@ -349,18 +355,15 @@ function repos.delete_version(name, version, deps_mode, quick)
349 assert(type(version) == "string") 355 assert(type(version) == "string")
350 assert(type(deps_mode) == "string") 356 assert(type(deps_mode) == "string")
351 357
352 local rock_manifest = manif.load_rock_manifest(name, version) 358 local rock_manifest, load_err = manif.load_rock_manifest(name, version)
353 if not rock_manifest then 359 if not rock_manifest then return nil, load_err end
354 return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?"
355 end
356 360
357 local function delete_deployed_file_tree(deploy_type, suffix) 361 local function delete_deployed_file_tree(deploy_type, suffix)
358 if not rock_manifest[deploy_type] then 362 if not rock_manifest[deploy_type] then
359 return true 363 return true
360 end 364 end
361 365
362 return recurse_rock_manifest_tree(rock_manifest[deploy_type], function(parent_path, parent_module, file) 366 return recurse_rock_manifest_tree(rock_manifest[deploy_type], function(file_path)
363 local file_path = parent_path .. file
364 local non_versioned, versioned = get_deploy_paths(name, version, deploy_type, file_path) 367 local non_versioned, versioned = get_deploy_paths(name, version, deploy_type, file_path)
365 368
366 -- Figure out if the file is deployed using versioned or non-versioned name. 369 -- Figure out if the file is deployed using versioned or non-versioned name.
@@ -417,7 +420,30 @@ function repos.delete_version(name, version, deps_mode, quick)
417 return true 420 return true
418 end 421 end
419 422
420 return manif.remove_from_manifest(name, version, nil, deps_mode) 423 local writer = require("luarocks.manif.writer")
424 return writer.remove_from_manifest(name, version, nil, deps_mode)
425end
426
427--- Find full path to a file providing a module or a command
428-- in a package.
429-- @param name string: name of package.
430-- @param version string: exact package version in string format.
431-- @param item_type string: "module" or "command".
432-- @param item_name string: module or command name.
433-- @param root string or nil: A local root dir for a rocks tree. If not given, the default is used.
434-- @return string: absolute path to the file providing given module
435-- or command.
436function repos.which(name, version, item_type, item_name, repo)
437 local deploy_type, file_path = manif.get_providing_file(name, version, item_type, item_name, repo)
438 local non_versioned, versioned = get_deploy_paths(name, version, deploy_type, file_path, repo)
439 local cur_name, cur_version = manif.get_current_provider(item_type, item_name)
440 local deploy_path = (name == cur_name and version == cur_version) and non_versioned or versioned
441
442 if deploy_type == "bin" and cfg.wrapper_suffix and cfg.wrapper_suffix ~= "" then
443 deploy_path = find_suffixed(deploy_path, cfg.wrapper_suffix) or deploy_path
444 end
445
446 return deploy_path
421end 447end
422 448
423--- Find full path to a file providing a module or a command 449--- Find full path to a file providing a module or a command
diff --git a/src/luarocks/search.lua b/src/luarocks/search.lua
index d22c2a18..35010c2b 100644
--- a/src/luarocks/search.lua
+++ b/src/luarocks/search.lua
@@ -1,28 +1,12 @@
1
2--- Module implementing the LuaRocks "search" command.
3-- Queries LuaRocks servers.
4local search = {} 1local search = {}
5package.loaded["luarocks.search"] = search
6 2
7local dir = require("luarocks.dir") 3local dir = require("luarocks.dir")
8local path = require("luarocks.path") 4local path = require("luarocks.path")
9local manif = require("luarocks.manif") 5local manif = require("luarocks.manif")
10local deps = require("luarocks.deps") 6local vers = require("luarocks.vers")
11local cfg = require("luarocks.cfg") 7local cfg = require("luarocks.core.cfg")
12local util = require("luarocks.util") 8local util = require("luarocks.util")
13 9
14util.add_run_function(search)
15search.help_summary = "Query the LuaRocks servers."
16search.help_arguments = "[--source] [--binary] { <name> [<version>] | --all }"
17search.help = [[
18--source Return only rockspecs and source rocks,
19 to be used with the "build" command.
20--binary Return only pure Lua and binary rocks (rocks that can be used
21 with the "install" command without requiring a C toolchain).
22--all List all contents of the server that are suitable to
23 this platform, do not filter by name.
24]]
25
26--- Convert the arch field of a query table to table format. 10--- Convert the arch field of a query table to table format.
27-- @param query table: A query table. 11-- @param query table: A query table.
28local function query_arch_as_table(query) 12local function query_arch_as_table(query)
@@ -56,7 +40,7 @@ end
56-- identifier), "rockspec" or "installed" 40-- identifier), "rockspec" or "installed"
57-- @param repo string: Pathname of a local repository of URL of 41-- @param repo string: Pathname of a local repository of URL of
58-- rocks server. 42-- rocks server.
59local function store_result(results, name, version, arch, repo) 43function search.store_result(results, name, version, arch, repo)
60 assert(type(results) == "table") 44 assert(type(results) == "table")
61 assert(type(name) == "string") 45 assert(type(name) == "string")
62 assert(type(version) == "string") 46 assert(type(version) == "string")
@@ -108,8 +92,8 @@ end
108local function store_if_match(results, repo, name, version, arch, query) 92local function store_if_match(results, repo, name, version, arch, query)
109 if match_name(query, name) then 93 if match_name(query, name) then
110 if query.arch[arch] or query.arch["any"] then 94 if query.arch[arch] or query.arch["any"] then
111 if deps.match_constraints(deps.parse_version(version), query.constraints) then 95 if vers.match_constraints(vers.parse_version(version), query.constraints) then
112 store_result(results, name, version, arch, repo) 96 search.store_result(results, name, version, arch, repo)
113 end 97 end
114 end 98 end
115 end 99 end
@@ -245,7 +229,7 @@ function search.make_query(name, version)
245 constraints = {} 229 constraints = {}
246 } 230 }
247 if version then 231 if version then
248 table.insert(query.constraints, { op = "==", version = deps.parse_version(version)}) 232 table.insert(query.constraints, { op = "==", version = vers.parse_version(version)})
249 end 233 end
250 return query 234 return query
251end 235end
@@ -262,7 +246,7 @@ local function pick_latest_version(name, versions)
262 246
263 local vtables = {} 247 local vtables = {}
264 for v, _ in pairs(versions) do 248 for v, _ in pairs(versions) do
265 table.insert(vtables, deps.parse_version(v)) 249 table.insert(vtables, vers.parse_version(v))
266 end 250 end
267 table.sort(vtables) 251 table.sort(vtables)
268 local version = vtables[#vtables].string 252 local version = vtables[#vtables].string
@@ -355,7 +339,7 @@ function search.print_results(results, porcelain)
355 if not porcelain then 339 if not porcelain then
356 util.printout(package) 340 util.printout(package)
357 end 341 end
358 for version, repos in util.sortedpairs(versions, deps.compare_versions) do 342 for version, repos in util.sortedpairs(versions, vers.compare_versions) do
359 for _, repo in ipairs(repos) do 343 for _, repo in ipairs(repos) do
360 repo.repo = dir.normalize(repo.repo) 344 repo.repo = dir.normalize(repo.repo)
361 if porcelain then 345 if porcelain then
@@ -371,28 +355,6 @@ function search.print_results(results, porcelain)
371 end 355 end
372end 356end
373 357
374--- Splits a list of search results into two lists, one for "source" results
375-- to be used with the "build" command, and one for "binary" results to be
376-- used with the "install" command.
377-- @param results table: A search results table.
378-- @return (table, table): Two tables, one for source and one for binary
379-- results.
380local function split_source_and_binary_results(results)
381 local sources, binaries = {}, {}
382 for name, versions in pairs(results) do
383 for version, repositories in pairs(versions) do
384 for _, repo in ipairs(repositories) do
385 local where = sources
386 if repo.arch == "all" or repo.arch == cfg.arch then
387 where = binaries
388 end
389 store_result(where, name, version, repo.arch, repo.repo)
390 end
391 end
392 end
393 return sources, binaries
394end
395
396--- Given a name and optionally a version, try to find in the rocks 358--- Given a name and optionally a version, try to find in the rocks
397-- servers a single .src.rock or .rockspec file that satisfies 359-- servers a single .src.rock or .rockspec file that satisfies
398-- the request, and run the given function on it; or display to the 360-- the request, and run the given function on it; or display to the
@@ -440,7 +402,7 @@ function search.pick_installed_rock(name, version, given_tree)
440 local package, versions = util.sortedpairs(results)() 402 local package, versions = util.sortedpairs(results)()
441 --question: what do we do about multiple versions? This should 403 --question: what do we do about multiple versions? This should
442 --give us the latest version on the last repo (which is usually the global one) 404 --give us the latest version on the last repo (which is usually the global one)
443 for vs, repositories in util.sortedpairs(versions, deps.compare_versions) do 405 for vs, repositories in util.sortedpairs(versions, vers.compare_versions) do
444 if not version then version = vs end 406 if not version then version = vs end
445 for _, rp in ipairs(repositories) do repo_url = rp.repo end 407 for _, rp in ipairs(repositories) do repo_url = rp.repo end
446 end 408 end
@@ -449,35 +411,4 @@ function search.pick_installed_rock(name, version, given_tree)
449 return name, version, repo, repo_url 411 return name, version, repo, repo_url
450end 412end
451 413
452--- Driver function for "search" command.
453-- @param name string: A substring of a rock name to search.
454-- @param version string or nil: a version may also be passed.
455-- @return boolean or (nil, string): True if build was successful; nil and an
456-- error message otherwise.
457function search.command(flags, name, version)
458 if flags["all"] then
459 name, version = "", nil
460 end
461
462 if type(name) ~= "string" and not flags["all"] then
463 return nil, "Enter name and version or use --all. "..util.see_help("search")
464 end
465
466 local query = search.make_query(name:lower(), version)
467 query.exact_name = false
468 local results, err = search.search_repos(query)
469 local porcelain = flags["porcelain"]
470 util.title("Search results:", porcelain, "=")
471 local sources, binaries = split_source_and_binary_results(results)
472 if next(sources) and not flags["binary"] then
473 util.title("Rockspecs and source rocks:", porcelain)
474 search.print_results(sources, porcelain)
475 end
476 if next(binaries) and not flags["source"] then
477 util.title("Binary and pure-Lua rocks:", porcelain)
478 search.print_results(binaries, porcelain)
479 end
480 return true
481end
482
483return search 414return search
diff --git a/src/luarocks/tools/tar.lua b/src/luarocks/tools/tar.lua
index 637a6c95..ae5c83e2 100644
--- a/src/luarocks/tools/tar.lua
+++ b/src/luarocks/tools/tar.lua
@@ -56,10 +56,11 @@ end
56local function read_header_block(block) 56local function read_header_block(block)
57 local header = {} 57 local header = {}
58 header.name = nullterm(block:sub(1,100)) 58 header.name = nullterm(block:sub(1,100))
59 header.mode = nullterm(block:sub(101,108)) 59 header.mode = nullterm(block:sub(101,108)):gsub(" ", "")
60 header.uid = octal_to_number(nullterm(block:sub(109,116))) 60 header.uid = octal_to_number(nullterm(block:sub(109,116)))
61 header.gid = octal_to_number(nullterm(block:sub(117,124))) 61 header.gid = octal_to_number(nullterm(block:sub(117,124)))
62 header.size = octal_to_number(nullterm(block:sub(125,136))) 62 header.size = octal_to_number(nullterm(block:sub(125,136)))
63print("{"..block:sub(125,136).."}", "{"..nullterm(block:sub(125,136)).."}", "{"..octal_to_number(nullterm(block:sub(125,136))).."}", header.size)
63 header.mtime = octal_to_number(nullterm(block:sub(137,148))) 64 header.mtime = octal_to_number(nullterm(block:sub(137,148)))
64 header.chksum = octal_to_number(nullterm(block:sub(149,156))) 65 header.chksum = octal_to_number(nullterm(block:sub(149,156)))
65 header.typeflag = get_typeflag(block:sub(157,157)) 66 header.typeflag = get_typeflag(block:sub(157,157))
@@ -93,13 +94,14 @@ function tar.untar(filename, destdir)
93 local long_name, long_link_name 94 local long_name, long_link_name
94 while true do 95 while true do
95 local block 96 local block
96 repeat 97 repeat
97 block = tar_handle:read(blocksize) 98 block = tar_handle:read(blocksize)
98 until (not block) or checksum_header(block) > 256 99 until (not block) or checksum_header(block) > 256
99 if not block then break end 100 if not block then break end
100 local header, err = read_header_block(block) 101 local header, err = read_header_block(block)
101 if not header then 102 if not header then
102 util.printerr(err) 103 util.printerr(err)
104 return nil, err
103 end 105 end
104 106
105 local file_data = tar_handle:read(math.ceil(header.size / blocksize) * blocksize):sub(1,header.size) 107 local file_data = tar_handle:read(math.ceil(header.size / blocksize) * blocksize):sub(1,header.size)
diff --git a/src/luarocks/type_check.lua b/src/luarocks/type_check.lua
index 63c59ca2..4b7e68d7 100644
--- a/src/luarocks/type_check.lua
+++ b/src/luarocks/type_check.lua
@@ -2,16 +2,15 @@
2-- Functions and definitions for doing a basic lint check on files 2-- Functions and definitions for doing a basic lint check on files
3-- loaded by LuaRocks. 3-- loaded by LuaRocks.
4local type_check = {} 4local type_check = {}
5package.loaded["luarocks.type_check"] = type_check 5setmetatable(type_check, { __index = require("luarocks.core.type_check") })
6 6
7local cfg = require("luarocks.cfg") 7type_check.rockspec_format = "3.0"
8local deps = require("luarocks.deps")
9 8
10type_check.rockspec_format = "1.1" 9local string_1 = type_check.string_1
10local mandatory_string_1 = type_check.mandatory_string_1
11 11
12local string_1 = { _type = "string" } 12local string_3 = { _type = "string", _version = "3.0" }
13local number_1 = { _type = "number" } 13local list_of_strings_3 = { _any = string_3, _version = "3.0" }
14local mandatory_string_1 = { _type = "string", _mandatory = true }
15 14
16-- Syntax for type-checking tables: 15-- Syntax for type-checking tables:
17-- 16--
@@ -37,6 +36,8 @@ local rockspec_types = {
37 homepage = string_1, 36 homepage = string_1,
38 license = string_1, 37 license = string_1,
39 maintainer = string_1, 38 maintainer = string_1,
39 labels = list_of_strings_3,
40 issues_url = string_3,
40 }, 41 },
41 dependencies = { 42 dependencies = {
42 platforms = {}, -- recursively defined below 43 platforms = {}, -- recursively defined below
@@ -112,206 +113,6 @@ rockspec_types.external_dependencies.platforms._any = rockspec_types.external_de
112rockspec_types.source.platforms._any = rockspec_types.source 113rockspec_types.source.platforms._any = rockspec_types.source
113rockspec_types.hooks.platforms._any = rockspec_types.hooks 114rockspec_types.hooks.platforms._any = rockspec_types.hooks
114 115
115local manifest_types = {
116 repository = {
117 _mandatory = true,
118 -- packages
119 _any = {
120 -- versions
121 _any = {
122 -- items
123 _any = {
124 arch = mandatory_string_1,
125 modules = { _any = string_1 },
126 commands = { _any = string_1 },
127 dependencies = { _any = string_1 },
128 -- TODO: to be extended with more metadata.
129 }
130 }
131 }
132 },
133 modules = {
134 _mandatory = true,
135 -- modules
136 _any = {
137 -- providers
138 _any = string_1
139 }
140 },
141 commands = {
142 _mandatory = true,
143 -- modules
144 _any = {
145 -- commands
146 _any = string_1
147 }
148 },
149 dependencies = {
150 -- each module
151 _any = {
152 -- each version
153 _any = {
154 -- each dependency
155 _any = {
156 name = string_1,
157 constraints = {
158 _any = {
159 no_upgrade = { _type = "boolean" },
160 op = string_1,
161 version = {
162 string = string_1,
163 _any = number_1,
164 }
165 }
166 }
167 }
168 }
169 }
170 }
171}
172
173local function check_version(version, typetbl, context)
174 local typetbl_version = typetbl._version or "1.0"
175 if deps.compare_versions(typetbl_version, version) then
176 if context == "" then
177 return nil, "Invalid rockspec_format version number in rockspec? Please fix rockspec accordingly."
178 else
179 return nil, context.." is not supported in rockspec format "..version.." (requires version "..typetbl_version.."), please fix the rockspec_format field accordingly."
180 end
181 end
182 return true
183end
184
185local type_check_table
186
187--- Type check an object.
188-- The object is compared against an archetypical value
189-- matching the expected type -- the actual values don't matter,
190-- only their types. Tables are type checked recursively.
191-- @param version string: The version of the item.
192-- @param item any: The object being checked.
193-- @param typetbl any: The type-checking table for the object.
194-- @param context string: A string indicating the "context" where the
195-- error occurred (the full table path), for error messages.
196-- @return boolean or (nil, string): true if type checking
197-- succeeded, or nil and an error message if it failed.
198-- @see type_check_table
199local function type_check_item(version, item, typetbl, context)
200 assert(type(version) == "string")
201
202 local ok, err = check_version(version, typetbl, context)
203 if not ok then
204 return nil, err
205 end
206
207 local item_type = type(item) or "nil"
208 local expected_type = typetbl._type or "table"
209
210 if expected_type == "number" then
211 if not tonumber(item) then
212 return nil, "Type mismatch on field "..context..": expected a number"
213 end
214 elseif expected_type == "string" then
215 if item_type ~= "string" then
216 return nil, "Type mismatch on field "..context..": expected a string, got "..item_type
217 end
218 if typetbl._pattern then
219 if not item:match("^"..typetbl._pattern.."$") then
220 return nil, "Type mismatch on field "..context..": invalid value "..item.." does not match '"..typetbl._pattern.."'"
221 end
222 end
223 elseif expected_type == "table" then
224 if item_type ~= expected_type then
225 return nil, "Type mismatch on field "..context..": expected a table"
226 else
227 return type_check_table(version, item, typetbl, context)
228 end
229 elseif item_type ~= expected_type then
230 return nil, "Type mismatch on field "..context..": expected "..expected_type
231 end
232 return true
233end
234
235local function mkfield(context, field)
236 if context == "" then
237 return tostring(field)
238 elseif type(field) == "string" then
239 return context.."."..field
240 else
241 return context.."["..tostring(field).."]"
242 end
243end
244
245--- Type check the contents of a table.
246-- The table's contents are compared against a reference table,
247-- which contains the recognized fields, with archetypical values
248-- matching the expected types -- the actual values of items in the
249-- reference table don't matter, only their types (ie, for field x
250-- in tbl that is correctly typed, type(tbl.x) == type(types.x)).
251-- If the reference table contains a field called MORE, then
252-- unknown fields in the checked table are accepted.
253-- If it contains a field called ANY, then its type will be
254-- used to check any unknown fields. If a field is prefixed
255-- with MUST_, it is mandatory; its absence from the table is
256-- a type error.
257-- Tables are type checked recursively.
258-- @param version string: The version of tbl.
259-- @param tbl table: The table to be type checked.
260-- @param typetbl table: The type-checking table, containing
261-- values for recognized fields in the checked table.
262-- @param context string: A string indicating the "context" where the
263-- error occurred (such as the name of the table the item is a part of),
264-- to be used by error messages.
265-- @return boolean or (nil, string): true if type checking
266-- succeeded, or nil and an error message if it failed.
267type_check_table = function(version, tbl, typetbl, context)
268 assert(type(version) == "string")
269 assert(type(tbl) == "table")
270 assert(type(typetbl) == "table")
271
272 local ok, err = check_version(version, typetbl, context)
273 if not ok then
274 return nil, err
275 end
276
277 for k, v in pairs(tbl) do
278 local t = typetbl[k] or typetbl._any
279 if t then
280 local ok, err = type_check_item(version, v, t, mkfield(context, k))
281 if not ok then return nil, err end
282 elseif typetbl._more then
283 -- Accept unknown field
284 else
285 if not cfg.accept_unknown_fields then
286 return nil, "Unknown field "..k
287 end
288 end
289 end
290 for k, v in pairs(typetbl) do
291 if k:sub(1,1) ~= "_" and v._mandatory then
292 if not tbl[k] then
293 return nil, "Mandatory field "..mkfield(context, k).." is missing."
294 end
295 end
296 end
297 return true
298end
299
300local function check_undeclared_globals(globals, typetbl)
301 local undeclared = {}
302 for glob, _ in pairs(globals) do
303 if not (typetbl[glob] or typetbl["MUST_"..glob]) then
304 table.insert(undeclared, glob)
305 end
306 end
307 if #undeclared == 1 then
308 return nil, "Unknown variable: "..undeclared[1]
309 elseif #undeclared > 1 then
310 return nil, "Unknown variables: "..table.concat(undeclared, ", ")
311 end
312 return true
313end
314
315--- Type check a rockspec table. 116--- Type check a rockspec table.
316-- Verify the correctness of elements from a 117-- Verify the correctness of elements from a
317-- rockspec table, reporting on unknown fields and type 118-- rockspec table, reporting on unknown fields and type
@@ -323,22 +124,9 @@ function type_check.type_check_rockspec(rockspec, globals)
323 if not rockspec.rockspec_format then 124 if not rockspec.rockspec_format then
324 rockspec.rockspec_format = "1.0" 125 rockspec.rockspec_format = "1.0"
325 end 126 end
326 local ok, err = check_undeclared_globals(globals, rockspec_types) 127 local ok, err = type_check.check_undeclared_globals(globals, rockspec_types)
327 if not ok then return nil, err end
328 return type_check_table(rockspec.rockspec_format, rockspec, rockspec_types, "")
329end
330
331--- Type check a manifest table.
332-- Verify the correctness of elements from a
333-- manifest table, reporting on unknown fields and type
334-- mismatches.
335-- @return boolean or (nil, string): true if type checking
336-- succeeded, or nil and an error message if it failed.
337function type_check.type_check_manifest(manifest, globals)
338 assert(type(manifest) == "table")
339 local ok, err = check_undeclared_globals(globals, manifest_types)
340 if not ok then return nil, err end 128 if not ok then return nil, err end
341 return type_check_table("1.0", manifest, manifest_types, "") 129 return type_check.type_check_table(rockspec.rockspec_format, rockspec, rockspec_types, "")
342end 130end
343 131
344return type_check 132return type_check
diff --git a/src/luarocks/upload/api.lua b/src/luarocks/upload/api.lua
index 2f0fa815..99162e75 100644
--- a/src/luarocks/upload/api.lua
+++ b/src/luarocks/upload/api.lua
@@ -1,7 +1,7 @@
1 1
2local api = {} 2local api = {}
3 3
4local cfg = require("luarocks.cfg") 4local cfg = require("luarocks.core.cfg")
5local fs = require("luarocks.fs") 5local fs = require("luarocks.fs")
6local dir = require("luarocks.dir") 6local dir = require("luarocks.dir")
7local util = require("luarocks.util") 7local util = require("luarocks.util")
diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua
index c9fb7d63..2c4724f4 100644
--- a/src/luarocks/util.lua
+++ b/src/luarocks/util.lua
@@ -2,9 +2,10 @@
2--- Assorted utilities for managing tables, plus a scheduler for rollback functions. 2--- Assorted utilities for managing tables, plus a scheduler for rollback functions.
3-- Does not requires modules directly (only as locals 3-- Does not requires modules directly (only as locals
4-- inside specific functions) to avoid interdependencies, 4-- inside specific functions) to avoid interdependencies,
5-- as this is used in the bootstrapping stage of luarocks.cfg. 5-- as this is used in the bootstrapping stage of luarocks.core.cfg.
6 6
7local util = {} 7local util = {}
8setmetatable(util, { __index = require("luarocks.core.util") })
8 9
9local unpack = unpack or table.unpack 10local unpack = unpack or table.unpack
10 11
@@ -86,7 +87,9 @@ local supported_flags = {
86 ["help"] = true, 87 ["help"] = true,
87 ["home"] = true, 88 ["home"] = true,
88 ["homepage"] = "\"<url>\"", 89 ["homepage"] = "\"<url>\"",
90 ["issues"] = true,
89 ["keep"] = true, 91 ["keep"] = true,
92 ["labels"] = true,
90 ["lib"] = "<library>", 93 ["lib"] = "<library>",
91 ["license"] = "\"<text>\"", 94 ["license"] = "\"<text>\"",
92 ["list"] = true, 95 ["list"] = true,
@@ -196,34 +199,6 @@ function util.parse_flags(...)
196 return flags, unpack(out) 199 return flags, unpack(out)
197end 200end
198 201
199-- Adds legacy 'run' function to a command module.
200-- @param command table: command module with 'command' function,
201-- the added 'run' function calls it after parseing command-line arguments.
202function util.add_run_function(command)
203 command.run = function(...) return command.command(util.parse_flags(...)) end
204end
205
206--- Merges contents of src on top of dst's contents.
207-- @param dst Destination table, which will receive src's contents.
208-- @param src Table which provides new contents to dst.
209-- @see platform_overrides
210function util.deep_merge(dst, src)
211 for k, v in pairs(src) do
212 if type(v) == "table" then
213 if not dst[k] then
214 dst[k] = {}
215 end
216 if type(dst[k]) == "table" then
217 util.deep_merge(dst[k], v)
218 else
219 dst[k] = v
220 end
221 else
222 dst[k] = v
223 end
224 end
225end
226
227--- Perform platform-specific overrides on a table. 202--- Perform platform-specific overrides on a table.
228-- Overrides values of table with the contents of the appropriate 203-- Overrides values of table with the contents of the appropriate
229-- subset of its "platforms" field. The "platforms" field should 204-- subset of its "platforms" field. The "platforms" field should
@@ -240,7 +215,7 @@ end
240function util.platform_overrides(tbl) 215function util.platform_overrides(tbl)
241 assert(type(tbl) == "table" or not tbl) 216 assert(type(tbl) == "table" or not tbl)
242 217
243 local cfg = require("luarocks.cfg") 218 local cfg = require("luarocks.core.cfg")
244 219
245 if not tbl then return end 220 if not tbl then return end
246 221
@@ -257,19 +232,6 @@ end
257 232
258local var_format_pattern = "%$%((%a[%a%d_]+)%)" 233local var_format_pattern = "%$%((%a[%a%d_]+)%)"
259 234
260--- Create a new shallow copy of a table: a new table with
261-- the same keys and values. Keys point to the same objects as
262-- the original table (ie, does not copy recursively).
263-- @param tbl table: the input table
264-- @return table: a new table with the same contents.
265function util.make_shallow_copy(tbl)
266 local copy = {}
267 for k,v in pairs(tbl) do
268 copy[k] = v
269 end
270 return copy
271end
272
273-- Check if a set of needed variables are referenced 235-- Check if a set of needed variables are referenced
274-- somewhere in a list of definitions, warning the user 236-- somewhere in a list of definitions, warning the user
275-- about any unused ones. Each key in needed_set should 237-- about any unused ones. Each key in needed_set should
@@ -332,86 +294,6 @@ function util.variable_substitutions(tbl, vars)
332 end 294 end
333end 295end
334 296
335--- Return an array of keys of a table.
336-- @param tbl table: The input table.
337-- @return table: The array of keys.
338function util.keys(tbl)
339 local ks = {}
340 for k,_ in pairs(tbl) do
341 table.insert(ks, k)
342 end
343 return ks
344end
345
346local function default_sort(a, b)
347 local ta = type(a)
348 local tb = type(b)
349 if ta == "number" and tb == "number" then
350 return a < b
351 elseif ta == "number" then
352 return true
353 elseif tb == "number" then
354 return false
355 else
356 return tostring(a) < tostring(b)
357 end
358end
359
360--- A table iterator generator that returns elements sorted by key,
361-- to be used in "for" loops.
362-- @param tbl table: The table to be iterated.
363-- @param sort_function function or table or nil: An optional comparison function
364-- to be used by table.sort when sorting keys, or an array listing an explicit order
365-- for keys. If a value itself is an array, it is taken so that the first element
366-- is a string representing the field name, and the second element is a priority table
367-- for that key, which is returned by the iterator as the third value after the key
368-- and the value.
369-- @return function: the iterator function.
370function util.sortedpairs(tbl, sort_function)
371 sort_function = sort_function or default_sort
372 local keys = util.keys(tbl)
373 local sub_orders = {}
374
375 if type(sort_function) == "function" then
376 table.sort(keys, sort_function)
377 else
378 local order = sort_function
379 local ordered_keys = {}
380 local all_keys = keys
381 keys = {}
382
383 for _, order_entry in ipairs(order) do
384 local key, sub_order
385 if type(order_entry) == "table" then
386 key = order_entry[1]
387 sub_order = order_entry[2]
388 else
389 key = order_entry
390 end
391
392 if tbl[key] then
393 ordered_keys[key] = true
394 sub_orders[key] = sub_order
395 table.insert(keys, key)
396 end
397 end
398
399 table.sort(all_keys, default_sort)
400 for _, key in ipairs(all_keys) do
401 if not ordered_keys[key] then
402 table.insert(keys, key)
403 end
404 end
405 end
406
407 local i = 1
408 return function()
409 local key = keys[i]
410 i = i + 1
411 return key, tbl[key], sub_orders[key]
412 end
413end
414
415function util.lua_versions() 297function util.lua_versions()
416 local versions = { "5.1", "5.2", "5.3" } 298 local versions = { "5.1", "5.2", "5.3" }
417 local i = 0 299 local i = 0
@@ -431,12 +313,6 @@ function util.printout(...)
431 io.stdout:write("\n") 313 io.stdout:write("\n")
432end 314end
433 315
434--- Print a line to standard error
435function util.printerr(...)
436 io.stderr:write(table.concat({...},"\t"))
437 io.stderr:write("\n")
438end
439
440--- Display a warning message. 316--- Display a warning message.
441-- @param msg string: the warning message 317-- @param msg string: the warning message
442function util.warning(msg) 318function util.warning(msg)
@@ -465,7 +341,7 @@ function util.this_program(default)
465end 341end
466 342
467function util.deps_mode_help(program) 343function util.deps_mode_help(program)
468 local cfg = require("luarocks.cfg") 344 local cfg = require("luarocks.core.cfg")
469 return [[ 345 return [[
470--deps-mode=<mode> How to handle dependencies. Four modes are supported: 346--deps-mode=<mode> How to handle dependencies. Four modes are supported:
471 * all - use all trees from the rocks_trees list 347 * all - use all trees from the rocks_trees list
@@ -488,7 +364,7 @@ function util.see_help(command, program)
488end 364end
489 365
490function util.announce_install(rockspec) 366function util.announce_install(rockspec)
491 local cfg = require("luarocks.cfg") 367 local cfg = require("luarocks.core.cfg")
492 local path = require("luarocks.path") 368 local path = require("luarocks.path")
493 369
494 local suffix = "" 370 local suffix = ""
@@ -511,7 +387,7 @@ local function collect_rockspecs(versions, paths, unnamed_paths, subdir)
511 local fs = require("luarocks.fs") 387 local fs = require("luarocks.fs")
512 local dir = require("luarocks.dir") 388 local dir = require("luarocks.dir")
513 local path = require("luarocks.path") 389 local path = require("luarocks.path")
514 local deps = require("luarocks.deps") 390 local vers = require("luarocks.vers")
515 391
516 if fs.is_dir(subdir) then 392 if fs.is_dir(subdir) then
517 for file in fs.dir(subdir) do 393 for file in fs.dir(subdir) do
@@ -521,7 +397,7 @@ local function collect_rockspecs(versions, paths, unnamed_paths, subdir)
521 local rock, version = path.parse_name(file) 397 local rock, version = path.parse_name(file)
522 398
523 if rock then 399 if rock then
524 if not versions[rock] or deps.compare_versions(version, versions[rock]) then 400 if not versions[rock] or vers.compare_versions(version, versions[rock]) then
525 versions[rock] = version 401 versions[rock] = version
526 paths[rock] = file 402 paths[rock] = file
527 end 403 end
@@ -566,135 +442,6 @@ function util.get_default_rockspec()
566 end 442 end
567end 443end
568 444
569-- from http://lua-users.org/wiki/SplitJoin
570-- by PhilippeLhoste
571function util.split_string(str, delim, maxNb)
572 -- Eliminate bad cases...
573 if string.find(str, delim) == nil then
574 return { str }
575 end
576 if maxNb == nil or maxNb < 1 then
577 maxNb = 0 -- No limit
578 end
579 local result = {}
580 local pat = "(.-)" .. delim .. "()"
581 local nb = 0
582 local lastPos
583 for part, pos in string.gmatch(str, pat) do
584 nb = nb + 1
585 result[nb] = part
586 lastPos = pos
587 if nb == maxNb then break end
588 end
589 -- Handle the last field
590 if nb ~= maxNb then
591 result[nb + 1] = string.sub(str, lastPos)
592 end
593 return result
594end
595
596--- Remove repeated entries from a path-style string.
597-- Example: given ("a;b;c;a;b;d", ";"), returns "a;b;c;d".
598-- @param list string: A path string (from $PATH or package.path)
599-- @param sep string: The separator
600function util.remove_path_dupes(list, sep)
601 assert(type(list) == "string")
602 assert(type(sep) == "string")
603 local parts = util.split_string(list, sep)
604 local final, entries = {}, {}
605 for _, part in ipairs(parts) do
606 part = part:gsub("//", "/")
607 if not entries[part] then
608 table.insert(final, part)
609 entries[part] = true
610 end
611 end
612 return table.concat(final, sep)
613end
614
615---
616-- Formats tables with cycles recursively to any depth.
617-- References to other tables are shown as values.
618-- Self references are indicated.
619-- The string returned is "Lua code", which can be procesed
620-- (in the case in which indent is composed by spaces or "--").
621-- Userdata and function keys and values are shown as strings,
622-- which logically are exactly not equivalent to the original code.
623-- This routine can serve for pretty formating tables with
624-- proper indentations, apart from printing them:
625-- io.write(table.show(t, "t")) -- a typical use
626-- Written by Julio Manuel Fernandez-Diaz,
627-- Heavily based on "Saving tables with cycles", PIL2, p. 113.
628-- @param t table: is the table.
629-- @param name string: is the name of the table (optional)
630-- @param indent string: is a first indentation (optional).
631-- @return string: the pretty-printed table
632function util.show_table(t, name, indent)
633 local cart -- a container
634 local autoref -- for self references
635
636 local function isemptytable(t) return next(t) == nil end
637
638 local function basicSerialize (o)
639 local so = tostring(o)
640 if type(o) == "function" then
641 local info = debug.getinfo(o, "S")
642 -- info.name is nil because o is not a calling level
643 if info.what == "C" then
644 return ("%q"):format(so .. ", C function")
645 else
646 -- the information is defined through lines
647 return ("%q"):format(so .. ", defined in (" .. info.linedefined .. "-" .. info.lastlinedefined .. ")" .. info.source)
648 end
649 elseif type(o) == "number" then
650 return so
651 else
652 return ("%q"):format(so)
653 end
654 end
655
656 local function addtocart (value, name, indent, saved, field)
657 indent = indent or ""
658 saved = saved or {}
659 field = field or name
660
661 cart = cart .. indent .. field
662
663 if type(value) ~= "table" then
664 cart = cart .. " = " .. basicSerialize(value) .. ";\n"
665 else
666 if saved[value] then
667 cart = cart .. " = {}; -- " .. saved[value] .. " (self reference)\n"
668 autoref = autoref .. name .. " = " .. saved[value] .. ";\n"
669 else
670 saved[value] = name
671 --if tablecount(value) == 0 then
672 if isemptytable(value) then
673 cart = cart .. " = {};\n"
674 else
675 cart = cart .. " = {\n"
676 for k, v in pairs(value) do
677 k = basicSerialize(k)
678 local fname = ("%s[%s]"):format(name, k)
679 field = ("[%s]"):format(k)
680 -- three spaces between levels
681 addtocart(v, fname, indent .. " ", saved, field)
682 end
683 cart = cart .. indent .. "};\n"
684 end
685 end
686 end
687 end
688
689 name = name or "__unnamed__"
690 if type(t) ~= "table" then
691 return name .. " = " .. basicSerialize(t)
692 end
693 cart, autoref = "", ""
694 addtocart(t, name, indent)
695 return cart .. autoref
696end
697
698function util.array_contains(tbl, value) 445function util.array_contains(tbl, value)
699 for _, v in ipairs(tbl) do 446 for _, v in ipairs(tbl) do
700 if v == value then 447 if v == value then
diff --git a/src/luarocks/validate.lua b/src/luarocks/validate.lua
deleted file mode 100644
index c4570aa4..00000000
--- a/src/luarocks/validate.lua
+++ /dev/null
@@ -1,159 +0,0 @@
1
2--- Sandboxed test of build/install of all packages in a repository (unfinished and disabled).
3local validate = {}
4package.loaded["luarocks.validate"] = validate
5
6local fs = require("luarocks.fs")
7local dir = require("luarocks.dir")
8local path = require("luarocks.path")
9local cfg = require("luarocks.cfg")
10local build = require("luarocks.build")
11local install = require("luarocks.install")
12local util = require("luarocks.util")
13
14util.add_run_function(validate)
15validate.help_summary = "Sandboxed test of build/install of all packages in a repository."
16
17validate.help = [[
18<argument>, if given, is a local repository pathname.
19]]
20
21local function save_settings(repo)
22 local protocol, path = dir.split_url(repo)
23 table.insert(cfg.rocks_servers, 1, protocol.."://"..path)
24 return {
25 root_dir = cfg.root_dir,
26 rocks_dir = cfg.rocks_dir,
27 deploy_bin_dir = cfg.deploy_bin_dir,
28 deploy_lua_dir = cfg.deploy_lua_dir,
29 deploy_lib_dir = cfg.deploy_lib_dir,
30 }
31end
32
33local function restore_settings(settings)
34 cfg.root_dir = settings.root_dir
35 cfg.rocks_dir = settings.rocks_dir
36 cfg.deploy_bin_dir = settings.deploy_bin_dir
37 cfg.deploy_lua_dir = settings.deploy_lua_dir
38 cfg.deploy_lib_dir = settings.deploy_lib_dir
39 cfg.variables.ROCKS_TREE = settings.rocks_dir
40 cfg.variables.SCRIPTS_DIR = settings.deploy_bin_dir
41 table.remove(cfg.rocks_servers, 1)
42end
43
44local function prepare_sandbox(file)
45 local root_dir = fs.make_temp_dir(file):gsub("/+$", "")
46 cfg.root_dir = root_dir
47 cfg.rocks_dir = path.rocks_dir(root_dir)
48 cfg.deploy_bin_dir = path.deploy_bin_dir(root_dir)
49 cfg.variables.ROCKS_TREE = cfg.rocks_dir
50 cfg.variables.SCRIPTS_DIR = cfg.deploy_bin_dir
51 return root_dir
52end
53
54local function validate_rockspec(file)
55 local ok, err, errcode = build.build_rockspec(file, true, "one")
56 if not ok then
57 util.printerr(err)
58 end
59 return ok, err, errcode
60end
61
62local function validate_src_rock(file)
63 local ok, err, errcode = build.build_rock(file, false, "one")
64 if not ok then
65 util.printerr(err)
66 end
67 return ok, err, errcode
68end
69
70local function validate_rock(file)
71 local ok, err, errcode = install.install_binary_rock(file, "one")
72 if not ok then
73 util.printerr(err)
74 end
75 return ok, err, errcode
76end
77
78function validate.command(flags, repo)
79 repo = repo or cfg.rocks_dir
80
81 util.printout("Verifying contents of "..repo)
82
83 local results = {
84 ok = {}
85 }
86 local settings = save_settings(repo)
87 local sandbox
88 if flags["quick"] then
89 sandbox = prepare_sandbox("luarocks_validate")
90 end
91 if not fs.exists(repo) then
92 return nil, repo.." is not a local repository."
93 end
94 for file in fs.dir(repo) do for _=1,1 do
95 if file == "manifest" or file == "index.html" then
96 break -- continue for
97 end
98 local pathname = fs.absolute_name(dir.path(repo, file))
99 if not flags["quick"] then
100 sandbox = prepare_sandbox(file)
101 end
102 local ok, err, errcode
103 util.printout()
104 util.printout("Verifying "..pathname)
105 if file:match("%.rockspec$") then
106 ok, err, errcode = validate_rockspec(pathname, "one")
107 elseif file:match("%.src%.rock$") then
108 ok, err, errcode = validate_src_rock(pathname)
109 elseif file:match("%.rock$") then
110 ok, err, errcode = validate_rock(pathname)
111 end
112 if ok then
113 table.insert(results.ok, {file=file} )
114 else
115 if not errcode then
116 errcode = "misc"
117 end
118 if not results[errcode] then
119 results[errcode] = {}
120 end
121 table.insert(results[errcode], {file=file, err=err} )
122 end
123 util.run_scheduled_functions()
124 if not flags["quick"] then
125 fs.delete(sandbox)
126 end
127 repeat until not fs.pop_dir()
128 end end
129 if flags["quick"] then
130 fs.delete(sandbox)
131 end
132 restore_settings(settings)
133 util.title("Results:")
134 util.printout("OK: "..tostring(#results.ok))
135 for _, entry in ipairs(results.ok) do
136 util.printout(entry.file)
137 end
138 for errcode, errors in pairs(results) do
139 if errcode ~= "ok" then
140 util.printout()
141 util.printout(errcode.." errors: "..tostring(#errors))
142 for _, entry in ipairs(errors) do
143 util.printout(entry.file, entry.err)
144 end
145 end
146 end
147
148 util.title("Summary:")
149 local total = 0
150 for errcode, errors in pairs(results) do
151 util.printout(errcode..": "..tostring(#errors))
152 total = total + #errors
153 end
154 util.printout("Total: "..total)
155 return true
156end
157
158
159return validate
diff --git a/src/luarocks/vers.lua b/src/luarocks/vers.lua
new file mode 100644
index 00000000..6ce5d738
--- /dev/null
+++ b/src/luarocks/vers.lua
@@ -0,0 +1,138 @@
1
2--- Dependency format 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.
12local vers = {}
13setmetatable(vers, { __index = require("luarocks.core.vers") })
14
15--- Check if rockspec format version satisfies version requirement.
16-- @param rockspec table: The rockspec table.
17-- @param version string: required version.
18-- @return boolean: true if rockspec format matches version or is newer, false otherwise.
19function vers.format_is_at_least(rockspec, version)
20 local rockspec_format = rockspec.rockspec_format or "1.0"
21 return vers.parse_version(rockspec_format) >= vers.parse_version(version)
22end
23
24local operators = {
25 ["=="] = "==",
26 ["~="] = "~=",
27 [">"] = ">",
28 ["<"] = "<",
29 [">="] = ">=",
30 ["<="] = "<=",
31 ["~>"] = "~>",
32 -- plus some convenience translations
33 [""] = "==",
34 ["="] = "==",
35 ["!="] = "~="
36}
37
38--- Consumes a constraint from a string, converting it to table format.
39-- For example, a string ">= 1.0, > 2.0" is converted to a table in the
40-- format {op = ">=", version={1,0}} and the rest, "> 2.0", is returned
41-- back to the caller.
42-- @param input string: A list of constraints in string format.
43-- @return (table, string) or nil: A table representing the same
44-- constraints and the string with the unused input, or nil if the
45-- input string is invalid.
46local function parse_constraint(input)
47 assert(type(input) == "string")
48
49 local no_upgrade, op, version, rest = input:match("^(@?)([<>=~!]*)%s*([%w%.%_%-]+)[%s,]*(.*)")
50 local _op = operators[op]
51 version = vers.parse_version(version)
52 if not _op then
53 return nil, "Encountered bad constraint operator: '"..tostring(op).."' in '"..input.."'"
54 end
55 if not version then
56 return nil, "Could not parse version from constraint: '"..input.."'"
57 end
58 return { op = _op, version = version, no_upgrade = no_upgrade=="@" and true or nil }, rest
59end
60
61--- Convert a list of constraints from string to table format.
62-- For example, a string ">= 1.0, < 2.0" is converted to a table in the format
63-- {{op = ">=", version={1,0}}, {op = "<", version={2,0}}}.
64-- Version tables use a metatable allowing later comparison through
65-- relational operators.
66-- @param input string: A list of constraints in string format.
67-- @return table or nil: A table representing the same constraints,
68-- or nil if the input string is invalid.
69function vers.parse_constraints(input)
70 assert(type(input) == "string")
71
72 local constraints, oinput, constraint = {}, input
73 while #input > 0 do
74 constraint, input = parse_constraint(input)
75 if constraint then
76 table.insert(constraints, constraint)
77 else
78 return nil, "Failed to parse constraint '"..tostring(oinput).."' with error: ".. input
79 end
80 end
81 return constraints
82end
83
84--- Convert a dependency from string to table format.
85-- For example, a string "foo >= 1.0, < 2.0"
86-- is converted to a table in the format
87-- {name = "foo", constraints = {{op = ">=", version={1,0}},
88-- {op = "<", version={2,0}}}}. Version tables use a metatable
89-- allowing later comparison through relational operators.
90-- @param dep string: A dependency in string format
91-- as entered in rockspec files.
92-- @return table or nil: A table representing the same dependency relation,
93-- or nil if the input string is invalid.
94function vers.parse_dep(dep)
95 assert(type(dep) == "string")
96
97 local name, rest = dep:match("^%s*([a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*(.*)")
98 if not name then return nil, "failed to extract dependency name from '"..tostring(dep).."'" end
99 local constraints, err = vers.parse_constraints(rest)
100 if not constraints then return nil, err end
101 return { name = name, constraints = constraints }
102end
103
104--- Convert a version table to a string.
105-- @param v table: The version table
106-- @param internal boolean or nil: Whether to display versions in their
107-- internal representation format or how they were specified.
108-- @return string: The dependency information pretty-printed as a string.
109function vers.show_version(v, internal)
110 assert(type(v) == "table")
111 assert(type(internal) == "boolean" or not internal)
112
113 return (internal
114 and table.concat(v, ":")..(v.revision and tostring(v.revision) or "")
115 or v.string)
116end
117
118--- Convert a dependency in table format to a string.
119-- @param dep table: The dependency in table format
120-- @param internal boolean or nil: Whether to display versions in their
121-- internal representation format or how they were specified.
122-- @return string: The dependency information pretty-printed as a string.
123function vers.show_dep(dep, internal)
124 assert(type(dep) == "table")
125 assert(type(internal) == "boolean" or not internal)
126
127 if #dep.constraints > 0 then
128 local pretty = {}
129 for _, c in ipairs(dep.constraints) do
130 table.insert(pretty, c.op .. " " .. vers.show_version(c.version, internal))
131 end
132 return dep.name.." "..table.concat(pretty, ", ")
133 else
134 return dep.name
135 end
136end
137
138return vers