summaryrefslogtreecommitdiff
path: root/src/luarocks/fetch
diff options
context:
space:
mode:
Diffstat (limited to 'src/luarocks/fetch')
-rw-r--r--src/luarocks/fetch/cvs.lua55
-rw-r--r--src/luarocks/fetch/git.lua165
-rw-r--r--src/luarocks/fetch/git_file.lua19
-rw-r--r--src/luarocks/fetch/git_http.lua26
-rw-r--r--src/luarocks/fetch/git_https.lua7
-rw-r--r--src/luarocks/fetch/git_ssh.lua32
-rw-r--r--src/luarocks/fetch/hg.lua65
-rw-r--r--src/luarocks/fetch/hg_http.lua24
-rw-r--r--src/luarocks/fetch/hg_https.lua8
-rw-r--r--src/luarocks/fetch/hg_ssh.lua8
-rw-r--r--src/luarocks/fetch/sscm.lua44
-rw-r--r--src/luarocks/fetch/svn.lua64
12 files changed, 517 insertions, 0 deletions
diff --git a/src/luarocks/fetch/cvs.lua b/src/luarocks/fetch/cvs.lua
new file mode 100644
index 0000000..d78e6e6
--- /dev/null
+++ b/src/luarocks/fetch/cvs.lua
@@ -0,0 +1,55 @@
+
+--- Fetch back-end for retrieving sources from CVS.
+local cvs = {}
+
+local unpack = unpack or table.unpack
+
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+local util = require("luarocks.util")
+
+--- Download sources for building a rock, using CVS.
+-- @param rockspec table: The rockspec table
+-- @param extract boolean: Unused in this module (required for API purposes.)
+-- @param dest_dir string or nil: If set, will extract to the given directory.
+-- @return (string, string) or (nil, string): The absolute pathname of
+-- the fetched source tarball and the temporary directory created to
+-- store it; or nil and an error message.
+function cvs.get_sources(rockspec, extract, dest_dir)
+ assert(rockspec:type() == "rockspec")
+ assert(type(dest_dir) == "string" or not 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 ok, err = fs.change_dir(store_dir)
+ if not ok then return nil, err end
+ if not fs.execute(unpack(command)) then
+ return nil, "Failed fetching files from CVS."
+ end
+ 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 0000000..29892e9
--- /dev/null
+++ b/src/luarocks/fetch/git.lua
@@ -0,0 +1,165 @@
+
+--- Fetch back-end for retrieving sources from GIT.
+local git = {}
+
+local unpack = unpack or table.unpack
+
+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
+
+--- Get git version.
+-- @param git_cmd string: name of git command.
+-- @return table: git version as returned by luarocks.core.vers.parse_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
+
+--- Check if git satisfies version requirement.
+-- @param git_cmd string: name of git command.
+-- @param version string: required version.
+-- @return boolean: true if git matches version or is newer, false otherwise.
+local function git_is_at_least(git_cmd, version)
+ return git_version(git_cmd) >= vers.parse_version(version)
+end
+
+--- Git >= 1.7.10 can clone a branch **or tag**, < 1.7.10 by branch only. We
+-- need to know this in order to build the appropriate command; if we can't
+-- clone by tag then we'll have to issue a subsequent command to check out the
+-- given tag.
+-- @param git_cmd string: name of git command.
+-- @return boolean: Whether Git can clone by tag.
+local function git_can_clone_by_tag(git_cmd)
+ return git_is_at_least(git_cmd, "1.7.10")
+end
+
+--- Git >= 1.8.4 can fetch submodules shallowly, saving bandwidth and time for
+-- submodules with large history.
+-- @param git_cmd string: name of git command.
+-- @return boolean: Whether Git can fetch submodules shallowly.
+local function git_supports_shallow_submodules(git_cmd)
+ return git_is_at_least(git_cmd, "1.8.4")
+end
+
+--- Git >= 2.10 can fetch submodules shallowly according to .gitmodules configuration, allowing more granularity.
+-- @param git_cmd string: name of git command.
+-- @return boolean: Whether Git can fetch submodules shallowly according to .gitmodules.
+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
+
+--- Download sources for building a rock, using git.
+-- @param rockspec table: The rockspec table
+-- @param extract boolean: Unused in this module (required for API purposes.)
+-- @param dest_dir string or nil: If set, will extract to the given directory.
+-- @return (string, string) or (nil, string): The absolute pathname of
+-- the fetched source tarball and the temporary directory created to
+-- store it; or nil and an error message.
+function git.get_sources(rockspec, extract, dest_dir, depth)
+ assert(rockspec:type() == "rockspec")
+ assert(type(dest_dir) == "string" or not dest_dir)
+
+ local git_cmd = rockspec.variables.GIT
+ local name_version = rockspec.name .. "-" .. rockspec.version
+ local module = dir.base_name(rockspec.source.url)
+ -- Strip off .git from base name if present
+ module = module:gsub("%.git$", "")
+
+ local ok, err_msg = fs.is_tool_available(git_cmd, "Git")
+ if not ok 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 the tag or branch is explicitly set to "master" in the rockspec, then
+ -- we can avoid passing it to Git since it's the default.
+ if tag_or_branch == "master" then tag_or_branch = nil end
+ if tag_or_branch then
+ if git_can_clone_by_tag(git_cmd) then
+ -- The argument to `--branch` can actually be a branch or a tag as of
+ -- Git 1.7.10.
+ table.insert(command, 3, "--branch=" .. tag_or_branch)
+ end
+ end
+ if not fs.execute(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
+
+ -- Fetching git submodules is supported only when rockspec format is >= 3.0.
+ 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
+ -- Fetch only the last commit of each submodule.
+ table.insert(command, 5, "--depth=1")
+ end
+
+ if not fs.execute(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 0000000..8d46bbc
--- /dev/null
+++ b/src/luarocks/fetch/git_file.lua
@@ -0,0 +1,19 @@
+
+--- Fetch back-end for retrieving sources from local Git repositories.
+local git_file = {}
+
+local git = require("luarocks.fetch.git")
+
+--- Fetch sources for building a rock from a local Git repository.
+-- @param rockspec table: The rockspec table
+-- @param extract boolean: Unused in this module (required for API purposes.)
+-- @param dest_dir string or nil: If set, will extract to the given directory.
+-- @return (string, string) or (nil, string): The absolute pathname of
+-- the fetched source tarball and the temporary directory created to
+-- store it; or nil and an error message.
+function git_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 0000000..d85e257
--- /dev/null
+++ b/src/luarocks/fetch/git_http.lua
@@ -0,0 +1,26 @@
+
+--- Fetch back-end for retrieving sources from Git repositories
+-- that use http:// transport. For example, for fetching a repository
+-- that requires the following command line:
+-- `git clone http://example.com/foo.git`
+-- you can use this in the rockspec:
+-- source = { url = "git+http://example.com/foo.git" }
+-- Prefer using the normal git:// fetch mode as it is more widely
+-- available in older versions of LuaRocks.
+local git_http = {}
+
+local git = require("luarocks.fetch.git")
+
+--- Fetch sources for building a rock from a local Git repository.
+-- @param rockspec table: The rockspec table
+-- @param extract boolean: Unused in this module (required for API purposes.)
+-- @param dest_dir string or nil: If set, will extract to the given directory.
+-- @return (string, string) or (nil, string): The absolute pathname of
+-- the fetched source tarball and the temporary directory created to
+-- store it; or nil and an error message.
+function git_http.get_sources(rockspec, extract, dest_dir)
+ rockspec.source.url = rockspec.source.url:gsub("^git.", "")
+ return git.get_sources(rockspec, extract, dest_dir, "--")
+end
+
+return git_http
diff --git a/src/luarocks/fetch/git_https.lua b/src/luarocks/fetch/git_https.lua
new file mode 100644
index 0000000..67f8ad6
--- /dev/null
+++ b/src/luarocks/fetch/git_https.lua
@@ -0,0 +1,7 @@
+--- Fetch back-end for retrieving sources from Git repositories
+-- that use https:// transport. For example, for fetching a repository
+-- that requires the following command line:
+-- `git clone https://example.com/foo.git`
+-- you can use this in the rockspec:
+-- source = { url = "git+https://example.com/foo.git" }
+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 0000000..0c2c075
--- /dev/null
+++ b/src/luarocks/fetch/git_ssh.lua
@@ -0,0 +1,32 @@
+--- Fetch back-end for retrieving sources from Git repositories
+-- that use ssh:// transport. For example, for fetching a repository
+-- that requires the following command line:
+-- `git clone ssh://git@example.com/path/foo.git
+-- you can use this in the rockspec:
+-- source = { url = "git+ssh://git@example.com/path/foo.git" }
+-- It also handles scp-style ssh urls: git@example.com:path/foo.git,
+-- but you have to prepend the "git+ssh://" and why not use the "newer"
+-- style anyway?
+local git_ssh = {}
+
+local git = require("luarocks.fetch.git")
+
+--- Fetch sources for building a rock from a local Git repository.
+-- @param rockspec table: The rockspec table
+-- @param extract boolean: Unused in this module (required for API purposes.)
+-- @param dest_dir string or nil: If set, will extract to the given directory.
+-- @return (string, string) or (nil, string): The absolute pathname of
+-- the fetched source tarball and the temporary directory created to
+-- store it; or nil and an error message.
+function git_ssh.get_sources(rockspec, extract, dest_dir)
+ rockspec.source.url = rockspec.source.url:gsub("^git.", "")
+
+ -- Handle old-style scp-like git ssh urls
+ 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 0000000..0ef0f5e
--- /dev/null
+++ b/src/luarocks/fetch/hg.lua
@@ -0,0 +1,65 @@
+
+--- Fetch back-end for retrieving sources from HG.
+local hg = {}
+
+local unpack = unpack or table.unpack
+
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+local util = require("luarocks.util")
+
+--- Download sources for building a rock, using hg.
+-- @param rockspec table: The rockspec table
+-- @param extract boolean: Unused in this module (required for API purposes.)
+-- @param dest_dir string or nil: If set, will extract to the given directory.
+-- @return (string, string) or (nil, string): The absolute pathname of
+-- the fetched source tarball and the temporary directory created to
+-- store it; or nil and an error message.
+function hg.get_sources(rockspec, extract, dest_dir)
+ assert(rockspec:type() == "rockspec")
+ assert(type(dest_dir) == "string" or not dest_dir)
+
+ local hg_cmd = rockspec.variables.HG
+ local ok, err_msg = fs.is_tool_available(hg_cmd, "Mercurial")
+ if not ok then
+ return nil, err_msg
+ end
+
+ local name_version = rockspec.name .. "-" .. rockspec.version
+ -- Strip off special hg:// protocol type
+ local url = rockspec.source.url:gsub("^hg://", "")
+
+ local module = dir.base_name(url)
+
+ local command = {hg_cmd, "clone", url, module}
+ local tag_or_branch = rockspec.source.tag or rockspec.source.branch
+ if tag_or_branch then
+ command = {hg_cmd, "clone", "--rev", 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(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 0000000..8f506da
--- /dev/null
+++ b/src/luarocks/fetch/hg_http.lua
@@ -0,0 +1,24 @@
+
+--- Fetch back-end for retrieving sources from hg repositories
+-- that use http:// transport. For example, for fetching a repository
+-- that requires the following command line:
+-- `hg clone http://example.com/foo`
+-- you can use this in the rockspec:
+-- source = { url = "hg+http://example.com/foo" }
+local hg_http = {}
+
+local hg = require("luarocks.fetch.hg")
+
+--- Download sources for building a rock, using hg over http.
+-- @param rockspec table: The rockspec table
+-- @param extract boolean: Unused in this module (required for API purposes.)
+-- @param dest_dir string or nil: If set, will extract to the given directory.
+-- @return (string, string) or (nil, string): The absolute pathname of
+-- the fetched source tarball and the temporary directory created to
+-- store it; or nil and an error message.
+function 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 0000000..e67417f
--- /dev/null
+++ b/src/luarocks/fetch/hg_https.lua
@@ -0,0 +1,8 @@
+
+--- Fetch back-end for retrieving sources from hg repositories
+-- that use https:// transport. For example, for fetching a repository
+-- that requires the following command line:
+-- `hg clone https://example.com/foo`
+-- you can use this in the rockspec:
+-- source = { url = "hg+https://example.com/foo" }
+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 0000000..0c365fa
--- /dev/null
+++ b/src/luarocks/fetch/hg_ssh.lua
@@ -0,0 +1,8 @@
+
+--- Fetch back-end for retrieving sources from hg repositories
+-- that use ssh:// transport. For example, for fetching a repository
+-- that requires the following command line:
+-- `hg clone ssh://example.com/foo`
+-- you can use this in the rockspec:
+-- source = { url = "hg+ssh://example.com/foo" }
+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 0000000..32bb2ec
--- /dev/null
+++ b/src/luarocks/fetch/sscm.lua
@@ -0,0 +1,44 @@
+
+--- Fetch back-end for retrieving sources from Surround SCM Server
+local sscm = {}
+
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+
+--- Download sources via Surround SCM Server for building a rock.
+-- @param rockspec table: The rockspec table
+-- @param extract boolean: Unused in this module (required for API purposes.)
+-- @param dest_dir string or nil: If set, will extract to the given directory.
+-- @return (string, string) or (nil, string): The absolute pathname of
+-- the fetched source tarball and the temporary directory created to
+-- store it; or nil and an error message.
+function sscm.get_sources(rockspec, extract, dest_dir)
+ assert(rockspec:type() == "rockspec")
+ assert(type(dest_dir) == "string" or not dest_dir)
+
+ local sscm_cmd = rockspec.variables.SSCM
+ local module = rockspec.source.module or dir.base_name(rockspec.source.url)
+ local branch, repository = string.match(rockspec.source.pathname, "^([^/]*)/(.*)")
+ if not branch or not repository then
+ return nil, "Error retrieving branch and repository from rockspec."
+ end
+ -- Search for working directory.
+ local working_dir
+ local tmp = io.popen(string.format(sscm_cmd..[[ property "/" -d -b%s -p%s]], branch, repository))
+ for line in tmp:lines() do
+ --%c because a chr(13) comes in the end.
+ working_dir = string.match(line, "Working directory:[%s]*(.*)%c$")
+ if working_dir then break end
+ end
+ tmp:close()
+ if not working_dir then
+ return nil, "Error retrieving working directory from SSCM."
+ end
+ if not fs.execute(sscm_cmd, "get", "*", "-e" , "-r", "-b"..branch, "-p"..repository, "-tmodify", "-wreplace") then
+ return nil, "Failed fetching files from SSCM."
+ end
+ -- FIXME: This function does not honor the dest_dir parameter.
+ return module, working_dir
+end
+
+return sscm
diff --git a/src/luarocks/fetch/svn.lua b/src/luarocks/fetch/svn.lua
new file mode 100644
index 0000000..b6618af
--- /dev/null
+++ b/src/luarocks/fetch/svn.lua
@@ -0,0 +1,64 @@
+
+--- Fetch back-end for retrieving sources from Subversion.
+local svn = {}
+
+local unpack = unpack or table.unpack
+
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+local util = require("luarocks.util")
+
+--- Download sources for building a rock, using Subversion.
+-- @param rockspec table: The rockspec table
+-- @param extract boolean: Unused in this module (required for API purposes.)
+-- @param dest_dir string or nil: If set, will extract to the given directory.
+-- @return (string, string) or (nil, string): The absolute pathname of
+-- the fetched source tarball and the temporary directory created to
+-- store it; or nil and an error message.
+function svn.get_sources(rockspec, extract, dest_dir)
+ assert(rockspec:type() == "rockspec")
+ assert(type(dest_dir) == "string" or not 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 ok, err = fs.change_dir(store_dir)
+ if not ok then return nil, err end
+ if not fs.execute(unpack(command)) then
+ return nil, "Failed fetching files from Subversion."
+ end
+ ok, err = fs.change_dir(module)
+ if not ok then return nil, err end
+ for _, d in ipairs(fs.find(".")) do
+ if dir.base_name(d) == ".svn" then
+ fs.delete(dir.path(store_dir, module, d))
+ end
+ end
+ fs.pop_dir()
+ fs.pop_dir()
+ return module, store_dir
+end
+
+
+return svn