From ba518aa110d2fe84f6163c785da9fc7ab4ea9eae Mon Sep 17 00:00:00 2001
From: Hisham Muhammad
Date: Thu, 22 Aug 2024 17:49:09 -0300
Subject: Teal: add generated modules
---
src/luarocks/admin/cache.lua | 89 ++++
src/luarocks/admin/cmd/add.lua | 135 +++++
src/luarocks/admin/cmd/make_manifest.lua | 55 ++
src/luarocks/admin/cmd/refresh_cache.lua | 36 ++
src/luarocks/admin/cmd/remove.lua | 96 ++++
src/luarocks/admin/index.lua | 190 +++++++
src/luarocks/build.lua | 487 +++++++++++++++++
src/luarocks/build/builtin.lua | 403 ++++++++++++++
src/luarocks/build/cmake.lua | 91 ++++
src/luarocks/build/command.lua | 51 ++
src/luarocks/build/make.lua | 107 ++++
src/luarocks/cmd.lua | 807 ++++++++++++++++++++++++++++
src/luarocks/cmd/build.lua | 211 ++++++++
src/luarocks/cmd/config.lua | 402 ++++++++++++++
src/luarocks/cmd/doc.lua | 158 ++++++
src/luarocks/cmd/download.lua | 61 +++
src/luarocks/cmd/init.lua | 230 ++++++++
src/luarocks/cmd/install.lua | 259 +++++++++
src/luarocks/cmd/lint.lua | 59 +++
src/luarocks/cmd/list.lua | 113 ++++
src/luarocks/cmd/make.lua | 179 +++++++
src/luarocks/cmd/new_version.lua | 237 +++++++++
src/luarocks/cmd/pack.lua | 41 ++
src/luarocks/cmd/path.lua | 88 ++++
src/luarocks/cmd/purge.lua | 79 +++
src/luarocks/cmd/remove.lua | 77 +++
src/luarocks/cmd/search.lua | 91 ++++
src/luarocks/cmd/show.lua | 341 ++++++++++++
src/luarocks/cmd/test.lua | 53 ++
src/luarocks/cmd/unpack.lua | 172 ++++++
src/luarocks/cmd/upload.lua | 144 +++++
src/luarocks/cmd/which.lua | 44 ++
src/luarocks/cmd/write_rockspec.lua | 423 +++++++++++++++
src/luarocks/config.lua | 38 ++
src/luarocks/core/dir.lua | 95 ++++
src/luarocks/core/manif.lua | 124 +++++
src/luarocks/core/path.lua | 151 ++++++
src/luarocks/core/persist.lua | 70 +++
src/luarocks/core/sysdetect.lua | 508 ++++++++++++++++++
src/luarocks/core/types/query.lua | 13 +
src/luarocks/core/types/result.lua | 14 +
src/luarocks/core/types/rockspec.lua | 78 +++
src/luarocks/core/util.lua | 334 ++++++++++++
src/luarocks/core/vers.lua | 211 ++++++++
src/luarocks/deplocks.lua | 111 ++++
src/luarocks/deps.lua | 877 +++++++++++++++++++++++++++++++
src/luarocks/dir.lua | 65 +++
src/luarocks/download.lua | 76 +++
src/luarocks/fetch.lua | 615 ++++++++++++++++++++++
src/luarocks/fetch/cvs.lua | 53 ++
src/luarocks/fetch/git.lua | 166 ++++++
src/luarocks/fetch/git_file.lua | 22 +
src/luarocks/fetch/git_http.lua | 29 +
src/luarocks/fetch/git_https.lua | 7 +
src/luarocks/fetch/git_ssh.lua | 35 ++
src/luarocks/fetch/hg.lua | 64 +++
src/luarocks/fetch/hg_http.lua | 27 +
src/luarocks/fetch/hg_https.lua | 8 +
src/luarocks/fetch/hg_ssh.lua | 8 +
src/luarocks/fetch/sscm.lua | 45 ++
src/luarocks/fetch/svn.lua | 63 +++
src/luarocks/fun.lua | 138 +++++
src/luarocks/loader.lua | 333 ++++++++++++
src/luarocks/manif.lua | 229 ++++++++
src/luarocks/manif/writer.lua | 459 ++++++++++++++++
src/luarocks/pack.lua | 188 +++++++
src/luarocks/path.lua | 254 +++++++++
src/luarocks/persist.lua | 275 ++++++++++
src/luarocks/queries.lua | 211 ++++++++
src/luarocks/remove.lua | 140 +++++
src/luarocks/repo_writer.lua | 52 ++
src/luarocks/repos.lua | 690 ++++++++++++++++++++++++
src/luarocks/require.lua | 2 +
src/luarocks/results.lua | 60 +++
src/luarocks/rockspecs.lua | 184 +++++++
src/luarocks/search.lua | 385 ++++++++++++++
src/luarocks/signing.lua | 48 ++
src/luarocks/test.lua | 110 ++++
src/luarocks/test/busted.lua | 55 ++
src/luarocks/test/command.lua | 55 ++
src/luarocks/tools/patch.lua | 746 ++++++++++++++++++++++++++
src/luarocks/tools/tar.lua | 210 ++++++++
src/luarocks/tools/zip.lua | 575 ++++++++++++++++++++
src/luarocks/type/manifest.lua | 92 ++++
src/luarocks/type/rockspec.lua | 261 +++++++++
src/luarocks/type_check.lua | 237 +++++++++
src/luarocks/upload/api.lua | 300 +++++++++++
src/luarocks/upload/multipart.lua | 117 +++++
src/luarocks/util.lua | 611 +++++++++++++++++++++
89 files changed, 16933 insertions(+)
create mode 100644 src/luarocks/admin/cache.lua
create mode 100644 src/luarocks/admin/cmd/add.lua
create mode 100644 src/luarocks/admin/cmd/make_manifest.lua
create mode 100644 src/luarocks/admin/cmd/refresh_cache.lua
create mode 100644 src/luarocks/admin/cmd/remove.lua
create mode 100644 src/luarocks/admin/index.lua
create mode 100644 src/luarocks/build.lua
create mode 100644 src/luarocks/build/builtin.lua
create mode 100644 src/luarocks/build/cmake.lua
create mode 100644 src/luarocks/build/command.lua
create mode 100644 src/luarocks/build/make.lua
create mode 100644 src/luarocks/cmd.lua
create mode 100644 src/luarocks/cmd/build.lua
create mode 100644 src/luarocks/cmd/config.lua
create mode 100644 src/luarocks/cmd/doc.lua
create mode 100644 src/luarocks/cmd/download.lua
create mode 100644 src/luarocks/cmd/init.lua
create mode 100644 src/luarocks/cmd/install.lua
create mode 100644 src/luarocks/cmd/lint.lua
create mode 100644 src/luarocks/cmd/list.lua
create mode 100644 src/luarocks/cmd/make.lua
create mode 100644 src/luarocks/cmd/new_version.lua
create mode 100644 src/luarocks/cmd/pack.lua
create mode 100644 src/luarocks/cmd/path.lua
create mode 100644 src/luarocks/cmd/purge.lua
create mode 100644 src/luarocks/cmd/remove.lua
create mode 100644 src/luarocks/cmd/search.lua
create mode 100644 src/luarocks/cmd/show.lua
create mode 100644 src/luarocks/cmd/test.lua
create mode 100644 src/luarocks/cmd/unpack.lua
create mode 100644 src/luarocks/cmd/upload.lua
create mode 100644 src/luarocks/cmd/which.lua
create mode 100644 src/luarocks/cmd/write_rockspec.lua
create mode 100644 src/luarocks/config.lua
create mode 100644 src/luarocks/core/dir.lua
create mode 100644 src/luarocks/core/manif.lua
create mode 100644 src/luarocks/core/path.lua
create mode 100644 src/luarocks/core/persist.lua
create mode 100644 src/luarocks/core/sysdetect.lua
create mode 100644 src/luarocks/core/types/query.lua
create mode 100644 src/luarocks/core/types/result.lua
create mode 100644 src/luarocks/core/types/rockspec.lua
create mode 100644 src/luarocks/core/util.lua
create mode 100644 src/luarocks/core/vers.lua
create mode 100644 src/luarocks/deplocks.lua
create mode 100644 src/luarocks/deps.lua
create mode 100644 src/luarocks/dir.lua
create mode 100644 src/luarocks/download.lua
create mode 100644 src/luarocks/fetch.lua
create mode 100644 src/luarocks/fetch/cvs.lua
create mode 100644 src/luarocks/fetch/git.lua
create mode 100644 src/luarocks/fetch/git_file.lua
create mode 100644 src/luarocks/fetch/git_http.lua
create mode 100644 src/luarocks/fetch/git_https.lua
create mode 100644 src/luarocks/fetch/git_ssh.lua
create mode 100644 src/luarocks/fetch/hg.lua
create mode 100644 src/luarocks/fetch/hg_http.lua
create mode 100644 src/luarocks/fetch/hg_https.lua
create mode 100644 src/luarocks/fetch/hg_ssh.lua
create mode 100644 src/luarocks/fetch/sscm.lua
create mode 100644 src/luarocks/fetch/svn.lua
create mode 100644 src/luarocks/fun.lua
create mode 100644 src/luarocks/loader.lua
create mode 100644 src/luarocks/manif.lua
create mode 100644 src/luarocks/manif/writer.lua
create mode 100644 src/luarocks/pack.lua
create mode 100644 src/luarocks/path.lua
create mode 100644 src/luarocks/persist.lua
create mode 100644 src/luarocks/queries.lua
create mode 100644 src/luarocks/remove.lua
create mode 100644 src/luarocks/repo_writer.lua
create mode 100644 src/luarocks/repos.lua
create mode 100644 src/luarocks/require.lua
create mode 100644 src/luarocks/results.lua
create mode 100644 src/luarocks/rockspecs.lua
create mode 100644 src/luarocks/search.lua
create mode 100644 src/luarocks/signing.lua
create mode 100644 src/luarocks/test.lua
create mode 100644 src/luarocks/test/busted.lua
create mode 100644 src/luarocks/test/command.lua
create mode 100644 src/luarocks/tools/patch.lua
create mode 100644 src/luarocks/tools/tar.lua
create mode 100644 src/luarocks/tools/zip.lua
create mode 100644 src/luarocks/type/manifest.lua
create mode 100644 src/luarocks/type/rockspec.lua
create mode 100644 src/luarocks/type_check.lua
create mode 100644 src/luarocks/upload/api.lua
create mode 100644 src/luarocks/upload/multipart.lua
create mode 100644 src/luarocks/util.lua
(limited to 'src')
diff --git a/src/luarocks/admin/cache.lua b/src/luarocks/admin/cache.lua
new file mode 100644
index 00000000..11bcc818
--- /dev/null
+++ b/src/luarocks/admin/cache.lua
@@ -0,0 +1,89 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local os = _tl_compat and _tl_compat.os or os; local string = _tl_compat and _tl_compat.string or string
+
+
+local cache = {}
+
+
+local fs = require("luarocks.fs")
+local cfg = require("luarocks.core.cfg")
+local dir = require("luarocks.dir")
+local util = require("luarocks.util")
+
+function cache.get_upload_server(server)
+ if not server then server = cfg.upload_server end
+ if not server then
+ return nil, nil, "No server specified and no default configured with upload_server."
+ end
+ return server, cfg.upload_servers and cfg.upload_servers[server]
+end
+
+function cache.get_server_urls(server, upload_server)
+ local download_url = server
+ local login_url = nil
+ if upload_server then
+ if upload_server.rsync then download_url = "rsync://" .. upload_server.rsync
+ elseif upload_server.http then download_url = "http://" .. upload_server.http
+ elseif upload_server.ftp then download_url = "ftp://" .. upload_server.ftp
+ end
+
+ if upload_server.ftp then login_url = "ftp://" .. upload_server.ftp
+ elseif upload_server.sftp then login_url = "sftp://" .. upload_server.sftp
+ end
+ end
+ return download_url, login_url
+end
+
+function cache.split_server_url(url, user, password)
+ local protocol, server_path = dir.split_url(url)
+ if protocol == "file" then
+ server_path = fs.absolute_name(server_path)
+ elseif server_path:match("@") then
+ local credentials
+ credentials, server_path = server_path:match("([^@]*)@(.*)")
+ if credentials:match(":") then
+ user, password = credentials:match("([^:]*):(.*)")
+ else
+ user = credentials
+ end
+ end
+ local local_cache = dir.path(cfg.local_cache, (server_path:gsub("[\\/]", "_")))
+ return local_cache, protocol, server_path, user, password
+end
+
+local function download_cache(protocol, server_path, user, password)
+ os.remove("index.html")
+
+ if protocol == "rsync" then
+ local srv, path = server_path:match("([^/]+)(/.+)")
+ return fs.execute(cfg.variables.RSYNC .. " " .. cfg.variables.RSYNCFLAGS .. " -e ssh " .. user .. "@" .. srv .. ":" .. path .. "/ ./")
+ elseif protocol == "file" then
+ return fs.copy_contents(server_path, ".")
+ else
+ local login_info = ""
+ if user then login_info = " --user=" .. user end
+ if password then login_info = login_info .. " --password=" .. password end
+ return fs.execute(cfg.variables.WGET .. " --no-cache -q -m -np -nd " .. protocol .. "://" .. server_path .. login_info)
+ end
+end
+
+function cache.refresh_local_cache(url, given_user, given_password)
+ local local_cache, protocol, server_path, user, password = cache.split_server_url(url, given_user, given_password)
+
+ local ok, err = fs.make_dir(local_cache)
+ if not ok then
+ return nil, "Failed creating local cache dir: " .. err
+ end
+
+ fs.change_dir(local_cache)
+
+ util.printout("Refreshing cache " .. local_cache .. "...")
+
+ ok = download_cache(protocol, server_path, user, password)
+ if not ok then
+ return nil, "Failed downloading cache."
+ end
+
+ return local_cache, protocol, server_path, user, password
+end
+
+return cache
diff --git a/src/luarocks/admin/cmd/add.lua b/src/luarocks/admin/cmd/add.lua
new file mode 100644
index 00000000..862a08a8
--- /dev/null
+++ b/src/luarocks/admin/cmd/add.lua
@@ -0,0 +1,135 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+
+
+local add = {}
+
+
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+local dir = require("luarocks.dir")
+local writer = require("luarocks.manif.writer")
+local fs = require("luarocks.fs")
+local cache = require("luarocks.admin.cache")
+local index = require("luarocks.admin.index")
+
+
+
+
+
+function add.add_to_parser(parser)
+ local cmd = parser:command("add", "Add a rock or rockspec to a rocks server.", util.see_also())
+
+ cmd:argument("rocks", "A local rockspec or rock file."):
+ args("+")
+
+ cmd:option("--server", "The server to use. If not given, the default server " ..
+ "set in the upload_server variable from the configuration file is used instead."):
+ target("add_server")
+ cmd:flag("--no-refresh", "Do not refresh the local cache prior to " ..
+ "generation of the updated manifest.")
+ cmd:flag("--index", "Produce an index.html file for the manifest. This " ..
+ "flag is automatically set if an index.html file already exists.")
+end
+
+local function zip_manifests()
+ for ver in util.lua_versions() do
+ local file = "manifest-" .. ver
+ local zip = file .. ".zip"
+ fs.delete(dir.path(fs.current_dir(), zip))
+ fs.zip(zip, file)
+ end
+end
+
+local function add_files_to_server(refresh, rockfiles, server, upload_server, do_index)
+
+ local download_url, login_url = cache.get_server_urls(server, upload_server)
+ local at = fs.current_dir()
+ local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url
+
+ local local_cache, protocol, server_path, user, password = refresh_fn(download_url, cfg.upload_user, cfg.upload_password)
+ if not local_cache then
+ return nil, protocol
+ end
+
+ if not login_url then
+ login_url = protocol .. "://" .. server_path
+ end
+
+ local ok, err = fs.change_dir(at)
+ if not ok then return nil, err end
+
+ local files = {}
+ for _, rockfile in ipairs(rockfiles) do
+ if fs.exists(rockfile) then
+ util.printout("Copying file " .. rockfile .. " to " .. local_cache .. "...")
+ local absolute = fs.absolute_name(rockfile)
+ fs.copy(absolute, local_cache, "read")
+ table.insert(files, dir.base_name(absolute))
+ else
+ util.printerr("File " .. rockfile .. " not found")
+ end
+ end
+ if #files == 0 then
+ return nil, "No files found"
+ end
+
+ local ok, err = fs.change_dir(local_cache)
+ if not ok then return nil, err end
+
+ util.printout("Updating manifest...")
+ writer.make_manifest(local_cache, "one", true)
+
+ zip_manifests()
+
+ if fs.exists("index.html") then
+ do_index = true
+ end
+
+ if do_index then
+ util.printout("Updating index.html...")
+ index.make_index(local_cache)
+ end
+
+ local login_info = ""
+ if user then login_info = " -u " .. user end
+ if password then login_info = login_info .. ":" .. password end
+ if not login_url:match("/$") then
+ login_url = login_url .. "/"
+ end
+
+ if do_index then
+ table.insert(files, "index.html")
+ end
+ table.insert(files, "manifest")
+ for ver in util.lua_versions() do
+ table.insert(files, "manifest-" .. ver)
+ table.insert(files, "manifest-" .. ver .. ".zip")
+ end
+
+
+
+ local cmd
+ if protocol == "rsync" then
+ local srv, path = server_path:match("([^/]+)(/.+)")
+ cmd = cfg.variables.RSYNC .. " " .. cfg.variables.RSYNCFLAGS .. " -e ssh " .. local_cache .. "/ " .. user .. "@" .. srv .. ":" .. path .. "/"
+ elseif protocol == "file" then
+ return fs.copy_contents(local_cache, server_path)
+ elseif upload_server and upload_server.sftp then
+ local part1, part2 = upload_server.sftp:match("^([^/]*)/(.*)$")
+ cmd = cfg.variables.SCP .. " " .. table.concat(files, " ") .. " " .. user .. "@" .. part1 .. ":/" .. part2
+ else
+ cmd = cfg.variables.CURL .. " " .. login_info .. " -T '{" .. table.concat(files, ",") .. "}' " .. login_url
+ end
+
+ util.printout(cmd)
+ return fs.execute(cmd)
+end
+
+function add.command(args)
+ local server, server_table, err = cache.get_upload_server(args.add_server or args.server)
+ if not server then return nil, err end
+ return add_files_to_server(not args.no_refresh, args.rocks, server, server_table, args.index)
+end
+
+
+return add
diff --git a/src/luarocks/admin/cmd/make_manifest.lua b/src/luarocks/admin/cmd/make_manifest.lua
new file mode 100644
index 00000000..b028f900
--- /dev/null
+++ b/src/luarocks/admin/cmd/make_manifest.lua
@@ -0,0 +1,55 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local string = _tl_compat and _tl_compat.string or string
+
+
+local make_manifest = {}
+
+
+local writer = require("luarocks.manif.writer")
+local index = require("luarocks.admin.index")
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+local deps = require("luarocks.deps")
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+
+
+
+
+
+function make_manifest.add_to_parser(parser)
+ local cmd = parser:command("make_manifest", "Compile a manifest file for a repository.", util.see_also())
+
+ cmd:argument("repository", "Local repository pathname."):
+ args("?")
+
+ cmd:flag("--local-tree", "If given, do not write versioned versions of the manifest file.\n" ..
+ "Use this when rebuilding the manifest of a local rocks tree.")
+ util.deps_mode_option(cmd)
+end
+
+
+
+
+function make_manifest.command(args)
+ local repo = args.repository or cfg.rocks_dir
+
+ util.printout("Making manifest for " .. repo)
+
+ if repo:match("/lib/luarocks") and not args.local_tree then
+ util.warning("This looks like a local rocks tree, but you did not pass --local-tree.")
+ end
+
+ local ok, err = writer.make_manifest(repo, deps.get_deps_mode(args), not args.local_tree)
+ if ok and not args.local_tree then
+ util.printout("Generating index.html for " .. repo)
+ index.make_index(repo)
+ end
+ if args.local_tree then
+ for luaver in util.lua_versions() do
+ fs.delete(dir.path(repo, "manifest-" .. luaver))
+ end
+ end
+ return ok, err
+end
+
+return make_manifest
diff --git a/src/luarocks/admin/cmd/refresh_cache.lua b/src/luarocks/admin/cmd/refresh_cache.lua
new file mode 100644
index 00000000..08e90bbc
--- /dev/null
+++ b/src/luarocks/admin/cmd/refresh_cache.lua
@@ -0,0 +1,36 @@
+
+
+local refresh_cache = {}
+
+
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+local cache = require("luarocks.admin.cache")
+
+
+
+
+
+function refresh_cache.add_to_parser(parser)
+ local cmd = parser:command("refresh_cache", "Refresh local cache of a remote rocks server.", util.see_also())
+
+ cmd:option("--from", "The server to use. If not given, the default server " ..
+ "set in the upload_server variable from the configuration file is used instead."):
+ argname("")
+end
+
+function refresh_cache.command(args)
+ local server, upload_server, err = cache.get_upload_server(args.server)
+ if not server then return nil, err end
+ local download_url = cache.get_server_urls(server, upload_server)
+
+ local ok, err = cache.refresh_local_cache(download_url, cfg.upload_user, cfg.upload_password)
+ if not ok then
+ return nil, err
+ else
+ return true
+ end
+end
+
+
+return refresh_cache
diff --git a/src/luarocks/admin/cmd/remove.lua b/src/luarocks/admin/cmd/remove.lua
new file mode 100644
index 00000000..eee761fa
--- /dev/null
+++ b/src/luarocks/admin/cmd/remove.lua
@@ -0,0 +1,96 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local string = _tl_compat and _tl_compat.string or string
+
+
+local admin_remove = {}
+
+
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+local dir = require("luarocks.dir")
+local writer = require("luarocks.manif.writer")
+local fs = require("luarocks.fs")
+local cache = require("luarocks.admin.cache")
+local index = require("luarocks.admin.index")
+
+
+
+
+
+function admin_remove.add_to_parser(parser)
+ local cmd = parser:command("remove", "Remove a rock or rockspec from a rocks server.", util.see_also())
+
+ cmd:argument("rocks", "A local rockspec or rock file."):
+ args("+")
+
+ cmd:option("--server", "The server to use. If not given, the default server " ..
+ "set in the upload_server variable from the configuration file is used instead.")
+ cmd:flag("--no-refresh", "Do not refresh the local cache prior to " ..
+ "generation of the updated manifest.")
+end
+
+local function remove_files_from_server(refresh, rockfiles, server, upload_server)
+
+ local download_url, login_url = cache.get_server_urls(server, upload_server)
+ local at = fs.current_dir()
+ local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url
+
+ local local_cache, protocol, server_path, user, password = refresh_fn(download_url, cfg.upload_user, cfg.upload_password)
+ if not local_cache then
+ return nil, protocol
+ end
+
+ local ok, err = fs.change_dir(at)
+ if not ok then return nil, err end
+
+ local nr_files = 0
+ for _, rockfile in ipairs(rockfiles) do
+ local basename = dir.base_name(rockfile)
+ local file = dir.path(local_cache, basename)
+ util.printout("Removing file " .. file .. "...")
+ fs.delete(file)
+ if not fs.exists(file) then
+ nr_files = nr_files + 1
+ else
+ util.printerr("Failed removing " .. file)
+ end
+ end
+ if nr_files == 0 then
+ return nil, "No files removed."
+ end
+
+ local ok, err = fs.change_dir(local_cache)
+ if not ok then return nil, err end
+
+ util.printout("Updating manifest...")
+ writer.make_manifest(local_cache, "one", true)
+ util.printout("Updating index.html...")
+ index.make_index(local_cache)
+
+ if protocol == "file" then
+ local cmd = cfg.variables.RSYNC .. " " .. cfg.variables.RSYNCFLAGS .. " --delete " .. local_cache .. "/ " .. server_path .. "/"
+ util.printout(cmd)
+ fs.execute(cmd)
+ return true
+ end
+
+ if protocol ~= "rsync" then
+ return nil, "This command requires 'rsync', check your configuration."
+ end
+
+ local srv, path = server_path:match("([^/]+)(/.+)")
+ local cmd = cfg.variables.RSYNC .. " " .. cfg.variables.RSYNCFLAGS .. " --delete -e ssh " .. local_cache .. "/ " .. user .. "@" .. srv .. ":" .. path .. "/"
+
+ util.printout(cmd)
+ fs.execute(cmd)
+
+ return true
+end
+
+function admin_remove.command(args)
+ local server, server_table, err = cache.get_upload_server(args.server)
+ if not server then return nil, err end
+ return remove_files_from_server(not args.no_refresh, args.rocks, server, server_table)
+end
+
+
+return admin_remove
diff --git a/src/luarocks/admin/index.lua b/src/luarocks/admin/index.lua
new file mode 100644
index 00000000..e2e2deff
--- /dev/null
+++ b/src/luarocks/admin/index.lua
@@ -0,0 +1,190 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local package = _tl_compat and _tl_compat.package or package; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+
+local index = {}
+
+
+local util = require("luarocks.util")
+local fs = require("luarocks.fs")
+local vers = require("luarocks.core.vers")
+local persist = require("luarocks.persist")
+local dir = require("luarocks.dir")
+local manif = require("luarocks.manif")
+
+
+
+
+
+local ext_url_target = ' target="_blank"'
+
+local index_header = [[
+
+
+
+Available rocks
+
+
+
+
+Available rocks
+
+Lua modules available from this location for use with LuaRocks:
+
+
+]]
+
+local index_package_begin = [[
+
+ $package - $summary
+ $detailed
+$externaldependencies
+latest sources $homepage | License: $license
+
|
+
+]]
+
+local index_package_end = [[
+ |
+ |
+]]
+
+local index_footer_begin = [[
+
+
+manifest file
+]]
+local index_manifest_ver = [[
+• Lua $VER manifest file (zip)
+]]
+local index_footer_end = [[
+
+
+
+]]
+
+function index.format_external_dependencies(rockspec)
+ if rockspec.external_dependencies then
+ local deplist = {}
+ local listed_set = {}
+ local plats = nil
+ for name, desc in util.sortedpairs(rockspec.external_dependencies) do
+ if name ~= "platforms" then
+ table.insert(deplist, name:lower())
+ listed_set[name] = true
+ else
+ plats = desc
+ end
+ end
+ if plats then
+ for plat, entries in util.sortedpairs(plats) do
+ for name, desc in util.sortedpairs(entries) do
+ if not listed_set[name] then
+ table.insert(deplist, name:lower() .. " (on " .. plat .. ")")
+ end
+ end
+ end
+ end
+ return 'External dependencies: ' .. table.concat(deplist, ', ') .. '
'
+ else
+ return ""
+ end
+end
+
+function index.make_index(repo)
+ if not fs.is_dir(repo) then
+ return nil, "Cannot access repository at " .. repo
+ end
+ local manifest = manif.load_manifest(repo)
+ local out = io.open(dir.path(repo, "index.html"), "w")
+
+ out:write(index_header)
+ for package, version_list in util.sortedpairs(manifest.repository) do
+ local latest_rockspec = nil
+ local output = index_package_begin
+ for version, data in util.sortedpairs(version_list, vers.compare_versions) do
+ local versions = {}
+ output = output .. version .. ': '
+ table.sort(data, function(a, b) return a.arch < b.arch end)
+ for _, item in ipairs(data) do
+ local file
+ if item.arch == 'rockspec' then
+ file = ("%s-%s.rockspec"):format(package, version)
+ if not latest_rockspec then latest_rockspec = file end
+ else
+ file = ("%s-%s.%s.rock"):format(package, version, item.arch)
+ end
+ table.insert(versions, '' .. item.arch .. '')
+ end
+ output = output .. table.concat(versions, ', ') .. '
'
+ end
+ output = output .. index_package_end
+ if latest_rockspec then
+ local rockspec = persist.load_into_table(dir.path(repo, latest_rockspec))
+ local descript = rockspec.description or {}
+ local vars = {
+ anchor = package,
+ package = rockspec.package,
+ original = rockspec.source.url,
+ summary = descript.summary or "",
+ detailed = descript.detailed or "",
+ license = descript.license or "N/A",
+ homepage = descript.homepage and ('| project homepage') or "",
+ externaldependencies = index.format_external_dependencies(rockspec),
+ }
+ vars.detailed = vars.detailed:gsub("\n\n", "
"):gsub("%s+", " ")
+ vars.detailed = vars.detailed:gsub("(https?://[a-zA-Z0-9%.%%-_%+%[%]=%?&/$@;:]+)", '%1')
+ output = output:gsub("$(%w+)", vars)
+ else
+ output = output:gsub("$anchor", package)
+ output = output:gsub("$package", package)
+ output = output:gsub("$(%w+)", "")
+ end
+ out:write(output)
+ end
+ out:write(index_footer_begin)
+ for ver in util.lua_versions() do
+ out:write((index_manifest_ver:gsub("$VER", ver)))
+ end
+ out:write(index_footer_end)
+ out:close()
+end
+
+return index
diff --git a/src/luarocks/build.lua b/src/luarocks/build.lua
new file mode 100644
index 00000000..63082077
--- /dev/null
+++ b/src/luarocks/build.lua
@@ -0,0 +1,487 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pairs = _tl_compat and _tl_compat.pairs or pairs; local pcall = _tl_compat and _tl_compat.pcall or pcall; local string = _tl_compat and _tl_compat.string or string
+local build = {Builder = {}, }
+
+
+
+
+
+
+local path = require("luarocks.path")
+local util = require("luarocks.util")
+local fun = require("luarocks.fun")
+local fetch = require("luarocks.fetch")
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+local deps = require("luarocks.deps")
+local cfg = require("luarocks.core.cfg")
+local vers = require("luarocks.core.vers")
+local repos = require("luarocks.repos")
+local repo_writer = require("luarocks.repo_writer")
+local deplocks = require("luarocks.deplocks")
+
+
+
+
+
+
+
+
+
+
+
+
+
+do
+
+
+
+ local function extract_from_rockspec(files)
+ for name, content in pairs(files) do
+ local fd = io.open(dir.path(fs.current_dir(), name), "w+")
+ fd:write(content)
+ fd:close()
+ end
+ end
+
+
+
+
+
+
+
+ function build.apply_patches(rockspec)
+
+ if not (rockspec.build.extra_files or rockspec.build.patches) then
+ return true
+ end
+
+ local fd = io.open(fs.absolute_name(".luarocks.patches.applied"), "r")
+ if fd then
+ fd:close()
+ return true
+ end
+
+ if rockspec.build.extra_files then
+ extract_from_rockspec(rockspec.build.extra_files)
+ end
+ if rockspec.build.patches then
+ extract_from_rockspec(rockspec.build.patches)
+ for patch, patchdata in util.sortedpairs(rockspec.build.patches) do
+ util.printout("Applying patch " .. patch .. "...")
+ local create_delete = rockspec:format_is_at_least("3.0")
+ local ok, err = fs.apply_patch(tostring(patch), patchdata, create_delete)
+ if not ok then
+ return nil, "Failed applying patch " .. patch
+ end
+ end
+ end
+
+ fd = io.open(fs.absolute_name(".luarocks.patches.applied"), "w")
+ if fd then
+ fd:close()
+ end
+ return true
+ end
+end
+
+local function check_macosx_deployment_target(rockspec)
+ local target = rockspec.build.macosx_deployment_target
+ local function patch_variable(var)
+ local rockspec_variables = rockspec.variables
+ if rockspec_variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then
+ rockspec_variables[var] = (rockspec_variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET=" .. target)
+ else
+ rockspec_variables[var] = "env MACOSX_DEPLOYMENT_TARGET=" .. target .. " " .. rockspec_variables[var]
+ end
+ end
+ if cfg.is_platform("macosx") and rockspec:format_is_at_least("3.0") and target then
+ local version = util.popen_read("sw_vers -productVersion")
+ if version:match("^%d+%.%d+%.%d+$") or version:match("^%d+%.%d+$") then
+ if vers.compare_versions(target, version) then
+ return nil, ("This rock requires Mac OSX %s, and you are running %s."):format(target, version)
+ end
+ end
+ patch_variable("CC")
+ patch_variable("LD")
+ end
+ return true
+end
+
+local function process_dependencies(rockspec, opts, cwd)
+ if not opts.build_only_deps then
+ local ok, err, errcode = deps.check_external_deps(rockspec, "build")
+ if err then
+ return nil, err, errcode
+ end
+ end
+
+ if opts.deps_mode == "none" then
+ return true
+ end
+
+ local deplock_dir = fs.exists(dir.path(cwd, "luarocks.lock")) and cwd or nil
+
+ if not opts.build_only_deps then
+ if next(rockspec.build_dependencies) then
+
+ local user_lua_version = cfg.lua_version
+ local running_lua_version = _VERSION:sub(5)
+
+ if running_lua_version ~= user_lua_version then
+
+
+
+
+
+
+ cfg.lua_version = running_lua_version
+ cfg.lua_modules_path = cfg.lua_modules_path:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version)
+ cfg.lib_modules_path = cfg.lib_modules_path:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version)
+ cfg.rocks_subdir = cfg.rocks_subdir:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version)
+ path.use_tree(cfg.root_dir)
+ end
+
+ local ok, err, errcode = deps.fulfill_dependencies(rockspec, "build_dependencies", "all", opts.verify, deplock_dir)
+
+ path.add_to_package_paths(cfg.root_dir)
+
+ if running_lua_version ~= user_lua_version then
+
+ cfg.lua_version = user_lua_version
+ cfg.lua_modules_path = cfg.lua_modules_path:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version)
+ cfg.lib_modules_path = cfg.lib_modules_path:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version)
+ cfg.rocks_subdir = cfg.rocks_subdir:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version)
+ path.use_tree(cfg.root_dir)
+ end
+
+ if err then
+ return nil, err, errcode
+ end
+ end
+ end
+
+ return deps.fulfill_dependencies(rockspec, "dependencies", opts.deps_mode, opts.verify, deplock_dir)
+end
+
+local function fetch_and_change_to_source_dir(rockspec, opts)
+ if opts.minimal_mode or opts.build_only_deps then
+ return true
+ end
+ if opts.need_to_fetch then
+ if opts.branch then
+ rockspec.source.branch = opts.branch
+ end
+ local oks, source_dir, errcode = fetch.fetch_sources(rockspec, true)
+ if not oks then
+ return nil, source_dir, errcode
+ end
+ local ok, err
+ ok, err = fs.change_dir(source_dir)
+ if not ok then
+ return nil, err
+ end
+ else
+ if rockspec.source.file then
+ local ok, err = fs.unpack_archive(rockspec.source.file)
+ if not ok then
+ return nil, err
+ end
+ end
+ local ok, err = fetch.find_rockspec_source_dir(rockspec, ".")
+ if not ok then
+ return nil, err
+ end
+ end
+ fs.change_dir(rockspec.source.dir)
+ return true
+end
+
+local function prepare_install_dirs(name, version)
+ local dirs = {
+ lua = { name = path.lua_dir(name, version), is_module_path = true, perms = "read" },
+ lib = { name = path.lib_dir(name, version), is_module_path = true, perms = "exec" },
+ bin = { name = path.bin_dir(name, version), is_module_path = false, perms = "exec" },
+ conf = { name = path.conf_dir(name, version), is_module_path = false, perms = "read" },
+ }
+
+ for _, d in pairs(dirs) do
+ local ok, err = fs.make_dir(d.name)
+ if not ok then
+ return nil, err
+ end
+ end
+
+ return dirs
+end
+
+local function run_build_driver(rockspec, no_install)
+ local btype = rockspec.build.type
+ if btype == "none" then
+ return true
+ end
+
+ if btype == "module" then
+ util.printout("Do not use 'module' as a build type. Use 'builtin' instead.")
+ btype = "builtin"
+ rockspec.build.type = btype
+ end
+ local driver
+ if cfg.accepted_build_types and not fun.contains(cfg.accepted_build_types, btype) then
+ return nil, "This rockspec uses the '" .. btype .. "' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration."
+ end
+ local pok, driver_str = pcall(require, "luarocks.build." .. btype)
+ if not (type(driver_str) == "table") then
+ return nil, "Failed initializing build back-end for build type '" .. btype .. "': " .. driver_str
+ else
+ driver = driver_str
+ end
+
+ if not driver.skip_lua_inc_lib_check then
+ local ok, err, errcode = deps.check_lua_incdir(rockspec.variables)
+ if not ok then
+ return nil, err, errcode
+ end
+
+ if cfg.link_lua_explicitly then
+ ok, err, errcode = deps.check_lua_libdir(rockspec.variables)
+ if not ok then
+ return nil, err, errcode
+ end
+ end
+ end
+
+ local ok, err = driver.run(rockspec, no_install)
+ if not ok then
+ return nil, "Build error: " .. err
+ end
+ return true
+end
+
+local install_files
+do
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ local function install_to(files, location, is_module_path, perms)
+ if not files then
+ return true
+ end
+ for k, file in pairs(files) do
+ local dest = location
+ local filename = dir.base_name(file)
+ if type(k) == "string" then
+ local modname = k
+ if is_module_path then
+ dest = dir.path(location, path.module_to_path(modname))
+ local ok, err = fs.make_dir(dest)
+ if not ok then return nil, err end
+ if filename:match("%.lua$") then
+ local basename = modname:match("([^.]+)$")
+ filename = basename .. ".lua"
+ end
+ else
+ dest = dir.path(location, dir.dir_name(modname))
+ local ok, err = fs.make_dir(dest)
+ if not ok then return nil, err end
+ filename = dir.base_name(modname)
+ end
+ else
+ local ok, err = fs.make_dir(dest)
+ if not ok then return nil, err end
+ end
+ local ok = fs.copy(file, dir.path(dest, filename), perms)
+ if not ok then
+ return nil, "Failed copying " .. file
+ end
+ end
+ return true
+ end
+
+ local function install_default_docs(name, version)
+ local patterns = { "readme", "license", "copying", ".*%.md" }
+ local dest = dir.path(path.install_dir(name, version), "doc")
+ local has_dir = false
+ for file in fs.dir() do
+ for _, pattern in ipairs(patterns) do
+ if file:lower():match("^" .. pattern) then
+ if not has_dir then
+ fs.make_dir(dest)
+ has_dir = true
+ end
+ fs.copy(file, dest, "read")
+ break
+ end
+ end
+ end
+ end
+
+ install_files = function(rockspec, dirs)
+ local name, version = rockspec.name, rockspec.version
+
+ if rockspec.build.install then
+ for k, d in pairs(dirs) do
+ local ok, err = install_to((rockspec.build.install)[k], d.name, d.is_module_path, d.perms)
+ if not ok then return nil, err end
+ end
+ end
+
+ local copy_directories = rockspec.build.copy_directories
+ local copying_default = false
+ if not copy_directories then
+ copy_directories = { "doc" }
+ copying_default = true
+ end
+
+ local any_docs = false
+ for _, copy_dir in ipairs(copy_directories) do
+ if fs.is_dir(copy_dir) then
+ local dest = dir.path(path.install_dir(name, version), copy_dir)
+ fs.make_dir(dest)
+ fs.copy_contents(copy_dir, dest)
+ any_docs = true
+ else
+ if not copying_default then
+ return nil, "Directory '" .. copy_dir .. "' not found"
+ end
+ end
+ end
+ if not any_docs then
+ install_default_docs(name, version)
+ end
+
+ return true
+ end
+end
+
+
+
+
+
+
+function build.build_rockspec(rockspec, opts, cwd)
+
+ cwd = cwd or dir.path(".")
+
+ if not rockspec.build then
+ if rockspec:format_is_at_least("3.0") then
+ rockspec.build = {
+ type = "builtin",
+ }
+ else
+ return nil, "Rockspec error: build table not specified"
+ end
+ end
+
+ if not rockspec.build.type then
+ if rockspec:format_is_at_least("3.0") then
+ rockspec.build.type = "builtin"
+ else
+ return nil, "Rockspec error: build type not specified"
+ end
+ end
+
+ local ok, err = fetch_and_change_to_source_dir(rockspec, opts)
+ if not ok then return nil, err end
+
+ if opts.pin then
+ deplocks.init(rockspec.name, ".")
+ end
+
+ ok, err = process_dependencies(rockspec, opts, cwd)
+ if not ok then return nil, err end
+
+ local name, version = rockspec.name, rockspec.version
+ if opts.build_only_deps then
+ if opts.pin then
+ deplocks.write_file()
+ end
+ return name, version
+ end
+
+ local dirs, err
+ local rollback
+ if not opts.no_install then
+ if repos.is_installed(name, version) then
+ repo_writer.delete_version(name, version, opts.deps_mode)
+ end
+
+ dirs, err = prepare_install_dirs(name, version)
+ if not dirs then return nil, err end
+
+ rollback = util.schedule_function(function()
+ fs.delete(path.install_dir(name, version))
+ fs.remove_dir_if_empty(path.versions_dir(name))
+ end)
+ end
+
+ ok, err = build.apply_patches(rockspec)
+ if not ok then return nil, err end
+
+ ok, err = check_macosx_deployment_target(rockspec)
+ if not ok then return nil, err end
+
+ ok, err = run_build_driver(rockspec, opts.no_install)
+ if not ok then return nil, err end
+
+ if opts.no_install then
+ fs.pop_dir()
+ if opts.need_to_fetch then
+ fs.pop_dir()
+ end
+ return name, version
+ end
+
+ ok, err = install_files(rockspec, dirs)
+ if not ok then return nil, err end
+
+ for _, d in pairs(dirs) do
+ fs.remove_dir_if_empty(d.name)
+ end
+
+ fs.pop_dir()
+ if opts.need_to_fetch then
+ fs.pop_dir()
+ end
+
+ if opts.pin then
+ deplocks.write_file()
+ end
+
+ fs.copy(rockspec.local_abs_filename, path.rockspec_file(name, version), "read")
+
+ local deplock_file = deplocks.get_abs_filename(name)
+ if deplock_file then
+ fs.copy(deplock_file, dir.path(path.install_dir(name, version), "luarocks.lock"), "read")
+ end
+
+ ok, err = repo_writer.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), opts.deps_mode, opts.namespace)
+ if not ok then return nil, err end
+
+ util.remove_scheduled_function(rollback)
+ rollback = util.schedule_function(function()
+ repo_writer.delete_version(name, version, opts.deps_mode)
+ end)
+
+ ok, err = repos.run_hook(rockspec, "post_install")
+ if not ok then return nil, err end
+
+ util.announce_install(rockspec)
+ util.remove_scheduled_function(rollback)
+ return name, version
+end
+
+return build
diff --git a/src/luarocks/build/builtin.lua b/src/luarocks/build/builtin.lua
new file mode 100644
index 00000000..24434fef
--- /dev/null
+++ b/src/luarocks/build/builtin.lua
@@ -0,0 +1,403 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local os = _tl_compat and _tl_compat.os or os; local package = _tl_compat and _tl_compat.package or package; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table; local _tl_table_unpack = unpack or table.unpack
+
+local builtin = {}
+
+
+
+
+
+
+
+
+
+
+
+
+builtin.skip_lua_inc_lib_check = true
+
+local dir_sep = package.config:sub(1, 1)
+
+local fs = require("luarocks.fs")
+local path = require("luarocks.path")
+local util = require("luarocks.util")
+local cfg = require("luarocks.core.cfg")
+local dir = require("luarocks.dir")
+local deps = require("luarocks.deps")
+
+local function autoextract_libs(external_dependencies, variables)
+ if not external_dependencies then
+ return nil, nil, nil
+ end
+ local libs = {}
+ local incdirs = {}
+ local libdirs = {}
+ for name, data in pairs(external_dependencies) do
+ if data.library then
+ table.insert(libs, data.library)
+ table.insert(incdirs, variables[name .. "_INCDIR"])
+ table.insert(libdirs, variables[name .. "_LIBDIR"])
+ end
+ end
+ return libs, incdirs, libdirs
+end
+
+do
+ local function get_cmod_name(file)
+ local fd = io.open(dir.path(fs.current_dir(), file), "r")
+ if not fd then return nil end
+ local data = fd:read("*a")
+ fd:close()
+ return (data:match("int%s+luaopen_([a-zA-Z0-9_]+)"))
+ end
+
+ local skiplist = {
+ ["spec"] = true,
+ [".luarocks"] = true,
+ ["lua_modules"] = true,
+ ["test.lua"] = true,
+ ["tests.lua"] = true,
+ }
+
+ function builtin.autodetect_modules(libs, incdirs, libdirs)
+ local modules = {}
+ local install
+ local copy_directories
+
+ local prefix = ""
+ for _, parent in ipairs({ "src", "lua", "lib" }) do
+ if fs.is_dir(parent) then
+ fs.change_dir(parent)
+ prefix = parent .. dir_sep
+ break
+ end
+ end
+
+ for _, file in ipairs(fs.find()) do
+ local base = file:match("^([^\\/]*)")
+ if not skiplist[base] then
+ local luamod = file:match("(.*)%.lua$")
+ if luamod then
+ modules[path.path_to_module(file)] = prefix .. file
+ else
+ local cmod = file:match("(.*)%.c$")
+ if cmod then
+ local modname = get_cmod_name(file) or path.path_to_module((file:gsub("%.c$", ".lua")))
+ modules[modname] = {
+ sources = prefix .. file,
+ libraries = libs,
+ incdirs = incdirs,
+ libdirs = libdirs,
+ }
+ end
+ end
+ end
+ end
+
+ if prefix ~= "" then
+ fs.pop_dir()
+ end
+
+ local bindir = (fs.is_dir(dir.path("src", "bin")) and dir.path("src", "bin")) or
+ (fs.is_dir("bin") and "bin")
+ if bindir then
+ install = { bin = {} }
+ for _, file in ipairs(fs.list_dir(bindir)) do
+ table.insert((install.bin), dir.path(bindir, file))
+ end
+ end
+
+ for _, directory in ipairs({ "doc", "docs", "samples", "tests" }) do
+ if fs.is_dir(directory) then
+ if not copy_directories then
+ copy_directories = {}
+ end
+ table.insert(copy_directories, directory)
+ end
+ end
+
+ return modules, install, copy_directories
+ end
+end
+
+
+
+
+local function execute(...)
+ io.stdout:write(table.concat({ ... }, " ") .. "\n")
+ return fs.execute(...)
+end
+
+
+
+
+
+function builtin.run(rockspec, no_install)
+ local compile_object
+ local compile_library
+ local compile_static_library
+
+ local build = rockspec.build
+ local variables = rockspec.variables
+ local checked_lua_h = false
+
+ for _, var in ipairs({ "CC", "CFLAGS", "LDFLAGS" }) do
+ variables[var] = variables[var] or os.getenv(var) or ""
+ end
+
+ local function add_flags(extras, flag, flags)
+ if flags then
+ if not (type(flags) == "table") then
+ flags = { tostring(flags) }
+ end
+ util.variable_substitutions(flags, variables)
+ for _, v in ipairs(flags) do
+ table.insert(extras, flag:format(v))
+ end
+ end
+ end
+
+ if cfg.is_platform("mingw32") then
+ compile_object = function(object, source, defines, incdirs)
+ local extras = {}
+ add_flags(extras, "-D%s", defines)
+ add_flags(extras, "-I%s", incdirs)
+ return execute(variables.CC .. " " .. variables.CFLAGS, "-c", "-o", object, "-I" .. variables.LUA_INCDIR, source, _tl_table_unpack(extras))
+ end
+ compile_library = function(library, objects, libraries, libdirs, name)
+ local extras = { _tl_table_unpack(objects) }
+ add_flags(extras, "-L%s", libdirs)
+ add_flags(extras, "-l%s", libraries)
+ extras[#extras + 1] = dir.path(variables.LUA_LIBDIR, variables.LUALIB)
+
+ if variables.CC == "clang" or variables.CC == "clang-cl" then
+ local exported_name = name:gsub("%.", "_")
+ exported_name = exported_name:match('^[^%-]+%-(.+)$') or exported_name
+ extras[#extras + 1] = string.format("-Wl,-export:luaopen_%s", exported_name)
+ else
+ extras[#extras + 1] = "-l" .. (variables.MSVCRT or "m")
+ end
+
+ local ok = execute(variables.LD .. " " .. variables.LDFLAGS .. " " .. variables.LIBFLAG, "-o", library, _tl_table_unpack(extras))
+ return ok
+ end
+
+
+
+
+
+
+
+
+
+ elseif cfg.is_platform("win32") then
+ compile_object = function(object, source, defines, incdirs)
+ local extras = {}
+ add_flags(extras, "-D%s", defines)
+ add_flags(extras, "-I%s", incdirs)
+ return execute(variables.CC .. " " .. variables.CFLAGS, "-c", "-Fo" .. object, "-I" .. variables.LUA_INCDIR, source, _tl_table_unpack(extras))
+ end
+ compile_library = function(library, objects, libraries, libdirs, name)
+ local extras = { _tl_table_unpack(objects) }
+ add_flags(extras, "-libpath:%s", libdirs)
+ add_flags(extras, "%s.lib", libraries)
+ local basename = dir.base_name(library):gsub(".[^.]*$", "")
+ local deffile = basename .. ".def"
+ local def = io.open(dir.path(fs.current_dir(), deffile), "w+")
+ local exported_name = name:gsub("%.", "_")
+ exported_name = exported_name:match('^[^%-]+%-(.+)$') or exported_name
+ def:write("EXPORTS\n")
+ def:write("luaopen_" .. exported_name .. "\n")
+ def:close()
+ local ok = execute(variables.LD, "-dll", "-def:" .. deffile, "-out:" .. library, dir.path(variables.LUA_LIBDIR, variables.LUALIB), _tl_table_unpack(extras))
+ local basedir = ""
+ if name:find("%.") then
+ basedir = name:gsub("%.%w+$", "\\")
+ basedir = basedir:gsub("%.", "\\")
+ end
+ local manifestfile = basedir .. basename .. ".dll.manifest"
+
+ if ok and fs.exists(manifestfile) then
+ ok = execute(variables.MT, "-manifest", manifestfile, "-outputresource:" .. basedir .. basename .. ".dll;2")
+ end
+ return ok
+ end
+
+
+
+
+
+
+ else
+ compile_object = function(object, source, defines, incdirs)
+ local extras = {}
+ add_flags(extras, "-D%s", defines)
+ add_flags(extras, "-I%s", incdirs)
+ return execute(variables.CC .. " " .. variables.CFLAGS, "-I" .. variables.LUA_INCDIR, "-c", source, "-o", object, _tl_table_unpack(extras))
+ end
+ compile_library = function(library, objects, libraries, libdirs)
+ local extras = { _tl_table_unpack(objects) }
+ add_flags(extras, "-L%s", libdirs)
+ if cfg.gcc_rpath then
+ add_flags(extras, "-Wl,-rpath,%s", libdirs)
+ end
+ add_flags(extras, "-l%s", libraries)
+ if cfg.link_lua_explicitly then
+ extras[#extras + 1] = "-L" .. variables.LUA_LIBDIR
+ extras[#extras + 1] = "-llua"
+ end
+ return execute(variables.LD .. " " .. variables.LDFLAGS .. " " .. variables.LIBFLAG, "-o", library, _tl_table_unpack(extras))
+ end
+ compile_static_library = function(library, objects, libraries, libdirs, name)
+ local ok = execute(variables.AR, "rc", library, _tl_table_unpack(objects))
+ if ok then
+ ok = execute(variables.RANLIB, library)
+ end
+ return ok
+ end
+ end
+
+ local ok, err
+ local lua_modules = {}
+ local lib_modules = {}
+ local luadir = path.lua_dir(rockspec.name, rockspec.version)
+ local libdir = path.lib_dir(rockspec.name, rockspec.version)
+
+ local autolibs, autoincdirs, autolibdirs = autoextract_libs(rockspec.external_dependencies, rockspec.variables)
+
+ if not build.modules then
+ if rockspec:format_is_at_least("3.0") then
+ local install, copy_directories
+ build.modules, install, copy_directories = builtin.autodetect_modules(autolibs, autoincdirs, autolibdirs)
+ build.install = build.install or install
+ build.copy_directories = build.copy_directories or copy_directories
+ else
+ return nil, "Missing build.modules table"
+ end
+ end
+
+ local compile_temp_dir
+
+ local mkdir_cache = {}
+ local function cached_make_dir(name)
+ if name == "" or mkdir_cache[name] then
+ return true
+ end
+ mkdir_cache[name] = true
+ return fs.make_dir(name)
+ end
+
+ for name, info in pairs(build.modules) do
+ local moddir = path.module_to_path(name)
+ if type(info) == "string" then
+ local ext = info:match("%.([^.]+)$")
+ if ext == "lua" then
+ local filename = dir.base_name(info)
+ if filename == "init.lua" and not name:match("%.init$") then
+ moddir = path.module_to_path(name .. ".init")
+ else
+ local basename = name:match("([^.]+)$")
+ filename = basename .. ".lua"
+ end
+ local dest = dir.path(luadir, moddir, filename)
+ lua_modules[info] = dest
+ else
+ info = { info }
+ end
+ end
+ if type(info) == "table" then
+ if not checked_lua_h then
+ local ok, err, errcode = deps.check_lua_incdir(rockspec.variables)
+ if not ok then
+ return nil, err, errcode
+ end
+
+ if cfg.link_lua_explicitly then
+ local ok, err, errcode = deps.check_lua_libdir(rockspec.variables)
+ if not ok then
+ return nil, err, errcode
+ end
+ end
+ checked_lua_h = true
+ end
+ local objects = {}
+ local sources = info.sources
+ if info[1] then sources = info end
+ if type(sources) == "string" then sources = { sources } end
+ if not (type(sources) == "table") then
+ return nil, "error in rockspec: module '" .. name .. "' entry has no 'sources' list"
+ end
+ for _, source in ipairs(sources) do
+ if not (type(source) == "string") then
+ return nil, "error in rockspec: module '" .. name .. "' does not specify source correctly."
+ end
+ local object = source:gsub("%.[^.]*$", "." .. cfg.obj_extension)
+ if not object then
+ object = source .. "." .. cfg.obj_extension
+ end
+ ok = compile_object(object, source, info.defines, info.incdirs or autoincdirs)
+ if not ok then
+ return nil, "Failed compiling object " .. object
+ end
+ table.insert(objects, object)
+ end
+
+ if not compile_temp_dir then
+ compile_temp_dir = fs.make_temp_dir("build-" .. rockspec.package .. "-" .. rockspec.version)
+ util.schedule_function(fs.delete, compile_temp_dir)
+ end
+
+ local module_name = name:match("([^.]*)$") .. "." .. util.matchquote(cfg.lib_extension)
+ if moddir ~= "" then
+ module_name = dir.path(moddir, module_name)
+ end
+
+ local build_name = dir.path(compile_temp_dir, module_name)
+ local build_dir = dir.dir_name(build_name)
+ cached_make_dir(build_dir)
+
+ lib_modules[build_name] = dir.path(libdir, module_name)
+ ok = compile_library(build_name, objects, info.libraries, info.libdirs or autolibdirs, name)
+ if not ok then
+ return nil, "Failed compiling module " .. module_name
+ end
+
+
+
+ if cached_make_dir(dir.dir_name(module_name)) then
+ fs.copy(build_name, module_name)
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+ end
+ end
+ if not no_install then
+ for _, mods in ipairs({ { tbl = lua_modules, perms = "read" }, { tbl = lib_modules, perms = "exec" } }) do
+ for name, dest in pairs(mods.tbl) do
+ cached_make_dir(dir.dir_name(dest))
+ ok, err = fs.copy(name, dest, mods.perms)
+ if not ok then
+ return nil, "Failed installing " .. name .. " in " .. dest .. ": " .. err
+ end
+ end
+ end
+ if fs.is_dir("lua") then
+ ok, err = fs.copy_contents("lua", luadir)
+ if not ok then
+ return nil, "Failed copying contents of 'lua' directory: " .. err
+ end
+ end
+ end
+ return true
+end
+
+return builtin
diff --git a/src/luarocks/build/cmake.lua b/src/luarocks/build/cmake.lua
new file mode 100644
index 00000000..57d7535c
--- /dev/null
+++ b/src/luarocks/build/cmake.lua
@@ -0,0 +1,91 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local io = _tl_compat and _tl_compat.io or io; local os = _tl_compat and _tl_compat.os or os; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string
+
+
+
+local cmake = {CMakeBuild = {Install = {}, }, }
+
+
+
+
+
+
+
+
+
+
+local fs = require("luarocks.fs")
+local util = require("luarocks.util")
+local cfg = require("luarocks.core.cfg")
+
+
+
+
+
+
+
+function cmake.run(rockspec, no_install)
+ local build = rockspec.build
+ local variables = build.variables or {}
+
+
+ variables.CMAKE_MODULE_PATH = os.getenv("CMAKE_MODULE_PATH")
+ variables.CMAKE_LIBRARY_PATH = os.getenv("CMAKE_LIBRARY_PATH")
+ variables.CMAKE_INCLUDE_PATH = os.getenv("CMAKE_INCLUDE_PATH")
+
+ util.variable_substitutions(variables, rockspec.variables)
+
+ local ok, err_msg = fs.is_tool_available(rockspec.variables.CMAKE, "CMake")
+ if not ok then
+ return nil, err_msg
+ end
+
+
+ local build_cmake = build.cmake
+ if type(build_cmake) == "string" then
+ local cmake_handler = assert((io.open(fs.current_dir() .. "/CMakeLists.txt", "w")))
+ cmake_handler:write(build.cmake)
+ cmake_handler:close()
+ end
+
+
+ local args = ""
+
+
+ if cfg.cmake_generator then
+ args = args .. ' -G"' .. cfg.cmake_generator .. '"'
+ elseif cfg.is_platform("windows") and cfg.target_cpu:match("x86_64$") then
+ args = args .. " -DCMAKE_GENERATOR_PLATFORM=x64"
+ end
+
+ for k, v in pairs(variables) do
+ args = args .. ' -D' .. k .. '="' .. tostring(v) .. '"'
+ end
+
+ if not fs.execute_string(rockspec.variables.CMAKE .. " -H. -Bbuild.luarocks " .. args) then
+ return nil, "Failed cmake."
+ end
+
+ local do_build, do_install
+ if rockspec:format_is_at_least("3.0") then
+ do_build = (build.build_pass == nil) and true or build.build_pass
+ do_install = (build.install_pass == nil) and true or build.install_pass
+ else
+ do_build = true
+ do_install = true
+ end
+
+ if do_build then
+ if not fs.execute_string(rockspec.variables.CMAKE .. " --build build.luarocks --config Release") then
+ return nil, "Failed building."
+ end
+ end
+ if do_install and not no_install then
+ if not fs.execute_string(rockspec.variables.CMAKE .. " --build build.luarocks --target install --config Release") then
+ return nil, "Failed installing."
+ end
+ end
+
+ return true
+end
+
+return cmake
diff --git a/src/luarocks/build/command.lua b/src/luarocks/build/command.lua
new file mode 100644
index 00000000..f795321b
--- /dev/null
+++ b/src/luarocks/build/command.lua
@@ -0,0 +1,51 @@
+
+
+
+
+local command = {CommandBuild = {Install = {}, }, }
+
+
+
+
+
+
+
+
+local fs = require("luarocks.fs")
+local util = require("luarocks.util")
+local cfg = require("luarocks.core.cfg")
+
+
+
+
+
+
+
+function command.run(rockspec, not_install)
+
+ local build = rockspec.build
+
+ util.variable_substitutions(build, rockspec.variables)
+
+ local env = {
+ CC = cfg.variables.CC,
+
+
+ }
+
+ if build.build_command then
+ util.printout(build.build_command)
+ if not fs.execute_env(env, build.build_command) then
+ return nil, "Failed building."
+ end
+ end
+ if build.install_command and not not_install then
+ util.printout(build.install_command)
+ if not fs.execute_env(env, build.install_command) then
+ return nil, "Failed installing."
+ end
+ end
+ return true
+end
+
+return command
diff --git a/src/luarocks/build/make.lua b/src/luarocks/build/make.lua
new file mode 100644
index 00000000..3110198c
--- /dev/null
+++ b/src/luarocks/build/make.lua
@@ -0,0 +1,107 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pairs = _tl_compat and _tl_compat.pairs or pairs; local table = _tl_compat and _tl_compat.table or table; local _tl_table_unpack = unpack or table.unpack
+
+
+
+local make = {MakeBuild = {Install = {}, }, }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local fs = require("luarocks.fs")
+local util = require("luarocks.util")
+local cfg = require("luarocks.core.cfg")
+
+
+
+
+
+
+
+
+
+
+
+
+
+local function make_pass(make_cmd, pass, target, variables)
+ local assignments = {}
+ for k, v in pairs(variables) do
+ table.insert(assignments, k .. "=" .. v)
+ end
+ if pass then
+ return fs.execute(make_cmd .. " " .. target, _tl_table_unpack(assignments))
+ else
+ return true
+ end
+end
+
+
+
+
+
+function make.run(rockspec, not_install)
+
+ local build = rockspec.build
+
+ if build.build_pass == nil then build.build_pass = true end
+ if build.install_pass == nil then build.install_pass = true end
+ build.build_variables = build.build_variables or {}
+ build.install_variables = build.install_variables or {}
+ build.build_target = build.build_target or ""
+ build.install_target = build.install_target or "install"
+ local makefile = build.makefile or cfg.makefile
+ if makefile then
+
+ build.build_target = "-f " .. makefile .. " " .. build.build_target
+ build.install_target = "-f " .. makefile .. " " .. build.install_target
+ end
+
+ if build.variables then
+ for var, val in pairs(build.variables) do
+ build.build_variables[var] = val
+ build.install_variables[var] = val
+ end
+ end
+
+ util.warn_if_not_used(build.build_variables, { CFLAGS = true }, "variable %s was not passed in build_variables")
+ util.variable_substitutions(build.build_variables, rockspec.variables)
+ util.variable_substitutions(build.install_variables, rockspec.variables)
+
+ local auto_variables = { "CC" }
+
+ for _, variable in ipairs(auto_variables) do
+ if not build.build_variables[variable] then
+ build.build_variables[variable] = rockspec.variables[variable]
+ end
+ if not build.install_variables[variable] then
+ build.install_variables[variable] = rockspec.variables[variable]
+ end
+ end
+
+
+ local make_cmd = cfg.make or rockspec.variables.MAKE
+
+ local ok = make_pass(make_cmd, build.build_pass, build.build_target, build.build_variables)
+ if not ok then
+ return nil, "Failed building."
+ end
+ if not not_install then
+ ok = make_pass(make_cmd, build.install_pass, build.install_target, build.install_variables)
+ if not ok then
+ return nil, "Failed installing."
+ end
+ end
+ return true
+end
+
+return make
diff --git a/src/luarocks/cmd.lua b/src/luarocks/cmd.lua
new file mode 100644
index 00000000..e9c81a0f
--- /dev/null
+++ b/src/luarocks/cmd.lua
@@ -0,0 +1,807 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local debug = _tl_compat and _tl_compat.debug or debug; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local loadfile = _tl_compat and _tl_compat.loadfile or loadfile; local os = _tl_compat and _tl_compat.os or os; local package = _tl_compat and _tl_compat.package or package; local pairs = _tl_compat and _tl_compat.pairs or pairs; local pcall = _tl_compat and _tl_compat.pcall or pcall; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table; local _tl_table_pack = table.pack or function(...) return { n = select("#", ...), ... } end; local _tl_table_unpack = unpack or table.unpack; local xpcall = _tl_compat and _tl_compat.xpcall or xpcall
+
+local cmd = {Module = {}, }
+
+
+
+
+
+
+
+
+
+
+
+local manif = require("luarocks.manif")
+local config = require("luarocks.config")
+local util = require("luarocks.util")
+local path = require("luarocks.path")
+local cfg = require("luarocks.core.cfg")
+local dir = require("luarocks.dir")
+local fun = require("luarocks.fun")
+local fs = require("luarocks.fs")
+local argparse = require("luarocks.vendor.argparse")
+
+
+
+
+
+
+
+
+
+
+
+
+
+local hc_ok, hardcoded = pcall(require, "luarocks.core.hardcoded")
+if not hc_ok then
+ hardcoded = {}
+end
+
+local program = util.this_program("luarocks")
+
+cmd.errorcodes = {
+ OK = 0,
+ UNSPECIFIED = 1,
+ PERMISSIONDENIED = 2,
+ CONFIGFILE = 3,
+ LOCK = 4,
+ CRASH = 99,
+}
+
+local function check_popen()
+ local popen_ok, popen_result = pcall(io.popen, "")
+ if popen_ok then
+ if popen_result then
+ popen_result:close()
+ end
+ else
+ io.stderr:write("Your version of Lua does not support io.popen,\n")
+ io.stderr:write("which is required by LuaRocks. Please check your Lua installation.\n")
+ os.exit(cmd.errorcodes.UNSPECIFIED)
+ end
+end
+
+local process_tree_args
+do
+ local function replace_tree(args, root, tree)
+ root = dir.normalize(root)
+ args.tree = root
+ path.use_tree(tree or root)
+ end
+
+ local function strip_trailing_slashes()
+ local cfg_root_dir = cfg.root_dir
+ if type(cfg_root_dir) == "string" then
+ cfg.root_dir = (cfg.root_dir):gsub("/+$", "")
+ else
+ (cfg.root_dir).root = (cfg.root_dir).root:gsub("/+$", "")
+ end
+ cfg.rocks_dir = cfg.rocks_dir:gsub("/+$", "")
+ cfg.deploy_bin_dir = cfg.deploy_bin_dir:gsub("/+$", "")
+ cfg.deploy_lua_dir = cfg.deploy_lua_dir:gsub("/+$", "")
+ cfg.deploy_lib_dir = cfg.deploy_lib_dir:gsub("/+$", "")
+ end
+
+ local function set_named_tree(args, name)
+ for _, tree in ipairs(cfg.rocks_trees) do
+ if type(tree) == "table" and name == tree.name then
+ if not tree.root then
+ return nil, "Configuration error: tree '" .. tree.name .. "' has no 'root' field."
+ end
+ replace_tree(args, tree.root, tree)
+ return true
+ end
+ end
+ return false
+ end
+
+ process_tree_args = function(args, project_dir)
+
+ if args.global then
+ local ok, err = set_named_tree(args, "system")
+ if not ok then
+ return nil, err
+ end
+ elseif args.tree then
+ local named = set_named_tree(args, args.tree)
+ if not named then
+ local root_dir = fs.absolute_name(args.tree)
+ replace_tree(args, root_dir)
+ if (args.deps_mode or cfg.deps_mode) ~= "order" then
+ table.insert(cfg.rocks_trees, 1, { name = "arg", root = root_dir })
+ end
+ end
+ elseif args["local"] then
+ if fs.is_superuser() then
+ return nil, "The --local flag is meant for operating in a user's home directory.\n" ..
+ "You are running as a superuser, which is intended for system-wide operation.\n" ..
+ "To force using the superuser's home, use --tree explicitly."
+ else
+ local ok, err = set_named_tree(args, "user")
+ if not ok then
+ return nil, err
+ end
+ end
+ elseif args.project_tree then
+ local tree = args.project_tree
+ table.insert(cfg.rocks_trees, 1, { name = "project", root = tree })
+ manif.load_rocks_tree_manifests()
+ path.use_tree(tree)
+ elseif project_dir then
+ local project_tree = project_dir .. "/lua_modules"
+ table.insert(cfg.rocks_trees, 1, { name = "project", root = project_tree })
+ manif.load_rocks_tree_manifests()
+ path.use_tree(project_tree)
+ elseif cfg.local_by_default then
+ local ok, err = set_named_tree(args, "user")
+ if not ok then
+ return nil, err
+ end
+ else
+ local trees = cfg.rocks_trees
+ path.use_tree(trees[#trees])
+ end
+
+ strip_trailing_slashes()
+
+ cfg.variables.ROCKS_TREE = cfg.rocks_dir
+ cfg.variables.SCRIPTS_DIR = cfg.deploy_bin_dir
+
+ return true
+ end
+end
+
+local function process_server_args(args)
+ if args.server then
+ local protocol, pathname = dir.split_url(args.server)
+ table.insert(cfg.rocks_servers, 1, protocol .. "://" .. pathname)
+ end
+
+ if args.dev then
+ for i, server in ipairs(cfg.rocks_servers) do
+ if type(server) == "string" then
+ cfg.rocks_servers[i] = dir.path(server, "dev")
+ else
+ for j, mirror in ipairs(server) do
+ server[j] = dir.path(mirror, "dev")
+ end
+ end
+ end
+ end
+
+ if args.only_server then
+ if args.dev then
+ return nil, "--only-server cannot be used with --dev"
+ end
+ if args.server then
+ return nil, "--only-server cannot be used with --server"
+ end
+ cfg.rocks_servers = { args.only_server }
+ end
+
+ return true
+end
+
+local function error_handler(err)
+ if not debug then
+ return err
+ end
+ local mode = "Arch.: " .. (cfg and cfg.arch or "unknown")
+ if package.config:sub(1, 1) == "\\" then
+ if cfg and cfg.fs_use_modules then
+ mode = mode .. " (fs_use_modules = true)"
+ end
+ end
+ if cfg and cfg.is_binary then
+ mode = mode .. " (binary)"
+ end
+ return debug.traceback("LuaRocks " .. cfg.program_version ..
+ " bug (please report at https://github.com/luarocks/luarocks/issues).\n" ..
+ mode .. "\n" .. err, 2)
+end
+
+
+
+
+local function die(message, exitcode)
+ assert(type(message) == "string", "bad error, expected string, got: " .. type(message))
+ assert(exitcode == nil or type(exitcode) == "number", "bad error, expected number, got: " .. type(exitcode) .. " - " .. tostring(exitcode))
+ util.printerr("\nError: " .. message)
+
+ local ok, err = xpcall(util.run_scheduled_functions, error_handler)
+ if not ok then
+ util.printerr("\nError: " .. err)
+ exitcode = cmd.errorcodes.CRASH
+ end
+
+ os.exit(exitcode or cmd.errorcodes.UNSPECIFIED)
+end
+
+local function search_lua(lua_version, verbose, search_at)
+ if search_at then
+ return util.find_lua(search_at, lua_version, verbose)
+ end
+
+ local path_sep = (package.config:sub(1, 1) == "\\" and ";" or ":")
+ local all_tried = {}
+ for bindir in (os.getenv("PATH") or ""):gmatch("[^" .. path_sep .. "]+") do
+ local searchdir = (bindir:gsub("[\\/]+bin[\\/]?$", ""))
+ local detected, tried = util.find_lua(searchdir, lua_version)
+ if detected then
+ return detected
+ else
+ table.insert(all_tried, tried)
+ end
+ end
+ return nil, "Could not find " ..
+ (lua_version and "Lua " .. lua_version or "Lua") ..
+ " in PATH." ..
+ (verbose and " Tried:\n" .. table.concat(all_tried, "\n") or "")
+end
+
+local init_config
+do
+ local detect_config_via_args
+ do
+ local function find_project_dir(project_tree)
+ if project_tree then
+ return project_tree:gsub("[/\\][^/\\]+$", ""), true
+ else
+ local try = "."
+ for _ = 1, 10 do
+ if util.exists(try .. "/.luarocks") and util.exists(try .. "/lua_modules") then
+ return dir.normalize(try), false
+ elseif util.exists(try .. "/.luarocks-no-project") then
+ break
+ end
+ try = try .. "/.."
+ end
+ end
+ return nil
+ end
+
+ local function find_default_lua_version(args, project_dir)
+ if hardcoded.FORCE_CONFIG then
+ return nil
+ end
+
+ local dirs = {}
+ if project_dir then
+ table.insert(dirs, dir.path(project_dir, ".luarocks"))
+ end
+ if cfg.homeconfdir then
+ table.insert(dirs, cfg.homeconfdir)
+ end
+ table.insert(dirs, cfg.sysconfdir)
+ for _, d in ipairs(dirs) do
+ local f = dir.path(d, "default-lua-version.lua")
+ local mod, _ = loadfile(f, "t")
+ if mod then
+ local pok, ver = pcall(mod)
+ if pok and type(ver) == "string" and ver:match("%d+.%d+") then
+ if args.verbose then
+ util.printout("Defaulting to Lua " .. ver .. " based on " .. f .. " ...")
+ end
+ return ver
+ end
+ end
+ end
+ return nil
+ end
+
+ local function find_version_from_config(dirname)
+ return fun.find(util.lua_versions("descending"), function(v)
+ if util.exists(dir.path(dirname, ".luarocks", "config-" .. v .. ".lua")) then
+ return v
+ end
+ end)
+ end
+
+ local function detect_lua_via_args(args, project_dir)
+ local lua_version = args.lua_version or
+ find_default_lua_version(args, project_dir) or
+ (project_dir and find_version_from_config(project_dir))
+
+ if args.lua_dir then
+ local detected, err = util.find_lua(args.lua_dir, lua_version)
+ if not detected then
+ local suggestion = (not args.lua_version) and
+ "\nYou may want to specify a different Lua version with --lua-version\n" or
+ ""
+ die(err .. suggestion)
+ end
+ return detected
+ end
+
+ if lua_version then
+ local detected = search_lua(lua_version)
+ if detected then
+ return detected
+ end
+ return {
+ lua_version = lua_version,
+ }
+ end
+
+ return {}
+ end
+
+ detect_config_via_args = function(args)
+ local project_dir, given
+ if not args.no_project then
+ project_dir, given = find_project_dir(args.project_tree)
+ end
+
+ local detected = detect_lua_via_args(args, project_dir)
+ if args.lua_version then
+ detected.given_lua_version = args.lua_version
+ end
+ if args.lua_dir then
+ detected.given_lua_dir = args.lua_dir
+ end
+ if given then
+ detected.given_project_dir = project_dir
+ end
+ detected.project_dir = project_dir
+ return detected
+ end
+ end
+
+ init_config = function(args)
+ local detected = detect_config_via_args(args)
+
+ local ok, err = cfg.init(detected, util.warning)
+ if not ok then
+ return nil, err
+ end
+
+ return (detected.lua_dir ~= nil)
+ end
+end
+
+local variables_help = [[
+Variables:
+ Variables from the "variables" table of the configuration file can be
+ overridden with VAR=VALUE assignments.
+
+]]
+
+local lua_example = package.config:sub(1, 1) == "\\" and
+"" or
+""
+
+local function show_status(file, status, err)
+ return (file and file .. " " or "") .. (status and "(ok)" or ("(" .. (err or "not found") .. ")"))
+end
+
+local function use_to_fix_location(key, what)
+ local buf = " ****************************************\n"
+ buf = buf .. " Use the command\n\n"
+ buf = buf .. " luarocks config " .. key .. " " .. (what or "") .. "\n\n"
+ buf = buf .. " to fix the location\n"
+ buf = buf .. " ****************************************\n"
+ return buf
+end
+
+local function get_config_text(cfg)
+ local deps = require("luarocks.deps")
+
+ local libdir_ok = deps.check_lua_libdir(cfg.variables)
+ local incdir_ok = deps.check_lua_incdir(cfg.variables)
+ local lua_ok = cfg.variables.LUA and fs.exists(cfg.variables.LUA)
+
+ local buf = "Configuration:\n"
+ buf = buf .. " Lua:\n"
+ buf = buf .. " Version : " .. cfg.lua_version .. "\n"
+ if cfg.luajit_version then
+ buf = buf .. " LuaJIT : " .. cfg.luajit_version .. "\n"
+ end
+ buf = buf .. " LUA : " .. show_status(cfg.variables.LUA, lua_ok, "interpreter not found") .. "\n"
+ if not lua_ok then
+ buf = buf .. use_to_fix_location("variables.LUA", lua_example)
+ end
+ buf = buf .. " LUA_INCDIR : " .. show_status(cfg.variables.LUA_INCDIR, incdir_ok, "lua.h not found") .. "\n"
+ if lua_ok and not incdir_ok then
+ buf = buf .. use_to_fix_location("variables.LUA_INCDIR")
+ end
+ buf = buf .. " LUA_LIBDIR : " .. show_status(cfg.variables.LUA_LIBDIR, libdir_ok, "Lua library itself not found") .. "\n"
+ if lua_ok and not libdir_ok then
+ buf = buf .. use_to_fix_location("variables.LUA_LIBDIR")
+ end
+
+ buf = buf .. "\n Configuration files:\n"
+ local conf = cfg.config_files
+ buf = buf .. " System : " .. show_status(fs.absolute_name(conf.system.file), conf.system.found) .. "\n"
+ if conf.user.file then
+ buf = buf .. " User : " .. show_status(fs.absolute_name(conf.user.file), conf.user.found) .. "\n"
+ else
+ buf = buf .. " User : disabled in this LuaRocks installation.\n"
+ end
+ if conf.project then
+ buf = buf .. " Project : " .. show_status(fs.absolute_name(conf.project.file), conf.project.found) .. "\n"
+ end
+ buf = buf .. "\n Rocks trees in use: \n"
+ for _, tree in ipairs(cfg.rocks_trees) do
+ if type(tree) == "string" then
+ buf = buf .. " " .. fs.absolute_name(tree)
+ else
+ local name = tree.name and " (\"" .. tree.name .. "\")" or ""
+ buf = buf .. " " .. fs.absolute_name(tree.root) .. name
+ end
+ buf = buf .. "\n"
+ end
+
+ return buf
+end
+
+local function get_parser(description, cmd_modules)
+ local basename = dir.base_name(program)
+ local parser = argparse(
+ basename, "LuaRocks " .. cfg.program_version .. ", the Lua package manager\n\n" ..
+ program .. " - " .. description, variables_help .. "Run '" .. basename ..
+ "' without any arguments to see the configuration."):
+ help_max_width(80):
+ add_help_command():
+ add_complete_command({
+ help_max_width = 100,
+ summary = "Output a shell completion script.",
+ description = [[
+Output a shell completion script.
+
+Enabling completions for Bash:
+
+ Add the following line to your ~/.bashrc:
+ source <(]] .. basename .. [[ completion bash)
+ or save the completion script to the local completion directory:
+ ]] .. basename .. [[ completion bash > ~/.local/share/bash-completion/completions/]] .. basename .. [[
+
+
+Enabling completions for Zsh:
+
+ Save the completion script to a file in your $fpath.
+ You can add a new directory to your $fpath by adding e.g.
+ fpath=(~/.zfunc $fpath)
+ to your ~/.zshrc.
+ Then run:
+ ]] .. basename .. [[ completion zsh > ~/.zfunc/_]] .. basename .. [[
+
+
+Enabling completion for Fish:
+
+ Add the following line to your ~/.config/fish/config.fish:
+ ]] .. basename .. [[ completion fish | source
+ or save the completion script to the local completion directory:
+ ]] .. basename .. [[ completion fish > ~/.config/fish/completions/]] .. basename .. [[.fish
+]], }):
+ command_target("command"):
+ require_command(false)
+
+ parser:flag("--version", "Show version info and exit."):
+ action(function()
+ util.printout(program .. " " .. cfg.program_version)
+ util.printout(description)
+ util.printout()
+ os.exit(cmd.errorcodes.OK)
+ end)
+ parser:flag("--dev", "Enable the sub-repositories in rocks servers for " ..
+ "rockspecs of in-development versions.")
+ parser:option("--server", "Fetch rocks/rockspecs from this server " ..
+ "(takes priority over config file)."):
+ hidden_name("--from")
+ parser:option("--only-server", "Fetch rocks/rockspecs from this server only " ..
+ "(overrides any entries in the config file)."):
+ argname(""):
+ hidden_name("--only-from")
+ parser:option("--only-sources", "Restrict downloads to paths matching the given URL."):
+ argname(""):
+ hidden_name("--only-sources-from")
+ parser:option("--namespace", "Specify the rocks server namespace to use."):
+ convert(string.lower)
+ parser:option("--lua-dir", "Which Lua installation to use."):
+ argname("")
+ parser:option("--lua-version", "Which Lua version to use."):
+ argname(""):
+ convert(function(s) return (s:match("^%d+%.%d+$")) end)
+ parser:option("--tree", "Which tree to operate on."):
+ hidden_name("--to")
+ parser:flag("--local", "Use the tree in the user's home directory.\n" ..
+ "To enable it, see '" .. program .. " help path'.")
+ parser:flag("--global", "Use the system tree when `local_by_default` is `true`.")
+ parser:flag("--no-project", "Do not use project tree even if running from a project folder.")
+ parser:flag("--force-lock", "Attempt to overwrite the lock for commands " ..
+ "that require exclusive access, such as 'install'")
+ parser:flag("--verbose", "Display verbose output of commands executed.")
+ parser:option("--timeout", "Timeout on network operations, in seconds.\n" ..
+ "0 means no timeout (wait forever). Default is " ..
+ tostring(cfg.connection_timeout) .. "."):
+ argname(""):
+ convert(tonumber)
+
+
+ parser:option("--project-tree"):hidden(true)
+
+ for _, module in util.sortedpairs(cmd_modules) do
+ module.add_to_parser(parser)
+ end
+
+ return parser
+end
+
+local function get_first_arg()
+ if not arg then
+ return
+ end
+ local first_arg = arg[0]
+ local i = -1
+ while arg[i] do
+ first_arg = arg[i]
+ i = i - 1
+ end
+ return first_arg
+end
+
+
+
+
+
+
+
+
+
+function cmd.run_command(description, commands, external_namespace, ...)
+
+ check_popen()
+
+
+ cfg.init()
+
+ fs.init()
+
+ for _, module_name in ipairs(fs.modules(external_namespace)) do
+ if not commands[module_name] then
+ commands[module_name] = external_namespace .. "." .. module_name
+ end
+ end
+
+ local cmd_modules = {}
+ for name, module in pairs(commands) do
+ local pok, mod = pcall(require, module)
+ if pok and type(mod) == "table" then
+ local original_command = mod.command
+ if original_command then
+ if not mod.add_to_parser then
+ mod.add_to_parser = function(parser)
+ parser:command(name, mod.help, util.see_also()):
+ summary(mod.help_summary):
+ handle_options(false):
+ argument("input"):
+ args("*")
+ end
+
+ mod.command = function(args)
+ return original_command(args, _tl_table_unpack(args.input))
+ end
+ end
+ cmd_modules[name] = mod
+ else
+ util.warning("command module " .. module .. " does not implement command(), skipping")
+ end
+ else
+ util.warning("failed to load command module " .. module .. ": " .. tostring(mod))
+ end
+ end
+
+ local function process_cmdline_vars(...)
+ local args = _tl_table_pack(...)
+ local cmdline_vars = {}
+ local last = args.n
+ for i = 1, args.n do
+ if args[i] == "--" then
+ last = i - 1
+ break
+ end
+ end
+ for i = last, 1, -1 do
+ local arg = args[i]
+ if arg:match("^[^-][^=]*=") then
+ local var, val = arg:match("^([A-Z_][A-Z0-9_]*)=(.*)")
+ if val then
+ cmdline_vars[var] = val
+ table.remove(args, i)
+ else
+ die("Invalid assignment: " .. arg)
+ end
+ end
+ end
+
+ return args, cmdline_vars
+ end
+
+ local cmdline_args, cmdline_vars = process_cmdline_vars(...)
+ local parser = get_parser(description, cmd_modules)
+ local args = parser:parse(cmdline_args)
+
+
+ if args.nodeps then
+ args.deps_mode = "none"
+ end
+
+ if args.timeout then
+ cfg.connection_timeout = args.timeout
+ end
+
+ if args.command == "config" then
+ if args.key == "lua_version" and args.value then
+ args.lua_version = args.value
+ elseif args.key == "lua_dir" and args.value then
+ args.lua_dir = args.value
+ end
+ end
+
+
+ local lua_found, err = init_config(args)
+ if err then
+ die(err)
+ end
+
+
+
+
+ fs.init()
+
+
+
+ local tried
+ if not lua_found then
+ local detected
+ detected, tried = search_lua(cfg.lua_version, args.verbose, cfg.variables.LUA_DIR)
+ if detected then
+ lua_found = true
+ cfg.variables.LUA = detected.lua
+ cfg.variables.LUA_DIR = detected.lua_dir
+ cfg.variables.LUA_BINDIR = detected.lua_bindir
+ if args.lua_dir then
+ cfg.variables.LUA_INCDIR = nil
+ cfg.variables.LUA_LIBDIR = nil
+ end
+ else
+ cfg.variables.LUA = nil
+ cfg.variables.LUA_DIR = nil
+ cfg.variables.LUA_BINDIR = nil
+ cfg.variables.LUA_INCDIR = nil
+ cfg.variables.LUA_LIBDIR = nil
+ end
+ end
+
+ if lua_found then
+ assert(cfg.variables.LUA)
+ else
+
+
+
+
+ if not cfg.variables.LUA then
+ local first_arg = get_first_arg()
+ local bin_dir = dir.dir_name(fs.absolute_name(first_arg))
+ local exe = dir.base_name(first_arg)
+ exe = exe:match("rocks") and ("lua" .. (cfg.arch:match("win") and ".exe" or "")) or exe
+ local full_path = dir.path(bin_dir, exe)
+ if util.check_lua_version(full_path, cfg.lua_version) then
+ cfg.variables.LUA = dir.path(bin_dir, exe)
+ cfg.variables.LUA_DIR = bin_dir:gsub("[/\\]bin[/\\]?$", "")
+ cfg.variables.LUA_BINDIR = bin_dir
+ cfg.variables.LUA_INCDIR = nil
+ cfg.variables.LUA_LIBDIR = nil
+ end
+ end
+ end
+
+ cfg.lua_found = lua_found
+
+ if cfg.project_dir then
+ cfg.project_dir = fs.absolute_name(cfg.project_dir)
+ end
+
+ if args.verbose then
+ cfg.verbose = true
+ print(("-"):rep(79))
+ print("Current configuration:")
+ print(("-"):rep(79))
+ print(config.to_string(cfg))
+ print(("-"):rep(79))
+ fs.verbose()
+ end
+
+ if (not fs.current_dir()) or fs.current_dir() == "" then
+ die("Current directory does not exist. Please run LuaRocks from an existing directory.")
+ end
+
+ local ok, err = process_tree_args(args, cfg.project_dir)
+ if not ok then
+ die(err)
+ end
+
+ ok, err = process_server_args(args)
+ if not ok then
+ die(err)
+ end
+
+ if args.only_sources then
+ cfg.only_sources_from = args.only_sources
+ end
+
+ for k, v in pairs(cmdline_vars) do
+ cfg.variables[k] = v
+ end
+
+
+ if fs.is_superuser() then
+ cfg.local_cache = dir.path(fs.system_cache_dir(), "luarocks")
+ end
+
+ if args.no_manifest then
+ cfg.no_manifest = true
+ end
+
+ if not args.command then
+ parser:epilog(variables_help .. get_config_text(cfg))
+ util.printout()
+ util.printout(parser:get_help())
+ util.printout()
+ os.exit(cmd.errorcodes.OK)
+ end
+
+ if not cfg.variables["LUA"] and args.command ~= "config" and args.command ~= "help" then
+ local flag = (not cfg.project_tree) and
+ "--local " or
+ ""
+ if args.lua_version then
+ flag = "--lua-version=" .. args.lua_version .. " " .. flag
+ end
+ die((tried or "Lua interpreter not found.") ..
+ "\nPlease set your Lua interpreter with:\n\n" ..
+ " luarocks " .. flag .. "config variables.LUA " .. lua_example .. "\n")
+ end
+
+ local cmd_mod = cmd_modules[args.command]
+
+ local lock
+ if cmd_mod.needs_lock and cmd_mod.needs_lock(args) then
+ local ok, err = fs.check_command_permissions(args)
+ if not ok then
+ die(err, cmd.errorcodes.PERMISSIONDENIED)
+ end
+
+ lock, err = fs.lock_access(path.root_dir(cfg.root_dir), args.force_lock)
+ if not lock then
+ err = args.force_lock and
+ ("failed to force the lock" .. (err and ": " .. err or "")) or
+ (err and err ~= "File exists") and
+ err or
+ "try --force-lock to overwrite the lock"
+
+ die("command '" .. args.command .. "' " ..
+ "requires exclusive write access to " .. path.root_dir(cfg.root_dir) .. " - " ..
+ err, cmd.errorcodes.LOCK)
+ end
+ end
+
+ local call_ok, ok, err, exitcode = xpcall(function()
+ return cmd_mod.command(args)
+ end, error_handler)
+
+ if lock then
+ fs.unlock_access(lock)
+ end
+
+ if not call_ok then
+ die(tostring(ok), cmd.errorcodes.CRASH)
+ elseif not ok then
+ die(err, exitcode)
+ end
+ util.run_scheduled_functions()
+end
+
+return cmd
diff --git a/src/luarocks/cmd/build.lua b/src/luarocks/cmd/build.lua
new file mode 100644
index 00000000..fb894c20
--- /dev/null
+++ b/src/luarocks/cmd/build.lua
@@ -0,0 +1,211 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local string = _tl_compat and _tl_compat.string or string
+
+
+local cmd_build = {}
+
+
+
+local pack = require("luarocks.pack")
+local path = require("luarocks.path")
+local dir = require("luarocks.dir")
+local util = require("luarocks.util")
+local fetch = require("luarocks.fetch")
+local fs = require("luarocks.fs")
+local deps = require("luarocks.deps")
+local remove = require("luarocks.remove")
+local cfg = require("luarocks.core.cfg")
+local build = require("luarocks.build")
+local search = require("luarocks.search")
+local make = require("luarocks.cmd.make")
+local repos = require("luarocks.repos")
+
+
+
+
+
+
+
+
+
+function cmd_build.add_to_parser(parser)
+ local cmd = parser:command("build", "Build and install a rock, compiling its C parts if any.\n" ..
+ "If the sources contain a luarocks.lock file, uses it as an authoritative source for " ..
+ "exact version of dependencies.\n" ..
+ "If no arguments are given, behaves as luarocks make.", util.see_also()):
+ summary("Build/compile a rock.")
+
+ cmd:argument("rock", "A rockspec file, a source rock file, or the name of " ..
+ "a rock to be fetched from a repository."):
+ args("?"):
+ action(util.namespaced_name_action)
+ cmd:argument("version", "Rock version."):
+ args("?")
+
+ cmd:flag("--only-deps --deps-only", "Install only the dependencies of the rock.")
+ cmd:option("--branch", "Override the `source.branch` field in the loaded " ..
+ "rockspec. Allows to specify a different branch to fetch. Particularly " ..
+ 'for "dev" rocks.'):
+ argname("")
+ cmd:flag("--pin", "Create a luarocks.lock file listing the exact " ..
+ "versions of each dependency found for this rock (recursively), " ..
+ "and store it in the rock's directory. " ..
+ "Ignores any existing luarocks.lock file in the rock's sources.")
+ make.cmd_options(cmd)
+end
+
+
+
+
+
+
+local function build_rock(rock_filename, opts)
+
+ local cwd = fs.absolute_name(dir.path("."))
+
+ local ok, err, errcode
+
+ local unpack_dir
+ unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_filename, nil, opts.verify)
+ if not unpack_dir then
+ return nil, err, errcode
+ end
+
+ local rockspec_filename = path.rockspec_name_from_rock(rock_filename)
+
+ ok, err = fs.change_dir(unpack_dir)
+ if not ok then return nil, err end
+
+ local rockspec
+ rockspec, err, errcode = fetch.load_rockspec(rockspec_filename)
+ if not rockspec then
+ return nil, err, errcode
+ end
+
+ local n, v = build.build_rockspec(rockspec, opts, cwd)
+
+ ok, err, errcode = n ~= nil, v, nil
+
+ fs.pop_dir()
+ return ok, err, errcode
+end
+
+local function do_build(name, namespace, version, opts)
+
+ local url, err
+ if name:match("%.rockspec$") or name:match("%.rock$") then
+ url = name
+ else
+ url, err = search.find_src_or_rockspec(name, namespace, version, opts.check_lua_versions)
+ if not url then
+ return nil, err
+ end
+ end
+
+ name, version = path.parse_name(url)
+ if name and repos.is_installed(name, version) then
+ if not opts.rebuild then
+ util.printout(name .. " " .. version .. " is already installed in " .. path.root_dir(cfg.root_dir))
+ util.printout("Use --force to reinstall.")
+ return name, version, "skip"
+ end
+ end
+
+ if url:match("%.rockspec$") then
+ local cwd = fs.absolute_name(dir.path("."))
+ local rockspec, err = fetch.load_rockspec(url, nil, opts.verify)
+ if not rockspec then
+ return nil, err
+ end
+ return build.build_rockspec(rockspec, opts, cwd)
+ end
+
+ if url:match("%.src%.rock$") then
+ opts.need_to_fetch = false
+ end
+
+ local ok, err, errcode = build_rock(url, opts)
+ if not ok then
+ return nil, err, errcode
+ end
+ return name, version
+end
+
+
+
+
+
+
+
+function cmd_build.command(args)
+ if not args.rock then
+ return make.command(args)
+ end
+
+ local opts = {
+ need_to_fetch = true,
+ minimal_mode = false,
+ deps_mode = deps.get_deps_mode(args),
+ build_only_deps = not not (args.only_deps and not args.pack_binary_rock),
+ namespace = args.namespace,
+ branch = args.branch,
+ verify = not not args.verify,
+ check_lua_versions = not not args.check_lua_versions,
+ pin = not not args.pin,
+ rebuild = not not (args.force or args.force_fast),
+ no_install = false,
+ }
+
+ if args.sign and not args.pack_binary_rock then
+ return nil, "In the build command, --sign is meant to be used only with --pack-binary-rock"
+ end
+
+ if args.pack_binary_rock then
+ return pack.pack_binary_rock(args.rock, args.namespace, args.version, args.sign, function()
+ local name, version = do_build(args.rock, args.namespace, args.version, opts)
+ if name and args.no_doc then
+ util.remove_doc_dir(name, version)
+ end
+ return name, version
+ end)
+ end
+
+ local name, version, skip = do_build(args.rock, args.namespace, args.version, opts)
+ if not name then
+ return nil, version
+ end
+ if skip == "skip" then
+ return name ~= nil, version
+ end
+
+ if args.no_doc then
+ util.remove_doc_dir(name, version)
+ end
+
+ if opts.build_only_deps then
+ util.printout("Stopping after installing dependencies for " .. name .. " " .. version)
+ util.printout()
+ else
+ if (not args.keep) and not cfg.keep_other_versions then
+ local ok, err, warn = remove.remove_other_versions(name, version, args.force, args.force_fast)
+ if not ok then
+ return nil, err
+ elseif warn then
+ util.printerr(err)
+ end
+ end
+ end
+
+ if opts.deps_mode ~= "none" then
+ deps.check_dependencies(nil, deps.get_deps_mode(args))
+ end
+ return name ~= nil, version
+end
+
+cmd_build.needs_lock = function(args)
+ if args.pack_binary_rock then
+ return false
+ end
+ return true
+end
+
+return cmd_build
diff --git a/src/luarocks/cmd/config.lua b/src/luarocks/cmd/config.lua
new file mode 100644
index 00000000..b0b04913
--- /dev/null
+++ b/src/luarocks/cmd/config.lua
@@ -0,0 +1,402 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string
+
+local config_cmd = {}
+
+
+local persist = require("luarocks.persist")
+local config = require("luarocks.config")
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+local deps = require("luarocks.deps")
+local dir = require("luarocks.dir")
+local fs = require("luarocks.fs")
+local json = require("luarocks.vendor.dkjson")
+
+
+
+
+
+
+
+function config_cmd.add_to_parser(parser)
+ local cmd = parser:command("config", [[
+Query information about the LuaRocks configuration.
+
+* When given a configuration key, it prints the value of that key according to
+ the currently active configuration (taking into account all config files and
+ any command-line flags passed)
+
+ Examples:
+ luarocks config variables.LUA_INCDIR
+ luarocks config lua_version
+
+* When given a configuration key and a value, it overwrites the config file (see
+ the --scope option below to determine which) and replaces the value of the
+ given key with the given value.
+
+ * `lua_dir` is a special key as it checks for a valid Lua installation
+ (equivalent to --lua-dir) and sets several keys at once.
+ * `lua_version` is a special key as it changes the default Lua version
+ used by LuaRocks commands (equivalent to passing --lua-version).
+
+ Examples:
+ luarocks config variables.OPENSSL_DIR /usr/local/openssl
+ luarocks config lua_dir /usr/local
+ luarocks config lua_version 5.3
+
+* When given a configuration key and --unset, it overwrites the config file (see
+ the --scope option below to determine which) and deletes that key from the
+ file.
+
+ Example: luarocks config variables.OPENSSL_DIR --unset
+
+* When given no arguments, it prints the entire currently active configuration,
+ resulting from reading the config files from all scopes.
+
+ Example: luarocks config]], util.see_also([[
+ https://github.com/luarocks/luarocks/wiki/Config-file-format
+ for detailed information on the LuaRocks config file format.
+]])):
+ summary("Query information about the LuaRocks configuration.")
+
+ cmd:argument("key", "The configuration key."):
+ args("?")
+ cmd:argument("value", "The configuration value."):
+ args("?")
+
+ cmd:option("--scope", "The scope indicates which config file should be rewritten.\n" ..
+ '* Using a wrapper created with `luarocks init`, the default is "project".\n' ..
+ '* Using --local (or when `local_by_default` is `true`), the default is "user".\n' ..
+ '* Otherwise, the default is "system".'):
+ choices({ "system", "user", "project" })
+ cmd:flag("--unset", "Delete the key from the configuration file.")
+ cmd:flag("--json", "Output as JSON.")
+
+
+ cmd:flag("--lua-incdir"):hidden(true)
+ cmd:flag("--lua-libdir"):hidden(true)
+ cmd:flag("--lua-ver"):hidden(true)
+ cmd:flag("--system-config"):hidden(true)
+ cmd:flag("--user-config"):hidden(true)
+ cmd:flag("--rock-trees"):hidden(true)
+end
+
+local function config_file(conf)
+ print(dir.normalize(conf.file))
+ if conf.found then
+ return true
+ else
+ return nil, "file not found"
+ end
+end
+
+local function traverse_varstring(var, tbl, fn, missing_parent)
+ local k
+ local r
+ k, r = var:match("^%[([0-9]+)%]%.(.*)$")
+ if k then
+ k = tonumber(k)
+ else
+ k, r = var:match("^([^.[]+)%.(.*)$")
+ if not k then
+ k, r = var:match("^([^[]+)(%[.*)$")
+ end
+ end
+
+ if k then
+ if not tbl[k] and missing_parent then
+ missing_parent(tbl, k)
+ end
+
+ if tbl[k] then
+ return traverse_varstring(r, tbl[k], fn, missing_parent)
+ else
+ return nil, "Unknown entry " .. tostring(k)
+ end
+ end
+
+ local i = var:match("^%[([0-9]+)%]$")
+ if i then
+ return fn(tbl, tonumber(i))
+ end
+
+ return fn(tbl, var)
+end
+
+local function print_json(value)
+ print(json.encode(value))
+ return true
+end
+
+local function print_entry(var, tbl, is_json)
+ return traverse_varstring(var, tbl, function(t, k)
+ if not t[k] then
+ return nil, "Unknown entry " .. k
+ end
+ local val = t[k]
+
+ if not config.should_skip(var, val) then
+ if is_json then
+ return print_json(val)
+ elseif type(val) == "string" then
+ print(val)
+ else
+ persist.write_value(io.stdout, val)
+ end
+ end
+ return true
+ end)
+end
+
+local function infer_type(var)
+ local typ
+ traverse_varstring(var, cfg, function(t, k)
+ if t[k] then
+ typ = type(t[k])
+ end
+ end)
+ return typ
+end
+
+local function write_entries(keys, scope, do_unset)
+ local wrote = {}
+ if scope == "project" and not cfg.config_files.project then
+ return nil, "Current directory is not part of a project. You may want to run `luarocks init`."
+ end
+
+ local file_name = (cfg.config_files)[scope].file
+
+ local tbl, err = persist.load_config_file_if_basic(file_name, cfg)
+ if not tbl then
+ return nil, err
+ end
+
+ for var, val in util.sortedpairs(keys) do
+ traverse_varstring(var, tbl, function(t, k)
+ if do_unset then
+ t[k] = nil
+ wrote[var] = ""
+ else
+ local typ = infer_type(var)
+ local v
+ if typ == "number" and tonumber(val) then
+ v = tonumber(val)
+ elseif typ == "boolean" and val == "true" then
+ v = true
+ elseif typ == "boolean" and val == "false" then
+ v = false
+ else
+ v = val
+ end
+ t[k] = v
+ wrote[var] = v
+ end
+ return true
+ end, function(p, k)
+ p[k] = {}
+ end)
+ end
+
+ local ok, err = fs.make_dir(dir.dir_name(file_name))
+ if not ok then
+ return nil, err
+ end
+
+ ok, err = persist.save_from_table(file_name, tbl)
+ if ok then
+ print(do_unset and "Removed" or "Wrote")
+ for var, val in util.sortedpairs(wrote) do
+ if do_unset then
+ print(("\t%s"):format(var))
+ else
+ if type(val) == "string" then
+ print(("\t%s = %q"):format(var, val))
+ else
+ print(("\t%s = %s"):format(var, tostring(val)))
+ end
+ end
+ end
+ print(do_unset and "from" or "to")
+ print("\t" .. file_name)
+ return true
+ else
+ return nil, err
+ end
+end
+
+local function get_scope(args)
+ return args.scope or
+ (args["local"] and "user") or
+ (args.project_tree and "project") or
+ (cfg.local_by_default and "user") or
+ (fs.is_writable(cfg.config_files["system"].file) and "system") or
+ "user"
+end
+
+local function report_on_lua_incdir_config(value)
+ local variables = {
+ ["LUA_DIR"] = cfg.variables.LUA_DIR,
+ ["LUA_BINDIR"] = cfg.variables.LUA_BINDIR,
+ ["LUA_INCDIR"] = value,
+ ["LUA_LIBDIR"] = cfg.variables.LUA_LIBDIR,
+ ["LUA"] = cfg.variables.LUA,
+ }
+
+ local ok, err = deps.check_lua_incdir(variables)
+ if not ok then
+ util.printerr()
+ util.warning((err:gsub(" You can use.*", "")))
+ end
+ return ok
+end
+
+local function report_on_lua_libdir_config(value)
+ local variables = {
+ ["LUA_DIR"] = cfg.variables.LUA_DIR,
+ ["LUA_BINDIR"] = cfg.variables.LUA_BINDIR,
+ ["LUA_INCDIR"] = cfg.variables.LUA_INCDIR,
+ ["LUA_LIBDIR"] = value,
+ ["LUA"] = cfg.variables.LUA,
+ }
+
+ local ok, err, _, err_files = deps.check_lua_libdir(variables)
+ if not ok then
+ util.printerr()
+ util.warning((err:gsub(" You can use.*", "")))
+ util.printerr("Tried:")
+ for _, l in pairs(err_files or {}) do
+ for _, d in ipairs(l) do
+ util.printerr("\t" .. d)
+ end
+ end
+ end
+ return ok
+end
+
+local function warn_bad_c_config()
+ util.printerr()
+ util.printerr("LuaRocks may not work correctly when building C modules using this configuration.")
+ util.printerr()
+end
+
+
+
+function config_cmd.command(args)
+
+ deps.check_lua_incdir(cfg.variables)
+ deps.check_lua_libdir(cfg.variables)
+
+
+ if args.lua_incdir then
+ print(cfg.variables.LUA_INCDIR)
+ return true
+ end
+ if args.lua_libdir then
+ print(cfg.variables.LUA_LIBDIR)
+ return true
+ end
+ if args.lua_ver then
+ print(cfg.lua_version)
+ return true
+ end
+ if args.system_config then
+ return config_file(cfg.config_files.system)
+ end
+ if args.user_config then
+ return config_file(cfg.config_files.user)
+ end
+ if args.rock_trees then
+ for _, tree in ipairs(cfg.rocks_trees) do
+ if type(tree) == "string" then
+ util.printout(dir.normalize(tree))
+ else
+ local name = tree.name and "\t" .. tree.name or ""
+ util.printout(dir.normalize(tree.root) .. name)
+ end
+ end
+ return true
+ end
+
+ if args.key == "lua_version" and args.value then
+ local scope = get_scope(args)
+ if scope == "project" and not cfg.config_files.project then
+ return nil, "Current directory is not part of a project. You may want to run `luarocks init`."
+ end
+
+ local location = (cfg.config_files)[scope]
+ if (not location) or (not location.file) then
+ return nil, "could not get config file location for " .. tostring(scope) .. " scope"
+ end
+
+ local prefix = dir.dir_name(location.file)
+ local ok, err = persist.save_default_lua_version(prefix, args.value)
+ if not ok then
+ return nil, "could not set default Lua version: " .. err
+ end
+ print("Lua version will default to " .. args.value .. " in " .. prefix)
+ end
+
+ if args.key == "lua_dir" and args.value then
+ local scope = get_scope(args)
+ local keys = {
+ ["variables.LUA_DIR"] = cfg.variables.LUA_DIR,
+ ["variables.LUA_BINDIR"] = cfg.variables.LUA_BINDIR,
+ ["variables.LUA_INCDIR"] = cfg.variables.LUA_INCDIR,
+ ["variables.LUA_LIBDIR"] = cfg.variables.LUA_LIBDIR,
+ ["variables.LUA"] = cfg.variables.LUA,
+ }
+ if args.lua_version then
+ local prefix = dir.dir_name((cfg.config_files)[scope].file)
+ persist.save_default_lua_version(prefix, args.lua_version)
+ end
+ local ok, err = write_entries(keys, scope, args.unset)
+ if ok then
+ local inc_ok = report_on_lua_incdir_config(cfg.variables.LUA_INCDIR)
+ local lib_ok = ok and report_on_lua_libdir_config(cfg.variables.LUA_LIBDIR)
+ if not (inc_ok and lib_ok) then
+ warn_bad_c_config()
+ end
+ end
+
+ return ok, err
+ end
+
+ if args.key then
+ if args.key:match("^[A-Z]") then
+ args.key = "variables." .. args.key
+ end
+
+ if args.value or args.unset then
+ local scope = get_scope(args)
+
+ local ok, err = write_entries({ [args.key] = args.value or "" }, scope, args.unset)
+
+ if ok then
+ if args.key == "variables.LUA_INCDIR" then
+ local ok = report_on_lua_incdir_config(args.value)
+ if not ok then
+ warn_bad_c_config()
+ end
+ elseif args.key == "variables.LUA_LIBDIR" then
+ local ok = report_on_lua_libdir_config(args.value)
+ if not ok then
+ warn_bad_c_config()
+ end
+ end
+ end
+
+ return ok, err
+ else
+ return print_entry(args.key, cfg, args.json)
+ end
+ end
+
+ if args.json then
+ return print_json(config.get_config_for_display(cfg))
+ else
+ print(config.to_string(cfg))
+ return true
+ end
+end
+
+return config_cmd
diff --git a/src/luarocks/cmd/doc.lua b/src/luarocks/cmd/doc.lua
new file mode 100644
index 00000000..1389d80c
--- /dev/null
+++ b/src/luarocks/cmd/doc.lua
@@ -0,0 +1,158 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local string = _tl_compat and _tl_compat.string or string
+
+
+local doc = {}
+
+
+local util = require("luarocks.util")
+local queries = require("luarocks.queries")
+local search = require("luarocks.search")
+local path = require("luarocks.path")
+local dir = require("luarocks.dir")
+local fetch = require("luarocks.fetch")
+local fs = require("luarocks.fs")
+local download = require("luarocks.download")
+
+
+
+
+
+function doc.add_to_parser(parser)
+ local cmd = parser:command("doc", "Show documentation for an installed rock.\n\n" ..
+ "Without any flags, tries to load the documentation using a series of heuristics.\n" ..
+ "With flags, return only the desired information.", util.see_also([[
+ For more information about a rock, see the 'show' command.
+]])):
+ summary("Show documentation for an installed rock.")
+
+ cmd:argument("rock", "Name of the rock."):
+ action(util.namespaced_name_action)
+ cmd:argument("version", "Version of the rock."):
+ args("?")
+
+ cmd:flag("--home", "Open the home page of project.")
+ cmd:flag("--list", "List documentation files only.")
+ cmd:flag("--porcelain", "Produce machine-friendly output.")
+end
+
+local function show_homepage(homepage, name, namespace, version)
+ if not homepage then
+ return nil, "No 'homepage' field in rockspec for " .. util.format_rock_name(name, namespace, version)
+ end
+ util.printout("Opening " .. homepage .. " ...")
+ fs.browser(homepage)
+ return true
+end
+
+local function try_to_open_homepage(name, namespace, version)
+ local temp_dir, err = fs.make_temp_dir("doc-" .. name .. "-" .. (version or ""))
+ if not temp_dir then
+ return nil, "Failed creating temporary directory: " .. err
+ end
+ util.schedule_function(fs.delete, temp_dir)
+ local ok, err = fs.change_dir(temp_dir)
+ if not ok then return nil, err end
+ local filename, err = download.download_file("rockspec", name, namespace, version)
+ if not filename then return nil, err end
+ local rockspec, err = fetch.load_local_rockspec(filename)
+ if not rockspec then return nil, err end
+ fs.pop_dir()
+ local descript = rockspec.description or {}
+ return show_homepage(descript.homepage, name, namespace, version)
+end
+
+
+
+function doc.command(args)
+ local query = queries.new(args.rock, args.namespace, args.version)
+ local iname, iversion, repo = search.pick_installed_rock(query, args.tree)
+ if not iname then
+ local rock = util.format_rock_name(args.rock, args.namespace, args.version)
+ util.printout(rock .. " is not installed. Looking for it in the rocks servers...")
+ return try_to_open_homepage(args.rock, args.namespace, args.version)
+ end
+ local name, version = iname, iversion
+
+ local rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version, repo))
+ if not rockspec then return nil, err end
+ local descript = rockspec.description or {}
+
+ if args.home then
+ return show_homepage(descript.homepage, name, args.namespace, version)
+ end
+
+ local directory = path.install_dir(name, version, repo)
+
+ local docdir
+ local directories = { "doc", "docs" }
+ for _, d in ipairs(directories) do
+ local dirname = dir.path(directory, d)
+ if fs.is_dir(dirname) then
+ docdir = dirname
+ break
+ end
+ end
+ if not docdir then
+ if descript.homepage and not args.list then
+ util.printout("Local documentation directory not found -- opening " .. descript.homepage .. " ...")
+ fs.browser(descript.homepage)
+ return true
+ end
+ return nil, "Documentation directory not found for " .. name .. " " .. version
+ end
+
+ docdir = dir.normalize(docdir)
+ local files = fs.find(docdir)
+ local htmlpatt = "%.html?$"
+ local extensions = { htmlpatt, "%.md$", "%.txt$", "%.textile$", "" }
+ local basenames = { "index", "readme", "manual" }
+
+ local porcelain = args.porcelain
+ if #files > 0 then
+ util.title("Documentation files for " .. name .. " " .. version, porcelain)
+ if porcelain then
+ for _, file in ipairs(files) do
+ util.printout(docdir .. "/" .. file)
+ end
+ else
+ util.printout(docdir .. "/")
+ for _, file in ipairs(files) do
+ util.printout("\t" .. file)
+ end
+ end
+ end
+
+ if args.list then
+ return true
+ end
+
+ for _, extension in ipairs(extensions) do
+ for _, basename in ipairs(basenames) do
+ local filename = basename .. extension
+ local found
+ for _, file in ipairs(files) do
+ if file:lower():match(filename) and ((not found) or #file < #found) then
+ found = file
+ end
+ end
+ if found then
+ local pathname = dir.path(docdir, found)
+ util.printout()
+ util.printout("Opening " .. pathname .. " ...")
+ util.printout()
+ local ok = fs.browser(pathname)
+ if not ok and not pathname:match(htmlpatt) then
+ local fd = io.open(pathname, "r")
+ util.printout(fd:read("*a"))
+ fd:close()
+ end
+ return true
+ end
+ end
+ end
+
+ return true
+end
+
+
+return doc
diff --git a/src/luarocks/cmd/download.lua b/src/luarocks/cmd/download.lua
new file mode 100644
index 00000000..3be0e1ec
--- /dev/null
+++ b/src/luarocks/cmd/download.lua
@@ -0,0 +1,61 @@
+
+
+
+local cmd_download = {}
+
+
+local util = require("luarocks.util")
+local download = require("luarocks.download")
+
+
+
+
+
+function cmd_download.add_to_parser(parser)
+ local cmd = parser:command("download", "Download a specific rock file from a rocks server.", util.see_also())
+
+ cmd:argument("name", "Name of the rock."):
+ args("?"):
+ action(util.namespaced_name_action)
+ cmd:argument("version", "Version of the rock."):
+ args("?")
+
+ cmd:flag("--all", "Download all files if there are multiple matches.")
+ cmd:mutex(
+ cmd:flag("--source", "Download .src.rock if available."),
+ cmd:flag("--rockspec", "Download .rockspec if available."),
+ cmd:option("--arch", "Download rock for a specific architecture."))
+ cmd:flag("--check-lua-versions", "If the rock can't be found, check repository " ..
+ "and report if it is available for another Lua version.")
+end
+
+
+
+
+function cmd_download.command(args)
+ if not args.name and not args.all then
+ return nil, "Argument missing. " .. util.see_help("download")
+ end
+
+ args.name = args.name or ""
+
+ local arch
+
+ if args.source then
+ arch = "src"
+ elseif args.rockspec then
+ arch = "rockspec"
+ elseif args.arch then
+ arch = args.arch
+ end
+
+ if args.all then
+ local ok, err = download.download_all(arch, args.name, args.namespace, args.version)
+ return ok, err
+ else
+ local dl, err = download.download_file(arch, args.name, args.namespace, args.version, args.check_lua_versions)
+ return dl and true, err
+ end
+end
+
+return cmd_download
diff --git a/src/luarocks/cmd/init.lua b/src/luarocks/cmd/init.lua
new file mode 100644
index 00000000..9b124974
--- /dev/null
+++ b/src/luarocks/cmd/init.lua
@@ -0,0 +1,230 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+local init = {}
+
+
+
+local cfg = require("luarocks.core.cfg")
+local fs = require("luarocks.fs")
+local path = require("luarocks.path")
+local deps = require("luarocks.deps")
+local dir = require("luarocks.dir")
+local util = require("luarocks.util")
+local persist = require("luarocks.persist")
+local write_rockspec = require("luarocks.cmd.write_rockspec")
+
+
+
+
+
+
+
+
+
+
+function init.add_to_parser(parser)
+ local cmd = parser:command("init", "Initialize a directory for a Lua project using LuaRocks.", util.see_also())
+
+ cmd:argument("name", "The project name."):
+ args("?")
+ cmd:argument("version", "An optional project version."):
+ args("?")
+ cmd:option("--wrapper-dir", "Location where the 'lua' and 'luarocks' wrapper scripts " ..
+ "should be generated; if not given, the current directory is used as a default.")
+ cmd:flag("--reset", "Delete any .luarocks/config-5.x.lua and ./lua and generate new ones.")
+ cmd:flag("--no-wrapper-scripts", "Do not generate wrapper ./lua and ./luarocks launcher scripts.")
+ cmd:flag("--no-gitignore", "Do not generate a .gitignore file.")
+
+ cmd:group("Options for specifying rockspec data", write_rockspec.cmd_options(cmd))
+end
+
+local function gitignore_path(pwd, wrapper_dir, filename)
+ local norm_cur = fs.absolute_name(pwd)
+ local norm_file = fs.absolute_name(dir.path(wrapper_dir, filename))
+ if norm_file:sub(1, #norm_cur) == norm_cur then
+ return norm_file:sub(#norm_cur + 2)
+ else
+ return filename
+ end
+end
+
+local function write_gitignore(entries)
+ local gitignore = ""
+ local fd = io.open(".gitignore", "r")
+ if fd then
+ gitignore = fd:read("*a")
+ fd:close()
+ gitignore = "\n" .. gitignore .. "\n"
+ end
+
+ fd = io.open(".gitignore", gitignore and "a" or "w")
+ if fd then
+ for _, entry in ipairs(entries) do
+ entry = "/" .. entry
+ if not gitignore:find("\n" .. entry .. "\n", 1, true) then
+ fd:write(entry .. "\n")
+ end
+ end
+ fd:close()
+ end
+end
+
+local function inject_tree(tree)
+ path.use_tree(tree)
+ local tree_set = false
+ for _, t in ipairs(cfg.rocks_trees) do
+ if type(t) == "table" then
+ if t.name == "project" then
+ t.root = tree
+ tree_set = true
+ end
+ end
+ end
+ if not tree_set then
+ table.insert(cfg.rocks_trees, 1, { name = "project", root = tree })
+ end
+end
+
+local function write_wrapper_scripts(wrapper_dir, luarocks_wrapper, lua_wrapper)
+ local tree = dir.path(fs.current_dir(), "lua_modules")
+
+ fs.make_dir(wrapper_dir)
+
+ luarocks_wrapper = dir.path(wrapper_dir, luarocks_wrapper)
+ if not fs.exists(luarocks_wrapper) then
+ util.printout("Preparing " .. luarocks_wrapper .. " ...")
+ fs.wrap_script(arg[0], luarocks_wrapper, "none", nil, nil, "--project-tree", tree)
+ else
+ util.printout(luarocks_wrapper .. " already exists. Not overwriting it!")
+ end
+
+ lua_wrapper = dir.path(wrapper_dir, lua_wrapper)
+ local write_lua_wrapper = true
+ if fs.exists(lua_wrapper) then
+ if not util.lua_is_wrapper(lua_wrapper) then
+ util.printout(lua_wrapper .. " already exists and does not look like a wrapper script. Not overwriting.")
+ write_lua_wrapper = false
+ end
+ end
+
+ if write_lua_wrapper then
+ if util.check_lua_version(cfg.variables.LUA, cfg.lua_version) then
+ util.printout("Preparing " .. lua_wrapper .. " for version " .. cfg.lua_version .. "...")
+
+
+ inject_tree(tree)
+
+ fs.wrap_script(nil, lua_wrapper, "all")
+ else
+ util.warning("No Lua interpreter detected for version " .. cfg.lua_version .. ". Not creating " .. lua_wrapper)
+ end
+ end
+end
+
+
+
+function init.command(args)
+ local do_gitignore = not args.no_gitignore
+ local do_wrapper_scripts = not args.no_wrapper_scripts
+ local wrapper_dir = args.wrapper_dir or "."
+
+ local pwd = fs.current_dir()
+
+ if not args.name then
+ args.name = dir.base_name(pwd)
+ if args.name == "/" then
+ return nil, "When running from the root directory, please specify the argument"
+ end
+ end
+
+ util.title("Initializing project '" .. args.name .. "' for Lua " .. cfg.lua_version .. " ...")
+
+ local ok, err = deps.check_lua_incdir(cfg.variables)
+ if not ok then
+ return nil, err
+ end
+
+ local has_rockspec = false
+ for file in fs.dir() do
+ if file:match("%.rockspec$") then
+ has_rockspec = true
+ break
+ end
+ end
+
+ if not has_rockspec then
+ args.version = args.version or "dev"
+ args.location = pwd
+ local ok, err = write_rockspec.command(args)
+ if not ok then
+ util.printerr(err)
+ end
+ end
+
+ local ext = cfg.wrapper_suffix
+ local luarocks_wrapper = "luarocks" .. ext
+ local lua_wrapper = "lua" .. ext
+
+ if do_gitignore then
+ util.printout("Adding entries to .gitignore ...")
+ local ignores = { "lua_modules", ".luarocks" }
+ if do_wrapper_scripts then
+ table.insert(ignores, 1, gitignore_path(pwd, wrapper_dir, luarocks_wrapper))
+ table.insert(ignores, 2, gitignore_path(pwd, wrapper_dir, lua_wrapper))
+ end
+ write_gitignore(ignores)
+ end
+
+ util.printout("Preparing ./.luarocks/ ...")
+ fs.make_dir(".luarocks")
+ local config_file = ".luarocks/config-" .. cfg.lua_version .. ".lua"
+
+ if args.reset then
+ if do_wrapper_scripts then
+ fs.delete(fs.absolute_name(dir.path(wrapper_dir, lua_wrapper)))
+ end
+ fs.delete(fs.absolute_name(config_file))
+ end
+
+ local config_tbl, err = persist.load_config_file_if_basic(config_file, cfg)
+ if config_tbl then
+ local varnames = {
+ "LUA_DIR",
+ "LUA_INCDIR",
+ "LUA_LIBDIR",
+ "LUA_BINDIR",
+ "LUA",
+ }
+ for _, varname in ipairs(varnames) do
+ if cfg.variables[varname] then
+ config_tbl.variables = config_tbl.variables or {};
+ (config_tbl.variables)[varname] = cfg.variables[varname]
+ end
+ end
+ local ok, err = persist.save_from_table(config_file, config_tbl)
+ if ok then
+ util.printout("Wrote " .. config_file)
+ else
+ util.printout("Failed writing " .. config_file .. ": " .. err)
+ end
+ else
+ util.printout("Will not attempt to overwrite " .. config_file)
+ end
+
+ ok, err = persist.save_default_lua_version(".luarocks", cfg.lua_version)
+ if not ok then
+ util.printout("Failed setting default Lua version: " .. err)
+ end
+
+ util.printout("Preparing ./lua_modules/ ...")
+ fs.make_dir("lua_modules/lib/luarocks/rocks-" .. cfg.lua_version)
+
+ if do_wrapper_scripts then
+ write_wrapper_scripts(wrapper_dir, luarocks_wrapper, lua_wrapper)
+ end
+
+ return true
+end
+
+init.needs_lock = function() return true end
+
+return init
diff --git a/src/luarocks/cmd/install.lua b/src/luarocks/cmd/install.lua
new file mode 100644
index 00000000..e582f6e1
--- /dev/null
+++ b/src/luarocks/cmd/install.lua
@@ -0,0 +1,259 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local string = _tl_compat and _tl_compat.string or string
+
+local install = {}
+
+
+
+local dir = require("luarocks.dir")
+local path = require("luarocks.path")
+local repos = require("luarocks.repos")
+local fetch = require("luarocks.fetch")
+local util = require("luarocks.util")
+local fs = require("luarocks.fs")
+local deps = require("luarocks.deps")
+local repo_writer = require("luarocks.repo_writer")
+local remove = require("luarocks.remove")
+local search = require("luarocks.search")
+local queries = require("luarocks.queries")
+local cfg = require("luarocks.core.cfg")
+
+
+
+
+
+
+
+
+function install.add_to_parser(parser)
+ local cmd = parser:command("install", "Install a rock.", util.see_also())
+
+ cmd:argument("rock", "The name of a rock to be fetched from a repository " ..
+ "or a filename of a locally available rock."):
+ action(util.namespaced_name_action)
+ cmd:argument("version", "Version of the rock."):
+ args("?")
+
+ cmd:flag("--keep", "Do not remove previously installed versions of the " ..
+ "rock after building a new one. This behavior can be made permanent by " ..
+ "setting keep_other_versions=true in the configuration file.")
+ cmd:flag("--force", "If --keep is not specified, force removal of " ..
+ "previously installed versions if it would break dependencies. " ..
+ "If rock is already installed, reinstall it anyway.")
+ cmd:flag("--force-fast", "Like --force, but performs a forced removal " ..
+ "without reporting dependency issues.")
+ cmd:flag("--only-deps --deps-only", "Install only the dependencies of the rock.")
+ cmd:flag("--no-doc", "Install the rock without its documentation.")
+ cmd:flag("--verify", "Verify signature of the rockspec or src.rock being " ..
+ "built. If the rockspec or src.rock is being downloaded, LuaRocks will " ..
+ "attempt to download the signature as well. Otherwise, the signature " ..
+ "file should be already available locally in the same directory.\n" ..
+ "You need the signer’s public key in your local keyring for this " ..
+ "option to work properly.")
+ cmd:flag("--check-lua-versions", "If the rock can't be found, check repository " ..
+ "and report if it is available for another Lua version.")
+ util.deps_mode_option(cmd)
+ cmd:flag("--no-manifest", "Skip creating/updating the manifest")
+ cmd:flag("--pin", "If the installed rock is a Lua module, create a " ..
+ "luarocks.lock file listing the exact versions of each dependency found for " ..
+ "this rock (recursively), and store it in the rock's directory. " ..
+ "Ignores any existing luarocks.lock file in the rock's sources.")
+
+ parser:flag("--pack-binary-rock"):hidden(true)
+ parser:option("--branch"):hidden(true)
+ parser:flag("--sign"):hidden(true)
+end
+
+
+
+
+
+
+
+function install.install_binary_rock(rock_file, opts)
+
+ local namespace = opts.namespace
+ local deps_mode = opts.deps_mode
+
+ local name, version, arch = path.parse_name(rock_file)
+ if not name then
+ return nil, "Filename " .. rock_file .. " does not match format 'name-version-revision.arch.rock'."
+ end
+
+ if arch ~= "all" and arch ~= cfg.arch then
+ return nil, "Incompatible architecture " .. arch, "arch"
+ end
+ if repos.is_installed(name, version) then
+ if not (opts.force or opts.force_fast) then
+ util.printout(name .. " " .. version .. " is already installed in " .. path.root_dir(cfg.root_dir))
+ util.printout("Use --force to reinstall.")
+ return name, version
+ end
+ repo_writer.delete_version(name, version, opts.deps_mode)
+ end
+
+ local install_dir = path.install_dir(name, version)
+
+ local rollback = util.schedule_function(function()
+ fs.delete(install_dir)
+ fs.remove_dir_if_empty(path.versions_dir(name))
+ end)
+ local ok
+ local oks, err, errcode = fetch.fetch_and_unpack_rock(rock_file, install_dir, opts.verify)
+ if not oks then return nil, err, errcode end
+
+ local rockspec, err = fetch.load_rockspec(path.rockspec_file(name, version))
+ if err then
+ return nil, "Failed loading rockspec for installed package: " .. err, errcode
+ end
+
+ if opts.deps_mode ~= "none" then
+ ok, err, errcode = deps.check_external_deps(rockspec, "install")
+ if err then return nil, err, errcode end
+ end
+
+ if deps_mode ~= "none" then
+ local deplock_dir = fs.exists(dir.path(".", "luarocks.lock")) and
+ "." or
+ install_dir
+ ok, err, errcode = deps.fulfill_dependencies(rockspec, "dependencies", deps_mode, opts.verify, deplock_dir)
+ if err then return nil, err, errcode end
+ end
+
+ ok, err = repo_writer.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode, namespace)
+ if err then return nil, err end
+
+ util.remove_scheduled_function(rollback)
+ rollback = util.schedule_function(function()
+ repo_writer.delete_version(name, version, deps_mode)
+ end)
+
+ ok, err = repos.run_hook(rockspec, "post_install")
+ if err then return nil, err end
+
+ util.announce_install(rockspec)
+ util.remove_scheduled_function(rollback)
+ return name, version
+end
+
+
+
+
+
+
+
+function install.install_binary_rock_deps(rock_file, opts)
+
+ local name, version, arch = path.parse_name(rock_file)
+ if not name then
+ return nil, "Filename " .. rock_file .. " does not match format 'name-version-revision.arch.rock'."
+ end
+
+ if arch ~= "all" and arch ~= cfg.arch then
+ return nil, "Incompatible architecture " .. arch, "arch"
+ end
+
+ local install_dir = path.install_dir(name, version)
+
+ local ok
+ local oks, err, errcode = fetch.fetch_and_unpack_rock(rock_file, install_dir, opts.verify)
+ if not oks then return nil, err, errcode end
+
+ local rockspec, err = fetch.load_rockspec(path.rockspec_file(name, version))
+ if err then
+ return nil, "Failed loading rockspec for installed package: " .. err, errcode
+ end
+
+ local deplock_dir = fs.exists(dir.path(".", "luarocks.lock")) and
+ "." or
+ install_dir
+ ok, err, errcode = deps.fulfill_dependencies(rockspec, "dependencies", opts.deps_mode, opts.verify, deplock_dir)
+ if err then return nil, err, errcode end
+
+ util.printout()
+ util.printout("Successfully installed dependencies for " .. name .. " " .. version)
+
+ return name, version
+end
+
+local function install_rock_file_deps(filename, opts)
+
+ local name, version = install.install_binary_rock_deps(filename, opts)
+ if not name then return nil, version end
+
+ deps.check_dependencies(nil, opts.deps_mode)
+ return true
+end
+
+local function install_rock_file(filename, opts)
+
+ local name, version = install.install_binary_rock(filename, opts)
+ if not name then return nil, version end
+
+ if opts.no_doc then
+ util.remove_doc_dir(name, version)
+ end
+
+ if (not opts.keep) and not cfg.keep_other_versions then
+ local ok, err, warn = remove.remove_other_versions(name, version, opts.force, opts.force_fast)
+ if not ok then
+ return nil, err
+ elseif warn then
+ util.printerr(err)
+ end
+ end
+
+ deps.check_dependencies(nil, opts.deps_mode)
+ return true
+end
+
+
+
+
+
+
+
+
+
+function install.command(args)
+ if args.rock:match("%.rockspec$") or args.rock:match("%.src%.rock$") then
+ local build = require("luarocks.cmd.build")
+ return build.command(args)
+ elseif args.rock:match("%.rock$") then
+ local deps_mode = deps.get_deps_mode(args)
+ local opts = {
+ namespace = args.namespace,
+ keep = not not args.keep,
+ force = not not args.force,
+ force_fast = not not args.force_fast,
+ no_doc = not not args.no_doc,
+ deps_mode = deps_mode,
+ verify = not not args.verify,
+ }
+ if args.only_deps then
+ return install_rock_file_deps(args.rock, opts)
+ else
+ return install_rock_file(args.rock, opts)
+ end
+ else
+ local url, err = search.find_rock_checking_lua_versions(
+ queries.new(args.rock, args.namespace, args.version),
+ args.check_lua_versions)
+ if not url then
+ return nil, err
+ end
+ util.printout("Installing " .. url)
+ args.rock = url
+ return install.command(args)
+ end
+end
+
+install.needs_lock = function(args)
+ if args.pack_binary_rock then
+ return false
+ end
+ return true
+end
+
+deps.installer = install.command
+
+return install
diff --git a/src/luarocks/cmd/lint.lua b/src/luarocks/cmd/lint.lua
new file mode 100644
index 00000000..7a8443cc
--- /dev/null
+++ b/src/luarocks/cmd/lint.lua
@@ -0,0 +1,59 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local string = _tl_compat and _tl_compat.string or string
+
+
+local lint = {}
+
+
+local util = require("luarocks.util")
+local download = require("luarocks.download")
+local fetch = require("luarocks.fetch")
+
+
+
+
+
+function lint.add_to_parser(parser)
+ local cmd = parser:command("lint", "Check syntax of a rockspec.\n\n" ..
+ "Returns success if the text of the rockspec is syntactically correct, else failure.",
+ util.see_also()):
+ summary("Check syntax of a rockspec.")
+
+ cmd:argument("rockspec", "The rockspec to check.")
+end
+
+function lint.command(args)
+
+ local filename = args.rockspec
+ if not filename:match(".rockspec$") then
+ local err
+ filename, err = download.download_file("rockspec", filename:lower())
+ if not filename then
+ return nil, err
+ end
+ end
+
+ local rs, err = fetch.load_local_rockspec(filename)
+ if not rs then
+ return nil, "Failed loading rockspec: " .. err
+ end
+
+ local ok = true
+
+
+
+
+
+
+ if not rs.description or not rs.description.license then
+ util.printerr("Rockspec has no description.license field.")
+ ok = false
+ end
+
+ if ok then
+ return ok
+ end
+
+ return nil, filename .. " failed consistency checks."
+end
+
+return lint
diff --git a/src/luarocks/cmd/list.lua b/src/luarocks/cmd/list.lua
new file mode 100644
index 00000000..84fb6588
--- /dev/null
+++ b/src/luarocks/cmd/list.lua
@@ -0,0 +1,113 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+
+
+local list = {Outdated = {}, }
+
+
+
+
+
+
+
+
+local search = require("luarocks.search")
+local queries = require("luarocks.queries")
+local vers = require("luarocks.core.vers")
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+local path = require("luarocks.path")
+
+
+
+
+
+
+
+
+
+
+
+function list.add_to_parser(parser)
+ local cmd = parser:command("list", "List currently installed rocks.", util.see_also())
+
+ cmd:argument("filter", "A substring of a rock name to filter by."):
+ args("?")
+ cmd:argument("version", "Rock version to filter by."):
+ args("?")
+
+ cmd:flag("--outdated", "List only rocks for which there is a higher " ..
+ "version available in the rocks server.")
+ cmd:flag("--porcelain", "Produce machine-friendly output.")
+end
+
+local function check_outdated(trees, query)
+ local results_installed = {}
+ for _, tree in ipairs(trees) do
+ search.local_manifest_search(results_installed, path.rocks_dir(tree), query)
+ end
+ local outdated = {}
+ for name, versions in util.sortedpairs(results_installed) do
+ local versionsk = util.keys(versions)
+ table.sort(versionsk, vers.compare_versions)
+ local latest_installed = versionsk[1]
+
+ local query_available = queries.new(name:lower())
+ local results_available = search.search_repos(query_available)
+
+ if results_available[name] then
+ local available_versions = util.keys(results_available[name])
+ table.sort(available_versions, vers.compare_versions)
+ local latest_available = available_versions[1]
+ local latest_available_repo = results_available[name][latest_available][1].repo
+
+ if vers.compare_versions(latest_available, latest_installed) then
+ table.insert(outdated, { name = name, installed = latest_installed, available = latest_available, repo = latest_available_repo })
+ end
+ end
+ end
+ return outdated
+end
+
+local function list_outdated(trees, query, porcelain)
+ util.title("Outdated rocks:", porcelain)
+ local outdated = check_outdated(trees, query)
+ for _, item in ipairs(outdated) do
+ if porcelain then
+ util.printout(item.name, item.installed, item.available, item.repo)
+ else
+ util.printout(item.name)
+ util.printout(" " .. item.installed .. " < " .. item.available .. " at " .. item.repo)
+ util.printout()
+ end
+ end
+ return true
+end
+
+
+
+function list.command(args)
+ local query = queries.new(args.filter and args.filter:lower() or "", args.namespace, args.version, true)
+ local trees = cfg.rocks_trees
+ local title = "Rocks installed for Lua " .. cfg.lua_version
+ if args.tree then
+ trees = { args.tree }
+ title = title .. " in " .. args.tree
+ end
+
+ if args.outdated then
+ return list_outdated(trees, query, args.porcelain)
+ end
+
+ local results = {}
+ for _, tree in ipairs(trees) do
+ local ok, err, errcode = search.local_manifest_search(results, path.rocks_dir(tree), query)
+ if not ok and errcode ~= "open" then
+ util.warning(err)
+ end
+ end
+ util.title(title, args.porcelain)
+ search.print_result_tree(results, args.porcelain)
+ return true
+end
+
+return list
diff --git a/src/luarocks/cmd/make.lua b/src/luarocks/cmd/make.lua
new file mode 100644
index 00000000..e8a52906
--- /dev/null
+++ b/src/luarocks/cmd/make.lua
@@ -0,0 +1,179 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local string = _tl_compat and _tl_compat.string or string
+
+
+
+
+local make = {}
+
+
+
+local build = require("luarocks.build")
+local util = require("luarocks.util")
+local cfg = require("luarocks.core.cfg")
+local fetch = require("luarocks.fetch")
+local pack = require("luarocks.pack")
+local remove = require("luarocks.remove")
+local deps = require("luarocks.deps")
+local dir = require("luarocks.dir")
+local fs = require("luarocks.fs")
+
+
+
+
+
+
+
+function make.cmd_options(parser)
+ parser:flag("--no-install", "Do not install the rock.")
+ parser:flag("--no-doc", "Install the rock without its documentation.")
+ parser:flag("--pack-binary-rock", "Do not install rock. Instead, produce a " ..
+ ".rock file with the contents of compilation in the current directory.")
+ parser:flag("--keep", "Do not remove previously installed versions of the " ..
+ "rock after building a new one. This behavior can be made permanent by " ..
+ "setting keep_other_versions=true in the configuration file.")
+ parser:flag("--force", "If --keep is not specified, force removal of " ..
+ "previously installed versions if it would break dependencies. " ..
+ "If rock is already installed, reinstall it anyway.")
+ parser:flag("--force-fast", "Like --force, but performs a forced removal " ..
+ "without reporting dependency issues.")
+ parser:flag("--verify", "Verify signature of the rockspec or src.rock being " ..
+ "built. If the rockspec or src.rock is being downloaded, LuaRocks will " ..
+ "attempt to download the signature as well. Otherwise, the signature " ..
+ "file should be already available locally in the same directory.\n" ..
+ "You need the signer's public key in your local keyring for this " ..
+ "option to work properly.")
+ parser:flag("--sign", "To be used with --pack-binary-rock. Also produce a " ..
+ "signature file for the generated .rock file.")
+ parser:flag("--check-lua-versions", "If the rock can't be found, check repository " ..
+ "and report if it is available for another Lua version.")
+ parser:flag("--pin", "Pin the exact dependencies used for the rockspec" ..
+ "being built into a luarocks.lock file in the current directory.")
+ parser:flag("--no-manifest", "Skip creating/updating the manifest")
+ parser:flag("--only-deps --deps-only", "Install only the dependencies of the rock.")
+ util.deps_mode_option(parser)
+end
+
+function make.add_to_parser(parser)
+
+ local cmd = parser:command("make", [[
+Builds sources in the current directory, but unlike "build", it does not fetch
+sources, etc., assuming everything is available in the current directory. If no
+argument is given, it looks for a rockspec in the current directory and in
+"rockspec/" and "rockspecs/" subdirectories, picking the rockspec with newest
+version or without version name. If rockspecs for different rocks are found or
+there are several rockspecs without version, you must specify which to use,
+through the command-line.
+
+This command is useful as a tool for debugging rockspecs.
+To install rocks, you'll normally want to use the "install" and "build"
+commands. See the help on those for details.
+
+If the current directory contains a luarocks.lock file, it is used as the
+authoritative source for exact version of dependencies. The --pin flag
+overrides and recreates this file scanning dependency based on ranges.
+]], util.see_also()):
+ summary("Compile package in current directory using a rockspec.")
+
+
+ cmd:argument("rockspec", "Rockspec for the rock to build."):
+ args("?")
+
+ make.cmd_options(cmd)
+end
+
+
+
+
+function make.command(args)
+ local name, namespace, version
+ local rockspec_filename = args.rockspec
+ if not rockspec_filename then
+ local err
+ rockspec_filename, err = util.get_default_rockspec()
+ if not rockspec_filename then
+ return nil, err
+ end
+ end
+ if not rockspec_filename:match("rockspec$") then
+ return nil, "Invalid argument: 'make' takes a rockspec as a parameter. " .. util.see_help("make")
+ end
+
+ local cwd = fs.absolute_name(dir.path("."))
+ local rockspec, err, errcode = fetch.load_rockspec(rockspec_filename)
+ if not rockspec then
+ return nil, err
+ end
+
+ name, namespace = util.split_namespace(rockspec.name)
+ namespace = namespace or args.namespace
+
+ local opts = {
+ need_to_fetch = false,
+ minimal_mode = true,
+ deps_mode = deps.get_deps_mode(args),
+ build_only_deps = not not (args.only_deps and not args.pack_binary_rock),
+ namespace = namespace,
+ branch = args.branch,
+ verify = not not args.verify,
+ check_lua_versions = not not args.check_lua_versions,
+ pin = not not args.pin,
+ rebuild = true,
+ no_install = not not args.no_install,
+ }
+
+ if args.sign and not args.pack_binary_rock then
+ return nil, "In the make command, --sign is meant to be used only with --pack-binary-rock"
+ end
+
+ if args.no_install then
+ name, version = build.build_rockspec(rockspec, opts, cwd)
+ if name then
+ return true
+ else
+ return nil, version
+ end
+ elseif args.pack_binary_rock then
+ return pack.pack_binary_rock(name, namespace, rockspec.version, args.sign, function()
+ name, version = build.build_rockspec(rockspec, opts, cwd)
+ if name and args.no_doc then
+ util.remove_doc_dir(name, version)
+ end
+ return name, version
+ end)
+ else
+ local ok, err = build.build_rockspec(rockspec, opts, cwd)
+ if not ok then return nil, err end
+ name, version = ok, err
+
+ if opts.build_only_deps then
+ util.printout("Stopping after installing dependencies for " .. name .. " " .. version)
+ util.printout()
+ return name ~= nil, version
+ end
+
+ if args.no_doc then
+ util.remove_doc_dir(name, version)
+ end
+
+ if (not args.keep) and not cfg.keep_other_versions then
+ local ok, err, warn = remove.remove_other_versions(name, version, args.force, args.force_fast)
+ if not ok then
+ return nil, err
+ elseif warn then
+ util.printerr(warn)
+ end
+ end
+
+ deps.check_dependencies(nil, deps.get_deps_mode(args))
+ return name ~= nil, version
+ end
+end
+
+make.needs_lock = function(args)
+ if args.pack_binary_rock or args.no_install then
+ return false
+ end
+ return true
+end
+
+return make
diff --git a/src/luarocks/cmd/new_version.lua b/src/luarocks/cmd/new_version.lua
new file mode 100644
index 00000000..ea70a874
--- /dev/null
+++ b/src/luarocks/cmd/new_version.lua
@@ -0,0 +1,237 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local math = _tl_compat and _tl_compat.math or math; local string = _tl_compat and _tl_compat.string or string
+
+
+local new_version = {}
+
+
+local util = require("luarocks.util")
+local download = require("luarocks.download")
+local fetch = require("luarocks.fetch")
+local persist = require("luarocks.persist")
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+local type_rockspec = require("luarocks.type.rockspec")
+
+
+
+
+
+
+
+
+
+function new_version.add_to_parser(parser)
+ local cmd = parser:command("new_version", [[
+This is a utility function that writes a new rockspec, updating data from a
+previous one.
+
+If a package name is given, it downloads the latest rockspec from the default
+server. If a rockspec is given, it uses it instead. If no argument is given, it
+looks for a rockspec same way 'luarocks make' does.
+
+If the version number is not given and tag is passed using --tag, it is used as
+the version, with 'v' removed from beginning. Otherwise, it only increments the
+revision number of the given (or downloaded) rockspec.
+
+If a URL is given, it replaces the one from the old rockspec with the given URL.
+If a URL is not given and a new version is given, it tries to guess the new URL
+by replacing occurrences of the version number in the URL or tag; if the guessed
+URL is invalid, the old URL is restored. It also tries to download the new URL
+to determine the new MD5 checksum.
+
+If a tag is given, it replaces the one from the old rockspec. If there is an old
+tag but no new one passed, it is guessed in the same way URL is.
+
+If a directory is not given, it defaults to the current directory.
+
+WARNING: it writes the new rockspec to the given directory, overwriting the file
+if it already exists.]], util.see_also()):
+ summary("Auto-write a rockspec for a new version of a rock.")
+
+ cmd:argument("rock", "Package name or rockspec."):
+ args("?")
+ cmd:argument("new_version", "New version of the rock."):
+ args("?")
+ cmd:argument("new_url", "New URL of the rock."):
+ args("?")
+
+ cmd:option("--dir", "Output directory for the new rockspec.")
+ cmd:option("--tag", "New SCM tag.")
+end
+
+
+local function try_replace(tbl, field, old, new)
+ if not tbl[field] then
+ return false
+ end
+ local old_field = tbl[field]
+ local new_field = tbl[field]:gsub(old, new)
+ if new_field ~= old_field then
+ util.printout("Guessing new '" .. field .. "' field as " .. new_field)
+ tbl[field] = new_field
+ return true
+ end
+ return false
+end
+
+
+
+
+
+local function check_url_and_update_md5(out_rs, invalid_is_error)
+ local file, temp_dir = fetch.fetch_url_at_temp_dir(out_rs.source.url, "luarocks-new-version-" .. out_rs.package)
+ if not file then
+ if invalid_is_error then
+ return nil, "invalid URL - " .. temp_dir
+ end
+ util.warning("invalid URL - " .. temp_dir)
+ return true, false
+ end
+ do
+ local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, out_rs.source.url, out_rs.source.dir)
+ if not inferred_dir then
+ return nil, found_dir
+ end
+
+ if found_dir and found_dir ~= inferred_dir then
+ out_rs.source.dir = found_dir
+ end
+ end
+ if file then
+ if out_rs.source.md5 then
+ util.printout("File successfully downloaded. Updating MD5 checksum...")
+ local new_md5, err = fs.get_md5(file)
+ if not new_md5 then
+ return nil, err
+ end
+ local old_md5 = out_rs.source.md5
+ out_rs.source.md5 = new_md5
+ return true, new_md5 ~= old_md5
+ else
+ util.printout("File successfully downloaded.")
+ return true, false
+ end
+ end
+end
+
+local function update_source_section(out_rs, url, tag, old_ver, new_ver)
+ if tag then
+ out_rs.source.tag = tag
+ end
+ if url then
+ out_rs.source.url = url
+ return check_url_and_update_md5(out_rs)
+ end
+ if new_ver == old_ver then
+ return true
+ end
+ if out_rs.source.dir then
+ try_replace(out_rs.source, "dir", old_ver, new_ver)
+ end
+ if out_rs.source.file then
+ try_replace(out_rs.source, "file", old_ver, new_ver)
+ end
+
+ local old_url = out_rs.source.url
+ if try_replace(out_rs.source, "url", old_ver, new_ver) then
+ local ok, md5_changed = check_url_and_update_md5(out_rs, true)
+ if ok then
+ return ok, md5_changed
+ end
+ out_rs.source.url = old_url
+ end
+ if tag or try_replace(out_rs.source, "tag", old_ver, new_ver) then
+ return true
+ end
+
+ local ok, md5_changed = check_url_and_update_md5(out_rs)
+ if not ok then
+ return nil, md5_changed
+ end
+ if md5_changed then
+ util.warning("URL is the same, but MD5 has changed. Old rockspec is broken.")
+ end
+ return true
+end
+
+function new_version.command(args)
+ if not args.rock then
+ local err
+ args.rock, err = util.get_default_rockspec()
+ if not args.rock then
+ return nil, err
+ end
+ end
+
+ local filename, err
+ if args.rock:match("rockspec$") then
+ filename, err = fetch.fetch_url(args.rock)
+ if not filename then
+ return nil, err
+ end
+ else
+ filename, err = download.download_file("rockspec", args.rock:lower())
+ if not filename then
+ return nil, err
+ end
+ end
+
+ local valid_rs, err = fetch.load_rockspec(filename)
+ if not valid_rs then
+ return nil, err
+ end
+
+ local old_ver, old_rev = valid_rs.version:match("(.*)%-(%d+)$")
+ local new_ver, new_rev_str, new_rev
+
+ if args.tag and not args.new_version then
+ args.new_version = args.tag:gsub("^v", "")
+ end
+
+ local out_dir
+ if args.dir then
+ out_dir = dir.normalize(args.dir)
+ end
+
+ if args.new_version then
+ new_ver, new_rev_str = args.new_version:match("(.*)%-(%d+)$")
+ new_rev = math.tointeger(new_rev_str)
+ if not new_rev then
+ new_ver = args.new_version
+ new_rev = 1
+ end
+ else
+ new_ver = old_ver
+ new_rev = math.tointeger(old_rev) + 1
+ end
+ local new_rockver = new_ver:gsub("-", "")
+
+ local out_rs, err = persist.load_into_table(filename), string
+ local out_name = out_rs.package:lower()
+ out_rs.version = new_rockver .. "-" .. tostring(new_rev)
+
+ local ok, err = update_source_section(out_rs, args.new_url, args.tag, old_ver, new_ver)
+ if not ok then return nil, err end
+
+ if out_rs.build and out_rs.build.type == "module" then
+ out_rs.build.type = "builtin"
+ end
+
+ local out_filename = out_name .. "-" .. new_rockver .. "-" .. tostring(new_rev) .. ".rockspec"
+ if out_dir then
+ out_filename = dir.path(out_dir, out_filename)
+ fs.make_dir(out_dir)
+ end
+ persist.save_from_table(out_filename, out_rs, type_rockspec.order)
+
+ util.printout("Wrote " .. out_filename)
+
+ local valid_out_rs, err = fetch.load_local_rockspec(out_filename)
+ if not valid_out_rs then
+ return nil, "Failed loading generated rockspec: " .. err
+ end
+
+ return true
+end
+
+return new_version
diff --git a/src/luarocks/cmd/pack.lua b/src/luarocks/cmd/pack.lua
new file mode 100644
index 00000000..7c67d9cf
--- /dev/null
+++ b/src/luarocks/cmd/pack.lua
@@ -0,0 +1,41 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local string = _tl_compat and _tl_compat.string or string
+
+
+local cmd_pack = {}
+
+
+local util = require("luarocks.util")
+local pack = require("luarocks.pack")
+local queries = require("luarocks.queries")
+
+
+
+
+
+function cmd_pack.add_to_parser(parser)
+ local cmd = parser:command("pack", "Create a rock, packing sources or binaries.", util.see_also())
+
+ cmd:argument("rock", "A rockspec file, for creating a source rock, or the " ..
+ "name of an installed package, for creating a binary rock."):
+ action(util.namespaced_name_action)
+ cmd:argument("version", "A version may be given if the first argument is a rock name."):
+ args("?")
+
+ cmd:flag("--sign", "Produce a signature file as well.")
+end
+
+
+
+
+function cmd_pack.command(args)
+ local file, err
+ if args.rock:match(".*%.rockspec") then
+ file, err = pack.pack_source_rock(args.rock)
+ else
+ local query = queries.new(args.rock, args.namespace, args.version)
+ file, err = pack.pack_installed_rock(query, args.tree)
+ end
+ return pack.report_and_sign_local_file(file, err, args.sign)
+end
+
+return cmd_pack
diff --git a/src/luarocks/cmd/path.lua b/src/luarocks/cmd/path.lua
new file mode 100644
index 00000000..96fb3b9b
--- /dev/null
+++ b/src/luarocks/cmd/path.lua
@@ -0,0 +1,88 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local os = _tl_compat and _tl_compat.os or os; local package = _tl_compat and _tl_compat.package or package
+
+
+local path_cmd = {}
+
+
+local util = require("luarocks.util")
+local cfg = require("luarocks.core.cfg")
+local fs = require("luarocks.fs")
+
+
+
+
+
+function path_cmd.add_to_parser(parser)
+ local cmd = parser:command("path", [[
+Returns the package path currently configured for this installation
+of LuaRocks, formatted as shell commands to update LUA_PATH and LUA_CPATH.
+
+On Unix systems, you may run:
+ eval `luarocks path`
+And on Windows:
+ luarocks path > "%temp%\_lrp.bat"
+ call "%temp%\_lrp.bat" && del "%temp%\_lrp.bat"]],
+ util.see_also()):
+ summary("Return the currently configured package path.")
+
+ cmd:flag("--no-bin", "Do not export the PATH variable.")
+ cmd:flag("--append", "Appends the paths to the existing paths. Default is " ..
+ "to prefix the LR paths to the existing paths.")
+ cmd:flag("--lr-path", "Prints Lua path components defined by the configured rocks trees " ..
+ "(not formatted as a shell command)")
+ cmd:flag("--lr-cpath", "Prints Lua cpath components defined by the configured rocks trees " ..
+ "(not formatted as a shell command)")
+ cmd:flag("--full", "By default, --lr-path and --lr-cpath only include the paths " ..
+ "derived by the LuaRocks rocks_trees. Using --full includes any other components " ..
+ "defined in your system's package.(c)path, either via the running interpreter's " ..
+ "default paths or via LUA_(C)PATH(_5_x) environment variables (in short, using " ..
+ "--full produces the same lists as shown in the shell outputs of 'luarocks path').")
+ cmd:flag("--lr-bin", "Exports the system path (not formatted as shell command).")
+ cmd:flag("--bin"):hidden(true)
+end
+
+
+
+function path_cmd.command(args)
+ local lr_path, lr_cpath, lr_bin = cfg.package_paths(args.tree)
+ local path_sep = cfg.export_path_separator
+
+ local full_list = ((not args.lr_path) and (not args.lr_cpath) and (not args.lr_bin)) or
+ args.full
+
+ local clean_path = util.cleanup_path(os.getenv("PATH") or "", path_sep, nil, true)
+
+ if full_list then
+ if args.append then
+ lr_path = package.path .. ";" .. lr_path
+ lr_cpath = package.cpath .. ";" .. lr_cpath
+ lr_bin = clean_path .. path_sep .. lr_bin
+ else
+ lr_path = lr_path .. ";" .. package.path
+ lr_cpath = lr_cpath .. ";" .. package.cpath
+ lr_bin = lr_bin .. path_sep .. clean_path
+ end
+ end
+
+ if args.lr_path then
+ util.printout(util.cleanup_path(lr_path, ';', cfg.lua_version, true))
+ return true
+ elseif args.lr_cpath then
+ util.printout(util.cleanup_path(lr_cpath, ';', cfg.lua_version, true))
+ return true
+ elseif args.lr_bin then
+ util.printout(util.cleanup_path(lr_bin, path_sep, nil, true))
+ return true
+ end
+
+ local lpath_var, lcpath_var = util.lua_path_variables()
+
+ util.printout(fs.export_cmd(lpath_var, util.cleanup_path(lr_path, ';', cfg.lua_version, args.append)))
+ util.printout(fs.export_cmd(lcpath_var, util.cleanup_path(lr_cpath, ';', cfg.lua_version, args.append)))
+ if not args.no_bin then
+ util.printout(fs.export_cmd("PATH", util.cleanup_path(lr_bin, path_sep, nil, args.append)))
+ end
+ return true
+end
+
+return path_cmd
diff --git a/src/luarocks/cmd/purge.lua b/src/luarocks/cmd/purge.lua
new file mode 100644
index 00000000..b7d28f82
--- /dev/null
+++ b/src/luarocks/cmd/purge.lua
@@ -0,0 +1,79 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local package = _tl_compat and _tl_compat.package or package
+
+
+local purge = {}
+
+
+
+local util = require("luarocks.util")
+local path = require("luarocks.path")
+local search = require("luarocks.search")
+local vers = require("luarocks.core.vers")
+local repo_writer = require("luarocks.repo_writer")
+local cfg = require("luarocks.core.cfg")
+local remove = require("luarocks.remove")
+local queries = require("luarocks.queries")
+
+
+
+
+
+
+function purge.add_to_parser(parser)
+
+ local cmd = parser:command("purge", [[
+This command removes rocks en masse from a given tree.
+By default, it removes all rocks from a tree.
+
+The --tree option is mandatory: luarocks purge does not assume a default tree.]],
+ util.see_also()):
+ summary("Remove all installed rocks from a tree.")
+
+
+ cmd:flag("--old-versions", "Keep the highest-numbered version of each " ..
+ "rock and remove the other ones. By default it only removes old " ..
+ "versions if they are not needed as dependencies. This can be " ..
+ "overridden with the flag --force.")
+ cmd:flag("--force", "If --old-versions is specified, force removal of " ..
+ "previously installed versions if it would break dependencies.")
+ cmd:flag("--force-fast", "Like --force, but performs a forced removal " ..
+ "without reporting dependency issues.")
+end
+
+function purge.command(args)
+ local tree = args.tree
+
+ local results = {}
+ search.local_manifest_search(results, path.rocks_dir(tree), queries.all())
+
+ local sort = function(a, b) return vers.compare_versions(b, a) end
+ if args.old_versions then
+ sort = vers.compare_versions
+ end
+
+ for package, versions in util.sortedpairs(results) do
+ for version, _ in util.sortedpairs(versions, sort) do
+ if args.old_versions then
+ util.printout("Keeping " .. package .. " " .. version .. "...")
+ local ok, err, warn = remove.remove_other_versions(package, version, args.force, args.force_fast)
+ if not ok then
+ util.printerr(err)
+ elseif warn then
+ util.printerr(err)
+ end
+ break
+ else
+ util.printout("Removing " .. package .. " " .. version .. "...")
+ local ok, err = repo_writer.delete_version(package, version, "none", true)
+ if not ok then
+ util.printerr(err)
+ end
+ end
+ end
+ end
+ return repo_writer.refresh_manifest(cfg.rocks_dir)
+end
+
+purge.needs_lock = function() return true end
+
+return purge
diff --git a/src/luarocks/cmd/remove.lua b/src/luarocks/cmd/remove.lua
new file mode 100644
index 00000000..5a0f7dc5
--- /dev/null
+++ b/src/luarocks/cmd/remove.lua
@@ -0,0 +1,77 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local string = _tl_compat and _tl_compat.string or string
+
+
+local cmd_remove = {}
+
+
+
+local remove = require("luarocks.remove")
+local util = require("luarocks.util")
+local cfg = require("luarocks.core.cfg")
+local search = require("luarocks.search")
+local path = require("luarocks.path")
+local deps = require("luarocks.deps")
+local queries = require("luarocks.queries")
+
+
+
+
+
+function cmd_remove.add_to_parser(parser)
+
+ local cmd = parser:command("remove", [[
+Uninstall a rock.
+
+If a version is not given, try to remove all versions at once.
+Will only perform the removal if it does not break dependencies.
+To override this check and force the removal, use --force or --force-fast.]],
+ util.see_also()):
+ summary("Uninstall a rock.")
+
+
+ cmd:argument("rock", "Name of the rock to be uninstalled."):
+ action(util.namespaced_name_action)
+ cmd:argument("version", "Version of the rock to uninstall."):
+ args("?")
+
+ cmd:flag("--force", "Force removal if it would break dependencies.")
+ cmd:flag("--force-fast", "Perform a forced removal without reporting dependency issues.")
+ util.deps_mode_option(cmd)
+end
+
+
+
+
+function cmd_remove.command(args)
+ local name = args.rock
+ local deps_mode = deps.get_deps_mode(args)
+
+ local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$")
+ local version = args.version
+ local filename = name
+ if rock_type then
+ name, version = path.parse_name(filename)
+ if not name then return nil, "Invalid " .. rock_type .. " filename: " .. filename end
+ end
+
+ name = name:lower()
+
+ local results = {}
+ search.local_manifest_search(results, cfg.rocks_dir, queries.new(name, args.namespace, version))
+ if not results[name] then
+ local rock = util.format_rock_name(name, args.namespace, version)
+ return nil, "Could not find rock '" .. rock .. "' in " .. path.rocks_tree_to_string(cfg.root_dir)
+ end
+
+ local ok, err = remove.remove_search_results(results, name, deps_mode, args.force, args.force_fast)
+ if not ok then
+ return nil, err
+ end
+
+ deps.check_dependencies(nil, deps.get_deps_mode(args))
+ return true
+end
+
+cmd_remove.needs_lock = function() return true end
+
+return cmd_remove
diff --git a/src/luarocks/cmd/search.lua b/src/luarocks/cmd/search.lua
new file mode 100644
index 00000000..3eaeda4d
--- /dev/null
+++ b/src/luarocks/cmd/search.lua
@@ -0,0 +1,91 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pairs = _tl_compat and _tl_compat.pairs or pairs
+
+
+local cmd_search = {}
+
+
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+local search = require("luarocks.search")
+local queries = require("luarocks.queries")
+local results = require("luarocks.results")
+
+
+
+
+
+
+
+function cmd_search.add_to_parser(parser)
+ local cmd = parser:command("search", "Query the LuaRocks servers.", util.see_also())
+
+ cmd:argument("name", "Name of the rock to search for."):
+ args("?"):
+ action(util.namespaced_name_action)
+ cmd:argument("version", "Rock version to search for."):
+ args("?")
+
+ cmd:flag("--source", "Return only rockspecs and source rocks, to be used " ..
+ 'with the "build" command.')
+ cmd:flag("--binary", "Return only pure Lua and binary rocks (rocks that " ..
+ 'can be used with the "install" command without requiring a C toolchain).')
+ cmd:flag("--all", "List all contents of the server that are suitable to " ..
+ "this platform, do not filter by name.")
+ cmd:flag("--porcelain", "Return a machine readable format.")
+end
+
+
+
+
+
+
+
+local function split_source_and_binary_results(result_tree)
+ local sources, binaries = {}, {}
+ for name, versions in pairs(result_tree) do
+ for version, repositories in pairs(versions) do
+ for _, repo in ipairs(repositories) do
+ local where = sources
+ if repo.arch == "all" or repo.arch == cfg.arch then
+ where = binaries
+ end
+ local entry = results.new(name, version, repo.repo, repo.arch)
+ search.store_result(where, entry)
+ end
+ end
+ end
+ return sources, binaries
+end
+
+
+
+
+function cmd_search.command(args)
+ local name = args.name
+
+ if args.all then
+ name, args.version = "", nil
+ end
+
+ if not args.name and not args.all then
+ return nil, "Enter name and version or use --all. " .. util.see_help("search")
+ end
+
+ local query = queries.new(name, args.namespace, args.version, true)
+ local result_tree = search.search_repos(query)
+ local porcelain = args.porcelain
+ local full_name = util.format_rock_name(name, args.namespace, args.version)
+ util.title(full_name .. " - Search results for Lua " .. cfg.lua_version .. ":", porcelain, "=")
+ local sources, binaries = split_source_and_binary_results(result_tree)
+ if next(sources) and not args.binary then
+ util.title("Rockspecs and source rocks:", porcelain)
+ search.print_result_tree(sources, porcelain)
+ end
+ if next(binaries) and not args.source then
+ util.title("Binary and pure-Lua rocks:", porcelain)
+ search.print_result_tree(binaries, porcelain)
+ end
+ return true
+end
+
+return cmd_search
diff --git a/src/luarocks/cmd/show.lua b/src/luarocks/cmd/show.lua
new file mode 100644
index 00000000..58d6701e
--- /dev/null
+++ b/src/luarocks/cmd/show.lua
@@ -0,0 +1,341 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local math = _tl_compat and _tl_compat.math or math; local os = _tl_compat and _tl_compat.os or os; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+
+local show = {Return = {}, }
+
+
+
+
+
+
+
+local queries = require("luarocks.queries")
+local search = require("luarocks.search")
+local dir = require("luarocks.core.dir")
+local fs = require("luarocks.fs")
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+local path = require("luarocks.path")
+local fetch = require("luarocks.fetch")
+local manif = require("luarocks.manif")
+local repos = require("luarocks.repos")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+function show.add_to_parser(parser)
+ local cmd = parser:command("show", [[
+Show information about an installed rock.
+
+Without any flags, show all module information.
+With flags, return only the desired information.]], util.see_also()):
+ summary("Show information about an installed rock.")
+
+ cmd:argument("rock", "Name of an installed rock."):
+ action(util.namespaced_name_action)
+ cmd:argument("version", "Rock version."):
+ args("?")
+
+ cmd:flag("--home", "Show home page of project.")
+ cmd:flag("--modules", "Show all modules provided by the package as used by require().")
+ cmd:flag("--deps", "Show packages the package depends on.")
+ cmd:flag("--build-deps", "Show build-only dependencies for the package.")
+ cmd:flag("--test-deps", "Show dependencies for testing the package.")
+ cmd:flag("--rockspec", "Show the full path of the rockspec file.")
+ cmd:flag("--mversion", "Show the package version.")
+ cmd:flag("--rock-tree", "Show local tree where rock is installed.")
+ cmd:flag("--rock-namespace", "Show rock namespace.")
+ cmd:flag("--rock-dir", "Show data directory of the installed rock.")
+ cmd:flag("--rock-license", "Show rock license.")
+ cmd:flag("--issues", "Show URL for project's issue tracker.")
+ cmd:flag("--labels", "List the labels of the rock.")
+ cmd:flag("--porcelain", "Produce machine-friendly output.")
+end
+
+local friendly_template = [[
+ :
+?namespace:${namespace}/${package} ${version} - ${summary}
+!namespace:${package} ${version} - ${summary}
+ :
+*detailed :${detailed}
+?detailed :
+?license :License: \t${license}
+?homepage :Homepage: \t${homepage}
+?issues :Issues: \t${issues}
+?labels :Labels: \t${labels}
+?location :Installed in: \t${location}
+?commands :
+?commands :Commands:
+*commands :\t${name} (${file})
+?modules :
+?modules :Modules:
+*modules :\t${name} (${file})
+?bdeps :
+?bdeps :Has build dependency on:
+*bdeps :\t${name} (${label})
+?tdeps :
+?tdeps :Tests depend on:
+*tdeps :\t${name} (${label})
+?deps :
+?deps :Depends on:
+*deps :\t${name} (${label})
+?ideps :
+?ideps :Indirectly pulling:
+*ideps :\t${name} (${label})
+ :
+]]
+
+local porcelain_template = [[
+?namespace:namespace\t${namespace}
+?package :package\t${package}
+?version :version\t${version}
+?summary :summary\t${summary}
+*detailed :detailed\t${detailed}
+?license :license\t${license}
+?homepage :homepage\t${homepage}
+?issues :issues\t${issues}
+?labels :labels\t${labels}
+?location :location\t${location}
+*commands :command\t${name}\t${file}
+*modules :module\t${name}\t${file}
+*bdeps :build_dependency\t${name}\t${label}
+*tdeps :test_dependency\t${name}\t${label}
+*deps :dependency\t${name}\t${label}
+*ideps :indirect_dependency\t${name}\t${label}
+]]
+
+local function keys_as_string(t, sep)
+ local keys = util.keys(t)
+ table.sort(keys)
+ return table.concat(keys, sep or " ")
+end
+
+local function word_wrap(line)
+ local width = math.tointeger(os.getenv("COLUMNS")) or 80
+ if width > 80 then width = 80 end
+ if #line > width then
+ local brk = width
+ while brk > 0 and line:sub(brk, brk) ~= " " do
+ brk = brk - 1
+ end
+ if brk > 0 then
+ return line:sub(1, brk - 1) .. "\n" .. word_wrap(line:sub(brk + 1))
+ end
+ end
+ return line
+end
+
+local function format_text(text)
+ text = text:gsub("^%s*", ""):gsub("%s$", ""):gsub("\n[ \t]+", "\n"):gsub("([^\n])\n([^\n])", "%1 %2")
+ local paragraphs = util.split_string(text, "\n\n")
+ for n, line in ipairs(paragraphs) do
+ paragraphs[n] = word_wrap(line)
+ end
+ return (table.concat(paragraphs, "\n\n"):gsub("%s$", ""))
+end
+
+local function installed_rock_label(dep, tree)
+ local installed, version
+ local rocks_provided = util.get_rocks_provided()
+ if rocks_provided[dep.name] then
+ installed, version = true, rocks_provided[dep.name]
+ else
+ local name
+ name, version = search.pick_installed_rock(dep, tree)
+ installed = name ~= nil
+ end
+ return installed and "using " .. version or "missing"
+end
+
+local function render(template, data)
+ local out = {}
+ for cmd, var, line in template:gmatch("(.)([a-z]*)%s*:([^\n]*)\n") do
+ line = line:gsub("\\t", "\t")
+ local d = data[var]
+ if cmd == " " then
+ table.insert(out, line)
+ elseif cmd == "?" or cmd == "*" or cmd == "!" then
+ if (cmd == "!" and d == nil) or
+ (cmd ~= "!" and (type(d) == "string" or
+ (type(d) == "table" and next(d)))) then
+ local n = type(d) == "table" and #d or 1
+ if cmd ~= "*" then
+ n = 1
+ end
+ for i = 1, n do
+ local tbl = cmd == "*" and type(d) == "table" and d[i] or data
+ if type(tbl) == "string" then
+ tbl = tbl:gsub("%%", "%%%%")
+ end
+ table.insert(out, (line:gsub("${([a-z]+)}", tbl)))
+ end
+ end
+ end
+ end
+ return table.concat(out, "\n")
+end
+
+local function adjust_path(name, version, basedir, pathname, suffix)
+ pathname = dir.path(basedir, pathname)
+ local vpathname = path.versioned_name(pathname, basedir, name, version)
+ return (fs.exists(vpathname) and
+ vpathname or
+ pathname) .. (suffix or "")
+end
+
+local function modules_to_list(name, version, repo)
+ local ret = {}
+ local rock_manifest = manif.load_rock_manifest(name, version, repo)
+
+ local lua_dir = path.deploy_lua_dir(repo)
+ local lib_dir = path.deploy_lib_dir(repo)
+ repos.recurse_rock_manifest_entry(rock_manifest.lua, function(pathname)
+ table.insert(ret, {
+ name = path.path_to_module(pathname),
+ file = adjust_path(name, version, lua_dir, pathname),
+ })
+ end)
+ repos.recurse_rock_manifest_entry(rock_manifest.lib, function(pathname)
+ table.insert(ret, {
+ name = path.path_to_module(pathname),
+ file = adjust_path(name, version, lib_dir, pathname),
+ })
+ end)
+ table.sort(ret, function(a, b)
+ if a.name == b.name then
+ return a.file < b.file
+ end
+ return a.name < b.name
+ end)
+ return ret
+end
+
+local function commands_to_list(name, version, repo)
+ local ret = {}
+ local rock_manifest = manif.load_rock_manifest(name, version, repo)
+
+ local bin_dir = path.deploy_bin_dir(repo)
+ repos.recurse_rock_manifest_entry(rock_manifest.bin, function(pathname)
+ table.insert(ret, {
+ name = name,
+ file = adjust_path(name, version, bin_dir, pathname, cfg.wrapper_suffix),
+ })
+ end)
+ table.sort(ret, function(a, b)
+ if a.name == b.name then
+ return a.file < b.file
+ end
+ return a.name < b.name
+ end)
+ return ret
+end
+
+local function deps_to_list(dependencies, tree)
+ local ret = {}
+ for _, dep in ipairs(dependencies.queries or {}) do
+ table.insert(ret, { name = tostring(dep), label = installed_rock_label(dep, tree) })
+ end
+ return ret
+end
+
+local function indirect_deps(mdeps, rdeps, tree)
+ local ret = {}
+ local direct_deps = {}
+ for _, dep in ipairs(rdeps) do
+ direct_deps[dep] = true
+ end
+ for dep_name in util.sortedpairs(mdeps or {}) do
+ if not direct_deps[dep_name] then
+ table.insert(ret, { name = tostring(dep_name), label = installed_rock_label(queries.new(dep_name), tree) })
+ end
+ end
+ return ret
+end
+
+local function show_rock(template, namespace, name, version, rockspec, repo, minfo, tree)
+ local desc = rockspec.description or {}
+ local data = {
+ namespace = namespace,
+ package = rockspec.package,
+ version = rockspec.version,
+ summary = desc.summary or "",
+ detailed = desc.detailed and util.split_string(format_text(desc.detailed), "\n"),
+ license = desc.license,
+ homepage = desc.homepage,
+ issues = desc.issues_url,
+ labels = desc.labels and table.concat(desc.labels, ", "),
+ location = path.rocks_tree_to_string(repo),
+ commands = commands_to_list(name, version, repo),
+ modules = modules_to_list(name, version, repo),
+ bdeps = deps_to_list(rockspec.build_dependencies, tree),
+ tdeps = deps_to_list(rockspec.test_dependencies, tree),
+ deps = deps_to_list(rockspec.dependencies, tree),
+ ideps = indirect_deps(minfo.dependencies, rockspec.dependencies, tree),
+ }
+ util.printout(render(template, data))
+end
+
+
+
+function show.command(args)
+ local query = queries.new(args.rock, args.namespace, args.version, true)
+
+ local name, version, repo, repo_url = search.pick_installed_rock(query, args.tree)
+ if not name then
+ return nil, version
+ end
+ local tree = path.rocks_tree_to_string(repo)
+ local directory = path.install_dir(name, version, repo)
+ local namespace = path.read_namespace(name, version, tree)
+ local rockspec_file = path.rockspec_file(name, version, repo)
+ local rockspec, err = fetch.load_local_rockspec(rockspec_file)
+ if not rockspec then return nil, err end
+
+ local descript = rockspec.description or {}
+ local manifest, err = manif.load_manifest(repo_url)
+ if not manifest then return nil, err end
+ local minfo = manifest.repository[name][version][1]
+
+ if args.rock_tree then util.printout(tree)
+ elseif args.rock_namespace then util.printout(namespace)
+ elseif args.rock_dir then util.printout(directory)
+ elseif args.home then util.printout(descript.homepage)
+ elseif args.rock_license then util.printout(descript.license)
+ elseif args.issues then util.printout(descript.issues_url)
+ elseif args.labels then util.printout(descript.labels and table.concat(descript.labels, "\n"))
+ elseif args.modules then util.printout(keys_as_string(minfo.modules, "\n"))
+ elseif args.deps then
+ for _, dep in ipairs(rockspec.dependencies) do
+ util.printout(tostring(dep))
+ end
+ elseif args.build_deps then
+ for _, dep in ipairs(rockspec.build_dependencies) do
+ util.printout(tostring(dep))
+ end
+ elseif args.test_deps then
+ for _, dep in ipairs(rockspec.test_dependencies) do
+ util.printout(tostring(dep))
+ end
+ elseif args.rockspec then util.printout(rockspec_file)
+ elseif args.mversion then util.printout(version)
+ elseif args.porcelain then
+ show_rock(porcelain_template, namespace, name, version, rockspec, repo, minfo, args.tree)
+ else
+ show_rock(friendly_template, namespace, name, version, rockspec, repo, minfo, args.tree)
+ end
+ return true
+end
+
+return show
diff --git a/src/luarocks/cmd/test.lua b/src/luarocks/cmd/test.lua
new file mode 100644
index 00000000..9305edba
--- /dev/null
+++ b/src/luarocks/cmd/test.lua
@@ -0,0 +1,53 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+
+
+local cmd_test = {}
+
+
+local util = require("luarocks.util")
+local test = require("luarocks.test")
+
+
+
+
+
+function cmd_test.add_to_parser(parser)
+ local cmd = parser:command("test", [[
+Run the test suite for the Lua project in the current directory.
+
+If the first argument is a rockspec, it will use it to determine the parameters
+for running tests; otherwise, it will attempt to detect the rockspec.
+
+Any additional arguments are forwarded to the test suite.
+To make sure that test suite flags are not interpreted as LuaRocks flags, use --
+to separate LuaRocks arguments from test suite arguments.]],
+ util.see_also()):
+ summary("Run the test suite in the current directory.")
+
+ cmd:argument("rockspec", "Project rockspec."):
+ args("?")
+ cmd:argument("args", "Test suite arguments."):
+ args("*")
+ cmd:flag("--prepare", "Only install dependencies needed for testing only, but do not run the test")
+
+ cmd:option("--test-type", "Specify the test suite type manually if it was " ..
+ "not specified in the rockspec and it could not be auto-detected."):
+ argname("")
+end
+
+function cmd_test.command(args)
+ if args.rockspec and args.rockspec:match("rockspec$") then
+ return test.run_test_suite(args.rockspec, args.test_type, args.args, args.prepare)
+ end
+
+ table.insert(args.args, 1, args.rockspec)
+
+ local rockspec, err = util.get_default_rockspec()
+ if not rockspec then
+ return nil, err
+ end
+
+ return test.run_test_suite(rockspec, args.test_type, args.args, args.prepare)
+end
+
+return cmd_test
diff --git a/src/luarocks/cmd/unpack.lua b/src/luarocks/cmd/unpack.lua
new file mode 100644
index 00000000..5a1ae799
--- /dev/null
+++ b/src/luarocks/cmd/unpack.lua
@@ -0,0 +1,172 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local string = _tl_compat and _tl_compat.string or string
+
+
+local unpack = {}
+
+
+local fetch = require("luarocks.fetch")
+local fs = require("luarocks.fs")
+local util = require("luarocks.util")
+local build = require("luarocks.build")
+local dir = require("luarocks.dir")
+local search = require("luarocks.search")
+
+
+
+
+
+
+
+function unpack.add_to_parser(parser)
+ local cmd = parser:command("unpack", [[
+Unpacks the contents of a rock in a newly created directory.
+Argument may be a rock file, or the name of a rock in a rocks server.
+In the latter case, the rock version may be given as a second argument.]],
+ util.see_also()):
+ summary("Unpack the contents of a rock.")
+
+ cmd:argument("rock", "A rock file or the name of a rock."):
+ action(util.namespaced_name_action)
+ cmd:argument("version", "Rock version."):
+ args("?")
+
+ cmd:flag("--force", "Unpack files even if the output directory already exists.")
+ cmd:flag("--check-lua-versions", "If the rock can't be found, check repository " ..
+ "and report if it is available for another Lua version.")
+end
+
+
+
+
+
+
+
+local function unpack_rockspec(rockspec_file, dir_name)
+
+ local rockspec, err = fetch.load_rockspec(rockspec_file)
+ if not rockspec then
+ return nil, "Failed loading rockspec " .. rockspec_file .. ": " .. err
+ end
+ local ok, err = fs.change_dir(dir_name)
+ if not ok then return nil, err end
+ local filename, sources_dir = fetch.fetch_sources(rockspec, true, ".")
+ if not filename then
+ return nil, sources_dir
+ end
+ ok, err = fs.change_dir(sources_dir)
+ if not ok then return nil, err end
+ ok, err = build.apply_patches(rockspec)
+ fs.pop_dir()
+ if not ok then return nil, err end
+ return rockspec
+end
+
+
+
+
+
+
+
+
+local function unpack_rock(rock_file, dir_name, kind)
+
+ local ok, filename, err, errcode
+ filename, err, errcode = fetch.fetch_and_unpack_rock(rock_file, dir_name)
+ if not filename then
+ return nil, err, errcode
+ end
+ ok, err = fs.change_dir(dir_name)
+ if not ok then return nil, err end
+ local rockspec_file = dir_name .. ".rockspec"
+ local rockspec, err = fetch.load_rockspec(rockspec_file)
+ if not rockspec then
+ return nil, "Failed loading rockspec " .. rockspec_file .. ": " .. err
+ end
+ if kind == "src" then
+ if rockspec.source.file then
+ ok, err = fs.unpack_archive(rockspec.source.file)
+ if not ok then return nil, err end
+ ok, err = fetch.find_rockspec_source_dir(rockspec, ".")
+ if not ok then return nil, err end
+ ok, err = fs.change_dir(rockspec.source.dir)
+ if not ok then return nil, err end
+ ok, err = build.apply_patches(rockspec)
+ fs.pop_dir()
+ if not ok then return nil, err end
+ end
+ end
+ return rockspec
+end
+
+
+
+
+
+
+
+local function run_unpacker(file, force)
+
+ local base_name = dir.base_name(file)
+ local dir_name, kind, extension = base_name:match("(.*)%.([^.]+)%.(rock)$")
+ if not extension then
+ dir_name, extension = base_name:match("(.*)%.(rockspec)$")
+ kind = "rockspec"
+ end
+ if not extension then
+ return nil, file .. " does not seem to be a valid filename."
+ end
+
+ local exists = fs.exists(dir_name)
+ if exists and not force then
+ return nil, "Directory " .. dir_name .. " already exists."
+ end
+ if not exists then
+ local ok, err = fs.make_dir(dir_name)
+ if not ok then return nil, err end
+ end
+ local rollback = util.schedule_function(fs.delete, fs.absolute_name(dir_name))
+
+ local rockspec, err
+ if extension == "rock" then
+ rockspec, err = unpack_rock(file, dir_name, kind)
+ elseif extension == "rockspec" then
+ rockspec, err = unpack_rockspec(file, dir_name)
+ end
+ if not rockspec then
+ return nil, err
+ end
+ if kind == "src" or kind == "rockspec" then
+ fetch.find_rockspec_source_dir(rockspec, ".")
+ if rockspec.source.dir ~= "." then
+ local ok = fs.copy(rockspec.local_abs_filename, rockspec.source.dir, "read")
+ if not ok then
+ return nil, "Failed copying unpacked rockspec into unpacked source directory."
+ end
+ end
+ util.printout()
+ util.printout("Done. You may now enter directory ")
+ util.printout(dir.path(dir_name, rockspec.source.dir))
+ util.printout("and type 'luarocks make' to build.")
+ end
+ util.remove_scheduled_function(rollback)
+ return true
+end
+
+
+
+
+function unpack.command(args)
+ local url, err
+ if args.rock:match(".*%.rock") or args.rock:match(".*%.rockspec") then
+ url = args.rock
+ else
+ url, err = search.find_src_or_rockspec(args.rock, args.namespace, args.version, args.check_lua_versions)
+ if not url then
+ return nil, err
+ end
+ end
+
+ return run_unpacker(url, args.force)
+end
+
+return unpack
diff --git a/src/luarocks/cmd/upload.lua b/src/luarocks/cmd/upload.lua
new file mode 100644
index 00000000..70a1f9b9
--- /dev/null
+++ b/src/luarocks/cmd/upload.lua
@@ -0,0 +1,144 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local string = _tl_compat and _tl_compat.string or string
+local upload = {Response = {version = {}, }, }
+
+
+
+
+
+
+
+
+
+
+
+local signing = require("luarocks.signing")
+local util = require("luarocks.util")
+local fetch = require("luarocks.fetch")
+local pack = require("luarocks.pack")
+local cfg = require("luarocks.core.cfg")
+local Api = require("luarocks.upload.api")
+
+
+
+
+
+
+
+function upload.add_to_parser(parser)
+ local cmd = parser:command("upload", "Pack a source rock file (.src.rock extension) " ..
+ "and upload it and the rockspec to the public rocks repository.", util.see_also()):
+ summary("Upload a rockspec to the public rocks repository.")
+
+ cmd:argument("rockspec", "Rockspec for the rock to upload.")
+ cmd:argument("src-rock", "A corresponding .src.rock file; if not given it will be generated."):
+ args("?")
+
+ cmd:flag("--skip-pack", "Do not pack and send source rock.")
+ cmd:option("--api-key", "Pass an API key. It will be stored for subsequent uses."):
+ argname("")
+ cmd:option("--temp-key", "Use the given a temporary API key in this " ..
+ "invocation only. It will not be stored."):
+ argname("")
+ cmd:flag("--force", "Replace existing rockspec if the same revision of a " ..
+ "module already exists. This should be used only in case of upload " ..
+ "mistakes: when updating a rockspec, increment the revision number " ..
+ "instead.")
+ cmd:flag("--sign", "Upload a signature file alongside each file as well.")
+ cmd:flag("--debug"):hidden(true)
+end
+
+local function is_dev_version(version)
+ return version:match("^dev") or version:match("^scm")
+end
+
+function upload.command(args)
+ local api, err = Api.new(args)
+ if not api then
+ return nil, err
+ end
+ if cfg.verbose then
+ api.debug = true
+ end
+
+ local rockspec, err, errcode = fetch.load_rockspec(args.rockspec)
+ if err then
+ return nil, err, errcode
+ end
+
+ util.printout("Sending " .. tostring(args.rockspec) .. " ...")
+ local res, err = api:method("check_rockspec", {
+ package = rockspec.package,
+ version = rockspec.version,
+ })
+ if not res then return nil, err end
+
+ if not res.module then
+ util.printout("Will create new module (" .. tostring(rockspec.package) .. ")")
+ end
+ if res.version and not args.force then
+ return nil, "Revision " .. rockspec.version .. " already exists on the server. " .. util.see_help("upload")
+ end
+
+ local sigfname
+ local rock_sigfname
+
+ if args.sign then
+ sigfname, err = signing.sign_file(args.rockspec)
+ if err then
+ return nil, "Failed signing rockspec: " .. err
+ end
+ util.printout("Signed rockspec: " .. sigfname)
+ end
+
+ local rock_fname
+ if args.src_rock then
+ rock_fname = args.src_rock
+ elseif not args.skip_pack and not is_dev_version(rockspec.version) then
+ util.printout("Packing " .. tostring(rockspec.package))
+ rock_fname, err = pack.pack_source_rock(args.rockspec)
+ if not rock_fname then
+ return nil, err
+ end
+ end
+
+ if rock_fname and args.sign then
+ rock_sigfname, err = signing.sign_file(rock_fname)
+ if err then
+ return nil, "Failed signing rock: " .. err
+ end
+ util.printout("Signed packed rock: " .. rock_sigfname)
+ end
+
+ local multipart = require("luarocks.upload.multipart")
+
+ res, err = api:method("upload", nil, {
+ rockspec_file = multipart.new_file(args.rockspec),
+ rockspec_sig = sigfname and multipart.new_file(sigfname),
+ })
+ if not res then return nil, err end
+
+ if res.is_new and #res.manifests == 0 then
+ util.printerr("Warning: module not added to root manifest due to name taken.")
+ end
+
+ local module_url = res.module_url
+
+ if rock_fname then
+ if (not res.version) or (not res.version.id) then
+ return nil, "Invalid response from server."
+ end
+ util.printout(("Sending " .. tostring(rock_fname) .. " ..."))
+ res, err = api:method("upload_rock/" .. ("%d"):format(res.version.id), nil, {
+ rock_file = multipart.new_file(rock_fname),
+ rock_sig = rock_sigfname and multipart.new_file(rock_sigfname),
+ })
+ if not res then return nil, err end
+ end
+
+ util.printout()
+ util.printout("Done: " .. tostring(module_url))
+ util.printout()
+ return true
+end
+
+return upload
diff --git a/src/luarocks/cmd/which.lua b/src/luarocks/cmd/which.lua
new file mode 100644
index 00000000..c49d43c4
--- /dev/null
+++ b/src/luarocks/cmd/which.lua
@@ -0,0 +1,44 @@
+
+
+
+local which_cmd = {}
+
+
+local loader = require("luarocks.loader")
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+
+
+
+
+
+function which_cmd.add_to_parser(parser)
+ local cmd = parser:command("which", 'Given a module name like "foo.bar", ' ..
+ "output which file would be loaded to resolve that module by " ..
+ 'luarocks.loader, like "/usr/local/lua/' .. cfg.lua_version .. '/foo/bar.lua".',
+ util.see_also()):
+ summary("Tell which file corresponds to a given module name.")
+
+ cmd:argument("modname", "Module name.")
+end
+
+
+
+function which_cmd.command(args)
+ local pathname, rock_name, rock_version, where = loader.which(args.modname, "lp")
+
+ if pathname then
+ util.printout(pathname)
+ if where == "l" then
+ util.printout("(provided by " .. tostring(rock_name) .. " " .. tostring(rock_version) .. ")")
+ else
+ local key = rock_name
+ util.printout("(found directly via package." .. key .. " -- not installed as a rock?)")
+ end
+ return true
+ end
+
+ return nil, "Module '" .. args.modname .. "' not found."
+end
+
+return which_cmd
diff --git a/src/luarocks/cmd/write_rockspec.lua b/src/luarocks/cmd/write_rockspec.lua
new file mode 100644
index 00000000..156339b8
--- /dev/null
+++ b/src/luarocks/cmd/write_rockspec.lua
@@ -0,0 +1,423 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+local write_rockspec = {}
+
+
+local builtin = require("luarocks.build.builtin")
+local cfg = require("luarocks.core.cfg")
+local dir = require("luarocks.dir")
+local fetch = require("luarocks.fetch")
+local fs = require("luarocks.fs")
+local persist = require("luarocks.persist")
+local rockspecs = require("luarocks.rockspecs")
+local type_rockspec = require("luarocks.type.rockspec")
+local util = require("luarocks.util")
+
+
+
+
+
+
+
+
+
+
+
+
+local lua_versions = {
+ "5.1",
+ "5.2",
+ "5.3",
+ "5.4",
+ "5.1,5.2",
+ "5.2,5.3",
+ "5.3,5.4",
+ "5.1,5.2,5.3",
+ "5.2,5.3,5.4",
+ "5.1,5.2,5.3,5.4",
+}
+
+function write_rockspec.cmd_options(parser)
+ parser:option("--output", "Write the rockspec with the given filename.\n" ..
+ "If not given, a file is written in the current directory with a " ..
+ "filename based on given name and version."):
+ argname("")
+ parser:option("--license", 'A license string, such as "MIT/X11" or "GNU GPL v3".'):
+ argname("")
+ parser:option("--summary", "A short one-line description summary."):
+ argname("")
+ parser:option("--detailed", "A longer description string."):
+ argname("")
+ parser:option("--homepage", "Project homepage."):
+ argname("")
+ parser:option("--lua-versions", 'Supported Lua versions. Accepted values are: "' ..
+ table.concat(lua_versions, '", "') .. '".'):
+ argname(""):
+ choices(lua_versions)
+ parser:option("--rockspec-format", 'Rockspec format version, such as "1.0" or "1.1".'):
+ argname("")
+ parser:option("--tag", "Tag to use. Will attempt to extract version number from it.")
+ parser:option("--lib", "A comma-separated list of libraries that C files need to link to."):
+ argname("")
+end
+
+function write_rockspec.add_to_parser(parser)
+ local cmd = parser:command("write_rockspec", [[
+This command writes an initial version of a rockspec file,
+based on a name, a version, and a location (an URL or a local path).
+If only two arguments are given, the first one is considered the name and the
+second one is the location.
+If only one argument is given, it must be the location.
+If no arguments are given, current directory is used as the location.
+LuaRocks will attempt to infer name and version if not given,
+using 'dev' as a fallback default version.
+
+Note that the generated file is a _starting point_ for writing a
+rockspec, and is not guaranteed to be complete or correct. ]], util.see_also()):
+ summary("Write a template for a rockspec file.")
+
+ cmd:argument("name", "Name of the rock."):
+ args("?")
+ cmd:argument("version", "Rock version."):
+ args("?")
+ cmd:argument("location", "URL or path to the rock sources."):
+ args("?")
+
+ write_rockspec.cmd_options(cmd)
+end
+
+local function open_file(name)
+ return io.open(dir.path(fs.current_dir(), name), "r")
+end
+
+local function fetch_url(rockspec)
+ local file, temp_dir, err_code, err_file, err_temp_dir = fetch.fetch_sources(rockspec, false)
+ if err_code == "source.dir" then
+ file, temp_dir = err_file, err_temp_dir
+ elseif not file then
+ util.warning("Could not fetch sources - " .. temp_dir)
+ return false
+ end
+ util.printout("File successfully downloaded. Making checksum and checking base dir...")
+ if dir.is_basic_protocol(rockspec.source.protocol) then
+ rockspec.source.md5 = fs.get_md5(file)
+ end
+ local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, rockspec.source.url)
+ return true, found_dir or inferred_dir, temp_dir
+end
+
+local lua_version_dep = {
+ ["5.1"] = "lua ~> 5.1",
+ ["5.2"] = "lua ~> 5.2",
+ ["5.3"] = "lua ~> 5.3",
+ ["5.4"] = "lua ~> 5.4",
+ ["5.1,5.2"] = "lua >= 5.1, < 5.3",
+ ["5.2,5.3"] = "lua >= 5.2, < 5.4",
+ ["5.3,5.4"] = "lua >= 5.3, < 5.5",
+ ["5.1,5.2,5.3"] = "lua >= 5.1, < 5.4",
+ ["5.2,5.3,5.4"] = "lua >= 5.2, < 5.5",
+ ["5.1,5.2,5.3,5.4"] = "lua >= 5.1, < 5.5",
+}
+
+local simple_scm_protocols = {
+ git = true,
+ ["git+http"] = true,
+ ["git+https"] = true,
+ ["git+ssh"] = true,
+ hg = true,
+ ["hg+http"] = true,
+ ["hg+https"] = true,
+ ["hg+ssh"] = true,
+}
+
+local detect_url
+do
+ local function detect_url_from_command(program, args, directory)
+ local command = fs.Q(cfg.variables[program:upper()]) .. " " .. args
+ local pipe = io.popen(fs.command_at(directory, fs.quiet_stderr(command)))
+ if not pipe then return nil end
+ local url = pipe:read("*a"):match("^([^\r\n]+)")
+ pipe:close()
+ if not url then return nil end
+ if url:match("^[^@:/]+@[^@:/]+:.*$") then
+ local u, h, p = url:match("^([^@]+)@([^:]+):(.*)$")
+ url = program .. "+ssh://" .. u .. "@" .. h .. "/" .. p
+ elseif not util.starts_with(url, program .. "://") then
+ url = program .. "+" .. url
+ end
+
+ if (simple_scm_protocols)[dir.split_url(url)] then
+ return url
+ end
+ end
+
+ local function detect_scm_url(directory)
+ return detect_url_from_command("git", "config --get remote.origin.url", directory) or
+ detect_url_from_command("hg", "paths default", directory)
+ end
+
+ detect_url = function(url_or_dir)
+ if url_or_dir:match("://") then
+ return url_or_dir
+ else
+ return detect_scm_url(url_or_dir) or "*** please add URL for source tarball, zip or repository here ***"
+ end
+ end
+end
+
+local function detect_homepage(url, homepage)
+ if homepage then
+ return homepage
+ end
+ local url_protocol, url_path = dir.split_url(url)
+
+ if (simple_scm_protocols)[url_protocol] then
+ for _, domain in ipairs({ "github.com", "bitbucket.org", "gitlab.com" }) do
+ if util.starts_with(url_path, domain) then
+ return "https://" .. url_path:gsub("%.git$", "")
+ end
+ end
+ end
+
+ return "*** please enter a project homepage ***"
+end
+
+local function detect_description()
+ local fd = open_file("README.md") or open_file("README")
+ if not fd then return end
+ local data = fd:read("*a")
+ fd:close()
+ local paragraph = data:match("\n\n([^%[].-)\n\n")
+ if not paragraph then paragraph = data:match("\n\n(.*)") end
+ local summary, detailed
+ if paragraph then
+ detailed = paragraph
+
+ if #paragraph < 80 then
+ summary = paragraph:gsub("\n", "")
+ else
+ summary = paragraph:gsub("\n", " "):match("([^.]*%.) ")
+ end
+ end
+ return summary, detailed
+end
+
+local licenses = {
+ [78656] = "MIT",
+ [49311] = "ISC",
+}
+
+local function detect_license(data)
+ local strip_copyright = (data:gsub("^Copyright [^\n]*\n", ""))
+ local sum = 0
+ for i = 1, #strip_copyright do
+ local num = string.byte(strip_copyright:sub(i, i))
+ if num > 32 and num <= 128 then
+ sum = sum + num
+ end
+ end
+ return licenses[sum]
+end
+
+local function check_license()
+ local fd = open_file("COPYING") or open_file("LICENSE") or open_file("MIT-LICENSE.txt")
+ if not fd then return nil end
+ local data = fd:read("*a")
+ fd:close()
+ local license = detect_license(data)
+ if license then
+ return license, data
+ end
+ return nil, data
+end
+
+local function fill_as_builtin(rockspec, libs)
+ rockspec.build.type = "builtin"
+
+ local incdirs, libdirs
+ if libs then
+ incdirs, libdirs = {}, {}
+ for _, lib in ipairs(libs) do
+ local upper = lib:upper()
+ incdirs[#incdirs + 1] = "$(" .. upper .. "_INCDIR)"
+ libdirs[#libdirs + 1] = "$(" .. upper .. "_LIBDIR)"
+ end
+ end
+ (rockspec.build).modules, rockspec.build.install, rockspec.build.copy_directories = builtin.autodetect_modules(libs, incdirs, libdirs)
+end
+
+local function rockspec_cleanup(rockspec)
+ rockspec.source.file = nil
+ rockspec.source.protocol = nil
+ rockspec.source.identifier = nil
+ rockspec.source.dir = nil
+ rockspec.source.dir_set = nil
+ rockspec.source.pathname = nil
+ rockspec.variables = nil
+ rockspec.name = nil
+ rockspec.format_is_at_least = nil
+ rockspec.local_abs_filename = nil
+ rockspec.rocks_provided = nil
+
+ local dep_lists = {
+ dependencies = rockspec.dependencies,
+ build_dependencies = rockspec.build_dependencies,
+ test_dependencies = rockspec.test_dependencies,
+ }
+
+ for name, data in pairs(dep_lists) do
+ if not next(data) then
+ (rockspec)[name] = nil
+ else
+ for i, item in ipairs(data) do
+ data[i] = tostring(item)
+ end
+ end
+ end
+end
+
+function write_rockspec.command(args)
+ local name, version = args.name, args.version
+ local location = args.location
+
+ if not name then
+ location = "."
+ elseif not version then
+ location = name
+ name = nil
+ elseif not location then
+ location = version
+ version = nil
+ end
+
+ if args.tag then
+ if not version then
+ version = args.tag:gsub("^v", "")
+ end
+ end
+
+ local protocol, pathname = dir.split_url(location)
+ if protocol == "file" then
+ if pathname == "." then
+ name = name or dir.base_name(fs.current_dir())
+ end
+ elseif dir.is_basic_protocol(protocol) then
+ local filename = dir.base_name(location)
+ local newname, newversion = filename:match("(.*)-([^-]+)")
+ if newname then
+ name = name or newname
+ version = version or newversion:gsub("%.[a-z]+$", ""):gsub("%.tar$", "")
+ end
+ else
+ name = name or dir.base_name(location):gsub("%.[^.]+$", "")
+ end
+
+ if not name then
+ return nil, "Could not infer rock name. " .. util.see_help("write_rockspec")
+ end
+ version = version or "dev"
+
+ local filename = args.output or dir.path(fs.current_dir(), name:lower() .. "-" .. version .. "-1.rockspec")
+
+ local url = detect_url(location)
+ local homepage = detect_homepage(url, args.homepage)
+
+ local rockspec, err = rockspecs.from_persisted_table(filename, {
+ rockspec_format = args.rockspec_format,
+ package = name,
+ version = version .. "-1",
+ source = {
+ url = url,
+ tag = args.tag,
+ },
+ description = {
+ summary = args.summary or "*** please specify description summary ***",
+ detailed = args.detailed or "*** please enter a detailed description ***",
+ homepage = homepage,
+ license = args.license or "*** please specify a license ***",
+ },
+ dependencies = {
+ (lua_version_dep)[args.lua_versions],
+ },
+ build = {},
+ })
+ assert(not err, err)
+ rockspec.source.protocol = protocol
+
+ if not next(rockspec.dependencies) then
+ util.warning("Please specify supported Lua versions with --lua-versions=. " .. util.see_help("write_rockspec"))
+ end
+
+ local local_dir = location
+
+ if location:match("://") then
+ rockspec.source.file = dir.base_name(location)
+ if not dir.is_basic_protocol(rockspec.source.protocol) then
+ if version ~= "dev" then
+ rockspec.source.tag = args.tag or "v" .. version
+ end
+ end
+ rockspec.source.dir = nil
+ local ok, base_dir, temp_dir = fetch_url(rockspec)
+ if ok then
+ if base_dir ~= dir.base_name(location) then
+ rockspec.source.dir = base_dir
+ end
+ end
+ if base_dir then
+ local_dir = dir.path(temp_dir, base_dir)
+ else
+ local_dir = nil
+ end
+ end
+
+ if not local_dir then
+ local_dir = "."
+ end
+
+ local libs = nil
+ if args.lib then
+ libs = {}
+ rockspec.external_dependencies = {}
+ for lib in args.lib:gmatch("([^,]+)") do
+ table.insert(libs, lib)
+ rockspec.external_dependencies[lib:upper()] = {
+ library = lib,
+ }
+ end
+ end
+
+ local ok, err = fs.change_dir(local_dir)
+ if not ok then return nil, "Failed reaching files from project - error entering directory " .. local_dir end
+
+ if not (args.summary and args.detailed) then
+ local summary, detailed = detect_description()
+ rockspec.description.summary = args.summary or summary
+ rockspec.description.detailed = args.detailed or detailed
+ end
+
+ if not args.license then
+ local license, fulltext = check_license()
+ if license then
+ rockspec.description.license = license
+ elseif license then
+ util.title("Could not auto-detect type for project license:")
+ util.printout(fulltext)
+ util.printout()
+ util.title("Please fill in the source.license field manually or use --license.")
+ end
+ end
+
+ fill_as_builtin(rockspec, libs)
+
+ rockspec_cleanup(rockspec)
+
+ persist.save_from_table(filename, rockspec, type_rockspec.order)
+
+ util.printout()
+ util.printout("Wrote template at " .. filename .. " -- you should now edit and finish it.")
+ util.printout()
+
+ return true
+end
+
+return write_rockspec
diff --git a/src/luarocks/config.lua b/src/luarocks/config.lua
new file mode 100644
index 00000000..18ec90da
--- /dev/null
+++ b/src/luarocks/config.lua
@@ -0,0 +1,38 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local pairs = _tl_compat and _tl_compat.pairs or pairs; local config = {}
+
+local persist = require("luarocks.persist")
+
+local cfg_skip = {
+ errorcodes = true,
+ flags = true,
+ platforms = true,
+ root_dir = true,
+ upload_servers = true,
+}
+
+
+
+function config.should_skip(k, v)
+ return type(v) == "function" or cfg_skip[k]
+end
+
+local function cleanup(tbl)
+ local copy = {}
+ for k, v in pairs(tbl) do
+ if not (type(k) == "string" and config.should_skip(k, v)) then
+ copy[k] = v
+ end
+ end
+ return copy
+end
+
+function config.get_config_for_display(cfg)
+ return cleanup(cfg)
+end
+
+function config.to_string(cfg)
+ local cleancfg = config.get_config_for_display(cfg)
+ return persist.save_from_table_to_string(cleancfg)
+end
+
+return config
diff --git a/src/luarocks/core/dir.lua b/src/luarocks/core/dir.lua
new file mode 100644
index 00000000..b9b71c14
--- /dev/null
+++ b/src/luarocks/core/dir.lua
@@ -0,0 +1,95 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local package = _tl_compat and _tl_compat.package or package; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table; local dir = {}
+
+
+
+local dir_sep = package.config:sub(1, 1)
+
+local function unquote(c)
+ local first, last = c:sub(1, 1), c:sub(-1)
+ if (first == '"' and last == '"') or
+ (first == "'" and last == "'") then
+ return c:sub(2, -2)
+ end
+ return c
+end
+
+
+
+
+
+
+function dir.split_url(url)
+
+ url = unquote(url)
+ local protocol, pathname = url:match("^([^:]*)://(.*)")
+ if not protocol then
+ protocol = "file"
+ pathname = url
+ end
+ return protocol, pathname
+end
+
+
+
+
+
+
+
+function dir.normalize(name)
+ local protocol, pathname = dir.split_url(name)
+ pathname = pathname:gsub("\\", "/"):gsub("(.)/*$", "%1"):gsub("//", "/")
+ local pieces = {}
+ local drive = ""
+ if pathname:match("^.:") then
+ drive, pathname = pathname:match("^(.:)(.*)$")
+ end
+ pathname = pathname .. "/"
+ for piece in pathname:gmatch("(.-)/") do
+ if piece == ".." then
+ local prev = pieces[#pieces]
+ if not prev or prev == ".." then
+ table.insert(pieces, "..")
+ elseif prev ~= "" then
+ table.remove(pieces)
+ end
+ elseif piece ~= "." then
+ table.insert(pieces, piece)
+ end
+ end
+ if #pieces == 0 then
+ pathname = drive .. "."
+ elseif #pieces == 1 and pieces[1] == "" then
+ pathname = drive .. "/"
+ else
+ pathname = drive .. table.concat(pieces, "/")
+ end
+ if protocol ~= "file" then
+ pathname = protocol .. "://" .. pathname
+ else
+ pathname = pathname:gsub("/", dir_sep)
+ end
+ return pathname
+end
+
+
+
+
+
+
+
+
+
+
+
+function dir.path(...)
+ local t = { ... }
+ while t[1] == "" do
+ table.remove(t, 1)
+ end
+ for i, c in ipairs(t) do
+ t[i] = unquote(c)
+ end
+ return dir.normalize(table.concat(t, "/"))
+end
+
+return dir
diff --git a/src/luarocks/core/manif.lua b/src/luarocks/core/manif.lua
new file mode 100644
index 00000000..11a6cf41
--- /dev/null
+++ b/src/luarocks/core/manif.lua
@@ -0,0 +1,124 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local table = _tl_compat and _tl_compat.table or table
+
+local manif = {}
+
+
+local persist = require("luarocks.core.persist")
+local cfg = require("luarocks.core.cfg")
+local dir = require("luarocks.core.dir")
+local util = require("luarocks.core.util")
+local vers = require("luarocks.core.vers")
+local path = require("luarocks.core.path")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local manifest_cache = {}
+
+
+
+
+
+function manif.cache_manifest(repo_url, lua_version, manifest)
+ lua_version = lua_version or cfg.lua_version
+ manifest_cache[repo_url] = manifest_cache[repo_url] or {}
+ manifest_cache[repo_url][lua_version] = manifest
+end
+
+
+
+
+
+function manif.get_cached_manifest(repo_url, lua_version)
+ lua_version = lua_version or cfg.lua_version
+ return manifest_cache[repo_url] and manifest_cache[repo_url][lua_version]
+end
+
+
+
+
+
+
+
+
+function manif.manifest_loader(file, repo_url, lua_version)
+ local manifest, err, errcode = persist.load_into_table(file)
+ if not manifest and type(err) == "string" then
+ return nil, "Failed loading manifest for " .. repo_url .. ": " .. err, errcode
+ end
+
+ manif.cache_manifest(repo_url, lua_version, manifest)
+ return manifest, err, errcode
+end
+
+
+
+
+
+
+function manif.fast_load_local_manifest(repo_url)
+
+ local cached_manifest = manif.get_cached_manifest(repo_url)
+ if cached_manifest then
+ return cached_manifest
+ end
+
+ local pathname = dir.path(repo_url, "manifest")
+ return manif.manifest_loader(pathname, repo_url, nil)
+end
+
+function manif.load_rocks_tree_manifests(deps_mode)
+ local trees = {}
+ path.map_trees(deps_mode, function(tree)
+ local manifest = manif.fast_load_local_manifest(path.rocks_dir(tree))
+ if manifest then
+ table.insert(trees, { tree = tree, manifest = manifest })
+ end
+ end)
+ return trees
+end
+
+function manif.scan_dependencies(name, version, tree_manifests, dest)
+ if dest[name] then
+ return
+ end
+ dest[name] = version
+
+ for _, tree in ipairs(tree_manifests) do
+ local manifest = tree.manifest
+
+ local pkgdeps
+ if manifest.dependencies and manifest.dependencies[name] then
+ pkgdeps = manifest.dependencies[name][version]
+ end
+ if pkgdeps then
+ for _, dep in ipairs(pkgdeps) do
+ local pkg, constraints = dep.name, dep.constraints
+
+ for _, t in ipairs(tree_manifests) do
+ local entries = t.manifest.repository[pkg]
+ if entries then
+ for ver, _ in util.sortedpairs(entries, vers.compare_versions) do
+ if (not constraints) or vers.match_constraints(vers.parse_version(ver), constraints) then
+ manif.scan_dependencies(pkg, ver, tree_manifests, dest)
+ end
+ end
+ end
+ end
+ end
+ return
+ end
+ end
+end
+
+return manif
diff --git a/src/luarocks/core/path.lua b/src/luarocks/core/path.lua
new file mode 100644
index 00000000..b88b3bfc
--- /dev/null
+++ b/src/luarocks/core/path.lua
@@ -0,0 +1,151 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local package = _tl_compat and _tl_compat.package or package; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+local path = {}
+
+
+local cfg = require("luarocks.core.cfg")
+local dir = require("luarocks.core.dir")
+
+
+
+local dir_sep = package.config:sub(1, 1)
+
+
+function path.rocks_dir(tree)
+ if tree == nil then
+ tree = cfg.root_dir
+ end
+ if type(tree) == "string" then
+ return dir.path(tree, cfg.rocks_subdir)
+ end
+ return tree.rocks_dir or dir.path(tree.root, cfg.rocks_subdir)
+end
+
+
+
+
+
+
+
+function path.versioned_name(file, prefix, name, version)
+ assert(not name:match(dir_sep))
+
+ local rest = file:sub(#prefix + 1):gsub("^" .. dir_sep .. "*", "")
+ local name_version = (name .. "_" .. version):gsub("%-", "_"):gsub("%.", "_")
+ return dir.path(prefix, name_version .. "-" .. rest)
+end
+
+
+
+
+
+
+
+
+function path.path_to_module(file)
+
+ local exts = {}
+ local paths = package.path .. ";" .. package.cpath
+ for entry in paths:gmatch("[^;]+") do
+ local ext = entry:match("%.([a-z]+)$")
+ if ext then
+ exts[ext] = true
+ end
+ end
+
+ local name
+ for ext, _ in pairs(exts) do
+ name = file:match("(.*)%." .. ext .. "$")
+ if name then
+ name = name:gsub("[\\/]", ".")
+ break
+ end
+ end
+
+ if not name then name = file end
+
+
+ name = name:gsub("^%.+", ""):gsub("%.+$", "")
+
+ return name
+end
+
+function path.deploy_lua_dir(tree)
+ if type(tree) == "string" then
+ return dir.path(tree, cfg.lua_modules_path)
+ else
+ return tree.lua_dir or dir.path(tree.root, cfg.lua_modules_path)
+ end
+end
+
+function path.deploy_lib_dir(tree)
+ if type(tree) == "string" then
+ return dir.path(tree, cfg.lib_modules_path)
+ else
+ return tree.lib_dir or dir.path(tree.root, cfg.lib_modules_path)
+ end
+end
+
+local is_src_extension = { [".lua"] = true, [".tl"] = true, [".tld"] = true, [".moon"] = true }
+
+
+
+
+
+
+
+
+
+function path.which_i(file_name, name, version, tree, i)
+ local deploy_dir
+ local extension = file_name:match("%.[a-z]+$")
+ if is_src_extension[extension] then
+ deploy_dir = path.deploy_lua_dir(tree)
+ file_name = dir.path(deploy_dir, file_name)
+ else
+ deploy_dir = path.deploy_lib_dir(tree)
+ file_name = dir.path(deploy_dir, file_name)
+ end
+ if i > 1 then
+ file_name = path.versioned_name(file_name, deploy_dir, name, version)
+ end
+ return file_name
+end
+
+function path.rocks_tree_to_string(tree)
+ if type(tree) == "string" then
+ return tree
+ else
+ return tree.root
+ end
+end
+
+
+
+
+
+
+
+
+function path.map_trees(deps_mode, fn, ...)
+ local result = {}
+ local current = cfg.root_dir or cfg.rocks_trees[1]
+ if deps_mode == "one" then
+ table.insert(result, (fn(current, ...)) or 0)
+ else
+ local use = false
+ if deps_mode == "all" then
+ use = true
+ end
+ for _, tree in ipairs(cfg.rocks_trees or {}) do
+ if dir.normalize(path.rocks_tree_to_string(tree)) == dir.normalize(path.rocks_tree_to_string(current)) then
+ use = true
+ end
+ if use then
+ table.insert(result, (fn(tree, ...)) or 0)
+ end
+ end
+ end
+ return result
+end
+
+return path
diff --git a/src/luarocks/core/persist.lua b/src/luarocks/core/persist.lua
new file mode 100644
index 00000000..258a42c0
--- /dev/null
+++ b/src/luarocks/core/persist.lua
@@ -0,0 +1,70 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local load = _tl_compat and _tl_compat.load or load; local pcall = _tl_compat and _tl_compat.pcall or pcall; local string = _tl_compat and _tl_compat.string or string
+local persist = {}
+
+
+
+
+
+
+
+
+
+
+function persist.run_file(filename, env)
+ local fd, open_err = io.open(filename)
+ if not fd then
+ return nil, open_err, "open"
+ end
+ local str, read_err = fd:read("*a")
+ fd:close()
+ if not str then
+ return nil, read_err, "open"
+ end
+ str = str:gsub("^#![^\n]*\n", "")
+ local chunk, ran, err
+ chunk, err = load(str, filename, "t", env)
+ if chunk then
+ ran, err = pcall(chunk)
+ end
+ if not chunk then
+ return nil, "Error loading file: " .. tostring(err), "load"
+ end
+ if not ran then
+ return nil, "Error running file: " .. tostring(err), "run"
+ end
+ return true, err
+end
+
+
+
+
+
+
+
+
+
+
+
+function persist.load_into_table(filename, tbl)
+
+ local result = tbl or {}
+ local globals = {}
+ local globals_mt = {
+ __index = function(_, k)
+ globals[k] = true
+ end,
+ }
+ local save_mt = getmetatable(result)
+ setmetatable(result, globals_mt)
+
+ local ok, err, errcode = persist.run_file(filename, result)
+
+ setmetatable(result, save_mt)
+
+ if not ok then
+ return nil, tostring(err), errcode
+ end
+ return result, globals
+end
+
+return persist
diff --git a/src/luarocks/core/sysdetect.lua b/src/luarocks/core/sysdetect.lua
new file mode 100644
index 00000000..3a2527f4
--- /dev/null
+++ b/src/luarocks/core/sysdetect.lua
@@ -0,0 +1,508 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local os = _tl_compat and _tl_compat.os or os; local package = _tl_compat and _tl_compat.package or package; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+
+
+
+
+
+local sysdetect = {}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local function hex(s)
+ return (s:gsub("$(..)", function(x)
+ return string.char(tonumber(x, 16))
+ end))
+end
+
+local function read_int8(fd)
+ if io.type(fd) == "closed file" then
+ return nil
+ end
+ local s = fd:read(1)
+ if not s then
+ fd:close()
+ return nil
+ end
+ return s:byte()
+end
+
+local function bytes2number(s, endian)
+ local r = 0
+ if endian == "little" then
+ for i = #s, 1, -1 do
+ r = r * 256 + s:byte(i, i)
+ end
+ else
+ for i = 1, #s do
+ r = r * 256 + s:byte(i, i)
+ end
+ end
+ return r
+end
+
+local function read(fd, bytes, endian)
+ if io.type(fd) == "closed file" then
+ return nil
+ end
+ local s = fd:read(bytes)
+ if not s then
+ fd:close()
+ return nil
+ end
+ return bytes2number(s, endian)
+end
+
+local function read_int32le(fd)
+ return read(fd, 4, "little")
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local endians = {
+ [0x01] = "little",
+ [0x02] = "big",
+}
+
+local e_osabi = {
+ [0x00] = "sysv",
+ [0x01] = "hpux",
+ [0x02] = "netbsd",
+ [0x03] = "linux",
+ [0x04] = "hurd",
+ [0x06] = "solaris",
+ [0x07] = "aix",
+ [0x08] = "irix",
+ [0x09] = "freebsd",
+ [0x0c] = "openbsd",
+}
+
+local e_machines = {
+ [0x02] = "sparc",
+ [0x03] = "x86",
+ [0x08] = "mips",
+ [0x0f] = "hppa",
+ [0x12] = "sparcv8",
+ [0x14] = "ppc",
+ [0x15] = "ppc64",
+ [0x16] = "s390",
+ [0x28] = "arm",
+ [0x2a] = "superh",
+ [0x2b] = "sparcv9",
+ [0x32] = "ia_64",
+ [0x3E] = "x86_64",
+ [0xB6] = "alpha",
+ [0xB7] = "aarch64",
+ [0xF3] = "riscv64",
+ [0x9026] = "alpha",
+}
+
+local SHT_NOTE = 7
+
+local function read_elf_section_headers(fd, hdr)
+ local endian = endians[hdr.endian]
+ local word = hdr.word
+
+ local strtab_offset
+ local sections = {}
+ local secarray = {}
+ for i = 0, hdr.e_shnum - 1 do
+ fd:seek("set", hdr.e_shoff + (i * hdr.e_shentsize))
+ local section = {}
+ section.sh_name_off = read(fd, 4, endian)
+ section.sh_type = read(fd, 4, endian)
+ section.sh_flags = read(fd, word, endian)
+ section.sh_addr = read(fd, word, endian)
+ section.sh_offset = read(fd, word, endian)
+ section.sh_size = read(fd, word, endian)
+ section.sh_link = read(fd, 4, endian)
+ section.sh_info = read(fd, 4, endian)
+ if section.sh_type == SHT_NOTE then
+ fd:seek("set", section.sh_offset)
+ section.namesz = read(fd, 4, endian)
+ section.descsz = read(fd, 4, endian)
+ section.type = read(fd, 4, endian)
+ section.namedata = fd:read(section.namesz):gsub("%z.*", "")
+ section.descdata = fd:read(section.descsz)
+ elseif i == hdr.e_shstrndx then
+ strtab_offset = section.sh_offset
+ end
+ table.insert(secarray, section)
+ end
+ if strtab_offset then
+ for _, section in ipairs(secarray) do
+ fd:seek("set", strtab_offset + section.sh_name_off)
+ section.name = fd:read(32):gsub("%z.*", "")
+ sections[section.name] = section
+ end
+ end
+ return sections
+end
+
+local function detect_elf_system(fd, hdr, sections)
+ local system = e_osabi[hdr.osabi]
+ local endian = endians[hdr.endian]
+
+ if system == "sysv" then
+ local abitag = sections[".note.ABI-tag"]
+ if abitag then
+ if abitag.namedata == "GNU" and abitag.type == 1 and
+ abitag.descdata:sub(0, 4) == "\0\0\0\0" then
+ return "linux"
+ end
+ elseif sections[".SUNW_version"] or
+ sections[".SUNW_signature"] then
+ return "solaris"
+ elseif sections[".note.netbsd.ident"] then
+ return "netbsd"
+ elseif sections[".note.openbsd.ident"] then
+ return "openbsd"
+ elseif sections[".note.tag"] and
+ sections[".note.tag"].namedata == "DragonFly" then
+ return "dragonfly"
+ end
+
+ local gnu_version_r = sections[".gnu.version_r"]
+ if gnu_version_r then
+
+ local dynstr = sections[".dynstr"].sh_offset
+
+ local idx = 0
+ for _ = 0, gnu_version_r.sh_info - 1 do
+ fd:seek("set", gnu_version_r.sh_offset + idx)
+ assert(read(fd, 2, endian))
+ local vn_cnt = read(fd, 2, endian)
+ local vn_file = read(fd, 4, endian)
+ local vn_next = read(fd, 2, endian)
+
+ fd:seek("set", dynstr + vn_file)
+ local libname = fd:read(64):gsub("%z.*", "")
+
+ if hdr.e_type == 0x03 and libname == "libroot.so" then
+ return "haiku"
+ elseif libname:match("linux") then
+ return "linux"
+ end
+
+ idx = idx + (vn_next * (vn_cnt + 1))
+ end
+ end
+
+ local procfile = io.open("/proc/sys/kernel/ostype")
+ if procfile then
+ local version = procfile:read(6)
+ procfile:close()
+ if version == "Linux\n" then
+ return "linux"
+ end
+ end
+ end
+
+ return system
+end
+
+local function read_elf_header(fd)
+ local hdr = {}
+
+ hdr.bits = read_int8(fd)
+ hdr.endian = read_int8(fd)
+ hdr.elf_version = read_int8(fd)
+ if hdr.elf_version ~= 1 then
+ return nil
+ end
+ hdr.osabi = read_int8(fd)
+ if not hdr.osabi then
+ return nil
+ end
+
+ local endian = endians[hdr.endian]
+ fd:seek("set", 0x10)
+ hdr.e_type = read(fd, 2, endian)
+ local machine = read(fd, 2, endian)
+ local processor = e_machines[machine] or "unknown"
+ if endian == "little" and processor == "ppc64" then
+ processor = "ppc64le"
+ end
+
+ local elfversion = read(fd, 4, endian)
+ if elfversion ~= 1 then
+ return nil
+ end
+
+ local word = (hdr.bits == 1) and 4 or 8
+ hdr.word = word
+
+ hdr.e_entry = read(fd, word, endian)
+ hdr.e_phoff = read(fd, word, endian)
+ hdr.e_shoff = read(fd, word, endian)
+ hdr.e_flags = read(fd, 4, endian)
+ hdr.e_ehsize = read(fd, 2, endian)
+ hdr.e_phentsize = read(fd, 2, endian)
+ hdr.e_phnum = read(fd, 2, endian)
+ hdr.e_shentsize = read(fd, 2, endian)
+ hdr.e_shnum = read(fd, 2, endian)
+ hdr.e_shstrndx = read(fd, 2, endian)
+
+ return hdr, processor
+end
+
+local function detect_elf(fd)
+ local hdr, processor = read_elf_header(fd)
+ if not hdr then
+ return nil
+ end
+ local sections = read_elf_section_headers(fd, hdr)
+ local system = detect_elf_system(fd, hdr, sections)
+ return system, processor
+end
+
+
+
+
+
+local mach_l64 = {
+ [7] = "x86_64",
+ [12] = "aarch64",
+}
+
+local mach_b64 = {
+ [0] = "ppc64",
+}
+
+local mach_l32 = {
+ [7] = "x86",
+ [12] = "arm",
+}
+
+local mach_b32 = {
+ [0] = "ppc",
+}
+
+local function detect_mach(magic, fd)
+ if not magic then
+ return nil
+ end
+
+ if magic == hex("$CA$FE$BA$BE") then
+
+ fd:seek("set", 0x12)
+ local offs = read_int8(fd)
+ if not offs then
+ return nil
+ end
+ fd:seek("set", offs * 256)
+ magic = fd:read(4)
+ return detect_mach(magic, fd)
+ end
+
+ local cputype = read_int8(fd)
+
+ if magic == hex("$CF$FA$ED$FE") then
+ return "macosx", mach_l64[cputype] or "unknown"
+ elseif magic == hex("$FE$ED$CF$FA") then
+ return "macosx", mach_b64[cputype] or "unknown"
+ elseif magic == hex("$CE$FA$ED$FE") then
+ return "macosx", mach_l32[cputype] or "unknown"
+ elseif magic == hex("$FE$ED$FA$CE") then
+ return "macosx", mach_b32[cputype] or "unknown"
+ end
+end
+
+
+
+
+
+local pe_machine = {
+ [0x8664] = "x86_64",
+ [0x01c0] = "arm",
+ [0x01c4] = "armv7l",
+ [0xaa64] = "arm64",
+ [0x014c] = "x86",
+}
+
+local function detect_pe(fd)
+ fd:seek("set", 60)
+ local peoffset = read_int32le(fd)
+ if not peoffset then
+ return nil
+ end
+ local system = "windows"
+ fd:seek("set", peoffset + 4)
+ local machine = read(fd, 2, "little")
+ local processor = pe_machine[machine]
+
+ local rdata_pos_s = fd:read(736):match(".rdata%z%z............(....)")
+ if rdata_pos_s then
+ local rdata_pos = bytes2number(rdata_pos_s, "little")
+ fd:seek("set", rdata_pos)
+ local data = fd:read(512)
+ if data:match("cygwin") or data:match("cyggcc") then
+ system = "cygwin"
+ end
+ end
+
+ return system, processor or "unknown"
+end
+
+
+
+
+
+function sysdetect.detect_file(file)
+ local fd = io.open(file, "rb")
+ if not fd then
+ return nil
+ end
+ local magic = fd:read(4)
+ if magic == hex("$7FELF") then
+ return detect_elf(fd)
+ end
+ if magic == hex("MZ$90$00") then
+ return detect_pe(fd)
+ end
+ return detect_mach(magic, fd)
+end
+
+local cache_system
+local cache_processor
+
+function sysdetect.detect(input_file)
+ local dirsep = package.config:sub(1, 1)
+ local files
+
+ if input_file then
+ files = { input_file }
+ else
+ if cache_system then
+ return cache_system, cache_processor
+ end
+
+ local PATHsep
+ local interp = arg and arg[-1]
+ if dirsep == "/" then
+
+ files = {
+ "/bin/sh",
+ "/proc/self/exe",
+ }
+ PATHsep = ":"
+ else
+
+ local systemroot = os.getenv("SystemRoot")
+ files = {
+ systemroot .. "\\system32\\notepad.exe",
+ systemroot .. "\\explorer.exe",
+ }
+ if interp and not interp:lower():match("exe$") then
+ interp = interp .. ".exe"
+ end
+ PATHsep = ";"
+ end
+ if interp then
+ if interp:match(dirsep) then
+
+ table.insert(files, 1, interp)
+ else
+ for d in (os.getenv("PATH") or ""):gmatch("[^" .. PATHsep .. "]+") do
+ table.insert(files, d .. dirsep .. interp)
+ end
+ end
+ end
+ end
+ for _, f in ipairs(files) do
+ local system, processor = sysdetect.detect_file(f)
+ if system then
+ cache_system = system
+ cache_processor = processor
+ return system, processor
+ end
+ end
+end
+
+return sysdetect
diff --git a/src/luarocks/core/types/query.lua b/src/luarocks/core/types/query.lua
new file mode 100644
index 00000000..e8f318fe
--- /dev/null
+++ b/src/luarocks/core/types/query.lua
@@ -0,0 +1,13 @@
+
+
+local query = {Query = {}, }
+
+
+
+
+
+
+
+
+
+return query
diff --git a/src/luarocks/core/types/result.lua b/src/luarocks/core/types/result.lua
new file mode 100644
index 00000000..974bd23a
--- /dev/null
+++ b/src/luarocks/core/types/result.lua
@@ -0,0 +1,14 @@
+
+
+local result = {Result = {}, }
+
+
+
+
+
+
+
+
+
+
+return result
diff --git a/src/luarocks/core/types/rockspec.lua b/src/luarocks/core/types/rockspec.lua
new file mode 100644
index 00000000..5d90fbf1
--- /dev/null
+++ b/src/luarocks/core/types/rockspec.lua
@@ -0,0 +1,78 @@
+
+
+
+
+local rockspec = {Description = {}, Source = {}, Test = {}, Dependencies = {}, Hooks = {}, Deploy = {}, Rockspec = {}, }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+return rockspec
diff --git a/src/luarocks/core/util.lua b/src/luarocks/core/util.lua
new file mode 100644
index 00000000..6a457c54
--- /dev/null
+++ b/src/luarocks/core/util.lua
@@ -0,0 +1,334 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local debug = _tl_compat and _tl_compat.debug or debug; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local math = _tl_compat and _tl_compat.math or math; local os = _tl_compat and _tl_compat.os or os; local package = _tl_compat and _tl_compat.package or package; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+local util = {}
+
+
+
+
+
+
+
+local dir_sep = package.config:sub(1, 1)
+
+
+
+
+
+
+
+
+function util.popen_read(cmd, spec)
+ local tmpfile = (dir_sep == "\\") and
+ (os.getenv("TMP") .. "/luarocks-" .. tostring(math.floor(math.random() * 10000))) or
+ os.tmpname()
+ os.execute(cmd .. " > " .. tmpfile)
+ local fd = io.open(tmpfile, "rb")
+ if not fd then
+ os.remove(tmpfile)
+ return ""
+ end
+ local out = fd:read(spec or "*l")
+ fd:close()
+ os.remove(tmpfile)
+ return out or ""
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+function util.show_table(t, tname, top_indent)
+ local cart
+ local autoref
+
+ local function is_empty_table(tbl) return next(tbl) == nil end
+
+ local function basic_serialize(o)
+ local so = tostring(o)
+ if type(o) == "function" then
+ local info = debug and debug.getinfo(o, "S")
+ if not info then
+ return ("%q"):format(so)
+ end
+
+ if info.what == "C" then
+ return ("%q"):format(so .. ", C function")
+ else
+
+ return ("%q"):format(so .. ", defined in (" .. info.linedefined .. "-" .. info.lastlinedefined .. ")" .. info.source)
+ end
+ elseif type(o) == "number" then
+ return so
+ else
+ return ("%q"):format(so)
+ end
+ end
+
+ local function add_to_cart(value, name, indent, saved, field)
+ indent = indent or ""
+ saved = saved or {}
+ field = field or name
+
+ cart = cart .. indent .. field
+
+ if not (type(value) == "table") then
+ cart = cart .. " = " .. basic_serialize(value) .. ";\n"
+ else
+ if saved[value] then
+ cart = cart .. " = {}; -- " .. saved[value] .. " (self reference)\n"
+ autoref = autoref .. name .. " = " .. saved[value] .. ";\n"
+ else
+ saved[value] = name
+ if is_empty_table(value) then
+ cart = cart .. " = {};\n"
+ else
+ cart = cart .. " = {\n"
+ for k, v in pairs(value) do
+ k = basic_serialize(k)
+ local fname = ("%s[%s]"):format(name, k)
+ field = ("[%s]"):format(k)
+
+ add_to_cart(v, fname, indent .. " ", saved, field)
+ end
+ cart = cart .. indent .. "};\n"
+ end
+ end
+ end
+ end
+
+ tname = tname or "__unnamed__"
+ if not (type(t) == "table") then
+ return tname .. " = " .. basic_serialize(t)
+ end
+ cart, autoref = "", ""
+ add_to_cart(t, tname, top_indent)
+ return cart .. autoref
+end
+
+
+
+
+
+
+function util.matchquote(s)
+ return (s:gsub("[?%-+*%[%].%%()$^]", "%%%1"))
+end
+
+
+
+
+
+function util.deep_merge(dst, src)
+ for k, v in pairs(src) do
+ if type(v) == "table" then
+ local dstk = dst[k]
+ if dstk == nil then
+ dst[k] = {}
+ dstk = dst[k]
+ end
+ if type(dstk) == "table" then
+ util.deep_merge(dstk, v)
+ else
+ dst[k] = v
+ end
+ else
+ dst[k] = v
+ end
+ end
+end
+
+
+
+
+
+function util.deep_merge_under(dst, src)
+ for k, v in pairs(src) do
+ if type(v) == "table" then
+ local dstk = dst[k]
+ if dstk == nil then
+ dst[k] = {}
+ dstk = dst[k]
+ end
+ if type(dstk) == "table" then
+ util.deep_merge_under(dstk, v)
+ end
+ elseif dst[k] == nil then
+ dst[k] = v
+ end
+ end
+end
+
+
+
+function util.split_string(str, delim, maxNb)
+
+ if string.find(str, delim) == nil then
+ return { str }
+ end
+ if maxNb == nil or maxNb < 1 then
+ maxNb = 0
+ end
+ local result = {}
+ local pat = "(.-)" .. delim .. "()"
+ local nb = 0
+ local lastPos
+ for part, pos in string.gmatch(str, pat) do
+ nb = nb + 1
+ result[nb] = part
+ lastPos = tonumber(pos)
+ if nb == maxNb then break end
+ end
+
+ if nb ~= maxNb then
+ result[nb + 1] = string.sub(str, lastPos)
+ end
+ return result
+end
+
+
+
+
+
+
+
+
+
+
+
+function util.cleanup_path(list, sep, lua_version, keep_first)
+
+ list = list:gsub(dir_sep, "/")
+
+ local parts = util.split_string(list, sep)
+ local final, entries = {}, {}
+ local start, stop, step
+
+ if keep_first then
+ start, stop, step = 1, #parts, 1
+ else
+ start, stop, step = #parts, 1, -1
+ end
+
+ for i = start, stop, step do
+ local part = parts[i]:gsub("//", "/")
+ if lua_version then
+ part = part:gsub("/lua/([%d.]+)/", function(part_version)
+ if part_version:sub(1, #lua_version) ~= lua_version then
+ return "/lua/" .. lua_version .. "/"
+ end
+ end)
+ end
+ if not entries[part] then
+ local at = keep_first and #final + 1 or 1
+ table.insert(final, at, part)
+ entries[part] = true
+ end
+ end
+
+ return (table.concat(final, sep):gsub("/", dir_sep))
+end
+
+
+
+
+function util.keys(tbl)
+ local ks = {}
+ for k, _ in pairs(tbl) do
+ table.insert(ks, k)
+ end
+ return ks
+end
+
+
+function util.printerr(...)
+ io.stderr:write(table.concat({ ... }, "\t"))
+ io.stderr:write("\n")
+end
+
+
+
+function util.warning(msg)
+ util.printerr("Warning: " .. msg)
+end
+
+
+local function default_sort(a, b)
+ local ta = type(a)
+ local tb = type(b)
+ if ta == "number" and tb == "number" then
+ return tonumber(a) < tonumber(b)
+ elseif ta == "number" then
+ return true
+ elseif tb == "number" then
+ return false
+ else
+ return tostring(a) < tostring(b)
+ end
+end
+
+
+
+
+
+
+
+
+
+
+
+function util.sortedpairs(tbl, sort_by)
+ local keys = util.keys(tbl)
+ local sub_orders = nil
+
+ if sort_by == nil then
+ table.sort(keys, default_sort)
+ elseif type(sort_by) == "function" then
+ table.sort(keys, sort_by)
+ else
+
+
+ sub_orders = sort_by.sub_orders
+
+ local seen_ordered_key = {}
+
+ local my_ordered_keys = {}
+
+ for _, key in ipairs(sort_by) do
+ if tbl[key] then
+ seen_ordered_key[key] = true
+ table.insert(my_ordered_keys, key)
+ end
+ end
+
+ table.sort(keys, default_sort)
+
+ for _, key in ipairs(keys) do
+ if not seen_ordered_key[key] then
+ table.insert(my_ordered_keys, key)
+ end
+ end
+
+ keys = my_ordered_keys
+ end
+
+ local i = 1
+ return function()
+ local key = keys[i]
+ i = i + 1
+ return key, tbl[key], sub_orders and sub_orders[key]
+ end
+end
+
+return util
diff --git a/src/luarocks/core/vers.lua b/src/luarocks/core/vers.lua
new file mode 100644
index 00000000..4ffb8c0d
--- /dev/null
+++ b/src/luarocks/core/vers.lua
@@ -0,0 +1,211 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local math = _tl_compat and _tl_compat.math or math; local string = _tl_compat and _tl_compat.string or string; local vers = {}
+
+
+local util = require("luarocks.core.util")
+
+
+
+
+local deltas = {
+ dev = 120000000,
+ scm = 110000000,
+ cvs = 100000000,
+ rc = -1000,
+ pre = -10000,
+ beta = -100000,
+ alpha = -1000000,
+}
+
+local version_mt = {
+
+
+
+
+
+
+
+ __eq = function(v1, v2)
+ if #v1 ~= #v2 then
+ return false
+ end
+ for i = 1, #v1 do
+ if v1[i] ~= v2[i] then
+ return false
+ end
+ end
+ if v1.revision and v2.revision then
+ return (v1.revision == v2.revision)
+ end
+ return true
+ end,
+
+
+
+
+
+
+
+ __lt = function(v1, v2)
+ for i = 1, math.max(#v1, #v2) do
+ local v1i, v2i = v1[i] or 0, v2[i] or 0
+ if v1i ~= v2i then
+ return (v1i < v2i)
+ end
+ end
+ if v1.revision and v2.revision then
+ return (v1.revision < v2.revision)
+ end
+ return false
+ end,
+
+
+
+ __le = function(v1, v2)
+ return not (v2 < v1)
+ end,
+
+
+
+ __tostring = function(v)
+ return v.string
+ end,
+}
+
+local version_cache = {}
+setmetatable(version_cache, {
+ __mode = "kv",
+})
+
+
+
+
+
+
+
+
+
+
+
+
+function vers.parse_version(vstring)
+ if not vstring then return nil end
+
+ local cached = version_cache[vstring]
+ if cached then
+ return cached
+ end
+
+ local version = {}
+ local i = 1
+
+ local function add_token(number)
+ version[i] = version[i] and version[i] + number / 100000 or number
+ i = i + 1
+ end
+
+
+ local v = vstring:match("^%s*(.*)%s*$")
+ version.string = v
+
+ local main, revision = v:match("(.*)%-(%d+)$")
+ if revision then
+ v = main
+ version.revision = tonumber(revision)
+ end
+ while #v > 0 do
+
+ local token, rest = v:match("^(%d+)[%.%-%_]*(.*)")
+ if token then
+ add_token(tonumber(token))
+ else
+
+ token, rest = v:match("^(%a+)[%.%-%_]*(.*)")
+ if not token then
+ util.warning("version number '" .. v .. "' could not be parsed.")
+ version[i] = 0
+ break
+ end
+ version[i] = deltas[token] or (token:byte() / 1000)
+ end
+ v = rest
+ end
+ setmetatable(version, version_mt)
+ version_cache[vstring] = version
+ return version
+end
+
+
+
+
+
+function vers.compare_versions(a, b)
+ if a == b then
+ return false
+ end
+ return vers.parse_version(b) < vers.parse_version(a)
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+local function partial_match(input_version, input_requested)
+
+ local version, requested
+
+ if not (type(input_version) == "table") then version = vers.parse_version(input_version)
+ else version = input_version end
+ if not (type(input_requested) == "table") then requested = vers.parse_version(input_requested)
+ else requested = input_requested end
+ if not (type(version) == "table") or not (type(requested) == "table") then return false end
+
+ for i, ri in ipairs(requested) do
+ local vi = version[i] or 0
+ if ri ~= vi then return false end
+ end
+ if requested.revision then
+ return requested.revision == version.revision
+ end
+ return true
+end
+
+
+
+
+
+
+function vers.match_constraints(version, constraints)
+ local ok = true
+ setmetatable(version, version_mt)
+ for _, constr in ipairs(constraints) do
+ local constr_version, constr_op = constr.version, constr.op
+ local cv
+ if type(constr_version) == "string" then
+ cv = vers.parse_version(constr_version)
+ constr.version = cv
+ else
+ cv = constr_version
+ end
+ setmetatable(cv, version_mt)
+ if constr_op == "==" then ok = version == cv
+ elseif constr_op == "~=" then ok = version ~= cv
+ elseif constr_op == ">" then ok = cv < version
+ elseif constr_op == "<" then ok = version < cv
+ elseif constr_op == ">=" then ok = cv <= version
+ elseif constr_op == "<=" then ok = version <= cv
+ elseif constr_op == "~>" then ok = partial_match(version, cv)
+ end
+ if not ok then break end
+ end
+ return ok
+end
+
+return vers
diff --git a/src/luarocks/deplocks.lua b/src/luarocks/deplocks.lua
new file mode 100644
index 00000000..8a21ef1b
--- /dev/null
+++ b/src/luarocks/deplocks.lua
@@ -0,0 +1,111 @@
+local deplocks = {}
+
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+local util = require("luarocks.util")
+local persist = require("luarocks.persist")
+
+
+
+local deptable = {}
+local deptable_mode = "start"
+local deplock_abs_filename
+local deplock_root_rock_name
+
+function deplocks.init(root_rock_name, dirname)
+ if deptable_mode ~= "start" then
+ return
+ end
+ deptable_mode = "create"
+
+ local filename = dir.path(dirname, "luarocks.lock")
+ deplock_abs_filename = fs.absolute_name(filename)
+ deplock_root_rock_name = root_rock_name
+
+ deptable = {}
+end
+
+function deplocks.get_abs_filename(root_rock_name)
+ if root_rock_name == deplock_root_rock_name then
+ return deplock_abs_filename
+ end
+end
+
+function deplocks.load(root_rock_name, dirname)
+ if deptable_mode ~= "start" then
+ return true, nil
+ end
+ deptable_mode = "locked"
+
+ local filename = dir.path(dirname, "luarocks.lock")
+ local _, result, errcode = persist.run_file(filename, {})
+ if errcode == "load" or errcode == "run" then
+
+ return nil, nil, "Could not read existing lockfile " .. filename
+ end
+
+ if errcode == "open" then
+
+ return true, nil
+ end
+
+ deplock_abs_filename = fs.absolute_name(filename)
+ deplock_root_rock_name = root_rock_name
+
+ deptable = result
+ return true, filename
+end
+
+function deplocks.add(depskey, name, version)
+ if deptable_mode == "locked" then
+ return
+ end
+
+ local dk = deptable[depskey]
+ if not dk then
+ dk = {}
+ deptable[depskey] = dk
+ end
+
+ if type(dk) == "table" and not dk[name] then
+ dk[name] = version
+ end
+end
+
+function deplocks.get(depskey, name)
+ local dk = deptable[depskey]
+ if not dk then
+ return nil
+ end
+ if type(dk) == "table" then
+ return dk[name]
+ else
+ return dk
+ end
+end
+
+function deplocks.write_file()
+ if deptable_mode ~= "create" then
+ return true
+ end
+
+ return persist.save_as_module(deplock_abs_filename, deptable)
+end
+
+
+function deplocks.proxy(depskey)
+ return setmetatable({}, {
+ __index = function(_, k)
+ return deplocks.get(depskey, k)
+ end,
+ __newindex = function(_, k, v)
+ return deplocks.add(depskey, k, v)
+ end,
+ })
+end
+
+function deplocks.each(depskey)
+ return util.sortedpairs(deptable[depskey] or {})
+end
+
+return deplocks
diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua
new file mode 100644
index 00000000..c960f3b7
--- /dev/null
+++ b/src/luarocks/deps.lua
@@ -0,0 +1,877 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+
+local deps = {}
+
+
+
+local cfg = require("luarocks.core.cfg")
+local manif = require("luarocks.manif")
+local path = require("luarocks.path")
+local dir = require("luarocks.dir")
+local fun = require("luarocks.fun")
+local util = require("luarocks.util")
+local vers = require("luarocks.core.vers")
+local queries = require("luarocks.queries")
+local deplocks = require("luarocks.deplocks")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local function prepare_get_versions(deps_mode, rocks_provided, depskey, skip_set)
+
+ return function(dep)
+ local versions, locations
+ local provided = rocks_provided[dep.name]
+ if provided then
+
+ versions, locations = { provided }, {}
+ else
+ if deps_mode == "none" then
+ deps_mode = "one"
+ end
+ versions, locations = manif.get_versions(dep, deps_mode)
+ end
+
+ if skip_set and skip_set[dep.name] then
+ for i = #versions, 1, -1 do
+ local v = versions[i]
+ if skip_set[dep.name][v] then
+ table.remove(versions, i)
+ end
+ end
+ end
+
+ local lockversion = deplocks.get(depskey, dep.name)
+
+ return versions, locations, lockversion, provided ~= nil
+ end
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+local function match_dep(depq,
+ get_versions)
+
+ local versions, locations, lockversion, provided = get_versions(depq)
+
+ local latest_version
+ local latest_vstring
+ for _, vstring in ipairs(versions) do
+ local version = vers.parse_version(vstring)
+ if vers.match_constraints(version, depq.constraints) then
+ if not latest_version or version > latest_version then
+ latest_version = version
+ latest_vstring = vstring
+ end
+ end
+ end
+
+ if lockversion and not locations[lockversion] then
+ local latest_matching_msg = ""
+ if latest_vstring and latest_vstring ~= lockversion then
+ latest_matching_msg = " (latest matching is " .. latest_vstring .. ")"
+ end
+ util.printout("Forcing " .. depq.name .. " to pinned version " .. lockversion .. latest_matching_msg)
+ return nil, nil, queries.new(depq.name, depq.namespace, lockversion)
+ end
+
+ return latest_vstring, locations[latest_vstring], depq, provided
+end
+
+local function match_all_deps(dependencies,
+ get_versions)
+
+ local matched, missing, no_upgrade = {}, {}, {}
+
+ for _, depq in ipairs(dependencies) do
+ local found, _, provided
+ found, _, depq, provided = match_dep(depq, get_versions)
+ if found then
+ if not provided then
+ matched[depq] = { name = depq.name, version = found }
+ end
+ else
+ if depq.constraints and depq.constraints[1] and depq.constraints[1].no_upgrade then
+ no_upgrade[depq.name] = depq
+ else
+ missing[depq.name] = depq
+ end
+ end
+ end
+ return matched, missing, no_upgrade
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+function deps.match_deps(dependencies, rocks_provided, deps_mode, skip_set)
+
+ local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies", skip_set)
+ return match_all_deps(dependencies, get_versions)
+end
+
+local function rock_status(dep, get_versions)
+ local installed, _, _, provided = match_dep(dep, get_versions)
+ local installation_type = provided and "provided by VM" or "installed"
+ return installed and installed .. " " .. installation_type .. ": success" or "not installed"
+end
+
+
+
+
+
+
+
+
+
+
+function deps.report_missing_dependencies(name, version, dependencies, deps_mode, rocks_provided)
+
+ if deps_mode == "none" then
+ return
+ end
+
+ local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies")
+
+ local first_missing_dep = true
+
+ for _, depq in ipairs(dependencies) do
+ local found, _
+ found, _, depq = match_dep(depq, get_versions)
+ if not found then
+ if first_missing_dep then
+ util.printout(("Missing dependencies for %s %s:"):format(name, version))
+ first_missing_dep = false
+ end
+
+ util.printout((" %s (%s)"):format(tostring(depq), rock_status(depq, get_versions)))
+ end
+ end
+end
+
+function deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey)
+
+ deps_mode = deps_mode or "all"
+ rocks_provided = rocks_provided or {}
+
+ local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey)
+
+ local found, where
+ found, where, dep = match_dep(dep, get_versions)
+ if found then
+ local tree_manifests = manif.load_rocks_tree_manifests(deps_mode)
+ manif.scan_dependencies(dep.name, found, tree_manifests, deplocks.proxy(depskey))
+ return true, found, where
+ end
+
+ local search = require("luarocks.search")
+
+ local url, search_err = search.find_suitable_rock(dep)
+ if not url then
+ return nil, "Could not satisfy dependency " .. tostring(dep) .. ": " .. search_err
+ end
+ util.printout("Installing " .. url)
+ local install_args = {
+ rock = url,
+ deps_mode = deps_mode,
+ namespace = dep.namespace,
+ verify = verify,
+ }
+ local ok, install_err, errcode = deps.installer(install_args)
+ if not ok then
+ return nil, "Failed installing dependency: " .. url .. " - " .. install_err, errcode
+ end
+
+ found, where = match_dep(dep, get_versions)
+ if not found then
+ return nil, "Repository inconsistency detected (previously unfinished/corrupted installation?)"
+ end
+ return true, found, where
+end
+
+local function check_supported_platforms(rockspec)
+ if rockspec.supported_platforms and next(rockspec.supported_platforms) then
+ local all_negative = true
+ local supported = false
+ for _, plat in ipairs(rockspec.supported_platforms) do
+ local neg
+ neg, plat = plat:match("^(!?)(.*)")
+ if neg == "!" then
+ if cfg.is_platform(plat) then
+ return nil, "This rockspec for " .. rockspec.package .. " does not support " .. plat .. " platforms."
+ end
+ else
+ all_negative = false
+ if cfg.is_platform(plat) then
+ supported = true
+ break
+ end
+ end
+ end
+ if supported == false and not all_negative then
+ local plats = cfg.print_platforms()
+ return nil, "This rockspec for " .. rockspec.package .. " does not support " .. plats .. " platforms."
+ end
+ end
+
+ return true
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+function deps.fulfill_dependencies(rockspec, depskey, deps_mode, verify, deplock_dir)
+ local name = rockspec.name
+ local version = rockspec.version
+ local rocks_provided = rockspec.rocks_provided
+
+ local ok, filename, err = deplocks.load(name, deplock_dir or ".")
+ if filename then
+ util.printout("Using dependencies pinned in lockfile: " .. filename)
+
+ local get_versions = prepare_get_versions("none", rocks_provided, depskey)
+ local dnsnamestr, dversionstr
+ for dnsname, dversion in deplocks.each(depskey) do
+ if type(dnsname) == "string" then
+ dnsnamestr = dnsname
+ end
+ if type(dversion) == "string" then
+ dversionstr = dversion
+ end
+ local dname, dnamespace = util.split_namespace(dnsnamestr)
+ local depq = queries.new(dname, dnamespace, dversionstr)
+
+ util.printout(("%s %s is pinned to %s (%s)"):format(
+ name, version, tostring(depq), rock_status(depq, get_versions)))
+
+ local okfullfill, errfullfill = deps.fulfill_dependency(depq, "none", rocks_provided, verify, depskey)
+ if not okfullfill then
+ return nil, errfullfill
+ end
+ end
+ util.printout()
+ return true
+ elseif err then
+ util.warning(err)
+ end
+
+ ok, err = check_supported_platforms(rockspec)
+ if not ok then
+ return nil, err
+ end
+
+ deps.report_missing_dependencies(name, version, (rockspec)[depskey].queries, deps_mode, rocks_provided)
+
+ util.printout()
+
+ local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey)
+ for _, depq in ipairs((rockspec)[depskey].queries) do
+
+ util.printout(("%s %s depends on %s (%s)"):format(
+ name, version, tostring(depq), rock_status(depq, get_versions)))
+
+ local okfulfill, found_or_err, _ = deps.fulfill_dependency(depq, deps_mode, rocks_provided, verify, depskey)
+ if okfulfill then
+ deplocks.add(depskey, depq.name, found_or_err)
+ else
+
+
+
+
+
+
+
+
+ return nil, found_or_err
+ end
+ end
+
+ return true
+end
+
+
+
+
+
+
+
+
+local function deconstruct_pattern(file, pattern)
+ local depattern = "^" .. (pattern:gsub("%.", "%%."):gsub("%*", ".*"):gsub("?", "(.*)")) .. "$"
+ return (file:match(depattern))
+end
+
+
+
+
+
+
+
+
+local function add_all_patterns(file, patterns, files)
+ for _, pattern in ipairs(patterns) do
+ table.insert(files, { #files + 1, (pattern:gsub("?", file)) })
+ end
+end
+
+local function get_external_deps_dirs(mode)
+ local patterns = cfg.external_deps_patterns
+ local subdirs = cfg.external_deps_subdirs
+ if mode == "install" then
+ patterns = cfg.runtime_external_deps_patterns
+ subdirs = cfg.runtime_external_deps_subdirs
+ end
+ local dirs = {
+ BINDIR = { subdir = subdirs.bin, testfile = "program", pattern = patterns.bin },
+ INCDIR = { subdir = subdirs.include, testfile = "header", pattern = patterns.include },
+ LIBDIR = { subdir = subdirs.lib, testfile = "library", pattern = patterns.lib },
+ }
+ if mode == "install" then
+ dirs.INCDIR = nil
+ end
+ return dirs
+end
+
+local function resolve_prefix(prefix, dirs)
+ if type(prefix) == "string" then
+ return prefix
+ elseif type(prefix) == "table" then
+ if prefix.bin then
+ dirs.BINDIR.subdir = prefix.bin
+ end
+ if prefix.include then
+ if dirs.INCDIR then
+ dirs.INCDIR.subdir = prefix.include
+ end
+ end
+ if prefix.lib then
+ dirs.LIBDIR.subdir = prefix.lib
+ end
+ return prefix.prefix
+ end
+end
+
+local function add_patterns_for_file(files, file, patterns)
+
+ if not (file:match("%.[a-z]+$") or file:match("%.[a-z]+%.")) then
+ add_all_patterns(file, patterns, files)
+ else
+ for _, pattern in ipairs(patterns) do
+ local matched = deconstruct_pattern(file, pattern)
+ if matched then
+ add_all_patterns(matched, patterns, files)
+ end
+ end
+ table.insert(files, { #files + 1, file })
+ end
+end
+
+local function check_external_dependency_at(
+ prefix,
+ name,
+ ext_files,
+ vars,
+ dirs,
+ err_files,
+ cache)
+
+ local fs = require("luarocks.fs")
+ cache = cache or {}
+
+ for dirname, dirdata in util.sortedpairs(dirs) do
+ local paths
+ local path_var_value = vars[name .. "_" .. dirname]
+ local dirdatastr = dirdata.subdir
+ if path_var_value then
+ paths = { path_var_value }
+ elseif type(dirdatastr) == "table" then
+ paths = {}
+ for i, v in ipairs(dirdatastr) do
+ paths[i] = dir.path(prefix, v)
+ end
+ else
+ paths = { dir.path(prefix, dirdatastr) }
+ end
+ local file_or_files = ext_files[dirdata.testfile]
+ if file_or_files then
+ local files = {}
+ if type(file_or_files) == "string" then
+ add_patterns_for_file(files, file_or_files, dirdata.pattern)
+ elseif type(file_or_files) == "table" then
+ for _, f in ipairs(file_or_files) do
+ add_patterns_for_file(files, f, dirdata.pattern)
+ end
+ end
+
+ local found = false
+ table.sort(files, function(a, b)
+ if (not a[2]:match("%*")) and b[2]:match("%*") then
+ return true
+ elseif a[2]:match("%*") and (not b[2]:match("%*")) then
+ return false
+ else
+ return a[1] < b[1]
+ end
+ end)
+ for _, fa in ipairs(files) do
+
+ local f = fa[2]
+
+ if f:match("%.so$") or f:match("%.dylib$") or f:match("%.dll$") then
+ f = f:gsub("%.[^.]+$", "." .. cfg.external_lib_extension)
+ end
+
+ local pattern
+ if f:match("%*") then
+ pattern = "^" .. f:gsub("([-.+])", "%%%1"):gsub("%*", ".*") .. "$"
+ f = "matching " .. f
+ end
+
+ for _, d in ipairs(paths) do
+ if pattern then
+ if not cache[d] then
+ cache[d] = fs.list_dir(d)
+ end
+ local match = string.match
+ for _, entry in ipairs(cache[d]) do
+ if match(entry, pattern) then
+ found = true
+ break
+ end
+ end
+ else
+ found = fs.is_file(dir.path(d, f))
+ end
+ if found then
+ dirdata.dir = d
+ dirdata.file = f
+ break
+ else
+ table.insert(err_files[dirdata.testfile], f .. " in " .. d)
+ end
+ end
+ if found then
+ break
+ end
+ end
+ if not found then
+ return nil, dirname, dirdata.testfile
+ end
+ else
+
+
+
+ dirdata.dir = paths[1]
+ for _, p in ipairs(paths) do
+ if fs.exists(p) then
+ dirdata.dir = p
+ break
+ end
+ end
+ end
+ end
+
+ for dirname, dirdata in pairs(dirs) do
+ vars[name .. "_" .. dirname] = dirdata.dir
+ vars[name .. "_" .. dirname .. "_FILE"] = dirdata.file
+ end
+ vars[name .. "_DIR"] = prefix
+ return true
+end
+
+local function check_external_dependency(
+ name,
+ ext_files,
+ vars,
+ mode,
+ cache)
+ local ok
+ local err_dirname
+ local err_testfile
+ local err_files = { program = {}, header = {}, library = {} }
+
+ local dirs = get_external_deps_dirs(mode)
+
+ local prefixes
+ if vars[name .. "_DIR"] then
+ prefixes = { vars[name .. "_DIR"] }
+ elseif vars.DEPS_DIR then
+ prefixes = { vars.DEPS_DIR }
+ else
+ prefixes = cfg.external_deps_dirs
+ end
+
+ for _, prefix in ipairs(prefixes) do
+ prefix = resolve_prefix(prefix, dirs)
+ if cfg.is_platform("mingw32") and name == "LUA" then
+ dirs.LIBDIR.pattern = fun.filter(util.deep_copy(dirs.LIBDIR.pattern), function(s)
+ return not s:match("%.a$")
+ end)
+ elseif cfg.is_platform("windows") and name == "LUA" then
+ dirs.LIBDIR.pattern = fun.filter(util.deep_copy(dirs.LIBDIR.pattern), function(s)
+ return not s:match("%.dll$")
+ end)
+ end
+ ok, err_dirname, err_testfile = check_external_dependency_at(prefix, name, ext_files, vars, dirs, err_files, cache)
+ if ok then
+ return true
+ end
+ end
+
+ return nil, err_dirname, err_testfile, err_files
+end
+
+function deps.autodetect_external_dependencies(build)
+
+ if not build or not (build).modules then
+ return nil
+ end
+
+ local extdeps = {}
+ local any = false
+ for _, data in pairs((build).modules) do
+ if type(data) == "table" and data.libraries then
+ local libraries
+ local librariesstr = data.libraries
+ if type(librariesstr) == "string" then
+ libraries = { librariesstr }
+ else
+ libraries = librariesstr
+ end
+ local incdirs = {}
+ local libdirs = {}
+ for _, lib in ipairs(libraries) do
+ local upper = lib:upper():gsub("%+", "P"):gsub("[^%w]", "_")
+ any = true
+ extdeps[upper] = { library = lib }
+ table.insert(incdirs, "$(" .. upper .. "_INCDIR)")
+ table.insert(libdirs, "$(" .. upper .. "_LIBDIR)")
+ end
+ if not data.incdirs then
+ data.incdirs = incdirs
+ end
+ if not data.libdirs then
+ data.libdirs = libdirs
+ end
+ end
+ end
+ return any and extdeps or nil
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+function deps.check_external_deps(rockspec, mode)
+
+ if not rockspec.external_dependencies then
+ rockspec.external_dependencies = deps.autodetect_external_dependencies(rockspec.build)
+ end
+ if not rockspec.external_dependencies then
+ return true
+ end
+
+ for name, ext_files in util.sortedpairs(rockspec.external_dependencies) do
+ local ok, err_dirname, err_testfile, err_files = check_external_dependency(name, ext_files, rockspec.variables, mode)
+ if not ok then
+ local lines = { "Could not find " .. err_testfile .. " file for " .. name }
+
+ local err_paths = {}
+ for _, err_file in ipairs(err_files[err_testfile]) do
+ if not err_paths[err_file] then
+ err_paths[err_file] = true
+ table.insert(lines, " No file " .. err_file)
+ end
+ end
+
+ table.insert(lines, "You may have to install " .. name .. " in your system and/or pass " .. name .. "_DIR or " .. name .. "_" .. err_dirname .. " to the luarocks command.")
+ table.insert(lines, "Example: luarocks install " .. rockspec.name .. " " .. name .. "_DIR=/usr/local")
+
+ return nil, table.concat(lines, "\n"), "dependency"
+ end
+ end
+ return true
+end
+
+
+
+
+
+
+
+
+function deps.scan_deps(results, mdeps, name, version, deps_mode)
+ assert(not name:match("/"))
+
+ local fetch = require("luarocks.fetch")
+
+ if results[name] then
+ return
+ end
+ if not mdeps[name] then mdeps[name] = {} end
+ local mdn = mdeps[name]
+ local dependencies = mdn[version]
+ local rocks_provided
+ if not dependencies then
+ local rockspec = fetch.load_local_rockspec(path.rockspec_file(name, version), false)
+ if not rockspec then
+ return
+ end
+ dependencies = rockspec.dependencies.queries
+ rocks_provided = rockspec.rocks_provided
+ mdn[version] = dependencies
+ else
+ rocks_provided = util.get_rocks_provided()
+ end
+
+ local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies")
+
+ local matched = match_all_deps(dependencies, get_versions)
+ results[name] = version
+ for _, match in pairs(matched) do
+ deps.scan_deps(results, mdeps, match.name, match.version, deps_mode)
+ end
+end
+
+local function lua_h_exists(d, luaver)
+ local major, minor = luaver:match("(%d+)%.(%d+)")
+ local luanum = ("%s%02d"):format(major, tonumber(minor))
+
+ local lua_h = dir.path(d, "lua.h")
+ local fd = io.open(lua_h)
+ if fd then
+ local data = fd:read("*a")
+ fd:close()
+ if data:match("LUA_VERSION_NUM%s*" .. tostring(luanum)) then
+ return d ~= nil
+ end
+ return nil, "Lua header lua.h found at " .. d .. " does not match Lua version " .. luaver .. ". You can use `luarocks config variables.LUA_INCDIR ` to set the correct location.", "dependency", 2
+ end
+
+ return nil, "Failed finding Lua header lua.h (searched at " .. d .. "). You may need to install Lua development headers. You can use `luarocks config variables.LUA_INCDIR ` to set the correct location.", "dependency", 1
+end
+
+local function find_lua_incdir(prefix, luaver, luajitver)
+ luajitver = luajitver and luajitver:gsub("%-.*", "")
+ local shortv = luaver:gsub("%.", "")
+ local incdirs = {
+ prefix .. "/include/lua/" .. luaver,
+ prefix .. "/include/lua" .. luaver,
+ prefix .. "/include/lua-" .. luaver,
+ prefix .. "/include/lua" .. shortv,
+ prefix .. "/include",
+ prefix,
+ luajitver and (prefix .. "/include/luajit-" .. (luajitver:match("^(%d+%.%d+)") or "")),
+ }
+ local errprio = 0
+ local mainerr
+ for _, d in ipairs(incdirs) do
+ local ok, err, _, prio = lua_h_exists(d, luaver)
+ if ok then
+ return d
+ end
+ if prio > errprio then
+ mainerr = err
+ errprio = prio
+ end
+ end
+
+
+ return nil, mainerr
+end
+
+function deps.check_lua_incdir(vars)
+ if vars.LUA_INCDIR_OK == "ok" then
+ return true
+ end
+
+ local ljv = util.get_luajit_version()
+
+ if vars.LUA_INCDIR then
+ local ok, err = lua_h_exists(vars.LUA_INCDIR, cfg.lua_version)
+ if ok then
+ vars.LUA_INCDIR_OK = "ok"
+ end
+ return ok, err
+ end
+
+ if vars.LUA_DIR then
+ local d, err = find_lua_incdir(vars.LUA_DIR, cfg.lua_version, ljv)
+ if d then
+ vars.LUA_INCDIR = d
+ vars.LUA_INCDIR_OK = "ok"
+ return true
+ end
+ return nil, err
+ end
+
+ return nil, "Failed finding Lua headers; neither LUA_DIR or LUA_INCDIR are set. You may need to install them or configure LUA_INCDIR.", "dependency"
+end
+
+function deps.check_lua_libdir(vars)
+ if vars.LUA_LIBDIR_OK == "ok" then
+ return true
+ end
+
+ local fs = require("luarocks.fs")
+ local ljv = util.get_luajit_version()
+
+ if vars.LUA_LIBDIR and vars.LUALIB and fs.exists(dir.path(vars.LUA_LIBDIR, vars.LUALIB)) then
+ vars.LUA_LIBDIR_OK = "ok"
+ return true
+ end
+
+ local shortv = cfg.lua_version:gsub("%.", "")
+ local libnames = {
+ "lua" .. cfg.lua_version,
+ "lua" .. shortv,
+ "lua-" .. cfg.lua_version,
+ "lua-" .. shortv,
+ "lua",
+ }
+ if ljv then
+ table.insert(libnames, 1, "luajit-" .. cfg.lua_version)
+ table.insert(libnames, 2, "luajit")
+ end
+ local cache = {}
+ local save_LUA_INCDIR = vars.LUA_INCDIR
+ local ok, _, _, errfiles = check_external_dependency("LUA", { library = libnames }, vars, "build", cache)
+ vars.LUA_INCDIR = save_LUA_INCDIR
+ local err
+ if ok then
+ local filename = dir.path(vars.LUA_LIBDIR, vars.LUA_LIBDIR_FILE)
+ local fd = io.open(filename, "r")
+ if fd then
+ if not vars.LUA_LIBDIR_FILE:match((cfg.lua_version:gsub("%.", "%%.?"))) then
+
+ local txt = fd:read("*a")
+ ok = txt:find("Lua " .. cfg.lua_version, 1, true) or
+ txt:find("lua" .. (cfg.lua_version:gsub("%.", "")), 1, true) and
+ true
+ if not ok then
+ err = "Lua library at " .. filename .. " does not match Lua version " .. cfg.lua_version .. ". You can use `luarocks config variables.LUA_LIBDIR ` to set the correct location."
+ end
+ end
+
+ fd:close()
+ end
+ end
+
+ if ok then
+ vars.LUALIB = vars.LUA_LIBDIR_FILE
+ vars.LUA_LIBDIR_OK = "ok"
+ return true
+ else
+ err = err or "Failed finding the Lua library. You can use `luarocks config variables.LUA_LIBDIR ` to set the correct location."
+ return nil, err, "dependency", errfiles
+ end
+end
+
+function deps.get_deps_mode(args)
+ return args.deps_mode or cfg.deps_mode
+end
+
+
+
+
+
+
+
+function deps.check_dependencies(repo, deps_mode)
+ local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
+ if deps_mode == "none" then deps_mode = cfg.deps_mode end
+
+ local manifest = manif.load_manifest(rocks_dir)
+ if not manifest then
+ return
+ end
+
+ for name, versions in util.sortedpairs(manifest.repository) do
+ for version, version_entries in util.sortedpairs(versions, vers.compare_versions) do
+ for _, entry in ipairs(version_entries) do
+ if entry.arch == "installed" then
+ if manifest.dependencies[name] and manifest.dependencies[name][version] then
+ deps.report_missing_dependencies(name, version, manifest.dependencies[name][version], deps_mode, util.get_rocks_provided())
+ end
+ end
+ end
+ end
+ end
+end
+
+return deps
diff --git a/src/luarocks/dir.lua b/src/luarocks/dir.lua
new file mode 100644
index 00000000..b9989bbe
--- /dev/null
+++ b/src/luarocks/dir.lua
@@ -0,0 +1,65 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local package = _tl_compat and _tl_compat.package or package; local string = _tl_compat and _tl_compat.string or string
+
+local dir = {}
+
+
+
+
+
+local core = require("luarocks.core.dir")
+
+dir.path = core.path
+dir.split_url = core.split_url
+dir.normalize = core.normalize
+
+local dir_sep = package.config:sub(1, 1)
+
+
+
+
+
+function dir.base_name(pathname)
+
+ local b
+ b = pathname:gsub("[/\\]", "/")
+ b = b:gsub("/*$", "")
+ b = b:match(".*[/\\]([^/\\]*)")
+ b = b or pathname
+
+ return b
+end
+
+
+
+
+
+
+function dir.dir_name(pathname)
+
+ local d
+ d = pathname:gsub("[/\\]", "/")
+ d = d:gsub("/*$", "")
+ d = d:match("(.*)[/]+[^/]*")
+ d = d or ""
+ d = d:gsub("/", dir_sep)
+
+ return d
+end
+
+
+
+function dir.is_basic_protocol(protocol)
+ return protocol == "http" or protocol == "https" or protocol == "ftp" or protocol == "file"
+end
+
+function dir.deduce_base_dir(url)
+
+ local known_exts = {}
+ for _, ext in ipairs({ "zip", "git", "tgz", "tar", "gz", "bz2" }) do
+ known_exts[ext] = ""
+ end
+ local base = dir.base_name(url)
+ return (base:gsub("%.([^.]*)$", known_exts):gsub("%.tar", ""))
+end
+
+return dir
diff --git a/src/luarocks/download.lua b/src/luarocks/download.lua
new file mode 100644
index 00000000..068ade96
--- /dev/null
+++ b/src/luarocks/download.lua
@@ -0,0 +1,76 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pairs = _tl_compat and _tl_compat.pairs or pairs; local download = {}
+
+
+local path = require("luarocks.path")
+local fetch = require("luarocks.fetch")
+local search = require("luarocks.search")
+local queries = require("luarocks.queries")
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+local util = require("luarocks.util")
+
+local function get_file(filename)
+ local protocol, pathname = dir.split_url(filename)
+ if protocol == "file" then
+ local ok, err = fs.copy(pathname, fs.current_dir(), "read")
+ if ok then
+ return pathname
+ else
+ return nil, err
+ end
+ else
+
+ local ok, err = fetch.fetch_url(filename)
+ return ok, err
+ end
+end
+
+function download.download_all(arch, name, namespace, version)
+ local substring = (name == "")
+ local query = queries.new(name, namespace, version, substring, arch)
+ local search_err
+
+ local results = search.search_repos(query)
+ local has_result = false
+ local all_ok = true
+ local any_err = ""
+ for name, result in pairs(results) do
+ for version, items in pairs(result) do
+ for _, item in ipairs(items) do
+
+ if item.arch ~= "installed" then
+ has_result = true
+ local filename = path.make_url(item.repo, name, version, item.arch)
+ local ok, err = get_file(filename)
+ if not ok then
+ all_ok = false
+ any_err = any_err .. "\n" .. err
+ end
+ end
+ end
+ end
+ end
+
+ if has_result then
+ return all_ok, any_err
+ end
+
+ local rock = util.format_rock_name(name, namespace, version)
+ return nil, "Could not find a result named " .. rock .. (search_err and ": " .. search_err or ".")
+end
+
+function download.download_file(arch, name, namespace, version, check_lua_versions)
+ local query = queries.new(name, namespace, version, false, arch)
+ local search_err
+
+ local url
+ url, search_err = search.find_rock_checking_lua_versions(query, check_lua_versions)
+ if url then
+ return get_file(url)
+ end
+
+ local rock = util.format_rock_name(name, namespace, version)
+ return nil, "Could not find a result named " .. rock .. (search_err and ": " .. search_err or ".")
+end
+
+return download
diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua
new file mode 100644
index 00000000..7472ee3f
--- /dev/null
+++ b/src/luarocks/fetch.lua
@@ -0,0 +1,615 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pcall = _tl_compat and _tl_compat.pcall or pcall; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+
+local fetch = {Fetch = {}, }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+local rockspecs = require("luarocks.rockspecs")
+local signing = require("luarocks.signing")
+local persist = require("luarocks.persist")
+local util = require("luarocks.util")
+local cfg = require("luarocks.core.cfg")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+function fetch.fetch_caching(url, mirroring)
+ local repo_url, filename = url:match("^(.*)/([^/]+)$")
+ local name = repo_url:gsub("[/:]", "_")
+ local cache_dir = dir.path(cfg.local_cache, name)
+ local ok = fs.make_dir(cache_dir)
+
+ local cachefile = dir.path(cache_dir, filename)
+ local checkfile = cachefile .. ".check"
+
+ if (fs.file_age(checkfile) < 10 or
+ cfg.aggressive_cache and (not name:match("^manifest"))) and fs.exists(cachefile) then
+
+ return cachefile, nil, nil, true
+ end
+
+ local lock, errlock
+ if ok then
+ lock, errlock = fs.lock_access(cache_dir)
+ end
+
+ if not (ok and lock) then
+ cfg.local_cache = fs.make_temp_dir("local_cache")
+ if not cfg.local_cache then
+ return nil, "Failed creating temporary local_cache directory"
+ end
+ cache_dir = dir.path(cfg.local_cache, name)
+ ok = fs.make_dir(cache_dir)
+ if not ok then
+ return nil, "Failed creating temporary cache directory " .. cache_dir
+ end
+ lock = fs.lock_access(cache_dir)
+ end
+
+ local file, err, errcode, from_cache = fetch.fetch_url(url, cachefile, true, mirroring)
+ if not file then
+ fs.unlock_access(lock)
+ return nil, err or "Failed downloading " .. url, errcode
+ end
+
+ local fd, erropen = io.open(checkfile, "wb")
+ if erropen then
+ fs.unlock_access(lock)
+ return nil, erropen
+ end
+ fd:write("!")
+ fd:close()
+
+ fs.unlock_access(lock)
+ return file, nil, nil, from_cache
+end
+
+local function ensure_trailing_slash(url)
+ return (url:gsub("/*$", "/"))
+end
+
+local function is_url_relative_to_rocks_servers(url, servers)
+ for _, item in ipairs(servers) do
+ if type(item) == "table" then
+ for i, s in ipairs(item) do
+ local base = ensure_trailing_slash(s)
+ if string.find(url, base, 1, true) == 1 then
+ return i, url:sub(#base + 1), item
+ end
+ end
+ end
+ end
+end
+
+local function download_with_mirrors(url, filename, cache, servers)
+ local idx, rest, mirrors = is_url_relative_to_rocks_servers(url, servers)
+
+ if not idx then
+
+ return fs.download(url, filename, cache)
+ end
+
+
+ local err = "\n"
+ for i = idx, #mirrors do
+ local try_url = ensure_trailing_slash(mirrors[i]) .. rest
+ if i > idx then
+ util.warning("Failed downloading. Attempting mirror at " .. try_url)
+ end
+ local name, _, _, from_cache = fs.download(try_url, filename, cache)
+ if name then
+ return name, nil, nil, from_cache
+ else
+ err = err .. name .. "\n"
+ end
+ end
+
+ return nil, err, "network"
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+function fetch.fetch_url(url, filename, cache, mirroring)
+
+ local protocol, pathname = dir.split_url(url)
+ if protocol == "file" then
+ local fullname = fs.absolute_name(pathname)
+ if not fs.exists(fullname) then
+ local hint = (not pathname:match("^/")) and
+ (" - note that given path in rockspec is not absolute: " .. url) or
+ ""
+ return nil, "Local file not found: " .. fullname .. hint
+ end
+ filename = filename or dir.base_name(pathname)
+ local dstname = fs.absolute_name(dir.path(".", filename))
+ local ok, err
+ if fullname == dstname then
+ ok = true
+ else
+ ok, err = fs.copy(fullname, dstname)
+ end
+ if ok then
+ return dstname
+ else
+ return nil, "Failed copying local file " .. fullname .. " to " .. dstname .. ": " .. err
+ end
+ elseif dir.is_basic_protocol(protocol) then
+ local name, err, err_code, from_cache
+ if mirroring ~= "no_mirror" then
+ name, err, err_code, from_cache = download_with_mirrors(url, filename, cache, cfg.rocks_servers)
+ else
+ name, err, err_code, from_cache = fs.download(url, filename, cache)
+ end
+ if not name then
+ return nil, "Failed downloading " .. url .. (err and " - " .. err or ""), err_code
+ end
+ return name, nil, nil, from_cache
+ else
+ return nil, "Unsupported protocol " .. protocol
+ end
+end
+
+
+
+
+
+
+
+
+
+
+
+
+function fetch.fetch_url_at_temp_dir(url, tmpname, filename, cache)
+ filename = filename or dir.base_name(url)
+
+ local protocol, pathname = dir.split_url(url)
+ if protocol == "file" then
+ if fs.exists(pathname) then
+ return pathname, dir.dir_name(fs.absolute_name(pathname))
+ else
+ return nil, "File not found: " .. pathname
+ end
+ else
+ local temp_dir, errmake = fs.make_temp_dir(tmpname)
+ if not temp_dir then
+ return nil, "Failed creating temporary directory " .. tmpname .. ": " .. errmake
+ end
+ util.schedule_function(fs.delete, temp_dir)
+ local ok, errchange = fs.change_dir(temp_dir)
+ if not ok then return nil, errchange end
+
+ local file, err, errcode
+
+ if cache then
+ local cachefile
+ cachefile, err, errcode = fetch.fetch_caching(url)
+
+ if cachefile then
+ file = dir.path(temp_dir, filename)
+ fs.copy(cachefile, file)
+ end
+ end
+
+ if not file then
+ file, err, errcode = fetch.fetch_url(url, filename, cache)
+ end
+
+ fs.pop_dir()
+ if not file then
+ return nil, "Error fetching file: " .. err, errcode
+ end
+
+ return file, temp_dir
+ end
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+function fetch.find_base_dir(file, temp_dir, src_url, src_dir)
+ local ok, err = fs.change_dir(temp_dir)
+ if not ok then return nil, err end
+ fs.unpack_archive(file)
+
+ if not src_dir then
+ local rockspec = {
+ source = {
+ file = file,
+ dir = src_dir,
+ url = src_url,
+ },
+ }
+ ok, err = fetch.find_rockspec_source_dir(rockspec, ".")
+ if ok then
+ src_dir = rockspec.source.dir
+ end
+ end
+
+ local inferred_dir = src_dir or dir.deduce_base_dir(src_url)
+ local found_dir = nil
+ if fs.exists(inferred_dir) then
+ found_dir = inferred_dir
+ else
+ util.printerr("Directory " .. inferred_dir .. " not found")
+ local files = fs.list_dir()
+ if files then
+ table.sort(files)
+ for _, filename in ipairs(files) do
+ if fs.is_dir(filename) then
+ util.printerr("Found " .. filename)
+ found_dir = filename
+ break
+ end
+ end
+ end
+ end
+ fs.pop_dir()
+ return inferred_dir, found_dir
+end
+
+
+local function fetch_and_verify_signature_for(url, filename, tmpdir)
+ local sig_url = signing.signature_url(url)
+ local sig_file, errfetch, errcode = fetch.fetch_url_at_temp_dir(sig_url, tmpdir)
+ if not sig_file then
+ return nil, "Could not fetch signature file for verification: " .. errfetch, errcode
+ end
+
+ local ok, err = signing.verify_signature(filename, sig_file)
+ if not ok then
+ return nil, "Failed signature verification: " .. err
+ end
+
+ return fs.absolute_name(sig_file)
+end
+
+
+
+
+
+
+
+
+
+
+function fetch.fetch_and_unpack_rock(url, dest, verify)
+
+ local name = dir.base_name(url):match("(.*)%.[^.]*%.rock")
+ local tmpname = "luarocks-rock-" .. name
+
+ local rock_file, err, errcode = fetch.fetch_url_at_temp_dir(url, tmpname, nil, true)
+ if not rock_file then
+ return nil, "Could not fetch rock file: " .. err, errcode
+ end
+
+ local sig_file
+ if verify then
+ sig_file, err = fetch_and_verify_signature_for(url, rock_file, tmpname)
+ if err then
+ return nil, err
+ end
+ end
+
+ rock_file = fs.absolute_name(rock_file)
+
+ local unpack_dir
+ if dest then
+ unpack_dir = dest
+ local ok, errmake = fs.make_dir(unpack_dir)
+ if not ok then
+ return nil, "Failed unpacking rock file: " .. errmake
+ end
+ else
+ unpack_dir, err = fs.make_temp_dir(name)
+ if not unpack_dir then
+ return nil, "Failed creating temporary dir: " .. err
+ end
+ end
+ if not dest then
+ util.schedule_function(fs.delete, unpack_dir)
+ end
+ local ok, errchange = fs.change_dir(unpack_dir)
+ if not ok then return nil, errchange end
+ ok, err = fs.unzip(rock_file)
+ if not ok then
+ return nil, "Failed unpacking rock file: " .. rock_file .. ": " .. err
+ end
+ if sig_file then
+ ok, err = fs.copy(sig_file, ".")
+ if not ok then
+ return nil, "Failed copying signature file"
+ end
+ end
+ fs.pop_dir()
+ return unpack_dir
+end
+
+
+
+
+
+
+
+
+function fetch.load_local_rockspec(rel_filename, quick)
+ local abs_filename = fs.absolute_name(rel_filename)
+
+ local basename = dir.base_name(abs_filename)
+ if basename ~= "rockspec" then
+ if not basename:match("(.*)%-[^-]*%-[0-9]*") then
+ return nil, "Expected filename in format 'name-version-revision.rockspec'."
+ end
+ end
+
+ local tbl, err = persist.load_into_table(abs_filename)
+ if not tbl and type(err) == "string" then
+ return nil, "Could not load rockspec file " .. abs_filename .. " (" .. err .. ")"
+ end
+
+ local rockspec, errrock = rockspecs.from_persisted_table(abs_filename, tbl, err, quick)
+ if not rockspec then
+ return nil, abs_filename .. ": " .. errrock
+ end
+
+ local name_version = rockspec.package:lower() .. "-" .. rockspec.version
+ if basename ~= "rockspec" and basename ~= name_version .. ".rockspec" then
+ return nil, "Inconsistency between rockspec filename (" .. basename .. ") and its contents (" .. name_version .. ".rockspec)."
+ end
+
+ return rockspec
+end
+
+
+
+
+
+
+
+
+
+
+
+function fetch.load_rockspec(url, location, verify)
+
+ local name
+ local basename = dir.base_name(url)
+ if basename == "rockspec" then
+ name = "rockspec"
+ else
+ name = basename:match("(.*)%.rockspec")
+ if not name then
+ return nil, "Filename '" .. url .. "' does not look like a rockspec."
+ end
+ end
+
+ local tmpname = "luarocks-rockspec-" .. name
+ local filename, err, errcode, ok
+ if location then
+ ok, err = fs.change_dir(location)
+ if not ok then return nil, err end
+ filename, err = fetch.fetch_url(url)
+ fs.pop_dir()
+ else
+ filename, err, errcode = fetch.fetch_url_at_temp_dir(url, tmpname, nil, true)
+ end
+ if not filename then
+ return nil, err, errcode
+ end
+
+ if verify then
+ local _, errfetch = fetch_and_verify_signature_for(url, filename, tmpname)
+ if err then
+ return nil, errfetch
+ end
+ end
+
+ return fetch.load_local_rockspec(filename)
+end
+
+
+
+
+
+
+
+
+
+
+function fetch.get_sources(rockspec, extract, dest_dir)
+
+ local url = rockspec.source.url
+ local name = rockspec.name .. "-" .. rockspec.version
+ local filename = rockspec.source.file
+ local source_file, store_dir
+ local ok, err, errcode
+ if dest_dir then
+ ok, err = fs.change_dir(dest_dir)
+ if not ok then return nil, err, "dest_dir" end
+ source_file, err, errcode = fetch.fetch_url(url, filename)
+ fs.pop_dir()
+ store_dir = dest_dir
+ else
+ source_file, store_dir, errcode = fetch.fetch_url_at_temp_dir(url, "luarocks-source-" .. name, filename)
+ end
+ if not source_file then
+ return nil, err or store_dir, errcode
+ end
+ if rockspec.source.md5 then
+ if not fs.check_md5(source_file, rockspec.source.md5) then
+ return nil, "MD5 check for " .. filename .. " has failed.", "md5"
+ end
+ end
+ if extract then
+ ok, err = fs.change_dir(store_dir)
+ if not ok then return nil, err end
+ ok, err = fs.unpack_archive(rockspec.source.file)
+ if not ok then return nil, err end
+ ok, err = fetch.find_rockspec_source_dir(rockspec, ".")
+ if not ok then return nil, err end
+ fs.pop_dir()
+ end
+ return source_file, store_dir
+end
+
+function fetch.find_rockspec_source_dir(rockspec, store_dir)
+ local ok, err = fs.change_dir(store_dir)
+ if not ok then return nil, err end
+
+ local file_count, dir_count = 0, 0
+ local found_dir
+
+ if rockspec.source.dir and fs.exists(rockspec.source.dir) then
+ ok, err = true, nil
+ elseif rockspec.source.file and rockspec.source.dir then
+ ok, err = nil, "Directory " .. rockspec.source.dir .. " not found inside archive " .. rockspec.source.file
+ elseif not rockspec.source.dir_set then
+
+ local name = dir.base_name(rockspec.source.file or rockspec.source.url or "")
+
+ if name:match("%.lua$") or name:match("%.c$") then
+ if fs.is_file(name) then
+ rockspec.source.dir = "."
+ ok, err = true, nil
+ end
+ end
+
+ if not rockspec.source.dir then
+ for file in fs.dir() do
+ file_count = file_count + 1
+ if fs.is_dir(file) then
+ dir_count = dir_count + 1
+ found_dir = file
+ end
+ end
+
+ if dir_count == 1 then
+ rockspec.source.dir = found_dir
+ ok, err = true, nil
+ else
+ ok, err = nil, "Could not determine source directory from rock contents (" .. tostring(file_count) .. " file(s), " .. tostring(dir_count) .. " dir(s))"
+ end
+ end
+ else
+ ok, err = nil, "Could not determine source directory, please set source.dir in rockspec."
+ end
+
+ fs.pop_dir()
+
+ assert(rockspec.source.dir or not ok)
+ return ok, err
+end
+
+
+
+
+
+
+
+
+
+
+function fetch.fetch_sources(rockspec, extract, dest_dir)
+
+
+
+ if rockspec.source.url:match("^git://github%.com/") or
+ rockspec.source.url:match("^git://www%.github%.com/") then
+ rockspec.source.url = rockspec.source.url:gsub("^git://", "git+https://")
+ rockspec.source.protocol = "git+https"
+ end
+
+ local protocol = rockspec.source.protocol
+ local ok, err, proto
+
+ if dir.is_basic_protocol(protocol) then
+ proto = fetch
+ else
+ ok, proto = pcall(require, "luarocks.fetch." .. protocol:gsub("[+-]", "_"))
+ if not ok then
+ return nil, "Unknown protocol " .. protocol
+ end
+ end
+
+ if cfg.only_sources_from and
+ rockspec.source.pathname and
+ #rockspec.source.pathname > 0 then
+ if #cfg.only_sources_from == 0 then
+ return nil, "Can't download " .. rockspec.source.url .. " -- download from remote servers disabled"
+ elseif rockspec.source.pathname:find(cfg.only_sources_from, 1, true) ~= 1 then
+ return nil, "Can't download " .. rockspec.source.url .. " -- only downloading from " .. cfg.only_sources_from
+ end
+ end
+
+ local source_file, store_dir = proto.get_sources(rockspec, extract, dest_dir)
+ if not source_file then return nil, store_dir end
+
+ ok, err = fetch.find_rockspec_source_dir(rockspec, store_dir)
+ if not ok then return nil, err, "source.dir", source_file, store_dir end
+
+ return source_file, store_dir
+end
+
+return fetch
diff --git a/src/luarocks/fetch/cvs.lua b/src/luarocks/fetch/cvs.lua
new file mode 100644
index 00000000..6aabfb31
--- /dev/null
+++ b/src/luarocks/fetch/cvs.lua
@@ -0,0 +1,53 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local table = _tl_compat and _tl_compat.table or table; local _tl_table_unpack = unpack or table.unpack
+
+local cvs = {}
+
+
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+local util = require("luarocks.util")
+
+
+
+
+
+
+
+
+
+
+function cvs.get_sources(rockspec, extract, dest_dir)
+ local cvs_cmd = rockspec.variables.CVS
+ local ok, err_msg = fs.is_tool_available(cvs_cmd, "CVS")
+ if not ok then
+ return nil, err_msg
+ end
+
+ local name_version = rockspec.name .. "-" .. rockspec.version
+ local module = rockspec.source.module or dir.base_name(rockspec.source.url)
+ local command = { cvs_cmd, "-d" .. rockspec.source.pathname, "export", module }
+ if rockspec.source.tag then
+ table.insert(command, 4, "-r")
+ table.insert(command, 5, rockspec.source.tag)
+ end
+ local store_dir
+ if not dest_dir then
+ store_dir = fs.make_temp_dir(name_version)
+ if not store_dir then
+ return nil, "Failed creating temporary directory."
+ end
+ util.schedule_function(fs.delete, store_dir)
+ else
+ store_dir = dest_dir
+ end
+ local okchange, err = fs.change_dir(store_dir)
+ if not okchange then return nil, err end
+ if not fs.execute(_tl_table_unpack(command)) then
+ return nil, "Failed fetching files from CVS."
+ end
+ fs.pop_dir()
+ return module, store_dir
+end
+
+
+return cvs
diff --git a/src/luarocks/fetch/git.lua b/src/luarocks/fetch/git.lua
new file mode 100644
index 00000000..b32dfb1b
--- /dev/null
+++ b/src/luarocks/fetch/git.lua
@@ -0,0 +1,166 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table; local _tl_table_unpack = unpack or table.unpack
+
+local git = {}
+
+
+
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+local vers = require("luarocks.core.vers")
+local util = require("luarocks.util")
+
+
+
+
+local cached_git_version
+
+
+
+
+local function git_version(git_cmd)
+ if not cached_git_version then
+ local version_line = io.popen(fs.Q(git_cmd) .. ' --version'):read()
+ local version_string = version_line:match('%d-%.%d+%.?%d*')
+ cached_git_version = vers.parse_version(version_string)
+ end
+
+ return cached_git_version
+end
+
+
+
+
+
+local function git_is_at_least(git_cmd, version)
+ return git_version(git_cmd) >= vers.parse_version(version)
+end
+
+
+
+
+
+
+
+local function git_can_clone_by_tag(git_cmd)
+ return git_is_at_least(git_cmd, "1.7.10")
+end
+
+
+
+
+
+local function git_supports_shallow_submodules(git_cmd)
+ return git_is_at_least(git_cmd, "1.8.4")
+end
+
+
+
+
+local function git_supports_shallow_recommendations(git_cmd)
+ return git_is_at_least(git_cmd, "2.10.0")
+end
+
+local function git_identifier(git_cmd, ver)
+ if not (ver:match("^dev%-%d+$") or ver:match("^scm%-%d+$")) then
+ return nil
+ end
+ local pd = io.popen(fs.command_at(fs.current_dir(), fs.Q(git_cmd) .. " log --pretty=format:%ai_%h -n 1"))
+ if not pd then
+ return nil
+ end
+ local date_hash = pd:read("*l")
+ pd:close()
+ if not date_hash then
+ return nil
+ end
+ local date, time, _tz, hash = date_hash:match("([^%s]+) ([^%s]+) ([^%s]+)_([^%s]+)")
+ date = date:gsub("%-", "")
+ time = time:gsub(":", "")
+ return date .. "." .. time .. "." .. hash
+end
+
+
+
+
+
+
+
+
+function git.get_sources(rockspec, extract, dest_dir, depth)
+
+ local git_cmd = rockspec.variables.GIT
+ local name_version = rockspec.name .. "-" .. rockspec.version
+ local module = dir.base_name(rockspec.source.url)
+
+ module = module:gsub("%.git$", "")
+
+ local ok_available, err_msg = fs.is_tool_available(git_cmd, "Git")
+ if not ok_available then
+ return nil, err_msg
+ end
+
+ local store_dir
+ if not dest_dir then
+ store_dir = fs.make_temp_dir(name_version)
+ if not store_dir then
+ return nil, "Failed creating temporary directory."
+ end
+ util.schedule_function(fs.delete, store_dir)
+ else
+ store_dir = dest_dir
+ end
+ store_dir = fs.absolute_name(store_dir)
+ local ok, err = fs.change_dir(store_dir)
+ if not ok then return nil, err end
+
+ local command = { fs.Q(git_cmd), "clone", depth or "--depth=1", rockspec.source.url, module }
+ local tag_or_branch = rockspec.source.tag or rockspec.source.branch
+
+
+ if tag_or_branch == "master" then tag_or_branch = nil end
+ if tag_or_branch then
+ if git_can_clone_by_tag(git_cmd) then
+
+
+ table.insert(command, 3, "--branch=" .. tag_or_branch)
+ end
+ end
+ if not fs.execute(_tl_table_unpack(command)) then
+ return nil, "Failed cloning git repository."
+ end
+ ok, err = fs.change_dir(module)
+ if not ok then return nil, err end
+ if tag_or_branch and not git_can_clone_by_tag() then
+ if not fs.execute(fs.Q(git_cmd), "checkout", tag_or_branch) then
+ return nil, 'Failed to check out the "' .. tag_or_branch .. '" tag or branch.'
+ end
+ end
+
+
+ if rockspec:format_is_at_least("3.0") then
+ command = { fs.Q(git_cmd), "submodule", "update", "--init", "--recursive" }
+
+ if git_supports_shallow_recommendations(git_cmd) then
+ table.insert(command, 5, "--recommend-shallow")
+ elseif git_supports_shallow_submodules(git_cmd) then
+
+ table.insert(command, 5, "--depth=1")
+ end
+
+ if not fs.execute(_tl_table_unpack(command)) then
+ return nil, 'Failed to fetch submodules.'
+ end
+ end
+
+ if not rockspec.source.tag then
+ rockspec.source.identifier = git_identifier(git_cmd, rockspec.version)
+ end
+
+ fs.delete(dir.path(store_dir, module, ".git"))
+ fs.delete(dir.path(store_dir, module, ".gitignore"))
+ fs.pop_dir()
+ fs.pop_dir()
+ return module, store_dir
+end
+
+return git
diff --git a/src/luarocks/fetch/git_file.lua b/src/luarocks/fetch/git_file.lua
new file mode 100644
index 00000000..60d06323
--- /dev/null
+++ b/src/luarocks/fetch/git_file.lua
@@ -0,0 +1,22 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local string = _tl_compat and _tl_compat.string or string
+
+local git_file = {}
+
+
+local git = require("luarocks.fetch.git")
+
+
+
+
+
+
+
+
+
+
+function git_file.get_sources(rockspec, extract, dest_dir)
+ rockspec.source.url = rockspec.source.url:gsub("^git.file://", "")
+ return git.get_sources(rockspec, extract, dest_dir)
+end
+
+return git_file
diff --git a/src/luarocks/fetch/git_http.lua b/src/luarocks/fetch/git_http.lua
new file mode 100644
index 00000000..9275209c
--- /dev/null
+++ b/src/luarocks/fetch/git_http.lua
@@ -0,0 +1,29 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local string = _tl_compat and _tl_compat.string or string
+
+
+
+
+
+
+
+
+local git_http = {}
+
+
+local git = require("luarocks.fetch.git")
+
+
+
+
+
+
+
+
+
+
+function git_http.get_sources(rockspec, extract, dest_dir)
+ rockspec.source.url = rockspec.source.url:gsub("^git.", "")
+ return git.get_sources(rockspec, extract, dest_dir, "--")
+end
+
+return git_http
diff --git a/src/luarocks/fetch/git_https.lua b/src/luarocks/fetch/git_https.lua
new file mode 100644
index 00000000..463f2f08
--- /dev/null
+++ b/src/luarocks/fetch/git_https.lua
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+return require("luarocks.fetch.git_http")
diff --git a/src/luarocks/fetch/git_ssh.lua b/src/luarocks/fetch/git_ssh.lua
new file mode 100644
index 00000000..8a95a722
--- /dev/null
+++ b/src/luarocks/fetch/git_ssh.lua
@@ -0,0 +1,35 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local string = _tl_compat and _tl_compat.string or string
+
+
+
+
+
+
+
+
+local git_ssh = {}
+
+
+local git = require("luarocks.fetch.git")
+
+
+
+
+
+
+
+
+
+
+function git_ssh.get_sources(rockspec, extract, dest_dir)
+ rockspec.source.url = rockspec.source.url:gsub("^git.", "")
+
+
+ if rockspec.source.url:match("^ssh://[^/]+:[^%d]") then
+ rockspec.source.url = rockspec.source.url:gsub("^ssh://", "")
+ end
+
+ return git.get_sources(rockspec, extract, dest_dir, "--")
+end
+
+return git_ssh
diff --git a/src/luarocks/fetch/hg.lua b/src/luarocks/fetch/hg.lua
new file mode 100644
index 00000000..5265920f
--- /dev/null
+++ b/src/luarocks/fetch/hg.lua
@@ -0,0 +1,64 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table; local _tl_table_unpack = unpack or table.unpack
+
+local hg = {}
+
+
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+local util = require("luarocks.util")
+
+
+
+
+
+
+
+
+
+
+function hg.get_sources(rockspec, extract, dest_dir)
+
+ local hg_cmd = rockspec.variables.HG
+ local ok_available, err_msg = fs.is_tool_available(hg_cmd, "Mercurial")
+ if not ok_available then
+ return nil, err_msg
+ end
+
+ local name_version = rockspec.name .. "-" .. rockspec.version
+
+ local url = rockspec.source.url:gsub("^hg://", "")
+
+ local module = dir.base_name(url)
+
+ local command = { hg_cmd, "clone", url, module }
+ local tag_or_branch = rockspec.source.tag or rockspec.source.branch
+ if tag_or_branch then
+ command = { hg_cmd, "clone", "--rev", tag_or_branch, url, module }
+ end
+ local store_dir
+ if not dest_dir then
+ store_dir = fs.make_temp_dir(name_version)
+ if not store_dir then
+ return nil, "Failed creating temporary directory."
+ end
+ util.schedule_function(fs.delete, store_dir)
+ else
+ store_dir = dest_dir
+ end
+ local ok, err = fs.change_dir(store_dir)
+ if not ok then return nil, err end
+ if not fs.execute(_tl_table_unpack(command)) then
+ return nil, "Failed cloning hg repository."
+ end
+ ok, err = fs.change_dir(module)
+ if not ok then return nil, err end
+
+ fs.delete(dir.path(store_dir, module, ".hg"))
+ fs.delete(dir.path(store_dir, module, ".hgignore"))
+ fs.pop_dir()
+ fs.pop_dir()
+ return module, store_dir
+end
+
+
+return hg
diff --git a/src/luarocks/fetch/hg_http.lua b/src/luarocks/fetch/hg_http.lua
new file mode 100644
index 00000000..9f56c02d
--- /dev/null
+++ b/src/luarocks/fetch/hg_http.lua
@@ -0,0 +1,27 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local string = _tl_compat and _tl_compat.string or string
+
+
+
+
+
+
+local hg_http = {}
+
+
+local hg = require("luarocks.fetch.hg")
+
+
+
+
+
+
+
+
+
+
+function hg_http.get_sources(rockspec, extract, dest_dir)
+ rockspec.source.url = rockspec.source.url:gsub("^hg.", "")
+ return hg.get_sources(rockspec, extract, dest_dir)
+end
+
+return hg_http
diff --git a/src/luarocks/fetch/hg_https.lua b/src/luarocks/fetch/hg_https.lua
new file mode 100644
index 00000000..1b6475ad
--- /dev/null
+++ b/src/luarocks/fetch/hg_https.lua
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+return require("luarocks.fetch.hg_http")
diff --git a/src/luarocks/fetch/hg_ssh.lua b/src/luarocks/fetch/hg_ssh.lua
new file mode 100644
index 00000000..1b6475ad
--- /dev/null
+++ b/src/luarocks/fetch/hg_ssh.lua
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+return require("luarocks.fetch.hg_http")
diff --git a/src/luarocks/fetch/sscm.lua b/src/luarocks/fetch/sscm.lua
new file mode 100644
index 00000000..f635c02f
--- /dev/null
+++ b/src/luarocks/fetch/sscm.lua
@@ -0,0 +1,45 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local string = _tl_compat and _tl_compat.string or string
+
+local sscm = {}
+
+
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+
+
+
+
+
+
+
+
+
+
+function sscm.get_sources(rockspec, extract, dest_dir)
+
+ local sscm_cmd = rockspec.variables.SSCM
+ local module = rockspec.source.module or dir.base_name(rockspec.source.url)
+ local branch, repository = string.match(rockspec.source.pathname, "^([^/]*)/(.*)")
+ if not branch or not repository then
+ return nil, "Error retrieving branch and repository from rockspec."
+ end
+
+ local working_dir
+ local tmp = io.popen(string.format(sscm_cmd .. [[ property "/" -d -b%s -p%s]], branch, repository))
+ for line in tmp:lines() do
+
+ working_dir = string.match(line, "Working directory:[%s]*(.*)%c$")
+ if working_dir then break end
+ end
+ tmp:close()
+ if not working_dir then
+ return nil, "Error retrieving working directory from SSCM."
+ end
+ if not fs.execute(sscm_cmd, "get", "*", "-e", "-r", "-b" .. branch, "-p" .. repository, "-tmodify", "-wreplace") then
+ return nil, "Failed fetching files from SSCM."
+ end
+
+ return module, working_dir
+end
+
+return sscm
diff --git a/src/luarocks/fetch/svn.lua b/src/luarocks/fetch/svn.lua
new file mode 100644
index 00000000..cd467cef
--- /dev/null
+++ b/src/luarocks/fetch/svn.lua
@@ -0,0 +1,63 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table; local _tl_table_unpack = unpack or table.unpack
+
+local svn = {}
+
+
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+local util = require("luarocks.util")
+
+
+
+
+
+
+
+
+
+
+function svn.get_sources(rockspec, extract, dest_dir)
+
+ local svn_cmd = rockspec.variables.SVN
+ local ok, err_msg = fs.is_tool_available(svn_cmd, "Subversion")
+ if not ok then
+ return nil, err_msg
+ end
+
+ local name_version = rockspec.name .. "-" .. rockspec.version
+ local module = rockspec.source.module or dir.base_name(rockspec.source.url)
+ local url = rockspec.source.url:gsub("^svn://", "")
+ local command = { svn_cmd, "checkout", url, module }
+ if rockspec.source.tag then
+ table.insert(command, 5, "-r")
+ table.insert(command, 6, rockspec.source.tag)
+ end
+ local store_dir
+ if not dest_dir then
+ store_dir = fs.make_temp_dir(name_version)
+ if not store_dir then
+ return nil, "Failed creating temporary directory."
+ end
+ util.schedule_function(fs.delete, store_dir)
+ else
+ store_dir = dest_dir
+ end
+ local okchange, err = fs.change_dir(store_dir)
+ if not okchange then return nil, err end
+ if not fs.execute(_tl_table_unpack(command)) then
+ return nil, "Failed fetching files from Subversion."
+ end
+ okchange, err = fs.change_dir(module)
+ if not okchange then return nil, err end
+ for _, d in ipairs(fs.find(".")) do
+ if dir.base_name(d) == ".svn" then
+ fs.delete(dir.path(store_dir, module, d))
+ end
+ end
+ fs.pop_dir()
+ fs.pop_dir()
+ return module, store_dir
+end
+
+
+return svn
diff --git a/src/luarocks/fun.lua b/src/luarocks/fun.lua
new file mode 100644
index 00000000..86030ccc
--- /dev/null
+++ b/src/luarocks/fun.lua
@@ -0,0 +1,138 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local math = _tl_compat and _tl_compat.math or math; local table = _tl_compat and _tl_compat.table or table; local _tl_table_unpack = unpack or table.unpack
+
+local fun = {}
+
+
+function fun.concat(xs, ys)
+ local rs = {}
+ local n = #xs
+ for i = 1, n do
+ rs[i] = xs[i]
+ end
+ for i = 1, #ys do
+ rs[i + n] = ys[i]
+ end
+
+ return rs
+end
+
+function fun.contains(xs, v)
+ for _, x in ipairs(xs) do
+ if v == x then
+ return true
+ end
+ end
+ return false
+end
+
+function fun.map(xs, f)
+ local rs = {}
+ for i = 1, #xs do
+ rs[i] = f(xs[i])
+ end
+ return rs
+end
+
+function fun.filter(xs, f)
+ local rs = {}
+ for i = 1, #xs do
+ local v = xs[i]
+ if f(v) then
+ rs[#rs + 1] = v
+ end
+ end
+ return rs
+end
+
+function fun.reverse_in(t)
+ for i = 1, math.floor(#t / 2) do
+ local m, n = i, #t - i + 1
+ local a, b = t[m], t[n]
+ t[m] = b
+ t[n] = a
+ end
+ return t
+end
+
+function fun.sort_in(t, f)
+ table.sort(t, f)
+ return t
+end
+
+function fun.flip(f)
+ return function(a, b)
+ return f(b, a)
+ end
+end
+
+function fun.find(xs, f)
+ if type(xs) == "table" then
+ for _, x in ipairs(xs) do
+ local r = f(x)
+ if r then
+ return r
+ end
+ end
+ else
+ for x in xs do
+ local r = f(x)
+ if r then
+ return r
+ end
+ end
+ end
+end
+
+
+function fun.partial(f, ...)
+ local n = select("#", ...)
+ if n == 1 then
+ local a = ...
+ return function(...)
+ return f(a, ...)
+ end
+ elseif n == 2 then
+ local a, b = ...
+ return function(...)
+ return f(a, b, ...)
+ end
+ else
+ local pargs = { n = n, ... }
+ return function(...)
+ local m = select("#", ...)
+ local fargs = { ... }
+ local args = {}
+ for i = 1, n do
+ args[i] = pargs[i]
+ end
+ for i = 1, m do
+ args[i + n] = fargs[i]
+ end
+ return f(_tl_table_unpack(args, 1, n + m))
+ end
+ end
+end
+
+function fun.memoize(fn)
+ local memory = setmetatable({}, { __mode = "k" })
+ local errors = setmetatable({}, { __mode = "k" })
+ local NIL = {}
+ return function(a)
+ if memory[a] then
+ if memory[a] == NIL then
+ return nil, errors[a]
+ end
+ return memory[a]
+ end
+ local ret1, ret2 = fn(a)
+ if ret1 then
+ memory[a] = ret1
+ else
+ memory[a] = NIL
+ errors[a] = ret2
+ end
+ return ret1, ret2
+ end
+end
+
+return fun
diff --git a/src/luarocks/loader.lua b/src/luarocks/loader.lua
new file mode 100644
index 00000000..ac7bb5fb
--- /dev/null
+++ b/src/luarocks/loader.lua
@@ -0,0 +1,333 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local debug = _tl_compat and _tl_compat.debug or debug; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local package = _tl_compat and _tl_compat.package or package; local pairs = _tl_compat and _tl_compat.pairs or pairs; local pcall = _tl_compat and _tl_compat.pcall or pcall; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+
+
+
+
+
+
+local loaders = package.loaders or package.searchers
+local require, ipairs, table, type, next, tostring, error =
+require, ipairs, table, type, next, tostring, error
+
+local loader = {}
+
+
+
+
+local is_clean = not package.loaded["luarocks.core.cfg"]
+
+
+local cfg = require("luarocks.core.cfg")
+local cfg_ok, _err = cfg.init()
+if cfg_ok then
+ cfg.init_package_paths()
+end
+
+local path = require("luarocks.core.path")
+local manif = require("luarocks.core.manif")
+local vers = require("luarocks.core.vers")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local temporary_global = false
+local status, luarocks_value = pcall(function()
+ return luarocks
+end)
+if status and luarocks_value then
+
+
+
+ luarocks.loader = loader
+else
+
+
+
+
+
+ local info = debug and debug.getinfo(2, "nS")
+ if info and info.what == "C" and not info.name then
+ luarocks = { loader = loader }
+ temporary_global = true
+
+
+ end
+end
+
+
+
+
+
+loader.context = {}
+
+
+
+
+
+
+function loader.add_context(name, version)
+ if temporary_global then
+
+
+ luarocks = nil
+ temporary_global = false
+ end
+
+ local tree_manifests = manif.load_rocks_tree_manifests()
+ if not tree_manifests then
+ return
+ end
+
+ manif.scan_dependencies(name, version, tree_manifests, loader.context)
+end
+
+
+
+
+
+
+
+local function sort_versions(a, b)
+ return a.version > b.version
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local function call_other_loaders(module, name, version, module_name)
+ for _, a_loader in ipairs(loaders) do
+ if a_loader ~= loader.luarocks_loader then
+ local results = { a_loader(module_name) }
+ local f = results[1]
+ if type(f) == "function" then
+ if #results == 2 then
+ return f, results[2]
+ else
+ return f
+ end
+ end
+ end
+ end
+ return "Failed loading module " .. module .. " in LuaRocks rock " .. name .. " " .. version
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local function add_providers(providers, entries, tree, module, filter_name)
+ for i, entry in ipairs(entries) do
+ local name, version = entry:match("^([^/]*)/(.*)$")
+
+ local file_name = tree.manifest.repository[name][version][1].modules[module]
+ if type(file_name) ~= "string" then
+ error("Invalid data in manifest file for module " .. tostring(module) .. " (invalid data for " .. tostring(name) .. " " .. tostring(version) .. ")")
+ end
+
+ file_name = filter_name(file_name, name, version, tree.tree, i)
+
+ if loader.context[name] == version then
+ return name, version, file_name
+ end
+
+ table.insert(providers, {
+ name = name,
+ version = vers.parse_version(version),
+ module_name = file_name,
+ tree = tree,
+ })
+ end
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+local function select_module(module, filter_name)
+
+ local tree_manifests = manif.load_rocks_tree_manifests()
+ if not tree_manifests then
+ return nil
+ end
+
+ local providers = {}
+ local initmodule
+ for _, tree in ipairs(tree_manifests) do
+ local entries = tree.manifest.modules[module]
+ if entries then
+ local n, v, f = add_providers(providers, entries, tree, module, filter_name)
+ if n then
+ return n, v, f
+ end
+ else
+ initmodule = initmodule or module .. ".init"
+ entries = tree.manifest.modules[initmodule]
+ if entries then
+ local n, v, f = add_providers(providers, entries, tree, initmodule, filter_name)
+ if n then
+ return n, v, f
+ end
+ end
+ end
+ end
+
+ if next(providers) then
+ table.sort(providers, sort_versions)
+ local first = providers[1]
+ return first.name, first.version.string, first.module_name
+ end
+end
+
+
+
+
+
+
+
+
+
+
+
+
+local function filter_module_name(file_name, name, version, _tree, i)
+ if i > 1 then
+ file_name = path.versioned_name(file_name, "", name, version)
+ end
+ return path.path_to_module(file_name)
+end
+
+
+
+
+
+
+
+
+
+local function pick_module(module)
+ return select_module(module, filter_module_name)
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+function loader.which(module, where)
+ where = where or "l"
+ if where:match("l") then
+ local rock_name, rock_version, file_name = select_module(module, path.which_i)
+ if rock_name then
+ local fd = io.open(file_name)
+ if fd then
+ fd:close()
+ return file_name, rock_name, rock_version, "l"
+ end
+ end
+ end
+ if where:match("p") then
+ local modpath = module:gsub("%.", "/")
+ for _, v in ipairs({ package.path, package.cpath }) do
+ for p in v:gmatch("([^;]+)") do
+ local file_name = p:gsub("%?", modpath)
+ local fd = io.open(file_name)
+ if fd then
+ fd:close()
+ return file_name, v, nil, "p"
+ end
+ end
+ end
+ end
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+function loader.luarocks_loader(module)
+ local name, version, module_name = pick_module(module)
+ if not name then
+ return "No LuaRocks module found for " .. module
+ else
+ loader.add_context(name, version)
+ return call_other_loaders(module, name, version, module_name)
+ end
+end
+
+table.insert(loaders, 1, loader.luarocks_loader)
+
+if is_clean then
+ for modname, _ in pairs(package.loaded) do
+ if modname:match("^luarocks%.") then
+ package.loaded[modname] = nil
+ end
+ end
+end
+
+return loader
diff --git a/src/luarocks/manif.lua b/src/luarocks/manif.lua
new file mode 100644
index 00000000..27144894
--- /dev/null
+++ b/src/luarocks/manif.lua
@@ -0,0 +1,229 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string
+
+
+
+local manif = {}
+
+
+
+
+
+
+
+
+
+local core = require("luarocks.core.manif")
+local persist = require("luarocks.persist")
+local fetch = require("luarocks.fetch")
+local dir = require("luarocks.dir")
+local fs = require("luarocks.fs")
+local cfg = require("luarocks.core.cfg")
+local path = require("luarocks.path")
+local util = require("luarocks.util")
+local queries = require("luarocks.queries")
+local type_manifest = require("luarocks.type.manifest")
+
+
+
+
+
+
+manif.cache_manifest = core.cache_manifest
+manif.load_rocks_tree_manifests = core.load_rocks_tree_manifests
+manif.scan_dependencies = core.scan_dependencies
+
+manif.rock_manifest_cache = {}
+
+local function check_manifest(repo_url, manifest, globals)
+ local ok, err = type_manifest.check(manifest, globals)
+ if not ok then
+ core.cache_manifest(repo_url, cfg.lua_version, nil)
+ return nil, "Error checking manifest: " .. err, "type"
+ end
+ return manifest
+end
+
+local postprocess_dependencies
+do
+ local postprocess_check = setmetatable({}, { __mode = "k" })
+ postprocess_dependencies = function(manifest)
+ if postprocess_check[manifest] then
+ return
+ end
+ if manifest.dependencies then
+ for _, versions in pairs(manifest.dependencies) do
+ for _, entries in pairs(versions) do
+ for k, v in ipairs(entries) do
+ entries[k] = queries.from_persisted_table(v)
+ end
+ end
+ end
+ end
+ postprocess_check[manifest] = true
+ end
+end
+
+function manif.load_rock_manifest(name, version, root)
+ assert(not name:match("/"))
+
+ local name_version = name .. "/" .. version
+ if manif.rock_manifest_cache[name_version] then
+ return manif.rock_manifest_cache[name_version].rock_manifest
+ end
+ local pathname = path.rock_manifest_file(name, version, root)
+ local rock_manifest = persist.load_into_table(pathname)
+ if not rock_manifest then
+ return nil, "rock_manifest file not found for " .. name .. " " .. version .. " - not a LuaRocks tree?"
+ end
+ manif.rock_manifest_cache[name_version] = rock_manifest
+ return rock_manifest.rock_manifest
+end
+
+
+
+
+
+
+
+
+
+
+function manif.load_manifest(repo_url, lua_version, versioned_only)
+ lua_version = lua_version or cfg.lua_version
+
+ local cached_manifest = core.get_cached_manifest(repo_url, lua_version)
+ if cached_manifest then
+ postprocess_dependencies(cached_manifest)
+ return cached_manifest
+ end
+
+ local filenames = {
+ "manifest-" .. lua_version .. ".zip",
+ "manifest-" .. lua_version,
+ not versioned_only and "manifest" or nil,
+ }
+
+ local protocol, repodir = dir.split_url(repo_url)
+ local pathname, from_cache
+ if protocol == "file" then
+ for _, filename in ipairs(filenames) do
+ pathname = dir.path(repodir, filename)
+ if fs.exists(pathname) then
+ break
+ end
+ end
+ else
+ local err, errcode
+ for _, filename in ipairs(filenames) do
+ pathname, err, errcode, from_cache = fetch.fetch_caching(dir.path(repo_url, filename), "no_mirror")
+ if pathname then
+ break
+ end
+ end
+ if not pathname then
+ return nil, err, errcode
+ end
+ end
+ if pathname:match(".*%.zip$") then
+ pathname = fs.absolute_name(pathname)
+ local nozip = pathname:match("(.*)%.zip$")
+ if not from_cache then
+ local dirname = dir.dir_name(pathname)
+ fs.change_dir(dirname)
+ fs.delete(nozip)
+ local ok, err = fs.unzip(pathname)
+ fs.pop_dir()
+ if not ok then
+ fs.delete(pathname)
+ fs.delete(pathname .. ".timestamp")
+ return nil, "Failed extracting manifest file: " .. err
+ end
+ end
+ pathname = nozip
+ end
+ local manifest, err, errcode = core.manifest_loader(pathname, repo_url, lua_version)
+ if not manifest and type(err) == "string" then
+ return nil, err, errcode
+ end
+
+ postprocess_dependencies(manifest)
+ return check_manifest(repo_url, manifest, err)
+end
+
+
+
+
+
+function manif.get_provided_item(deploy_type, file_path)
+ local item_type = deploy_type == "bin" and "command" or "module"
+ local item_name = item_type == "command" and file_path or path.path_to_module(file_path)
+ return item_type, item_name
+end
+
+local function get_providers(item_type, item_name, repo)
+ local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
+ local manifest = manif.load_manifest(rocks_dir)
+ return manifest and (manifest)[item_type .. "s"][item_name]
+end
+
+
+
+
+
+
+
+
+function manif.get_current_provider(item_type, item_name, repo)
+ local providers = get_providers(item_type, item_name, repo)
+ if providers then
+ return providers[1]:match("([^/]*)/([^/]*)")
+ end
+end
+
+function manif.get_next_provider(item_type, item_name, repo)
+ local providers = get_providers(item_type, item_name, repo)
+ if providers and providers[2] then
+ return providers[2]:match("([^/]*)/([^/]*)")
+ end
+end
+
+
+
+
+
+
+
+
+
+function manif.get_versions(dep, deps_mode)
+
+ local name = dep.name
+ local namespace = dep.namespace
+
+ local version_set = {}
+ path.map_trees(deps_mode, function(tree)
+ local manifest = manif.load_manifest(path.rocks_dir(tree))
+
+ if manifest and manifest.repository[name] then
+ for version in pairs(manifest.repository[name]) do
+ if dep.namespace then
+ local ns_file = path.rock_namespace_file(name, version, tree)
+ local fd = io.open(ns_file, "r")
+ if fd then
+ local ns = fd:read("*a")
+ fd:close()
+ if ns == namespace then
+ version_set[version] = tree
+ end
+ end
+ else
+ version_set[version] = tree
+ end
+ end
+ end
+ end)
+
+ return util.keys(version_set), version_set
+end
+
+return manif
diff --git a/src/luarocks/manif/writer.lua b/src/luarocks/manif/writer.lua
new file mode 100644
index 00000000..04becbac
--- /dev/null
+++ b/src/luarocks/manif/writer.lua
@@ -0,0 +1,459 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+local writer = {}
+
+
+local cfg = require("luarocks.core.cfg")
+local search = require("luarocks.search")
+local repos = require("luarocks.repos")
+local deps = require("luarocks.deps")
+local vers = require("luarocks.core.vers")
+local fs = require("luarocks.fs")
+local util = require("luarocks.util")
+local dir = require("luarocks.dir")
+local fetch = require("luarocks.fetch")
+local path = require("luarocks.path")
+local persist = require("luarocks.persist")
+local manif = require("luarocks.manif")
+local queries = require("luarocks.queries")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local function store_package_items(storage, name, version, items)
+ assert(not name:match("/"))
+
+ local package_identifier = name .. "/" .. version
+
+ for item_name, _ in pairs(items) do
+ if not storage[item_name] then
+ storage[item_name] = {}
+ end
+
+ table.insert(storage[item_name], package_identifier)
+ end
+end
+
+
+
+
+
+
+
+
+local function remove_package_items(storage, name, version, items)
+ assert(not name:match("/"))
+
+ local package_identifier = name .. "/" .. version
+
+ for item_name, _path in pairs(items) do
+ local key = item_name
+ local all_identifiers = storage[key]
+ if not all_identifiers then
+ key = key .. ".init"
+ all_identifiers = storage[key]
+ end
+
+ if all_identifiers then
+ for i, identifier in ipairs(all_identifiers) do
+ if identifier == package_identifier then
+ table.remove(all_identifiers, i)
+ break
+ end
+ end
+
+ if #all_identifiers == 0 then
+ storage[key] = nil
+ end
+ else
+ util.warning("Cannot find entry for " .. item_name .. " in manifest -- corrupted manifest?")
+ end
+ end
+end
+
+
+
+
+
+
+
+
+
+local function update_dependencies(manifest, deps_mode)
+
+ if not manifest.dependencies then manifest.dependencies = {} end
+ local mdeps = manifest.dependencies
+
+ for pkg, versions in pairs(manifest.repository) do
+ for version, repositories in pairs(versions) do
+ for _, repo in ipairs(repositories) do
+ if repo.arch == "installed" then
+ local rd = {}
+ repo.dependencies = rd
+ deps.scan_deps(rd, mdeps, pkg, version, deps_mode)
+ rd[pkg] = nil
+ end
+ end
+ end
+ end
+end
+
+
+
+
+
+
+
+
+
+
+local function sort_pkgs(a, b)
+ local na, va = a:match("(.*)/(.*)$")
+ local nb, vb = b:match("(.*)/(.*)$")
+
+ return (na == nb) and vers.compare_versions(va, vb) or na < nb
+end
+
+
+
+
+local function sort_package_matching_table(tbl)
+
+ if next(tbl) then
+ for item, pkgs in pairs(tbl) do
+ if #pkgs > 1 then
+ table.sort(pkgs, sort_pkgs)
+
+ local prev = nil
+ local i = 1
+ while pkgs[i] do
+ local curr = pkgs[i]
+ if curr == prev then
+ table.remove(pkgs, i)
+ else
+ prev = curr
+ i = i + 1
+ end
+ end
+ end
+ end
+ end
+end
+
+
+
+
+
+
+
+local function filter_by_lua_version(manifest, lua_version_str, repodir, cache)
+
+ cache = cache or {}
+ local lua_version = vers.parse_version(lua_version_str)
+ for pkg, versions in pairs(manifest.repository) do
+ local to_remove = {}
+ for version, repositories in pairs(versions) do
+ for _, repo in ipairs(repositories) do
+ if repo.arch == "rockspec" then
+ local pathname = dir.path(repodir, pkg .. "-" .. version .. ".rockspec")
+ local rockspec = cache[pathname]
+ local err
+ if not rockspec then
+ rockspec, err = fetch.load_local_rockspec(pathname, true)
+ end
+ if rockspec then
+ cache[pathname] = rockspec
+ for _, dep in ipairs(rockspec.dependencies.queries) do
+ if dep.name == "lua" then
+ if not vers.match_constraints(lua_version, dep.constraints) then
+ table.insert(to_remove, version)
+ end
+ break
+ end
+ end
+ else
+ util.printerr("Error loading rockspec for " .. pkg .. " " .. version .. ": " .. err)
+ end
+ end
+ end
+ end
+ if next(to_remove) then
+ for _, incompat in ipairs(to_remove) do
+ versions[incompat] = nil
+ end
+ if not next(versions) then
+ manifest.repository[pkg] = nil
+ end
+ end
+ end
+end
+
+
+
+
+
+
+local function store_results(results, manifest)
+
+ for name, versions in pairs(results) do
+ local pkgtable = manifest.repository[name] or {}
+ for version, entries in pairs(versions) do
+ local versiontable = {}
+ for _, entry in ipairs(entries) do
+ local entrytable = {}
+ entrytable.arch = entry.arch
+ if entry.arch == "installed" then
+ local rock_manifest, err = manif.load_rock_manifest(name, version)
+ if not rock_manifest then return nil, err end
+
+ entrytable.modules = repos.package_modules(name, version)
+ store_package_items(manifest.modules, name, version, entrytable.modules)
+ entrytable.commands = repos.package_commands(name, version)
+ store_package_items(manifest.commands, name, version, entrytable.commands)
+ end
+ table.insert(versiontable, entrytable)
+ end
+ pkgtable[version] = versiontable
+ end
+ manifest.repository[name] = pkgtable
+ end
+ sort_package_matching_table(manifest.modules)
+ sort_package_matching_table(manifest.commands)
+ return true
+end
+
+
+
+
+
+
+
+local function save_table(where, name, tbl)
+ assert(not name:match("/"))
+
+ local filename = dir.path(where, name)
+ local ok, err = persist.save_from_table(filename .. ".tmp", tbl)
+ if ok then
+ ok, err = fs.replace_file(filename, filename .. ".tmp")
+ end
+ return ok, err
+end
+
+function writer.make_rock_manifest(name, version)
+ local install_dir = path.install_dir(name, version)
+ local tree = {}
+ for _, file in ipairs(fs.find(install_dir)) do
+ local full_path = dir.path(install_dir, file)
+ local walk = tree
+ local last
+ local last_name
+ local next
+ for filename in file:gmatch("[^\\/]+") do
+ next = walk[filename]
+ if not next then
+ next = {}
+ walk[filename] = next
+ end
+ last = walk
+ last_name = filename
+ assert(type(next) == "table")
+ walk = next
+ end
+ if fs.is_file(full_path) then
+
+ local sum, err = fs.get_md5(full_path)
+ if not sum then
+ return nil, "Failed producing checksum: " .. tostring(err)
+ end
+ last[last_name] = sum
+ end
+ end
+ local rock_manifest = { rock_manifest = tree }
+ manif.rock_manifest_cache[name .. "/" .. version] = rock_manifest
+ save_table(install_dir, "rock_manifest", rock_manifest)
+ return true
+end
+
+
+
+
+
+
+
+function writer.make_namespace_file(name, version, namespace)
+ assert(not name:match("/"))
+ if not namespace then
+ return true
+ end
+ local fd, err = io.open(path.rock_namespace_file(name, version), "w")
+ if not fd then
+ return nil, err
+ end
+ local ok, err = fd:write(namespace)
+ if not ok then
+ return nil, err
+ end
+ fd:close()
+ return true
+end
+
+
+
+
+
+
+
+
+
+
+
+function writer.make_manifest(repo, deps_mode, remote)
+
+ if deps_mode == "none" then deps_mode = cfg.deps_mode end
+
+ if not fs.is_dir(repo) then
+ return nil, "Cannot access repository at " .. repo
+ end
+
+ local query = queries.all("any")
+ local results = search.disk_search(repo, query)
+ local manifest = { repository = {}, modules = {}, commands = {} }
+
+ manif.cache_manifest(repo, nil, manifest)
+
+ local ok, err = store_results(results, manifest)
+ if not ok then return nil, err end
+
+ if remote then
+ local cache = {}
+ for luaver in util.lua_versions() do
+ local vmanifest = { repository = {}, modules = {}, commands = {} }
+ ok, err = store_results(results, vmanifest)
+ filter_by_lua_version(vmanifest, luaver, repo, cache)
+ if not cfg.no_manifest then
+ save_table(repo, "manifest-" .. luaver, vmanifest)
+ end
+ end
+ else
+ update_dependencies(manifest, deps_mode)
+ end
+
+ if cfg.no_manifest then
+
+ return true
+ end
+ return save_table(repo, "manifest", manifest)
+end
+
+
+
+
+
+
+
+
+
+
+
+
+function writer.add_to_manifest(name, version, repo, deps_mode)
+ assert(not name:match("/"))
+ local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
+
+ if deps_mode == "none" then deps_mode = cfg.deps_mode end
+
+ local manifest, err = manif.load_manifest(rocks_dir)
+ if not manifest then
+ util.printerr("No existing manifest. Attempting to rebuild...")
+
+
+
+ return writer.make_manifest(rocks_dir, deps_mode)
+ end
+
+ local results = { [name] = { [version] = { { arch = "installed", repo = rocks_dir } } } }
+
+ local ok
+ ok, err = store_results(results, manifest)
+ if not ok then return nil, err end
+
+ update_dependencies(manifest, deps_mode)
+
+ if cfg.no_manifest then
+ return true
+ end
+ return save_table(rocks_dir, "manifest", manifest)
+end
+
+
+
+
+
+
+
+
+
+
+
+
+function writer.remove_from_manifest(name, version, repo, deps_mode)
+ assert(not name:match("/"))
+ local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
+
+ if deps_mode == "none" then deps_mode = cfg.deps_mode end
+
+ local manifest, err = manif.load_manifest(rocks_dir)
+ if not manifest then
+ util.printerr("No existing manifest. Attempting to rebuild...")
+
+
+ return writer.make_manifest(rocks_dir, deps_mode)
+ end
+
+ local package_entry = manifest.repository[name]
+ if package_entry == nil or package_entry[version] == nil then
+
+ return true
+ end
+
+ local version_entry = package_entry[version][1]
+ if not version_entry then
+
+ return writer.make_manifest(rocks_dir, deps_mode)
+ end
+
+ remove_package_items(manifest.modules, name, version, version_entry.modules)
+ remove_package_items(manifest.commands, name, version, version_entry.commands)
+
+ package_entry[version] = nil
+ manifest.dependencies[name][version] = nil
+
+ if not next(package_entry) then
+
+ manifest.repository[name] = nil
+ manifest.dependencies[name] = nil
+ end
+
+ update_dependencies(manifest, deps_mode)
+
+ if cfg.no_manifest then
+ return true
+ end
+ return save_table(rocks_dir, "manifest", manifest)
+end
+
+return writer
diff --git a/src/luarocks/pack.lua b/src/luarocks/pack.lua
new file mode 100644
index 00000000..8a4df1a0
--- /dev/null
+++ b/src/luarocks/pack.lua
@@ -0,0 +1,188 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table; local _tl_table_unpack = unpack or table.unpack
+
+local pack = {}
+
+
+local queries = require("luarocks.queries")
+local path = require("luarocks.path")
+local repos = require("luarocks.repos")
+local fetch = require("luarocks.fetch")
+local fs = require("luarocks.fs")
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+local dir = require("luarocks.dir")
+local manif = require("luarocks.manif")
+local search = require("luarocks.search")
+local signing = require("luarocks.signing")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+function pack.pack_source_rock(rockspec_file)
+
+ local rockspec, errload = fetch.load_rockspec(rockspec_file)
+ if errload then
+ return nil, "Error loading rockspec: " .. errload
+ end
+ rockspec_file = rockspec.local_abs_filename
+
+ local name_version = rockspec.name .. "-" .. rockspec.version
+ local rock_file = fs.absolute_name(name_version .. ".src.rock")
+
+ local temp_dir, err = fs.make_temp_dir("pack-" .. name_version)
+ if not temp_dir then
+ return nil, "Failed creating temporary directory: " .. err
+ end
+ util.schedule_function(fs.delete, temp_dir)
+
+ local source_file, source_dir = fetch.fetch_sources(rockspec, true, temp_dir)
+ if not source_file then
+ return nil, source_dir
+ end
+ local ok, errchange = fs.change_dir(source_dir)
+ if not ok then return nil, errchange end
+
+ fs.delete(rock_file)
+ fs.copy(rockspec_file, source_dir, "read")
+ ok, err = fs.zip(rock_file, dir.base_name(rockspec_file), dir.base_name(source_file))
+ if not ok then
+ return nil, "Failed packing " .. rock_file .. " - " .. err
+ end
+ fs.pop_dir()
+
+ return rock_file
+end
+
+local function copy_back_files(name, version, file_tree, deploy_dir, pack_dir, perms)
+ local ok, err = fs.make_dir(pack_dir)
+ if not ok then return nil, err end
+ for file, sub in pairs(file_tree) do
+ local source = dir.path(deploy_dir, file)
+ local target = dir.path(pack_dir, file)
+ if type(sub) == "table" then
+ ok, err = copy_back_files(name, version, sub, source, target)
+ if not ok then return nil, err end
+ else
+ local versioned = path.versioned_name(source, deploy_dir, name, version)
+ if fs.exists(versioned) then
+ fs.copy(versioned, target, perms)
+ else
+ fs.copy(source, target, perms)
+ end
+ end
+ end
+ return true
+end
+
+
+
+
+
+
+function pack.pack_installed_rock(query, tree)
+
+ local name, version, repo, repo_url = search.pick_installed_rock(query, tree)
+ if not name then
+ return nil, version
+ end
+
+ local root = path.root_from_rocks_dir(repo_url)
+ local prefix = path.install_dir(name, version, root)
+ if not fs.exists(prefix) then
+ return nil, "'" .. name .. " " .. version .. "' does not seem to be an installed rock."
+ end
+
+ local rock_manifest, err = manif.load_rock_manifest(name, version, root)
+ if not rock_manifest then return nil, err end
+
+ local name_version = name .. "-" .. version
+ local rock_file = fs.absolute_name(name_version .. "." .. cfg.arch .. ".rock")
+
+ local temp_dir = fs.make_temp_dir("pack")
+ fs.copy_contents(prefix, temp_dir)
+
+ local is_binary = false
+ if rock_manifest.lib then
+ local ok, err = copy_back_files(name, version, (rock_manifest.lib), path.deploy_lib_dir(repo), dir.path(temp_dir, "lib"), "exec")
+ if not ok then return nil, "Failed copying back files: " .. err end
+ is_binary = true
+ end
+ if rock_manifest.lua then
+ local ok, err = copy_back_files(name, version, (rock_manifest.lua), path.deploy_lua_dir(repo), dir.path(temp_dir, "lua"), "read")
+ if not ok then return nil, "Failed copying back files: " .. err end
+ end
+
+ local ok, err = fs.change_dir(temp_dir)
+ if not ok then return nil, err end
+ if not is_binary and not repos.has_binaries(name, version) then
+ rock_file = rock_file:gsub("%." .. cfg.arch:gsub("%-", "%%-") .. "%.", ".all.")
+ end
+ fs.delete(rock_file)
+ ok, err = fs.zip(rock_file, _tl_table_unpack(fs.list_dir()))
+ if not ok then
+ return nil, "Failed packing " .. rock_file .. " - " .. err
+ end
+ fs.pop_dir()
+ fs.delete(temp_dir)
+ return rock_file
+end
+
+function pack.report_and_sign_local_file(file, err, sign)
+ if err then
+ return nil, err
+ end
+ local sigfile
+ if sign then
+ sigfile, err = signing.sign_file(file)
+ util.printout()
+ end
+ util.printout("Packed: " .. file)
+ if sigfile then
+ util.printout("Signature stored in: " .. sigfile)
+ end
+ if err then
+ return nil, err
+ end
+ return true
+end
+
+function pack.pack_binary_rock(name, namespace, version, sign, cmd)
+
+
+
+
+
+
+
+
+ local temp_dir, err = fs.make_temp_dir("luarocks-build-pack-" .. dir.base_name(name))
+ if not temp_dir then
+ return nil, "Failed creating temporary directory: " .. err
+ end
+ util.schedule_function(fs.delete, temp_dir)
+
+ path.use_tree(temp_dir)
+ local ok, err = cmd()
+ if not ok then
+ return nil, err
+ end
+ local rname, rversion = path.parse_name(name)
+ if not rname then
+ rname, rversion = name, version
+ end
+ local query = queries.new(rname, namespace, rversion)
+ local file, err = pack.pack_installed_rock(query, temp_dir)
+ return pack.report_and_sign_local_file(file, err, sign)
+end
+
+return pack
diff --git a/src/luarocks/path.lua b/src/luarocks/path.lua
new file mode 100644
index 00000000..65c1a7d2
--- /dev/null
+++ b/src/luarocks/path.lua
@@ -0,0 +1,254 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local io = _tl_compat and _tl_compat.io or io; local package = _tl_compat and _tl_compat.package or package; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+
+
+
+
+local cfg = require("luarocks.core.cfg")
+local core = require("luarocks.core.path")
+local dir = require("luarocks.dir")
+local util = require("luarocks.core.util")
+
+
+
+local path = {}
+
+
+
+
+
+
+
+
+
+path.rocks_dir = core.rocks_dir
+path.versioned_name = core.versioned_name
+path.path_to_module = core.path_to_module
+path.deploy_lua_dir = core.deploy_lua_dir
+path.deploy_lib_dir = core.deploy_lib_dir
+path.map_trees = core.map_trees
+path.rocks_tree_to_string = core.rocks_tree_to_string
+
+function path.root_dir(tree)
+ if type(tree) == "string" then
+ return tree
+ else
+ return tree.root
+ end
+end
+
+
+
+
+function path.rockspec_name_from_rock(rock_name)
+ local base_name = dir.base_name(rock_name)
+ return base_name:match("(.*)%.[^.]*.rock") .. ".rockspec"
+end
+
+function path.root_from_rocks_dir(rocks_dir)
+ return rocks_dir:match("(.*)" .. util.matchquote(cfg.rocks_subdir) .. ".*$")
+end
+
+function path.deploy_bin_dir(tree)
+ return dir.path(path.root_dir(tree), "bin")
+end
+
+function path.manifest_file(tree)
+ return dir.path(path.rocks_dir(tree), "manifest")
+end
+
+
+
+
+
+
+function path.versions_dir(name, tree)
+ assert(not name:match("/"))
+ return dir.path(path.rocks_dir(tree), name)
+end
+
+
+
+
+
+
+
+function path.install_dir(name, version, tree)
+ assert(not name:match("/"))
+ return dir.path(path.rocks_dir(tree), name, version)
+end
+
+
+
+
+
+
+
+function path.rockspec_file(name, version, tree)
+ assert(not name:match("/"))
+ return dir.path(path.rocks_dir(tree), name, version, name .. "-" .. version .. ".rockspec")
+end
+
+
+
+
+
+
+
+function path.rock_manifest_file(name, version, tree)
+ assert(not name:match("/"))
+ return dir.path(path.rocks_dir(tree), name, version, "rock_manifest")
+end
+
+
+
+
+
+
+
+function path.rock_namespace_file(name, version, tree)
+ assert(not name:match("/"))
+ return dir.path(path.rocks_dir(tree), name, version, "rock_namespace")
+end
+
+
+
+
+
+
+
+function path.lib_dir(name, version, tree)
+ assert(not name:match("/"))
+ return dir.path(path.rocks_dir(tree), name, version, "lib")
+end
+
+
+
+
+
+
+
+function path.lua_dir(name, version, tree)
+ assert(not name:match("/"))
+ return dir.path(path.rocks_dir(tree), name, version, "lua")
+end
+
+
+
+
+
+
+
+function path.doc_dir(name, version, tree)
+ assert(not name:match("/"))
+ return dir.path(path.rocks_dir(tree), name, version, "doc")
+end
+
+
+
+
+
+
+
+function path.conf_dir(name, version, tree)
+ assert(not name:match("/"))
+ return dir.path(path.rocks_dir(tree), name, version, "conf")
+end
+
+
+
+
+
+
+
+
+function path.bin_dir(name, version, tree)
+ assert(not name:match("/"))
+ return dir.path(path.rocks_dir(tree), name, version, "bin")
+end
+
+
+
+
+
+
+function path.parse_name(file_name)
+ if file_name:match("%.rock$") then
+ return dir.base_name(file_name):match("(.*)-([^-]+-%d+)%.([^.]+)%.rock$")
+ else
+ return dir.base_name(file_name):match("(.*)-([^-]+-%d+)%.(rockspec)")
+ end
+end
+
+
+
+
+
+
+
+function path.make_url(pathname, name, version, arch)
+ assert(not name:match("/"))
+ local filename = name .. "-" .. version
+ if arch == "installed" then
+ filename = dir.path(name, version, filename .. ".rockspec")
+ elseif arch == "rockspec" then
+ filename = filename .. ".rockspec"
+ else
+ filename = filename .. "." .. arch .. ".rock"
+ end
+ return dir.path(pathname, filename)
+end
+
+
+
+
+
+function path.module_to_path(mod)
+ return (mod:gsub("[^.]*$", ""):gsub("%.", "/"))
+end
+
+function path.use_tree(tree)
+ cfg.root_dir = tree
+ cfg.rocks_dir = path.rocks_dir(tree)
+ cfg.deploy_bin_dir = path.deploy_bin_dir(tree)
+ cfg.deploy_lua_dir = path.deploy_lua_dir(tree)
+ cfg.deploy_lib_dir = path.deploy_lib_dir(tree)
+end
+
+function path.add_to_package_paths(tree)
+ package.path = dir.path(path.deploy_lua_dir(tree), "?.lua") .. ";" ..
+ dir.path(path.deploy_lua_dir(tree), "?/init.lua") .. ";" ..
+ package.path
+ package.cpath = dir.path(path.deploy_lib_dir(tree), "?." .. cfg.lib_extension) .. ";" ..
+ package.cpath
+end
+
+
+
+
+
+
+function path.read_namespace(name, version, tree)
+ assert(not name:match("/"))
+
+ local namespace
+ local fd = io.open(path.rock_namespace_file(name, version, tree), "r")
+ if fd then
+ namespace = fd:read("*a")
+ fd:close()
+ end
+ return namespace
+end
+
+function path.package_paths(deps_mode)
+ local lpaths = {}
+ local lcpaths = {}
+ path.map_trees(deps_mode, function(tree)
+ local root = path.root_dir(tree)
+ table.insert(lpaths, dir.path(root, cfg.lua_modules_path, "?.lua"))
+ table.insert(lpaths, dir.path(root, cfg.lua_modules_path, "?/init.lua"))
+ table.insert(lcpaths, dir.path(root, cfg.lib_modules_path, "?." .. cfg.lib_extension))
+ end)
+ return table.concat(lpaths, ";"), table.concat(lcpaths, ";")
+end
+
+return path
diff --git a/src/luarocks/persist.lua b/src/luarocks/persist.lua
new file mode 100644
index 00000000..34ea2a52
--- /dev/null
+++ b/src/luarocks/persist.lua
@@ -0,0 +1,275 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+
+
+local persist = {}
+
+
+
+
+
+
+
+
+
+local core = require("luarocks.core.persist")
+local util = require("luarocks.util")
+local dir = require("luarocks.dir")
+local fs = require("luarocks.fs")
+local cfg = require("luarocks.core.cfg")
+
+
+
+
+
+
+
+
+
+persist.run_file = core.run_file
+persist.load_into_table = core.load_into_table
+
+local write_table
+
+
+
+
+
+
+
+
+
+function persist.write_value(out, v, level, sub_order)
+ if type(v) == "table" then
+ level = level or 0
+ write_table(out, v, level + 1, sub_order)
+ elseif type(v) == "string" then
+ if v:match("[\r\n]") then
+ local open, close = "[[", "]]"
+ local equals = 0
+ local v_with_bracket = v .. "]"
+ while v_with_bracket:find(close, 1, true) do
+ equals = equals + 1
+ local eqs = ("="):rep(equals)
+ open, close = "[" .. eqs .. "[", "]" .. eqs .. "]"
+ end
+ out:write(open .. "\n" .. v .. close)
+ else
+ out:write(("%q"):format(v))
+ end
+ else
+ out:write(tostring(v))
+ end
+end
+
+local is_valid_plain_key
+do
+ local keywords = {
+ ["and"] = true,
+ ["break"] = true,
+ ["do"] = true,
+ ["else"] = true,
+ ["elseif"] = true,
+ ["end"] = true,
+ ["false"] = true,
+ ["for"] = true,
+ ["function"] = true,
+ ["goto"] = true,
+ ["if"] = true,
+ ["in"] = true,
+ ["local"] = true,
+ ["nil"] = true,
+ ["not"] = true,
+ ["or"] = true,
+ ["repeat"] = true,
+ ["return"] = true,
+ ["then"] = true,
+ ["true"] = true,
+ ["until"] = true,
+ ["while"] = true,
+ }
+ function is_valid_plain_key(k)
+ return k:match("^[a-zA-Z_][a-zA-Z0-9_]*$") and
+ not keywords[k]
+ end
+end
+
+local function write_table_key_assignment(out, k, level)
+ if type(k) == "string" and is_valid_plain_key(k) then
+ out:write(k)
+ else
+ out:write("[")
+ persist.write_value(out, k, level)
+ out:write("]")
+ end
+
+ out:write(" = ")
+end
+
+
+
+
+
+
+
+
+write_table = function(out, tbl, level, sort_by)
+ out:write("{")
+ local sep = "\n"
+ local indentation = " "
+ local indent = true
+ local i = 1
+ for k, v, sub_order in util.sortedpairs(tbl, sort_by) do
+ out:write(sep)
+ if indent then
+ for _ = 1, level do out:write(indentation) end
+ end
+
+ if type(k) == "number" then
+ i = i + 1
+ else
+ write_table_key_assignment(out, k, level)
+ end
+
+ persist.write_value(out, v, level, sub_order)
+ if type(v) == "number" then
+ sep = ", "
+ indent = false
+ else
+ sep = ",\n"
+ indent = true
+ end
+ end
+ if sep ~= "\n" then
+ out:write("\n")
+ for _ = 1, level - 1 do out:write(indentation) end
+ end
+ out:write("}")
+end
+
+
+
+
+
+
+local function write_table_as_assignments(out, tbl, sort_by)
+ for k, v, sub_order in util.sortedpairs(tbl, sort_by) do
+ if not (type(k) == "string" and is_valid_plain_key(k)) then
+ return nil, "cannot store '" .. tostring(k) .. "' as a plain key."
+ end
+ out:write(k .. " = ")
+ persist.write_value(out, v, 0, sub_order)
+ out:write("\n")
+ end
+ return true
+end
+
+
+
+
+local function write_table_as_table(out, tbl)
+ out:write("return {\n")
+ for k, v, sub_order in util.sortedpairs(tbl) do
+ out:write(" ")
+ write_table_key_assignment(out, k, 1)
+ persist.write_value(out, v, 1, sub_order)
+ out:write(",\n")
+ end
+ out:write("}\n")
+end
+
+
+
+
+
+
+
+
+function persist.save_from_table_to_string(tbl, sort_by)
+ local out = { buffer = {} }
+ function out:write(data) table.insert(self.buffer, data) end
+ local ok, err = write_table_as_assignments(out, tbl, sort_by)
+ if not ok then
+ return nil, err
+ end
+ return table.concat(out.buffer)
+end
+
+
+
+
+
+
+
+
+
+
+function persist.save_from_table(filename, tbl, sort_by)
+ local prefix = dir.dir_name(filename)
+ fs.make_dir(prefix)
+ local out = io.open(filename, "w")
+ if not out then
+ return nil, "Cannot create file at " .. filename
+ end
+ local ok, err = write_table_as_assignments(out, tbl, sort_by)
+ out:close()
+ if not ok then
+ return nil, err
+ end
+ return true
+end
+
+
+
+
+
+
+
+
+
+function persist.save_as_module(filename, tbl)
+ local out = io.open(filename, "w")
+ if not out then
+ return nil, "Cannot create file at " .. filename
+ end
+ write_table_as_table(out, tbl)
+ out:close()
+ return true
+end
+
+function persist.load_config_file_if_basic(filename, config)
+ local env = {
+ home = config.home,
+ }
+ local result, _, errcode = persist.load_into_table(filename, env)
+ if errcode == "load" or errcode == "run" then
+
+ return nil, "Could not read existing config file " .. filename
+ end
+
+ local tbl
+ if errcode == "open" then
+
+ tbl = {}
+ else
+ tbl = result
+ tbl.home = nil
+ end
+
+ return tbl
+end
+
+function persist.save_default_lua_version(prefix, lua_version)
+ local ok, err_makedir = fs.make_dir(prefix)
+ if not ok then
+ return nil, err_makedir
+ end
+ local fd, err_open = io.open(dir.path(prefix, "default-lua-version.lua"), "w")
+ if not fd then
+ return nil, err_open
+ end
+ fd:write('return "' .. lua_version .. '"\n')
+ fd:close()
+ return true
+end
+
+return persist
diff --git a/src/luarocks/queries.lua b/src/luarocks/queries.lua
new file mode 100644
index 00000000..34b6d687
--- /dev/null
+++ b/src/luarocks/queries.lua
@@ -0,0 +1,211 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+local queries = {}
+
+
+local vers = require("luarocks.core.vers")
+local util = require("luarocks.util")
+local cfg = require("luarocks.core.cfg")
+
+local query = require("luarocks.core.types.query")
+
+
+
+
+
+local query_mt = {}
+
+query_mt.__index = query.Query
+
+
+query.Query.arch = {
+ src = true,
+ all = true,
+ rockspec = true,
+ installed = true,
+
+}
+
+
+query.Query.substring = false
+
+
+
+local function arch_to_table(input)
+ if type(input) == "table" then
+ return input
+ elseif type(input) == "string" then
+ local arch = {}
+ for a in input:gmatch("[%w_-]+") do
+ arch[a] = true
+ end
+ return arch
+ end
+end
+
+
+
+
+
+
+
+
+
+
+function queries.new(name, namespace, version, substring, arch, operator)
+
+ operator = operator or "=="
+
+ local self = {
+ name = name,
+ namespace = namespace,
+ constraints = {},
+ substring = substring,
+ arch = arch_to_table(arch),
+ }
+ if version then
+ table.insert(self.constraints, { op = operator, version = vers.parse_version(version) })
+ end
+
+ query.Query.arch[cfg.arch] = true
+ return setmetatable(self, query_mt)
+end
+
+
+
+function queries.all(arch)
+
+ return queries.new("", nil, nil, true, arch)
+end
+
+do
+ local parse_constraints
+ do
+ local parse_constraint
+ do
+ local operators = {
+ ["=="] = "==",
+ ["~="] = "~=",
+ [">"] = ">",
+ ["<"] = "<",
+ [">="] = ">=",
+ ["<="] = "<=",
+ ["~>"] = "~>",
+
+ [""] = "==",
+ ["="] = "==",
+ ["!="] = "~=",
+ }
+
+
+
+
+
+
+
+
+
+ parse_constraint = function(input)
+
+ local no_upgrade, op, versionstr, rest = input:match("^(@?)([<>=~!]*)%s*([%w%.%_%-]+)[%s,]*(.*)")
+ local _op = operators[op]
+ local version = vers.parse_version(versionstr)
+ if not _op then
+ return nil, "Encountered bad constraint operator: '" .. tostring(op) .. "' in '" .. input .. "'"
+ end
+ if not version then
+ return nil, "Could not parse version from constraint: '" .. input .. "'"
+ end
+ return { op = _op, version = version, no_upgrade = no_upgrade == "@" and true or nil }, rest
+ end
+ end
+
+
+
+
+
+
+
+
+
+ parse_constraints = function(input)
+
+ local constraints, oinput = {}, input
+ local constraint
+ while #input > 0 do
+ constraint, input = parse_constraint(input)
+ if constraint then
+ table.insert(constraints, constraint)
+ else
+ return nil, "Failed to parse constraint '" .. tostring(oinput) .. "' with error: " .. input
+ end
+ end
+ return constraints
+ end
+ end
+
+
+
+
+
+ function queries.from_dep_string(depstr)
+
+ local ns_name, rest = depstr:match("^%s*([a-zA-Z0-9%.%-%_]*/?[a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)")
+ if not ns_name then
+ return nil, "failed to extract dependency name from '" .. depstr .. "'"
+ end
+
+ ns_name = ns_name:lower()
+
+ local constraints, err = parse_constraints(rest)
+ if not constraints then
+ return nil, err
+ end
+
+ local name, namespace = util.split_namespace(ns_name)
+
+ local self = {
+ name = name,
+ namespace = namespace,
+ constraints = constraints,
+ }
+
+ query.Query.arch[cfg.arch] = true
+ return setmetatable(self, query_mt)
+ end
+end
+
+function queries.from_persisted_table(tbl)
+ query.Query.arch[cfg.arch] = true
+ return setmetatable(tbl, query_mt)
+end
+
+
+
+
+
+function query_mt.__tostring(self)
+ local out = {}
+ if self.namespace then
+ table.insert(out, self.namespace)
+ table.insert(out, "/")
+ end
+ table.insert(out, self.name)
+
+ if #self.constraints > 0 then
+ local pretty = {}
+ for _, c in ipairs(self.constraints) do
+ local v = tostring(c.version)
+ if c.op == "==" then
+ table.insert(pretty, v)
+ else
+ table.insert(pretty, c.op .. " " .. v)
+ end
+ end
+ table.insert(out, " ")
+ table.insert(out, table.concat(pretty, ", "))
+ end
+
+ return table.concat(out)
+end
+
+return queries
diff --git a/src/luarocks/remove.lua b/src/luarocks/remove.lua
new file mode 100644
index 00000000..72d3a1d8
--- /dev/null
+++ b/src/luarocks/remove.lua
@@ -0,0 +1,140 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pairs = _tl_compat and _tl_compat.pairs or pairs; local table = _tl_compat and _tl_compat.table or table
+local remove = {}
+
+
+local search = require("luarocks.search")
+local deps = require("luarocks.deps")
+local fetch = require("luarocks.fetch")
+local repos = require("luarocks.repos")
+local repo_writer = require("luarocks.repo_writer")
+local path = require("luarocks.path")
+local util = require("luarocks.util")
+local cfg = require("luarocks.core.cfg")
+local manif = require("luarocks.manif")
+local queries = require("luarocks.queries")
+
+
+
+
+
+
+
+
+
+local function check_dependents(name, versions, deps_mode)
+ local dependents = {}
+
+ local skip_set = {}
+ skip_set[name] = {}
+ for version, _ in pairs(versions) do
+ skip_set[name][version] = true
+ end
+
+ local local_rocks = {}
+ local query_all = queries.all()
+ search.local_manifest_search(local_rocks, cfg.rocks_dir, query_all)
+ local_rocks[name] = nil
+ for rock_name, rock_versions in pairs(local_rocks) do
+ for rock_version, _ in pairs(rock_versions) do
+ local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version))
+ if rockspec then
+ local _, missing = deps.match_deps(rockspec.dependencies.queries, rockspec.rocks_provided, deps_mode, skip_set)
+ if missing[name] then
+ table.insert(dependents, { name = rock_name, version = rock_version })
+ end
+ end
+ end
+ end
+
+ return dependents
+end
+
+
+
+
+
+
+
+
+local function delete_versions(name, versions, deps_mode)
+
+ for version, _ in pairs(versions) do
+ util.printout("Removing " .. name .. " " .. version .. "...")
+ local ok, err = repo_writer.delete_version(name, version, deps_mode)
+ if not ok then return nil, err end
+ end
+
+ return true
+end
+
+function remove.remove_search_results(results, name, deps_mode, force, fast)
+ local versions = results[name]
+
+ local version = next(versions)
+ local second = next(versions, version)
+
+ local dependents = {}
+ if not fast then
+ util.printout("Checking stability of dependencies in the absence of")
+ util.printout(name .. " " .. table.concat((util.keys(versions)), ", ") .. "...")
+ util.printout()
+ dependents = check_dependents(name, versions, deps_mode)
+ end
+
+ if #dependents > 0 then
+ if force or fast then
+ util.printerr("The following packages may be broken by this forced removal:")
+ for _, dependent in ipairs(dependents) do
+ util.printerr(dependent.name .. " " .. dependent.version)
+ end
+ util.printerr()
+ else
+ if not second then
+ util.printerr("Will not remove " .. name .. " " .. version .. ".")
+ util.printerr("Removing it would break dependencies for: ")
+ else
+ util.printerr("Will not remove installed versions of " .. name .. ".")
+ util.printerr("Removing them would break dependencies for: ")
+ end
+ for _, dependent in ipairs(dependents) do
+ util.printerr(dependent.name .. " " .. dependent.version)
+ end
+ util.printerr()
+ util.printerr("Use --force to force removal (warning: this may break modules).")
+ return nil, "Failed removing."
+ end
+ end
+
+ local ok, err = delete_versions(name, versions, deps_mode)
+ if not ok then return nil, err end
+
+ util.printout("Removal successful.")
+ return true
+end
+
+function remove.remove_other_versions(name, version, force, fast)
+ local results = {}
+ local query = queries.new(name, nil, version, false, nil, "~=")
+ search.local_manifest_search(results, cfg.rocks_dir, query)
+ local warn
+ if results[name] then
+ local ok, err = remove.remove_search_results(results, name, cfg.deps_mode, force, fast)
+ if not ok then
+ warn = err
+ end
+ end
+
+ if not fast then
+
+
+ local rock_manifest, load_err = manif.load_rock_manifest(name, version)
+ local ok, err = repos.check_everything_is_installed(name, version, rock_manifest, cfg.root_dir, false)
+ if not ok then
+ return nil, err
+ end
+ end
+
+ return true, nil, warn
+end
+
+return remove
diff --git a/src/luarocks/repo_writer.lua b/src/luarocks/repo_writer.lua
new file mode 100644
index 00000000..0ccb3e91
--- /dev/null
+++ b/src/luarocks/repo_writer.lua
@@ -0,0 +1,52 @@
+local repo_writer = {}
+
+
+local fs = require("luarocks.fs")
+local path = require("luarocks.path")
+local repos = require("luarocks.repos")
+local writer = require("luarocks.manif.writer")
+
+function repo_writer.deploy_files(name, version, wrap_bin_scripts, deps_mode, namespace)
+ local ok, err
+
+ if not fs.exists(path.rock_manifest_file(name, version)) then
+ ok, err = writer.make_rock_manifest(name, version)
+ if err then
+ return nil, err
+ end
+ end
+
+ if namespace then
+ ok, err = writer.make_namespace_file(name, version, namespace)
+ if not ok then
+ return nil, err
+ end
+ end
+
+ ok, err = repos.deploy_local_files(name, version, wrap_bin_scripts, deps_mode)
+ if not ok then
+ return nil, err
+ end
+
+ ok, err = writer.add_to_manifest(name, version, nil, deps_mode)
+ return ok, err
+end
+
+function repo_writer.delete_version(name, version, deps_mode, quick)
+ local ok, err, op = repos.delete_local_version(name, version, deps_mode, quick)
+
+ if op == "remove" then
+ local rok, rerr = writer.remove_from_manifest(name, version, nil, deps_mode)
+ if ok and not rok then
+ ok, err = rok, rerr
+ end
+ end
+
+ return ok, err
+end
+
+function repo_writer.refresh_manifest(rocks_dir)
+ return writer.make_manifest(rocks_dir, "one")
+end
+
+return repo_writer
diff --git a/src/luarocks/repos.lua b/src/luarocks/repos.lua
new file mode 100644
index 00000000..84c01815
--- /dev/null
+++ b/src/luarocks/repos.lua
@@ -0,0 +1,690 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local os = _tl_compat and _tl_compat.os or os; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+
+local repos = {Op = {}, Paths = {}, }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local fs = require("luarocks.fs")
+local path = require("luarocks.path")
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+local dir = require("luarocks.dir")
+local manif = require("luarocks.manif")
+local vers = require("luarocks.core.vers")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local function get_installed_versions(name)
+ assert(not name:match("/"))
+
+ local dirs = fs.list_dir(path.versions_dir(name))
+ return (dirs and #dirs > 0) and dirs or nil
+end
+
+
+
+
+
+
+
+function repos.is_installed(name, version)
+ assert(not name:match("/"))
+
+ return fs.is_dir(path.install_dir(name, version))
+end
+
+function repos.recurse_rock_manifest_entry(entry, action)
+ if entry == nil then
+ return true
+ end
+
+ local function do_recurse_rock_manifest_entry(tree, parent_path)
+
+ for file, sub in pairs(tree) do
+ local sub_path = (parent_path and (parent_path .. "/") or "") .. file
+ local ok, err
+
+ if type(sub) == "table" then
+ ok, err = do_recurse_rock_manifest_entry(sub, sub_path)
+ else
+ ok, err = action(sub_path)
+ end
+
+ if err then return nil, err end
+ end
+ return true
+ end
+ return do_recurse_rock_manifest_entry(entry)
+end
+
+local function store_package_data(result, rock_manifest, deploy_type)
+ if rock_manifest[deploy_type] then
+ repos.recurse_rock_manifest_entry(rock_manifest[deploy_type], function(file_path)
+ local _, item_name = manif.get_provided_item(deploy_type, file_path)
+ result[item_name] = file_path
+ return true
+ end)
+ end
+end
+
+
+
+
+
+
+
+
+
+
+function repos.package_modules(name, version)
+ assert(not name:match("/"))
+
+ local result = {}
+ local rock_manifest = manif.load_rock_manifest(name, version)
+ if not rock_manifest then return result end
+ store_package_data(result, rock_manifest, "lib")
+ store_package_data(result, rock_manifest, "lua")
+ return result
+end
+
+
+
+
+
+
+
+
+
+
+function repos.package_commands(name, version)
+ assert(not name:match("/"))
+
+ local result = {}
+ local rock_manifest = manif.load_rock_manifest(name, version)
+ if not rock_manifest then return result end
+ store_package_data(result, rock_manifest, "bin")
+ return result
+end
+
+
+
+
+
+
+
+function repos.has_binaries(name, version)
+ assert(not name:match("/"))
+
+ local entries = manif.load_rock_manifest(name, version)
+ if not entries then
+ return false
+ end
+ local bin = entries["bin"]
+ if type(bin) == "table" then
+ for bin_name, md5 in pairs(bin) do
+
+ if fs.is_actual_binary(dir.path(cfg.deploy_bin_dir, bin_name)) then
+ return true
+ end
+ end
+ end
+ return false
+end
+
+function repos.run_hook(rockspec, hook_name)
+
+ local hooks = rockspec.hooks
+ if not hooks then
+ return true
+ end
+
+ if cfg.hooks_enabled == false then
+ return nil, "This rockspec contains hooks, which are blocked by the 'hooks_enabled' setting in your LuaRocks configuration."
+ end
+
+ if not hooks.substituted_variables then
+ util.variable_substitutions(hooks, rockspec.variables)
+ hooks.substituted_variables = true
+ end
+ local hook = (hooks)[hook_name]
+ if hook then
+ util.printout(hook)
+ if not fs.execute(hook) then
+ return nil, "Failed running " .. hook_name .. " hook."
+ end
+ end
+ return true
+end
+
+function repos.should_wrap_bin_scripts(rockspec)
+
+ if cfg.wrap_bin_scripts then
+ return cfg.wrap_bin_scripts
+ end
+ if rockspec.deploy and rockspec.deploy.wrap_bin_scripts == false then
+ return false
+ end
+ return true
+end
+
+local function find_suffixed(file, suffix)
+ local filenames = { file }
+ if suffix and suffix ~= "" then
+ table.insert(filenames, 1, file .. suffix)
+ end
+
+ for _, filename in ipairs(filenames) do
+ if fs.exists(filename) then
+ return filename
+ end
+ end
+
+ return nil, table.concat(filenames, ", ") .. " not found"
+end
+
+local function check_suffix(filename, suffix)
+ local suffixed_filename, err = find_suffixed(filename, suffix)
+ if not suffixed_filename then
+ return ""
+ end
+ return suffixed_filename:sub(#filename + 1)
+end
+
+
+
+
+
+
+
+local function get_deploy_paths(name, version, deploy_type, file_path, repo)
+
+ repo = repo or cfg.root_dir
+ local deploy_dir = (path)["deploy_" .. deploy_type .. "_dir"](repo)
+ local non_versioned = dir.path(deploy_dir, file_path)
+ local versioned = path.versioned_name(non_versioned, deploy_dir, name, version)
+ return { nv = non_versioned, v = versioned }
+end
+
+local function check_spot_if_available(name, version, deploy_type, file_path)
+ local item_type, item_name = manif.get_provided_item(deploy_type, file_path)
+ local cur_name, cur_version = manif.get_current_provider(item_type, item_name)
+
+
+
+
+ if not cur_name and deploy_type == "lua" and item_name:match("%.init$") then
+ cur_name, cur_version = manif.get_current_provider(item_type, (item_name:gsub("%.init$", "")))
+ end
+
+ if (not cur_name) or
+ (name < cur_name) or
+ (name == cur_name and (version == cur_version or
+ vers.compare_versions(version, cur_version))) then
+ return "nv", cur_name, cur_version, item_name
+ else
+
+ return "v", cur_name, cur_version, item_name
+ end
+end
+
+local function backup_existing(should_backup, target)
+ if not should_backup then
+ fs.delete(target)
+ return
+ end
+ if fs.exists(target) then
+ local backup = target
+ repeat
+ backup = backup .. "~"
+ until not fs.exists(backup)
+
+ util.warning(target .. " is not tracked by this installation of LuaRocks. Moving it to " .. backup)
+ local move_ok, move_err = os.rename(target, backup)
+ if not move_ok then
+ return nil, move_err
+ end
+ return backup
+ end
+end
+
+local function prepare_op_install()
+ local mkdirs = {}
+ local rmdirs = {}
+
+ local function memoize_mkdir(d)
+ if mkdirs[d] then
+ return true
+ end
+ local ok, err = fs.make_dir(d)
+ if not ok then
+ return nil, err
+ end
+ mkdirs[d] = true
+ return true
+ end
+
+ local function op_install(op)
+ local ok, err = memoize_mkdir(dir.dir_name(op.dst))
+ if not ok then
+ return nil, err
+ end
+
+ local backup, err = backup_existing(op.backup, op.dst)
+ if err then
+ return nil, err
+ end
+ if backup then
+ op.backup_file = backup
+ end
+
+ ok, err = op.fn(op.src, op.dst, op.backup)
+ if not ok then
+ return nil, err
+ end
+
+ rmdirs[dir.dir_name(op.src)] = true
+ return true
+ end
+
+ local function done_op_install()
+ for d, _ in pairs(rmdirs) do
+ fs.remove_dir_tree_if_empty(d)
+ end
+ end
+
+ return op_install, done_op_install
+end
+
+local function rollback_install(op)
+ fs.delete(op.dst)
+ if op.backup_file then
+ os.rename(op.backup_file, op.dst)
+ end
+ fs.remove_dir_tree_if_empty(dir.dir_name(op.dst))
+ return true
+end
+
+local function op_rename(op)
+ if op.suffix then
+ local suffix = check_suffix(op.src, op.suffix)
+ op.src = op.src .. suffix
+ op.dst = op.dst .. suffix
+ end
+
+ if fs.exists(op.src) then
+ fs.make_dir(dir.dir_name(op.dst))
+ fs.delete(op.dst)
+ local ok, err = os.rename(op.src, op.dst)
+ fs.remove_dir_tree_if_empty(dir.dir_name(op.src))
+ return ok, err
+ else
+ return true
+ end
+end
+
+local function rollback_rename(op)
+ return op_rename({ src = op.dst, dst = op.src })
+end
+
+local function prepare_op_delete()
+ local deletes = {}
+ local rmdirs = {}
+
+ local function done_op_delete()
+ for _, f in ipairs(deletes) do
+ os.remove(f)
+ end
+
+ for d, _ in pairs(rmdirs) do
+ fs.remove_dir_tree_if_empty(d)
+ end
+ end
+
+ local function op_delete(op)
+ if op.suffix then
+ local suffix = check_suffix(op.name, op.suffix)
+ op.name = op.name .. suffix
+ end
+
+ table.insert(deletes, op.name)
+
+ rmdirs[dir.dir_name(op.name)] = true
+ end
+
+ return op_delete, done_op_delete
+end
+
+local function rollback_ops(ops, op_fn, n)
+ for i = 1, n do
+ op_fn(ops[i])
+ end
+end
+
+
+function repos.check_everything_is_installed(name, version, rock_manifest, repo, accept_versioned)
+ local missing = {}
+ local suffix = cfg.wrapper_suffix or ""
+ for _, category in ipairs({ "bin", "lua", "lib" }) do
+ if rock_manifest[category] then
+ repos.recurse_rock_manifest_entry(rock_manifest[category], function(file_path)
+ local paths = get_deploy_paths(name, version, category, file_path, repo)
+ if category == "bin" then
+ if (fs.exists(paths.nv) or fs.exists(paths.nv .. suffix)) or
+ (accept_versioned and (fs.exists(paths.v) or fs.exists(paths.v .. suffix))) then
+ return
+ end
+ else
+ if fs.exists(paths.nv) or (accept_versioned and fs.exists(paths.v)) then
+ return
+ end
+ end
+ table.insert(missing, paths.nv)
+ end)
+ end
+ end
+ if #missing > 0 then
+ return nil, "failed deploying files. " ..
+ "The following files were not installed:\n" ..
+ table.concat(missing, "\n")
+ end
+ return true
+end
+
+
+
+
+
+
+
+
+function repos.deploy_local_files(name, version, wrap_bin_scripts, deps_mode)
+ assert(not name:match("/"))
+
+ local rock_manifest, load_err = manif.load_rock_manifest(name, version)
+ if not rock_manifest then return nil, load_err end
+
+ local repo = cfg.root_dir
+ local renames = {}
+ local installs = {}
+
+ local function install_binary(source, target)
+ if wrap_bin_scripts and fs.is_lua(source) then
+ return fs.wrap_script(source, target, deps_mode, name, version)
+ else
+ return fs.copy_binary(source, target)
+ end
+ end
+
+ local function move_lua(source, target)
+ return fs.move(source, target, "read")
+ end
+
+ local function move_lib(source, target)
+ return fs.move(source, target, "exec")
+ end
+
+ if rock_manifest.bin then
+ local source_dir = path.bin_dir(name, version)
+ repos.recurse_rock_manifest_entry(rock_manifest.bin, function(file_path)
+ local source = dir.path(source_dir, file_path)
+ local paths = get_deploy_paths(name, version, "bin", file_path, repo)
+ local mode, cur_name, cur_version = check_spot_if_available(name, version, "bin", file_path)
+
+ if mode == "nv" and cur_name then
+ local cur_paths = get_deploy_paths(cur_name, cur_version, "bin", file_path, repo)
+ table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v, suffix = cfg.wrapper_suffix })
+ end
+ local target = mode == "nv" and paths.nv or paths.v
+ local backup = name ~= cur_name or version ~= cur_version
+ if wrap_bin_scripts and fs.is_lua(source) then
+ target = target .. (cfg.wrapper_suffix or "")
+ end
+ table.insert(installs, { fn = install_binary, src = source, dst = target, backup = backup })
+ end)
+ end
+
+ if rock_manifest.lua then
+ local source_dir = path.lua_dir(name, version)
+ repos.recurse_rock_manifest_entry(rock_manifest.lua, function(file_path)
+ local source = dir.path(source_dir, file_path)
+ local paths = get_deploy_paths(name, version, "lua", file_path, repo)
+ local mode, cur_name, cur_version = check_spot_if_available(name, version, "lua", file_path)
+
+ if mode == "nv" and cur_name then
+ local cur_paths = get_deploy_paths(cur_name, cur_version, "lua", file_path, repo)
+ table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v })
+ cur_paths = get_deploy_paths(cur_name, cur_version, "lib", file_path:gsub("%.lua$", "." .. cfg.lib_extension), repo)
+ table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v })
+ end
+ local target = mode == "nv" and paths.nv or paths.v
+ local backup = name ~= cur_name or version ~= cur_version
+ table.insert(installs, { fn = move_lua, src = source, dst = target, backup = backup })
+ end)
+ end
+
+ if rock_manifest.lib then
+ local source_dir = path.lib_dir(name, version)
+ repos.recurse_rock_manifest_entry(rock_manifest.lib, function(file_path)
+ local source = dir.path(source_dir, file_path)
+ local paths = get_deploy_paths(name, version, "lib", file_path, repo)
+ local mode, cur_name, cur_version = check_spot_if_available(name, version, "lib", file_path)
+
+ if mode == "nv" and cur_name then
+ local cur_paths = get_deploy_paths(cur_name, cur_version, "lua", file_path:gsub("%.[^.]+$", ".lua"), repo)
+ table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v })
+ cur_paths = get_deploy_paths(cur_name, cur_version, "lib", file_path, repo)
+ table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v })
+ end
+ local target = mode == "nv" and paths.nv or paths.v
+ local backup = name ~= cur_name or version ~= cur_version
+ table.insert(installs, { fn = move_lib, src = source, dst = target, backup = backup })
+ end)
+ end
+
+ for i, op in ipairs(renames) do
+ local ok, err = op_rename(op)
+ if not ok then
+ rollback_ops(renames, rollback_rename, i - 1)
+ return nil, err
+ end
+ end
+ local op_install, done_op_install = prepare_op_install()
+ for i, op in ipairs(installs) do
+ local ok, err = op_install(op)
+ if not ok then
+ rollback_ops(installs, rollback_install, i - 1)
+ rollback_ops(renames, rollback_rename, #renames)
+ return nil, err
+ end
+ end
+ done_op_install()
+
+ local ok, err = repos.check_everything_is_installed(name, version, rock_manifest, repo, true)
+ if not ok then
+ return nil, err
+ end
+
+ return true
+end
+
+local function add_to_double_checks(double_checks, name, version)
+ double_checks[name] = double_checks[name] or {}
+ double_checks[name][version] = true
+end
+
+local function double_check_all(double_checks, repo)
+ local errs = {}
+ for next_name, versions in pairs(double_checks) do
+ for next_version in pairs(versions) do
+ local rock_manifest, load_err = manif.load_rock_manifest(next_name, next_version)
+ local ok, err = repos.check_everything_is_installed(next_name, next_version, rock_manifest, repo, true)
+ if not ok then
+ table.insert(errs, err)
+ end
+ end
+ end
+ if next(errs) then
+ return nil, table.concat(errs, "\n")
+ end
+ return true
+end
+
+
+
+
+
+
+
+
+
+
+
+function repos.delete_local_version(name, version, deps_mode, quick)
+ assert(not name:match("/"))
+
+ local rock_manifest, load_err = manif.load_rock_manifest(name, version)
+ if not rock_manifest then
+ if not quick then
+ return nil, "rock_manifest file not found for " .. name .. " " .. version .. " - removed entry from the manifest", "remove"
+ end
+ return nil, load_err, "fail"
+ end
+
+ local repo = cfg.root_dir
+ local renames = {}
+ local deletes = {}
+
+ local double_checks = {}
+
+ if rock_manifest.bin then
+ repos.recurse_rock_manifest_entry(rock_manifest.bin, function(file_path)
+ local paths = get_deploy_paths(name, version, "bin", file_path, repo)
+ local mode, cur_name, cur_version, item_name = check_spot_if_available(name, version, "bin", file_path)
+ if mode == "v" then
+ table.insert(deletes, { name = paths.v, suffix = cfg.wrapper_suffix })
+ else
+ table.insert(deletes, { name = paths.nv, suffix = cfg.wrapper_suffix })
+
+ local next_name, next_version = manif.get_next_provider("command", item_name)
+ if next_name then
+ add_to_double_checks(double_checks, next_name, next_version)
+ local next_paths = get_deploy_paths(next_name, next_version, "bin", file_path, repo)
+ table.insert(renames, { src = next_paths.v, dst = next_paths.nv, suffix = cfg.wrapper_suffix })
+ end
+ end
+ end)
+ end
+
+ if rock_manifest.lua then
+ repos.recurse_rock_manifest_entry(rock_manifest.lua, function(file_path)
+ local paths = get_deploy_paths(name, version, "lua", file_path, repo)
+ local mode, cur_name, cur_version, item_name = check_spot_if_available(name, version, "lua", file_path)
+ if mode == "v" then
+ table.insert(deletes, { name = paths.v })
+ else
+ table.insert(deletes, { name = paths.nv })
+
+ local next_name, next_version = manif.get_next_provider("module", item_name)
+ if next_name then
+ add_to_double_checks(double_checks, next_name, next_version)
+ local next_lua_paths = get_deploy_paths(next_name, next_version, "lua", file_path, repo)
+ table.insert(renames, { src = next_lua_paths.v, dst = next_lua_paths.nv })
+ local next_lib_paths = get_deploy_paths(next_name, next_version, "lib", file_path:gsub("%.[^.]+$", ".lua"), repo)
+ table.insert(renames, { src = next_lib_paths.v, dst = next_lib_paths.nv })
+ end
+ end
+ end)
+ end
+
+ if rock_manifest.lib then
+ repos.recurse_rock_manifest_entry(rock_manifest.lib, function(file_path)
+ local paths = get_deploy_paths(name, version, "lib", file_path, repo)
+ local mode, cur_name, cur_version, item_name = check_spot_if_available(name, version, "lib", file_path)
+ if mode == "v" then
+ table.insert(deletes, { name = paths.v })
+ else
+ table.insert(deletes, { name = paths.nv })
+
+ local next_name, next_version = manif.get_next_provider("module", item_name)
+ if next_name then
+ add_to_double_checks(double_checks, next_name, next_version)
+ local next_lua_paths = get_deploy_paths(next_name, next_version, "lua", file_path:gsub("%.[^.]+$", ".lua"), repo)
+ table.insert(renames, { src = next_lua_paths.v, dst = next_lua_paths.nv })
+ local next_lib_paths = get_deploy_paths(next_name, next_version, "lib", file_path, repo)
+ table.insert(renames, { src = next_lib_paths.v, dst = next_lib_paths.nv })
+ end
+ end
+ end)
+ end
+
+ local op_delete, done_op_delete = prepare_op_delete()
+ for _, op in ipairs(deletes) do
+ op_delete(op)
+ end
+ done_op_delete()
+
+ if not quick then
+ for _, op in ipairs(renames) do
+ op_rename(op)
+ end
+
+ local ok, err = double_check_all(double_checks, repo)
+ if not ok then
+ return nil, err, "fail"
+ end
+ end
+
+ fs.delete(path.install_dir(name, version))
+ if not get_installed_versions(name) then
+ fs.delete(dir.path(cfg.rocks_dir, name))
+ end
+
+ if quick then
+ return true, nil, "ok"
+ end
+
+ return true, nil, "remove"
+end
+
+return repos
diff --git a/src/luarocks/require.lua b/src/luarocks/require.lua
new file mode 100644
index 00000000..5b7ca3c3
--- /dev/null
+++ b/src/luarocks/require.lua
@@ -0,0 +1,2 @@
+
+return require("luarocks.loader")
diff --git a/src/luarocks/results.lua b/src/luarocks/results.lua
new file mode 100644
index 00000000..fec1e463
--- /dev/null
+++ b/src/luarocks/results.lua
@@ -0,0 +1,60 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local string = _tl_compat and _tl_compat.string or string; local results = {}
+
+
+local vers = require("luarocks.core.vers")
+local util = require("luarocks.util")
+
+
+local result = require("luarocks.core.types.result")
+
+
+local result_mt = {}
+
+result_mt.__index = result.Result
+
+function results.new(name, version, repo, arch, namespace)
+
+ assert(not name:match("/"))
+
+
+ if not namespace then
+ name, namespace = util.split_namespace(name)
+ end
+
+ local self = {
+ name = name,
+ version = version,
+ namespace = namespace,
+ arch = arch,
+ repo = repo,
+ }
+
+ return setmetatable(self, result_mt)
+end
+
+
+
+
+
+
+
+
+local function match_name(query, name)
+ if query.substring then
+ return name:find(query.name, 0, true) and true or false
+ else
+ return name == query.name
+ end
+end
+
+
+
+
+function result.Result:satisfies(query)
+ return match_name(query, self.name) and
+ (query.arch[self.arch] or query.arch["any"]) and
+ ((not query.namespace) or (query.namespace == self.namespace)) and
+ (vers.match_constraints(vers.parse_version(self.version), query.constraints))
+end
+
+return results
diff --git a/src/luarocks/rockspecs.lua b/src/luarocks/rockspecs.lua
new file mode 100644
index 00000000..c41469fd
--- /dev/null
+++ b/src/luarocks/rockspecs.lua
@@ -0,0 +1,184 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+local rockspecs = {}
+
+
+local cfg = require("luarocks.core.cfg")
+local dir = require("luarocks.dir")
+local path = require("luarocks.path")
+local queries = require("luarocks.queries")
+local type_rockspec = require("luarocks.type.rockspec")
+local util = require("luarocks.util")
+local vers = require("luarocks.core.vers")
+
+
+
+
+
+
+local vendored_build_type_set = {
+ ["builtin"] = true,
+ ["cmake"] = true,
+ ["command"] = true,
+ ["make"] = true,
+ ["module"] = true,
+ ["none"] = true,
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local function platform_overrides(tbl)
+
+ if not tbl then return end
+
+ local tblp = tbl.platforms
+
+ if type(tblp) == "table" then
+ for platform in cfg.each_platform() do
+ local platform_tbl = tblp[platform]
+ if type(platform_tbl) == "table" then
+ util.deep_merge(tbl, platform_tbl)
+ end
+ end
+ end
+ tbl.platforms = nil
+end
+
+local function convert_dependencies(dependencies)
+ local qs = {}
+ for i = 1, #dependencies do
+ local parsed, err = queries.from_dep_string(dependencies[i])
+ if not parsed then
+ return nil, "Parse error processing dependency '" .. dependencies[i] .. "': " .. tostring(err)
+ end
+ qs[i] = parsed
+ end
+ dependencies.queries = qs
+ return true
+end
+
+
+
+
+
+local function configure_paths(rockspec)
+ local vars = {}
+ for k, v in pairs(cfg.variables) do
+ vars[k] = v
+ end
+ local name, version = rockspec.name, rockspec.version
+ vars.PREFIX = path.install_dir(name, version)
+ vars.LUADIR = path.lua_dir(name, version)
+ vars.LIBDIR = path.lib_dir(name, version)
+ vars.CONFDIR = path.conf_dir(name, version)
+ vars.BINDIR = path.bin_dir(name, version)
+ vars.DOCDIR = path.doc_dir(name, version)
+ rockspec.variables = vars
+end
+
+function rockspecs.from_persisted_table(filename, rockspec, globals, quick)
+
+ if rockspec.rockspec_format then
+ if vers.compare_versions(rockspec.rockspec_format, type_rockspec.rockspec_format) then
+ return nil, "Rockspec format " .. rockspec.rockspec_format .. " is not supported, please upgrade LuaRocks."
+ end
+ end
+
+ if not quick then
+ local ok, err = type_rockspec.check(rockspec, globals or {})
+ if not ok then
+ return nil, err
+ end
+ end
+
+
+
+
+
+ do
+ local parsed_format = vers.parse_version(rockspec.rockspec_format or "1.0")
+ rockspec.format_is_at_least = function(self, version)
+ return parsed_format >= vers.parse_version(version)
+ end
+ end
+
+ platform_overrides(rockspec.build)
+ platform_overrides(rockspec.dependencies)
+ platform_overrides(rockspec.build_dependencies)
+ platform_overrides(rockspec.test_dependencies)
+ platform_overrides(rockspec.external_dependencies)
+ platform_overrides(rockspec.source)
+ platform_overrides(rockspec.hooks)
+ platform_overrides(rockspec.test)
+
+ rockspec.name = rockspec.package:lower()
+
+ local protocol, pathname = dir.split_url(rockspec.source.url)
+ if dir.is_basic_protocol(protocol) then
+ rockspec.source.file = rockspec.source.file or dir.base_name(rockspec.source.url)
+ end
+ rockspec.source.protocol, rockspec.source.pathname = protocol, pathname
+
+
+ if rockspec.source.cvs_module then rockspec.source.module = rockspec.source.cvs_module end
+ if rockspec.source.cvs_tag then rockspec.source.tag = rockspec.source.cvs_tag end
+
+ rockspec.local_abs_filename = filename
+ rockspec.source.dir_set = rockspec.source.dir ~= nil
+ rockspec.source.dir = rockspec.source.dir or rockspec.source.module
+
+ rockspec.rocks_provided = util.get_rocks_provided(rockspec)
+
+ rockspec.dependencies = rockspec.dependencies or {}
+ rockspec.build_dependencies = rockspec.build_dependencies or {}
+ rockspec.test_dependencies = rockspec.test_dependencies or {}
+ for _, d in ipairs({ rockspec.dependencies, rockspec.build_dependencies, rockspec.test_dependencies }) do
+ local _, err = convert_dependencies(d)
+ if err then
+ return nil, err
+ end
+ end
+
+ if rockspec.build and
+ rockspec.build.type and
+ not vendored_build_type_set[rockspec.build.type] then
+ local build_pkg_name = "luarocks-build-" .. rockspec.build.type
+ if not rockspec.build_dependencies then
+ rockspec.build_dependencies = {}
+ end
+
+ local found = false
+ for _, dep in ipairs(rockspec.build_dependencies.queries) do
+ if dep.name == build_pkg_name then
+ found = true
+ break
+ end
+ end
+
+ if not found then
+ local query, errfromdep = queries.from_dep_string(build_pkg_name)
+ if errfromdep then
+ return nil, "Invalid dependency in rockspec: " .. errfromdep
+ end
+ table.insert(rockspec.build_dependencies.queries, query)
+ end
+ end
+
+ if not quick then
+ configure_paths(rockspec)
+ end
+
+ return rockspec
+end
+
+return rockspecs
diff --git a/src/luarocks/search.lua b/src/luarocks/search.lua
new file mode 100644
index 00000000..eca17a8f
--- /dev/null
+++ b/src/luarocks/search.lua
@@ -0,0 +1,385 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table; local search = {}
+
+local dir = require("luarocks.dir")
+local path = require("luarocks.path")
+local manif = require("luarocks.manif")
+local vers = require("luarocks.core.vers")
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+local queries = require("luarocks.queries")
+local results = require("luarocks.results")
+
+
+
+
+
+
+
+
+
+
+
+
+function search.store_result(result_tree, result)
+
+ local name = result.name
+ local version = result.version
+
+ if not result_tree[name] then result_tree[name] = {} end
+ if not result_tree[name][version] then result_tree[name][version] = {} end
+ table.insert(result_tree[name][version], {
+ arch = result.arch,
+ repo = result.repo,
+ namespace = result.namespace,
+ })
+end
+
+
+
+
+
+
+
+
+
+
+local function store_if_match(result_tree, result, query)
+
+ if result:satisfies(query) then
+ search.store_result(result_tree, result)
+ end
+end
+
+
+
+
+
+
+
+
+
+
+function search.disk_search(repo, query, result_tree)
+
+ local fs = require("luarocks.fs")
+
+ if not result_tree then
+ result_tree = {}
+ end
+
+ for name in fs.dir(repo) do
+ local pathname = dir.path(repo, name)
+ local rname, rversion, rarch = path.parse_name(name)
+
+ if rname and (pathname:match(".rockspec$") or pathname:match(".rock$")) then
+ local result = results.new(rname, rversion, repo, rarch)
+ store_if_match(result_tree, result, query)
+ elseif fs.is_dir(pathname) then
+ for version in fs.dir(pathname) do
+ if version:match("-%d+$") then
+ local namespace = path.read_namespace(name, version, repo)
+ local result = results.new(name, version, repo, "installed", namespace)
+ store_if_match(result_tree, result, query)
+ end
+ end
+ end
+ end
+ return result_tree
+end
+
+
+
+
+
+
+
+
+
+
+
+local function manifest_search(result_tree, repo, query, lua_version, is_local)
+
+
+ if (not is_local) and query.namespace then
+ repo = repo .. "/manifests/" .. query.namespace
+ end
+
+ local manifest, err, errcode = manif.load_manifest(repo, lua_version, not is_local)
+ if not manifest then
+ return nil, err, errcode
+ end
+ for name, versions in pairs(manifest.repository) do
+ for version, items in pairs(versions) do
+ local namespace = is_local and path.read_namespace(name, version, repo) or query.namespace
+ for _, item in ipairs(items) do
+ local result = results.new(name, version, repo, item.arch, namespace)
+ store_if_match(result_tree, result, query)
+ end
+ end
+ end
+ return true
+end
+
+local function remote_manifest_search(result_tree, repo, query, lua_version)
+ return manifest_search(result_tree, repo, query, lua_version, false)
+end
+
+function search.local_manifest_search(result_tree, repo, query, lua_version)
+ return manifest_search(result_tree, repo, query, lua_version, true)
+end
+
+
+
+
+
+
+
+function search.search_repos(query, lua_version)
+
+ local result_tree = {}
+ local repo = {}
+ for _, repostr in ipairs(cfg.rocks_servers) do
+ if type(repostr) == "string" then
+ repo = { repostr }
+ else
+ repo = repostr
+ end
+ for _, mirror in ipairs(repo) do
+ if not cfg.disabled_servers[mirror] then
+ local protocol, pathname = dir.split_url(mirror)
+ if protocol == "file" then
+ mirror = pathname
+ end
+ local ok, err, errcode = remote_manifest_search(result_tree, mirror, query, lua_version)
+ if errcode == "network" then
+ cfg.disabled_servers[mirror] = true
+ end
+ if ok then
+ break
+ else
+ util.warning("Failed searching manifest: " .. err)
+ if errcode == "downloader" then
+ break
+ end
+ end
+ end
+ end
+ end
+
+ local provided_repo = "provided by VM or rocks_provided"
+ for name, version in pairs(util.get_rocks_provided()) do
+ local result = results.new(name, version, provided_repo, "installed")
+ store_if_match(result_tree, result, query)
+ end
+ return result_tree
+end
+
+
+
+
+
+
+
+local function pick_latest_version(name, versions)
+ assert(not name:match("/"))
+
+ local vtables = {}
+ for v, _ in pairs(versions) do
+ table.insert(vtables, vers.parse_version(v))
+ end
+ table.sort(vtables)
+ local version = vtables[#vtables].string
+ local items = versions[version]
+ if items then
+ local pick = 1
+ for i, item in ipairs(items) do
+ if (item.arch == 'src' and items[pick].arch == 'rockspec') or
+ (item.arch ~= 'src' and item.arch ~= 'rockspec') then
+ pick = i
+ end
+ end
+ return path.make_url(items[pick].repo, name, version, items[pick].arch)
+ end
+ return nil
+end
+
+
+
+
+local function supported_lua_versions(query)
+ local result_tree = {}
+
+ for lua_version in util.lua_versions() do
+ if lua_version ~= cfg.lua_version then
+ util.printout("Checking for Lua " .. lua_version .. "...")
+ if search.search_repos(query, lua_version)[query.name] then
+ table.insert(result_tree, lua_version)
+ end
+ end
+ end
+
+ return result_tree
+end
+
+
+
+
+
+
+function search.find_suitable_rock(query)
+
+ local rocks_provided = util.get_rocks_provided()
+
+ if rocks_provided[query.name] then
+
+ return nil, "Rock " .. query.name .. " " .. rocks_provided[query.name] ..
+ " is already provided by VM or via 'rocks_provided' in the config file.", "provided"
+ end
+
+ local result_tree = search.search_repos(query)
+ local first_rock = next(result_tree)
+ if not first_rock then
+ return nil, "No results matching query were found for Lua " .. cfg.lua_version .. ".", "notfound"
+ elseif next(result_tree, first_rock) then
+
+ return nil, "Several rocks matched query.", "manyfound"
+ else
+ return pick_latest_version(query.name, result_tree[first_rock])
+ end
+end
+
+function search.find_rock_checking_lua_versions(query, check_lua_versions)
+ local url, err, errcode = search.find_suitable_rock(query)
+ if url then
+ return url
+ end
+
+ if errcode == "notfound" then
+ local add
+ if check_lua_versions then
+ util.printout(query.name .. " not found for Lua " .. cfg.lua_version .. ".")
+ util.printout("Checking if available for other Lua versions...")
+
+
+ local lua_versions = supported_lua_versions(query)
+
+ if #lua_versions ~= 0 then
+
+ for i, lua_version in ipairs(lua_versions) do
+ lua_versions[i] = "Lua " .. lua_version
+ end
+
+ local versions_message = "only " .. table.concat(lua_versions, " and ") ..
+ " but not Lua " .. cfg.lua_version .. "."
+
+ if #query.constraints == 0 then
+ add = query.name .. " supports " .. versions_message
+ elseif #query.constraints == 1 and query.constraints[1].op == "==" then
+ local queryversion = tostring(query.constraints[1].version)
+ add = query.name .. " " .. queryversion .. " supports " .. versions_message
+ else
+ add = "Matching " .. query.name .. " versions support " .. versions_message
+ end
+ else
+ add = query.name .. " is not available for any Lua versions."
+ end
+ else
+ add = "To check if it is available for other Lua versions, use --check-lua-versions."
+ end
+ err = err .. "\n" .. add
+ end
+
+ return nil, err
+end
+
+function search.find_src_or_rockspec(name, namespace, version, check_lua_versions)
+ local query = queries.new(name, namespace, version, false, "src|rockspec")
+ local url, err = search.find_rock_checking_lua_versions(query, check_lua_versions)
+ if not url then
+ return nil, "Could not find a result named " .. tostring(query) .. ": " .. err
+ end
+ return url
+end
+
+
+
+
+function search.print_result_tree(result_tree, porcelain)
+
+ if porcelain then
+ for packagestr, versions in util.sortedpairs(result_tree) do
+ for version, repos in util.sortedpairs(versions, vers.compare_versions) do
+ for _, repo in ipairs(repos) do
+ local nrepo = dir.normalize(repo.repo)
+ util.printout(packagestr, version, repo.arch, nrepo, repo.namespace)
+ end
+ end
+ end
+ return
+ end
+
+ for packagestr, versions in util.sortedpairs(result_tree) do
+ local namespaces = {}
+ for version, repos in util.sortedpairs(versions, vers.compare_versions) do
+ for _, repo in ipairs(repos) do
+ local key = repo.namespace or ""
+ local list = namespaces[key] or {}
+ namespaces[key] = list
+
+ repo.repo = dir.normalize(repo.repo)
+ table.insert(list, " " .. version .. " (" .. repo.arch .. ") - " .. path.root_dir(repo.repo))
+ end
+ end
+ for key, list in util.sortedpairs(namespaces) do
+ util.printout(key == "" and packagestr or key .. "/" .. packagestr)
+ for _, line in ipairs(list) do
+ util.printout(line)
+ end
+ util.printout()
+ end
+ end
+end
+
+function search.pick_installed_rock(query, given_tree)
+
+ local result_tree = {}
+ local tree_map = {}
+ local trees = cfg.rocks_trees
+ if given_tree then
+ trees = { given_tree }
+ end
+ for _, tree in ipairs(trees) do
+ local rocks_dir = path.rocks_dir(tree)
+ tree_map[rocks_dir] = tree
+ search.local_manifest_search(result_tree, rocks_dir, query)
+ end
+ if not next(result_tree) then
+ return nil, "cannot find package " .. tostring(query) .. "\nUse 'list' to find installed rocks."
+ end
+
+ if not result_tree[query.name] and next(result_tree, next(result_tree)) then
+ local out = { "multiple installed packages match the name '" .. tostring(query) .. "':\n\n" }
+ for name, _ in util.sortedpairs(result_tree) do
+ table.insert(out, " " .. name .. "\n")
+ end
+ table.insert(out, "\nPlease specify a single rock.\n")
+ return nil, table.concat(out)
+ end
+
+ local repo_url
+
+ local name, versions
+ if result_tree[query.name] then
+ name, versions = query.name, result_tree[query.name]
+ else
+ name, versions = util.sortedpairs(result_tree)()
+ end
+
+ local version, repositories = util.sortedpairs(versions, vers.compare_versions)()
+ for _, rp in ipairs(repositories) do repo_url = rp.repo end
+
+ local repo = tree_map[repo_url]
+ return name, version, repo, repo_url
+end
+
+return search
diff --git a/src/luarocks/signing.lua b/src/luarocks/signing.lua
new file mode 100644
index 00000000..cb91643a
--- /dev/null
+++ b/src/luarocks/signing.lua
@@ -0,0 +1,48 @@
+local signing = {}
+
+local cfg = require("luarocks.core.cfg")
+local fs = require("luarocks.fs")
+
+local function get_gpg()
+ local vars = cfg.variables
+ local gpg = vars.GPG
+ local gpg_ok, err = fs.is_tool_available(gpg, "gpg")
+ if not gpg_ok then
+ return nil, err
+ end
+ return gpg
+end
+
+function signing.signature_url(url)
+ return url .. ".asc"
+end
+
+function signing.sign_file(file)
+ local gpg, err = get_gpg()
+ if not gpg then
+ return nil, err
+ end
+
+ local sigfile = file .. ".asc"
+ if fs.execute(gpg, "--armor", "--output", sigfile, "--detach-sign", file) then
+ return sigfile
+ else
+ return nil, "failed running " .. gpg .. " to sign " .. file
+ end
+end
+
+function signing.verify_signature(file, sigfile)
+ local gpg, err = get_gpg()
+ if not gpg then
+ return nil, err
+ end
+
+ if fs.execute(gpg, "--verify", sigfile, file) then
+ return true
+ else
+ return nil, "GPG returned a verification error"
+ end
+
+end
+
+return signing
diff --git a/src/luarocks/test.lua b/src/luarocks/test.lua
new file mode 100644
index 00000000..01dfae12
--- /dev/null
+++ b/src/luarocks/test.lua
@@ -0,0 +1,110 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pcall = _tl_compat and _tl_compat.pcall or pcall; local table = _tl_compat and _tl_compat.table or table; local test = {}
+
+
+local fetch = require("luarocks.fetch")
+local deps = require("luarocks.deps")
+local util = require("luarocks.util")
+
+
+
+
+
+
+
+local test_types = {
+ "busted",
+ "command",
+}
+
+local test_modules = {}
+local typetomod = {}
+local modtotype = {}
+
+for _, test_type in ipairs(test_types) do
+ local mod
+ if test_type == "command" then
+ mod = require("luarocks.test.command")
+ elseif test_type == "busted" then
+ mod = require("luarocks.test.busted")
+ end
+ table.insert(test_modules, mod)
+ typetomod[test_type] = mod
+ modtotype[mod] = test_type
+end
+
+local function get_test_type(rockspec)
+ if rockspec.test and rockspec.test.type then
+ return rockspec.test.type
+ end
+
+ for _, test_module in ipairs(test_modules) do
+ if test_module.detect_type() then
+ return modtotype[test_module]
+ end
+ end
+
+ return nil, "could not detect test type -- no test suite for " .. rockspec.package .. "?"
+end
+
+
+function test.run_test_suite(rockspec_arg, test_type, args, prepare)
+ local rockspec
+ if type(rockspec_arg) == "string" then
+ local err, errcode
+ rockspec, err, errcode = fetch.load_rockspec(rockspec_arg)
+ if err then
+ return nil, err, errcode
+ end
+ else
+ rockspec = rockspec_arg
+ end
+
+ if not test_type then
+ local err
+ test_type, err = get_test_type(rockspec)
+ if not test_type then
+ return nil, err
+ end
+ end
+
+ local all_deps = {
+ "dependencies",
+ "build_dependencies",
+ "test_dependencies",
+ }
+ for _, dep_kind in ipairs(all_deps) do
+ if (rockspec)[dep_kind] and next((rockspec)[dep_kind]) ~= nil then
+ local _, err, errcode = deps.fulfill_dependencies(rockspec, dep_kind, "all")
+ if err then
+ return nil, err, errcode
+ end
+ end
+ end
+
+ local pok, test_mod = pcall(require, "luarocks.test." .. test_type)
+ if not pok then
+ return nil, "failed loading test execution module luarocks.test." .. test_type
+ end
+
+ if prepare then
+ if test_type == "busted" then
+ return test_mod.run_tests(rockspec.test, { "--version" })
+ else
+ return true
+ end
+ else
+ local flags = rockspec.test and rockspec.test.flags
+ if type(flags) == "table" then
+ util.variable_substitutions(flags, rockspec.variables)
+
+
+ for i = 1, #flags do
+ table.insert(args, i, flags[i])
+ end
+ end
+
+ return test_mod.run_tests(rockspec.test, args)
+ end
+end
+
+return test
diff --git a/src/luarocks/test/busted.lua b/src/luarocks/test/busted.lua
new file mode 100644
index 00000000..bc00b33a
--- /dev/null
+++ b/src/luarocks/test/busted.lua
@@ -0,0 +1,55 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local table = _tl_compat and _tl_compat.table or table; local _tl_table_unpack = unpack or table.unpack
+local busted = {}
+
+
+local fs = require("luarocks.fs")
+local deps = require("luarocks.deps")
+local path = require("luarocks.path")
+local dir = require("luarocks.dir")
+local queries = require("luarocks.queries")
+local install = require("luarocks.cmd.install")
+
+
+
+function busted.detect_type()
+ if fs.exists(".busted") then
+ return true
+ end
+ return false
+end
+
+function busted.run_tests(test, args)
+ if not test then
+ test = {}
+ end
+
+ local ok, bustedver, where = deps.fulfill_dependency(queries.new("busted"), nil, nil, nil, "test_dependencies")
+ if not ok then
+ return nil, bustedver
+ end
+
+ local busted_exe
+ if test.busted_executable then
+ busted_exe = test.busted_executable
+ else
+ busted_exe = dir.path(path.root_dir(where), "bin", "busted")
+
+
+ local busted_bat = dir.path(path.root_dir(where), "bin", "busted.bat")
+
+ if not fs.exists(busted_exe) and not fs.exists(busted_bat) then
+ return nil, "'busted' executable failed to be installed"
+ end
+ end
+
+ local err
+ ok, err = fs.execute(busted_exe, _tl_table_unpack(args))
+ if ok then
+ return true
+ else
+ return nil, err or "test suite failed."
+ end
+end
+
+
+return busted
diff --git a/src/luarocks/test/command.lua b/src/luarocks/test/command.lua
new file mode 100644
index 00000000..41d30378
--- /dev/null
+++ b/src/luarocks/test/command.lua
@@ -0,0 +1,55 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local table = _tl_compat and _tl_compat.table or table; local _tl_table_unpack = unpack or table.unpack
+local command = {}
+
+
+local fs = require("luarocks.fs")
+local cfg = require("luarocks.core.cfg")
+
+
+
+function command.detect_type()
+ if fs.exists("test.lua") then
+ return true
+ end
+ return false
+end
+
+function command.run_tests(test, args)
+ if not test then
+ test = {
+ script = "test.lua",
+ }
+ end
+
+ if not test.script and not test.command then
+ test.script = "test.lua"
+ end
+
+ local ok
+
+ if test.script then
+ local test_script = test.script
+ if not (type(test_script) == "string") then
+ return nil, "Malformed rockspec: 'script' expects a string"
+ end
+ if not fs.exists(test.script) then
+ return nil, "Test script " .. test.script .. " does not exist"
+ end
+ local lua = fs.Q(cfg.variables["LUA"])
+ ok = fs.execute(lua, test.script, _tl_table_unpack(args))
+ elseif test.command then
+ local test_command = test.command
+ if not (type(test_command) == "string") then
+ return nil, "Malformed rockspec: 'command' expects a string"
+ end
+ ok = fs.execute(test.command, _tl_table_unpack(args))
+ end
+
+ if ok then
+ return true
+ else
+ return nil, "tests failed with non-zero exit code"
+ end
+end
+
+return command
diff --git a/src/luarocks/tools/patch.lua b/src/luarocks/tools/patch.lua
new file mode 100644
index 00000000..c9567bc6
--- /dev/null
+++ b/src/luarocks/tools/patch.lua
@@ -0,0 +1,746 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local debug = _tl_compat and _tl_compat.debug or debug; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local math = _tl_compat and _tl_compat.math or math; local os = _tl_compat and _tl_compat.os or os; local pairs = _tl_compat and _tl_compat.pairs or pairs; local pcall = _tl_compat and _tl_compat.pcall or pcall; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+
+
+
+
+
+
+
+
+
+local patch = {Lineends = {}, Hunk = {}, File = {}, Files = {}, }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local fs = require("luarocks.fs")
+
+
+
+
+
+
+
+local debugmode = false
+local function debug(_) end
+local function info(_) end
+local function warning(s) io.stderr:write(s .. '\n') end
+
+
+local function startswith(s, s2)
+ return s:sub(1, #s2) == s2
+end
+
+
+local function endswith(s, s2)
+ return #s >= #s2 and s:sub(#s - #s2 + 1) == s2
+end
+
+
+local function endlstrip(s)
+ return s:gsub('[\r\n]+$', '')
+end
+
+
+local function table_copy(t)
+ local t2 = {}
+ for k, v in pairs(t) do t2[k] = v end
+ return t2
+end
+
+local function exists(filename)
+ local fh = io.open(filename)
+ local result = fh ~= nil
+ if fh then fh:close() end
+ return result
+end
+local function isfile() return true end
+
+local function string_as_file(s)
+ return {
+ at = 0,
+ str = s,
+ len = #s,
+ eof = false,
+ read = function(self, n)
+ if self.eof then return nil end
+ local chunk = self.str:sub(self.at, self.at + n - 1)
+ self.at = self.at + n
+ if self.at > self.len then
+ self.eof = true
+ end
+ return chunk
+ end,
+ close = function(self)
+ self.eof = true
+ end,
+ }
+end
+
+
+
+
+
+
+
+
+
+
+local function file_lines(f)
+ local CHUNK_SIZE = 1024
+ local buffer = ""
+ local pos_beg = 1
+ return function()
+ local pos, chars
+ while 1 do
+ pos, chars = buffer:match('()([\r\n].)', pos_beg)
+ if pos or not f then
+ break
+ elseif f then
+ local chunk = f:read(CHUNK_SIZE)
+ if chunk then
+ buffer = buffer:sub(pos_beg) .. chunk
+ pos_beg = 1
+ else
+ f = nil
+ end
+ end
+ end
+ local posi = math.tointeger(pos)
+ if not posi then
+ posi = #buffer
+ elseif chars == '\r\n' then
+ posi = posi + 1
+ end
+ local line = buffer:sub(pos_beg, posi)
+ pos_beg = posi + 1
+ if #line > 0 then
+ return line
+ end
+ end
+end
+
+local function match_linerange(line)
+ local m1, m2, m3, m4 = line:match("^@@ %-(%d+),(%d+) %+(%d+),(%d+)")
+ if not m1 then m1, m3, m4 = line:match("^@@ %-(%d+) %+(%d+),(%d+)") end
+ if not m1 then m1, m2, m3 = line:match("^@@ %-(%d+),(%d+) %+(%d+)") end
+ if not m1 then m1, m3 = line:match("^@@ %-(%d+) %+(%d+)") end
+ return m1, m2, m3, m4
+end
+
+local function match_epoch(str)
+ return str:match("[^0-9]1969[^0-9]") or str:match("[^0-9]1970[^0-9]")
+end
+
+function patch.read_patch(filename, data)
+
+ local state = 'header'
+
+
+
+
+
+
+ local all_ok = true
+ local lineends = { lf = 0, crlf = 0, cr = 0 }
+ local files = { source = {}, target = {}, epoch = {}, hunks = {}, fileends = {}, hunkends = {} }
+ local nextfileno = 0
+ local nexthunkno = 0
+
+
+
+ local hunkinfo = {
+ startsrc = nil, linessrc = nil, starttgt = nil, linestgt = nil,
+ invalid = false, text = {},
+ }
+ local hunkactual = { linessrc = nil, linestgt = nil }
+
+ info(string.format("reading patch %s", filename))
+
+ local fp
+ if data then
+ fp = string_as_file(data)
+ else
+ fp = filename == '-' and io.stdin or assert(io.open(filename, "rb"))
+ end
+ local lineno = 0
+
+ for line in file_lines(fp) do
+ lineno = lineno + 1
+ if state == 'header' then
+ if startswith(line, "--- ") then
+ state = 'filenames'
+ end
+
+ end
+ if state == 'hunkbody' then
+
+
+ if line:match("^[\r\n]*$") then
+
+ line = " " .. line
+ end
+
+
+ if line:match("^[- +\\]") then
+
+ local he = files.hunkends[nextfileno]
+ if endswith(line, "\r\n") then
+ he.crlf = he.crlf + 1
+ elseif endswith(line, "\n") then
+ he.lf = he.lf + 1
+ elseif endswith(line, "\r") then
+ he.cr = he.cr + 1
+ end
+ if startswith(line, "-") then
+ hunkactual.linessrc = hunkactual.linessrc + 1
+ elseif startswith(line, "+") then
+ hunkactual.linestgt = hunkactual.linestgt + 1
+ elseif startswith(line, "\\") then
+
+ else
+ hunkactual.linessrc = hunkactual.linessrc + 1
+ hunkactual.linestgt = hunkactual.linestgt + 1
+ end
+ table.insert(hunkinfo.text, line)
+
+ else
+ warning(string.format("invalid hunk no.%d at %d for target file %s",
+ nexthunkno, lineno, files.target[nextfileno]))
+
+ table.insert(files.hunks[nextfileno], table_copy(hunkinfo))
+ files.hunks[nextfileno][nexthunkno].invalid = true
+ all_ok = false
+ state = 'hunkskip'
+ end
+
+
+ if hunkactual.linessrc > hunkinfo.linessrc or
+ hunkactual.linestgt > hunkinfo.linestgt then
+
+ warning(string.format("extra hunk no.%d lines at %d for target %s",
+ nexthunkno, lineno, files.target[nextfileno]))
+
+ table.insert(files.hunks[nextfileno], table_copy(hunkinfo))
+ files.hunks[nextfileno][nexthunkno].invalid = true
+ state = 'hunkskip'
+ elseif hunkinfo.linessrc == hunkactual.linessrc and
+ hunkinfo.linestgt == hunkactual.linestgt then
+
+ table.insert(files.hunks[nextfileno], table_copy(hunkinfo))
+ state = 'hunkskip'
+
+
+ local ends = files.hunkends[nextfileno]
+ if (ends.cr ~= 0 and 1 or 0) + (ends.crlf ~= 0 and 1 or 0) +
+ (ends.lf ~= 0 and 1 or 0) > 1 then
+
+ warning(string.format("inconsistent line ends in patch hunks for %s",
+ files.source[nextfileno]))
+ end
+ end
+
+ end
+
+ if state == 'hunkskip' then
+ if match_linerange(line) then
+ state = 'hunkhead'
+ elseif startswith(line, "--- ") then
+ state = 'filenames'
+ if debugmode and #files.source > 0 then
+ debug(string.format("- %2d hunks for %s", #files.hunks[nextfileno],
+ files.source[nextfileno]))
+ end
+ end
+
+ end
+ local advance
+ if state == 'filenames' then
+ if startswith(line, "--- ") then
+ if files.source[nextfileno] then
+ all_ok = false
+ warning(string.format("skipping invalid patch for %s",
+ files.source[nextfileno + 1]))
+ table.remove(files.source, nextfileno + 1)
+
+
+ end
+
+
+
+ local match, rest = line:match("^%-%-%- ([^ \t\r\n]+)(.*)")
+ if not match then
+ all_ok = false
+ warning(string.format("skipping invalid filename at line %d", lineno + 1))
+ state = 'header'
+ else
+ if match_epoch(rest) then
+ files.epoch[nextfileno + 1] = true
+ end
+ table.insert(files.source, match)
+ end
+ elseif not startswith(line, "+++ ") then
+ if files.source[nextfileno] then
+ all_ok = false
+ warning(string.format("skipping invalid patch with no target for %s",
+ files.source[nextfileno + 1]))
+ table.remove(files.source, nextfileno + 1)
+ else
+
+ warning("skipping invalid target patch")
+ end
+ state = 'header'
+ else
+ if files.target[nextfileno] then
+ all_ok = false
+ warning(string.format("skipping invalid patch - double target at line %d",
+ lineno + 1))
+ table.remove(files.source, nextfileno + 1)
+ table.remove(files.target, nextfileno + 1)
+ nextfileno = nextfileno - 1
+
+
+ state = 'header'
+ else
+
+
+
+ local re_filename = "^%+%+%+ ([^ \t\r\n]+)(.*)$"
+ local match, rest = line:match(re_filename)
+ if not match then
+ all_ok = false
+ warning(string.format(
+ "skipping invalid patch - no target filename at line %d",
+ lineno + 1))
+ state = 'header'
+ else
+ table.insert(files.target, match)
+ nextfileno = nextfileno + 1
+ if match_epoch(rest) then
+ files.epoch[nextfileno] = true
+ end
+ nexthunkno = 0
+ table.insert(files.hunks, {})
+ table.insert(files.hunkends, table_copy(lineends))
+ table.insert(files.fileends, table_copy(lineends))
+ state = 'hunkhead'
+ advance = true
+ end
+ end
+ end
+
+ end
+ if not advance and state == 'hunkhead' then
+ local m1, m2, m3, m4 = match_linerange(line)
+ if not m1 then
+ if not files.hunks[nextfileno - 1] then
+ all_ok = false
+ warning(string.format("skipping invalid patch with no hunks for file %s",
+ files.target[nextfileno]))
+ end
+ state = 'header'
+ else
+ hunkinfo.startsrc = math.tointeger(m1)
+ hunkinfo.linessrc = math.tointeger(m2) or 1
+ hunkinfo.starttgt = math.tointeger(m3)
+ hunkinfo.linestgt = math.tointeger(m4) or 1
+ hunkinfo.invalid = false
+ hunkinfo.text = {}
+
+ hunkactual.linessrc = 0
+ hunkactual.linestgt = 0
+
+ state = 'hunkbody'
+ nexthunkno = nexthunkno + 1
+ end
+
+ end
+ end
+ if state ~= 'hunkskip' then
+ warning(string.format("patch file incomplete - %s", filename))
+ all_ok = false
+
+ else
+
+ if debugmode and #files.source > 0 then
+ debug(string.format("- %2d hunks for %s", #files.hunks[nextfileno],
+ files.source[nextfileno]))
+ end
+ end
+
+ local sum = 0; for _, hset in ipairs(files.hunks) do sum = sum + #hset end
+ info(string.format("total files: %d total hunks: %d", #files.source, sum))
+ fp:close()
+ return files, all_ok
+end
+
+local function find_hunk(file, h, hno)
+ for fuzz = 0, 2 do
+ local lineno = h.startsrc
+ for i = 0, #file do
+ local found = true
+ local location = lineno
+ for l, hline in ipairs(h.text) do
+ if l > fuzz then
+
+ if startswith(hline, " ") or startswith(hline, "-") then
+ local line = file[lineno]
+ lineno = lineno + 1
+ if not line or #line == 0 then
+ found = false
+ break
+ end
+ if endlstrip(line) ~= endlstrip(hline:sub(2)) then
+ found = false
+ break
+ end
+ end
+ end
+ end
+ if found then
+ local offset = location - h.startsrc - fuzz
+ if offset ~= 0 then
+ warning(string.format("Hunk %d found at offset %d%s...", hno, offset, fuzz == 0 and "" or string.format(" (fuzz %d)", fuzz)))
+ end
+ h.startsrc = location
+ h.starttgt = h.starttgt + offset
+ for _ = 1, fuzz do
+ table.remove(h.text, 1)
+ table.remove(h.text, #h.text)
+ end
+ return true
+ end
+ lineno = i
+ end
+ end
+ return false
+end
+
+local function load_file(filename)
+ local fp = assert(io.open(filename))
+ local file = {}
+ local readline = file_lines(fp)
+ while true do
+ local line = readline()
+ if not line then break end
+ table.insert(file, line)
+ end
+ fp:close()
+ return file
+end
+
+local function find_hunks(file, hunks)
+ for hno, h in ipairs(hunks) do
+ find_hunk(file, h, hno)
+ end
+end
+
+local function check_patched(file, hunks)
+ local lineno = 1
+ local _, err = pcall(function()
+ if #file == 0 then
+ error('nomatch', 0)
+ end
+ for hno, h in ipairs(hunks) do
+
+ if #file < h.starttgt then
+ error('nomatch', 0)
+ end
+ lineno = h.starttgt
+ for _, hline in ipairs(h.text) do
+
+ if not startswith(hline, "-") and not startswith(hline, "\\") then
+ local line = file[lineno]
+ lineno = lineno + 1
+ if #line == 0 then
+ error('nomatch', 0)
+ end
+ if endlstrip(line) ~= endlstrip(hline:sub(2)) then
+ warning(string.format("file is not patched - failed hunk: %d", hno))
+ error('nomatch', 0)
+ end
+ end
+ end
+ end
+ end)
+
+ return err ~= 'nomatch'
+end
+
+local function patch_hunks(srcname, tgtname, hunks)
+ local src = assert(io.open(srcname, "rb"))
+ local tgt = assert(io.open(tgtname, "wb"))
+
+ local src_readline = file_lines(src)
+
+
+
+
+
+
+
+ local srclineno = 1
+ local lineends = { ['\n'] = 0, ['\r\n'] = 0, ['\r'] = 0 }
+ for hno, h in ipairs(hunks) do
+ debug(string.format("processing hunk %d for file %s", hno, tgtname))
+
+ while srclineno < h.startsrc do
+ local line = src_readline()
+
+ if endswith(line, "\r\n") then
+ lineends["\r\n"] = lineends["\r\n"] + 1
+ elseif endswith(line, "\n") then
+ lineends["\n"] = lineends["\n"] + 1
+ elseif endswith(line, "\r") then
+ lineends["\r"] = lineends["\r"] + 1
+ end
+ tgt:write(line)
+ srclineno = srclineno + 1
+ end
+
+ for _, hline in ipairs(h.text) do
+
+ if startswith(hline, "-") or startswith(hline, "\\") then
+ src_readline()
+ srclineno = srclineno + 1
+ else
+ if not startswith(hline, "+") then
+ src_readline()
+ srclineno = srclineno + 1
+ end
+ local line2write = hline:sub(2)
+
+ local sum = 0
+ for _, v in pairs(lineends) do if v > 0 then sum = sum + 1 end end
+ if sum == 1 then
+ local newline
+ for k, v in pairs(lineends) do if v ~= 0 then newline = k end end
+ tgt:write(endlstrip(line2write) .. newline)
+ else
+ tgt:write(line2write)
+ end
+ end
+ end
+ end
+ for line in src_readline do
+ tgt:write(line)
+ end
+ tgt:close()
+ src:close()
+ return true
+end
+
+local function strip_dirs(filename, strip)
+ if strip == nil then return filename end
+ for _ = 1, strip do
+ filename = filename:gsub("^[^/]*/", "")
+ end
+ return filename
+end
+
+local function write_new_file(filename, hunk)
+ local fh = io.open(filename, "wb")
+ if not fh then return false end
+ for _, hline in ipairs(hunk.text) do
+ local c = hline:sub(1, 1)
+ if c ~= "+" and c ~= "-" and c ~= " " then
+ return false, "malformed patch"
+ end
+ fh:write(hline:sub(2))
+ end
+ fh:close()
+ return true
+end
+
+local function patch_file(source, target, epoch, hunks, strip, create_delete)
+ local create_file = false
+ if create_delete then
+ local is_src_epoch = epoch and #hunks == 1 and hunks[1].startsrc == 0 and hunks[1].linessrc == 0
+ if is_src_epoch or source == "/dev/null" then
+ info(string.format("will create %s", target))
+ create_file = true
+ end
+ end
+ if create_file then
+ return write_new_file(fs.absolute_name(strip_dirs(target, strip)), hunks[1])
+ end
+ source = strip_dirs(source, strip)
+ local f2patch = source
+ if not exists(f2patch) then
+ f2patch = strip_dirs(target, strip)
+ f2patch = fs.absolute_name(f2patch)
+ if not exists(f2patch) then
+ warning(string.format("source/target file does not exist\n--- %s\n+++ %s",
+ source, f2patch))
+ return false
+ end
+ end
+
+ if not isfile() then
+ warning(string.format("not a file - %s", f2patch))
+ return false
+ end
+
+ source = f2patch
+
+
+ local file = load_file(source)
+ local hunkno = 1
+ local hunk = hunks[hunkno]
+ local hunkfind = {}
+ local validhunks = 0
+ local canpatch = false
+ local hunklineno
+ if not file then
+ return nil, "failed reading file " .. source
+ end
+
+ if create_delete then
+ if epoch and #hunks == 1 and hunks[1].starttgt == 0 and hunks[1].linestgt == 0 then
+ local ok = os.remove(source)
+ if not ok then
+ return false
+ end
+ info(string.format("successfully removed %s", source))
+ return true
+ end
+ end
+
+ find_hunks(file, hunks)
+
+ local function process_line(line, lineno)
+ if not hunk or lineno < hunk.startsrc then
+ return false
+ end
+ if lineno == hunk.startsrc then
+ hunkfind = {}
+ for _, x in ipairs(hunk.text) do
+ if x:sub(1, 1) == ' ' or x:sub(1, 1) == '-' then
+ hunkfind[#hunkfind + 1] = endlstrip(x:sub(2))
+ end
+ end
+ hunklineno = 1
+
+
+ end
+
+ if lineno < hunk.startsrc + #hunkfind - 1 then
+ if endlstrip(line) == hunkfind[hunklineno] then
+ hunklineno = hunklineno + 1
+ else
+ debug(string.format("hunk no.%d doesn't match source file %s",
+ hunkno, source))
+
+ hunkno = hunkno + 1
+ if hunkno <= #hunks then
+ hunk = hunks[hunkno]
+ return false
+ else
+ return true
+ end
+ end
+ end
+
+ if lineno == hunk.startsrc + #hunkfind - 1 then
+ debug(string.format("file %s hunk no.%d -- is ready to be patched",
+ source, hunkno))
+ hunkno = hunkno + 1
+ validhunks = validhunks + 1
+ if hunkno <= #hunks then
+ hunk = hunks[hunkno]
+ else
+ if validhunks == #hunks then
+
+ canpatch = true
+ return true
+ end
+ end
+ end
+ return false
+ end
+
+ local done = false
+ for lineno, line in ipairs(file) do
+ done = process_line(line, lineno)
+ if done then
+ break
+ end
+ end
+ if not done then
+ if hunkno <= #hunks and not create_file then
+ warning(string.format("premature end of source file %s at hunk %d",
+ source, hunkno))
+ return false
+ end
+ end
+ if validhunks < #hunks then
+ if check_patched(file, hunks) then
+ warning(string.format("already patched %s", source))
+ elseif not create_file then
+ warning(string.format("source file is different - %s", source))
+ return false
+ end
+ end
+ if not canpatch then
+ return true
+ end
+ local backupname = source .. ".orig"
+ if exists(backupname) then
+ warning(string.format("can't backup original file to %s - aborting",
+ backupname))
+ return false
+ end
+ local ok = os.rename(source, backupname)
+ if not ok then
+ warning(string.format("failed backing up %s when patching", source))
+ return false
+ end
+ patch_hunks(backupname, source, hunks)
+ info(string.format("successfully patched %s", source))
+ os.remove(backupname)
+ return true
+end
+
+function patch.apply_patch(the_patch, strip, create_delete)
+ local all_ok = true
+ local total = #the_patch.source
+ for fileno, source in ipairs(the_patch.source) do
+ local target = the_patch.target[fileno]
+ local hunks = the_patch.hunks[fileno]
+ local epoch = the_patch.epoch[fileno]
+ info(string.format("processing %d/%d:\t %s", fileno, total, source))
+ local ok = patch_file(source, target, epoch, hunks, strip, create_delete)
+ all_ok = all_ok and ok
+ end
+
+ return all_ok
+end
+
+return patch
diff --git a/src/luarocks/tools/tar.lua b/src/luarocks/tools/tar.lua
new file mode 100644
index 00000000..25e1d0ec
--- /dev/null
+++ b/src/luarocks/tools/tar.lua
@@ -0,0 +1,210 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local math = _tl_compat and _tl_compat.math or math; local string = _tl_compat and _tl_compat.string or string
+
+local tar = {Header = {}, }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+local fun = require("luarocks.fun")
+
+
+
+local blocksize = 512
+
+local function get_typeflag(flag)
+ if flag == "0" or flag == "\0" then return "file"
+ elseif flag == "1" then return "link"
+ elseif flag == "2" then return "symlink"
+ elseif flag == "3" then return "character"
+ elseif flag == "4" then return "block"
+ elseif flag == "5" then return "directory"
+ elseif flag == "6" then return "fifo"
+ elseif flag == "7" then return "contiguous"
+ elseif flag == "x" then return "next file"
+ elseif flag == "g" then return "global extended header"
+ elseif flag == "L" then return "long name"
+ elseif flag == "K" then return "long link name"
+ end
+ return "unknown"
+end
+
+local function octal_to_number(octal)
+ local exp = 0
+ local number = 0
+ octal = octal:gsub("%s", "")
+ for i = #octal, 1, -1 do
+ local digit = math.tointeger(octal:sub(i, i))
+ if not digit then
+ break
+ end
+ number = number + (digit * math.tointeger(8 ^ exp))
+ exp = exp + 1
+ end
+ return number
+end
+
+local function checksum_header(block)
+ local sum = 256
+
+ if block:byte(1) == 0 then
+ return 0
+ end
+
+ for i = 1, 148 do
+ local b = block:byte(i) or 0
+ sum = sum + b
+ end
+ for i = 157, 500 do
+ local b = block:byte(i) or 0
+ sum = sum + b
+ end
+
+ return sum
+end
+
+local function nullterm(s)
+ return s:match("^[^%z]*")
+end
+
+local function read_header_block(block)
+ local header = {}
+ header.name = nullterm(block:sub(1, 100))
+ header.mode = nullterm(block:sub(101, 108)):gsub(" ", "")
+ header.uid = octal_to_number(nullterm(block:sub(109, 116)))
+ header.gid = octal_to_number(nullterm(block:sub(117, 124)))
+ header.size = octal_to_number(nullterm(block:sub(125, 136)))
+ header.mtime = octal_to_number(nullterm(block:sub(137, 148)))
+ header.chksum = octal_to_number(nullterm(block:sub(149, 156)))
+ header.typeflag = get_typeflag(block:sub(157, 157))
+ header.linkname = nullterm(block:sub(158, 257))
+ header.magic = block:sub(258, 263)
+ header.version = block:sub(264, 265)
+ header.uname = nullterm(block:sub(266, 297))
+ header.gname = nullterm(block:sub(298, 329))
+ header.devmajor = octal_to_number(nullterm(block:sub(330, 337)))
+ header.devminor = octal_to_number(nullterm(block:sub(338, 345)))
+ header.prefix = block:sub(346, 500)
+
+
+
+
+
+
+
+ if header.typeflag == "unknown" then
+ if checksum_header(block) ~= header.chksum then
+ return false, "Failed header checksum"
+ end
+ end
+ return header
+end
+
+function tar.untar(filename, destdir)
+
+ local tar_handle = io.open(filename, "rb")
+ if not tar_handle then return nil, "Error opening file " .. filename end
+
+ local long_name, long_link_name
+ local ok, err
+ local make_dir = fun.memoize(fs.make_dir)
+ while true do
+ local block
+ repeat
+ block = tar_handle:read(blocksize)
+ until (not block) or block:byte(1) > 0
+ if not block then break end
+ if #block < blocksize then
+ ok, err = nil, "Invalid block size -- corrupted file?"
+ break
+ end
+
+ local headerp
+ headerp, err = read_header_block(block)
+ if not headerp then
+ ok = false
+ break
+ end
+ local header = headerp
+ local file_data = ""
+ if header.size > 0 then
+ local nread = math.ceil(header.size / blocksize) * blocksize
+ file_data = tar_handle:read(header.size)
+ if nread > header.size then
+ tar_handle:seek("cur", nread - header.size)
+ end
+ end
+
+ if header.typeflag == "long name" then
+ long_name = nullterm(file_data)
+ elseif header.typeflag == "long link name" then
+ long_link_name = nullterm(file_data)
+ else
+ if long_name then
+ header.name = long_name
+ long_name = nil
+ end
+ if long_link_name then
+ header.name = long_link_name
+ long_link_name = nil
+ end
+ end
+ local pathname = dir.path(destdir, header.name)
+ pathname = fs.absolute_name(pathname)
+ if header.typeflag == "directory" then
+ ok, err = make_dir(pathname)
+ if not ok then
+ break
+ end
+ elseif header.typeflag == "file" then
+ local dirname = dir.dir_name(pathname)
+ if dirname ~= "" then
+ ok, err = make_dir(dirname)
+ if not ok then
+ break
+ end
+ end
+ local file_handle
+ file_handle, err = io.open(pathname, "wb")
+ if not file_handle then
+ ok = nil
+ break
+ end
+ file_handle:write(file_data)
+ file_handle:close()
+ fs.set_time(pathname, header.mtime)
+ if header.mode:match("[75]") then
+ fs.set_permissions(pathname, "exec", "all")
+ else
+ fs.set_permissions(pathname, "read", "all")
+ end
+ end
+
+
+
+
+
+
+ end
+ tar_handle:close()
+ return ok, err
+end
+
+return tar
diff --git a/src/luarocks/tools/zip.lua b/src/luarocks/tools/zip.lua
new file mode 100644
index 00000000..b54d9e86
--- /dev/null
+++ b/src/luarocks/tools/zip.lua
@@ -0,0 +1,575 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local math = _tl_compat and _tl_compat.math or math; local os = _tl_compat and _tl_compat.os or os; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table; local _tl_table_pack = table.pack or function(...) return { n = select("#", ...), ... } end
+
+
+local zip = {ZipHandle = {}, LocalFileHeader = {}, Zip = {}, }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local zlib = require("zlib")
+local fs = require("luarocks.fs")
+local fun = require("luarocks.fun")
+local dir = require("luarocks.dir")
+
+
+
+
+
+local function shr(n, m)
+ return math.floor(n / 2 ^ m)
+end
+
+local function shl(n, m)
+ return (n * 2 ^ m)
+end
+
+local function lowbits(n, m)
+ return (n % 2 ^ m)
+end
+
+local function mode_to_windowbits(mode)
+ if mode == "gzip" then
+ return 31
+ elseif mode == "zlib" then
+ return 0
+ elseif mode == "raw" then
+ return -15
+ end
+end
+
+
+
+local zlib_compress
+local zlib_uncompress
+local zlib_crc32
+if zlib._VERSION:match("^lua%-zlib") then
+ function zlib_compress(data, mode)
+ return (zlib.deflate(6, mode_to_windowbits(mode))(data, "finish"))
+ end
+
+ function zlib_uncompress(data, mode)
+ return (zlib.inflate(mode_to_windowbits(mode))(data))
+ end
+
+ function zlib_crc32(data)
+ return zlib.crc32()(data)
+ end
+elseif zlib._VERSION:match("^lzlib") then
+ function zlib_compress(data, mode)
+ return zlib.compress(data, -1, nil, mode_to_windowbits(mode))
+ end
+
+ function zlib_uncompress(data, mode)
+ return zlib.decompress(data, mode_to_windowbits(mode))
+ end
+
+ function zlib_crc32(data)
+ return zlib.crc32(zlib.crc32(), data)
+ end
+else
+ error("unknown zlib library", 0)
+end
+
+local function number_to_lestring(number, nbytes)
+ local out = {}
+ for _ = 1, nbytes do
+ local byte = number % 256
+ table.insert(out, string.char(byte))
+ number = (number - byte) / 256
+ end
+ return table.concat(out)
+end
+
+local function lestring_to_number(str)
+ local n = 0
+ local bytes = { string.byte(str, 1, #str) }
+ for b = 1, #str do
+ n = n + shl(bytes[b], (b - 1) * 8)
+ end
+ return math.floor(n)
+end
+
+local LOCAL_FILE_HEADER_SIGNATURE = number_to_lestring(0x04034b50, 4)
+local DATA_DESCRIPTOR_SIGNATURE = number_to_lestring(0x08074b50, 4)
+local CENTRAL_DIRECTORY_SIGNATURE = number_to_lestring(0x02014b50, 4)
+local END_OF_CENTRAL_DIR_SIGNATURE = number_to_lestring(0x06054b50, 4)
+
+
+
+
+
+local function zipwriter_open_new_file_in_zip(self, filename)
+ if self.in_open_file then
+ self:close_file_in_zip()
+ return nil
+ end
+ local lfh = {}
+ self.local_file_header = lfh
+ lfh.last_mod_file_time = 0
+ lfh.last_mod_file_date = 0
+ lfh.file_name_length = #filename
+ lfh.extra_field_length = 0
+ lfh.file_name = filename:gsub("\\", "/")
+ lfh.external_attr = shl(493, 16)
+ self.in_open_file = true
+ return true
+end
+
+
+
+
+
+local function zipwriter_write_file_in_zip(self, data)
+ if not self.in_open_file then
+ return nil
+ end
+ local lfh = self.local_file_header
+ local compressed = zlib_compress(data, "raw")
+ lfh.crc32 = zlib_crc32(data)
+ lfh.compressed_size = #compressed
+ lfh.uncompressed_size = #data
+ self.data = compressed
+ return true
+end
+
+
+
+
+local function zipwriter_close_file_in_zip(self)
+ local zh = self.ZipHandle
+
+ if not self.in_open_file then
+ return nil
+ end
+
+
+ local lfh = self.local_file_header
+ lfh.offset = zh:seek()
+ zh:write(LOCAL_FILE_HEADER_SIGNATURE)
+ zh:write(number_to_lestring(20, 2))
+ zh:write(number_to_lestring(4, 2))
+ zh:write(number_to_lestring(8, 2))
+ zh:write(number_to_lestring(lfh.last_mod_file_time, 2))
+ zh:write(number_to_lestring(lfh.last_mod_file_date, 2))
+ zh:write(number_to_lestring(lfh.crc32, 4))
+ zh:write(number_to_lestring(lfh.compressed_size, 4))
+ zh:write(number_to_lestring(lfh.uncompressed_size, 4))
+ zh:write(number_to_lestring(lfh.file_name_length, 2))
+ zh:write(number_to_lestring(lfh.extra_field_length, 2))
+ zh:write(lfh.file_name)
+
+
+ zh:write(self.data)
+
+
+ zh:write(DATA_DESCRIPTOR_SIGNATURE)
+ zh:write(number_to_lestring(lfh.crc32, 4))
+ zh:write(number_to_lestring(lfh.compressed_size, 4))
+ zh:write(number_to_lestring(lfh.uncompressed_size, 4))
+
+ table.insert(self.files, lfh)
+ self.in_open_file = false
+
+ return true
+end
+
+
+
+local function zipwriter_add(self, file)
+ local fin
+ local ok, err = self:open_new_file_in_zip(file)
+ if not ok then
+ err = "error in opening " .. file .. " in zipfile"
+ else
+ fin = io.open(fs.absolute_name(file), "rb")
+ if not fin then
+ ok = false
+ err = "error opening " .. file .. " for reading"
+ end
+ end
+ if ok then
+ local data = fin:read("*a")
+ if not data then
+ err = "error reading " .. file
+ ok = false
+ else
+ ok = self:write_file_in_zip(data)
+ if not ok then
+ err = "error in writing " .. file .. " in the zipfile"
+ end
+ end
+ end
+ if fin then
+ fin:close()
+ end
+ if ok then
+ ok = self:close_file_in_zip()
+ if not ok then
+ err = "error in writing " .. file .. " in the zipfile"
+ end
+ end
+ return ok == true, err
+end
+
+
+
+
+local function zipwriter_close(self)
+ local zh = self.ZipHandle
+
+ local central_directory_offset = zh:seek()
+
+ local size_of_central_directory = 0
+
+ for _, lfh in ipairs(self.files) do
+ zh:write(CENTRAL_DIRECTORY_SIGNATURE)
+ zh:write(number_to_lestring(3, 2))
+ zh:write(number_to_lestring(20, 2))
+ zh:write(number_to_lestring(0, 2))
+ zh:write(number_to_lestring(8, 2))
+ zh:write(number_to_lestring(lfh.last_mod_file_time, 2))
+ zh:write(number_to_lestring(lfh.last_mod_file_date, 2))
+ zh:write(number_to_lestring(lfh.crc32, 4))
+ zh:write(number_to_lestring(lfh.compressed_size, 4))
+ zh:write(number_to_lestring(lfh.uncompressed_size, 4))
+ zh:write(number_to_lestring(lfh.file_name_length, 2))
+ zh:write(number_to_lestring(lfh.extra_field_length, 2))
+ zh:write(number_to_lestring(0, 2))
+ zh:write(number_to_lestring(0, 2))
+ zh:write(number_to_lestring(0, 2))
+ zh:write(number_to_lestring(lfh.external_attr, 4))
+ zh:write(number_to_lestring(lfh.offset, 4))
+ zh:write(lfh.file_name)
+ size_of_central_directory = size_of_central_directory + 46 + lfh.file_name_length
+ end
+
+
+ zh:write(END_OF_CENTRAL_DIR_SIGNATURE)
+ zh:write(number_to_lestring(0, 2))
+ zh:write(number_to_lestring(0, 2))
+ zh:write(number_to_lestring(#self.files, 2))
+ zh:write(number_to_lestring(#self.files, 2))
+ zh:write(number_to_lestring(size_of_central_directory, 4))
+ zh:write(number_to_lestring(central_directory_offset, 4))
+ zh:write(number_to_lestring(0, 2))
+ zh:close()
+
+ return true
+end
+
+
+
+
+function zip.new_zipwriter(name)
+
+ local zw = {}
+
+ zw.ZipHandle = io.open(fs.absolute_name(name), "wb")
+ if not zw.ZipHandle then
+ return nil
+ end
+ zw.files = {}
+ zw.in_open_file = false
+
+ zw.add = zipwriter_add
+ zw.close = zipwriter_close
+ zw.open_new_file_in_zip = zipwriter_open_new_file_in_zip
+ zw.write_file_in_zip = zipwriter_write_file_in_zip
+ zw.close_file_in_zip = zipwriter_close_file_in_zip
+
+ return zw
+end
+
+
+
+
+
+
+
+function zip.zip(zipfile, ...)
+ local zw = zip.new_zipwriter(zipfile)
+ if not zw then
+ return nil, "error opening " .. zipfile
+ end
+
+ local args = _tl_table_pack(...)
+ local ok, err
+ for i = 1, args.n do
+ local file = args[i]
+ if fs.is_dir(file) then
+ for _, entry in ipairs(fs.find(file)) do
+ local fullname = dir.path(file, entry)
+ if fs.is_file(fullname) then
+ ok, err = zw:add(fullname)
+ if not ok then break end
+ end
+ end
+ else
+ ok, err = zw:add(file)
+ if not ok then break end
+ end
+ end
+
+ zw:close()
+ return ok, err
+end
+
+
+local function ziptime_to_luatime(ztime, zdate)
+ local date = {
+ year = shr(zdate, 9) + 1980,
+ month = shr(lowbits(zdate, 9), 5),
+ day = lowbits(zdate, 5),
+ hour = shr(ztime, 11),
+ min = shr(lowbits(ztime, 11), 5),
+ sec = lowbits(ztime, 5) * 2,
+ }
+
+ if date.month == 0 then date.month = 1 end
+ if date.day == 0 then date.day = 1 end
+
+ return date
+end
+
+local function read_file_in_zip(zh, cdr)
+ local sig = zh:read(4)
+ if sig ~= LOCAL_FILE_HEADER_SIGNATURE then
+ return nil, "failed reading Local File Header signature"
+ end
+
+
+
+ zh:seek("cur", 22)
+ local file_name_length = lestring_to_number(zh:read(2))
+ local extra_field_length = lestring_to_number(zh:read(2))
+ zh:read(file_name_length)
+ zh:read(extra_field_length)
+
+ local data = zh:read(cdr.compressed_size)
+
+ local uncompressed
+ if cdr.compression_method == 8 then
+ uncompressed = zlib_uncompress(data, "raw")
+ elseif cdr.compression_method == 0 then
+ uncompressed = data
+ else
+ return nil, "unknown compression method " .. cdr.compression_method
+ end
+
+ if #uncompressed ~= cdr.uncompressed_size then
+ return nil, "uncompressed size doesn't match"
+ end
+ if cdr.crc32 ~= zlib_crc32(uncompressed) then
+ return nil, "crc32 failed (expected " .. cdr.crc32 .. ") - data: " .. uncompressed
+ end
+
+ return uncompressed
+end
+
+local function process_end_of_central_dir(zh)
+ local at, errend = zh:seek("end", -22)
+ if not at then
+ return nil, errend
+ end
+
+ while true do
+ local sig = zh:read(4)
+ if sig == END_OF_CENTRAL_DIR_SIGNATURE then
+ break
+ end
+ at = at - 1
+ local at1 = zh:seek("set", at)
+ if at1 ~= at then
+ return nil, "Could not find End of Central Directory signature"
+ end
+ end
+
+
+
+
+
+ zh:seek("cur", 6)
+
+ local central_directory_entries = lestring_to_number(zh:read(2))
+
+
+ zh:seek("cur", 4)
+
+ local central_directory_offset = lestring_to_number(zh:read(4))
+
+ return central_directory_entries, central_directory_offset
+end
+
+local function process_central_dir(zh, cd_entries)
+
+ local files = {}
+
+ for i = 1, cd_entries do
+ local sig = zh:read(4)
+ if sig ~= CENTRAL_DIRECTORY_SIGNATURE then
+ return nil, "failed reading Central Directory signature"
+ end
+
+ local cdr = {}
+ files[i] = cdr
+
+ cdr.version_made_by = lestring_to_number(zh:read(2))
+ cdr.version_needed = lestring_to_number(zh:read(2))
+ cdr.bitflag = lestring_to_number(zh:read(2))
+ cdr.compression_method = lestring_to_number(zh:read(2))
+ cdr.last_mod_file_time = lestring_to_number(zh:read(2))
+ cdr.last_mod_file_date = lestring_to_number(zh:read(2))
+ cdr.last_mod_luatime = ziptime_to_luatime(cdr.last_mod_file_time, cdr.last_mod_file_date)
+ cdr.crc32 = lestring_to_number(zh:read(4))
+ cdr.compressed_size = lestring_to_number(zh:read(4))
+ cdr.uncompressed_size = lestring_to_number(zh:read(4))
+ cdr.file_name_length = lestring_to_number(zh:read(2))
+ cdr.extra_field_length = lestring_to_number(zh:read(2))
+ cdr.file_comment_length = lestring_to_number(zh:read(2))
+ cdr.disk_number_start = lestring_to_number(zh:read(2))
+ cdr.internal_attr = lestring_to_number(zh:read(2))
+ cdr.external_attr = lestring_to_number(zh:read(4))
+ cdr.offset = lestring_to_number(zh:read(4))
+ cdr.file_name = zh:read(cdr.file_name_length)
+ cdr.extra_field = zh:read(cdr.extra_field_length)
+ cdr.file_comment = zh:read(cdr.file_comment_length)
+ end
+ return files
+end
+
+
+
+
+
+function zip.unzip(zipfile)
+ zipfile = fs.absolute_name(zipfile)
+ local zh, erropen = io.open(zipfile, "rb")
+ if not zh then
+ return nil, erropen
+ end
+
+ local cd_entries, cd_offset = process_end_of_central_dir(zh)
+ if type(cd_offset) == "string" then
+ return nil, cd_offset
+ end
+
+ local okseek, errseek = zh:seek("set", cd_offset)
+ if not okseek then
+ return nil, errseek
+ end
+
+ local files, errproc = process_central_dir(zh, cd_entries)
+ if not files then
+ return nil, errproc
+ end
+
+ for _, cdr in ipairs(files) do
+ local file = cdr.file_name
+ if file:sub(#file) == "/" then
+ local okmake, errmake = fs.make_dir(dir.path(fs.current_dir(), file))
+ if not okmake then
+ return nil, errmake
+ end
+ else
+ local base = dir.dir_name(file)
+ if base ~= "" then
+ base = dir.path(fs.current_dir(), base)
+ if not fs.is_dir(base) then
+ local okmake, errmake = fs.make_dir(base)
+ if not okmake then
+ return nil, errmake
+ end
+ end
+ end
+
+ local okseek2, errseek2 = zh:seek("set", cdr.offset)
+ if not okseek2 then
+ return nil, errseek2
+ end
+
+ local contents, err = read_file_in_zip(zh, cdr)
+ if not contents then
+ return nil, err
+ end
+ local pathname = dir.path(fs.current_dir(), file)
+ local wf, erropen2 = io.open(pathname, "wb")
+ if not wf then
+ zh:close()
+ return nil, erropen2
+ end
+ wf:write(contents)
+ wf:close()
+
+ if cdr.external_attr > 0 then
+ fs.set_permissions(pathname, "exec", "all")
+ else
+ fs.set_permissions(pathname, "read", "all")
+ end
+ fs.set_time(pathname, cdr.last_mod_luatime)
+ end
+ end
+ zh:close()
+ return true
+end
+
+function zip.gzip(input_filename, output_filename)
+
+ if not output_filename then
+ output_filename = input_filename .. ".gz"
+ end
+
+ local fn = fun.partial(fun.flip(zlib_compress), "gzip")
+ return fs.filter_file(fn, input_filename, output_filename)
+end
+
+function zip.gunzip(input_filename, output_filename)
+
+ if not output_filename then
+ output_filename = input_filename:gsub("%.gz$", "")
+ end
+
+ local fn = fun.partial(fun.flip(zlib_uncompress), "gzip")
+ return fs.filter_file(fn, input_filename, output_filename)
+end
+
+return zip
diff --git a/src/luarocks/type/manifest.lua b/src/luarocks/type/manifest.lua
new file mode 100644
index 00000000..3f3be728
--- /dev/null
+++ b/src/luarocks/type/manifest.lua
@@ -0,0 +1,92 @@
+local type_manifest = {}
+
+
+
+
+local type_check = require("luarocks.type_check")
+
+local manifest_formats = type_check.declare_schemas({
+ ["3.0"] = {
+ fields = {
+ repository = {
+ _mandatory = true,
+
+ _any = {
+
+ _any = {
+
+ _any = {
+ fields = {
+ arch = { _type = "string", _mandatory = true },
+ modules = { _any = { _type = "string" } },
+ commands = { _any = { _type = "string" } },
+ dependencies = { _any = { _type = "string" } },
+
+ },
+ },
+ },
+ },
+ },
+ modules = {
+ _mandatory = true,
+
+ _any = {
+
+ _any = { _type = "string" },
+ },
+ },
+ commands = {
+ _mandatory = true,
+
+ _any = {
+
+ _any = { _type = "string" },
+ },
+ },
+ dependencies = {
+
+ _any = {
+
+ _any = {
+
+ _any = {
+ fields = {
+ name = { _type = "string" },
+ namespace = { _type = "string" },
+ constraints = {
+ _any = {
+ fields = {
+ no_upgrade = { _type = "boolean" },
+ op = { _type = "string" },
+ version = {
+ fields = {
+ string = { _type = "string" },
+ },
+ _any = { _type = "number" },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+})
+
+
+
+
+
+
+
+function type_manifest.check(manifest, globals)
+ local format = manifest_formats["3.0"]
+ local ok, err = type_check.check_undeclared_globals(globals, format)
+ if not ok then return nil, err end
+ return type_check.type_check_table("3.0", manifest, format, "")
+end
+
+return type_manifest
diff --git a/src/luarocks/type/rockspec.lua b/src/luarocks/type/rockspec.lua
new file mode 100644
index 00000000..cd4044f6
--- /dev/null
+++ b/src/luarocks/type/rockspec.lua
@@ -0,0 +1,261 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local type_rockspec = {}
+
+
+
+
+
+
+
+
+local type_check = require("luarocks.type_check")
+
+
+
+type_rockspec.rockspec_format = "3.0"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local rockspec_formats, versions = type_check.declare_schemas({
+ ["1.0"] = {
+ fields = {
+ rockspec_format = { _type = "string" },
+ package = { _type = "string", _mandatory = true },
+ version = { _type = "string", _pattern = "[%w.]+-[%d]+", _mandatory = true },
+ description = {
+ fields = {
+ summary = { _type = "string" },
+ detailed = { _type = "string" },
+ homepage = { _type = "string" },
+ license = { _type = "string" },
+ maintainer = { _type = "string" },
+ },
+ },
+ dependencies = {
+ fields = {
+ platforms = type_check.MAGIC_PLATFORMS,
+ },
+ _any = {
+ _type = "string",
+ _name = "a valid dependency string",
+ _pattern = "%s*([a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)",
+ },
+ },
+ supported_platforms = {
+ _any = { _type = "string" },
+ },
+ external_dependencies = {
+ fields = {
+ platforms = type_check.MAGIC_PLATFORMS,
+ },
+ _any = {
+ fields = {
+ program = { _type = "string" },
+ header = { _type = "string" },
+ library = { _type = "string" },
+ },
+ },
+ },
+ source = {
+ _mandatory = true,
+ fields = {
+ platforms = type_check.MAGIC_PLATFORMS,
+ url = { _type = "string", _mandatory = true },
+ md5 = { _type = "string" },
+ file = { _type = "string" },
+ dir = { _type = "string" },
+ tag = { _type = "string" },
+ branch = { _type = "string" },
+ module = { _type = "string" },
+ cvs_tag = { _type = "string" },
+ cvs_module = { _type = "string" },
+ },
+ },
+ build = {
+ fields = {
+ platforms = type_check.MAGIC_PLATFORMS,
+ type = { _type = "string" },
+ install = {
+ fields = {
+ lua = {
+ _more = true,
+ },
+ lib = {
+ _more = true,
+ },
+ conf = {
+ _more = true,
+ },
+ bin = {
+ _more = true,
+ },
+ },
+ },
+ copy_directories = {
+ _any = { _type = "string" },
+ },
+ },
+ _more = true,
+ _mandatory = true,
+ },
+ hooks = {
+ fields = {
+ platforms = type_check.MAGIC_PLATFORMS,
+ post_install = { _type = "string" },
+ },
+ },
+ },
+ },
+
+ ["1.1"] = {
+ fields = {
+ deploy = {
+ fields = {
+ wrap_bin_scripts = { _type = "boolean" },
+ },
+ },
+ },
+ },
+
+ ["3.0"] = {
+ fields = {
+ description = {
+ fields = {
+ labels = {
+ _any = { _type = "string" },
+ },
+ issues_url = { _type = "string" },
+ },
+ },
+ dependencies = {
+ _any = {
+ _pattern = "%s*([a-zA-Z0-9%.%-%_]*/?[a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)",
+ },
+ },
+ build_dependencies = {
+ fields = {
+ platforms = type_check.MAGIC_PLATFORMS,
+ },
+ _any = {
+ _type = "string",
+ _name = "a valid dependency string",
+ _pattern = "%s*([a-zA-Z0-9%.%-%_]*/?[a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)",
+ },
+ },
+ test_dependencies = {
+ fields = {
+ platforms = type_check.MAGIC_PLATFORMS,
+ },
+ _any = {
+ _type = "string",
+ _name = "a valid dependency string",
+ _pattern = "%s*([a-zA-Z0-9%.%-%_]*/?[a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)",
+ },
+ },
+ build = {
+ _mandatory = false,
+ },
+ test = {
+ fields = {
+ platforms = type_check.MAGIC_PLATFORMS,
+ type = { _type = "string" },
+ },
+ _more = true,
+ },
+ },
+ },
+})
+
+
+
+
+
+
+
+
+
+type_rockspec.order = {
+ "rockspec_format",
+ "package",
+ "version",
+ "source",
+ "description",
+ "supported_platforms",
+ "dependencies",
+ "build_dependencies",
+ "external_dependencies",
+ "build",
+ "test_dependencies",
+ "test",
+ "hooks",
+ sub_orders = {
+ ["source"] = { "url", "tag", "branch", "md5" },
+ ["description"] = { "summary", "detailed", "homepage", "license" },
+ ["build"] = { "type", "modules", "copy_directories", "platforms" },
+ ["test"] = { "type" },
+ },
+}
+
+local function check_rockspec_using_version(rockspec, globals, version)
+ local schema = rockspec_formats[version]
+ if not schema then
+ return nil, "unknown rockspec format " .. version
+ end
+ local ok, err = type_check.check_undeclared_globals(globals, schema)
+ if ok then
+ ok, err = type_check.type_check_table(version, rockspec, schema, "")
+ end
+ if ok then
+ return true
+ else
+ return nil, err
+ end
+end
+
+
+
+
+
+
+
+function type_rockspec.check(rockspec, globals)
+
+ local version = rockspec.rockspec_format or "1.0"
+ local ok, err = check_rockspec_using_version(rockspec, globals, version)
+ if ok then
+ return true
+ end
+
+
+
+
+ local found = false
+ for _, v in ipairs(versions) do
+ if not found then
+ if v == version then
+ found = true
+ end
+ else
+ local v_ok = check_rockspec_using_version(rockspec, globals, v)
+ if v_ok then
+ return nil, err .. " (using rockspec format " .. version .. " -- " ..
+ [[adding 'rockspec_format = "]] .. v .. [["' to the rockspec ]] ..
+ [[will fix this)]]
+ end
+ end
+ end
+
+ return nil, err .. " (using rockspec format " .. version .. ")"
+end
+
+return type_rockspec
diff --git a/src/luarocks/type_check.lua b/src/luarocks/type_check.lua
new file mode 100644
index 00000000..23305baf
--- /dev/null
+++ b/src/luarocks/type_check.lua
@@ -0,0 +1,237 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+local type_check = {TableSchema = {}, }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local cfg = require("luarocks.core.cfg")
+local fun = require("luarocks.fun")
+local util = require("luarocks.util")
+local vers = require("luarocks.core.vers")
+
+
+
+
+
+
+type_check.MAGIC_PLATFORMS = {}
+
+do
+ local function fill_in_version(tbl, version)
+
+ if not tbl.fields then
+ return
+ end
+
+ for _, v in pairs(tbl.fields) do
+ if type(v) == "table" then
+ if v._version == nil then
+ v._version = version
+ end
+ fill_in_version(v)
+ end
+ end
+ end
+
+ local function expand_magic_platforms(tbl)
+ for k, v in pairs(tbl.fields) do
+ if v == type_check.MAGIC_PLATFORMS then
+ tbl.fields[k] = {
+ _any = util.deep_copy(tbl),
+ }
+ tbl.fields[k]._any.fields[k] = nil
+ expand_magic_platforms(v)
+ end
+ end
+ end
+
+
+
+
+
+
+ function type_check.declare_schemas(inputs)
+ local schemas = {}
+ local parent_version
+
+ local versions = fun.reverse_in(fun.sort_in(util.keys(inputs), vers.compare_versions))
+
+ for _, version in ipairs(versions) do
+ local schema = inputs[version]
+ if parent_version then
+ local copy = util.deep_copy(schemas[parent_version])
+ util.deep_merge(copy, schema)
+ schema = copy
+ end
+ fill_in_version(schema, version)
+ expand_magic_platforms(schema)
+ parent_version = version
+ schemas[version] = schema
+ end
+
+ return schemas, versions
+ end
+end
+
+
+
+local function check_version(version, typetbl, context)
+ local typetbl_version = typetbl._version or "1.0"
+ if vers.compare_versions(typetbl_version, version) then
+ if context == "" then
+ return nil, "Invalid rockspec_format version number in rockspec? Please fix rockspec accordingly."
+ else
+ return nil, context .. " is not supported in rockspec format " .. version .. " (requires version " .. typetbl_version .. "), please fix the rockspec_format field accordingly."
+ end
+ end
+ return true
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local function type_check_item(version, item, typetbl, context)
+
+ if typetbl._version and typetbl._version ~= "1.0" then
+ local ok, err = check_version(version, typetbl, context)
+ if not ok then
+ return nil, err
+ end
+ end
+
+ local expected_type = typetbl._type or "table"
+
+ if expected_type == "number" then
+ if not tonumber(item) then
+ return nil, "Type mismatch on field " .. context .. ": expected a number"
+ end
+ elseif expected_type == "string" then
+ if not (type(item) == "string") then
+ return nil, "Type mismatch on field " .. context .. ": expected a string, got " .. type(item)
+ end
+ local pattern = typetbl._pattern
+ if pattern then
+ if not item:match("^" .. pattern .. "$") then
+ local what = typetbl._name or ("'" .. pattern .. "'")
+ return nil, "Type mismatch on field " .. context .. ": invalid value '" .. item .. "' does not match " .. what
+ end
+ end
+ elseif expected_type == "table" then
+ if not (type(item) == "table") then
+ return nil, "Type mismatch on field " .. context .. ": expected a table"
+ else
+ return type_check.type_check_table(version, item, typetbl, context)
+ end
+ elseif type(item) ~= expected_type then
+ return nil, "Type mismatch on field " .. context .. ": expected " .. expected_type
+ end
+ return true
+end
+
+local function mkfield(context, field)
+ if context == "" then
+ return tostring(field)
+ elseif type(field) == "string" then
+ return context .. "." .. field
+ else
+ return context .. "[" .. tostring(field) .. "]"
+ end
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+function type_check.type_check_table(version, tbl, typetbl, context)
+
+ local ok, err = check_version(version, typetbl, context)
+ if not ok then
+ return nil, err
+ end
+
+ if not typetbl.fields then
+
+ return true
+ end
+
+ for k, v in pairs(tbl) do
+ local t = typetbl.fields[tostring(k)] or typetbl._any
+ if t then
+ ok, err = type_check_item(version, v, t, mkfield(context, k))
+ if not ok then return nil, err end
+ elseif typetbl._more then
+
+ else
+ if not cfg.accept_unknown_fields then
+ return nil, "Unknown field " .. tostring(k)
+ end
+ end
+ end
+
+ for k, v in pairs(typetbl.fields) do
+ if k:sub(1, 1) ~= "_" and v._mandatory then
+ if not tbl[k] then
+ return nil, "Mandatory field " .. mkfield(context, k) .. " is missing."
+ end
+ end
+ end
+ return true
+end
+
+function type_check.check_undeclared_globals(globals, typetbl)
+ local undeclared = {}
+ for glob, _ in pairs(globals) do
+ if not (typetbl.fields[glob] or typetbl.fields["MUST_" .. glob]) then
+ table.insert(undeclared, glob)
+ end
+ end
+ if #undeclared == 1 then
+ return nil, "Unknown variable: " .. undeclared[1]
+ elseif #undeclared > 1 then
+ return nil, "Unknown variables: " .. table.concat(undeclared, ", ")
+ end
+ return true
+end
+
+return type_check
diff --git a/src/luarocks/upload/api.lua b/src/luarocks/upload/api.lua
new file mode 100644
index 00000000..ba2de943
--- /dev/null
+++ b/src/luarocks/upload/api.lua
@@ -0,0 +1,300 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local os = _tl_compat and _tl_compat.os or os; local package = _tl_compat and _tl_compat.package or package; local pairs = _tl_compat and _tl_compat.pairs or pairs; local pcall = _tl_compat and _tl_compat.pcall or pcall; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table; local api = {Configuration = {}, Api = {}, }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local cfg = require("luarocks.core.cfg")
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+local util = require("luarocks.util")
+local persist = require("luarocks.persist")
+local multipart = require("luarocks.upload.multipart")
+local json = require("luarocks.vendor.dkjson")
+local dir_sep = package.config:sub(1, 1)
+
+
+local Api = api.Api
+
+
+
+
+
+
+local function upload_config_file()
+ if not cfg.config_files.user.file then
+ return nil
+ end
+ return (cfg.config_files.user.file:gsub("[\\/][^\\/]+$", dir_sep .. "upload_config.lua"))
+end
+
+function api.Api:load_config()
+ local upload_conf = upload_config_file()
+ if not upload_conf then return nil end
+ local config = persist.load_into_table(upload_conf)
+ return config
+end
+
+function api.Api:save_config()
+
+ local res, errraw = self:raw_method("status")
+ if not res then
+ return nil, errraw
+ end
+ local reserrors = res.errors
+ if type(reserrors) == "table" then
+ return nil, ("Server error: " .. tostring(reserrors[1]))
+ end
+ local upload_conf = upload_config_file()
+ if not upload_conf then return nil end
+ local ok, errmake = fs.make_dir(dir.dir_name(upload_conf))
+ if not ok then
+ return nil, errmake
+ end
+ persist.save_from_table(upload_conf, self.config)
+ fs.set_permissions(upload_conf, "read", "user")
+ return true
+end
+
+function api.Api:check_version()
+ if not self._server_tool_version then
+ local tool_version = cfg.upload.tool_version
+ local res, err = self:request(tostring(self.config.server) .. "/api/tool_version", {
+ current = tool_version,
+ })
+ if not res then
+ return nil, err
+ end
+ if not res.version then
+ return nil, "failed to fetch tool version"
+ end
+ self._server_tool_version = tostring(res.version)
+ if res.force_update then
+ return nil, "Your upload client is too out of date to continue, please upgrade LuaRocks."
+ end
+ if res.version ~= tool_version then
+ util.warning("your LuaRocks is out of date, consider upgrading.")
+ end
+ end
+ return true
+end
+
+function api.Api:method(path, ...)
+ local res, err = self:raw_method(path, ...)
+ if not res then
+ return nil, err
+ end
+ local reserrors = res.errors
+ if type(reserrors) == "table" then
+ if reserrors[1] == "Invalid key" then
+ return nil, reserrors[1] .. " (use the --api-key flag to change)"
+ end
+ local msg = table.concat(reserrors, ", ")
+ return nil, "API Failed: " .. msg
+ end
+ return res
+end
+
+function api.Api:raw_method(path, ...)
+ self:check_version()
+ local url = tostring(self.config.server) .. "/api/" .. tostring(cfg.upload.api_version) .. "/" .. tostring(self.config.key) .. "/" .. path
+ return self:request(url, ...)
+end
+
+local function encode_query_string(t, sep)
+ if sep == nil then
+ sep = "&"
+ end
+ local i = 0
+ local buf = {}
+ for k, v in pairs(t) do
+ local ks, vs
+ local vf = v
+ if type(vf) == "table" then
+ ks, vs = k, vf:content()
+ else
+ ks, vs = k, vf
+ end
+ buf[i + 1] = multipart.url_escape(ks)
+ buf[i + 2] = "="
+ buf[i + 3] = multipart.url_escape(vs)
+ buf[i + 4] = sep
+ i = i + 4
+ end
+ buf[i] = nil
+ return table.concat(buf)
+end
+
+local function redact_api_url(url)
+ local urls = tostring(url)
+ return (urls:gsub(".*/api/[^/]+/[^/]+", "")) or ""
+end
+
+local ltn12_ok, ltn12 = pcall(require, "ltn12")
+if not ltn12_ok then
+
+ api.Api.request = function(self, url, params, post_params)
+ local vars = cfg.variables
+
+ if fs.which_tool("downloader") == "wget" then
+ local curl_ok, err = fs.is_tool_available(vars.CURL, "curl")
+ if not curl_ok then
+ return nil, err
+ end
+ end
+
+ if not self.config.key then
+ return nil, "Must have API key before performing any actions."
+ end
+ if params and next(params) then
+ url = url .. ("?" .. encode_query_string(params))
+ end
+ local method = "GET"
+ local out
+ local tmpfile = fs.tmpname()
+ if post_params then
+ method = "POST"
+ local curl_cmd = vars.CURL .. " " .. vars.CURLNOCERTFLAG .. " -f -L --silent --user-agent \"" .. cfg.user_agent .. " via curl\" "
+ for k, v in pairs(post_params) do
+ local var
+ if type(v) == "table" then
+ var = "@" .. v.fname
+ else
+ var = v
+ end
+ curl_cmd = curl_cmd .. "--form \"" .. k .. "=" .. var .. "\" "
+ end
+ if cfg.connection_timeout and cfg.connection_timeout > 0 then
+ curl_cmd = curl_cmd .. "--connect-timeout " .. tonumber(cfg.connection_timeout) .. " "
+ end
+ local ok = fs.execute_string(curl_cmd .. fs.Q(url) .. " -o " .. fs.Q(tmpfile))
+ if not ok then
+ return nil, "API failure: " .. redact_api_url(url)
+ end
+ else
+ local name, err = fs.download(url, tmpfile)
+ if not name then
+ return nil, "API failure: " .. tostring(err) .. " - " .. redact_api_url(url)
+ end
+ end
+
+ local tmpfd = io.open(tmpfile)
+ if not tmpfd then
+ os.remove(tmpfile)
+ return nil, "API failure reading temporary file - " .. redact_api_url(url)
+ end
+ out = tmpfd:read("*a")
+ tmpfd:close()
+ os.remove(tmpfile)
+
+ if self.debug then
+ util.printout("[" .. tostring(method) .. " via curl] " .. redact_api_url(url) .. " ... ")
+ end
+
+ return json.decode(out)
+ end
+
+else
+
+ local warned_luasec = false
+
+ api.Api.request = function(self, url, params, post_params)
+ local server = tostring(self.config.server)
+
+ local http_ok, http
+ local via = "luasocket"
+ if server:match("^https://") then
+ http_ok, http = pcall(require, "ssl.https")
+ if http_ok then
+ via = "luasec"
+ else
+ if not warned_luasec then
+ util.printerr("LuaSec is not available; using plain HTTP. Install 'luasec' to enable HTTPS.")
+ warned_luasec = true
+ end
+ http_ok, http = pcall(require, "socket.http")
+ url = url:gsub("^https", "http")
+ via = "luasocket"
+ end
+ else
+ http_ok, http = pcall(require, "socket.http")
+ end
+ if not http_ok then
+ return nil, "Failed loading socket library!"
+ end
+
+ if not self.config.key then
+ return nil, "Must have API key before performing any actions."
+ end
+ local body
+ local headers = {}
+ if params and next(params) then
+ url = url .. ("?" .. encode_query_string(params))
+ end
+ if post_params then
+ local boundary
+ body, boundary = multipart.encode(post_params)
+ headers["Content-length"] = tostring(#body)
+ headers["Content-type"] = "multipart/form-data; boundary=" .. tostring(boundary)
+ end
+ local method = post_params and "POST" or "GET"
+ if self.debug then
+ util.printout("[" .. tostring(method) .. " via " .. via .. "] " .. redact_api_url(url) .. " ... ")
+ end
+ local out = {}
+ local _, status = http.request({
+ url = url,
+ headers = headers,
+ method = method,
+ sink = ltn12.sink.table(out),
+ source = body and ltn12.source.string(body),
+ })
+ if self.debug then
+ util.printout(tostring(status))
+ end
+ local pok, ret = pcall(json.decode, table.concat(out))
+ if pok and ret then
+ return ret
+ end
+ return nil, "API returned " .. tostring(status) .. " - " .. redact_api_url(url)
+ end
+
+end
+
+function api.new(args)
+ local self = {}
+ setmetatable(self, { __index = Api })
+ self.config = self:load_config() or {}
+ self.config.server = args.server or self.config.server or cfg.upload.server
+ self.config.version = self.config.version or cfg.upload.version
+ self.config.key = args.temp_key or args.api_key or self.config.key
+ self.debug = args.debug
+ if not self.config.key then
+ return nil, "You need an API key to upload rocks.\n" ..
+ "Navigate to " .. self.config.server .. "/settings to get a key\n" ..
+ "and then pass it through the --api-key= flag."
+ end
+ if args.api_key then
+ local ok, err = self:save_config()
+ if not ok then
+ return nil, err
+ end
+ end
+ return self
+end
+
+return api
diff --git a/src/luarocks/upload/multipart.lua b/src/luarocks/upload/multipart.lua
new file mode 100644
index 00000000..cf68bdef
--- /dev/null
+++ b/src/luarocks/upload/multipart.lua
@@ -0,0 +1,117 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local math = _tl_compat and _tl_compat.math or math; local pairs = _tl_compat and _tl_compat.pairs or pairs; local pcall = _tl_compat and _tl_compat.pcall or pcall; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table; local _tl_table_unpack = unpack or table.unpack
+local multipart = {File = {}, }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+local File = multipart.File
+
+
+function multipart.url_escape(s)
+ return (string.gsub(s, "([^A-Za-z0-9_])", function(c)
+ return string.format("%%%02x", string.byte(c))
+ end))
+end
+
+function multipart.File:mime()
+ if not self.mimetype then
+ local mimetypes_ok, mimetypes = pcall(require, "mimetypes")
+ if mimetypes_ok then
+ self.mimetype = mimetypes.guess(self.fname)
+ end
+ self.mimetype = self.mimetype or "application/octet-stream"
+ end
+ return self.mimetype
+end
+
+function multipart.File:content()
+ local fd = io.open(self.fname, "rb")
+ if not fd then
+ return nil, "Failed to open file: " .. self.fname
+ end
+ local data = fd:read("*a")
+ fd:close()
+ return data
+end
+
+local function rand_string(len)
+ local shuffled = {}
+ for i = 1, len do
+ local r = math.random(97, 122)
+ if math.random() >= 0.5 then
+ r = r - 32
+ end
+ shuffled[i] = r
+ end
+ return string.char(_tl_table_unpack(shuffled))
+end
+
+
+
+
+
+
+
+
+
+function multipart.encode(params)
+ local tuples = {}
+ for k, v in pairs(params) do
+ if type(k) == "string" then
+ table.insert(tuples, { k, v })
+ end
+ end
+ local chunks = {}
+ for _, tuple in ipairs(tuples) do
+ local k, v = _tl_table_unpack(tuple)
+ k = multipart.url_escape(k)
+ local buffer = { 'Content-Disposition: form-data; name="' .. k .. '"' }
+ local content
+ if type(v) == "table" then
+ buffer[1] = buffer[1] .. ('; filename="' .. v.fname:gsub(".*[/\\]", "") .. '"')
+ table.insert(buffer, "Content-type: " .. v:mime())
+ content = v:content()
+ else
+ content = v
+ end
+ table.insert(buffer, "")
+ table.insert(buffer, content)
+ table.insert(chunks, table.concat(buffer, "\r\n"))
+ end
+ local boundary
+ while not boundary do
+ boundary = "Boundary" .. rand_string(16)
+ for _, chunk in ipairs(chunks) do
+ if chunk:find(boundary) then
+ boundary = nil
+ break
+ end
+ end
+ end
+ local inner = "\r\n--" .. boundary .. "\r\n"
+ return table.concat({ "--", boundary, "\r\n",
+table.concat(chunks, inner),
+"\r\n", "--", boundary, "--", "\r\n", }), boundary
+end
+
+function multipart.new_file(fname, mime)
+ local self = {}
+
+ setmetatable(self, { __index = File })
+
+ self.fname = fname
+ self.mimetype = mime
+ return self
+end
+
+return multipart
diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua
new file mode 100644
index 00000000..7035f305
--- /dev/null
+++ b/src/luarocks/util.lua
@@ -0,0 +1,611 @@
+local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local debug = _tl_compat and _tl_compat.debug or debug; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local os = _tl_compat and _tl_compat.os or os; local package = _tl_compat and _tl_compat.package or package; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
+
+
+
+
+
+local core = require("luarocks.core.util")
+local cfg = require("luarocks.core.cfg")
+
+
+
+
+local util = {Fn = {}, }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+util.cleanup_path = core.cleanup_path
+util.split_string = core.split_string
+util.sortedpairs = core.sortedpairs
+util.deep_merge = core.deep_merge
+util.deep_merge_under = core.deep_merge_under
+util.popen_read = core.popen_read
+util.show_table = core.show_table
+util.printerr = core.printerr
+util.warning = core.warning
+util.keys = core.keys
+util.matchquote = core.matchquote
+
+
+
+
+
+
+
+local scheduled_functions = {}
+
+
+
+
+
+
+
+
+function util.schedule_function(f, ...)
+ local item = { fn = f, args = _tl_table_pack(...) }
+ table.insert(scheduled_functions, item)
+ return item
+end
+
+
+
+
+
+function util.remove_scheduled_function(item)
+ for k, v in ipairs(scheduled_functions) do
+ if v == item then
+ table.remove(scheduled_functions, k)
+ return
+ end
+ end
+end
+
+
+
+
+
+
+function util.run_scheduled_functions()
+ local fs = require("luarocks.fs")
+ if fs.change_dir_to_root then
+ fs.change_dir_to_root()
+ end
+ for i = #scheduled_functions, 1, -1 do
+ local item = scheduled_functions[i]
+ item.fn(_tl_table_unpack(item.args, 1, item.args.n))
+ end
+end
+
+local var_format_pattern = "%$%((%a[%a%d_]+)%)"
+
+
+
+
+
+
+
+
+
+
+
+function util.warn_if_not_used(var_defs, needed_set, msg)
+ local seen = {}
+ for _, val in pairs(var_defs) do
+ for used in val:gmatch(var_format_pattern) do
+ seen[used] = true
+ end
+ end
+ for var, _ in pairs(needed_set) do
+ if not seen[var] then
+ util.warning(msg:format(var))
+ end
+ end
+end
+
+
+
+
+local function warn_failed_matches(line)
+ local any_failed = false
+ if line:match(var_format_pattern) then
+ for unmatched in line:gmatch(var_format_pattern) do
+ util.warning("unmatched variable " .. unmatched)
+ any_failed = true
+ end
+ end
+ return any_failed
+end
+
+
+
+
+
+
+
+
+
+function util.variable_substitutions(tbl, vars)
+
+ local updated = {}
+ for k, v in pairs(tbl) do
+ if type(v) == "string" then
+ updated[k] = string.gsub(v, var_format_pattern, vars)
+ if warn_failed_matches(updated[k]) then
+ updated[k] = updated[k]:gsub(var_format_pattern, "")
+ end
+ end
+ end
+ for k, v in pairs(updated) do
+ tbl[k] = v
+ end
+end
+
+function util.lua_versions(sort)
+ local versions = { "5.1", "5.2", "5.3", "5.4" }
+ local i = 0
+ if sort == "descending" then
+ i = #versions + 1
+ return function()
+ i = i - 1
+ return versions[i]
+ end
+ else
+ return function()
+ i = i + 1
+ return versions[i]
+ end
+ end
+end
+
+function util.lua_path_variables()
+ local lpath_var = "LUA_PATH"
+ local lcpath_var = "LUA_CPATH"
+
+ local lv = cfg.lua_version:gsub("%.", "_")
+ if lv ~= "5_1" then
+ if os.getenv("LUA_PATH_" .. lv) then
+ lpath_var = "LUA_PATH_" .. lv
+ end
+ if os.getenv("LUA_CPATH_" .. lv) then
+ lcpath_var = "LUA_CPATH_" .. lv
+ end
+ end
+ return lpath_var, lcpath_var
+end
+
+function util.starts_with(s, prefix)
+ return s:sub(1, #prefix) == prefix
+end
+
+
+function util.printout(...)
+ io.stdout:write(table.concat({ ... }, "\t"))
+ io.stdout:write("\n")
+end
+
+function util.title(msg, porcelain, underline)
+ if porcelain then return end
+ util.printout()
+ util.printout(msg)
+ util.printout((underline or "-"):rep(#msg))
+ util.printout()
+end
+
+function util.this_program(default)
+ local i = 1
+ local last, cur = default, default
+ while i do
+ local dbg = debug and debug.getinfo(i, "S")
+ if not dbg then break end
+ last = cur
+ cur = dbg.source
+ i = i + 1
+ end
+ local prog = last:sub(1, 1) == "@" and last:sub(2) or last
+
+
+ local lrdir, binpath = prog:match("^(.*)/lib/luarocks/rocks%-[0-9.]*/[^/]+/[^/]+(/bin/[^/]+)$")
+ if lrdir then
+
+ return lrdir .. binpath
+ end
+
+ return prog
+end
+
+function util.format_rock_name(name, namespace, version)
+ return (namespace and namespace .. "/" or "") .. name .. (version and " " .. version or "")
+end
+
+function util.deps_mode_option(parser, program)
+
+ parser:option("--deps-mode", "How to handle dependencies. Four modes are supported:\n" ..
+ "* all - use all trees from the rocks_trees list for finding dependencies\n" ..
+ "* one - use only the current tree (possibly set with --tree)\n" ..
+ "* order - use trees based on order (use the current tree and all " ..
+ "trees below it on the rocks_trees list)\n" ..
+ "* none - ignore dependencies altogether.\n" ..
+ "The default mode may be set with the deps_mode entry in the configuration file.\n" ..
+ 'The current default is "' .. cfg.deps_mode .. '".\n' ..
+ "Type '" .. util.this_program(program or "luarocks") .. "' with no " ..
+ "arguments to see your list of rocks trees."):
+ argname(""):
+ choices({ "all", "one", "order", "none" })
+ parser:flag("--nodeps"):hidden(true)
+end
+
+function util.see_help(command, program)
+ return "See '" .. util.this_program(program or "luarocks") .. ' help' .. (command and " " .. command or "") .. "'."
+end
+
+function util.see_also(text)
+ local see_also = "See also:\n"
+ if text then
+ see_also = see_also .. text .. "\n"
+ end
+ return see_also .. " '" .. util.this_program("luarocks") .. " help' for general options and configuration."
+end
+
+function util.announce_install(rockspec)
+ local path = require("luarocks.path")
+
+ local suffix = ""
+ if rockspec.description and rockspec.description.license then
+ suffix = " (license: " .. rockspec.description.license .. ")"
+ end
+
+ util.printout(rockspec.name .. " " .. rockspec.version .. " is now installed in " .. path.root_dir(cfg.root_dir) .. suffix)
+ util.printout()
+end
+
+
+
+
+
+
+
+local function collect_rockspecs(versions, paths, unnamed_paths, subdir)
+ local fs = require("luarocks.fs")
+ local dir = require("luarocks.dir")
+ local path = require("luarocks.path")
+ local vers = require("luarocks.core.vers")
+ if fs.is_dir(subdir) then
+ for file in fs.dir(subdir) do
+ file = dir.path(subdir, file)
+
+ if file:match("rockspec$") and fs.is_file(file) then
+ local rock, version = path.parse_name(file)
+
+ if rock then
+ if not versions[rock] or vers.compare_versions(version, versions[rock]) then
+ versions[rock] = version
+ paths[rock] = file
+ end
+ else
+ table.insert(unnamed_paths, file)
+ end
+ end
+ end
+ end
+end
+
+
+
+function util.get_default_rockspec()
+
+ local versions = {}
+ local paths = {}
+ local unnamed_paths = {}
+
+ collect_rockspecs(versions, paths, unnamed_paths, ".")
+ collect_rockspecs(versions, paths, unnamed_paths, "rockspec")
+ collect_rockspecs(versions, paths, unnamed_paths, "rockspecs")
+
+ if #unnamed_paths > 0 then
+
+
+ if #unnamed_paths > 1 then
+ return nil, "Please specify which rockspec file to use."
+ else
+ return unnamed_paths[1]
+ end
+ else
+ local fs = require("luarocks.fs")
+ local dir = require("luarocks.dir")
+ local basename = dir.base_name(fs.current_dir())
+
+ if paths[basename] then
+ return paths[basename]
+ end
+
+ local rock = next(versions)
+
+ if rock then
+
+ if next(versions, rock) then
+ return nil, "Please specify which rockspec file to use."
+ else
+ return paths[rock]
+ end
+ else
+ return nil, "Argument missing: please specify a rockspec to use on current directory."
+ end
+ end
+end
+
+
+
+
+function util.LQ(s)
+ return ("%q"):format(s)
+end
+
+
+
+
+function util.split_namespace(ns_name)
+ local p1, p2 = ns_name:match("^([^/]+)/([^/]+)$")
+ if p1 then
+ return p2, p1
+ end
+ return ns_name
+end
+
+
+function util.namespaced_name_action(args, target, ns_name)
+
+ if not ns_name then
+ return
+ end
+
+ if ns_name:match("%.rockspec$") or ns_name:match("%.rock$") then
+ args[target] = ns_name
+ else
+ local name, namespace = util.split_namespace(ns_name)
+ args[target] = name:lower()
+ if namespace then
+ args.namespace = namespace:lower()
+ end
+ end
+end
+
+function util.deep_copy(tbl)
+ local copy = {}
+ for k, v in pairs(tbl) do
+ if type(v) == "table" then
+ copy[k] = util.deep_copy(v)
+ else
+ copy[k] = v
+ end
+ end
+ return copy
+end
+
+
+
+
+function util.exists(file)
+ local fd, _, code = io.open(file, "r")
+ if code == 13 then
+
+
+ return true
+ end
+ if fd then
+ fd:close()
+ return true
+ end
+ return false
+end
+
+function util.lua_is_wrapper(interp)
+ local fd, err = io.open(interp, "r")
+ if not fd then
+ return nil, err
+ end
+ local data
+ data, err = fd:read(1000)
+ fd:close()
+ if not data then
+ return nil, err
+ end
+ return not not data:match("LUAROCKS_SYSCONFDIR")
+end
+
+do
+ local function Q(pathname)
+ if pathname:match("^.:") then
+ return pathname:sub(1, 2) .. '"' .. pathname:sub(3) .. '"'
+ end
+ return '"' .. pathname .. '"'
+ end
+
+ function util.check_lua_version(lua, luaver)
+ if not util.exists(lua) then
+ return nil
+ end
+ local lv = util.popen_read(Q(lua) .. ' -e "io.write(_VERSION:sub(5))"')
+ if lv == "" then
+ return nil
+ end
+ if luaver and luaver ~= lv then
+ return nil
+ end
+ return lv
+ end
+
+ function util.get_luajit_version()
+ if cfg.cache.luajit_version_checked then
+ return cfg.cache.luajit_version
+ end
+ cfg.cache.luajit_version_checked = true
+
+ if not cfg.variables.LUA then
+ return nil
+ end
+
+ local ljv
+ if cfg.lua_version == "5.1" then
+
+ ljv = util.popen_read(Q(cfg.variables.LUA) .. ' -e "io.write(tostring(jit and jit.version:gsub([[^%S+ (%S+).*]], [[%1]])))"')
+ if ljv == "nil" then
+ ljv = nil
+ end
+ end
+ cfg.cache.luajit_version = ljv
+ return ljv
+ end
+
+ local find_lua_bindir
+ do
+ local exe_suffix = (package.config:sub(1, 1) == "\\" and ".exe" or "")
+
+ local function insert_lua_variants(names, luaver)
+ local variants = {
+ "lua" .. luaver .. exe_suffix,
+ "lua" .. luaver:gsub("%.", "") .. exe_suffix,
+ "lua-" .. luaver .. exe_suffix,
+ "lua-" .. luaver:gsub("%.", "") .. exe_suffix,
+ }
+ for _, name in ipairs(variants) do
+ table.insert(names, name)
+ end
+ end
+
+ find_lua_bindir = function(prefix, luaver, verbose)
+ local names = {}
+ if luaver then
+ insert_lua_variants(names, luaver)
+ else
+ for v in util.lua_versions("descending") do
+ insert_lua_variants(names, v)
+ end
+ end
+ if luaver == "5.1" or not luaver then
+ table.insert(names, "luajit" .. exe_suffix)
+ end
+ table.insert(names, "lua" .. exe_suffix)
+
+ local tried = {}
+ local dir_sep = package.config:sub(1, 1)
+ for _, d in ipairs({ prefix .. dir_sep .. "bin", prefix }) do
+ for _, name in ipairs(names) do
+ local lua = d .. dir_sep .. name
+ local is_wrapper, err = util.lua_is_wrapper(lua)
+ if is_wrapper == false then
+ local lv = util.check_lua_version(lua, luaver)
+ if lv then
+ return lua, d, lv
+ end
+ elseif is_wrapper == true or err == nil then
+ table.insert(tried, lua)
+ else
+ table.insert(tried, string.format("%-13s (%s)", lua, err))
+ end
+ end
+ end
+ local interp = luaver and
+ ("Lua " .. luaver .. " interpreter") or
+ "Lua interpreter"
+ return nil, interp .. " not found at " .. prefix .. "\n" ..
+ (verbose and "Tried:\t" .. table.concat(tried, "\n\t") or "")
+ end
+ end
+
+ function util.find_lua(prefix, luaver, verbose)
+ local lua, bindir
+ lua, bindir, luaver = find_lua_bindir(prefix, luaver, verbose)
+ if not lua then
+ return nil, bindir
+ end
+
+ return {
+ lua_version = luaver,
+ lua = lua,
+ lua_dir = prefix,
+ lua_bindir = bindir,
+ }
+ end
+end
+
+
+
+
+
+
+
+
+
+function util.get_rocks_provided(rockspec)
+
+ if not rockspec and cfg.cache.rocks_provided then
+ return cfg.cache.rocks_provided
+ end
+
+ local rocks_provided = {}
+
+ local lv = cfg.lua_version
+
+ rocks_provided["lua"] = lv .. "-1"
+
+ if lv == "5.2" then
+ rocks_provided["bit32"] = lv .. "-1"
+ end
+
+ if lv == "5.3" or lv == "5.4" then
+ rocks_provided["utf8"] = lv .. "-1"
+ end
+
+ if lv == "5.1" then
+ local ljv = util.get_luajit_version()
+ if ljv then
+ rocks_provided["luabitop"] = ljv .. "-1"
+ if (not rockspec) or rockspec:format_is_at_least("3.0") then
+ rocks_provided["luajit"] = ljv .. "-1"
+ end
+ end
+ end
+
+ if cfg.rocks_provided then
+ util.deep_merge_under(rocks_provided, cfg.rocks_provided)
+ end
+
+ if not rockspec then
+ cfg.cache.rocks_provided = rocks_provided
+ end
+
+ return rocks_provided
+end
+
+function util.remove_doc_dir(name, version)
+ local path = require("luarocks.path")
+ local fs = require("luarocks.fs")
+ local dir = require("luarocks.dir")
+
+ local install_dir = path.install_dir(name, version)
+ for _, f in ipairs(fs.list_dir(install_dir)) do
+ local doc_dirs = { "doc", "docs" }
+ for _, d in ipairs(doc_dirs) do
+ if f == d then
+ fs.delete(dir.path(install_dir, f))
+ end
+ end
+ end
+end
+
+return util
--
cgit v1.2.3-55-g6feb