diff options
| author | Hisham Muhammad <hisham@gobolinux.org> | 2024-08-22 17:49:09 -0300 |
|---|---|---|
| committer | Hisham Muhammad <hisham@gobolinux.org> | 2024-10-21 13:30:51 -0300 |
| commit | ba518aa110d2fe84f6163c785da9fc7ab4ea9eae (patch) | |
| tree | 7feda65cdd58434178c1a8f8e19447583b6ab234 /src | |
| parent | 259e0ca4855d775dc8ba61f6d30b1cc3b493ae61 (diff) | |
| download | luarocks-ba518aa110d2fe84f6163c785da9fc7ab4ea9eae.tar.gz luarocks-ba518aa110d2fe84f6163c785da9fc7ab4ea9eae.tar.bz2 luarocks-ba518aa110d2fe84f6163c785da9fc7ab4ea9eae.zip | |
Teal: add generated modules
Diffstat (limited to 'src')
89 files changed, 16933 insertions, 0 deletions
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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local cache = {} | ||
| 5 | |||
| 6 | |||
| 7 | local fs = require("luarocks.fs") | ||
| 8 | local cfg = require("luarocks.core.cfg") | ||
| 9 | local dir = require("luarocks.dir") | ||
| 10 | local util = require("luarocks.util") | ||
| 11 | |||
| 12 | function cache.get_upload_server(server) | ||
| 13 | if not server then server = cfg.upload_server end | ||
| 14 | if not server then | ||
| 15 | return nil, nil, "No server specified and no default configured with upload_server." | ||
| 16 | end | ||
| 17 | return server, cfg.upload_servers and cfg.upload_servers[server] | ||
| 18 | end | ||
| 19 | |||
| 20 | function cache.get_server_urls(server, upload_server) | ||
| 21 | local download_url = server | ||
| 22 | local login_url = nil | ||
| 23 | if upload_server then | ||
| 24 | if upload_server.rsync then download_url = "rsync://" .. upload_server.rsync | ||
| 25 | elseif upload_server.http then download_url = "http://" .. upload_server.http | ||
| 26 | elseif upload_server.ftp then download_url = "ftp://" .. upload_server.ftp | ||
| 27 | end | ||
| 28 | |||
| 29 | if upload_server.ftp then login_url = "ftp://" .. upload_server.ftp | ||
| 30 | elseif upload_server.sftp then login_url = "sftp://" .. upload_server.sftp | ||
| 31 | end | ||
| 32 | end | ||
| 33 | return download_url, login_url | ||
| 34 | end | ||
| 35 | |||
| 36 | function cache.split_server_url(url, user, password) | ||
| 37 | local protocol, server_path = dir.split_url(url) | ||
| 38 | if protocol == "file" then | ||
| 39 | server_path = fs.absolute_name(server_path) | ||
| 40 | elseif server_path:match("@") then | ||
| 41 | local credentials | ||
| 42 | credentials, server_path = server_path:match("([^@]*)@(.*)") | ||
| 43 | if credentials:match(":") then | ||
| 44 | user, password = credentials:match("([^:]*):(.*)") | ||
| 45 | else | ||
| 46 | user = credentials | ||
| 47 | end | ||
| 48 | end | ||
| 49 | local local_cache = dir.path(cfg.local_cache, (server_path:gsub("[\\/]", "_"))) | ||
| 50 | return local_cache, protocol, server_path, user, password | ||
| 51 | end | ||
| 52 | |||
| 53 | local function download_cache(protocol, server_path, user, password) | ||
| 54 | os.remove("index.html") | ||
| 55 | |||
| 56 | if protocol == "rsync" then | ||
| 57 | local srv, path = server_path:match("([^/]+)(/.+)") | ||
| 58 | return fs.execute(cfg.variables.RSYNC .. " " .. cfg.variables.RSYNCFLAGS .. " -e ssh " .. user .. "@" .. srv .. ":" .. path .. "/ ./") | ||
| 59 | elseif protocol == "file" then | ||
| 60 | return fs.copy_contents(server_path, ".") | ||
| 61 | else | ||
| 62 | local login_info = "" | ||
| 63 | if user then login_info = " --user=" .. user end | ||
| 64 | if password then login_info = login_info .. " --password=" .. password end | ||
| 65 | return fs.execute(cfg.variables.WGET .. " --no-cache -q -m -np -nd " .. protocol .. "://" .. server_path .. login_info) | ||
| 66 | end | ||
| 67 | end | ||
| 68 | |||
| 69 | function cache.refresh_local_cache(url, given_user, given_password) | ||
| 70 | local local_cache, protocol, server_path, user, password = cache.split_server_url(url, given_user, given_password) | ||
| 71 | |||
| 72 | local ok, err = fs.make_dir(local_cache) | ||
| 73 | if not ok then | ||
| 74 | return nil, "Failed creating local cache dir: " .. err | ||
| 75 | end | ||
| 76 | |||
| 77 | fs.change_dir(local_cache) | ||
| 78 | |||
| 79 | util.printout("Refreshing cache " .. local_cache .. "...") | ||
| 80 | |||
| 81 | ok = download_cache(protocol, server_path, user, password) | ||
| 82 | if not ok then | ||
| 83 | return nil, "Failed downloading cache." | ||
| 84 | end | ||
| 85 | |||
| 86 | return local_cache, protocol, server_path, user, password | ||
| 87 | end | ||
| 88 | |||
| 89 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local add = {} | ||
| 5 | |||
| 6 | |||
| 7 | local cfg = require("luarocks.core.cfg") | ||
| 8 | local util = require("luarocks.util") | ||
| 9 | local dir = require("luarocks.dir") | ||
| 10 | local writer = require("luarocks.manif.writer") | ||
| 11 | local fs = require("luarocks.fs") | ||
| 12 | local cache = require("luarocks.admin.cache") | ||
| 13 | local index = require("luarocks.admin.index") | ||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | function add.add_to_parser(parser) | ||
| 20 | local cmd = parser:command("add", "Add a rock or rockspec to a rocks server.", util.see_also()) | ||
| 21 | |||
| 22 | cmd:argument("rocks", "A local rockspec or rock file."): | ||
| 23 | args("+") | ||
| 24 | |||
| 25 | cmd:option("--server", "The server to use. If not given, the default server " .. | ||
| 26 | "set in the upload_server variable from the configuration file is used instead."): | ||
| 27 | target("add_server") | ||
| 28 | cmd:flag("--no-refresh", "Do not refresh the local cache prior to " .. | ||
| 29 | "generation of the updated manifest.") | ||
| 30 | cmd:flag("--index", "Produce an index.html file for the manifest. This " .. | ||
| 31 | "flag is automatically set if an index.html file already exists.") | ||
| 32 | end | ||
| 33 | |||
| 34 | local function zip_manifests() | ||
| 35 | for ver in util.lua_versions() do | ||
| 36 | local file = "manifest-" .. ver | ||
| 37 | local zip = file .. ".zip" | ||
| 38 | fs.delete(dir.path(fs.current_dir(), zip)) | ||
| 39 | fs.zip(zip, file) | ||
| 40 | end | ||
| 41 | end | ||
| 42 | |||
| 43 | local function add_files_to_server(refresh, rockfiles, server, upload_server, do_index) | ||
| 44 | |||
| 45 | local download_url, login_url = cache.get_server_urls(server, upload_server) | ||
| 46 | local at = fs.current_dir() | ||
| 47 | local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url | ||
| 48 | |||
| 49 | local local_cache, protocol, server_path, user, password = refresh_fn(download_url, cfg.upload_user, cfg.upload_password) | ||
| 50 | if not local_cache then | ||
| 51 | return nil, protocol | ||
| 52 | end | ||
| 53 | |||
| 54 | if not login_url then | ||
| 55 | login_url = protocol .. "://" .. server_path | ||
| 56 | end | ||
| 57 | |||
| 58 | local ok, err = fs.change_dir(at) | ||
| 59 | if not ok then return nil, err end | ||
| 60 | |||
| 61 | local files = {} | ||
| 62 | for _, rockfile in ipairs(rockfiles) do | ||
| 63 | if fs.exists(rockfile) then | ||
| 64 | util.printout("Copying file " .. rockfile .. " to " .. local_cache .. "...") | ||
| 65 | local absolute = fs.absolute_name(rockfile) | ||
| 66 | fs.copy(absolute, local_cache, "read") | ||
| 67 | table.insert(files, dir.base_name(absolute)) | ||
| 68 | else | ||
| 69 | util.printerr("File " .. rockfile .. " not found") | ||
| 70 | end | ||
| 71 | end | ||
| 72 | if #files == 0 then | ||
| 73 | return nil, "No files found" | ||
| 74 | end | ||
| 75 | |||
| 76 | local ok, err = fs.change_dir(local_cache) | ||
| 77 | if not ok then return nil, err end | ||
| 78 | |||
| 79 | util.printout("Updating manifest...") | ||
| 80 | writer.make_manifest(local_cache, "one", true) | ||
| 81 | |||
| 82 | zip_manifests() | ||
| 83 | |||
| 84 | if fs.exists("index.html") then | ||
| 85 | do_index = true | ||
| 86 | end | ||
| 87 | |||
| 88 | if do_index then | ||
| 89 | util.printout("Updating index.html...") | ||
| 90 | index.make_index(local_cache) | ||
| 91 | end | ||
| 92 | |||
| 93 | local login_info = "" | ||
| 94 | if user then login_info = " -u " .. user end | ||
| 95 | if password then login_info = login_info .. ":" .. password end | ||
| 96 | if not login_url:match("/$") then | ||
| 97 | login_url = login_url .. "/" | ||
| 98 | end | ||
| 99 | |||
| 100 | if do_index then | ||
| 101 | table.insert(files, "index.html") | ||
| 102 | end | ||
| 103 | table.insert(files, "manifest") | ||
| 104 | for ver in util.lua_versions() do | ||
| 105 | table.insert(files, "manifest-" .. ver) | ||
| 106 | table.insert(files, "manifest-" .. ver .. ".zip") | ||
| 107 | end | ||
| 108 | |||
| 109 | |||
| 110 | |||
| 111 | local cmd | ||
| 112 | if protocol == "rsync" then | ||
| 113 | local srv, path = server_path:match("([^/]+)(/.+)") | ||
| 114 | cmd = cfg.variables.RSYNC .. " " .. cfg.variables.RSYNCFLAGS .. " -e ssh " .. local_cache .. "/ " .. user .. "@" .. srv .. ":" .. path .. "/" | ||
| 115 | elseif protocol == "file" then | ||
| 116 | return fs.copy_contents(local_cache, server_path) | ||
| 117 | elseif upload_server and upload_server.sftp then | ||
| 118 | local part1, part2 = upload_server.sftp:match("^([^/]*)/(.*)$") | ||
| 119 | cmd = cfg.variables.SCP .. " " .. table.concat(files, " ") .. " " .. user .. "@" .. part1 .. ":/" .. part2 | ||
| 120 | else | ||
| 121 | cmd = cfg.variables.CURL .. " " .. login_info .. " -T '{" .. table.concat(files, ",") .. "}' " .. login_url | ||
| 122 | end | ||
| 123 | |||
| 124 | util.printout(cmd) | ||
| 125 | return fs.execute(cmd) | ||
| 126 | end | ||
| 127 | |||
| 128 | function add.command(args) | ||
| 129 | local server, server_table, err = cache.get_upload_server(args.add_server or args.server) | ||
| 130 | if not server then return nil, err end | ||
| 131 | return add_files_to_server(not args.no_refresh, args.rocks, server, server_table, args.index) | ||
| 132 | end | ||
| 133 | |||
| 134 | |||
| 135 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local make_manifest = {} | ||
| 5 | |||
| 6 | |||
| 7 | local writer = require("luarocks.manif.writer") | ||
| 8 | local index = require("luarocks.admin.index") | ||
| 9 | local cfg = require("luarocks.core.cfg") | ||
| 10 | local util = require("luarocks.util") | ||
| 11 | local deps = require("luarocks.deps") | ||
| 12 | local fs = require("luarocks.fs") | ||
| 13 | local dir = require("luarocks.dir") | ||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | function make_manifest.add_to_parser(parser) | ||
| 20 | local cmd = parser:command("make_manifest", "Compile a manifest file for a repository.", util.see_also()) | ||
| 21 | |||
| 22 | cmd:argument("repository", "Local repository pathname."): | ||
| 23 | args("?") | ||
| 24 | |||
| 25 | cmd:flag("--local-tree", "If given, do not write versioned versions of the manifest file.\n" .. | ||
| 26 | "Use this when rebuilding the manifest of a local rocks tree.") | ||
| 27 | util.deps_mode_option(cmd) | ||
| 28 | end | ||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | function make_manifest.command(args) | ||
| 34 | local repo = args.repository or cfg.rocks_dir | ||
| 35 | |||
| 36 | util.printout("Making manifest for " .. repo) | ||
| 37 | |||
| 38 | if repo:match("/lib/luarocks") and not args.local_tree then | ||
| 39 | util.warning("This looks like a local rocks tree, but you did not pass --local-tree.") | ||
| 40 | end | ||
| 41 | |||
| 42 | local ok, err = writer.make_manifest(repo, deps.get_deps_mode(args), not args.local_tree) | ||
| 43 | if ok and not args.local_tree then | ||
| 44 | util.printout("Generating index.html for " .. repo) | ||
| 45 | index.make_index(repo) | ||
| 46 | end | ||
| 47 | if args.local_tree then | ||
| 48 | for luaver in util.lua_versions() do | ||
| 49 | fs.delete(dir.path(repo, "manifest-" .. luaver)) | ||
| 50 | end | ||
| 51 | end | ||
| 52 | return ok, err | ||
| 53 | end | ||
| 54 | |||
| 55 | 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 @@ | |||
| 1 | |||
| 2 | |||
| 3 | local refresh_cache = {} | ||
| 4 | |||
| 5 | |||
| 6 | local cfg = require("luarocks.core.cfg") | ||
| 7 | local util = require("luarocks.util") | ||
| 8 | local cache = require("luarocks.admin.cache") | ||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | function refresh_cache.add_to_parser(parser) | ||
| 15 | local cmd = parser:command("refresh_cache", "Refresh local cache of a remote rocks server.", util.see_also()) | ||
| 16 | |||
| 17 | cmd:option("--from", "The server to use. If not given, the default server " .. | ||
| 18 | "set in the upload_server variable from the configuration file is used instead."): | ||
| 19 | argname("<server>") | ||
| 20 | end | ||
| 21 | |||
| 22 | function refresh_cache.command(args) | ||
| 23 | local server, upload_server, err = cache.get_upload_server(args.server) | ||
| 24 | if not server then return nil, err end | ||
| 25 | local download_url = cache.get_server_urls(server, upload_server) | ||
| 26 | |||
| 27 | local ok, err = cache.refresh_local_cache(download_url, cfg.upload_user, cfg.upload_password) | ||
| 28 | if not ok then | ||
| 29 | return nil, err | ||
| 30 | else | ||
| 31 | return true | ||
| 32 | end | ||
| 33 | end | ||
| 34 | |||
| 35 | |||
| 36 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local admin_remove = {} | ||
| 5 | |||
| 6 | |||
| 7 | local cfg = require("luarocks.core.cfg") | ||
| 8 | local util = require("luarocks.util") | ||
| 9 | local dir = require("luarocks.dir") | ||
| 10 | local writer = require("luarocks.manif.writer") | ||
| 11 | local fs = require("luarocks.fs") | ||
| 12 | local cache = require("luarocks.admin.cache") | ||
| 13 | local index = require("luarocks.admin.index") | ||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | function admin_remove.add_to_parser(parser) | ||
| 20 | local cmd = parser:command("remove", "Remove a rock or rockspec from a rocks server.", util.see_also()) | ||
| 21 | |||
| 22 | cmd:argument("rocks", "A local rockspec or rock file."): | ||
| 23 | args("+") | ||
| 24 | |||
| 25 | cmd:option("--server", "The server to use. If not given, the default server " .. | ||
| 26 | "set in the upload_server variable from the configuration file is used instead.") | ||
| 27 | cmd:flag("--no-refresh", "Do not refresh the local cache prior to " .. | ||
| 28 | "generation of the updated manifest.") | ||
| 29 | end | ||
| 30 | |||
| 31 | local function remove_files_from_server(refresh, rockfiles, server, upload_server) | ||
| 32 | |||
| 33 | local download_url, login_url = cache.get_server_urls(server, upload_server) | ||
| 34 | local at = fs.current_dir() | ||
| 35 | local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url | ||
| 36 | |||
| 37 | local local_cache, protocol, server_path, user, password = refresh_fn(download_url, cfg.upload_user, cfg.upload_password) | ||
| 38 | if not local_cache then | ||
| 39 | return nil, protocol | ||
| 40 | end | ||
| 41 | |||
| 42 | local ok, err = fs.change_dir(at) | ||
| 43 | if not ok then return nil, err end | ||
| 44 | |||
| 45 | local nr_files = 0 | ||
| 46 | for _, rockfile in ipairs(rockfiles) do | ||
| 47 | local basename = dir.base_name(rockfile) | ||
| 48 | local file = dir.path(local_cache, basename) | ||
| 49 | util.printout("Removing file " .. file .. "...") | ||
| 50 | fs.delete(file) | ||
| 51 | if not fs.exists(file) then | ||
| 52 | nr_files = nr_files + 1 | ||
| 53 | else | ||
| 54 | util.printerr("Failed removing " .. file) | ||
| 55 | end | ||
| 56 | end | ||
| 57 | if nr_files == 0 then | ||
| 58 | return nil, "No files removed." | ||
| 59 | end | ||
| 60 | |||
| 61 | local ok, err = fs.change_dir(local_cache) | ||
| 62 | if not ok then return nil, err end | ||
| 63 | |||
| 64 | util.printout("Updating manifest...") | ||
| 65 | writer.make_manifest(local_cache, "one", true) | ||
| 66 | util.printout("Updating index.html...") | ||
| 67 | index.make_index(local_cache) | ||
| 68 | |||
| 69 | if protocol == "file" then | ||
| 70 | local cmd = cfg.variables.RSYNC .. " " .. cfg.variables.RSYNCFLAGS .. " --delete " .. local_cache .. "/ " .. server_path .. "/" | ||
| 71 | util.printout(cmd) | ||
| 72 | fs.execute(cmd) | ||
| 73 | return true | ||
| 74 | end | ||
| 75 | |||
| 76 | if protocol ~= "rsync" then | ||
| 77 | return nil, "This command requires 'rsync', check your configuration." | ||
| 78 | end | ||
| 79 | |||
| 80 | local srv, path = server_path:match("([^/]+)(/.+)") | ||
| 81 | local cmd = cfg.variables.RSYNC .. " " .. cfg.variables.RSYNCFLAGS .. " --delete -e ssh " .. local_cache .. "/ " .. user .. "@" .. srv .. ":" .. path .. "/" | ||
| 82 | |||
| 83 | util.printout(cmd) | ||
| 84 | fs.execute(cmd) | ||
| 85 | |||
| 86 | return true | ||
| 87 | end | ||
| 88 | |||
| 89 | function admin_remove.command(args) | ||
| 90 | local server, server_table, err = cache.get_upload_server(args.server) | ||
| 91 | if not server then return nil, err end | ||
| 92 | return remove_files_from_server(not args.no_refresh, args.rocks, server, server_table) | ||
| 93 | end | ||
| 94 | |||
| 95 | |||
| 96 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local index = {} | ||
| 4 | |||
| 5 | |||
| 6 | local util = require("luarocks.util") | ||
| 7 | local fs = require("luarocks.fs") | ||
| 8 | local vers = require("luarocks.core.vers") | ||
| 9 | local persist = require("luarocks.persist") | ||
| 10 | local dir = require("luarocks.dir") | ||
| 11 | local manif = require("luarocks.manif") | ||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | local ext_url_target = ' target="_blank"' | ||
| 18 | |||
| 19 | local index_header = [[ | ||
| 20 | <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> | ||
| 21 | <html> | ||
| 22 | <head> | ||
| 23 | <title>Available rocks</title> | ||
| 24 | <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"> | ||
| 25 | <style> | ||
| 26 | body { | ||
| 27 | background-color: white; | ||
| 28 | font-family: "bitstream vera sans", "verdana", "sans"; | ||
| 29 | font-size: 14px; | ||
| 30 | } | ||
| 31 | a { | ||
| 32 | color: #0000c0; | ||
| 33 | text-decoration: none; | ||
| 34 | } | ||
| 35 | a.pkg { | ||
| 36 | color: black; | ||
| 37 | } | ||
| 38 | a:hover { | ||
| 39 | text-decoration: underline; | ||
| 40 | } | ||
| 41 | td.main { | ||
| 42 | border-style: none; | ||
| 43 | } | ||
| 44 | blockquote { | ||
| 45 | font-size: 12px; | ||
| 46 | } | ||
| 47 | td.package { | ||
| 48 | background-color: #f0f0f0; | ||
| 49 | vertical-align: top; | ||
| 50 | } | ||
| 51 | td.spacer { | ||
| 52 | height: 5px; | ||
| 53 | } | ||
| 54 | td.version { | ||
| 55 | background-color: #d0d0d0; | ||
| 56 | vertical-align: top; | ||
| 57 | text-align: left; | ||
| 58 | padding: 5px; | ||
| 59 | width: 100px; | ||
| 60 | } | ||
| 61 | p.manifest { | ||
| 62 | font-size: 8px; | ||
| 63 | } | ||
| 64 | </style> | ||
| 65 | </head> | ||
| 66 | <body> | ||
| 67 | <h1>Available rocks</h1> | ||
| 68 | <p> | ||
| 69 | Lua modules available from this location for use with <a href="http://www.luarocks.org">LuaRocks</a>: | ||
| 70 | </p> | ||
| 71 | <table class="main"> | ||
| 72 | ]] | ||
| 73 | |||
| 74 | local index_package_begin = [[ | ||
| 75 | <td class="package"> | ||
| 76 | <p><a name="$anchor"></a><a href="#$anchor" class="pkg"><b>$package</b></a> - $summary<br/> | ||
| 77 | </p><blockquote><p>$detailed<br/> | ||
| 78 | $externaldependencies | ||
| 79 | <font size="-1"><a href="$original">latest sources</a> $homepage | License: $license</font></p> | ||
| 80 | </blockquote></a></td> | ||
| 81 | <td class="version"> | ||
| 82 | ]] | ||
| 83 | |||
| 84 | local index_package_end = [[ | ||
| 85 | </td></tr> | ||
| 86 | <tr><td colspan="2" class="spacer"></td></tr> | ||
| 87 | ]] | ||
| 88 | |||
| 89 | local index_footer_begin = [[ | ||
| 90 | </table> | ||
| 91 | <p class="manifest"> | ||
| 92 | <a href="manifest">manifest file</a> | ||
| 93 | ]] | ||
| 94 | local index_manifest_ver = [[ | ||
| 95 | • <a href="manifest-$VER">Lua $VER manifest file</a> (<a href="manifest-$VER.zip">zip</a>) | ||
| 96 | ]] | ||
| 97 | local index_footer_end = [[ | ||
| 98 | </p> | ||
| 99 | </body> | ||
| 100 | </html> | ||
| 101 | ]] | ||
| 102 | |||
| 103 | function index.format_external_dependencies(rockspec) | ||
| 104 | if rockspec.external_dependencies then | ||
| 105 | local deplist = {} | ||
| 106 | local listed_set = {} | ||
| 107 | local plats = nil | ||
| 108 | for name, desc in util.sortedpairs(rockspec.external_dependencies) do | ||
| 109 | if name ~= "platforms" then | ||
| 110 | table.insert(deplist, name:lower()) | ||
| 111 | listed_set[name] = true | ||
| 112 | else | ||
| 113 | plats = desc | ||
| 114 | end | ||
| 115 | end | ||
| 116 | if plats then | ||
| 117 | for plat, entries in util.sortedpairs(plats) do | ||
| 118 | for name, desc in util.sortedpairs(entries) do | ||
| 119 | if not listed_set[name] then | ||
| 120 | table.insert(deplist, name:lower() .. " (on " .. plat .. ")") | ||
| 121 | end | ||
| 122 | end | ||
| 123 | end | ||
| 124 | end | ||
| 125 | return '<p><b>External dependencies:</b> ' .. table.concat(deplist, ', ') .. '</p>' | ||
| 126 | else | ||
| 127 | return "" | ||
| 128 | end | ||
| 129 | end | ||
| 130 | |||
| 131 | function index.make_index(repo) | ||
| 132 | if not fs.is_dir(repo) then | ||
| 133 | return nil, "Cannot access repository at " .. repo | ||
| 134 | end | ||
| 135 | local manifest = manif.load_manifest(repo) | ||
| 136 | local out = io.open(dir.path(repo, "index.html"), "w") | ||
| 137 | |||
| 138 | out:write(index_header) | ||
| 139 | for package, version_list in util.sortedpairs(manifest.repository) do | ||
| 140 | local latest_rockspec = nil | ||
| 141 | local output = index_package_begin | ||
| 142 | for version, data in util.sortedpairs(version_list, vers.compare_versions) do | ||
| 143 | local versions = {} | ||
| 144 | output = output .. version .. ': ' | ||
| 145 | table.sort(data, function(a, b) return a.arch < b.arch end) | ||
| 146 | for _, item in ipairs(data) do | ||
| 147 | local file | ||
| 148 | if item.arch == 'rockspec' then | ||
| 149 | file = ("%s-%s.rockspec"):format(package, version) | ||
| 150 | if not latest_rockspec then latest_rockspec = file end | ||
| 151 | else | ||
| 152 | file = ("%s-%s.%s.rock"):format(package, version, item.arch) | ||
| 153 | end | ||
| 154 | table.insert(versions, '<a href="' .. file .. '">' .. item.arch .. '</a>') | ||
| 155 | end | ||
| 156 | output = output .. table.concat(versions, ', ') .. '<br/>' | ||
| 157 | end | ||
| 158 | output = output .. index_package_end | ||
| 159 | if latest_rockspec then | ||
| 160 | local rockspec = persist.load_into_table(dir.path(repo, latest_rockspec)) | ||
| 161 | local descript = rockspec.description or {} | ||
| 162 | local vars = { | ||
| 163 | anchor = package, | ||
| 164 | package = rockspec.package, | ||
| 165 | original = rockspec.source.url, | ||
| 166 | summary = descript.summary or "", | ||
| 167 | detailed = descript.detailed or "", | ||
| 168 | license = descript.license or "N/A", | ||
| 169 | homepage = descript.homepage and ('| <a href="' .. descript.homepage .. '"' .. ext_url_target .. '>project homepage</a>') or "", | ||
| 170 | externaldependencies = index.format_external_dependencies(rockspec), | ||
| 171 | } | ||
| 172 | vars.detailed = vars.detailed:gsub("\n\n", "</p><p>"):gsub("%s+", " ") | ||
| 173 | vars.detailed = vars.detailed:gsub("(https?://[a-zA-Z0-9%.%%-_%+%[%]=%?&/$@;:]+)", '<a href="%1"' .. ext_url_target .. '>%1</a>') | ||
| 174 | output = output:gsub("$(%w+)", vars) | ||
| 175 | else | ||
| 176 | output = output:gsub("$anchor", package) | ||
| 177 | output = output:gsub("$package", package) | ||
| 178 | output = output:gsub("$(%w+)", "") | ||
| 179 | end | ||
| 180 | out:write(output) | ||
| 181 | end | ||
| 182 | out:write(index_footer_begin) | ||
| 183 | for ver in util.lua_versions() do | ||
| 184 | out:write((index_manifest_ver:gsub("$VER", ver))) | ||
| 185 | end | ||
| 186 | out:write(index_footer_end) | ||
| 187 | out:close() | ||
| 188 | end | ||
| 189 | |||
| 190 | 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 @@ | |||
| 1 | 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 | ||
| 2 | local build = {Builder = {}, } | ||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | local path = require("luarocks.path") | ||
| 10 | local util = require("luarocks.util") | ||
| 11 | local fun = require("luarocks.fun") | ||
| 12 | local fetch = require("luarocks.fetch") | ||
| 13 | local fs = require("luarocks.fs") | ||
| 14 | local dir = require("luarocks.dir") | ||
| 15 | local deps = require("luarocks.deps") | ||
| 16 | local cfg = require("luarocks.core.cfg") | ||
| 17 | local vers = require("luarocks.core.vers") | ||
| 18 | local repos = require("luarocks.repos") | ||
| 19 | local repo_writer = require("luarocks.repo_writer") | ||
| 20 | local deplocks = require("luarocks.deplocks") | ||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | do | ||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | local function extract_from_rockspec(files) | ||
| 39 | for name, content in pairs(files) do | ||
| 40 | local fd = io.open(dir.path(fs.current_dir(), name), "w+") | ||
| 41 | fd:write(content) | ||
| 42 | fd:close() | ||
| 43 | end | ||
| 44 | end | ||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | |||
| 49 | |||
| 50 | |||
| 51 | |||
| 52 | function build.apply_patches(rockspec) | ||
| 53 | |||
| 54 | if not (rockspec.build.extra_files or rockspec.build.patches) then | ||
| 55 | return true | ||
| 56 | end | ||
| 57 | |||
| 58 | local fd = io.open(fs.absolute_name(".luarocks.patches.applied"), "r") | ||
| 59 | if fd then | ||
| 60 | fd:close() | ||
| 61 | return true | ||
| 62 | end | ||
| 63 | |||
| 64 | if rockspec.build.extra_files then | ||
| 65 | extract_from_rockspec(rockspec.build.extra_files) | ||
| 66 | end | ||
| 67 | if rockspec.build.patches then | ||
| 68 | extract_from_rockspec(rockspec.build.patches) | ||
| 69 | for patch, patchdata in util.sortedpairs(rockspec.build.patches) do | ||
| 70 | util.printout("Applying patch " .. patch .. "...") | ||
| 71 | local create_delete = rockspec:format_is_at_least("3.0") | ||
| 72 | local ok, err = fs.apply_patch(tostring(patch), patchdata, create_delete) | ||
| 73 | if not ok then | ||
| 74 | return nil, "Failed applying patch " .. patch | ||
| 75 | end | ||
| 76 | end | ||
| 77 | end | ||
| 78 | |||
| 79 | fd = io.open(fs.absolute_name(".luarocks.patches.applied"), "w") | ||
| 80 | if fd then | ||
| 81 | fd:close() | ||
| 82 | end | ||
| 83 | return true | ||
| 84 | end | ||
| 85 | end | ||
| 86 | |||
| 87 | local function check_macosx_deployment_target(rockspec) | ||
| 88 | local target = rockspec.build.macosx_deployment_target | ||
| 89 | local function patch_variable(var) | ||
| 90 | local rockspec_variables = rockspec.variables | ||
| 91 | if rockspec_variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then | ||
| 92 | rockspec_variables[var] = (rockspec_variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET=" .. target) | ||
| 93 | else | ||
| 94 | rockspec_variables[var] = "env MACOSX_DEPLOYMENT_TARGET=" .. target .. " " .. rockspec_variables[var] | ||
| 95 | end | ||
| 96 | end | ||
| 97 | if cfg.is_platform("macosx") and rockspec:format_is_at_least("3.0") and target then | ||
| 98 | local version = util.popen_read("sw_vers -productVersion") | ||
| 99 | if version:match("^%d+%.%d+%.%d+$") or version:match("^%d+%.%d+$") then | ||
| 100 | if vers.compare_versions(target, version) then | ||
| 101 | return nil, ("This rock requires Mac OSX %s, and you are running %s."):format(target, version) | ||
| 102 | end | ||
| 103 | end | ||
| 104 | patch_variable("CC") | ||
| 105 | patch_variable("LD") | ||
| 106 | end | ||
| 107 | return true | ||
| 108 | end | ||
| 109 | |||
| 110 | local function process_dependencies(rockspec, opts, cwd) | ||
| 111 | if not opts.build_only_deps then | ||
| 112 | local ok, err, errcode = deps.check_external_deps(rockspec, "build") | ||
| 113 | if err then | ||
| 114 | return nil, err, errcode | ||
| 115 | end | ||
| 116 | end | ||
| 117 | |||
| 118 | if opts.deps_mode == "none" then | ||
| 119 | return true | ||
| 120 | end | ||
| 121 | |||
| 122 | local deplock_dir = fs.exists(dir.path(cwd, "luarocks.lock")) and cwd or nil | ||
| 123 | |||
| 124 | if not opts.build_only_deps then | ||
| 125 | if next(rockspec.build_dependencies) then | ||
| 126 | |||
| 127 | local user_lua_version = cfg.lua_version | ||
| 128 | local running_lua_version = _VERSION:sub(5) | ||
| 129 | |||
| 130 | if running_lua_version ~= user_lua_version then | ||
| 131 | |||
| 132 | |||
| 133 | |||
| 134 | |||
| 135 | |||
| 136 | |||
| 137 | cfg.lua_version = running_lua_version | ||
| 138 | cfg.lua_modules_path = cfg.lua_modules_path:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version) | ||
| 139 | cfg.lib_modules_path = cfg.lib_modules_path:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version) | ||
| 140 | cfg.rocks_subdir = cfg.rocks_subdir:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version) | ||
| 141 | path.use_tree(cfg.root_dir) | ||
| 142 | end | ||
| 143 | |||
| 144 | local ok, err, errcode = deps.fulfill_dependencies(rockspec, "build_dependencies", "all", opts.verify, deplock_dir) | ||
| 145 | |||
| 146 | path.add_to_package_paths(cfg.root_dir) | ||
| 147 | |||
| 148 | if running_lua_version ~= user_lua_version then | ||
| 149 | |||
| 150 | cfg.lua_version = user_lua_version | ||
| 151 | cfg.lua_modules_path = cfg.lua_modules_path:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version) | ||
| 152 | cfg.lib_modules_path = cfg.lib_modules_path:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version) | ||
| 153 | cfg.rocks_subdir = cfg.rocks_subdir:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version) | ||
| 154 | path.use_tree(cfg.root_dir) | ||
| 155 | end | ||
| 156 | |||
| 157 | if err then | ||
| 158 | return nil, err, errcode | ||
| 159 | end | ||
| 160 | end | ||
| 161 | end | ||
| 162 | |||
| 163 | return deps.fulfill_dependencies(rockspec, "dependencies", opts.deps_mode, opts.verify, deplock_dir) | ||
| 164 | end | ||
| 165 | |||
| 166 | local function fetch_and_change_to_source_dir(rockspec, opts) | ||
| 167 | if opts.minimal_mode or opts.build_only_deps then | ||
| 168 | return true | ||
| 169 | end | ||
| 170 | if opts.need_to_fetch then | ||
| 171 | if opts.branch then | ||
| 172 | rockspec.source.branch = opts.branch | ||
| 173 | end | ||
| 174 | local oks, source_dir, errcode = fetch.fetch_sources(rockspec, true) | ||
| 175 | if not oks then | ||
| 176 | return nil, source_dir, errcode | ||
| 177 | end | ||
| 178 | local ok, err | ||
| 179 | ok, err = fs.change_dir(source_dir) | ||
| 180 | if not ok then | ||
| 181 | return nil, err | ||
| 182 | end | ||
| 183 | else | ||
| 184 | if rockspec.source.file then | ||
| 185 | local ok, err = fs.unpack_archive(rockspec.source.file) | ||
| 186 | if not ok then | ||
| 187 | return nil, err | ||
| 188 | end | ||
| 189 | end | ||
| 190 | local ok, err = fetch.find_rockspec_source_dir(rockspec, ".") | ||
| 191 | if not ok then | ||
| 192 | return nil, err | ||
| 193 | end | ||
| 194 | end | ||
| 195 | fs.change_dir(rockspec.source.dir) | ||
| 196 | return true | ||
| 197 | end | ||
| 198 | |||
| 199 | local function prepare_install_dirs(name, version) | ||
| 200 | local dirs = { | ||
| 201 | lua = { name = path.lua_dir(name, version), is_module_path = true, perms = "read" }, | ||
| 202 | lib = { name = path.lib_dir(name, version), is_module_path = true, perms = "exec" }, | ||
| 203 | bin = { name = path.bin_dir(name, version), is_module_path = false, perms = "exec" }, | ||
| 204 | conf = { name = path.conf_dir(name, version), is_module_path = false, perms = "read" }, | ||
| 205 | } | ||
| 206 | |||
| 207 | for _, d in pairs(dirs) do | ||
| 208 | local ok, err = fs.make_dir(d.name) | ||
| 209 | if not ok then | ||
| 210 | return nil, err | ||
| 211 | end | ||
| 212 | end | ||
| 213 | |||
| 214 | return dirs | ||
| 215 | end | ||
| 216 | |||
| 217 | local function run_build_driver(rockspec, no_install) | ||
| 218 | local btype = rockspec.build.type | ||
| 219 | if btype == "none" then | ||
| 220 | return true | ||
| 221 | end | ||
| 222 | |||
| 223 | if btype == "module" then | ||
| 224 | util.printout("Do not use 'module' as a build type. Use 'builtin' instead.") | ||
| 225 | btype = "builtin" | ||
| 226 | rockspec.build.type = btype | ||
| 227 | end | ||
| 228 | local driver | ||
| 229 | if cfg.accepted_build_types and not fun.contains(cfg.accepted_build_types, btype) then | ||
| 230 | return nil, "This rockspec uses the '" .. btype .. "' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration." | ||
| 231 | end | ||
| 232 | local pok, driver_str = pcall(require, "luarocks.build." .. btype) | ||
| 233 | if not (type(driver_str) == "table") then | ||
| 234 | return nil, "Failed initializing build back-end for build type '" .. btype .. "': " .. driver_str | ||
| 235 | else | ||
| 236 | driver = driver_str | ||
| 237 | end | ||
| 238 | |||
| 239 | if not driver.skip_lua_inc_lib_check then | ||
| 240 | local ok, err, errcode = deps.check_lua_incdir(rockspec.variables) | ||
| 241 | if not ok then | ||
| 242 | return nil, err, errcode | ||
| 243 | end | ||
| 244 | |||
| 245 | if cfg.link_lua_explicitly then | ||
| 246 | ok, err, errcode = deps.check_lua_libdir(rockspec.variables) | ||
| 247 | if not ok then | ||
| 248 | return nil, err, errcode | ||
| 249 | end | ||
| 250 | end | ||
| 251 | end | ||
| 252 | |||
| 253 | local ok, err = driver.run(rockspec, no_install) | ||
| 254 | if not ok then | ||
| 255 | return nil, "Build error: " .. err | ||
| 256 | end | ||
| 257 | return true | ||
| 258 | end | ||
| 259 | |||
| 260 | local install_files | ||
| 261 | do | ||
| 262 | |||
| 263 | |||
| 264 | |||
| 265 | |||
| 266 | |||
| 267 | |||
| 268 | |||
| 269 | |||
| 270 | |||
| 271 | |||
| 272 | |||
| 273 | |||
| 274 | |||
| 275 | |||
| 276 | |||
| 277 | |||
| 278 | |||
| 279 | local function install_to(files, location, is_module_path, perms) | ||
| 280 | if not files then | ||
| 281 | return true | ||
| 282 | end | ||
| 283 | for k, file in pairs(files) do | ||
| 284 | local dest = location | ||
| 285 | local filename = dir.base_name(file) | ||
| 286 | if type(k) == "string" then | ||
| 287 | local modname = k | ||
| 288 | if is_module_path then | ||
| 289 | dest = dir.path(location, path.module_to_path(modname)) | ||
| 290 | local ok, err = fs.make_dir(dest) | ||
| 291 | if not ok then return nil, err end | ||
| 292 | if filename:match("%.lua$") then | ||
| 293 | local basename = modname:match("([^.]+)$") | ||
| 294 | filename = basename .. ".lua" | ||
| 295 | end | ||
| 296 | else | ||
| 297 | dest = dir.path(location, dir.dir_name(modname)) | ||
| 298 | local ok, err = fs.make_dir(dest) | ||
| 299 | if not ok then return nil, err end | ||
| 300 | filename = dir.base_name(modname) | ||
| 301 | end | ||
| 302 | else | ||
| 303 | local ok, err = fs.make_dir(dest) | ||
| 304 | if not ok then return nil, err end | ||
| 305 | end | ||
| 306 | local ok = fs.copy(file, dir.path(dest, filename), perms) | ||
| 307 | if not ok then | ||
| 308 | return nil, "Failed copying " .. file | ||
| 309 | end | ||
| 310 | end | ||
| 311 | return true | ||
| 312 | end | ||
| 313 | |||
| 314 | local function install_default_docs(name, version) | ||
| 315 | local patterns = { "readme", "license", "copying", ".*%.md" } | ||
| 316 | local dest = dir.path(path.install_dir(name, version), "doc") | ||
| 317 | local has_dir = false | ||
| 318 | for file in fs.dir() do | ||
| 319 | for _, pattern in ipairs(patterns) do | ||
| 320 | if file:lower():match("^" .. pattern) then | ||
| 321 | if not has_dir then | ||
| 322 | fs.make_dir(dest) | ||
| 323 | has_dir = true | ||
| 324 | end | ||
| 325 | fs.copy(file, dest, "read") | ||
| 326 | break | ||
| 327 | end | ||
| 328 | end | ||
| 329 | end | ||
| 330 | end | ||
| 331 | |||
| 332 | install_files = function(rockspec, dirs) | ||
| 333 | local name, version = rockspec.name, rockspec.version | ||
| 334 | |||
| 335 | if rockspec.build.install then | ||
| 336 | for k, d in pairs(dirs) do | ||
| 337 | local ok, err = install_to((rockspec.build.install)[k], d.name, d.is_module_path, d.perms) | ||
| 338 | if not ok then return nil, err end | ||
| 339 | end | ||
| 340 | end | ||
| 341 | |||
| 342 | local copy_directories = rockspec.build.copy_directories | ||
| 343 | local copying_default = false | ||
| 344 | if not copy_directories then | ||
| 345 | copy_directories = { "doc" } | ||
| 346 | copying_default = true | ||
| 347 | end | ||
| 348 | |||
| 349 | local any_docs = false | ||
| 350 | for _, copy_dir in ipairs(copy_directories) do | ||
| 351 | if fs.is_dir(copy_dir) then | ||
| 352 | local dest = dir.path(path.install_dir(name, version), copy_dir) | ||
| 353 | fs.make_dir(dest) | ||
| 354 | fs.copy_contents(copy_dir, dest) | ||
| 355 | any_docs = true | ||
| 356 | else | ||
| 357 | if not copying_default then | ||
| 358 | return nil, "Directory '" .. copy_dir .. "' not found" | ||
| 359 | end | ||
| 360 | end | ||
| 361 | end | ||
| 362 | if not any_docs then | ||
| 363 | install_default_docs(name, version) | ||
| 364 | end | ||
| 365 | |||
| 366 | return true | ||
| 367 | end | ||
| 368 | end | ||
| 369 | |||
| 370 | |||
| 371 | |||
| 372 | |||
| 373 | |||
| 374 | |||
| 375 | function build.build_rockspec(rockspec, opts, cwd) | ||
| 376 | |||
| 377 | cwd = cwd or dir.path(".") | ||
| 378 | |||
| 379 | if not rockspec.build then | ||
| 380 | if rockspec:format_is_at_least("3.0") then | ||
| 381 | rockspec.build = { | ||
| 382 | type = "builtin", | ||
| 383 | } | ||
| 384 | else | ||
| 385 | return nil, "Rockspec error: build table not specified" | ||
| 386 | end | ||
| 387 | end | ||
| 388 | |||
| 389 | if not rockspec.build.type then | ||
| 390 | if rockspec:format_is_at_least("3.0") then | ||
| 391 | rockspec.build.type = "builtin" | ||
| 392 | else | ||
| 393 | return nil, "Rockspec error: build type not specified" | ||
| 394 | end | ||
| 395 | end | ||
| 396 | |||
| 397 | local ok, err = fetch_and_change_to_source_dir(rockspec, opts) | ||
| 398 | if not ok then return nil, err end | ||
| 399 | |||
| 400 | if opts.pin then | ||
| 401 | deplocks.init(rockspec.name, ".") | ||
| 402 | end | ||
| 403 | |||
| 404 | ok, err = process_dependencies(rockspec, opts, cwd) | ||
| 405 | if not ok then return nil, err end | ||
| 406 | |||
| 407 | local name, version = rockspec.name, rockspec.version | ||
| 408 | if opts.build_only_deps then | ||
| 409 | if opts.pin then | ||
| 410 | deplocks.write_file() | ||
| 411 | end | ||
| 412 | return name, version | ||
| 413 | end | ||
| 414 | |||
| 415 | local dirs, err | ||
| 416 | local rollback | ||
| 417 | if not opts.no_install then | ||
| 418 | if repos.is_installed(name, version) then | ||
| 419 | repo_writer.delete_version(name, version, opts.deps_mode) | ||
| 420 | end | ||
| 421 | |||
| 422 | dirs, err = prepare_install_dirs(name, version) | ||
| 423 | if not dirs then return nil, err end | ||
| 424 | |||
| 425 | rollback = util.schedule_function(function() | ||
| 426 | fs.delete(path.install_dir(name, version)) | ||
| 427 | fs.remove_dir_if_empty(path.versions_dir(name)) | ||
| 428 | end) | ||
| 429 | end | ||
| 430 | |||
| 431 | ok, err = build.apply_patches(rockspec) | ||
| 432 | if not ok then return nil, err end | ||
| 433 | |||
| 434 | ok, err = check_macosx_deployment_target(rockspec) | ||
| 435 | if not ok then return nil, err end | ||
| 436 | |||
| 437 | ok, err = run_build_driver(rockspec, opts.no_install) | ||
| 438 | if not ok then return nil, err end | ||
| 439 | |||
| 440 | if opts.no_install then | ||
| 441 | fs.pop_dir() | ||
| 442 | if opts.need_to_fetch then | ||
| 443 | fs.pop_dir() | ||
| 444 | end | ||
| 445 | return name, version | ||
| 446 | end | ||
| 447 | |||
| 448 | ok, err = install_files(rockspec, dirs) | ||
| 449 | if not ok then return nil, err end | ||
| 450 | |||
| 451 | for _, d in pairs(dirs) do | ||
| 452 | fs.remove_dir_if_empty(d.name) | ||
| 453 | end | ||
| 454 | |||
| 455 | fs.pop_dir() | ||
| 456 | if opts.need_to_fetch then | ||
| 457 | fs.pop_dir() | ||
| 458 | end | ||
| 459 | |||
| 460 | if opts.pin then | ||
| 461 | deplocks.write_file() | ||
| 462 | end | ||
| 463 | |||
| 464 | fs.copy(rockspec.local_abs_filename, path.rockspec_file(name, version), "read") | ||
| 465 | |||
| 466 | local deplock_file = deplocks.get_abs_filename(name) | ||
| 467 | if deplock_file then | ||
| 468 | fs.copy(deplock_file, dir.path(path.install_dir(name, version), "luarocks.lock"), "read") | ||
| 469 | end | ||
| 470 | |||
| 471 | ok, err = repo_writer.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), opts.deps_mode, opts.namespace) | ||
| 472 | if not ok then return nil, err end | ||
| 473 | |||
| 474 | util.remove_scheduled_function(rollback) | ||
| 475 | rollback = util.schedule_function(function() | ||
| 476 | repo_writer.delete_version(name, version, opts.deps_mode) | ||
| 477 | end) | ||
| 478 | |||
| 479 | ok, err = repos.run_hook(rockspec, "post_install") | ||
| 480 | if not ok then return nil, err end | ||
| 481 | |||
| 482 | util.announce_install(rockspec) | ||
| 483 | util.remove_scheduled_function(rollback) | ||
| 484 | return name, version | ||
| 485 | end | ||
| 486 | |||
| 487 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local builtin = {} | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | builtin.skip_lua_inc_lib_check = true | ||
| 17 | |||
| 18 | local dir_sep = package.config:sub(1, 1) | ||
| 19 | |||
| 20 | local fs = require("luarocks.fs") | ||
| 21 | local path = require("luarocks.path") | ||
| 22 | local util = require("luarocks.util") | ||
| 23 | local cfg = require("luarocks.core.cfg") | ||
| 24 | local dir = require("luarocks.dir") | ||
| 25 | local deps = require("luarocks.deps") | ||
| 26 | |||
| 27 | local function autoextract_libs(external_dependencies, variables) | ||
| 28 | if not external_dependencies then | ||
| 29 | return nil, nil, nil | ||
| 30 | end | ||
| 31 | local libs = {} | ||
| 32 | local incdirs = {} | ||
| 33 | local libdirs = {} | ||
| 34 | for name, data in pairs(external_dependencies) do | ||
| 35 | if data.library then | ||
| 36 | table.insert(libs, data.library) | ||
| 37 | table.insert(incdirs, variables[name .. "_INCDIR"]) | ||
| 38 | table.insert(libdirs, variables[name .. "_LIBDIR"]) | ||
| 39 | end | ||
| 40 | end | ||
| 41 | return libs, incdirs, libdirs | ||
| 42 | end | ||
| 43 | |||
| 44 | do | ||
| 45 | local function get_cmod_name(file) | ||
| 46 | local fd = io.open(dir.path(fs.current_dir(), file), "r") | ||
| 47 | if not fd then return nil end | ||
| 48 | local data = fd:read("*a") | ||
| 49 | fd:close() | ||
| 50 | return (data:match("int%s+luaopen_([a-zA-Z0-9_]+)")) | ||
| 51 | end | ||
| 52 | |||
| 53 | local skiplist = { | ||
| 54 | ["spec"] = true, | ||
| 55 | [".luarocks"] = true, | ||
| 56 | ["lua_modules"] = true, | ||
| 57 | ["test.lua"] = true, | ||
| 58 | ["tests.lua"] = true, | ||
| 59 | } | ||
| 60 | |||
| 61 | function builtin.autodetect_modules(libs, incdirs, libdirs) | ||
| 62 | local modules = {} | ||
| 63 | local install | ||
| 64 | local copy_directories | ||
| 65 | |||
| 66 | local prefix = "" | ||
| 67 | for _, parent in ipairs({ "src", "lua", "lib" }) do | ||
| 68 | if fs.is_dir(parent) then | ||
| 69 | fs.change_dir(parent) | ||
| 70 | prefix = parent .. dir_sep | ||
| 71 | break | ||
| 72 | end | ||
| 73 | end | ||
| 74 | |||
| 75 | for _, file in ipairs(fs.find()) do | ||
| 76 | local base = file:match("^([^\\/]*)") | ||
| 77 | if not skiplist[base] then | ||
| 78 | local luamod = file:match("(.*)%.lua$") | ||
| 79 | if luamod then | ||
| 80 | modules[path.path_to_module(file)] = prefix .. file | ||
| 81 | else | ||
| 82 | local cmod = file:match("(.*)%.c$") | ||
| 83 | if cmod then | ||
| 84 | local modname = get_cmod_name(file) or path.path_to_module((file:gsub("%.c$", ".lua"))) | ||
| 85 | modules[modname] = { | ||
| 86 | sources = prefix .. file, | ||
| 87 | libraries = libs, | ||
| 88 | incdirs = incdirs, | ||
| 89 | libdirs = libdirs, | ||
| 90 | } | ||
| 91 | end | ||
| 92 | end | ||
| 93 | end | ||
| 94 | end | ||
| 95 | |||
| 96 | if prefix ~= "" then | ||
| 97 | fs.pop_dir() | ||
| 98 | end | ||
| 99 | |||
| 100 | local bindir = (fs.is_dir(dir.path("src", "bin")) and dir.path("src", "bin")) or | ||
| 101 | (fs.is_dir("bin") and "bin") | ||
| 102 | if bindir then | ||
| 103 | install = { bin = {} } | ||
| 104 | for _, file in ipairs(fs.list_dir(bindir)) do | ||
| 105 | table.insert((install.bin), dir.path(bindir, file)) | ||
| 106 | end | ||
| 107 | end | ||
| 108 | |||
| 109 | for _, directory in ipairs({ "doc", "docs", "samples", "tests" }) do | ||
| 110 | if fs.is_dir(directory) then | ||
| 111 | if not copy_directories then | ||
| 112 | copy_directories = {} | ||
| 113 | end | ||
| 114 | table.insert(copy_directories, directory) | ||
| 115 | end | ||
| 116 | end | ||
| 117 | |||
| 118 | return modules, install, copy_directories | ||
| 119 | end | ||
| 120 | end | ||
| 121 | |||
| 122 | |||
| 123 | |||
| 124 | |||
| 125 | local function execute(...) | ||
| 126 | io.stdout:write(table.concat({ ... }, " ") .. "\n") | ||
| 127 | return fs.execute(...) | ||
| 128 | end | ||
| 129 | |||
| 130 | |||
| 131 | |||
| 132 | |||
| 133 | |||
| 134 | function builtin.run(rockspec, no_install) | ||
| 135 | local compile_object | ||
| 136 | local compile_library | ||
| 137 | local compile_static_library | ||
| 138 | |||
| 139 | local build = rockspec.build | ||
| 140 | local variables = rockspec.variables | ||
| 141 | local checked_lua_h = false | ||
| 142 | |||
| 143 | for _, var in ipairs({ "CC", "CFLAGS", "LDFLAGS" }) do | ||
| 144 | variables[var] = variables[var] or os.getenv(var) or "" | ||
| 145 | end | ||
| 146 | |||
| 147 | local function add_flags(extras, flag, flags) | ||
| 148 | if flags then | ||
| 149 | if not (type(flags) == "table") then | ||
| 150 | flags = { tostring(flags) } | ||
| 151 | end | ||
| 152 | util.variable_substitutions(flags, variables) | ||
| 153 | for _, v in ipairs(flags) do | ||
| 154 | table.insert(extras, flag:format(v)) | ||
| 155 | end | ||
| 156 | end | ||
| 157 | end | ||
| 158 | |||
| 159 | if cfg.is_platform("mingw32") then | ||
| 160 | compile_object = function(object, source, defines, incdirs) | ||
| 161 | local extras = {} | ||
| 162 | add_flags(extras, "-D%s", defines) | ||
| 163 | add_flags(extras, "-I%s", incdirs) | ||
| 164 | return execute(variables.CC .. " " .. variables.CFLAGS, "-c", "-o", object, "-I" .. variables.LUA_INCDIR, source, _tl_table_unpack(extras)) | ||
| 165 | end | ||
| 166 | compile_library = function(library, objects, libraries, libdirs, name) | ||
| 167 | local extras = { _tl_table_unpack(objects) } | ||
| 168 | add_flags(extras, "-L%s", libdirs) | ||
| 169 | add_flags(extras, "-l%s", libraries) | ||
| 170 | extras[#extras + 1] = dir.path(variables.LUA_LIBDIR, variables.LUALIB) | ||
| 171 | |||
| 172 | if variables.CC == "clang" or variables.CC == "clang-cl" then | ||
| 173 | local exported_name = name:gsub("%.", "_") | ||
| 174 | exported_name = exported_name:match('^[^%-]+%-(.+)$') or exported_name | ||
| 175 | extras[#extras + 1] = string.format("-Wl,-export:luaopen_%s", exported_name) | ||
| 176 | else | ||
| 177 | extras[#extras + 1] = "-l" .. (variables.MSVCRT or "m") | ||
| 178 | end | ||
| 179 | |||
| 180 | local ok = execute(variables.LD .. " " .. variables.LDFLAGS .. " " .. variables.LIBFLAG, "-o", library, _tl_table_unpack(extras)) | ||
| 181 | return ok | ||
| 182 | end | ||
| 183 | |||
| 184 | |||
| 185 | |||
| 186 | |||
| 187 | |||
| 188 | |||
| 189 | |||
| 190 | |||
| 191 | |||
| 192 | elseif cfg.is_platform("win32") then | ||
| 193 | compile_object = function(object, source, defines, incdirs) | ||
| 194 | local extras = {} | ||
| 195 | add_flags(extras, "-D%s", defines) | ||
| 196 | add_flags(extras, "-I%s", incdirs) | ||
| 197 | return execute(variables.CC .. " " .. variables.CFLAGS, "-c", "-Fo" .. object, "-I" .. variables.LUA_INCDIR, source, _tl_table_unpack(extras)) | ||
| 198 | end | ||
| 199 | compile_library = function(library, objects, libraries, libdirs, name) | ||
| 200 | local extras = { _tl_table_unpack(objects) } | ||
| 201 | add_flags(extras, "-libpath:%s", libdirs) | ||
| 202 | add_flags(extras, "%s.lib", libraries) | ||
| 203 | local basename = dir.base_name(library):gsub(".[^.]*$", "") | ||
| 204 | local deffile = basename .. ".def" | ||
| 205 | local def = io.open(dir.path(fs.current_dir(), deffile), "w+") | ||
| 206 | local exported_name = name:gsub("%.", "_") | ||
| 207 | exported_name = exported_name:match('^[^%-]+%-(.+)$') or exported_name | ||
| 208 | def:write("EXPORTS\n") | ||
| 209 | def:write("luaopen_" .. exported_name .. "\n") | ||
| 210 | def:close() | ||
| 211 | local ok = execute(variables.LD, "-dll", "-def:" .. deffile, "-out:" .. library, dir.path(variables.LUA_LIBDIR, variables.LUALIB), _tl_table_unpack(extras)) | ||
| 212 | local basedir = "" | ||
| 213 | if name:find("%.") then | ||
| 214 | basedir = name:gsub("%.%w+$", "\\") | ||
| 215 | basedir = basedir:gsub("%.", "\\") | ||
| 216 | end | ||
| 217 | local manifestfile = basedir .. basename .. ".dll.manifest" | ||
| 218 | |||
| 219 | if ok and fs.exists(manifestfile) then | ||
| 220 | ok = execute(variables.MT, "-manifest", manifestfile, "-outputresource:" .. basedir .. basename .. ".dll;2") | ||
| 221 | end | ||
| 222 | return ok | ||
| 223 | end | ||
| 224 | |||
| 225 | |||
| 226 | |||
| 227 | |||
| 228 | |||
| 229 | |||
| 230 | else | ||
| 231 | compile_object = function(object, source, defines, incdirs) | ||
| 232 | local extras = {} | ||
| 233 | add_flags(extras, "-D%s", defines) | ||
| 234 | add_flags(extras, "-I%s", incdirs) | ||
| 235 | return execute(variables.CC .. " " .. variables.CFLAGS, "-I" .. variables.LUA_INCDIR, "-c", source, "-o", object, _tl_table_unpack(extras)) | ||
| 236 | end | ||
| 237 | compile_library = function(library, objects, libraries, libdirs) | ||
| 238 | local extras = { _tl_table_unpack(objects) } | ||
| 239 | add_flags(extras, "-L%s", libdirs) | ||
| 240 | if cfg.gcc_rpath then | ||
| 241 | add_flags(extras, "-Wl,-rpath,%s", libdirs) | ||
| 242 | end | ||
| 243 | add_flags(extras, "-l%s", libraries) | ||
| 244 | if cfg.link_lua_explicitly then | ||
| 245 | extras[#extras + 1] = "-L" .. variables.LUA_LIBDIR | ||
| 246 | extras[#extras + 1] = "-llua" | ||
| 247 | end | ||
| 248 | return execute(variables.LD .. " " .. variables.LDFLAGS .. " " .. variables.LIBFLAG, "-o", library, _tl_table_unpack(extras)) | ||
| 249 | end | ||
| 250 | compile_static_library = function(library, objects, libraries, libdirs, name) | ||
| 251 | local ok = execute(variables.AR, "rc", library, _tl_table_unpack(objects)) | ||
| 252 | if ok then | ||
| 253 | ok = execute(variables.RANLIB, library) | ||
| 254 | end | ||
| 255 | return ok | ||
| 256 | end | ||
| 257 | end | ||
| 258 | |||
| 259 | local ok, err | ||
| 260 | local lua_modules = {} | ||
| 261 | local lib_modules = {} | ||
| 262 | local luadir = path.lua_dir(rockspec.name, rockspec.version) | ||
| 263 | local libdir = path.lib_dir(rockspec.name, rockspec.version) | ||
| 264 | |||
| 265 | local autolibs, autoincdirs, autolibdirs = autoextract_libs(rockspec.external_dependencies, rockspec.variables) | ||
| 266 | |||
| 267 | if not build.modules then | ||
| 268 | if rockspec:format_is_at_least("3.0") then | ||
| 269 | local install, copy_directories | ||
| 270 | build.modules, install, copy_directories = builtin.autodetect_modules(autolibs, autoincdirs, autolibdirs) | ||
| 271 | build.install = build.install or install | ||
| 272 | build.copy_directories = build.copy_directories or copy_directories | ||
| 273 | else | ||
| 274 | return nil, "Missing build.modules table" | ||
| 275 | end | ||
| 276 | end | ||
| 277 | |||
| 278 | local compile_temp_dir | ||
| 279 | |||
| 280 | local mkdir_cache = {} | ||
| 281 | local function cached_make_dir(name) | ||
| 282 | if name == "" or mkdir_cache[name] then | ||
| 283 | return true | ||
| 284 | end | ||
| 285 | mkdir_cache[name] = true | ||
| 286 | return fs.make_dir(name) | ||
| 287 | end | ||
| 288 | |||
| 289 | for name, info in pairs(build.modules) do | ||
| 290 | local moddir = path.module_to_path(name) | ||
| 291 | if type(info) == "string" then | ||
| 292 | local ext = info:match("%.([^.]+)$") | ||
| 293 | if ext == "lua" then | ||
| 294 | local filename = dir.base_name(info) | ||
| 295 | if filename == "init.lua" and not name:match("%.init$") then | ||
| 296 | moddir = path.module_to_path(name .. ".init") | ||
| 297 | else | ||
| 298 | local basename = name:match("([^.]+)$") | ||
| 299 | filename = basename .. ".lua" | ||
| 300 | end | ||
| 301 | local dest = dir.path(luadir, moddir, filename) | ||
| 302 | lua_modules[info] = dest | ||
| 303 | else | ||
| 304 | info = { info } | ||
| 305 | end | ||
| 306 | end | ||
| 307 | if type(info) == "table" then | ||
| 308 | if not checked_lua_h then | ||
| 309 | local ok, err, errcode = deps.check_lua_incdir(rockspec.variables) | ||
| 310 | if not ok then | ||
| 311 | return nil, err, errcode | ||
| 312 | end | ||
| 313 | |||
| 314 | if cfg.link_lua_explicitly then | ||
| 315 | local ok, err, errcode = deps.check_lua_libdir(rockspec.variables) | ||
| 316 | if not ok then | ||
| 317 | return nil, err, errcode | ||
| 318 | end | ||
| 319 | end | ||
| 320 | checked_lua_h = true | ||
| 321 | end | ||
| 322 | local objects = {} | ||
| 323 | local sources = info.sources | ||
| 324 | if info[1] then sources = info end | ||
| 325 | if type(sources) == "string" then sources = { sources } end | ||
| 326 | if not (type(sources) == "table") then | ||
| 327 | return nil, "error in rockspec: module '" .. name .. "' entry has no 'sources' list" | ||
| 328 | end | ||
| 329 | for _, source in ipairs(sources) do | ||
| 330 | if not (type(source) == "string") then | ||
| 331 | return nil, "error in rockspec: module '" .. name .. "' does not specify source correctly." | ||
| 332 | end | ||
| 333 | local object = source:gsub("%.[^.]*$", "." .. cfg.obj_extension) | ||
| 334 | if not object then | ||
| 335 | object = source .. "." .. cfg.obj_extension | ||
| 336 | end | ||
| 337 | ok = compile_object(object, source, info.defines, info.incdirs or autoincdirs) | ||
| 338 | if not ok then | ||
| 339 | return nil, "Failed compiling object " .. object | ||
| 340 | end | ||
| 341 | table.insert(objects, object) | ||
| 342 | end | ||
| 343 | |||
| 344 | if not compile_temp_dir then | ||
| 345 | compile_temp_dir = fs.make_temp_dir("build-" .. rockspec.package .. "-" .. rockspec.version) | ||
| 346 | util.schedule_function(fs.delete, compile_temp_dir) | ||
| 347 | end | ||
| 348 | |||
| 349 | local module_name = name:match("([^.]*)$") .. "." .. util.matchquote(cfg.lib_extension) | ||
| 350 | if moddir ~= "" then | ||
| 351 | module_name = dir.path(moddir, module_name) | ||
| 352 | end | ||
| 353 | |||
| 354 | local build_name = dir.path(compile_temp_dir, module_name) | ||
| 355 | local build_dir = dir.dir_name(build_name) | ||
| 356 | cached_make_dir(build_dir) | ||
| 357 | |||
| 358 | lib_modules[build_name] = dir.path(libdir, module_name) | ||
| 359 | ok = compile_library(build_name, objects, info.libraries, info.libdirs or autolibdirs, name) | ||
| 360 | if not ok then | ||
| 361 | return nil, "Failed compiling module " .. module_name | ||
| 362 | end | ||
| 363 | |||
| 364 | |||
| 365 | |||
| 366 | if cached_make_dir(dir.dir_name(module_name)) then | ||
| 367 | fs.copy(build_name, module_name) | ||
| 368 | end | ||
| 369 | |||
| 370 | |||
| 371 | |||
| 372 | |||
| 373 | |||
| 374 | |||
| 375 | |||
| 376 | |||
| 377 | |||
| 378 | |||
| 379 | |||
| 380 | |||
| 381 | end | ||
| 382 | end | ||
| 383 | if not no_install then | ||
| 384 | for _, mods in ipairs({ { tbl = lua_modules, perms = "read" }, { tbl = lib_modules, perms = "exec" } }) do | ||
| 385 | for name, dest in pairs(mods.tbl) do | ||
| 386 | cached_make_dir(dir.dir_name(dest)) | ||
| 387 | ok, err = fs.copy(name, dest, mods.perms) | ||
| 388 | if not ok then | ||
| 389 | return nil, "Failed installing " .. name .. " in " .. dest .. ": " .. err | ||
| 390 | end | ||
| 391 | end | ||
| 392 | end | ||
| 393 | if fs.is_dir("lua") then | ||
| 394 | ok, err = fs.copy_contents("lua", luadir) | ||
| 395 | if not ok then | ||
| 396 | return nil, "Failed copying contents of 'lua' directory: " .. err | ||
| 397 | end | ||
| 398 | end | ||
| 399 | end | ||
| 400 | return true | ||
| 401 | end | ||
| 402 | |||
| 403 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | local cmake = {CMakeBuild = {Install = {}, }, } | ||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | local fs = require("luarocks.fs") | ||
| 17 | local util = require("luarocks.util") | ||
| 18 | local cfg = require("luarocks.core.cfg") | ||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | function cmake.run(rockspec, no_install) | ||
| 27 | local build = rockspec.build | ||
| 28 | local variables = build.variables or {} | ||
| 29 | |||
| 30 | |||
| 31 | variables.CMAKE_MODULE_PATH = os.getenv("CMAKE_MODULE_PATH") | ||
| 32 | variables.CMAKE_LIBRARY_PATH = os.getenv("CMAKE_LIBRARY_PATH") | ||
| 33 | variables.CMAKE_INCLUDE_PATH = os.getenv("CMAKE_INCLUDE_PATH") | ||
| 34 | |||
| 35 | util.variable_substitutions(variables, rockspec.variables) | ||
| 36 | |||
| 37 | local ok, err_msg = fs.is_tool_available(rockspec.variables.CMAKE, "CMake") | ||
| 38 | if not ok then | ||
| 39 | return nil, err_msg | ||
| 40 | end | ||
| 41 | |||
| 42 | |||
| 43 | local build_cmake = build.cmake | ||
| 44 | if type(build_cmake) == "string" then | ||
| 45 | local cmake_handler = assert((io.open(fs.current_dir() .. "/CMakeLists.txt", "w"))) | ||
| 46 | cmake_handler:write(build.cmake) | ||
| 47 | cmake_handler:close() | ||
| 48 | end | ||
| 49 | |||
| 50 | |||
| 51 | local args = "" | ||
| 52 | |||
| 53 | |||
| 54 | if cfg.cmake_generator then | ||
| 55 | args = args .. ' -G"' .. cfg.cmake_generator .. '"' | ||
| 56 | elseif cfg.is_platform("windows") and cfg.target_cpu:match("x86_64$") then | ||
| 57 | args = args .. " -DCMAKE_GENERATOR_PLATFORM=x64" | ||
| 58 | end | ||
| 59 | |||
| 60 | for k, v in pairs(variables) do | ||
| 61 | args = args .. ' -D' .. k .. '="' .. tostring(v) .. '"' | ||
| 62 | end | ||
| 63 | |||
| 64 | if not fs.execute_string(rockspec.variables.CMAKE .. " -H. -Bbuild.luarocks " .. args) then | ||
| 65 | return nil, "Failed cmake." | ||
| 66 | end | ||
| 67 | |||
| 68 | local do_build, do_install | ||
| 69 | if rockspec:format_is_at_least("3.0") then | ||
| 70 | do_build = (build.build_pass == nil) and true or build.build_pass | ||
| 71 | do_install = (build.install_pass == nil) and true or build.install_pass | ||
| 72 | else | ||
| 73 | do_build = true | ||
| 74 | do_install = true | ||
| 75 | end | ||
| 76 | |||
| 77 | if do_build then | ||
| 78 | if not fs.execute_string(rockspec.variables.CMAKE .. " --build build.luarocks --config Release") then | ||
| 79 | return nil, "Failed building." | ||
| 80 | end | ||
| 81 | end | ||
| 82 | if do_install and not no_install then | ||
| 83 | if not fs.execute_string(rockspec.variables.CMAKE .. " --build build.luarocks --target install --config Release") then | ||
| 84 | return nil, "Failed installing." | ||
| 85 | end | ||
| 86 | end | ||
| 87 | |||
| 88 | return true | ||
| 89 | end | ||
| 90 | |||
| 91 | 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 @@ | |||
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | local command = {CommandBuild = {Install = {}, }, } | ||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | local fs = require("luarocks.fs") | ||
| 15 | local util = require("luarocks.util") | ||
| 16 | local cfg = require("luarocks.core.cfg") | ||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | function command.run(rockspec, not_install) | ||
| 25 | |||
| 26 | local build = rockspec.build | ||
| 27 | |||
| 28 | util.variable_substitutions(build, rockspec.variables) | ||
| 29 | |||
| 30 | local env = { | ||
| 31 | CC = cfg.variables.CC, | ||
| 32 | |||
| 33 | |||
| 34 | } | ||
| 35 | |||
| 36 | if build.build_command then | ||
| 37 | util.printout(build.build_command) | ||
| 38 | if not fs.execute_env(env, build.build_command) then | ||
| 39 | return nil, "Failed building." | ||
| 40 | end | ||
| 41 | end | ||
| 42 | if build.install_command and not not_install then | ||
| 43 | util.printout(build.install_command) | ||
| 44 | if not fs.execute_env(env, build.install_command) then | ||
| 45 | return nil, "Failed installing." | ||
| 46 | end | ||
| 47 | end | ||
| 48 | return true | ||
| 49 | end | ||
| 50 | |||
| 51 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | local make = {MakeBuild = {Install = {}, }, } | ||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | local fs = require("luarocks.fs") | ||
| 21 | local util = require("luarocks.util") | ||
| 22 | local cfg = require("luarocks.core.cfg") | ||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | local function make_pass(make_cmd, pass, target, variables) | ||
| 37 | local assignments = {} | ||
| 38 | for k, v in pairs(variables) do | ||
| 39 | table.insert(assignments, k .. "=" .. v) | ||
| 40 | end | ||
| 41 | if pass then | ||
| 42 | return fs.execute(make_cmd .. " " .. target, _tl_table_unpack(assignments)) | ||
| 43 | else | ||
| 44 | return true | ||
| 45 | end | ||
| 46 | end | ||
| 47 | |||
| 48 | |||
| 49 | |||
| 50 | |||
| 51 | |||
| 52 | function make.run(rockspec, not_install) | ||
| 53 | |||
| 54 | local build = rockspec.build | ||
| 55 | |||
| 56 | if build.build_pass == nil then build.build_pass = true end | ||
| 57 | if build.install_pass == nil then build.install_pass = true end | ||
| 58 | build.build_variables = build.build_variables or {} | ||
| 59 | build.install_variables = build.install_variables or {} | ||
| 60 | build.build_target = build.build_target or "" | ||
| 61 | build.install_target = build.install_target or "install" | ||
| 62 | local makefile = build.makefile or cfg.makefile | ||
| 63 | if makefile then | ||
| 64 | |||
| 65 | build.build_target = "-f " .. makefile .. " " .. build.build_target | ||
| 66 | build.install_target = "-f " .. makefile .. " " .. build.install_target | ||
| 67 | end | ||
| 68 | |||
| 69 | if build.variables then | ||
| 70 | for var, val in pairs(build.variables) do | ||
| 71 | build.build_variables[var] = val | ||
| 72 | build.install_variables[var] = val | ||
| 73 | end | ||
| 74 | end | ||
| 75 | |||
| 76 | util.warn_if_not_used(build.build_variables, { CFLAGS = true }, "variable %s was not passed in build_variables") | ||
| 77 | util.variable_substitutions(build.build_variables, rockspec.variables) | ||
| 78 | util.variable_substitutions(build.install_variables, rockspec.variables) | ||
| 79 | |||
| 80 | local auto_variables = { "CC" } | ||
| 81 | |||
| 82 | for _, variable in ipairs(auto_variables) do | ||
| 83 | if not build.build_variables[variable] then | ||
| 84 | build.build_variables[variable] = rockspec.variables[variable] | ||
| 85 | end | ||
| 86 | if not build.install_variables[variable] then | ||
| 87 | build.install_variables[variable] = rockspec.variables[variable] | ||
| 88 | end | ||
| 89 | end | ||
| 90 | |||
| 91 | |||
| 92 | local make_cmd = cfg.make or rockspec.variables.MAKE | ||
| 93 | |||
| 94 | local ok = make_pass(make_cmd, build.build_pass, build.build_target, build.build_variables) | ||
| 95 | if not ok then | ||
| 96 | return nil, "Failed building." | ||
| 97 | end | ||
| 98 | if not not_install then | ||
| 99 | ok = make_pass(make_cmd, build.install_pass, build.install_target, build.install_variables) | ||
| 100 | if not ok then | ||
| 101 | return nil, "Failed installing." | ||
| 102 | end | ||
| 103 | end | ||
| 104 | return true | ||
| 105 | end | ||
| 106 | |||
| 107 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local cmd = {Module = {}, } | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | local manif = require("luarocks.manif") | ||
| 16 | local config = require("luarocks.config") | ||
| 17 | local util = require("luarocks.util") | ||
| 18 | local path = require("luarocks.path") | ||
| 19 | local cfg = require("luarocks.core.cfg") | ||
| 20 | local dir = require("luarocks.dir") | ||
| 21 | local fun = require("luarocks.fun") | ||
| 22 | local fs = require("luarocks.fs") | ||
| 23 | local argparse = require("luarocks.vendor.argparse") | ||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | local hc_ok, hardcoded = pcall(require, "luarocks.core.hardcoded") | ||
| 38 | if not hc_ok then | ||
| 39 | hardcoded = {} | ||
| 40 | end | ||
| 41 | |||
| 42 | local program = util.this_program("luarocks") | ||
| 43 | |||
| 44 | cmd.errorcodes = { | ||
| 45 | OK = 0, | ||
| 46 | UNSPECIFIED = 1, | ||
| 47 | PERMISSIONDENIED = 2, | ||
| 48 | CONFIGFILE = 3, | ||
| 49 | LOCK = 4, | ||
| 50 | CRASH = 99, | ||
| 51 | } | ||
| 52 | |||
| 53 | local function check_popen() | ||
| 54 | local popen_ok, popen_result = pcall(io.popen, "") | ||
| 55 | if popen_ok then | ||
| 56 | if popen_result then | ||
| 57 | popen_result:close() | ||
| 58 | end | ||
| 59 | else | ||
| 60 | io.stderr:write("Your version of Lua does not support io.popen,\n") | ||
| 61 | io.stderr:write("which is required by LuaRocks. Please check your Lua installation.\n") | ||
| 62 | os.exit(cmd.errorcodes.UNSPECIFIED) | ||
| 63 | end | ||
| 64 | end | ||
| 65 | |||
| 66 | local process_tree_args | ||
| 67 | do | ||
| 68 | local function replace_tree(args, root, tree) | ||
| 69 | root = dir.normalize(root) | ||
| 70 | args.tree = root | ||
| 71 | path.use_tree(tree or root) | ||
| 72 | end | ||
| 73 | |||
| 74 | local function strip_trailing_slashes() | ||
| 75 | local cfg_root_dir = cfg.root_dir | ||
| 76 | if type(cfg_root_dir) == "string" then | ||
| 77 | cfg.root_dir = (cfg.root_dir):gsub("/+$", "") | ||
| 78 | else | ||
| 79 | (cfg.root_dir).root = (cfg.root_dir).root:gsub("/+$", "") | ||
| 80 | end | ||
| 81 | cfg.rocks_dir = cfg.rocks_dir:gsub("/+$", "") | ||
| 82 | cfg.deploy_bin_dir = cfg.deploy_bin_dir:gsub("/+$", "") | ||
| 83 | cfg.deploy_lua_dir = cfg.deploy_lua_dir:gsub("/+$", "") | ||
| 84 | cfg.deploy_lib_dir = cfg.deploy_lib_dir:gsub("/+$", "") | ||
| 85 | end | ||
| 86 | |||
| 87 | local function set_named_tree(args, name) | ||
| 88 | for _, tree in ipairs(cfg.rocks_trees) do | ||
| 89 | if type(tree) == "table" and name == tree.name then | ||
| 90 | if not tree.root then | ||
| 91 | return nil, "Configuration error: tree '" .. tree.name .. "' has no 'root' field." | ||
| 92 | end | ||
| 93 | replace_tree(args, tree.root, tree) | ||
| 94 | return true | ||
| 95 | end | ||
| 96 | end | ||
| 97 | return false | ||
| 98 | end | ||
| 99 | |||
| 100 | process_tree_args = function(args, project_dir) | ||
| 101 | |||
| 102 | if args.global then | ||
| 103 | local ok, err = set_named_tree(args, "system") | ||
| 104 | if not ok then | ||
| 105 | return nil, err | ||
| 106 | end | ||
| 107 | elseif args.tree then | ||
| 108 | local named = set_named_tree(args, args.tree) | ||
| 109 | if not named then | ||
| 110 | local root_dir = fs.absolute_name(args.tree) | ||
| 111 | replace_tree(args, root_dir) | ||
| 112 | if (args.deps_mode or cfg.deps_mode) ~= "order" then | ||
| 113 | table.insert(cfg.rocks_trees, 1, { name = "arg", root = root_dir }) | ||
| 114 | end | ||
| 115 | end | ||
| 116 | elseif args["local"] then | ||
| 117 | if fs.is_superuser() then | ||
| 118 | return nil, "The --local flag is meant for operating in a user's home directory.\n" .. | ||
| 119 | "You are running as a superuser, which is intended for system-wide operation.\n" .. | ||
| 120 | "To force using the superuser's home, use --tree explicitly." | ||
| 121 | else | ||
| 122 | local ok, err = set_named_tree(args, "user") | ||
| 123 | if not ok then | ||
| 124 | return nil, err | ||
| 125 | end | ||
| 126 | end | ||
| 127 | elseif args.project_tree then | ||
| 128 | local tree = args.project_tree | ||
| 129 | table.insert(cfg.rocks_trees, 1, { name = "project", root = tree }) | ||
| 130 | manif.load_rocks_tree_manifests() | ||
| 131 | path.use_tree(tree) | ||
| 132 | elseif project_dir then | ||
| 133 | local project_tree = project_dir .. "/lua_modules" | ||
| 134 | table.insert(cfg.rocks_trees, 1, { name = "project", root = project_tree }) | ||
| 135 | manif.load_rocks_tree_manifests() | ||
| 136 | path.use_tree(project_tree) | ||
| 137 | elseif cfg.local_by_default then | ||
| 138 | local ok, err = set_named_tree(args, "user") | ||
| 139 | if not ok then | ||
| 140 | return nil, err | ||
| 141 | end | ||
| 142 | else | ||
| 143 | local trees = cfg.rocks_trees | ||
| 144 | path.use_tree(trees[#trees]) | ||
| 145 | end | ||
| 146 | |||
| 147 | strip_trailing_slashes() | ||
| 148 | |||
| 149 | cfg.variables.ROCKS_TREE = cfg.rocks_dir | ||
| 150 | cfg.variables.SCRIPTS_DIR = cfg.deploy_bin_dir | ||
| 151 | |||
| 152 | return true | ||
| 153 | end | ||
| 154 | end | ||
| 155 | |||
| 156 | local function process_server_args(args) | ||
| 157 | if args.server then | ||
| 158 | local protocol, pathname = dir.split_url(args.server) | ||
| 159 | table.insert(cfg.rocks_servers, 1, protocol .. "://" .. pathname) | ||
| 160 | end | ||
| 161 | |||
| 162 | if args.dev then | ||
| 163 | for i, server in ipairs(cfg.rocks_servers) do | ||
| 164 | if type(server) == "string" then | ||
| 165 | cfg.rocks_servers[i] = dir.path(server, "dev") | ||
| 166 | else | ||
| 167 | for j, mirror in ipairs(server) do | ||
| 168 | server[j] = dir.path(mirror, "dev") | ||
| 169 | end | ||
| 170 | end | ||
| 171 | end | ||
| 172 | end | ||
| 173 | |||
| 174 | if args.only_server then | ||
| 175 | if args.dev then | ||
| 176 | return nil, "--only-server cannot be used with --dev" | ||
| 177 | end | ||
| 178 | if args.server then | ||
| 179 | return nil, "--only-server cannot be used with --server" | ||
| 180 | end | ||
| 181 | cfg.rocks_servers = { args.only_server } | ||
| 182 | end | ||
| 183 | |||
| 184 | return true | ||
| 185 | end | ||
| 186 | |||
| 187 | local function error_handler(err) | ||
| 188 | if not debug then | ||
| 189 | return err | ||
| 190 | end | ||
| 191 | local mode = "Arch.: " .. (cfg and cfg.arch or "unknown") | ||
| 192 | if package.config:sub(1, 1) == "\\" then | ||
| 193 | if cfg and cfg.fs_use_modules then | ||
| 194 | mode = mode .. " (fs_use_modules = true)" | ||
| 195 | end | ||
| 196 | end | ||
| 197 | if cfg and cfg.is_binary then | ||
| 198 | mode = mode .. " (binary)" | ||
| 199 | end | ||
| 200 | return debug.traceback("LuaRocks " .. cfg.program_version .. | ||
| 201 | " bug (please report at https://github.com/luarocks/luarocks/issues).\n" .. | ||
| 202 | mode .. "\n" .. err, 2) | ||
| 203 | end | ||
| 204 | |||
| 205 | |||
| 206 | |||
| 207 | |||
| 208 | local function die(message, exitcode) | ||
| 209 | assert(type(message) == "string", "bad error, expected string, got: " .. type(message)) | ||
| 210 | assert(exitcode == nil or type(exitcode) == "number", "bad error, expected number, got: " .. type(exitcode) .. " - " .. tostring(exitcode)) | ||
| 211 | util.printerr("\nError: " .. message) | ||
| 212 | |||
| 213 | local ok, err = xpcall(util.run_scheduled_functions, error_handler) | ||
| 214 | if not ok then | ||
| 215 | util.printerr("\nError: " .. err) | ||
| 216 | exitcode = cmd.errorcodes.CRASH | ||
| 217 | end | ||
| 218 | |||
| 219 | os.exit(exitcode or cmd.errorcodes.UNSPECIFIED) | ||
| 220 | end | ||
| 221 | |||
| 222 | local function search_lua(lua_version, verbose, search_at) | ||
| 223 | if search_at then | ||
| 224 | return util.find_lua(search_at, lua_version, verbose) | ||
| 225 | end | ||
| 226 | |||
| 227 | local path_sep = (package.config:sub(1, 1) == "\\" and ";" or ":") | ||
| 228 | local all_tried = {} | ||
| 229 | for bindir in (os.getenv("PATH") or ""):gmatch("[^" .. path_sep .. "]+") do | ||
| 230 | local searchdir = (bindir:gsub("[\\/]+bin[\\/]?$", "")) | ||
| 231 | local detected, tried = util.find_lua(searchdir, lua_version) | ||
| 232 | if detected then | ||
| 233 | return detected | ||
| 234 | else | ||
| 235 | table.insert(all_tried, tried) | ||
| 236 | end | ||
| 237 | end | ||
| 238 | return nil, "Could not find " .. | ||
| 239 | (lua_version and "Lua " .. lua_version or "Lua") .. | ||
| 240 | " in PATH." .. | ||
| 241 | (verbose and " Tried:\n" .. table.concat(all_tried, "\n") or "") | ||
| 242 | end | ||
| 243 | |||
| 244 | local init_config | ||
| 245 | do | ||
| 246 | local detect_config_via_args | ||
| 247 | do | ||
| 248 | local function find_project_dir(project_tree) | ||
| 249 | if project_tree then | ||
| 250 | return project_tree:gsub("[/\\][^/\\]+$", ""), true | ||
| 251 | else | ||
| 252 | local try = "." | ||
| 253 | for _ = 1, 10 do | ||
| 254 | if util.exists(try .. "/.luarocks") and util.exists(try .. "/lua_modules") then | ||
| 255 | return dir.normalize(try), false | ||
| 256 | elseif util.exists(try .. "/.luarocks-no-project") then | ||
| 257 | break | ||
| 258 | end | ||
| 259 | try = try .. "/.." | ||
| 260 | end | ||
| 261 | end | ||
| 262 | return nil | ||
| 263 | end | ||
| 264 | |||
| 265 | local function find_default_lua_version(args, project_dir) | ||
| 266 | if hardcoded.FORCE_CONFIG then | ||
| 267 | return nil | ||
| 268 | end | ||
| 269 | |||
| 270 | local dirs = {} | ||
| 271 | if project_dir then | ||
| 272 | table.insert(dirs, dir.path(project_dir, ".luarocks")) | ||
| 273 | end | ||
| 274 | if cfg.homeconfdir then | ||
| 275 | table.insert(dirs, cfg.homeconfdir) | ||
| 276 | end | ||
| 277 | table.insert(dirs, cfg.sysconfdir) | ||
| 278 | for _, d in ipairs(dirs) do | ||
| 279 | local f = dir.path(d, "default-lua-version.lua") | ||
| 280 | local mod, _ = loadfile(f, "t") | ||
| 281 | if mod then | ||
| 282 | local pok, ver = pcall(mod) | ||
| 283 | if pok and type(ver) == "string" and ver:match("%d+.%d+") then | ||
| 284 | if args.verbose then | ||
| 285 | util.printout("Defaulting to Lua " .. ver .. " based on " .. f .. " ...") | ||
| 286 | end | ||
| 287 | return ver | ||
| 288 | end | ||
| 289 | end | ||
| 290 | end | ||
| 291 | return nil | ||
| 292 | end | ||
| 293 | |||
| 294 | local function find_version_from_config(dirname) | ||
| 295 | return fun.find(util.lua_versions("descending"), function(v) | ||
| 296 | if util.exists(dir.path(dirname, ".luarocks", "config-" .. v .. ".lua")) then | ||
| 297 | return v | ||
| 298 | end | ||
| 299 | end) | ||
| 300 | end | ||
| 301 | |||
| 302 | local function detect_lua_via_args(args, project_dir) | ||
| 303 | local lua_version = args.lua_version or | ||
| 304 | find_default_lua_version(args, project_dir) or | ||
| 305 | (project_dir and find_version_from_config(project_dir)) | ||
| 306 | |||
| 307 | if args.lua_dir then | ||
| 308 | local detected, err = util.find_lua(args.lua_dir, lua_version) | ||
| 309 | if not detected then | ||
| 310 | local suggestion = (not args.lua_version) and | ||
| 311 | "\nYou may want to specify a different Lua version with --lua-version\n" or | ||
| 312 | "" | ||
| 313 | die(err .. suggestion) | ||
| 314 | end | ||
| 315 | return detected | ||
| 316 | end | ||
| 317 | |||
| 318 | if lua_version then | ||
| 319 | local detected = search_lua(lua_version) | ||
| 320 | if detected then | ||
| 321 | return detected | ||
| 322 | end | ||
| 323 | return { | ||
| 324 | lua_version = lua_version, | ||
| 325 | } | ||
| 326 | end | ||
| 327 | |||
| 328 | return {} | ||
| 329 | end | ||
| 330 | |||
| 331 | detect_config_via_args = function(args) | ||
| 332 | local project_dir, given | ||
| 333 | if not args.no_project then | ||
| 334 | project_dir, given = find_project_dir(args.project_tree) | ||
| 335 | end | ||
| 336 | |||
| 337 | local detected = detect_lua_via_args(args, project_dir) | ||
| 338 | if args.lua_version then | ||
| 339 | detected.given_lua_version = args.lua_version | ||
| 340 | end | ||
| 341 | if args.lua_dir then | ||
| 342 | detected.given_lua_dir = args.lua_dir | ||
| 343 | end | ||
| 344 | if given then | ||
| 345 | detected.given_project_dir = project_dir | ||
| 346 | end | ||
| 347 | detected.project_dir = project_dir | ||
| 348 | return detected | ||
| 349 | end | ||
| 350 | end | ||
| 351 | |||
| 352 | init_config = function(args) | ||
| 353 | local detected = detect_config_via_args(args) | ||
| 354 | |||
| 355 | local ok, err = cfg.init(detected, util.warning) | ||
| 356 | if not ok then | ||
| 357 | return nil, err | ||
| 358 | end | ||
| 359 | |||
| 360 | return (detected.lua_dir ~= nil) | ||
| 361 | end | ||
| 362 | end | ||
| 363 | |||
| 364 | local variables_help = [[ | ||
| 365 | Variables: | ||
| 366 | Variables from the "variables" table of the configuration file can be | ||
| 367 | overridden with VAR=VALUE assignments. | ||
| 368 | |||
| 369 | ]] | ||
| 370 | |||
| 371 | local lua_example = package.config:sub(1, 1) == "\\" and | ||
| 372 | "<d:\\path\\lua.exe>" or | ||
| 373 | "</path/lua>" | ||
| 374 | |||
| 375 | local function show_status(file, status, err) | ||
| 376 | return (file and file .. " " or "") .. (status and "(ok)" or ("(" .. (err or "not found") .. ")")) | ||
| 377 | end | ||
| 378 | |||
| 379 | local function use_to_fix_location(key, what) | ||
| 380 | local buf = " ****************************************\n" | ||
| 381 | buf = buf .. " Use the command\n\n" | ||
| 382 | buf = buf .. " luarocks config " .. key .. " " .. (what or "<dir>") .. "\n\n" | ||
| 383 | buf = buf .. " to fix the location\n" | ||
| 384 | buf = buf .. " ****************************************\n" | ||
| 385 | return buf | ||
| 386 | end | ||
| 387 | |||
| 388 | local function get_config_text(cfg) | ||
| 389 | local deps = require("luarocks.deps") | ||
| 390 | |||
| 391 | local libdir_ok = deps.check_lua_libdir(cfg.variables) | ||
| 392 | local incdir_ok = deps.check_lua_incdir(cfg.variables) | ||
| 393 | local lua_ok = cfg.variables.LUA and fs.exists(cfg.variables.LUA) | ||
| 394 | |||
| 395 | local buf = "Configuration:\n" | ||
| 396 | buf = buf .. " Lua:\n" | ||
| 397 | buf = buf .. " Version : " .. cfg.lua_version .. "\n" | ||
| 398 | if cfg.luajit_version then | ||
| 399 | buf = buf .. " LuaJIT : " .. cfg.luajit_version .. "\n" | ||
| 400 | end | ||
| 401 | buf = buf .. " LUA : " .. show_status(cfg.variables.LUA, lua_ok, "interpreter not found") .. "\n" | ||
| 402 | if not lua_ok then | ||
| 403 | buf = buf .. use_to_fix_location("variables.LUA", lua_example) | ||
| 404 | end | ||
| 405 | buf = buf .. " LUA_INCDIR : " .. show_status(cfg.variables.LUA_INCDIR, incdir_ok, "lua.h not found") .. "\n" | ||
| 406 | if lua_ok and not incdir_ok then | ||
| 407 | buf = buf .. use_to_fix_location("variables.LUA_INCDIR") | ||
| 408 | end | ||
| 409 | buf = buf .. " LUA_LIBDIR : " .. show_status(cfg.variables.LUA_LIBDIR, libdir_ok, "Lua library itself not found") .. "\n" | ||
| 410 | if lua_ok and not libdir_ok then | ||
| 411 | buf = buf .. use_to_fix_location("variables.LUA_LIBDIR") | ||
| 412 | end | ||
| 413 | |||
| 414 | buf = buf .. "\n Configuration files:\n" | ||
| 415 | local conf = cfg.config_files | ||
| 416 | buf = buf .. " System : " .. show_status(fs.absolute_name(conf.system.file), conf.system.found) .. "\n" | ||
| 417 | if conf.user.file then | ||
| 418 | buf = buf .. " User : " .. show_status(fs.absolute_name(conf.user.file), conf.user.found) .. "\n" | ||
| 419 | else | ||
| 420 | buf = buf .. " User : disabled in this LuaRocks installation.\n" | ||
| 421 | end | ||
| 422 | if conf.project then | ||
| 423 | buf = buf .. " Project : " .. show_status(fs.absolute_name(conf.project.file), conf.project.found) .. "\n" | ||
| 424 | end | ||
| 425 | buf = buf .. "\n Rocks trees in use: \n" | ||
| 426 | for _, tree in ipairs(cfg.rocks_trees) do | ||
| 427 | if type(tree) == "string" then | ||
| 428 | buf = buf .. " " .. fs.absolute_name(tree) | ||
| 429 | else | ||
| 430 | local name = tree.name and " (\"" .. tree.name .. "\")" or "" | ||
| 431 | buf = buf .. " " .. fs.absolute_name(tree.root) .. name | ||
| 432 | end | ||
| 433 | buf = buf .. "\n" | ||
| 434 | end | ||
| 435 | |||
| 436 | return buf | ||
| 437 | end | ||
| 438 | |||
| 439 | local function get_parser(description, cmd_modules) | ||
| 440 | local basename = dir.base_name(program) | ||
| 441 | local parser = argparse( | ||
| 442 | basename, "LuaRocks " .. cfg.program_version .. ", the Lua package manager\n\n" .. | ||
| 443 | program .. " - " .. description, variables_help .. "Run '" .. basename .. | ||
| 444 | "' without any arguments to see the configuration."): | ||
| 445 | help_max_width(80): | ||
| 446 | add_help_command(): | ||
| 447 | add_complete_command({ | ||
| 448 | help_max_width = 100, | ||
| 449 | summary = "Output a shell completion script.", | ||
| 450 | description = [[ | ||
| 451 | Output a shell completion script. | ||
| 452 | |||
| 453 | Enabling completions for Bash: | ||
| 454 | |||
| 455 | Add the following line to your ~/.bashrc: | ||
| 456 | source <(]] .. basename .. [[ completion bash) | ||
| 457 | or save the completion script to the local completion directory: | ||
| 458 | ]] .. basename .. [[ completion bash > ~/.local/share/bash-completion/completions/]] .. basename .. [[ | ||
| 459 | |||
| 460 | |||
| 461 | Enabling completions for Zsh: | ||
| 462 | |||
| 463 | Save the completion script to a file in your $fpath. | ||
| 464 | You can add a new directory to your $fpath by adding e.g. | ||
| 465 | fpath=(~/.zfunc $fpath) | ||
| 466 | to your ~/.zshrc. | ||
| 467 | Then run: | ||
| 468 | ]] .. basename .. [[ completion zsh > ~/.zfunc/_]] .. basename .. [[ | ||
| 469 | |||
| 470 | |||
| 471 | Enabling completion for Fish: | ||
| 472 | |||
| 473 | Add the following line to your ~/.config/fish/config.fish: | ||
| 474 | ]] .. basename .. [[ completion fish | source | ||
| 475 | or save the completion script to the local completion directory: | ||
| 476 | ]] .. basename .. [[ completion fish > ~/.config/fish/completions/]] .. basename .. [[.fish | ||
| 477 | ]], }): | ||
| 478 | command_target("command"): | ||
| 479 | require_command(false) | ||
| 480 | |||
| 481 | parser:flag("--version", "Show version info and exit."): | ||
| 482 | action(function() | ||
| 483 | util.printout(program .. " " .. cfg.program_version) | ||
| 484 | util.printout(description) | ||
| 485 | util.printout() | ||
| 486 | os.exit(cmd.errorcodes.OK) | ||
| 487 | end) | ||
| 488 | parser:flag("--dev", "Enable the sub-repositories in rocks servers for " .. | ||
| 489 | "rockspecs of in-development versions.") | ||
| 490 | parser:option("--server", "Fetch rocks/rockspecs from this server " .. | ||
| 491 | "(takes priority over config file)."): | ||
| 492 | hidden_name("--from") | ||
| 493 | parser:option("--only-server", "Fetch rocks/rockspecs from this server only " .. | ||
| 494 | "(overrides any entries in the config file)."): | ||
| 495 | argname("<server>"): | ||
| 496 | hidden_name("--only-from") | ||
| 497 | parser:option("--only-sources", "Restrict downloads to paths matching the given URL."): | ||
| 498 | argname("<url>"): | ||
| 499 | hidden_name("--only-sources-from") | ||
| 500 | parser:option("--namespace", "Specify the rocks server namespace to use."): | ||
| 501 | convert(string.lower) | ||
| 502 | parser:option("--lua-dir", "Which Lua installation to use."): | ||
| 503 | argname("<prefix>") | ||
| 504 | parser:option("--lua-version", "Which Lua version to use."): | ||
| 505 | argname("<ver>"): | ||
| 506 | convert(function(s) return (s:match("^%d+%.%d+$")) end) | ||
| 507 | parser:option("--tree", "Which tree to operate on."): | ||
| 508 | hidden_name("--to") | ||
| 509 | parser:flag("--local", "Use the tree in the user's home directory.\n" .. | ||
| 510 | "To enable it, see '" .. program .. " help path'.") | ||
| 511 | parser:flag("--global", "Use the system tree when `local_by_default` is `true`.") | ||
| 512 | parser:flag("--no-project", "Do not use project tree even if running from a project folder.") | ||
| 513 | parser:flag("--force-lock", "Attempt to overwrite the lock for commands " .. | ||
| 514 | "that require exclusive access, such as 'install'") | ||
| 515 | parser:flag("--verbose", "Display verbose output of commands executed.") | ||
| 516 | parser:option("--timeout", "Timeout on network operations, in seconds.\n" .. | ||
| 517 | "0 means no timeout (wait forever). Default is " .. | ||
| 518 | tostring(cfg.connection_timeout) .. "."): | ||
| 519 | argname("<seconds>"): | ||
| 520 | convert(tonumber) | ||
| 521 | |||
| 522 | |||
| 523 | parser:option("--project-tree"):hidden(true) | ||
| 524 | |||
| 525 | for _, module in util.sortedpairs(cmd_modules) do | ||
| 526 | module.add_to_parser(parser) | ||
| 527 | end | ||
| 528 | |||
| 529 | return parser | ||
| 530 | end | ||
| 531 | |||
| 532 | local function get_first_arg() | ||
| 533 | if not arg then | ||
| 534 | return | ||
| 535 | end | ||
| 536 | local first_arg = arg[0] | ||
| 537 | local i = -1 | ||
| 538 | while arg[i] do | ||
| 539 | first_arg = arg[i] | ||
| 540 | i = i - 1 | ||
| 541 | end | ||
| 542 | return first_arg | ||
| 543 | end | ||
| 544 | |||
| 545 | |||
| 546 | |||
| 547 | |||
| 548 | |||
| 549 | |||
| 550 | |||
| 551 | |||
| 552 | |||
| 553 | function cmd.run_command(description, commands, external_namespace, ...) | ||
| 554 | |||
| 555 | check_popen() | ||
| 556 | |||
| 557 | |||
| 558 | cfg.init() | ||
| 559 | |||
| 560 | fs.init() | ||
| 561 | |||
| 562 | for _, module_name in ipairs(fs.modules(external_namespace)) do | ||
| 563 | if not commands[module_name] then | ||
| 564 | commands[module_name] = external_namespace .. "." .. module_name | ||
| 565 | end | ||
| 566 | end | ||
| 567 | |||
| 568 | local cmd_modules = {} | ||
| 569 | for name, module in pairs(commands) do | ||
| 570 | local pok, mod = pcall(require, module) | ||
| 571 | if pok and type(mod) == "table" then | ||
| 572 | local original_command = mod.command | ||
| 573 | if original_command then | ||
| 574 | if not mod.add_to_parser then | ||
| 575 | mod.add_to_parser = function(parser) | ||
| 576 | parser:command(name, mod.help, util.see_also()): | ||
| 577 | summary(mod.help_summary): | ||
| 578 | handle_options(false): | ||
| 579 | argument("input"): | ||
| 580 | args("*") | ||
| 581 | end | ||
| 582 | |||
| 583 | mod.command = function(args) | ||
| 584 | return original_command(args, _tl_table_unpack(args.input)) | ||
| 585 | end | ||
| 586 | end | ||
| 587 | cmd_modules[name] = mod | ||
| 588 | else | ||
| 589 | util.warning("command module " .. module .. " does not implement command(), skipping") | ||
| 590 | end | ||
| 591 | else | ||
| 592 | util.warning("failed to load command module " .. module .. ": " .. tostring(mod)) | ||
| 593 | end | ||
| 594 | end | ||
| 595 | |||
| 596 | local function process_cmdline_vars(...) | ||
| 597 | local args = _tl_table_pack(...) | ||
| 598 | local cmdline_vars = {} | ||
| 599 | local last = args.n | ||
| 600 | for i = 1, args.n do | ||
| 601 | if args[i] == "--" then | ||
| 602 | last = i - 1 | ||
| 603 | break | ||
| 604 | end | ||
| 605 | end | ||
| 606 | for i = last, 1, -1 do | ||
| 607 | local arg = args[i] | ||
| 608 | if arg:match("^[^-][^=]*=") then | ||
| 609 | local var, val = arg:match("^([A-Z_][A-Z0-9_]*)=(.*)") | ||
| 610 | if val then | ||
| 611 | cmdline_vars[var] = val | ||
| 612 | table.remove(args, i) | ||
| 613 | else | ||
| 614 | die("Invalid assignment: " .. arg) | ||
| 615 | end | ||
| 616 | end | ||
| 617 | end | ||
| 618 | |||
| 619 | return args, cmdline_vars | ||
| 620 | end | ||
| 621 | |||
| 622 | local cmdline_args, cmdline_vars = process_cmdline_vars(...) | ||
| 623 | local parser = get_parser(description, cmd_modules) | ||
| 624 | local args = parser:parse(cmdline_args) | ||
| 625 | |||
| 626 | |||
| 627 | if args.nodeps then | ||
| 628 | args.deps_mode = "none" | ||
| 629 | end | ||
| 630 | |||
| 631 | if args.timeout then | ||
| 632 | cfg.connection_timeout = args.timeout | ||
| 633 | end | ||
| 634 | |||
| 635 | if args.command == "config" then | ||
| 636 | if args.key == "lua_version" and args.value then | ||
| 637 | args.lua_version = args.value | ||
| 638 | elseif args.key == "lua_dir" and args.value then | ||
| 639 | args.lua_dir = args.value | ||
| 640 | end | ||
| 641 | end | ||
| 642 | |||
| 643 | |||
| 644 | local lua_found, err = init_config(args) | ||
| 645 | if err then | ||
| 646 | die(err) | ||
| 647 | end | ||
| 648 | |||
| 649 | |||
| 650 | |||
| 651 | |||
| 652 | fs.init() | ||
| 653 | |||
| 654 | |||
| 655 | |||
| 656 | local tried | ||
| 657 | if not lua_found then | ||
| 658 | local detected | ||
| 659 | detected, tried = search_lua(cfg.lua_version, args.verbose, cfg.variables.LUA_DIR) | ||
| 660 | if detected then | ||
| 661 | lua_found = true | ||
| 662 | cfg.variables.LUA = detected.lua | ||
| 663 | cfg.variables.LUA_DIR = detected.lua_dir | ||
| 664 | cfg.variables.LUA_BINDIR = detected.lua_bindir | ||
| 665 | if args.lua_dir then | ||
| 666 | cfg.variables.LUA_INCDIR = nil | ||
| 667 | cfg.variables.LUA_LIBDIR = nil | ||
| 668 | end | ||
| 669 | else | ||
| 670 | cfg.variables.LUA = nil | ||
| 671 | cfg.variables.LUA_DIR = nil | ||
| 672 | cfg.variables.LUA_BINDIR = nil | ||
| 673 | cfg.variables.LUA_INCDIR = nil | ||
| 674 | cfg.variables.LUA_LIBDIR = nil | ||
| 675 | end | ||
| 676 | end | ||
| 677 | |||
| 678 | if lua_found then | ||
| 679 | assert(cfg.variables.LUA) | ||
| 680 | else | ||
| 681 | |||
| 682 | |||
| 683 | |||
| 684 | |||
| 685 | if not cfg.variables.LUA then | ||
| 686 | local first_arg = get_first_arg() | ||
| 687 | local bin_dir = dir.dir_name(fs.absolute_name(first_arg)) | ||
| 688 | local exe = dir.base_name(first_arg) | ||
| 689 | exe = exe:match("rocks") and ("lua" .. (cfg.arch:match("win") and ".exe" or "")) or exe | ||
| 690 | local full_path = dir.path(bin_dir, exe) | ||
| 691 | if util.check_lua_version(full_path, cfg.lua_version) then | ||
| 692 | cfg.variables.LUA = dir.path(bin_dir, exe) | ||
| 693 | cfg.variables.LUA_DIR = bin_dir:gsub("[/\\]bin[/\\]?$", "") | ||
| 694 | cfg.variables.LUA_BINDIR = bin_dir | ||
| 695 | cfg.variables.LUA_INCDIR = nil | ||
| 696 | cfg.variables.LUA_LIBDIR = nil | ||
| 697 | end | ||
| 698 | end | ||
| 699 | end | ||
| 700 | |||
| 701 | cfg.lua_found = lua_found | ||
| 702 | |||
| 703 | if cfg.project_dir then | ||
| 704 | cfg.project_dir = fs.absolute_name(cfg.project_dir) | ||
| 705 | end | ||
| 706 | |||
| 707 | if args.verbose then | ||
| 708 | cfg.verbose = true | ||
| 709 | print(("-"):rep(79)) | ||
| 710 | print("Current configuration:") | ||
| 711 | print(("-"):rep(79)) | ||
| 712 | print(config.to_string(cfg)) | ||
| 713 | print(("-"):rep(79)) | ||
| 714 | fs.verbose() | ||
| 715 | end | ||
| 716 | |||
| 717 | if (not fs.current_dir()) or fs.current_dir() == "" then | ||
| 718 | die("Current directory does not exist. Please run LuaRocks from an existing directory.") | ||
| 719 | end | ||
| 720 | |||
| 721 | local ok, err = process_tree_args(args, cfg.project_dir) | ||
| 722 | if not ok then | ||
| 723 | die(err) | ||
| 724 | end | ||
| 725 | |||
| 726 | ok, err = process_server_args(args) | ||
| 727 | if not ok then | ||
| 728 | die(err) | ||
| 729 | end | ||
| 730 | |||
| 731 | if args.only_sources then | ||
| 732 | cfg.only_sources_from = args.only_sources | ||
| 733 | end | ||
| 734 | |||
| 735 | for k, v in pairs(cmdline_vars) do | ||
| 736 | cfg.variables[k] = v | ||
| 737 | end | ||
| 738 | |||
| 739 | |||
| 740 | if fs.is_superuser() then | ||
| 741 | cfg.local_cache = dir.path(fs.system_cache_dir(), "luarocks") | ||
| 742 | end | ||
| 743 | |||
| 744 | if args.no_manifest then | ||
| 745 | cfg.no_manifest = true | ||
| 746 | end | ||
| 747 | |||
| 748 | if not args.command then | ||
| 749 | parser:epilog(variables_help .. get_config_text(cfg)) | ||
| 750 | util.printout() | ||
| 751 | util.printout(parser:get_help()) | ||
| 752 | util.printout() | ||
| 753 | os.exit(cmd.errorcodes.OK) | ||
| 754 | end | ||
| 755 | |||
| 756 | if not cfg.variables["LUA"] and args.command ~= "config" and args.command ~= "help" then | ||
| 757 | local flag = (not cfg.project_tree) and | ||
| 758 | "--local " or | ||
| 759 | "" | ||
| 760 | if args.lua_version then | ||
| 761 | flag = "--lua-version=" .. args.lua_version .. " " .. flag | ||
| 762 | end | ||
| 763 | die((tried or "Lua interpreter not found.") .. | ||
| 764 | "\nPlease set your Lua interpreter with:\n\n" .. | ||
| 765 | " luarocks " .. flag .. "config variables.LUA " .. lua_example .. "\n") | ||
| 766 | end | ||
| 767 | |||
| 768 | local cmd_mod = cmd_modules[args.command] | ||
| 769 | |||
| 770 | local lock | ||
| 771 | if cmd_mod.needs_lock and cmd_mod.needs_lock(args) then | ||
| 772 | local ok, err = fs.check_command_permissions(args) | ||
| 773 | if not ok then | ||
| 774 | die(err, cmd.errorcodes.PERMISSIONDENIED) | ||
| 775 | end | ||
| 776 | |||
| 777 | lock, err = fs.lock_access(path.root_dir(cfg.root_dir), args.force_lock) | ||
| 778 | if not lock then | ||
| 779 | err = args.force_lock and | ||
| 780 | ("failed to force the lock" .. (err and ": " .. err or "")) or | ||
| 781 | (err and err ~= "File exists") and | ||
| 782 | err or | ||
| 783 | "try --force-lock to overwrite the lock" | ||
| 784 | |||
| 785 | die("command '" .. args.command .. "' " .. | ||
| 786 | "requires exclusive write access to " .. path.root_dir(cfg.root_dir) .. " - " .. | ||
| 787 | err, cmd.errorcodes.LOCK) | ||
| 788 | end | ||
| 789 | end | ||
| 790 | |||
| 791 | local call_ok, ok, err, exitcode = xpcall(function() | ||
| 792 | return cmd_mod.command(args) | ||
| 793 | end, error_handler) | ||
| 794 | |||
| 795 | if lock then | ||
| 796 | fs.unlock_access(lock) | ||
| 797 | end | ||
| 798 | |||
| 799 | if not call_ok then | ||
| 800 | die(tostring(ok), cmd.errorcodes.CRASH) | ||
| 801 | elseif not ok then | ||
| 802 | die(err, exitcode) | ||
| 803 | end | ||
| 804 | util.run_scheduled_functions() | ||
| 805 | end | ||
| 806 | |||
| 807 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local cmd_build = {} | ||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | local pack = require("luarocks.pack") | ||
| 9 | local path = require("luarocks.path") | ||
| 10 | local dir = require("luarocks.dir") | ||
| 11 | local util = require("luarocks.util") | ||
| 12 | local fetch = require("luarocks.fetch") | ||
| 13 | local fs = require("luarocks.fs") | ||
| 14 | local deps = require("luarocks.deps") | ||
| 15 | local remove = require("luarocks.remove") | ||
| 16 | local cfg = require("luarocks.core.cfg") | ||
| 17 | local build = require("luarocks.build") | ||
| 18 | local search = require("luarocks.search") | ||
| 19 | local make = require("luarocks.cmd.make") | ||
| 20 | local repos = require("luarocks.repos") | ||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | function cmd_build.add_to_parser(parser) | ||
| 31 | local cmd = parser:command("build", "Build and install a rock, compiling its C parts if any.\n" .. | ||
| 32 | "If the sources contain a luarocks.lock file, uses it as an authoritative source for " .. | ||
| 33 | "exact version of dependencies.\n" .. | ||
| 34 | "If no arguments are given, behaves as luarocks make.", util.see_also()): | ||
| 35 | summary("Build/compile a rock.") | ||
| 36 | |||
| 37 | cmd:argument("rock", "A rockspec file, a source rock file, or the name of " .. | ||
| 38 | "a rock to be fetched from a repository."): | ||
| 39 | args("?"): | ||
| 40 | action(util.namespaced_name_action) | ||
| 41 | cmd:argument("version", "Rock version."): | ||
| 42 | args("?") | ||
| 43 | |||
| 44 | cmd:flag("--only-deps --deps-only", "Install only the dependencies of the rock.") | ||
| 45 | cmd:option("--branch", "Override the `source.branch` field in the loaded " .. | ||
| 46 | "rockspec. Allows to specify a different branch to fetch. Particularly " .. | ||
| 47 | 'for "dev" rocks.'): | ||
| 48 | argname("<name>") | ||
| 49 | cmd:flag("--pin", "Create a luarocks.lock file listing the exact " .. | ||
| 50 | "versions of each dependency found for this rock (recursively), " .. | ||
| 51 | "and store it in the rock's directory. " .. | ||
| 52 | "Ignores any existing luarocks.lock file in the rock's sources.") | ||
| 53 | make.cmd_options(cmd) | ||
| 54 | end | ||
| 55 | |||
| 56 | |||
| 57 | |||
| 58 | |||
| 59 | |||
| 60 | |||
| 61 | local function build_rock(rock_filename, opts) | ||
| 62 | |||
| 63 | local cwd = fs.absolute_name(dir.path(".")) | ||
| 64 | |||
| 65 | local ok, err, errcode | ||
| 66 | |||
| 67 | local unpack_dir | ||
| 68 | unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_filename, nil, opts.verify) | ||
| 69 | if not unpack_dir then | ||
| 70 | return nil, err, errcode | ||
| 71 | end | ||
| 72 | |||
| 73 | local rockspec_filename = path.rockspec_name_from_rock(rock_filename) | ||
| 74 | |||
| 75 | ok, err = fs.change_dir(unpack_dir) | ||
| 76 | if not ok then return nil, err end | ||
| 77 | |||
| 78 | local rockspec | ||
| 79 | rockspec, err, errcode = fetch.load_rockspec(rockspec_filename) | ||
| 80 | if not rockspec then | ||
| 81 | return nil, err, errcode | ||
| 82 | end | ||
| 83 | |||
| 84 | local n, v = build.build_rockspec(rockspec, opts, cwd) | ||
| 85 | |||
| 86 | ok, err, errcode = n ~= nil, v, nil | ||
| 87 | |||
| 88 | fs.pop_dir() | ||
| 89 | return ok, err, errcode | ||
| 90 | end | ||
| 91 | |||
| 92 | local function do_build(name, namespace, version, opts) | ||
| 93 | |||
| 94 | local url, err | ||
| 95 | if name:match("%.rockspec$") or name:match("%.rock$") then | ||
| 96 | url = name | ||
| 97 | else | ||
| 98 | url, err = search.find_src_or_rockspec(name, namespace, version, opts.check_lua_versions) | ||
| 99 | if not url then | ||
| 100 | return nil, err | ||
| 101 | end | ||
| 102 | end | ||
| 103 | |||
| 104 | name, version = path.parse_name(url) | ||
| 105 | if name and repos.is_installed(name, version) then | ||
| 106 | if not opts.rebuild then | ||
| 107 | util.printout(name .. " " .. version .. " is already installed in " .. path.root_dir(cfg.root_dir)) | ||
| 108 | util.printout("Use --force to reinstall.") | ||
| 109 | return name, version, "skip" | ||
| 110 | end | ||
| 111 | end | ||
| 112 | |||
| 113 | if url:match("%.rockspec$") then | ||
| 114 | local cwd = fs.absolute_name(dir.path(".")) | ||
| 115 | local rockspec, err = fetch.load_rockspec(url, nil, opts.verify) | ||
| 116 | if not rockspec then | ||
| 117 | return nil, err | ||
| 118 | end | ||
| 119 | return build.build_rockspec(rockspec, opts, cwd) | ||
| 120 | end | ||
| 121 | |||
| 122 | if url:match("%.src%.rock$") then | ||
| 123 | opts.need_to_fetch = false | ||
| 124 | end | ||
| 125 | |||
| 126 | local ok, err, errcode = build_rock(url, opts) | ||
| 127 | if not ok then | ||
| 128 | return nil, err, errcode | ||
| 129 | end | ||
| 130 | return name, version | ||
| 131 | end | ||
| 132 | |||
| 133 | |||
| 134 | |||
| 135 | |||
| 136 | |||
| 137 | |||
| 138 | |||
| 139 | function cmd_build.command(args) | ||
| 140 | if not args.rock then | ||
| 141 | return make.command(args) | ||
| 142 | end | ||
| 143 | |||
| 144 | local opts = { | ||
| 145 | need_to_fetch = true, | ||
| 146 | minimal_mode = false, | ||
| 147 | deps_mode = deps.get_deps_mode(args), | ||
| 148 | build_only_deps = not not (args.only_deps and not args.pack_binary_rock), | ||
| 149 | namespace = args.namespace, | ||
| 150 | branch = args.branch, | ||
| 151 | verify = not not args.verify, | ||
| 152 | check_lua_versions = not not args.check_lua_versions, | ||
| 153 | pin = not not args.pin, | ||
| 154 | rebuild = not not (args.force or args.force_fast), | ||
| 155 | no_install = false, | ||
| 156 | } | ||
| 157 | |||
| 158 | if args.sign and not args.pack_binary_rock then | ||
| 159 | return nil, "In the build command, --sign is meant to be used only with --pack-binary-rock" | ||
| 160 | end | ||
| 161 | |||
| 162 | if args.pack_binary_rock then | ||
| 163 | return pack.pack_binary_rock(args.rock, args.namespace, args.version, args.sign, function() | ||
| 164 | local name, version = do_build(args.rock, args.namespace, args.version, opts) | ||
| 165 | if name and args.no_doc then | ||
| 166 | util.remove_doc_dir(name, version) | ||
| 167 | end | ||
| 168 | return name, version | ||
| 169 | end) | ||
| 170 | end | ||
| 171 | |||
| 172 | local name, version, skip = do_build(args.rock, args.namespace, args.version, opts) | ||
| 173 | if not name then | ||
| 174 | return nil, version | ||
| 175 | end | ||
| 176 | if skip == "skip" then | ||
| 177 | return name ~= nil, version | ||
| 178 | end | ||
| 179 | |||
| 180 | if args.no_doc then | ||
| 181 | util.remove_doc_dir(name, version) | ||
| 182 | end | ||
| 183 | |||
| 184 | if opts.build_only_deps then | ||
| 185 | util.printout("Stopping after installing dependencies for " .. name .. " " .. version) | ||
| 186 | util.printout() | ||
| 187 | else | ||
| 188 | if (not args.keep) and not cfg.keep_other_versions then | ||
| 189 | local ok, err, warn = remove.remove_other_versions(name, version, args.force, args.force_fast) | ||
| 190 | if not ok then | ||
| 191 | return nil, err | ||
| 192 | elseif warn then | ||
| 193 | util.printerr(err) | ||
| 194 | end | ||
| 195 | end | ||
| 196 | end | ||
| 197 | |||
| 198 | if opts.deps_mode ~= "none" then | ||
| 199 | deps.check_dependencies(nil, deps.get_deps_mode(args)) | ||
| 200 | end | ||
| 201 | return name ~= nil, version | ||
| 202 | end | ||
| 203 | |||
| 204 | cmd_build.needs_lock = function(args) | ||
| 205 | if args.pack_binary_rock then | ||
| 206 | return false | ||
| 207 | end | ||
| 208 | return true | ||
| 209 | end | ||
| 210 | |||
| 211 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local config_cmd = {} | ||
| 4 | |||
| 5 | |||
| 6 | local persist = require("luarocks.persist") | ||
| 7 | local config = require("luarocks.config") | ||
| 8 | local cfg = require("luarocks.core.cfg") | ||
| 9 | local util = require("luarocks.util") | ||
| 10 | local deps = require("luarocks.deps") | ||
| 11 | local dir = require("luarocks.dir") | ||
| 12 | local fs = require("luarocks.fs") | ||
| 13 | local json = require("luarocks.vendor.dkjson") | ||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | function config_cmd.add_to_parser(parser) | ||
| 22 | local cmd = parser:command("config", [[ | ||
| 23 | Query information about the LuaRocks configuration. | ||
| 24 | |||
| 25 | * When given a configuration key, it prints the value of that key according to | ||
| 26 | the currently active configuration (taking into account all config files and | ||
| 27 | any command-line flags passed) | ||
| 28 | |||
| 29 | Examples: | ||
| 30 | luarocks config variables.LUA_INCDIR | ||
| 31 | luarocks config lua_version | ||
| 32 | |||
| 33 | * When given a configuration key and a value, it overwrites the config file (see | ||
| 34 | the --scope option below to determine which) and replaces the value of the | ||
| 35 | given key with the given value. | ||
| 36 | |||
| 37 | * `lua_dir` is a special key as it checks for a valid Lua installation | ||
| 38 | (equivalent to --lua-dir) and sets several keys at once. | ||
| 39 | * `lua_version` is a special key as it changes the default Lua version | ||
| 40 | used by LuaRocks commands (equivalent to passing --lua-version). | ||
| 41 | |||
| 42 | Examples: | ||
| 43 | luarocks config variables.OPENSSL_DIR /usr/local/openssl | ||
| 44 | luarocks config lua_dir /usr/local | ||
| 45 | luarocks config lua_version 5.3 | ||
| 46 | |||
| 47 | * When given a configuration key and --unset, it overwrites the config file (see | ||
| 48 | the --scope option below to determine which) and deletes that key from the | ||
| 49 | file. | ||
| 50 | |||
| 51 | Example: luarocks config variables.OPENSSL_DIR --unset | ||
| 52 | |||
| 53 | * When given no arguments, it prints the entire currently active configuration, | ||
| 54 | resulting from reading the config files from all scopes. | ||
| 55 | |||
| 56 | Example: luarocks config]], util.see_also([[ | ||
| 57 | https://github.com/luarocks/luarocks/wiki/Config-file-format | ||
| 58 | for detailed information on the LuaRocks config file format. | ||
| 59 | ]])): | ||
| 60 | summary("Query information about the LuaRocks configuration.") | ||
| 61 | |||
| 62 | cmd:argument("key", "The configuration key."): | ||
| 63 | args("?") | ||
| 64 | cmd:argument("value", "The configuration value."): | ||
| 65 | args("?") | ||
| 66 | |||
| 67 | cmd:option("--scope", "The scope indicates which config file should be rewritten.\n" .. | ||
| 68 | '* Using a wrapper created with `luarocks init`, the default is "project".\n' .. | ||
| 69 | '* Using --local (or when `local_by_default` is `true`), the default is "user".\n' .. | ||
| 70 | '* Otherwise, the default is "system".'): | ||
| 71 | choices({ "system", "user", "project" }) | ||
| 72 | cmd:flag("--unset", "Delete the key from the configuration file.") | ||
| 73 | cmd:flag("--json", "Output as JSON.") | ||
| 74 | |||
| 75 | |||
| 76 | cmd:flag("--lua-incdir"):hidden(true) | ||
| 77 | cmd:flag("--lua-libdir"):hidden(true) | ||
| 78 | cmd:flag("--lua-ver"):hidden(true) | ||
| 79 | cmd:flag("--system-config"):hidden(true) | ||
| 80 | cmd:flag("--user-config"):hidden(true) | ||
| 81 | cmd:flag("--rock-trees"):hidden(true) | ||
| 82 | end | ||
| 83 | |||
| 84 | local function config_file(conf) | ||
| 85 | print(dir.normalize(conf.file)) | ||
| 86 | if conf.found then | ||
| 87 | return true | ||
| 88 | else | ||
| 89 | return nil, "file not found" | ||
| 90 | end | ||
| 91 | end | ||
| 92 | |||
| 93 | local function traverse_varstring(var, tbl, fn, missing_parent) | ||
| 94 | local k | ||
| 95 | local r | ||
| 96 | k, r = var:match("^%[([0-9]+)%]%.(.*)$") | ||
| 97 | if k then | ||
| 98 | k = tonumber(k) | ||
| 99 | else | ||
| 100 | k, r = var:match("^([^.[]+)%.(.*)$") | ||
| 101 | if not k then | ||
| 102 | k, r = var:match("^([^[]+)(%[.*)$") | ||
| 103 | end | ||
| 104 | end | ||
| 105 | |||
| 106 | if k then | ||
| 107 | if not tbl[k] and missing_parent then | ||
| 108 | missing_parent(tbl, k) | ||
| 109 | end | ||
| 110 | |||
| 111 | if tbl[k] then | ||
| 112 | return traverse_varstring(r, tbl[k], fn, missing_parent) | ||
| 113 | else | ||
| 114 | return nil, "Unknown entry " .. tostring(k) | ||
| 115 | end | ||
| 116 | end | ||
| 117 | |||
| 118 | local i = var:match("^%[([0-9]+)%]$") | ||
| 119 | if i then | ||
| 120 | return fn(tbl, tonumber(i)) | ||
| 121 | end | ||
| 122 | |||
| 123 | return fn(tbl, var) | ||
| 124 | end | ||
| 125 | |||
| 126 | local function print_json(value) | ||
| 127 | print(json.encode(value)) | ||
| 128 | return true | ||
| 129 | end | ||
| 130 | |||
| 131 | local function print_entry(var, tbl, is_json) | ||
| 132 | return traverse_varstring(var, tbl, function(t, k) | ||
| 133 | if not t[k] then | ||
| 134 | return nil, "Unknown entry " .. k | ||
| 135 | end | ||
| 136 | local val = t[k] | ||
| 137 | |||
| 138 | if not config.should_skip(var, val) then | ||
| 139 | if is_json then | ||
| 140 | return print_json(val) | ||
| 141 | elseif type(val) == "string" then | ||
| 142 | print(val) | ||
| 143 | else | ||
| 144 | persist.write_value(io.stdout, val) | ||
| 145 | end | ||
| 146 | end | ||
| 147 | return true | ||
| 148 | end) | ||
| 149 | end | ||
| 150 | |||
| 151 | local function infer_type(var) | ||
| 152 | local typ | ||
| 153 | traverse_varstring(var, cfg, function(t, k) | ||
| 154 | if t[k] then | ||
| 155 | typ = type(t[k]) | ||
| 156 | end | ||
| 157 | end) | ||
| 158 | return typ | ||
| 159 | end | ||
| 160 | |||
| 161 | local function write_entries(keys, scope, do_unset) | ||
| 162 | local wrote = {} | ||
| 163 | if scope == "project" and not cfg.config_files.project then | ||
| 164 | return nil, "Current directory is not part of a project. You may want to run `luarocks init`." | ||
| 165 | end | ||
| 166 | |||
| 167 | local file_name = (cfg.config_files)[scope].file | ||
| 168 | |||
| 169 | local tbl, err = persist.load_config_file_if_basic(file_name, cfg) | ||
| 170 | if not tbl then | ||
| 171 | return nil, err | ||
| 172 | end | ||
| 173 | |||
| 174 | for var, val in util.sortedpairs(keys) do | ||
| 175 | traverse_varstring(var, tbl, function(t, k) | ||
| 176 | if do_unset then | ||
| 177 | t[k] = nil | ||
| 178 | wrote[var] = "" | ||
| 179 | else | ||
| 180 | local typ = infer_type(var) | ||
| 181 | local v | ||
| 182 | if typ == "number" and tonumber(val) then | ||
| 183 | v = tonumber(val) | ||
| 184 | elseif typ == "boolean" and val == "true" then | ||
| 185 | v = true | ||
| 186 | elseif typ == "boolean" and val == "false" then | ||
| 187 | v = false | ||
| 188 | else | ||
| 189 | v = val | ||
| 190 | end | ||
| 191 | t[k] = v | ||
| 192 | wrote[var] = v | ||
| 193 | end | ||
| 194 | return true | ||
| 195 | end, function(p, k) | ||
| 196 | p[k] = {} | ||
| 197 | end) | ||
| 198 | end | ||
| 199 | |||
| 200 | local ok, err = fs.make_dir(dir.dir_name(file_name)) | ||
| 201 | if not ok then | ||
| 202 | return nil, err | ||
| 203 | end | ||
| 204 | |||
| 205 | ok, err = persist.save_from_table(file_name, tbl) | ||
| 206 | if ok then | ||
| 207 | print(do_unset and "Removed" or "Wrote") | ||
| 208 | for var, val in util.sortedpairs(wrote) do | ||
| 209 | if do_unset then | ||
| 210 | print(("\t%s"):format(var)) | ||
| 211 | else | ||
| 212 | if type(val) == "string" then | ||
| 213 | print(("\t%s = %q"):format(var, val)) | ||
| 214 | else | ||
| 215 | print(("\t%s = %s"):format(var, tostring(val))) | ||
| 216 | end | ||
| 217 | end | ||
| 218 | end | ||
| 219 | print(do_unset and "from" or "to") | ||
| 220 | print("\t" .. file_name) | ||
| 221 | return true | ||
| 222 | else | ||
| 223 | return nil, err | ||
| 224 | end | ||
| 225 | end | ||
| 226 | |||
| 227 | local function get_scope(args) | ||
| 228 | return args.scope or | ||
| 229 | (args["local"] and "user") or | ||
| 230 | (args.project_tree and "project") or | ||
| 231 | (cfg.local_by_default and "user") or | ||
| 232 | (fs.is_writable(cfg.config_files["system"].file) and "system") or | ||
| 233 | "user" | ||
| 234 | end | ||
| 235 | |||
| 236 | local function report_on_lua_incdir_config(value) | ||
| 237 | local variables = { | ||
| 238 | ["LUA_DIR"] = cfg.variables.LUA_DIR, | ||
| 239 | ["LUA_BINDIR"] = cfg.variables.LUA_BINDIR, | ||
| 240 | ["LUA_INCDIR"] = value, | ||
| 241 | ["LUA_LIBDIR"] = cfg.variables.LUA_LIBDIR, | ||
| 242 | ["LUA"] = cfg.variables.LUA, | ||
| 243 | } | ||
| 244 | |||
| 245 | local ok, err = deps.check_lua_incdir(variables) | ||
| 246 | if not ok then | ||
| 247 | util.printerr() | ||
| 248 | util.warning((err:gsub(" You can use.*", ""))) | ||
| 249 | end | ||
| 250 | return ok | ||
| 251 | end | ||
| 252 | |||
| 253 | local function report_on_lua_libdir_config(value) | ||
| 254 | local variables = { | ||
| 255 | ["LUA_DIR"] = cfg.variables.LUA_DIR, | ||
| 256 | ["LUA_BINDIR"] = cfg.variables.LUA_BINDIR, | ||
| 257 | ["LUA_INCDIR"] = cfg.variables.LUA_INCDIR, | ||
| 258 | ["LUA_LIBDIR"] = value, | ||
| 259 | ["LUA"] = cfg.variables.LUA, | ||
| 260 | } | ||
| 261 | |||
| 262 | local ok, err, _, err_files = deps.check_lua_libdir(variables) | ||
| 263 | if not ok then | ||
| 264 | util.printerr() | ||
| 265 | util.warning((err:gsub(" You can use.*", ""))) | ||
| 266 | util.printerr("Tried:") | ||
| 267 | for _, l in pairs(err_files or {}) do | ||
| 268 | for _, d in ipairs(l) do | ||
| 269 | util.printerr("\t" .. d) | ||
| 270 | end | ||
| 271 | end | ||
| 272 | end | ||
| 273 | return ok | ||
| 274 | end | ||
| 275 | |||
| 276 | local function warn_bad_c_config() | ||
| 277 | util.printerr() | ||
| 278 | util.printerr("LuaRocks may not work correctly when building C modules using this configuration.") | ||
| 279 | util.printerr() | ||
| 280 | end | ||
| 281 | |||
| 282 | |||
| 283 | |||
| 284 | function config_cmd.command(args) | ||
| 285 | |||
| 286 | deps.check_lua_incdir(cfg.variables) | ||
| 287 | deps.check_lua_libdir(cfg.variables) | ||
| 288 | |||
| 289 | |||
| 290 | if args.lua_incdir then | ||
| 291 | print(cfg.variables.LUA_INCDIR) | ||
| 292 | return true | ||
| 293 | end | ||
| 294 | if args.lua_libdir then | ||
| 295 | print(cfg.variables.LUA_LIBDIR) | ||
| 296 | return true | ||
| 297 | end | ||
| 298 | if args.lua_ver then | ||
| 299 | print(cfg.lua_version) | ||
| 300 | return true | ||
| 301 | end | ||
| 302 | if args.system_config then | ||
| 303 | return config_file(cfg.config_files.system) | ||
| 304 | end | ||
| 305 | if args.user_config then | ||
| 306 | return config_file(cfg.config_files.user) | ||
| 307 | end | ||
| 308 | if args.rock_trees then | ||
| 309 | for _, tree in ipairs(cfg.rocks_trees) do | ||
| 310 | if type(tree) == "string" then | ||
| 311 | util.printout(dir.normalize(tree)) | ||
| 312 | else | ||
| 313 | local name = tree.name and "\t" .. tree.name or "" | ||
| 314 | util.printout(dir.normalize(tree.root) .. name) | ||
| 315 | end | ||
| 316 | end | ||
| 317 | return true | ||
| 318 | end | ||
| 319 | |||
| 320 | if args.key == "lua_version" and args.value then | ||
| 321 | local scope = get_scope(args) | ||
| 322 | if scope == "project" and not cfg.config_files.project then | ||
| 323 | return nil, "Current directory is not part of a project. You may want to run `luarocks init`." | ||
| 324 | end | ||
| 325 | |||
| 326 | local location = (cfg.config_files)[scope] | ||
| 327 | if (not location) or (not location.file) then | ||
| 328 | return nil, "could not get config file location for " .. tostring(scope) .. " scope" | ||
| 329 | end | ||
| 330 | |||
| 331 | local prefix = dir.dir_name(location.file) | ||
| 332 | local ok, err = persist.save_default_lua_version(prefix, args.value) | ||
| 333 | if not ok then | ||
| 334 | return nil, "could not set default Lua version: " .. err | ||
| 335 | end | ||
| 336 | print("Lua version will default to " .. args.value .. " in " .. prefix) | ||
| 337 | end | ||
| 338 | |||
| 339 | if args.key == "lua_dir" and args.value then | ||
| 340 | local scope = get_scope(args) | ||
| 341 | local keys = { | ||
| 342 | ["variables.LUA_DIR"] = cfg.variables.LUA_DIR, | ||
| 343 | ["variables.LUA_BINDIR"] = cfg.variables.LUA_BINDIR, | ||
| 344 | ["variables.LUA_INCDIR"] = cfg.variables.LUA_INCDIR, | ||
| 345 | ["variables.LUA_LIBDIR"] = cfg.variables.LUA_LIBDIR, | ||
| 346 | ["variables.LUA"] = cfg.variables.LUA, | ||
| 347 | } | ||
| 348 | if args.lua_version then | ||
| 349 | local prefix = dir.dir_name((cfg.config_files)[scope].file) | ||
| 350 | persist.save_default_lua_version(prefix, args.lua_version) | ||
| 351 | end | ||
| 352 | local ok, err = write_entries(keys, scope, args.unset) | ||
| 353 | if ok then | ||
| 354 | local inc_ok = report_on_lua_incdir_config(cfg.variables.LUA_INCDIR) | ||
| 355 | local lib_ok = ok and report_on_lua_libdir_config(cfg.variables.LUA_LIBDIR) | ||
| 356 | if not (inc_ok and lib_ok) then | ||
| 357 | warn_bad_c_config() | ||
| 358 | end | ||
| 359 | end | ||
| 360 | |||
| 361 | return ok, err | ||
| 362 | end | ||
| 363 | |||
| 364 | if args.key then | ||
| 365 | if args.key:match("^[A-Z]") then | ||
| 366 | args.key = "variables." .. args.key | ||
| 367 | end | ||
| 368 | |||
| 369 | if args.value or args.unset then | ||
| 370 | local scope = get_scope(args) | ||
| 371 | |||
| 372 | local ok, err = write_entries({ [args.key] = args.value or "" }, scope, args.unset) | ||
| 373 | |||
| 374 | if ok then | ||
| 375 | if args.key == "variables.LUA_INCDIR" then | ||
| 376 | local ok = report_on_lua_incdir_config(args.value) | ||
| 377 | if not ok then | ||
| 378 | warn_bad_c_config() | ||
| 379 | end | ||
| 380 | elseif args.key == "variables.LUA_LIBDIR" then | ||
| 381 | local ok = report_on_lua_libdir_config(args.value) | ||
| 382 | if not ok then | ||
| 383 | warn_bad_c_config() | ||
| 384 | end | ||
| 385 | end | ||
| 386 | end | ||
| 387 | |||
| 388 | return ok, err | ||
| 389 | else | ||
| 390 | return print_entry(args.key, cfg, args.json) | ||
| 391 | end | ||
| 392 | end | ||
| 393 | |||
| 394 | if args.json then | ||
| 395 | return print_json(config.get_config_for_display(cfg)) | ||
| 396 | else | ||
| 397 | print(config.to_string(cfg)) | ||
| 398 | return true | ||
| 399 | end | ||
| 400 | end | ||
| 401 | |||
| 402 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local doc = {} | ||
| 5 | |||
| 6 | |||
| 7 | local util = require("luarocks.util") | ||
| 8 | local queries = require("luarocks.queries") | ||
| 9 | local search = require("luarocks.search") | ||
| 10 | local path = require("luarocks.path") | ||
| 11 | local dir = require("luarocks.dir") | ||
| 12 | local fetch = require("luarocks.fetch") | ||
| 13 | local fs = require("luarocks.fs") | ||
| 14 | local download = require("luarocks.download") | ||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | function doc.add_to_parser(parser) | ||
| 21 | local cmd = parser:command("doc", "Show documentation for an installed rock.\n\n" .. | ||
| 22 | "Without any flags, tries to load the documentation using a series of heuristics.\n" .. | ||
| 23 | "With flags, return only the desired information.", util.see_also([[ | ||
| 24 | For more information about a rock, see the 'show' command. | ||
| 25 | ]])): | ||
| 26 | summary("Show documentation for an installed rock.") | ||
| 27 | |||
| 28 | cmd:argument("rock", "Name of the rock."): | ||
| 29 | action(util.namespaced_name_action) | ||
| 30 | cmd:argument("version", "Version of the rock."): | ||
| 31 | args("?") | ||
| 32 | |||
| 33 | cmd:flag("--home", "Open the home page of project.") | ||
| 34 | cmd:flag("--list", "List documentation files only.") | ||
| 35 | cmd:flag("--porcelain", "Produce machine-friendly output.") | ||
| 36 | end | ||
| 37 | |||
| 38 | local function show_homepage(homepage, name, namespace, version) | ||
| 39 | if not homepage then | ||
| 40 | return nil, "No 'homepage' field in rockspec for " .. util.format_rock_name(name, namespace, version) | ||
| 41 | end | ||
| 42 | util.printout("Opening " .. homepage .. " ...") | ||
| 43 | fs.browser(homepage) | ||
| 44 | return true | ||
| 45 | end | ||
| 46 | |||
| 47 | local function try_to_open_homepage(name, namespace, version) | ||
| 48 | local temp_dir, err = fs.make_temp_dir("doc-" .. name .. "-" .. (version or "")) | ||
| 49 | if not temp_dir then | ||
| 50 | return nil, "Failed creating temporary directory: " .. err | ||
| 51 | end | ||
| 52 | util.schedule_function(fs.delete, temp_dir) | ||
| 53 | local ok, err = fs.change_dir(temp_dir) | ||
| 54 | if not ok then return nil, err end | ||
| 55 | local filename, err = download.download_file("rockspec", name, namespace, version) | ||
| 56 | if not filename then return nil, err end | ||
| 57 | local rockspec, err = fetch.load_local_rockspec(filename) | ||
| 58 | if not rockspec then return nil, err end | ||
| 59 | fs.pop_dir() | ||
| 60 | local descript = rockspec.description or {} | ||
| 61 | return show_homepage(descript.homepage, name, namespace, version) | ||
| 62 | end | ||
| 63 | |||
| 64 | |||
| 65 | |||
| 66 | function doc.command(args) | ||
| 67 | local query = queries.new(args.rock, args.namespace, args.version) | ||
| 68 | local iname, iversion, repo = search.pick_installed_rock(query, args.tree) | ||
| 69 | if not iname then | ||
| 70 | local rock = util.format_rock_name(args.rock, args.namespace, args.version) | ||
| 71 | util.printout(rock .. " is not installed. Looking for it in the rocks servers...") | ||
| 72 | return try_to_open_homepage(args.rock, args.namespace, args.version) | ||
| 73 | end | ||
| 74 | local name, version = iname, iversion | ||
| 75 | |||
| 76 | local rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version, repo)) | ||
| 77 | if not rockspec then return nil, err end | ||
| 78 | local descript = rockspec.description or {} | ||
| 79 | |||
| 80 | if args.home then | ||
| 81 | return show_homepage(descript.homepage, name, args.namespace, version) | ||
| 82 | end | ||
| 83 | |||
| 84 | local directory = path.install_dir(name, version, repo) | ||
| 85 | |||
| 86 | local docdir | ||
| 87 | local directories = { "doc", "docs" } | ||
| 88 | for _, d in ipairs(directories) do | ||
| 89 | local dirname = dir.path(directory, d) | ||
| 90 | if fs.is_dir(dirname) then | ||
| 91 | docdir = dirname | ||
| 92 | break | ||
| 93 | end | ||
| 94 | end | ||
| 95 | if not docdir then | ||
| 96 | if descript.homepage and not args.list then | ||
| 97 | util.printout("Local documentation directory not found -- opening " .. descript.homepage .. " ...") | ||
| 98 | fs.browser(descript.homepage) | ||
| 99 | return true | ||
| 100 | end | ||
| 101 | return nil, "Documentation directory not found for " .. name .. " " .. version | ||
| 102 | end | ||
| 103 | |||
| 104 | docdir = dir.normalize(docdir) | ||
| 105 | local files = fs.find(docdir) | ||
| 106 | local htmlpatt = "%.html?$" | ||
| 107 | local extensions = { htmlpatt, "%.md$", "%.txt$", "%.textile$", "" } | ||
| 108 | local basenames = { "index", "readme", "manual" } | ||
| 109 | |||
| 110 | local porcelain = args.porcelain | ||
| 111 | if #files > 0 then | ||
| 112 | util.title("Documentation files for " .. name .. " " .. version, porcelain) | ||
| 113 | if porcelain then | ||
| 114 | for _, file in ipairs(files) do | ||
| 115 | util.printout(docdir .. "/" .. file) | ||
| 116 | end | ||
| 117 | else | ||
| 118 | util.printout(docdir .. "/") | ||
| 119 | for _, file in ipairs(files) do | ||
| 120 | util.printout("\t" .. file) | ||
| 121 | end | ||
| 122 | end | ||
| 123 | end | ||
| 124 | |||
| 125 | if args.list then | ||
| 126 | return true | ||
| 127 | end | ||
| 128 | |||
| 129 | for _, extension in ipairs(extensions) do | ||
| 130 | for _, basename in ipairs(basenames) do | ||
| 131 | local filename = basename .. extension | ||
| 132 | local found | ||
| 133 | for _, file in ipairs(files) do | ||
| 134 | if file:lower():match(filename) and ((not found) or #file < #found) then | ||
| 135 | found = file | ||
| 136 | end | ||
| 137 | end | ||
| 138 | if found then | ||
| 139 | local pathname = dir.path(docdir, found) | ||
| 140 | util.printout() | ||
| 141 | util.printout("Opening " .. pathname .. " ...") | ||
| 142 | util.printout() | ||
| 143 | local ok = fs.browser(pathname) | ||
| 144 | if not ok and not pathname:match(htmlpatt) then | ||
| 145 | local fd = io.open(pathname, "r") | ||
| 146 | util.printout(fd:read("*a")) | ||
| 147 | fd:close() | ||
| 148 | end | ||
| 149 | return true | ||
| 150 | end | ||
| 151 | end | ||
| 152 | end | ||
| 153 | |||
| 154 | return true | ||
| 155 | end | ||
| 156 | |||
| 157 | |||
| 158 | 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 @@ | |||
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | local cmd_download = {} | ||
| 5 | |||
| 6 | |||
| 7 | local util = require("luarocks.util") | ||
| 8 | local download = require("luarocks.download") | ||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | function cmd_download.add_to_parser(parser) | ||
| 15 | local cmd = parser:command("download", "Download a specific rock file from a rocks server.", util.see_also()) | ||
| 16 | |||
| 17 | cmd:argument("name", "Name of the rock."): | ||
| 18 | args("?"): | ||
| 19 | action(util.namespaced_name_action) | ||
| 20 | cmd:argument("version", "Version of the rock."): | ||
| 21 | args("?") | ||
| 22 | |||
| 23 | cmd:flag("--all", "Download all files if there are multiple matches.") | ||
| 24 | cmd:mutex( | ||
| 25 | cmd:flag("--source", "Download .src.rock if available."), | ||
| 26 | cmd:flag("--rockspec", "Download .rockspec if available."), | ||
| 27 | cmd:option("--arch", "Download rock for a specific architecture.")) | ||
| 28 | cmd:flag("--check-lua-versions", "If the rock can't be found, check repository " .. | ||
| 29 | "and report if it is available for another Lua version.") | ||
| 30 | end | ||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | function cmd_download.command(args) | ||
| 36 | if not args.name and not args.all then | ||
| 37 | return nil, "Argument missing. " .. util.see_help("download") | ||
| 38 | end | ||
| 39 | |||
| 40 | args.name = args.name or "" | ||
| 41 | |||
| 42 | local arch | ||
| 43 | |||
| 44 | if args.source then | ||
| 45 | arch = "src" | ||
| 46 | elseif args.rockspec then | ||
| 47 | arch = "rockspec" | ||
| 48 | elseif args.arch then | ||
| 49 | arch = args.arch | ||
| 50 | end | ||
| 51 | |||
| 52 | if args.all then | ||
| 53 | local ok, err = download.download_all(arch, args.name, args.namespace, args.version) | ||
| 54 | return ok, err | ||
| 55 | else | ||
| 56 | local dl, err = download.download_file(arch, args.name, args.namespace, args.version, args.check_lua_versions) | ||
| 57 | return dl and true, err | ||
| 58 | end | ||
| 59 | end | ||
| 60 | |||
| 61 | 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 @@ | |||
| 1 | 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 | ||
| 2 | local init = {} | ||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | local cfg = require("luarocks.core.cfg") | ||
| 7 | local fs = require("luarocks.fs") | ||
| 8 | local path = require("luarocks.path") | ||
| 9 | local deps = require("luarocks.deps") | ||
| 10 | local dir = require("luarocks.dir") | ||
| 11 | local util = require("luarocks.util") | ||
| 12 | local persist = require("luarocks.persist") | ||
| 13 | local write_rockspec = require("luarocks.cmd.write_rockspec") | ||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | function init.add_to_parser(parser) | ||
| 25 | local cmd = parser:command("init", "Initialize a directory for a Lua project using LuaRocks.", util.see_also()) | ||
| 26 | |||
| 27 | cmd:argument("name", "The project name."): | ||
| 28 | args("?") | ||
| 29 | cmd:argument("version", "An optional project version."): | ||
| 30 | args("?") | ||
| 31 | cmd:option("--wrapper-dir", "Location where the 'lua' and 'luarocks' wrapper scripts " .. | ||
| 32 | "should be generated; if not given, the current directory is used as a default.") | ||
| 33 | cmd:flag("--reset", "Delete any .luarocks/config-5.x.lua and ./lua and generate new ones.") | ||
| 34 | cmd:flag("--no-wrapper-scripts", "Do not generate wrapper ./lua and ./luarocks launcher scripts.") | ||
| 35 | cmd:flag("--no-gitignore", "Do not generate a .gitignore file.") | ||
| 36 | |||
| 37 | cmd:group("Options for specifying rockspec data", write_rockspec.cmd_options(cmd)) | ||
| 38 | end | ||
| 39 | |||
| 40 | local function gitignore_path(pwd, wrapper_dir, filename) | ||
| 41 | local norm_cur = fs.absolute_name(pwd) | ||
| 42 | local norm_file = fs.absolute_name(dir.path(wrapper_dir, filename)) | ||
| 43 | if norm_file:sub(1, #norm_cur) == norm_cur then | ||
| 44 | return norm_file:sub(#norm_cur + 2) | ||
| 45 | else | ||
| 46 | return filename | ||
| 47 | end | ||
| 48 | end | ||
| 49 | |||
| 50 | local function write_gitignore(entries) | ||
| 51 | local gitignore = "" | ||
| 52 | local fd = io.open(".gitignore", "r") | ||
| 53 | if fd then | ||
| 54 | gitignore = fd:read("*a") | ||
| 55 | fd:close() | ||
| 56 | gitignore = "\n" .. gitignore .. "\n" | ||
| 57 | end | ||
| 58 | |||
| 59 | fd = io.open(".gitignore", gitignore and "a" or "w") | ||
| 60 | if fd then | ||
| 61 | for _, entry in ipairs(entries) do | ||
| 62 | entry = "/" .. entry | ||
| 63 | if not gitignore:find("\n" .. entry .. "\n", 1, true) then | ||
| 64 | fd:write(entry .. "\n") | ||
| 65 | end | ||
| 66 | end | ||
| 67 | fd:close() | ||
| 68 | end | ||
| 69 | end | ||
| 70 | |||
| 71 | local function inject_tree(tree) | ||
| 72 | path.use_tree(tree) | ||
| 73 | local tree_set = false | ||
| 74 | for _, t in ipairs(cfg.rocks_trees) do | ||
| 75 | if type(t) == "table" then | ||
| 76 | if t.name == "project" then | ||
| 77 | t.root = tree | ||
| 78 | tree_set = true | ||
| 79 | end | ||
| 80 | end | ||
| 81 | end | ||
| 82 | if not tree_set then | ||
| 83 | table.insert(cfg.rocks_trees, 1, { name = "project", root = tree }) | ||
| 84 | end | ||
| 85 | end | ||
| 86 | |||
| 87 | local function write_wrapper_scripts(wrapper_dir, luarocks_wrapper, lua_wrapper) | ||
| 88 | local tree = dir.path(fs.current_dir(), "lua_modules") | ||
| 89 | |||
| 90 | fs.make_dir(wrapper_dir) | ||
| 91 | |||
| 92 | luarocks_wrapper = dir.path(wrapper_dir, luarocks_wrapper) | ||
| 93 | if not fs.exists(luarocks_wrapper) then | ||
| 94 | util.printout("Preparing " .. luarocks_wrapper .. " ...") | ||
| 95 | fs.wrap_script(arg[0], luarocks_wrapper, "none", nil, nil, "--project-tree", tree) | ||
| 96 | else | ||
| 97 | util.printout(luarocks_wrapper .. " already exists. Not overwriting it!") | ||
| 98 | end | ||
| 99 | |||
| 100 | lua_wrapper = dir.path(wrapper_dir, lua_wrapper) | ||
| 101 | local write_lua_wrapper = true | ||
| 102 | if fs.exists(lua_wrapper) then | ||
| 103 | if not util.lua_is_wrapper(lua_wrapper) then | ||
| 104 | util.printout(lua_wrapper .. " already exists and does not look like a wrapper script. Not overwriting.") | ||
| 105 | write_lua_wrapper = false | ||
| 106 | end | ||
| 107 | end | ||
| 108 | |||
| 109 | if write_lua_wrapper then | ||
| 110 | if util.check_lua_version(cfg.variables.LUA, cfg.lua_version) then | ||
| 111 | util.printout("Preparing " .. lua_wrapper .. " for version " .. cfg.lua_version .. "...") | ||
| 112 | |||
| 113 | |||
| 114 | inject_tree(tree) | ||
| 115 | |||
| 116 | fs.wrap_script(nil, lua_wrapper, "all") | ||
| 117 | else | ||
| 118 | util.warning("No Lua interpreter detected for version " .. cfg.lua_version .. ". Not creating " .. lua_wrapper) | ||
| 119 | end | ||
| 120 | end | ||
| 121 | end | ||
| 122 | |||
| 123 | |||
| 124 | |||
| 125 | function init.command(args) | ||
| 126 | local do_gitignore = not args.no_gitignore | ||
| 127 | local do_wrapper_scripts = not args.no_wrapper_scripts | ||
| 128 | local wrapper_dir = args.wrapper_dir or "." | ||
| 129 | |||
| 130 | local pwd = fs.current_dir() | ||
| 131 | |||
| 132 | if not args.name then | ||
| 133 | args.name = dir.base_name(pwd) | ||
| 134 | if args.name == "/" then | ||
| 135 | return nil, "When running from the root directory, please specify the <name> argument" | ||
| 136 | end | ||
| 137 | end | ||
| 138 | |||
| 139 | util.title("Initializing project '" .. args.name .. "' for Lua " .. cfg.lua_version .. " ...") | ||
| 140 | |||
| 141 | local ok, err = deps.check_lua_incdir(cfg.variables) | ||
| 142 | if not ok then | ||
| 143 | return nil, err | ||
| 144 | end | ||
| 145 | |||
| 146 | local has_rockspec = false | ||
| 147 | for file in fs.dir() do | ||
| 148 | if file:match("%.rockspec$") then | ||
| 149 | has_rockspec = true | ||
| 150 | break | ||
| 151 | end | ||
| 152 | end | ||
| 153 | |||
| 154 | if not has_rockspec then | ||
| 155 | args.version = args.version or "dev" | ||
| 156 | args.location = pwd | ||
| 157 | local ok, err = write_rockspec.command(args) | ||
| 158 | if not ok then | ||
| 159 | util.printerr(err) | ||
| 160 | end | ||
| 161 | end | ||
| 162 | |||
| 163 | local ext = cfg.wrapper_suffix | ||
| 164 | local luarocks_wrapper = "luarocks" .. ext | ||
| 165 | local lua_wrapper = "lua" .. ext | ||
| 166 | |||
| 167 | if do_gitignore then | ||
| 168 | util.printout("Adding entries to .gitignore ...") | ||
| 169 | local ignores = { "lua_modules", ".luarocks" } | ||
| 170 | if do_wrapper_scripts then | ||
| 171 | table.insert(ignores, 1, gitignore_path(pwd, wrapper_dir, luarocks_wrapper)) | ||
| 172 | table.insert(ignores, 2, gitignore_path(pwd, wrapper_dir, lua_wrapper)) | ||
| 173 | end | ||
| 174 | write_gitignore(ignores) | ||
| 175 | end | ||
| 176 | |||
| 177 | util.printout("Preparing ./.luarocks/ ...") | ||
| 178 | fs.make_dir(".luarocks") | ||
| 179 | local config_file = ".luarocks/config-" .. cfg.lua_version .. ".lua" | ||
| 180 | |||
| 181 | if args.reset then | ||
| 182 | if do_wrapper_scripts then | ||
| 183 | fs.delete(fs.absolute_name(dir.path(wrapper_dir, lua_wrapper))) | ||
| 184 | end | ||
| 185 | fs.delete(fs.absolute_name(config_file)) | ||
| 186 | end | ||
| 187 | |||
| 188 | local config_tbl, err = persist.load_config_file_if_basic(config_file, cfg) | ||
| 189 | if config_tbl then | ||
| 190 | local varnames = { | ||
| 191 | "LUA_DIR", | ||
| 192 | "LUA_INCDIR", | ||
| 193 | "LUA_LIBDIR", | ||
| 194 | "LUA_BINDIR", | ||
| 195 | "LUA", | ||
| 196 | } | ||
| 197 | for _, varname in ipairs(varnames) do | ||
| 198 | if cfg.variables[varname] then | ||
| 199 | config_tbl.variables = config_tbl.variables or {}; | ||
| 200 | (config_tbl.variables)[varname] = cfg.variables[varname] | ||
| 201 | end | ||
| 202 | end | ||
| 203 | local ok, err = persist.save_from_table(config_file, config_tbl) | ||
| 204 | if ok then | ||
| 205 | util.printout("Wrote " .. config_file) | ||
| 206 | else | ||
| 207 | util.printout("Failed writing " .. config_file .. ": " .. err) | ||
| 208 | end | ||
| 209 | else | ||
| 210 | util.printout("Will not attempt to overwrite " .. config_file) | ||
| 211 | end | ||
| 212 | |||
| 213 | ok, err = persist.save_default_lua_version(".luarocks", cfg.lua_version) | ||
| 214 | if not ok then | ||
| 215 | util.printout("Failed setting default Lua version: " .. err) | ||
| 216 | end | ||
| 217 | |||
| 218 | util.printout("Preparing ./lua_modules/ ...") | ||
| 219 | fs.make_dir("lua_modules/lib/luarocks/rocks-" .. cfg.lua_version) | ||
| 220 | |||
| 221 | if do_wrapper_scripts then | ||
| 222 | write_wrapper_scripts(wrapper_dir, luarocks_wrapper, lua_wrapper) | ||
| 223 | end | ||
| 224 | |||
| 225 | return true | ||
| 226 | end | ||
| 227 | |||
| 228 | init.needs_lock = function() return true end | ||
| 229 | |||
| 230 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local install = {} | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | local dir = require("luarocks.dir") | ||
| 8 | local path = require("luarocks.path") | ||
| 9 | local repos = require("luarocks.repos") | ||
| 10 | local fetch = require("luarocks.fetch") | ||
| 11 | local util = require("luarocks.util") | ||
| 12 | local fs = require("luarocks.fs") | ||
| 13 | local deps = require("luarocks.deps") | ||
| 14 | local repo_writer = require("luarocks.repo_writer") | ||
| 15 | local remove = require("luarocks.remove") | ||
| 16 | local search = require("luarocks.search") | ||
| 17 | local queries = require("luarocks.queries") | ||
| 18 | local cfg = require("luarocks.core.cfg") | ||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | function install.add_to_parser(parser) | ||
| 28 | local cmd = parser:command("install", "Install a rock.", util.see_also()) | ||
| 29 | |||
| 30 | cmd:argument("rock", "The name of a rock to be fetched from a repository " .. | ||
| 31 | "or a filename of a locally available rock."): | ||
| 32 | action(util.namespaced_name_action) | ||
| 33 | cmd:argument("version", "Version of the rock."): | ||
| 34 | args("?") | ||
| 35 | |||
| 36 | cmd:flag("--keep", "Do not remove previously installed versions of the " .. | ||
| 37 | "rock after building a new one. This behavior can be made permanent by " .. | ||
| 38 | "setting keep_other_versions=true in the configuration file.") | ||
| 39 | cmd:flag("--force", "If --keep is not specified, force removal of " .. | ||
| 40 | "previously installed versions if it would break dependencies. " .. | ||
| 41 | "If rock is already installed, reinstall it anyway.") | ||
| 42 | cmd:flag("--force-fast", "Like --force, but performs a forced removal " .. | ||
| 43 | "without reporting dependency issues.") | ||
| 44 | cmd:flag("--only-deps --deps-only", "Install only the dependencies of the rock.") | ||
| 45 | cmd:flag("--no-doc", "Install the rock without its documentation.") | ||
| 46 | cmd:flag("--verify", "Verify signature of the rockspec or src.rock being " .. | ||
| 47 | "built. If the rockspec or src.rock is being downloaded, LuaRocks will " .. | ||
| 48 | "attempt to download the signature as well. Otherwise, the signature " .. | ||
| 49 | "file should be already available locally in the same directory.\n" .. | ||
| 50 | "You need the signer’s public key in your local keyring for this " .. | ||
| 51 | "option to work properly.") | ||
| 52 | cmd:flag("--check-lua-versions", "If the rock can't be found, check repository " .. | ||
| 53 | "and report if it is available for another Lua version.") | ||
| 54 | util.deps_mode_option(cmd) | ||
| 55 | cmd:flag("--no-manifest", "Skip creating/updating the manifest") | ||
| 56 | cmd:flag("--pin", "If the installed rock is a Lua module, create a " .. | ||
| 57 | "luarocks.lock file listing the exact versions of each dependency found for " .. | ||
| 58 | "this rock (recursively), and store it in the rock's directory. " .. | ||
| 59 | "Ignores any existing luarocks.lock file in the rock's sources.") | ||
| 60 | |||
| 61 | parser:flag("--pack-binary-rock"):hidden(true) | ||
| 62 | parser:option("--branch"):hidden(true) | ||
| 63 | parser:flag("--sign"):hidden(true) | ||
| 64 | end | ||
| 65 | |||
| 66 | |||
| 67 | |||
| 68 | |||
| 69 | |||
| 70 | |||
| 71 | |||
| 72 | function install.install_binary_rock(rock_file, opts) | ||
| 73 | |||
| 74 | local namespace = opts.namespace | ||
| 75 | local deps_mode = opts.deps_mode | ||
| 76 | |||
| 77 | local name, version, arch = path.parse_name(rock_file) | ||
| 78 | if not name then | ||
| 79 | return nil, "Filename " .. rock_file .. " does not match format 'name-version-revision.arch.rock'." | ||
| 80 | end | ||
| 81 | |||
| 82 | if arch ~= "all" and arch ~= cfg.arch then | ||
| 83 | return nil, "Incompatible architecture " .. arch, "arch" | ||
| 84 | end | ||
| 85 | if repos.is_installed(name, version) then | ||
| 86 | if not (opts.force or opts.force_fast) then | ||
| 87 | util.printout(name .. " " .. version .. " is already installed in " .. path.root_dir(cfg.root_dir)) | ||
| 88 | util.printout("Use --force to reinstall.") | ||
| 89 | return name, version | ||
| 90 | end | ||
| 91 | repo_writer.delete_version(name, version, opts.deps_mode) | ||
| 92 | end | ||
| 93 | |||
| 94 | local install_dir = path.install_dir(name, version) | ||
| 95 | |||
| 96 | local rollback = util.schedule_function(function() | ||
| 97 | fs.delete(install_dir) | ||
| 98 | fs.remove_dir_if_empty(path.versions_dir(name)) | ||
| 99 | end) | ||
| 100 | local ok | ||
| 101 | local oks, err, errcode = fetch.fetch_and_unpack_rock(rock_file, install_dir, opts.verify) | ||
| 102 | if not oks then return nil, err, errcode end | ||
| 103 | |||
| 104 | local rockspec, err = fetch.load_rockspec(path.rockspec_file(name, version)) | ||
| 105 | if err then | ||
| 106 | return nil, "Failed loading rockspec for installed package: " .. err, errcode | ||
| 107 | end | ||
| 108 | |||
| 109 | if opts.deps_mode ~= "none" then | ||
| 110 | ok, err, errcode = deps.check_external_deps(rockspec, "install") | ||
| 111 | if err then return nil, err, errcode end | ||
| 112 | end | ||
| 113 | |||
| 114 | if deps_mode ~= "none" then | ||
| 115 | local deplock_dir = fs.exists(dir.path(".", "luarocks.lock")) and | ||
| 116 | "." or | ||
| 117 | install_dir | ||
| 118 | ok, err, errcode = deps.fulfill_dependencies(rockspec, "dependencies", deps_mode, opts.verify, deplock_dir) | ||
| 119 | if err then return nil, err, errcode end | ||
| 120 | end | ||
| 121 | |||
| 122 | ok, err = repo_writer.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode, namespace) | ||
| 123 | if err then return nil, err end | ||
| 124 | |||
| 125 | util.remove_scheduled_function(rollback) | ||
| 126 | rollback = util.schedule_function(function() | ||
| 127 | repo_writer.delete_version(name, version, deps_mode) | ||
| 128 | end) | ||
| 129 | |||
| 130 | ok, err = repos.run_hook(rockspec, "post_install") | ||
| 131 | if err then return nil, err end | ||
| 132 | |||
| 133 | util.announce_install(rockspec) | ||
| 134 | util.remove_scheduled_function(rollback) | ||
| 135 | return name, version | ||
| 136 | end | ||
| 137 | |||
| 138 | |||
| 139 | |||
| 140 | |||
| 141 | |||
| 142 | |||
| 143 | |||
| 144 | function install.install_binary_rock_deps(rock_file, opts) | ||
| 145 | |||
| 146 | local name, version, arch = path.parse_name(rock_file) | ||
| 147 | if not name then | ||
| 148 | return nil, "Filename " .. rock_file .. " does not match format 'name-version-revision.arch.rock'." | ||
| 149 | end | ||
| 150 | |||
| 151 | if arch ~= "all" and arch ~= cfg.arch then | ||
| 152 | return nil, "Incompatible architecture " .. arch, "arch" | ||
| 153 | end | ||
| 154 | |||
| 155 | local install_dir = path.install_dir(name, version) | ||
| 156 | |||
| 157 | local ok | ||
| 158 | local oks, err, errcode = fetch.fetch_and_unpack_rock(rock_file, install_dir, opts.verify) | ||
| 159 | if not oks then return nil, err, errcode end | ||
| 160 | |||
| 161 | local rockspec, err = fetch.load_rockspec(path.rockspec_file(name, version)) | ||
| 162 | if err then | ||
| 163 | return nil, "Failed loading rockspec for installed package: " .. err, errcode | ||
| 164 | end | ||
| 165 | |||
| 166 | local deplock_dir = fs.exists(dir.path(".", "luarocks.lock")) and | ||
| 167 | "." or | ||
| 168 | install_dir | ||
| 169 | ok, err, errcode = deps.fulfill_dependencies(rockspec, "dependencies", opts.deps_mode, opts.verify, deplock_dir) | ||
| 170 | if err then return nil, err, errcode end | ||
| 171 | |||
| 172 | util.printout() | ||
| 173 | util.printout("Successfully installed dependencies for " .. name .. " " .. version) | ||
| 174 | |||
| 175 | return name, version | ||
| 176 | end | ||
| 177 | |||
| 178 | local function install_rock_file_deps(filename, opts) | ||
| 179 | |||
| 180 | local name, version = install.install_binary_rock_deps(filename, opts) | ||
| 181 | if not name then return nil, version end | ||
| 182 | |||
| 183 | deps.check_dependencies(nil, opts.deps_mode) | ||
| 184 | return true | ||
| 185 | end | ||
| 186 | |||
| 187 | local function install_rock_file(filename, opts) | ||
| 188 | |||
| 189 | local name, version = install.install_binary_rock(filename, opts) | ||
| 190 | if not name then return nil, version end | ||
| 191 | |||
| 192 | if opts.no_doc then | ||
| 193 | util.remove_doc_dir(name, version) | ||
| 194 | end | ||
| 195 | |||
| 196 | if (not opts.keep) and not cfg.keep_other_versions then | ||
| 197 | local ok, err, warn = remove.remove_other_versions(name, version, opts.force, opts.force_fast) | ||
| 198 | if not ok then | ||
| 199 | return nil, err | ||
| 200 | elseif warn then | ||
| 201 | util.printerr(err) | ||
| 202 | end | ||
| 203 | end | ||
| 204 | |||
| 205 | deps.check_dependencies(nil, opts.deps_mode) | ||
| 206 | return true | ||
| 207 | end | ||
| 208 | |||
| 209 | |||
| 210 | |||
| 211 | |||
| 212 | |||
| 213 | |||
| 214 | |||
| 215 | |||
| 216 | |||
| 217 | function install.command(args) | ||
| 218 | if args.rock:match("%.rockspec$") or args.rock:match("%.src%.rock$") then | ||
| 219 | local build = require("luarocks.cmd.build") | ||
| 220 | return build.command(args) | ||
| 221 | elseif args.rock:match("%.rock$") then | ||
| 222 | local deps_mode = deps.get_deps_mode(args) | ||
| 223 | local opts = { | ||
| 224 | namespace = args.namespace, | ||
| 225 | keep = not not args.keep, | ||
| 226 | force = not not args.force, | ||
| 227 | force_fast = not not args.force_fast, | ||
| 228 | no_doc = not not args.no_doc, | ||
| 229 | deps_mode = deps_mode, | ||
| 230 | verify = not not args.verify, | ||
| 231 | } | ||
| 232 | if args.only_deps then | ||
| 233 | return install_rock_file_deps(args.rock, opts) | ||
| 234 | else | ||
| 235 | return install_rock_file(args.rock, opts) | ||
| 236 | end | ||
| 237 | else | ||
| 238 | local url, err = search.find_rock_checking_lua_versions( | ||
| 239 | queries.new(args.rock, args.namespace, args.version), | ||
| 240 | args.check_lua_versions) | ||
| 241 | if not url then | ||
| 242 | return nil, err | ||
| 243 | end | ||
| 244 | util.printout("Installing " .. url) | ||
| 245 | args.rock = url | ||
| 246 | return install.command(args) | ||
| 247 | end | ||
| 248 | end | ||
| 249 | |||
| 250 | install.needs_lock = function(args) | ||
| 251 | if args.pack_binary_rock then | ||
| 252 | return false | ||
| 253 | end | ||
| 254 | return true | ||
| 255 | end | ||
| 256 | |||
| 257 | deps.installer = install.command | ||
| 258 | |||
| 259 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local lint = {} | ||
| 5 | |||
| 6 | |||
| 7 | local util = require("luarocks.util") | ||
| 8 | local download = require("luarocks.download") | ||
| 9 | local fetch = require("luarocks.fetch") | ||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | function lint.add_to_parser(parser) | ||
| 16 | local cmd = parser:command("lint", "Check syntax of a rockspec.\n\n" .. | ||
| 17 | "Returns success if the text of the rockspec is syntactically correct, else failure.", | ||
| 18 | util.see_also()): | ||
| 19 | summary("Check syntax of a rockspec.") | ||
| 20 | |||
| 21 | cmd:argument("rockspec", "The rockspec to check.") | ||
| 22 | end | ||
| 23 | |||
| 24 | function lint.command(args) | ||
| 25 | |||
| 26 | local filename = args.rockspec | ||
| 27 | if not filename:match(".rockspec$") then | ||
| 28 | local err | ||
| 29 | filename, err = download.download_file("rockspec", filename:lower()) | ||
| 30 | if not filename then | ||
| 31 | return nil, err | ||
| 32 | end | ||
| 33 | end | ||
| 34 | |||
| 35 | local rs, err = fetch.load_local_rockspec(filename) | ||
| 36 | if not rs then | ||
| 37 | return nil, "Failed loading rockspec: " .. err | ||
| 38 | end | ||
| 39 | |||
| 40 | local ok = true | ||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | if not rs.description or not rs.description.license then | ||
| 48 | util.printerr("Rockspec has no description.license field.") | ||
| 49 | ok = false | ||
| 50 | end | ||
| 51 | |||
| 52 | if ok then | ||
| 53 | return ok | ||
| 54 | end | ||
| 55 | |||
| 56 | return nil, filename .. " failed consistency checks." | ||
| 57 | end | ||
| 58 | |||
| 59 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local list = {Outdated = {}, } | ||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | local search = require("luarocks.search") | ||
| 14 | local queries = require("luarocks.queries") | ||
| 15 | local vers = require("luarocks.core.vers") | ||
| 16 | local cfg = require("luarocks.core.cfg") | ||
| 17 | local util = require("luarocks.util") | ||
| 18 | local path = require("luarocks.path") | ||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | function list.add_to_parser(parser) | ||
| 31 | local cmd = parser:command("list", "List currently installed rocks.", util.see_also()) | ||
| 32 | |||
| 33 | cmd:argument("filter", "A substring of a rock name to filter by."): | ||
| 34 | args("?") | ||
| 35 | cmd:argument("version", "Rock version to filter by."): | ||
| 36 | args("?") | ||
| 37 | |||
| 38 | cmd:flag("--outdated", "List only rocks for which there is a higher " .. | ||
| 39 | "version available in the rocks server.") | ||
| 40 | cmd:flag("--porcelain", "Produce machine-friendly output.") | ||
| 41 | end | ||
| 42 | |||
| 43 | local function check_outdated(trees, query) | ||
| 44 | local results_installed = {} | ||
| 45 | for _, tree in ipairs(trees) do | ||
| 46 | search.local_manifest_search(results_installed, path.rocks_dir(tree), query) | ||
| 47 | end | ||
| 48 | local outdated = {} | ||
| 49 | for name, versions in util.sortedpairs(results_installed) do | ||
| 50 | local versionsk = util.keys(versions) | ||
| 51 | table.sort(versionsk, vers.compare_versions) | ||
| 52 | local latest_installed = versionsk[1] | ||
| 53 | |||
| 54 | local query_available = queries.new(name:lower()) | ||
| 55 | local results_available = search.search_repos(query_available) | ||
| 56 | |||
| 57 | if results_available[name] then | ||
| 58 | local available_versions = util.keys(results_available[name]) | ||
| 59 | table.sort(available_versions, vers.compare_versions) | ||
| 60 | local latest_available = available_versions[1] | ||
| 61 | local latest_available_repo = results_available[name][latest_available][1].repo | ||
| 62 | |||
| 63 | if vers.compare_versions(latest_available, latest_installed) then | ||
| 64 | table.insert(outdated, { name = name, installed = latest_installed, available = latest_available, repo = latest_available_repo }) | ||
| 65 | end | ||
| 66 | end | ||
| 67 | end | ||
| 68 | return outdated | ||
| 69 | end | ||
| 70 | |||
| 71 | local function list_outdated(trees, query, porcelain) | ||
| 72 | util.title("Outdated rocks:", porcelain) | ||
| 73 | local outdated = check_outdated(trees, query) | ||
| 74 | for _, item in ipairs(outdated) do | ||
| 75 | if porcelain then | ||
| 76 | util.printout(item.name, item.installed, item.available, item.repo) | ||
| 77 | else | ||
| 78 | util.printout(item.name) | ||
| 79 | util.printout(" " .. item.installed .. " < " .. item.available .. " at " .. item.repo) | ||
| 80 | util.printout() | ||
| 81 | end | ||
| 82 | end | ||
| 83 | return true | ||
| 84 | end | ||
| 85 | |||
| 86 | |||
| 87 | |||
| 88 | function list.command(args) | ||
| 89 | local query = queries.new(args.filter and args.filter:lower() or "", args.namespace, args.version, true) | ||
| 90 | local trees = cfg.rocks_trees | ||
| 91 | local title = "Rocks installed for Lua " .. cfg.lua_version | ||
| 92 | if args.tree then | ||
| 93 | trees = { args.tree } | ||
| 94 | title = title .. " in " .. args.tree | ||
| 95 | end | ||
| 96 | |||
| 97 | if args.outdated then | ||
| 98 | return list_outdated(trees, query, args.porcelain) | ||
| 99 | end | ||
| 100 | |||
| 101 | local results = {} | ||
| 102 | for _, tree in ipairs(trees) do | ||
| 103 | local ok, err, errcode = search.local_manifest_search(results, path.rocks_dir(tree), query) | ||
| 104 | if not ok and errcode ~= "open" then | ||
| 105 | util.warning(err) | ||
| 106 | end | ||
| 107 | end | ||
| 108 | util.title(title, args.porcelain) | ||
| 109 | search.print_result_tree(results, args.porcelain) | ||
| 110 | return true | ||
| 111 | end | ||
| 112 | |||
| 113 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | local make = {} | ||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | local build = require("luarocks.build") | ||
| 11 | local util = require("luarocks.util") | ||
| 12 | local cfg = require("luarocks.core.cfg") | ||
| 13 | local fetch = require("luarocks.fetch") | ||
| 14 | local pack = require("luarocks.pack") | ||
| 15 | local remove = require("luarocks.remove") | ||
| 16 | local deps = require("luarocks.deps") | ||
| 17 | local dir = require("luarocks.dir") | ||
| 18 | local fs = require("luarocks.fs") | ||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | function make.cmd_options(parser) | ||
| 27 | parser:flag("--no-install", "Do not install the rock.") | ||
| 28 | parser:flag("--no-doc", "Install the rock without its documentation.") | ||
| 29 | parser:flag("--pack-binary-rock", "Do not install rock. Instead, produce a " .. | ||
| 30 | ".rock file with the contents of compilation in the current directory.") | ||
| 31 | parser:flag("--keep", "Do not remove previously installed versions of the " .. | ||
| 32 | "rock after building a new one. This behavior can be made permanent by " .. | ||
| 33 | "setting keep_other_versions=true in the configuration file.") | ||
| 34 | parser:flag("--force", "If --keep is not specified, force removal of " .. | ||
| 35 | "previously installed versions if it would break dependencies. " .. | ||
| 36 | "If rock is already installed, reinstall it anyway.") | ||
| 37 | parser:flag("--force-fast", "Like --force, but performs a forced removal " .. | ||
| 38 | "without reporting dependency issues.") | ||
| 39 | parser:flag("--verify", "Verify signature of the rockspec or src.rock being " .. | ||
| 40 | "built. If the rockspec or src.rock is being downloaded, LuaRocks will " .. | ||
| 41 | "attempt to download the signature as well. Otherwise, the signature " .. | ||
| 42 | "file should be already available locally in the same directory.\n" .. | ||
| 43 | "You need the signer's public key in your local keyring for this " .. | ||
| 44 | "option to work properly.") | ||
| 45 | parser:flag("--sign", "To be used with --pack-binary-rock. Also produce a " .. | ||
| 46 | "signature file for the generated .rock file.") | ||
| 47 | parser:flag("--check-lua-versions", "If the rock can't be found, check repository " .. | ||
| 48 | "and report if it is available for another Lua version.") | ||
| 49 | parser:flag("--pin", "Pin the exact dependencies used for the rockspec" .. | ||
| 50 | "being built into a luarocks.lock file in the current directory.") | ||
| 51 | parser:flag("--no-manifest", "Skip creating/updating the manifest") | ||
| 52 | parser:flag("--only-deps --deps-only", "Install only the dependencies of the rock.") | ||
| 53 | util.deps_mode_option(parser) | ||
| 54 | end | ||
| 55 | |||
| 56 | function make.add_to_parser(parser) | ||
| 57 | |||
| 58 | local cmd = parser:command("make", [[ | ||
| 59 | Builds sources in the current directory, but unlike "build", it does not fetch | ||
| 60 | sources, etc., assuming everything is available in the current directory. If no | ||
| 61 | argument is given, it looks for a rockspec in the current directory and in | ||
| 62 | "rockspec/" and "rockspecs/" subdirectories, picking the rockspec with newest | ||
| 63 | version or without version name. If rockspecs for different rocks are found or | ||
| 64 | there are several rockspecs without version, you must specify which to use, | ||
| 65 | through the command-line. | ||
| 66 | |||
| 67 | This command is useful as a tool for debugging rockspecs. | ||
| 68 | To install rocks, you'll normally want to use the "install" and "build" | ||
| 69 | commands. See the help on those for details. | ||
| 70 | |||
| 71 | If the current directory contains a luarocks.lock file, it is used as the | ||
| 72 | authoritative source for exact version of dependencies. The --pin flag | ||
| 73 | overrides and recreates this file scanning dependency based on ranges. | ||
| 74 | ]], util.see_also()): | ||
| 75 | summary("Compile package in current directory using a rockspec.") | ||
| 76 | |||
| 77 | |||
| 78 | cmd:argument("rockspec", "Rockspec for the rock to build."): | ||
| 79 | args("?") | ||
| 80 | |||
| 81 | make.cmd_options(cmd) | ||
| 82 | end | ||
| 83 | |||
| 84 | |||
| 85 | |||
| 86 | |||
| 87 | function make.command(args) | ||
| 88 | local name, namespace, version | ||
| 89 | local rockspec_filename = args.rockspec | ||
| 90 | if not rockspec_filename then | ||
| 91 | local err | ||
| 92 | rockspec_filename, err = util.get_default_rockspec() | ||
| 93 | if not rockspec_filename then | ||
| 94 | return nil, err | ||
| 95 | end | ||
| 96 | end | ||
| 97 | if not rockspec_filename:match("rockspec$") then | ||
| 98 | return nil, "Invalid argument: 'make' takes a rockspec as a parameter. " .. util.see_help("make") | ||
| 99 | end | ||
| 100 | |||
| 101 | local cwd = fs.absolute_name(dir.path(".")) | ||
| 102 | local rockspec, err, errcode = fetch.load_rockspec(rockspec_filename) | ||
| 103 | if not rockspec then | ||
| 104 | return nil, err | ||
| 105 | end | ||
| 106 | |||
| 107 | name, namespace = util.split_namespace(rockspec.name) | ||
| 108 | namespace = namespace or args.namespace | ||
| 109 | |||
| 110 | local opts = { | ||
| 111 | need_to_fetch = false, | ||
| 112 | minimal_mode = true, | ||
| 113 | deps_mode = deps.get_deps_mode(args), | ||
| 114 | build_only_deps = not not (args.only_deps and not args.pack_binary_rock), | ||
| 115 | namespace = namespace, | ||
| 116 | branch = args.branch, | ||
| 117 | verify = not not args.verify, | ||
| 118 | check_lua_versions = not not args.check_lua_versions, | ||
| 119 | pin = not not args.pin, | ||
| 120 | rebuild = true, | ||
| 121 | no_install = not not args.no_install, | ||
| 122 | } | ||
| 123 | |||
| 124 | if args.sign and not args.pack_binary_rock then | ||
| 125 | return nil, "In the make command, --sign is meant to be used only with --pack-binary-rock" | ||
| 126 | end | ||
| 127 | |||
| 128 | if args.no_install then | ||
| 129 | name, version = build.build_rockspec(rockspec, opts, cwd) | ||
| 130 | if name then | ||
| 131 | return true | ||
| 132 | else | ||
| 133 | return nil, version | ||
| 134 | end | ||
| 135 | elseif args.pack_binary_rock then | ||
| 136 | return pack.pack_binary_rock(name, namespace, rockspec.version, args.sign, function() | ||
| 137 | name, version = build.build_rockspec(rockspec, opts, cwd) | ||
| 138 | if name and args.no_doc then | ||
| 139 | util.remove_doc_dir(name, version) | ||
| 140 | end | ||
| 141 | return name, version | ||
| 142 | end) | ||
| 143 | else | ||
| 144 | local ok, err = build.build_rockspec(rockspec, opts, cwd) | ||
| 145 | if not ok then return nil, err end | ||
| 146 | name, version = ok, err | ||
| 147 | |||
| 148 | if opts.build_only_deps then | ||
| 149 | util.printout("Stopping after installing dependencies for " .. name .. " " .. version) | ||
| 150 | util.printout() | ||
| 151 | return name ~= nil, version | ||
| 152 | end | ||
| 153 | |||
| 154 | if args.no_doc then | ||
| 155 | util.remove_doc_dir(name, version) | ||
| 156 | end | ||
| 157 | |||
| 158 | if (not args.keep) and not cfg.keep_other_versions then | ||
| 159 | local ok, err, warn = remove.remove_other_versions(name, version, args.force, args.force_fast) | ||
| 160 | if not ok then | ||
| 161 | return nil, err | ||
| 162 | elseif warn then | ||
| 163 | util.printerr(warn) | ||
| 164 | end | ||
| 165 | end | ||
| 166 | |||
| 167 | deps.check_dependencies(nil, deps.get_deps_mode(args)) | ||
| 168 | return name ~= nil, version | ||
| 169 | end | ||
| 170 | end | ||
| 171 | |||
| 172 | make.needs_lock = function(args) | ||
| 173 | if args.pack_binary_rock or args.no_install then | ||
| 174 | return false | ||
| 175 | end | ||
| 176 | return true | ||
| 177 | end | ||
| 178 | |||
| 179 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local new_version = {} | ||
| 5 | |||
| 6 | |||
| 7 | local util = require("luarocks.util") | ||
| 8 | local download = require("luarocks.download") | ||
| 9 | local fetch = require("luarocks.fetch") | ||
| 10 | local persist = require("luarocks.persist") | ||
| 11 | local fs = require("luarocks.fs") | ||
| 12 | local dir = require("luarocks.dir") | ||
| 13 | local type_rockspec = require("luarocks.type.rockspec") | ||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | function new_version.add_to_parser(parser) | ||
| 24 | local cmd = parser:command("new_version", [[ | ||
| 25 | This is a utility function that writes a new rockspec, updating data from a | ||
| 26 | previous one. | ||
| 27 | |||
| 28 | If a package name is given, it downloads the latest rockspec from the default | ||
| 29 | server. If a rockspec is given, it uses it instead. If no argument is given, it | ||
| 30 | looks for a rockspec same way 'luarocks make' does. | ||
| 31 | |||
| 32 | If the version number is not given and tag is passed using --tag, it is used as | ||
| 33 | the version, with 'v' removed from beginning. Otherwise, it only increments the | ||
| 34 | revision number of the given (or downloaded) rockspec. | ||
| 35 | |||
| 36 | If a URL is given, it replaces the one from the old rockspec with the given URL. | ||
| 37 | If a URL is not given and a new version is given, it tries to guess the new URL | ||
| 38 | by replacing occurrences of the version number in the URL or tag; if the guessed | ||
| 39 | URL is invalid, the old URL is restored. It also tries to download the new URL | ||
| 40 | to determine the new MD5 checksum. | ||
| 41 | |||
| 42 | If a tag is given, it replaces the one from the old rockspec. If there is an old | ||
| 43 | tag but no new one passed, it is guessed in the same way URL is. | ||
| 44 | |||
| 45 | If a directory is not given, it defaults to the current directory. | ||
| 46 | |||
| 47 | WARNING: it writes the new rockspec to the given directory, overwriting the file | ||
| 48 | if it already exists.]], util.see_also()): | ||
| 49 | summary("Auto-write a rockspec for a new version of a rock.") | ||
| 50 | |||
| 51 | cmd:argument("rock", "Package name or rockspec."): | ||
| 52 | args("?") | ||
| 53 | cmd:argument("new_version", "New version of the rock."): | ||
| 54 | args("?") | ||
| 55 | cmd:argument("new_url", "New URL of the rock."): | ||
| 56 | args("?") | ||
| 57 | |||
| 58 | cmd:option("--dir", "Output directory for the new rockspec.") | ||
| 59 | cmd:option("--tag", "New SCM tag.") | ||
| 60 | end | ||
| 61 | |||
| 62 | |||
| 63 | local function try_replace(tbl, field, old, new) | ||
| 64 | if not tbl[field] then | ||
| 65 | return false | ||
| 66 | end | ||
| 67 | local old_field = tbl[field] | ||
| 68 | local new_field = tbl[field]:gsub(old, new) | ||
| 69 | if new_field ~= old_field then | ||
| 70 | util.printout("Guessing new '" .. field .. "' field as " .. new_field) | ||
| 71 | tbl[field] = new_field | ||
| 72 | return true | ||
| 73 | end | ||
| 74 | return false | ||
| 75 | end | ||
| 76 | |||
| 77 | |||
| 78 | |||
| 79 | |||
| 80 | |||
| 81 | local function check_url_and_update_md5(out_rs, invalid_is_error) | ||
| 82 | local file, temp_dir = fetch.fetch_url_at_temp_dir(out_rs.source.url, "luarocks-new-version-" .. out_rs.package) | ||
| 83 | if not file then | ||
| 84 | if invalid_is_error then | ||
| 85 | return nil, "invalid URL - " .. temp_dir | ||
| 86 | end | ||
| 87 | util.warning("invalid URL - " .. temp_dir) | ||
| 88 | return true, false | ||
| 89 | end | ||
| 90 | do | ||
| 91 | local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, out_rs.source.url, out_rs.source.dir) | ||
| 92 | if not inferred_dir then | ||
| 93 | return nil, found_dir | ||
| 94 | end | ||
| 95 | |||
| 96 | if found_dir and found_dir ~= inferred_dir then | ||
| 97 | out_rs.source.dir = found_dir | ||
| 98 | end | ||
| 99 | end | ||
| 100 | if file then | ||
| 101 | if out_rs.source.md5 then | ||
| 102 | util.printout("File successfully downloaded. Updating MD5 checksum...") | ||
| 103 | local new_md5, err = fs.get_md5(file) | ||
| 104 | if not new_md5 then | ||
| 105 | return nil, err | ||
| 106 | end | ||
| 107 | local old_md5 = out_rs.source.md5 | ||
| 108 | out_rs.source.md5 = new_md5 | ||
| 109 | return true, new_md5 ~= old_md5 | ||
| 110 | else | ||
| 111 | util.printout("File successfully downloaded.") | ||
| 112 | return true, false | ||
| 113 | end | ||
| 114 | end | ||
| 115 | end | ||
| 116 | |||
| 117 | local function update_source_section(out_rs, url, tag, old_ver, new_ver) | ||
| 118 | if tag then | ||
| 119 | out_rs.source.tag = tag | ||
| 120 | end | ||
| 121 | if url then | ||
| 122 | out_rs.source.url = url | ||
| 123 | return check_url_and_update_md5(out_rs) | ||
| 124 | end | ||
| 125 | if new_ver == old_ver then | ||
| 126 | return true | ||
| 127 | end | ||
| 128 | if out_rs.source.dir then | ||
| 129 | try_replace(out_rs.source, "dir", old_ver, new_ver) | ||
| 130 | end | ||
| 131 | if out_rs.source.file then | ||
| 132 | try_replace(out_rs.source, "file", old_ver, new_ver) | ||
| 133 | end | ||
| 134 | |||
| 135 | local old_url = out_rs.source.url | ||
| 136 | if try_replace(out_rs.source, "url", old_ver, new_ver) then | ||
| 137 | local ok, md5_changed = check_url_and_update_md5(out_rs, true) | ||
| 138 | if ok then | ||
| 139 | return ok, md5_changed | ||
| 140 | end | ||
| 141 | out_rs.source.url = old_url | ||
| 142 | end | ||
| 143 | if tag or try_replace(out_rs.source, "tag", old_ver, new_ver) then | ||
| 144 | return true | ||
| 145 | end | ||
| 146 | |||
| 147 | local ok, md5_changed = check_url_and_update_md5(out_rs) | ||
| 148 | if not ok then | ||
| 149 | return nil, md5_changed | ||
| 150 | end | ||
| 151 | if md5_changed then | ||
| 152 | util.warning("URL is the same, but MD5 has changed. Old rockspec is broken.") | ||
| 153 | end | ||
| 154 | return true | ||
| 155 | end | ||
| 156 | |||
| 157 | function new_version.command(args) | ||
| 158 | if not args.rock then | ||
| 159 | local err | ||
| 160 | args.rock, err = util.get_default_rockspec() | ||
| 161 | if not args.rock then | ||
| 162 | return nil, err | ||
| 163 | end | ||
| 164 | end | ||
| 165 | |||
| 166 | local filename, err | ||
| 167 | if args.rock:match("rockspec$") then | ||
| 168 | filename, err = fetch.fetch_url(args.rock) | ||
| 169 | if not filename then | ||
| 170 | return nil, err | ||
| 171 | end | ||
| 172 | else | ||
| 173 | filename, err = download.download_file("rockspec", args.rock:lower()) | ||
| 174 | if not filename then | ||
| 175 | return nil, err | ||
| 176 | end | ||
| 177 | end | ||
| 178 | |||
| 179 | local valid_rs, err = fetch.load_rockspec(filename) | ||
| 180 | if not valid_rs then | ||
| 181 | return nil, err | ||
| 182 | end | ||
| 183 | |||
| 184 | local old_ver, old_rev = valid_rs.version:match("(.*)%-(%d+)$") | ||
| 185 | local new_ver, new_rev_str, new_rev | ||
| 186 | |||
| 187 | if args.tag and not args.new_version then | ||
| 188 | args.new_version = args.tag:gsub("^v", "") | ||
| 189 | end | ||
| 190 | |||
| 191 | local out_dir | ||
| 192 | if args.dir then | ||
| 193 | out_dir = dir.normalize(args.dir) | ||
| 194 | end | ||
| 195 | |||
| 196 | if args.new_version then | ||
| 197 | new_ver, new_rev_str = args.new_version:match("(.*)%-(%d+)$") | ||
| 198 | new_rev = math.tointeger(new_rev_str) | ||
| 199 | if not new_rev then | ||
| 200 | new_ver = args.new_version | ||
| 201 | new_rev = 1 | ||
| 202 | end | ||
| 203 | else | ||
| 204 | new_ver = old_ver | ||
| 205 | new_rev = math.tointeger(old_rev) + 1 | ||
| 206 | end | ||
| 207 | local new_rockver = new_ver:gsub("-", "") | ||
| 208 | |||
| 209 | local out_rs, err = persist.load_into_table(filename), string | ||
| 210 | local out_name = out_rs.package:lower() | ||
| 211 | out_rs.version = new_rockver .. "-" .. tostring(new_rev) | ||
| 212 | |||
| 213 | local ok, err = update_source_section(out_rs, args.new_url, args.tag, old_ver, new_ver) | ||
| 214 | if not ok then return nil, err end | ||
| 215 | |||
| 216 | if out_rs.build and out_rs.build.type == "module" then | ||
| 217 | out_rs.build.type = "builtin" | ||
| 218 | end | ||
| 219 | |||
| 220 | local out_filename = out_name .. "-" .. new_rockver .. "-" .. tostring(new_rev) .. ".rockspec" | ||
| 221 | if out_dir then | ||
| 222 | out_filename = dir.path(out_dir, out_filename) | ||
| 223 | fs.make_dir(out_dir) | ||
| 224 | end | ||
| 225 | persist.save_from_table(out_filename, out_rs, type_rockspec.order) | ||
| 226 | |||
| 227 | util.printout("Wrote " .. out_filename) | ||
| 228 | |||
| 229 | local valid_out_rs, err = fetch.load_local_rockspec(out_filename) | ||
| 230 | if not valid_out_rs then | ||
| 231 | return nil, "Failed loading generated rockspec: " .. err | ||
| 232 | end | ||
| 233 | |||
| 234 | return true | ||
| 235 | end | ||
| 236 | |||
| 237 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local cmd_pack = {} | ||
| 5 | |||
| 6 | |||
| 7 | local util = require("luarocks.util") | ||
| 8 | local pack = require("luarocks.pack") | ||
| 9 | local queries = require("luarocks.queries") | ||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | function cmd_pack.add_to_parser(parser) | ||
| 16 | local cmd = parser:command("pack", "Create a rock, packing sources or binaries.", util.see_also()) | ||
| 17 | |||
| 18 | cmd:argument("rock", "A rockspec file, for creating a source rock, or the " .. | ||
| 19 | "name of an installed package, for creating a binary rock."): | ||
| 20 | action(util.namespaced_name_action) | ||
| 21 | cmd:argument("version", "A version may be given if the first argument is a rock name."): | ||
| 22 | args("?") | ||
| 23 | |||
| 24 | cmd:flag("--sign", "Produce a signature file as well.") | ||
| 25 | end | ||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | function cmd_pack.command(args) | ||
| 31 | local file, err | ||
| 32 | if args.rock:match(".*%.rockspec") then | ||
| 33 | file, err = pack.pack_source_rock(args.rock) | ||
| 34 | else | ||
| 35 | local query = queries.new(args.rock, args.namespace, args.version) | ||
| 36 | file, err = pack.pack_installed_rock(query, args.tree) | ||
| 37 | end | ||
| 38 | return pack.report_and_sign_local_file(file, err, args.sign) | ||
| 39 | end | ||
| 40 | |||
| 41 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local path_cmd = {} | ||
| 5 | |||
| 6 | |||
| 7 | local util = require("luarocks.util") | ||
| 8 | local cfg = require("luarocks.core.cfg") | ||
| 9 | local fs = require("luarocks.fs") | ||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | function path_cmd.add_to_parser(parser) | ||
| 16 | local cmd = parser:command("path", [[ | ||
| 17 | Returns the package path currently configured for this installation | ||
| 18 | of LuaRocks, formatted as shell commands to update LUA_PATH and LUA_CPATH. | ||
| 19 | |||
| 20 | On Unix systems, you may run: | ||
| 21 | eval `luarocks path` | ||
| 22 | And on Windows: | ||
| 23 | luarocks path > "%temp%\_lrp.bat" | ||
| 24 | call "%temp%\_lrp.bat" && del "%temp%\_lrp.bat"]], | ||
| 25 | util.see_also()): | ||
| 26 | summary("Return the currently configured package path.") | ||
| 27 | |||
| 28 | cmd:flag("--no-bin", "Do not export the PATH variable.") | ||
| 29 | cmd:flag("--append", "Appends the paths to the existing paths. Default is " .. | ||
| 30 | "to prefix the LR paths to the existing paths.") | ||
| 31 | cmd:flag("--lr-path", "Prints Lua path components defined by the configured rocks trees " .. | ||
| 32 | "(not formatted as a shell command)") | ||
| 33 | cmd:flag("--lr-cpath", "Prints Lua cpath components defined by the configured rocks trees " .. | ||
| 34 | "(not formatted as a shell command)") | ||
| 35 | cmd:flag("--full", "By default, --lr-path and --lr-cpath only include the paths " .. | ||
| 36 | "derived by the LuaRocks rocks_trees. Using --full includes any other components " .. | ||
| 37 | "defined in your system's package.(c)path, either via the running interpreter's " .. | ||
| 38 | "default paths or via LUA_(C)PATH(_5_x) environment variables (in short, using " .. | ||
| 39 | "--full produces the same lists as shown in the shell outputs of 'luarocks path').") | ||
| 40 | cmd:flag("--lr-bin", "Exports the system path (not formatted as shell command).") | ||
| 41 | cmd:flag("--bin"):hidden(true) | ||
| 42 | end | ||
| 43 | |||
| 44 | |||
| 45 | |||
| 46 | function path_cmd.command(args) | ||
| 47 | local lr_path, lr_cpath, lr_bin = cfg.package_paths(args.tree) | ||
| 48 | local path_sep = cfg.export_path_separator | ||
| 49 | |||
| 50 | local full_list = ((not args.lr_path) and (not args.lr_cpath) and (not args.lr_bin)) or | ||
| 51 | args.full | ||
| 52 | |||
| 53 | local clean_path = util.cleanup_path(os.getenv("PATH") or "", path_sep, nil, true) | ||
| 54 | |||
| 55 | if full_list then | ||
| 56 | if args.append then | ||
| 57 | lr_path = package.path .. ";" .. lr_path | ||
| 58 | lr_cpath = package.cpath .. ";" .. lr_cpath | ||
| 59 | lr_bin = clean_path .. path_sep .. lr_bin | ||
| 60 | else | ||
| 61 | lr_path = lr_path .. ";" .. package.path | ||
| 62 | lr_cpath = lr_cpath .. ";" .. package.cpath | ||
| 63 | lr_bin = lr_bin .. path_sep .. clean_path | ||
| 64 | end | ||
| 65 | end | ||
| 66 | |||
| 67 | if args.lr_path then | ||
| 68 | util.printout(util.cleanup_path(lr_path, ';', cfg.lua_version, true)) | ||
| 69 | return true | ||
| 70 | elseif args.lr_cpath then | ||
| 71 | util.printout(util.cleanup_path(lr_cpath, ';', cfg.lua_version, true)) | ||
| 72 | return true | ||
| 73 | elseif args.lr_bin then | ||
| 74 | util.printout(util.cleanup_path(lr_bin, path_sep, nil, true)) | ||
| 75 | return true | ||
| 76 | end | ||
| 77 | |||
| 78 | local lpath_var, lcpath_var = util.lua_path_variables() | ||
| 79 | |||
| 80 | util.printout(fs.export_cmd(lpath_var, util.cleanup_path(lr_path, ';', cfg.lua_version, args.append))) | ||
| 81 | util.printout(fs.export_cmd(lcpath_var, util.cleanup_path(lr_cpath, ';', cfg.lua_version, args.append))) | ||
| 82 | if not args.no_bin then | ||
| 83 | util.printout(fs.export_cmd("PATH", util.cleanup_path(lr_bin, path_sep, nil, args.append))) | ||
| 84 | end | ||
| 85 | return true | ||
| 86 | end | ||
| 87 | |||
| 88 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local purge = {} | ||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | local util = require("luarocks.util") | ||
| 9 | local path = require("luarocks.path") | ||
| 10 | local search = require("luarocks.search") | ||
| 11 | local vers = require("luarocks.core.vers") | ||
| 12 | local repo_writer = require("luarocks.repo_writer") | ||
| 13 | local cfg = require("luarocks.core.cfg") | ||
| 14 | local remove = require("luarocks.remove") | ||
| 15 | local queries = require("luarocks.queries") | ||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | function purge.add_to_parser(parser) | ||
| 23 | |||
| 24 | local cmd = parser:command("purge", [[ | ||
| 25 | This command removes rocks en masse from a given tree. | ||
| 26 | By default, it removes all rocks from a tree. | ||
| 27 | |||
| 28 | The --tree option is mandatory: luarocks purge does not assume a default tree.]], | ||
| 29 | util.see_also()): | ||
| 30 | summary("Remove all installed rocks from a tree.") | ||
| 31 | |||
| 32 | |||
| 33 | cmd:flag("--old-versions", "Keep the highest-numbered version of each " .. | ||
| 34 | "rock and remove the other ones. By default it only removes old " .. | ||
| 35 | "versions if they are not needed as dependencies. This can be " .. | ||
| 36 | "overridden with the flag --force.") | ||
| 37 | cmd:flag("--force", "If --old-versions is specified, force removal of " .. | ||
| 38 | "previously installed versions if it would break dependencies.") | ||
| 39 | cmd:flag("--force-fast", "Like --force, but performs a forced removal " .. | ||
| 40 | "without reporting dependency issues.") | ||
| 41 | end | ||
| 42 | |||
| 43 | function purge.command(args) | ||
| 44 | local tree = args.tree | ||
| 45 | |||
| 46 | local results = {} | ||
| 47 | search.local_manifest_search(results, path.rocks_dir(tree), queries.all()) | ||
| 48 | |||
| 49 | local sort = function(a, b) return vers.compare_versions(b, a) end | ||
| 50 | if args.old_versions then | ||
| 51 | sort = vers.compare_versions | ||
| 52 | end | ||
| 53 | |||
| 54 | for package, versions in util.sortedpairs(results) do | ||
| 55 | for version, _ in util.sortedpairs(versions, sort) do | ||
| 56 | if args.old_versions then | ||
| 57 | util.printout("Keeping " .. package .. " " .. version .. "...") | ||
| 58 | local ok, err, warn = remove.remove_other_versions(package, version, args.force, args.force_fast) | ||
| 59 | if not ok then | ||
| 60 | util.printerr(err) | ||
| 61 | elseif warn then | ||
| 62 | util.printerr(err) | ||
| 63 | end | ||
| 64 | break | ||
| 65 | else | ||
| 66 | util.printout("Removing " .. package .. " " .. version .. "...") | ||
| 67 | local ok, err = repo_writer.delete_version(package, version, "none", true) | ||
| 68 | if not ok then | ||
| 69 | util.printerr(err) | ||
| 70 | end | ||
| 71 | end | ||
| 72 | end | ||
| 73 | end | ||
| 74 | return repo_writer.refresh_manifest(cfg.rocks_dir) | ||
| 75 | end | ||
| 76 | |||
| 77 | purge.needs_lock = function() return true end | ||
| 78 | |||
| 79 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local cmd_remove = {} | ||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | local remove = require("luarocks.remove") | ||
| 9 | local util = require("luarocks.util") | ||
| 10 | local cfg = require("luarocks.core.cfg") | ||
| 11 | local search = require("luarocks.search") | ||
| 12 | local path = require("luarocks.path") | ||
| 13 | local deps = require("luarocks.deps") | ||
| 14 | local queries = require("luarocks.queries") | ||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | function cmd_remove.add_to_parser(parser) | ||
| 21 | |||
| 22 | local cmd = parser:command("remove", [[ | ||
| 23 | Uninstall a rock. | ||
| 24 | |||
| 25 | If a version is not given, try to remove all versions at once. | ||
| 26 | Will only perform the removal if it does not break dependencies. | ||
| 27 | To override this check and force the removal, use --force or --force-fast.]], | ||
| 28 | util.see_also()): | ||
| 29 | summary("Uninstall a rock.") | ||
| 30 | |||
| 31 | |||
| 32 | cmd:argument("rock", "Name of the rock to be uninstalled."): | ||
| 33 | action(util.namespaced_name_action) | ||
| 34 | cmd:argument("version", "Version of the rock to uninstall."): | ||
| 35 | args("?") | ||
| 36 | |||
| 37 | cmd:flag("--force", "Force removal if it would break dependencies.") | ||
| 38 | cmd:flag("--force-fast", "Perform a forced removal without reporting dependency issues.") | ||
| 39 | util.deps_mode_option(cmd) | ||
| 40 | end | ||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | |||
| 45 | function cmd_remove.command(args) | ||
| 46 | local name = args.rock | ||
| 47 | local deps_mode = deps.get_deps_mode(args) | ||
| 48 | |||
| 49 | local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$") | ||
| 50 | local version = args.version | ||
| 51 | local filename = name | ||
| 52 | if rock_type then | ||
| 53 | name, version = path.parse_name(filename) | ||
| 54 | if not name then return nil, "Invalid " .. rock_type .. " filename: " .. filename end | ||
| 55 | end | ||
| 56 | |||
| 57 | name = name:lower() | ||
| 58 | |||
| 59 | local results = {} | ||
| 60 | search.local_manifest_search(results, cfg.rocks_dir, queries.new(name, args.namespace, version)) | ||
| 61 | if not results[name] then | ||
| 62 | local rock = util.format_rock_name(name, args.namespace, version) | ||
| 63 | return nil, "Could not find rock '" .. rock .. "' in " .. path.rocks_tree_to_string(cfg.root_dir) | ||
| 64 | end | ||
| 65 | |||
| 66 | local ok, err = remove.remove_search_results(results, name, deps_mode, args.force, args.force_fast) | ||
| 67 | if not ok then | ||
| 68 | return nil, err | ||
| 69 | end | ||
| 70 | |||
| 71 | deps.check_dependencies(nil, deps.get_deps_mode(args)) | ||
| 72 | return true | ||
| 73 | end | ||
| 74 | |||
| 75 | cmd_remove.needs_lock = function() return true end | ||
| 76 | |||
| 77 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local cmd_search = {} | ||
| 5 | |||
| 6 | |||
| 7 | local cfg = require("luarocks.core.cfg") | ||
| 8 | local util = require("luarocks.util") | ||
| 9 | local search = require("luarocks.search") | ||
| 10 | local queries = require("luarocks.queries") | ||
| 11 | local results = require("luarocks.results") | ||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | function cmd_search.add_to_parser(parser) | ||
| 20 | local cmd = parser:command("search", "Query the LuaRocks servers.", util.see_also()) | ||
| 21 | |||
| 22 | cmd:argument("name", "Name of the rock to search for."): | ||
| 23 | args("?"): | ||
| 24 | action(util.namespaced_name_action) | ||
| 25 | cmd:argument("version", "Rock version to search for."): | ||
| 26 | args("?") | ||
| 27 | |||
| 28 | cmd:flag("--source", "Return only rockspecs and source rocks, to be used " .. | ||
| 29 | 'with the "build" command.') | ||
| 30 | cmd:flag("--binary", "Return only pure Lua and binary rocks (rocks that " .. | ||
| 31 | 'can be used with the "install" command without requiring a C toolchain).') | ||
| 32 | cmd:flag("--all", "List all contents of the server that are suitable to " .. | ||
| 33 | "this platform, do not filter by name.") | ||
| 34 | cmd:flag("--porcelain", "Return a machine readable format.") | ||
| 35 | end | ||
| 36 | |||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | local function split_source_and_binary_results(result_tree) | ||
| 44 | local sources, binaries = {}, {} | ||
| 45 | for name, versions in pairs(result_tree) do | ||
| 46 | for version, repositories in pairs(versions) do | ||
| 47 | for _, repo in ipairs(repositories) do | ||
| 48 | local where = sources | ||
| 49 | if repo.arch == "all" or repo.arch == cfg.arch then | ||
| 50 | where = binaries | ||
| 51 | end | ||
| 52 | local entry = results.new(name, version, repo.repo, repo.arch) | ||
| 53 | search.store_result(where, entry) | ||
| 54 | end | ||
| 55 | end | ||
| 56 | end | ||
| 57 | return sources, binaries | ||
| 58 | end | ||
| 59 | |||
| 60 | |||
| 61 | |||
| 62 | |||
| 63 | function cmd_search.command(args) | ||
| 64 | local name = args.name | ||
| 65 | |||
| 66 | if args.all then | ||
| 67 | name, args.version = "", nil | ||
| 68 | end | ||
| 69 | |||
| 70 | if not args.name and not args.all then | ||
| 71 | return nil, "Enter name and version or use --all. " .. util.see_help("search") | ||
| 72 | end | ||
| 73 | |||
| 74 | local query = queries.new(name, args.namespace, args.version, true) | ||
| 75 | local result_tree = search.search_repos(query) | ||
| 76 | local porcelain = args.porcelain | ||
| 77 | local full_name = util.format_rock_name(name, args.namespace, args.version) | ||
| 78 | util.title(full_name .. " - Search results for Lua " .. cfg.lua_version .. ":", porcelain, "=") | ||
| 79 | local sources, binaries = split_source_and_binary_results(result_tree) | ||
| 80 | if next(sources) and not args.binary then | ||
| 81 | util.title("Rockspecs and source rocks:", porcelain) | ||
| 82 | search.print_result_tree(sources, porcelain) | ||
| 83 | end | ||
| 84 | if next(binaries) and not args.source then | ||
| 85 | util.title("Binary and pure-Lua rocks:", porcelain) | ||
| 86 | search.print_result_tree(binaries, porcelain) | ||
| 87 | end | ||
| 88 | return true | ||
| 89 | end | ||
| 90 | |||
| 91 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local show = {Return = {}, } | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | local queries = require("luarocks.queries") | ||
| 12 | local search = require("luarocks.search") | ||
| 13 | local dir = require("luarocks.core.dir") | ||
| 14 | local fs = require("luarocks.fs") | ||
| 15 | local cfg = require("luarocks.core.cfg") | ||
| 16 | local util = require("luarocks.util") | ||
| 17 | local path = require("luarocks.path") | ||
| 18 | local fetch = require("luarocks.fetch") | ||
| 19 | local manif = require("luarocks.manif") | ||
| 20 | local repos = require("luarocks.repos") | ||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | function show.add_to_parser(parser) | ||
| 39 | local cmd = parser:command("show", [[ | ||
| 40 | Show information about an installed rock. | ||
| 41 | |||
| 42 | Without any flags, show all module information. | ||
| 43 | With flags, return only the desired information.]], util.see_also()): | ||
| 44 | summary("Show information about an installed rock.") | ||
| 45 | |||
| 46 | cmd:argument("rock", "Name of an installed rock."): | ||
| 47 | action(util.namespaced_name_action) | ||
| 48 | cmd:argument("version", "Rock version."): | ||
| 49 | args("?") | ||
| 50 | |||
| 51 | cmd:flag("--home", "Show home page of project.") | ||
| 52 | cmd:flag("--modules", "Show all modules provided by the package as used by require().") | ||
| 53 | cmd:flag("--deps", "Show packages the package depends on.") | ||
| 54 | cmd:flag("--build-deps", "Show build-only dependencies for the package.") | ||
| 55 | cmd:flag("--test-deps", "Show dependencies for testing the package.") | ||
| 56 | cmd:flag("--rockspec", "Show the full path of the rockspec file.") | ||
| 57 | cmd:flag("--mversion", "Show the package version.") | ||
| 58 | cmd:flag("--rock-tree", "Show local tree where rock is installed.") | ||
| 59 | cmd:flag("--rock-namespace", "Show rock namespace.") | ||
| 60 | cmd:flag("--rock-dir", "Show data directory of the installed rock.") | ||
| 61 | cmd:flag("--rock-license", "Show rock license.") | ||
| 62 | cmd:flag("--issues", "Show URL for project's issue tracker.") | ||
| 63 | cmd:flag("--labels", "List the labels of the rock.") | ||
| 64 | cmd:flag("--porcelain", "Produce machine-friendly output.") | ||
| 65 | end | ||
| 66 | |||
| 67 | local friendly_template = [[ | ||
| 68 | : | ||
| 69 | ?namespace:${namespace}/${package} ${version} - ${summary} | ||
| 70 | !namespace:${package} ${version} - ${summary} | ||
| 71 | : | ||
| 72 | *detailed :${detailed} | ||
| 73 | ?detailed : | ||
| 74 | ?license :License: \t${license} | ||
| 75 | ?homepage :Homepage: \t${homepage} | ||
| 76 | ?issues :Issues: \t${issues} | ||
| 77 | ?labels :Labels: \t${labels} | ||
| 78 | ?location :Installed in: \t${location} | ||
| 79 | ?commands : | ||
| 80 | ?commands :Commands: | ||
| 81 | *commands :\t${name} (${file}) | ||
| 82 | ?modules : | ||
| 83 | ?modules :Modules: | ||
| 84 | *modules :\t${name} (${file}) | ||
| 85 | ?bdeps : | ||
| 86 | ?bdeps :Has build dependency on: | ||
| 87 | *bdeps :\t${name} (${label}) | ||
| 88 | ?tdeps : | ||
| 89 | ?tdeps :Tests depend on: | ||
| 90 | *tdeps :\t${name} (${label}) | ||
| 91 | ?deps : | ||
| 92 | ?deps :Depends on: | ||
| 93 | *deps :\t${name} (${label}) | ||
| 94 | ?ideps : | ||
| 95 | ?ideps :Indirectly pulling: | ||
| 96 | *ideps :\t${name} (${label}) | ||
| 97 | : | ||
| 98 | ]] | ||
| 99 | |||
| 100 | local porcelain_template = [[ | ||
| 101 | ?namespace:namespace\t${namespace} | ||
| 102 | ?package :package\t${package} | ||
| 103 | ?version :version\t${version} | ||
| 104 | ?summary :summary\t${summary} | ||
| 105 | *detailed :detailed\t${detailed} | ||
| 106 | ?license :license\t${license} | ||
| 107 | ?homepage :homepage\t${homepage} | ||
| 108 | ?issues :issues\t${issues} | ||
| 109 | ?labels :labels\t${labels} | ||
| 110 | ?location :location\t${location} | ||
| 111 | *commands :command\t${name}\t${file} | ||
| 112 | *modules :module\t${name}\t${file} | ||
| 113 | *bdeps :build_dependency\t${name}\t${label} | ||
| 114 | *tdeps :test_dependency\t${name}\t${label} | ||
| 115 | *deps :dependency\t${name}\t${label} | ||
| 116 | *ideps :indirect_dependency\t${name}\t${label} | ||
| 117 | ]] | ||
| 118 | |||
| 119 | local function keys_as_string(t, sep) | ||
| 120 | local keys = util.keys(t) | ||
| 121 | table.sort(keys) | ||
| 122 | return table.concat(keys, sep or " ") | ||
| 123 | end | ||
| 124 | |||
| 125 | local function word_wrap(line) | ||
| 126 | local width = math.tointeger(os.getenv("COLUMNS")) or 80 | ||
| 127 | if width > 80 then width = 80 end | ||
| 128 | if #line > width then | ||
| 129 | local brk = width | ||
| 130 | while brk > 0 and line:sub(brk, brk) ~= " " do | ||
| 131 | brk = brk - 1 | ||
| 132 | end | ||
| 133 | if brk > 0 then | ||
| 134 | return line:sub(1, brk - 1) .. "\n" .. word_wrap(line:sub(brk + 1)) | ||
| 135 | end | ||
| 136 | end | ||
| 137 | return line | ||
| 138 | end | ||
| 139 | |||
| 140 | local function format_text(text) | ||
| 141 | text = text:gsub("^%s*", ""):gsub("%s$", ""):gsub("\n[ \t]+", "\n"):gsub("([^\n])\n([^\n])", "%1 %2") | ||
| 142 | local paragraphs = util.split_string(text, "\n\n") | ||
| 143 | for n, line in ipairs(paragraphs) do | ||
| 144 | paragraphs[n] = word_wrap(line) | ||
| 145 | end | ||
| 146 | return (table.concat(paragraphs, "\n\n"):gsub("%s$", "")) | ||
| 147 | end | ||
| 148 | |||
| 149 | local function installed_rock_label(dep, tree) | ||
| 150 | local installed, version | ||
| 151 | local rocks_provided = util.get_rocks_provided() | ||
| 152 | if rocks_provided[dep.name] then | ||
| 153 | installed, version = true, rocks_provided[dep.name] | ||
| 154 | else | ||
| 155 | local name | ||
| 156 | name, version = search.pick_installed_rock(dep, tree) | ||
| 157 | installed = name ~= nil | ||
| 158 | end | ||
| 159 | return installed and "using " .. version or "missing" | ||
| 160 | end | ||
| 161 | |||
| 162 | local function render(template, data) | ||
| 163 | local out = {} | ||
| 164 | for cmd, var, line in template:gmatch("(.)([a-z]*)%s*:([^\n]*)\n") do | ||
| 165 | line = line:gsub("\\t", "\t") | ||
| 166 | local d = data[var] | ||
| 167 | if cmd == " " then | ||
| 168 | table.insert(out, line) | ||
| 169 | elseif cmd == "?" or cmd == "*" or cmd == "!" then | ||
| 170 | if (cmd == "!" and d == nil) or | ||
| 171 | (cmd ~= "!" and (type(d) == "string" or | ||
| 172 | (type(d) == "table" and next(d)))) then | ||
| 173 | local n = type(d) == "table" and #d or 1 | ||
| 174 | if cmd ~= "*" then | ||
| 175 | n = 1 | ||
| 176 | end | ||
| 177 | for i = 1, n do | ||
| 178 | local tbl = cmd == "*" and type(d) == "table" and d[i] or data | ||
| 179 | if type(tbl) == "string" then | ||
| 180 | tbl = tbl:gsub("%%", "%%%%") | ||
| 181 | end | ||
| 182 | table.insert(out, (line:gsub("${([a-z]+)}", tbl))) | ||
| 183 | end | ||
| 184 | end | ||
| 185 | end | ||
| 186 | end | ||
| 187 | return table.concat(out, "\n") | ||
| 188 | end | ||
| 189 | |||
| 190 | local function adjust_path(name, version, basedir, pathname, suffix) | ||
| 191 | pathname = dir.path(basedir, pathname) | ||
| 192 | local vpathname = path.versioned_name(pathname, basedir, name, version) | ||
| 193 | return (fs.exists(vpathname) and | ||
| 194 | vpathname or | ||
| 195 | pathname) .. (suffix or "") | ||
| 196 | end | ||
| 197 | |||
| 198 | local function modules_to_list(name, version, repo) | ||
| 199 | local ret = {} | ||
| 200 | local rock_manifest = manif.load_rock_manifest(name, version, repo) | ||
| 201 | |||
| 202 | local lua_dir = path.deploy_lua_dir(repo) | ||
| 203 | local lib_dir = path.deploy_lib_dir(repo) | ||
| 204 | repos.recurse_rock_manifest_entry(rock_manifest.lua, function(pathname) | ||
| 205 | table.insert(ret, { | ||
| 206 | name = path.path_to_module(pathname), | ||
| 207 | file = adjust_path(name, version, lua_dir, pathname), | ||
| 208 | }) | ||
| 209 | end) | ||
| 210 | repos.recurse_rock_manifest_entry(rock_manifest.lib, function(pathname) | ||
| 211 | table.insert(ret, { | ||
| 212 | name = path.path_to_module(pathname), | ||
| 213 | file = adjust_path(name, version, lib_dir, pathname), | ||
| 214 | }) | ||
| 215 | end) | ||
| 216 | table.sort(ret, function(a, b) | ||
| 217 | if a.name == b.name then | ||
| 218 | return a.file < b.file | ||
| 219 | end | ||
| 220 | return a.name < b.name | ||
| 221 | end) | ||
| 222 | return ret | ||
| 223 | end | ||
| 224 | |||
| 225 | local function commands_to_list(name, version, repo) | ||
| 226 | local ret = {} | ||
| 227 | local rock_manifest = manif.load_rock_manifest(name, version, repo) | ||
| 228 | |||
| 229 | local bin_dir = path.deploy_bin_dir(repo) | ||
| 230 | repos.recurse_rock_manifest_entry(rock_manifest.bin, function(pathname) | ||
| 231 | table.insert(ret, { | ||
| 232 | name = name, | ||
| 233 | file = adjust_path(name, version, bin_dir, pathname, cfg.wrapper_suffix), | ||
| 234 | }) | ||
| 235 | end) | ||
| 236 | table.sort(ret, function(a, b) | ||
| 237 | if a.name == b.name then | ||
| 238 | return a.file < b.file | ||
| 239 | end | ||
| 240 | return a.name < b.name | ||
| 241 | end) | ||
| 242 | return ret | ||
| 243 | end | ||
| 244 | |||
| 245 | local function deps_to_list(dependencies, tree) | ||
| 246 | local ret = {} | ||
| 247 | for _, dep in ipairs(dependencies.queries or {}) do | ||
| 248 | table.insert(ret, { name = tostring(dep), label = installed_rock_label(dep, tree) }) | ||
| 249 | end | ||
| 250 | return ret | ||
| 251 | end | ||
| 252 | |||
| 253 | local function indirect_deps(mdeps, rdeps, tree) | ||
| 254 | local ret = {} | ||
| 255 | local direct_deps = {} | ||
| 256 | for _, dep in ipairs(rdeps) do | ||
| 257 | direct_deps[dep] = true | ||
| 258 | end | ||
| 259 | for dep_name in util.sortedpairs(mdeps or {}) do | ||
| 260 | if not direct_deps[dep_name] then | ||
| 261 | table.insert(ret, { name = tostring(dep_name), label = installed_rock_label(queries.new(dep_name), tree) }) | ||
| 262 | end | ||
| 263 | end | ||
| 264 | return ret | ||
| 265 | end | ||
| 266 | |||
| 267 | local function show_rock(template, namespace, name, version, rockspec, repo, minfo, tree) | ||
| 268 | local desc = rockspec.description or {} | ||
| 269 | local data = { | ||
| 270 | namespace = namespace, | ||
| 271 | package = rockspec.package, | ||
| 272 | version = rockspec.version, | ||
| 273 | summary = desc.summary or "", | ||
| 274 | detailed = desc.detailed and util.split_string(format_text(desc.detailed), "\n"), | ||
| 275 | license = desc.license, | ||
| 276 | homepage = desc.homepage, | ||
| 277 | issues = desc.issues_url, | ||
| 278 | labels = desc.labels and table.concat(desc.labels, ", "), | ||
| 279 | location = path.rocks_tree_to_string(repo), | ||
| 280 | commands = commands_to_list(name, version, repo), | ||
| 281 | modules = modules_to_list(name, version, repo), | ||
| 282 | bdeps = deps_to_list(rockspec.build_dependencies, tree), | ||
| 283 | tdeps = deps_to_list(rockspec.test_dependencies, tree), | ||
| 284 | deps = deps_to_list(rockspec.dependencies, tree), | ||
| 285 | ideps = indirect_deps(minfo.dependencies, rockspec.dependencies, tree), | ||
| 286 | } | ||
| 287 | util.printout(render(template, data)) | ||
| 288 | end | ||
| 289 | |||
| 290 | |||
| 291 | |||
| 292 | function show.command(args) | ||
| 293 | local query = queries.new(args.rock, args.namespace, args.version, true) | ||
| 294 | |||
| 295 | local name, version, repo, repo_url = search.pick_installed_rock(query, args.tree) | ||
| 296 | if not name then | ||
| 297 | return nil, version | ||
| 298 | end | ||
| 299 | local tree = path.rocks_tree_to_string(repo) | ||
| 300 | local directory = path.install_dir(name, version, repo) | ||
| 301 | local namespace = path.read_namespace(name, version, tree) | ||
| 302 | local rockspec_file = path.rockspec_file(name, version, repo) | ||
| 303 | local rockspec, err = fetch.load_local_rockspec(rockspec_file) | ||
| 304 | if not rockspec then return nil, err end | ||
| 305 | |||
| 306 | local descript = rockspec.description or {} | ||
| 307 | local manifest, err = manif.load_manifest(repo_url) | ||
| 308 | if not manifest then return nil, err end | ||
| 309 | local minfo = manifest.repository[name][version][1] | ||
| 310 | |||
| 311 | if args.rock_tree then util.printout(tree) | ||
| 312 | elseif args.rock_namespace then util.printout(namespace) | ||
| 313 | elseif args.rock_dir then util.printout(directory) | ||
| 314 | elseif args.home then util.printout(descript.homepage) | ||
| 315 | elseif args.rock_license then util.printout(descript.license) | ||
| 316 | elseif args.issues then util.printout(descript.issues_url) | ||
| 317 | elseif args.labels then util.printout(descript.labels and table.concat(descript.labels, "\n")) | ||
| 318 | elseif args.modules then util.printout(keys_as_string(minfo.modules, "\n")) | ||
| 319 | elseif args.deps then | ||
| 320 | for _, dep in ipairs(rockspec.dependencies) do | ||
| 321 | util.printout(tostring(dep)) | ||
| 322 | end | ||
| 323 | elseif args.build_deps then | ||
| 324 | for _, dep in ipairs(rockspec.build_dependencies) do | ||
| 325 | util.printout(tostring(dep)) | ||
| 326 | end | ||
| 327 | elseif args.test_deps then | ||
| 328 | for _, dep in ipairs(rockspec.test_dependencies) do | ||
| 329 | util.printout(tostring(dep)) | ||
| 330 | end | ||
| 331 | elseif args.rockspec then util.printout(rockspec_file) | ||
| 332 | elseif args.mversion then util.printout(version) | ||
| 333 | elseif args.porcelain then | ||
| 334 | show_rock(porcelain_template, namespace, name, version, rockspec, repo, minfo, args.tree) | ||
| 335 | else | ||
| 336 | show_rock(friendly_template, namespace, name, version, rockspec, repo, minfo, args.tree) | ||
| 337 | end | ||
| 338 | return true | ||
| 339 | end | ||
| 340 | |||
| 341 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local cmd_test = {} | ||
| 5 | |||
| 6 | |||
| 7 | local util = require("luarocks.util") | ||
| 8 | local test = require("luarocks.test") | ||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | function cmd_test.add_to_parser(parser) | ||
| 15 | local cmd = parser:command("test", [[ | ||
| 16 | Run the test suite for the Lua project in the current directory. | ||
| 17 | |||
| 18 | If the first argument is a rockspec, it will use it to determine the parameters | ||
| 19 | for running tests; otherwise, it will attempt to detect the rockspec. | ||
| 20 | |||
| 21 | Any additional arguments are forwarded to the test suite. | ||
| 22 | To make sure that test suite flags are not interpreted as LuaRocks flags, use -- | ||
| 23 | to separate LuaRocks arguments from test suite arguments.]], | ||
| 24 | util.see_also()): | ||
| 25 | summary("Run the test suite in the current directory.") | ||
| 26 | |||
| 27 | cmd:argument("rockspec", "Project rockspec."): | ||
| 28 | args("?") | ||
| 29 | cmd:argument("args", "Test suite arguments."): | ||
| 30 | args("*") | ||
| 31 | cmd:flag("--prepare", "Only install dependencies needed for testing only, but do not run the test") | ||
| 32 | |||
| 33 | cmd:option("--test-type", "Specify the test suite type manually if it was " .. | ||
| 34 | "not specified in the rockspec and it could not be auto-detected."): | ||
| 35 | argname("<type>") | ||
| 36 | end | ||
| 37 | |||
| 38 | function cmd_test.command(args) | ||
| 39 | if args.rockspec and args.rockspec:match("rockspec$") then | ||
| 40 | return test.run_test_suite(args.rockspec, args.test_type, args.args, args.prepare) | ||
| 41 | end | ||
| 42 | |||
| 43 | table.insert(args.args, 1, args.rockspec) | ||
| 44 | |||
| 45 | local rockspec, err = util.get_default_rockspec() | ||
| 46 | if not rockspec then | ||
| 47 | return nil, err | ||
| 48 | end | ||
| 49 | |||
| 50 | return test.run_test_suite(rockspec, args.test_type, args.args, args.prepare) | ||
| 51 | end | ||
| 52 | |||
| 53 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local unpack = {} | ||
| 5 | |||
| 6 | |||
| 7 | local fetch = require("luarocks.fetch") | ||
| 8 | local fs = require("luarocks.fs") | ||
| 9 | local util = require("luarocks.util") | ||
| 10 | local build = require("luarocks.build") | ||
| 11 | local dir = require("luarocks.dir") | ||
| 12 | local search = require("luarocks.search") | ||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | function unpack.add_to_parser(parser) | ||
| 21 | local cmd = parser:command("unpack", [[ | ||
| 22 | Unpacks the contents of a rock in a newly created directory. | ||
| 23 | Argument may be a rock file, or the name of a rock in a rocks server. | ||
| 24 | In the latter case, the rock version may be given as a second argument.]], | ||
| 25 | util.see_also()): | ||
| 26 | summary("Unpack the contents of a rock.") | ||
| 27 | |||
| 28 | cmd:argument("rock", "A rock file or the name of a rock."): | ||
| 29 | action(util.namespaced_name_action) | ||
| 30 | cmd:argument("version", "Rock version."): | ||
| 31 | args("?") | ||
| 32 | |||
| 33 | cmd:flag("--force", "Unpack files even if the output directory already exists.") | ||
| 34 | cmd:flag("--check-lua-versions", "If the rock can't be found, check repository " .. | ||
| 35 | "and report if it is available for another Lua version.") | ||
| 36 | end | ||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | local function unpack_rockspec(rockspec_file, dir_name) | ||
| 45 | |||
| 46 | local rockspec, err = fetch.load_rockspec(rockspec_file) | ||
| 47 | if not rockspec then | ||
| 48 | return nil, "Failed loading rockspec " .. rockspec_file .. ": " .. err | ||
| 49 | end | ||
| 50 | local ok, err = fs.change_dir(dir_name) | ||
| 51 | if not ok then return nil, err end | ||
| 52 | local filename, sources_dir = fetch.fetch_sources(rockspec, true, ".") | ||
| 53 | if not filename then | ||
| 54 | return nil, sources_dir | ||
| 55 | end | ||
| 56 | ok, err = fs.change_dir(sources_dir) | ||
| 57 | if not ok then return nil, err end | ||
| 58 | ok, err = build.apply_patches(rockspec) | ||
| 59 | fs.pop_dir() | ||
| 60 | if not ok then return nil, err end | ||
| 61 | return rockspec | ||
| 62 | end | ||
| 63 | |||
| 64 | |||
| 65 | |||
| 66 | |||
| 67 | |||
| 68 | |||
| 69 | |||
| 70 | |||
| 71 | local function unpack_rock(rock_file, dir_name, kind) | ||
| 72 | |||
| 73 | local ok, filename, err, errcode | ||
| 74 | filename, err, errcode = fetch.fetch_and_unpack_rock(rock_file, dir_name) | ||
| 75 | if not filename then | ||
| 76 | return nil, err, errcode | ||
| 77 | end | ||
| 78 | ok, err = fs.change_dir(dir_name) | ||
| 79 | if not ok then return nil, err end | ||
| 80 | local rockspec_file = dir_name .. ".rockspec" | ||
| 81 | local rockspec, err = fetch.load_rockspec(rockspec_file) | ||
| 82 | if not rockspec then | ||
| 83 | return nil, "Failed loading rockspec " .. rockspec_file .. ": " .. err | ||
| 84 | end | ||
| 85 | if kind == "src" then | ||
| 86 | if rockspec.source.file then | ||
| 87 | ok, err = fs.unpack_archive(rockspec.source.file) | ||
| 88 | if not ok then return nil, err end | ||
| 89 | ok, err = fetch.find_rockspec_source_dir(rockspec, ".") | ||
| 90 | if not ok then return nil, err end | ||
| 91 | ok, err = fs.change_dir(rockspec.source.dir) | ||
| 92 | if not ok then return nil, err end | ||
| 93 | ok, err = build.apply_patches(rockspec) | ||
| 94 | fs.pop_dir() | ||
| 95 | if not ok then return nil, err end | ||
| 96 | end | ||
| 97 | end | ||
| 98 | return rockspec | ||
| 99 | end | ||
| 100 | |||
| 101 | |||
| 102 | |||
| 103 | |||
| 104 | |||
| 105 | |||
| 106 | |||
| 107 | local function run_unpacker(file, force) | ||
| 108 | |||
| 109 | local base_name = dir.base_name(file) | ||
| 110 | local dir_name, kind, extension = base_name:match("(.*)%.([^.]+)%.(rock)$") | ||
| 111 | if not extension then | ||
| 112 | dir_name, extension = base_name:match("(.*)%.(rockspec)$") | ||
| 113 | kind = "rockspec" | ||
| 114 | end | ||
| 115 | if not extension then | ||
| 116 | return nil, file .. " does not seem to be a valid filename." | ||
| 117 | end | ||
| 118 | |||
| 119 | local exists = fs.exists(dir_name) | ||
| 120 | if exists and not force then | ||
| 121 | return nil, "Directory " .. dir_name .. " already exists." | ||
| 122 | end | ||
| 123 | if not exists then | ||
| 124 | local ok, err = fs.make_dir(dir_name) | ||
| 125 | if not ok then return nil, err end | ||
| 126 | end | ||
| 127 | local rollback = util.schedule_function(fs.delete, fs.absolute_name(dir_name)) | ||
| 128 | |||
| 129 | local rockspec, err | ||
| 130 | if extension == "rock" then | ||
| 131 | rockspec, err = unpack_rock(file, dir_name, kind) | ||
| 132 | elseif extension == "rockspec" then | ||
| 133 | rockspec, err = unpack_rockspec(file, dir_name) | ||
| 134 | end | ||
| 135 | if not rockspec then | ||
| 136 | return nil, err | ||
| 137 | end | ||
| 138 | if kind == "src" or kind == "rockspec" then | ||
| 139 | fetch.find_rockspec_source_dir(rockspec, ".") | ||
| 140 | if rockspec.source.dir ~= "." then | ||
| 141 | local ok = fs.copy(rockspec.local_abs_filename, rockspec.source.dir, "read") | ||
| 142 | if not ok then | ||
| 143 | return nil, "Failed copying unpacked rockspec into unpacked source directory." | ||
| 144 | end | ||
| 145 | end | ||
| 146 | util.printout() | ||
| 147 | util.printout("Done. You may now enter directory ") | ||
| 148 | util.printout(dir.path(dir_name, rockspec.source.dir)) | ||
| 149 | util.printout("and type 'luarocks make' to build.") | ||
| 150 | end | ||
| 151 | util.remove_scheduled_function(rollback) | ||
| 152 | return true | ||
| 153 | end | ||
| 154 | |||
| 155 | |||
| 156 | |||
| 157 | |||
| 158 | function unpack.command(args) | ||
| 159 | local url, err | ||
| 160 | if args.rock:match(".*%.rock") or args.rock:match(".*%.rockspec") then | ||
| 161 | url = args.rock | ||
| 162 | else | ||
| 163 | url, err = search.find_src_or_rockspec(args.rock, args.namespace, args.version, args.check_lua_versions) | ||
| 164 | if not url then | ||
| 165 | return nil, err | ||
| 166 | end | ||
| 167 | end | ||
| 168 | |||
| 169 | return run_unpacker(url, args.force) | ||
| 170 | end | ||
| 171 | |||
| 172 | 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 @@ | |||
| 1 | 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 | ||
| 2 | local upload = {Response = {version = {}, }, } | ||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | local signing = require("luarocks.signing") | ||
| 15 | local util = require("luarocks.util") | ||
| 16 | local fetch = require("luarocks.fetch") | ||
| 17 | local pack = require("luarocks.pack") | ||
| 18 | local cfg = require("luarocks.core.cfg") | ||
| 19 | local Api = require("luarocks.upload.api") | ||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | function upload.add_to_parser(parser) | ||
| 28 | local cmd = parser:command("upload", "Pack a source rock file (.src.rock extension) " .. | ||
| 29 | "and upload it and the rockspec to the public rocks repository.", util.see_also()): | ||
| 30 | summary("Upload a rockspec to the public rocks repository.") | ||
| 31 | |||
| 32 | cmd:argument("rockspec", "Rockspec for the rock to upload.") | ||
| 33 | cmd:argument("src-rock", "A corresponding .src.rock file; if not given it will be generated."): | ||
| 34 | args("?") | ||
| 35 | |||
| 36 | cmd:flag("--skip-pack", "Do not pack and send source rock.") | ||
| 37 | cmd:option("--api-key", "Pass an API key. It will be stored for subsequent uses."): | ||
| 38 | argname("<key>") | ||
| 39 | cmd:option("--temp-key", "Use the given a temporary API key in this " .. | ||
| 40 | "invocation only. It will not be stored."): | ||
| 41 | argname("<key>") | ||
| 42 | cmd:flag("--force", "Replace existing rockspec if the same revision of a " .. | ||
| 43 | "module already exists. This should be used only in case of upload " .. | ||
| 44 | "mistakes: when updating a rockspec, increment the revision number " .. | ||
| 45 | "instead.") | ||
| 46 | cmd:flag("--sign", "Upload a signature file alongside each file as well.") | ||
| 47 | cmd:flag("--debug"):hidden(true) | ||
| 48 | end | ||
| 49 | |||
| 50 | local function is_dev_version(version) | ||
| 51 | return version:match("^dev") or version:match("^scm") | ||
| 52 | end | ||
| 53 | |||
| 54 | function upload.command(args) | ||
| 55 | local api, err = Api.new(args) | ||
| 56 | if not api then | ||
| 57 | return nil, err | ||
| 58 | end | ||
| 59 | if cfg.verbose then | ||
| 60 | api.debug = true | ||
| 61 | end | ||
| 62 | |||
| 63 | local rockspec, err, errcode = fetch.load_rockspec(args.rockspec) | ||
| 64 | if err then | ||
| 65 | return nil, err, errcode | ||
| 66 | end | ||
| 67 | |||
| 68 | util.printout("Sending " .. tostring(args.rockspec) .. " ...") | ||
| 69 | local res, err = api:method("check_rockspec", { | ||
| 70 | package = rockspec.package, | ||
| 71 | version = rockspec.version, | ||
| 72 | }) | ||
| 73 | if not res then return nil, err end | ||
| 74 | |||
| 75 | if not res.module then | ||
| 76 | util.printout("Will create new module (" .. tostring(rockspec.package) .. ")") | ||
| 77 | end | ||
| 78 | if res.version and not args.force then | ||
| 79 | return nil, "Revision " .. rockspec.version .. " already exists on the server. " .. util.see_help("upload") | ||
| 80 | end | ||
| 81 | |||
| 82 | local sigfname | ||
| 83 | local rock_sigfname | ||
| 84 | |||
| 85 | if args.sign then | ||
| 86 | sigfname, err = signing.sign_file(args.rockspec) | ||
| 87 | if err then | ||
| 88 | return nil, "Failed signing rockspec: " .. err | ||
| 89 | end | ||
| 90 | util.printout("Signed rockspec: " .. sigfname) | ||
| 91 | end | ||
| 92 | |||
| 93 | local rock_fname | ||
| 94 | if args.src_rock then | ||
| 95 | rock_fname = args.src_rock | ||
| 96 | elseif not args.skip_pack and not is_dev_version(rockspec.version) then | ||
| 97 | util.printout("Packing " .. tostring(rockspec.package)) | ||
| 98 | rock_fname, err = pack.pack_source_rock(args.rockspec) | ||
| 99 | if not rock_fname then | ||
| 100 | return nil, err | ||
| 101 | end | ||
| 102 | end | ||
| 103 | |||
| 104 | if rock_fname and args.sign then | ||
| 105 | rock_sigfname, err = signing.sign_file(rock_fname) | ||
| 106 | if err then | ||
| 107 | return nil, "Failed signing rock: " .. err | ||
| 108 | end | ||
| 109 | util.printout("Signed packed rock: " .. rock_sigfname) | ||
| 110 | end | ||
| 111 | |||
| 112 | local multipart = require("luarocks.upload.multipart") | ||
| 113 | |||
| 114 | res, err = api:method("upload", nil, { | ||
| 115 | rockspec_file = multipart.new_file(args.rockspec), | ||
| 116 | rockspec_sig = sigfname and multipart.new_file(sigfname), | ||
| 117 | }) | ||
| 118 | if not res then return nil, err end | ||
| 119 | |||
| 120 | if res.is_new and #res.manifests == 0 then | ||
| 121 | util.printerr("Warning: module not added to root manifest due to name taken.") | ||
| 122 | end | ||
| 123 | |||
| 124 | local module_url = res.module_url | ||
| 125 | |||
| 126 | if rock_fname then | ||
| 127 | if (not res.version) or (not res.version.id) then | ||
| 128 | return nil, "Invalid response from server." | ||
| 129 | end | ||
| 130 | util.printout(("Sending " .. tostring(rock_fname) .. " ...")) | ||
| 131 | res, err = api:method("upload_rock/" .. ("%d"):format(res.version.id), nil, { | ||
| 132 | rock_file = multipart.new_file(rock_fname), | ||
| 133 | rock_sig = rock_sigfname and multipart.new_file(rock_sigfname), | ||
| 134 | }) | ||
| 135 | if not res then return nil, err end | ||
| 136 | end | ||
| 137 | |||
| 138 | util.printout() | ||
| 139 | util.printout("Done: " .. tostring(module_url)) | ||
| 140 | util.printout() | ||
| 141 | return true | ||
| 142 | end | ||
| 143 | |||
| 144 | 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 @@ | |||
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | local which_cmd = {} | ||
| 5 | |||
| 6 | |||
| 7 | local loader = require("luarocks.loader") | ||
| 8 | local cfg = require("luarocks.core.cfg") | ||
| 9 | local util = require("luarocks.util") | ||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | function which_cmd.add_to_parser(parser) | ||
| 16 | local cmd = parser:command("which", 'Given a module name like "foo.bar", ' .. | ||
| 17 | "output which file would be loaded to resolve that module by " .. | ||
| 18 | 'luarocks.loader, like "/usr/local/lua/' .. cfg.lua_version .. '/foo/bar.lua".', | ||
| 19 | util.see_also()): | ||
| 20 | summary("Tell which file corresponds to a given module name.") | ||
| 21 | |||
| 22 | cmd:argument("modname", "Module name.") | ||
| 23 | end | ||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | function which_cmd.command(args) | ||
| 28 | local pathname, rock_name, rock_version, where = loader.which(args.modname, "lp") | ||
| 29 | |||
| 30 | if pathname then | ||
| 31 | util.printout(pathname) | ||
| 32 | if where == "l" then | ||
| 33 | util.printout("(provided by " .. tostring(rock_name) .. " " .. tostring(rock_version) .. ")") | ||
| 34 | else | ||
| 35 | local key = rock_name | ||
| 36 | util.printout("(found directly via package." .. key .. " -- not installed as a rock?)") | ||
| 37 | end | ||
| 38 | return true | ||
| 39 | end | ||
| 40 | |||
| 41 | return nil, "Module '" .. args.modname .. "' not found." | ||
| 42 | end | ||
| 43 | |||
| 44 | 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 @@ | |||
| 1 | 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 | ||
| 2 | local write_rockspec = {} | ||
| 3 | |||
| 4 | |||
| 5 | local builtin = require("luarocks.build.builtin") | ||
| 6 | local cfg = require("luarocks.core.cfg") | ||
| 7 | local dir = require("luarocks.dir") | ||
| 8 | local fetch = require("luarocks.fetch") | ||
| 9 | local fs = require("luarocks.fs") | ||
| 10 | local persist = require("luarocks.persist") | ||
| 11 | local rockspecs = require("luarocks.rockspecs") | ||
| 12 | local type_rockspec = require("luarocks.type.rockspec") | ||
| 13 | local util = require("luarocks.util") | ||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | local lua_versions = { | ||
| 27 | "5.1", | ||
| 28 | "5.2", | ||
| 29 | "5.3", | ||
| 30 | "5.4", | ||
| 31 | "5.1,5.2", | ||
| 32 | "5.2,5.3", | ||
| 33 | "5.3,5.4", | ||
| 34 | "5.1,5.2,5.3", | ||
| 35 | "5.2,5.3,5.4", | ||
| 36 | "5.1,5.2,5.3,5.4", | ||
| 37 | } | ||
| 38 | |||
| 39 | function write_rockspec.cmd_options(parser) | ||
| 40 | parser:option("--output", "Write the rockspec with the given filename.\n" .. | ||
| 41 | "If not given, a file is written in the current directory with a " .. | ||
| 42 | "filename based on given name and version."): | ||
| 43 | argname("<file>") | ||
| 44 | parser:option("--license", 'A license string, such as "MIT/X11" or "GNU GPL v3".'): | ||
| 45 | argname("<string>") | ||
| 46 | parser:option("--summary", "A short one-line description summary."): | ||
| 47 | argname("<txt>") | ||
| 48 | parser:option("--detailed", "A longer description string."): | ||
| 49 | argname("<txt>") | ||
| 50 | parser:option("--homepage", "Project homepage."): | ||
| 51 | argname("<txt>") | ||
| 52 | parser:option("--lua-versions", 'Supported Lua versions. Accepted values are: "' .. | ||
| 53 | table.concat(lua_versions, '", "') .. '".'): | ||
| 54 | argname("<ver>"): | ||
| 55 | choices(lua_versions) | ||
| 56 | parser:option("--rockspec-format", 'Rockspec format version, such as "1.0" or "1.1".'): | ||
| 57 | argname("<ver>") | ||
| 58 | parser:option("--tag", "Tag to use. Will attempt to extract version number from it.") | ||
| 59 | parser:option("--lib", "A comma-separated list of libraries that C files need to link to."): | ||
| 60 | argname("<libs>") | ||
| 61 | end | ||
| 62 | |||
| 63 | function write_rockspec.add_to_parser(parser) | ||
| 64 | local cmd = parser:command("write_rockspec", [[ | ||
| 65 | This command writes an initial version of a rockspec file, | ||
| 66 | based on a name, a version, and a location (an URL or a local path). | ||
| 67 | If only two arguments are given, the first one is considered the name and the | ||
| 68 | second one is the location. | ||
| 69 | If only one argument is given, it must be the location. | ||
| 70 | If no arguments are given, current directory is used as the location. | ||
| 71 | LuaRocks will attempt to infer name and version if not given, | ||
| 72 | using 'dev' as a fallback default version. | ||
| 73 | |||
| 74 | Note that the generated file is a _starting point_ for writing a | ||
| 75 | rockspec, and is not guaranteed to be complete or correct. ]], util.see_also()): | ||
| 76 | summary("Write a template for a rockspec file.") | ||
| 77 | |||
| 78 | cmd:argument("name", "Name of the rock."): | ||
| 79 | args("?") | ||
| 80 | cmd:argument("version", "Rock version."): | ||
| 81 | args("?") | ||
| 82 | cmd:argument("location", "URL or path to the rock sources."): | ||
| 83 | args("?") | ||
| 84 | |||
| 85 | write_rockspec.cmd_options(cmd) | ||
| 86 | end | ||
| 87 | |||
| 88 | local function open_file(name) | ||
| 89 | return io.open(dir.path(fs.current_dir(), name), "r") | ||
| 90 | end | ||
| 91 | |||
| 92 | local function fetch_url(rockspec) | ||
| 93 | local file, temp_dir, err_code, err_file, err_temp_dir = fetch.fetch_sources(rockspec, false) | ||
| 94 | if err_code == "source.dir" then | ||
| 95 | file, temp_dir = err_file, err_temp_dir | ||
| 96 | elseif not file then | ||
| 97 | util.warning("Could not fetch sources - " .. temp_dir) | ||
| 98 | return false | ||
| 99 | end | ||
| 100 | util.printout("File successfully downloaded. Making checksum and checking base dir...") | ||
| 101 | if dir.is_basic_protocol(rockspec.source.protocol) then | ||
| 102 | rockspec.source.md5 = fs.get_md5(file) | ||
| 103 | end | ||
| 104 | local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, rockspec.source.url) | ||
| 105 | return true, found_dir or inferred_dir, temp_dir | ||
| 106 | end | ||
| 107 | |||
| 108 | local lua_version_dep = { | ||
| 109 | ["5.1"] = "lua ~> 5.1", | ||
| 110 | ["5.2"] = "lua ~> 5.2", | ||
| 111 | ["5.3"] = "lua ~> 5.3", | ||
| 112 | ["5.4"] = "lua ~> 5.4", | ||
| 113 | ["5.1,5.2"] = "lua >= 5.1, < 5.3", | ||
| 114 | ["5.2,5.3"] = "lua >= 5.2, < 5.4", | ||
| 115 | ["5.3,5.4"] = "lua >= 5.3, < 5.5", | ||
| 116 | ["5.1,5.2,5.3"] = "lua >= 5.1, < 5.4", | ||
| 117 | ["5.2,5.3,5.4"] = "lua >= 5.2, < 5.5", | ||
| 118 | ["5.1,5.2,5.3,5.4"] = "lua >= 5.1, < 5.5", | ||
| 119 | } | ||
| 120 | |||
| 121 | local simple_scm_protocols = { | ||
| 122 | git = true, | ||
| 123 | ["git+http"] = true, | ||
| 124 | ["git+https"] = true, | ||
| 125 | ["git+ssh"] = true, | ||
| 126 | hg = true, | ||
| 127 | ["hg+http"] = true, | ||
| 128 | ["hg+https"] = true, | ||
| 129 | ["hg+ssh"] = true, | ||
| 130 | } | ||
| 131 | |||
| 132 | local detect_url | ||
| 133 | do | ||
| 134 | local function detect_url_from_command(program, args, directory) | ||
| 135 | local command = fs.Q(cfg.variables[program:upper()]) .. " " .. args | ||
| 136 | local pipe = io.popen(fs.command_at(directory, fs.quiet_stderr(command))) | ||
| 137 | if not pipe then return nil end | ||
| 138 | local url = pipe:read("*a"):match("^([^\r\n]+)") | ||
| 139 | pipe:close() | ||
| 140 | if not url then return nil end | ||
| 141 | if url:match("^[^@:/]+@[^@:/]+:.*$") then | ||
| 142 | local u, h, p = url:match("^([^@]+)@([^:]+):(.*)$") | ||
| 143 | url = program .. "+ssh://" .. u .. "@" .. h .. "/" .. p | ||
| 144 | elseif not util.starts_with(url, program .. "://") then | ||
| 145 | url = program .. "+" .. url | ||
| 146 | end | ||
| 147 | |||
| 148 | if (simple_scm_protocols)[dir.split_url(url)] then | ||
| 149 | return url | ||
| 150 | end | ||
| 151 | end | ||
| 152 | |||
| 153 | local function detect_scm_url(directory) | ||
| 154 | return detect_url_from_command("git", "config --get remote.origin.url", directory) or | ||
| 155 | detect_url_from_command("hg", "paths default", directory) | ||
| 156 | end | ||
| 157 | |||
| 158 | detect_url = function(url_or_dir) | ||
| 159 | if url_or_dir:match("://") then | ||
| 160 | return url_or_dir | ||
| 161 | else | ||
| 162 | return detect_scm_url(url_or_dir) or "*** please add URL for source tarball, zip or repository here ***" | ||
| 163 | end | ||
| 164 | end | ||
| 165 | end | ||
| 166 | |||
| 167 | local function detect_homepage(url, homepage) | ||
| 168 | if homepage then | ||
| 169 | return homepage | ||
| 170 | end | ||
| 171 | local url_protocol, url_path = dir.split_url(url) | ||
| 172 | |||
| 173 | if (simple_scm_protocols)[url_protocol] then | ||
| 174 | for _, domain in ipairs({ "github.com", "bitbucket.org", "gitlab.com" }) do | ||
| 175 | if util.starts_with(url_path, domain) then | ||
| 176 | return "https://" .. url_path:gsub("%.git$", "") | ||
| 177 | end | ||
| 178 | end | ||
| 179 | end | ||
| 180 | |||
| 181 | return "*** please enter a project homepage ***" | ||
| 182 | end | ||
| 183 | |||
| 184 | local function detect_description() | ||
| 185 | local fd = open_file("README.md") or open_file("README") | ||
| 186 | if not fd then return end | ||
| 187 | local data = fd:read("*a") | ||
| 188 | fd:close() | ||
| 189 | local paragraph = data:match("\n\n([^%[].-)\n\n") | ||
| 190 | if not paragraph then paragraph = data:match("\n\n(.*)") end | ||
| 191 | local summary, detailed | ||
| 192 | if paragraph then | ||
| 193 | detailed = paragraph | ||
| 194 | |||
| 195 | if #paragraph < 80 then | ||
| 196 | summary = paragraph:gsub("\n", "") | ||
| 197 | else | ||
| 198 | summary = paragraph:gsub("\n", " "):match("([^.]*%.) ") | ||
| 199 | end | ||
| 200 | end | ||
| 201 | return summary, detailed | ||
| 202 | end | ||
| 203 | |||
| 204 | local licenses = { | ||
| 205 | [78656] = "MIT", | ||
| 206 | [49311] = "ISC", | ||
| 207 | } | ||
| 208 | |||
| 209 | local function detect_license(data) | ||
| 210 | local strip_copyright = (data:gsub("^Copyright [^\n]*\n", "")) | ||
| 211 | local sum = 0 | ||
| 212 | for i = 1, #strip_copyright do | ||
| 213 | local num = string.byte(strip_copyright:sub(i, i)) | ||
| 214 | if num > 32 and num <= 128 then | ||
| 215 | sum = sum + num | ||
| 216 | end | ||
| 217 | end | ||
| 218 | return licenses[sum] | ||
| 219 | end | ||
| 220 | |||
| 221 | local function check_license() | ||
| 222 | local fd = open_file("COPYING") or open_file("LICENSE") or open_file("MIT-LICENSE.txt") | ||
| 223 | if not fd then return nil end | ||
| 224 | local data = fd:read("*a") | ||
| 225 | fd:close() | ||
| 226 | local license = detect_license(data) | ||
| 227 | if license then | ||
| 228 | return license, data | ||
| 229 | end | ||
| 230 | return nil, data | ||
| 231 | end | ||
| 232 | |||
| 233 | local function fill_as_builtin(rockspec, libs) | ||
| 234 | rockspec.build.type = "builtin" | ||
| 235 | |||
| 236 | local incdirs, libdirs | ||
| 237 | if libs then | ||
| 238 | incdirs, libdirs = {}, {} | ||
| 239 | for _, lib in ipairs(libs) do | ||
| 240 | local upper = lib:upper() | ||
| 241 | incdirs[#incdirs + 1] = "$(" .. upper .. "_INCDIR)" | ||
| 242 | libdirs[#libdirs + 1] = "$(" .. upper .. "_LIBDIR)" | ||
| 243 | end | ||
| 244 | end | ||
| 245 | (rockspec.build).modules, rockspec.build.install, rockspec.build.copy_directories = builtin.autodetect_modules(libs, incdirs, libdirs) | ||
| 246 | end | ||
| 247 | |||
| 248 | local function rockspec_cleanup(rockspec) | ||
| 249 | rockspec.source.file = nil | ||
| 250 | rockspec.source.protocol = nil | ||
| 251 | rockspec.source.identifier = nil | ||
| 252 | rockspec.source.dir = nil | ||
| 253 | rockspec.source.dir_set = nil | ||
| 254 | rockspec.source.pathname = nil | ||
| 255 | rockspec.variables = nil | ||
| 256 | rockspec.name = nil | ||
| 257 | rockspec.format_is_at_least = nil | ||
| 258 | rockspec.local_abs_filename = nil | ||
| 259 | rockspec.rocks_provided = nil | ||
| 260 | |||
| 261 | local dep_lists = { | ||
| 262 | dependencies = rockspec.dependencies, | ||
| 263 | build_dependencies = rockspec.build_dependencies, | ||
| 264 | test_dependencies = rockspec.test_dependencies, | ||
| 265 | } | ||
| 266 | |||
| 267 | for name, data in pairs(dep_lists) do | ||
| 268 | if not next(data) then | ||
| 269 | (rockspec)[name] = nil | ||
| 270 | else | ||
| 271 | for i, item in ipairs(data) do | ||
| 272 | data[i] = tostring(item) | ||
| 273 | end | ||
| 274 | end | ||
| 275 | end | ||
| 276 | end | ||
| 277 | |||
| 278 | function write_rockspec.command(args) | ||
| 279 | local name, version = args.name, args.version | ||
| 280 | local location = args.location | ||
| 281 | |||
| 282 | if not name then | ||
| 283 | location = "." | ||
| 284 | elseif not version then | ||
| 285 | location = name | ||
| 286 | name = nil | ||
| 287 | elseif not location then | ||
| 288 | location = version | ||
| 289 | version = nil | ||
| 290 | end | ||
| 291 | |||
| 292 | if args.tag then | ||
| 293 | if not version then | ||
| 294 | version = args.tag:gsub("^v", "") | ||
| 295 | end | ||
| 296 | end | ||
| 297 | |||
| 298 | local protocol, pathname = dir.split_url(location) | ||
| 299 | if protocol == "file" then | ||
| 300 | if pathname == "." then | ||
| 301 | name = name or dir.base_name(fs.current_dir()) | ||
| 302 | end | ||
| 303 | elseif dir.is_basic_protocol(protocol) then | ||
| 304 | local filename = dir.base_name(location) | ||
| 305 | local newname, newversion = filename:match("(.*)-([^-]+)") | ||
| 306 | if newname then | ||
| 307 | name = name or newname | ||
| 308 | version = version or newversion:gsub("%.[a-z]+$", ""):gsub("%.tar$", "") | ||
| 309 | end | ||
| 310 | else | ||
| 311 | name = name or dir.base_name(location):gsub("%.[^.]+$", "") | ||
| 312 | end | ||
| 313 | |||
| 314 | if not name then | ||
| 315 | return nil, "Could not infer rock name. " .. util.see_help("write_rockspec") | ||
| 316 | end | ||
| 317 | version = version or "dev" | ||
| 318 | |||
| 319 | local filename = args.output or dir.path(fs.current_dir(), name:lower() .. "-" .. version .. "-1.rockspec") | ||
| 320 | |||
| 321 | local url = detect_url(location) | ||
| 322 | local homepage = detect_homepage(url, args.homepage) | ||
| 323 | |||
| 324 | local rockspec, err = rockspecs.from_persisted_table(filename, { | ||
| 325 | rockspec_format = args.rockspec_format, | ||
| 326 | package = name, | ||
| 327 | version = version .. "-1", | ||
| 328 | source = { | ||
| 329 | url = url, | ||
| 330 | tag = args.tag, | ||
| 331 | }, | ||
| 332 | description = { | ||
| 333 | summary = args.summary or "*** please specify description summary ***", | ||
| 334 | detailed = args.detailed or "*** please enter a detailed description ***", | ||
| 335 | homepage = homepage, | ||
| 336 | license = args.license or "*** please specify a license ***", | ||
| 337 | }, | ||
| 338 | dependencies = { | ||
| 339 | (lua_version_dep)[args.lua_versions], | ||
| 340 | }, | ||
| 341 | build = {}, | ||
| 342 | }) | ||
| 343 | assert(not err, err) | ||
| 344 | rockspec.source.protocol = protocol | ||
| 345 | |||
| 346 | if not next(rockspec.dependencies) then | ||
| 347 | util.warning("Please specify supported Lua versions with --lua-versions=<ver>. " .. util.see_help("write_rockspec")) | ||
| 348 | end | ||
| 349 | |||
| 350 | local local_dir = location | ||
| 351 | |||
| 352 | if location:match("://") then | ||
| 353 | rockspec.source.file = dir.base_name(location) | ||
| 354 | if not dir.is_basic_protocol(rockspec.source.protocol) then | ||
| 355 | if version ~= "dev" then | ||
| 356 | rockspec.source.tag = args.tag or "v" .. version | ||
| 357 | end | ||
| 358 | end | ||
| 359 | rockspec.source.dir = nil | ||
| 360 | local ok, base_dir, temp_dir = fetch_url(rockspec) | ||
| 361 | if ok then | ||
| 362 | if base_dir ~= dir.base_name(location) then | ||
| 363 | rockspec.source.dir = base_dir | ||
| 364 | end | ||
| 365 | end | ||
| 366 | if base_dir then | ||
| 367 | local_dir = dir.path(temp_dir, base_dir) | ||
| 368 | else | ||
| 369 | local_dir = nil | ||
| 370 | end | ||
| 371 | end | ||
| 372 | |||
| 373 | if not local_dir then | ||
| 374 | local_dir = "." | ||
| 375 | end | ||
| 376 | |||
| 377 | local libs = nil | ||
| 378 | if args.lib then | ||
| 379 | libs = {} | ||
| 380 | rockspec.external_dependencies = {} | ||
| 381 | for lib in args.lib:gmatch("([^,]+)") do | ||
| 382 | table.insert(libs, lib) | ||
| 383 | rockspec.external_dependencies[lib:upper()] = { | ||
| 384 | library = lib, | ||
| 385 | } | ||
| 386 | end | ||
| 387 | end | ||
| 388 | |||
| 389 | local ok, err = fs.change_dir(local_dir) | ||
| 390 | if not ok then return nil, "Failed reaching files from project - error entering directory " .. local_dir end | ||
| 391 | |||
| 392 | if not (args.summary and args.detailed) then | ||
| 393 | local summary, detailed = detect_description() | ||
| 394 | rockspec.description.summary = args.summary or summary | ||
| 395 | rockspec.description.detailed = args.detailed or detailed | ||
| 396 | end | ||
| 397 | |||
| 398 | if not args.license then | ||
| 399 | local license, fulltext = check_license() | ||
| 400 | if license then | ||
| 401 | rockspec.description.license = license | ||
| 402 | elseif license then | ||
| 403 | util.title("Could not auto-detect type for project license:") | ||
| 404 | util.printout(fulltext) | ||
| 405 | util.printout() | ||
| 406 | util.title("Please fill in the source.license field manually or use --license.") | ||
| 407 | end | ||
| 408 | end | ||
| 409 | |||
| 410 | fill_as_builtin(rockspec, libs) | ||
| 411 | |||
| 412 | rockspec_cleanup(rockspec) | ||
| 413 | |||
| 414 | persist.save_from_table(filename, rockspec, type_rockspec.order) | ||
| 415 | |||
| 416 | util.printout() | ||
| 417 | util.printout("Wrote template at " .. filename .. " -- you should now edit and finish it.") | ||
| 418 | util.printout() | ||
| 419 | |||
| 420 | return true | ||
| 421 | end | ||
| 422 | |||
| 423 | 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 @@ | |||
| 1 | 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 = {} | ||
| 2 | |||
| 3 | local persist = require("luarocks.persist") | ||
| 4 | |||
| 5 | local cfg_skip = { | ||
| 6 | errorcodes = true, | ||
| 7 | flags = true, | ||
| 8 | platforms = true, | ||
| 9 | root_dir = true, | ||
| 10 | upload_servers = true, | ||
| 11 | } | ||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | function config.should_skip(k, v) | ||
| 16 | return type(v) == "function" or cfg_skip[k] | ||
| 17 | end | ||
| 18 | |||
| 19 | local function cleanup(tbl) | ||
| 20 | local copy = {} | ||
| 21 | for k, v in pairs(tbl) do | ||
| 22 | if not (type(k) == "string" and config.should_skip(k, v)) then | ||
| 23 | copy[k] = v | ||
| 24 | end | ||
| 25 | end | ||
| 26 | return copy | ||
| 27 | end | ||
| 28 | |||
| 29 | function config.get_config_for_display(cfg) | ||
| 30 | return cleanup(cfg) | ||
| 31 | end | ||
| 32 | |||
| 33 | function config.to_string(cfg) | ||
| 34 | local cleancfg = config.get_config_for_display(cfg) | ||
| 35 | return persist.save_from_table_to_string(cleancfg) | ||
| 36 | end | ||
| 37 | |||
| 38 | 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 @@ | |||
| 1 | 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 = {} | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | local dir_sep = package.config:sub(1, 1) | ||
| 6 | |||
| 7 | local function unquote(c) | ||
| 8 | local first, last = c:sub(1, 1), c:sub(-1) | ||
| 9 | if (first == '"' and last == '"') or | ||
| 10 | (first == "'" and last == "'") then | ||
| 11 | return c:sub(2, -2) | ||
| 12 | end | ||
| 13 | return c | ||
| 14 | end | ||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | function dir.split_url(url) | ||
| 22 | |||
| 23 | url = unquote(url) | ||
| 24 | local protocol, pathname = url:match("^([^:]*)://(.*)") | ||
| 25 | if not protocol then | ||
| 26 | protocol = "file" | ||
| 27 | pathname = url | ||
| 28 | end | ||
| 29 | return protocol, pathname | ||
| 30 | end | ||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | function dir.normalize(name) | ||
| 39 | local protocol, pathname = dir.split_url(name) | ||
| 40 | pathname = pathname:gsub("\\", "/"):gsub("(.)/*$", "%1"):gsub("//", "/") | ||
| 41 | local pieces = {} | ||
| 42 | local drive = "" | ||
| 43 | if pathname:match("^.:") then | ||
| 44 | drive, pathname = pathname:match("^(.:)(.*)$") | ||
| 45 | end | ||
| 46 | pathname = pathname .. "/" | ||
| 47 | for piece in pathname:gmatch("(.-)/") do | ||
| 48 | if piece == ".." then | ||
| 49 | local prev = pieces[#pieces] | ||
| 50 | if not prev or prev == ".." then | ||
| 51 | table.insert(pieces, "..") | ||
| 52 | elseif prev ~= "" then | ||
| 53 | table.remove(pieces) | ||
| 54 | end | ||
| 55 | elseif piece ~= "." then | ||
| 56 | table.insert(pieces, piece) | ||
| 57 | end | ||
| 58 | end | ||
| 59 | if #pieces == 0 then | ||
| 60 | pathname = drive .. "." | ||
| 61 | elseif #pieces == 1 and pieces[1] == "" then | ||
| 62 | pathname = drive .. "/" | ||
| 63 | else | ||
| 64 | pathname = drive .. table.concat(pieces, "/") | ||
| 65 | end | ||
| 66 | if protocol ~= "file" then | ||
| 67 | pathname = protocol .. "://" .. pathname | ||
| 68 | else | ||
| 69 | pathname = pathname:gsub("/", dir_sep) | ||
| 70 | end | ||
| 71 | return pathname | ||
| 72 | end | ||
| 73 | |||
| 74 | |||
| 75 | |||
| 76 | |||
| 77 | |||
| 78 | |||
| 79 | |||
| 80 | |||
| 81 | |||
| 82 | |||
| 83 | |||
| 84 | function dir.path(...) | ||
| 85 | local t = { ... } | ||
| 86 | while t[1] == "" do | ||
| 87 | table.remove(t, 1) | ||
| 88 | end | ||
| 89 | for i, c in ipairs(t) do | ||
| 90 | t[i] = unquote(c) | ||
| 91 | end | ||
| 92 | return dir.normalize(table.concat(t, "/")) | ||
| 93 | end | ||
| 94 | |||
| 95 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local manif = {} | ||
| 4 | |||
| 5 | |||
| 6 | local persist = require("luarocks.core.persist") | ||
| 7 | local cfg = require("luarocks.core.cfg") | ||
| 8 | local dir = require("luarocks.core.dir") | ||
| 9 | local util = require("luarocks.core.util") | ||
| 10 | local vers = require("luarocks.core.vers") | ||
| 11 | local path = require("luarocks.core.path") | ||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | local manifest_cache = {} | ||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | function manif.cache_manifest(repo_url, lua_version, manifest) | ||
| 33 | lua_version = lua_version or cfg.lua_version | ||
| 34 | manifest_cache[repo_url] = manifest_cache[repo_url] or {} | ||
| 35 | manifest_cache[repo_url][lua_version] = manifest | ||
| 36 | end | ||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | function manif.get_cached_manifest(repo_url, lua_version) | ||
| 43 | lua_version = lua_version or cfg.lua_version | ||
| 44 | return manifest_cache[repo_url] and manifest_cache[repo_url][lua_version] | ||
| 45 | end | ||
| 46 | |||
| 47 | |||
| 48 | |||
| 49 | |||
| 50 | |||
| 51 | |||
| 52 | |||
| 53 | |||
| 54 | function manif.manifest_loader(file, repo_url, lua_version) | ||
| 55 | local manifest, err, errcode = persist.load_into_table(file) | ||
| 56 | if not manifest and type(err) == "string" then | ||
| 57 | return nil, "Failed loading manifest for " .. repo_url .. ": " .. err, errcode | ||
| 58 | end | ||
| 59 | |||
| 60 | manif.cache_manifest(repo_url, lua_version, manifest) | ||
| 61 | return manifest, err, errcode | ||
| 62 | end | ||
| 63 | |||
| 64 | |||
| 65 | |||
| 66 | |||
| 67 | |||
| 68 | |||
| 69 | function manif.fast_load_local_manifest(repo_url) | ||
| 70 | |||
| 71 | local cached_manifest = manif.get_cached_manifest(repo_url) | ||
| 72 | if cached_manifest then | ||
| 73 | return cached_manifest | ||
| 74 | end | ||
| 75 | |||
| 76 | local pathname = dir.path(repo_url, "manifest") | ||
| 77 | return manif.manifest_loader(pathname, repo_url, nil) | ||
| 78 | end | ||
| 79 | |||
| 80 | function manif.load_rocks_tree_manifests(deps_mode) | ||
| 81 | local trees = {} | ||
| 82 | path.map_trees(deps_mode, function(tree) | ||
| 83 | local manifest = manif.fast_load_local_manifest(path.rocks_dir(tree)) | ||
| 84 | if manifest then | ||
| 85 | table.insert(trees, { tree = tree, manifest = manifest }) | ||
| 86 | end | ||
| 87 | end) | ||
| 88 | return trees | ||
| 89 | end | ||
| 90 | |||
| 91 | function manif.scan_dependencies(name, version, tree_manifests, dest) | ||
| 92 | if dest[name] then | ||
| 93 | return | ||
| 94 | end | ||
| 95 | dest[name] = version | ||
| 96 | |||
| 97 | for _, tree in ipairs(tree_manifests) do | ||
| 98 | local manifest = tree.manifest | ||
| 99 | |||
| 100 | local pkgdeps | ||
| 101 | if manifest.dependencies and manifest.dependencies[name] then | ||
| 102 | pkgdeps = manifest.dependencies[name][version] | ||
| 103 | end | ||
| 104 | if pkgdeps then | ||
| 105 | for _, dep in ipairs(pkgdeps) do | ||
| 106 | local pkg, constraints = dep.name, dep.constraints | ||
| 107 | |||
| 108 | for _, t in ipairs(tree_manifests) do | ||
| 109 | local entries = t.manifest.repository[pkg] | ||
| 110 | if entries then | ||
| 111 | for ver, _ in util.sortedpairs(entries, vers.compare_versions) do | ||
| 112 | if (not constraints) or vers.match_constraints(vers.parse_version(ver), constraints) then | ||
| 113 | manif.scan_dependencies(pkg, ver, tree_manifests, dest) | ||
| 114 | end | ||
| 115 | end | ||
| 116 | end | ||
| 117 | end | ||
| 118 | end | ||
| 119 | return | ||
| 120 | end | ||
| 121 | end | ||
| 122 | end | ||
| 123 | |||
| 124 | 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 @@ | |||
| 1 | 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 | ||
| 2 | local path = {} | ||
| 3 | |||
| 4 | |||
| 5 | local cfg = require("luarocks.core.cfg") | ||
| 6 | local dir = require("luarocks.core.dir") | ||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | local dir_sep = package.config:sub(1, 1) | ||
| 11 | |||
| 12 | |||
| 13 | function path.rocks_dir(tree) | ||
| 14 | if tree == nil then | ||
| 15 | tree = cfg.root_dir | ||
| 16 | end | ||
| 17 | if type(tree) == "string" then | ||
| 18 | return dir.path(tree, cfg.rocks_subdir) | ||
| 19 | end | ||
| 20 | return tree.rocks_dir or dir.path(tree.root, cfg.rocks_subdir) | ||
| 21 | end | ||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | function path.versioned_name(file, prefix, name, version) | ||
| 30 | assert(not name:match(dir_sep)) | ||
| 31 | |||
| 32 | local rest = file:sub(#prefix + 1):gsub("^" .. dir_sep .. "*", "") | ||
| 33 | local name_version = (name .. "_" .. version):gsub("%-", "_"):gsub("%.", "_") | ||
| 34 | return dir.path(prefix, name_version .. "-" .. rest) | ||
| 35 | end | ||
| 36 | |||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | function path.path_to_module(file) | ||
| 45 | |||
| 46 | local exts = {} | ||
| 47 | local paths = package.path .. ";" .. package.cpath | ||
| 48 | for entry in paths:gmatch("[^;]+") do | ||
| 49 | local ext = entry:match("%.([a-z]+)$") | ||
| 50 | if ext then | ||
| 51 | exts[ext] = true | ||
| 52 | end | ||
| 53 | end | ||
| 54 | |||
| 55 | local name | ||
| 56 | for ext, _ in pairs(exts) do | ||
| 57 | name = file:match("(.*)%." .. ext .. "$") | ||
| 58 | if name then | ||
| 59 | name = name:gsub("[\\/]", ".") | ||
| 60 | break | ||
| 61 | end | ||
| 62 | end | ||
| 63 | |||
| 64 | if not name then name = file end | ||
| 65 | |||
| 66 | |||
| 67 | name = name:gsub("^%.+", ""):gsub("%.+$", "") | ||
| 68 | |||
| 69 | return name | ||
| 70 | end | ||
| 71 | |||
| 72 | function path.deploy_lua_dir(tree) | ||
| 73 | if type(tree) == "string" then | ||
| 74 | return dir.path(tree, cfg.lua_modules_path) | ||
| 75 | else | ||
| 76 | return tree.lua_dir or dir.path(tree.root, cfg.lua_modules_path) | ||
| 77 | end | ||
| 78 | end | ||
| 79 | |||
| 80 | function path.deploy_lib_dir(tree) | ||
| 81 | if type(tree) == "string" then | ||
| 82 | return dir.path(tree, cfg.lib_modules_path) | ||
| 83 | else | ||
| 84 | return tree.lib_dir or dir.path(tree.root, cfg.lib_modules_path) | ||
| 85 | end | ||
| 86 | end | ||
| 87 | |||
| 88 | local is_src_extension = { [".lua"] = true, [".tl"] = true, [".tld"] = true, [".moon"] = true } | ||
| 89 | |||
| 90 | |||
| 91 | |||
| 92 | |||
| 93 | |||
| 94 | |||
| 95 | |||
| 96 | |||
| 97 | |||
| 98 | function path.which_i(file_name, name, version, tree, i) | ||
| 99 | local deploy_dir | ||
| 100 | local extension = file_name:match("%.[a-z]+$") | ||
| 101 | if is_src_extension[extension] then | ||
| 102 | deploy_dir = path.deploy_lua_dir(tree) | ||
| 103 | file_name = dir.path(deploy_dir, file_name) | ||
| 104 | else | ||
| 105 | deploy_dir = path.deploy_lib_dir(tree) | ||
| 106 | file_name = dir.path(deploy_dir, file_name) | ||
| 107 | end | ||
| 108 | if i > 1 then | ||
| 109 | file_name = path.versioned_name(file_name, deploy_dir, name, version) | ||
| 110 | end | ||
| 111 | return file_name | ||
| 112 | end | ||
| 113 | |||
| 114 | function path.rocks_tree_to_string(tree) | ||
| 115 | if type(tree) == "string" then | ||
| 116 | return tree | ||
| 117 | else | ||
| 118 | return tree.root | ||
| 119 | end | ||
| 120 | end | ||
| 121 | |||
| 122 | |||
| 123 | |||
| 124 | |||
| 125 | |||
| 126 | |||
| 127 | |||
| 128 | |||
| 129 | function path.map_trees(deps_mode, fn, ...) | ||
| 130 | local result = {} | ||
| 131 | local current = cfg.root_dir or cfg.rocks_trees[1] | ||
| 132 | if deps_mode == "one" then | ||
| 133 | table.insert(result, (fn(current, ...)) or 0) | ||
| 134 | else | ||
| 135 | local use = false | ||
| 136 | if deps_mode == "all" then | ||
| 137 | use = true | ||
| 138 | end | ||
| 139 | for _, tree in ipairs(cfg.rocks_trees or {}) do | ||
| 140 | if dir.normalize(path.rocks_tree_to_string(tree)) == dir.normalize(path.rocks_tree_to_string(current)) then | ||
| 141 | use = true | ||
| 142 | end | ||
| 143 | if use then | ||
| 144 | table.insert(result, (fn(tree, ...)) or 0) | ||
| 145 | end | ||
| 146 | end | ||
| 147 | end | ||
| 148 | return result | ||
| 149 | end | ||
| 150 | |||
| 151 | 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 @@ | |||
| 1 | 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 | ||
| 2 | local persist = {} | ||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | function persist.run_file(filename, env) | ||
| 14 | local fd, open_err = io.open(filename) | ||
| 15 | if not fd then | ||
| 16 | return nil, open_err, "open" | ||
| 17 | end | ||
| 18 | local str, read_err = fd:read("*a") | ||
| 19 | fd:close() | ||
| 20 | if not str then | ||
| 21 | return nil, read_err, "open" | ||
| 22 | end | ||
| 23 | str = str:gsub("^#![^\n]*\n", "") | ||
| 24 | local chunk, ran, err | ||
| 25 | chunk, err = load(str, filename, "t", env) | ||
| 26 | if chunk then | ||
| 27 | ran, err = pcall(chunk) | ||
| 28 | end | ||
| 29 | if not chunk then | ||
| 30 | return nil, "Error loading file: " .. tostring(err), "load" | ||
| 31 | end | ||
| 32 | if not ran then | ||
| 33 | return nil, "Error running file: " .. tostring(err), "run" | ||
| 34 | end | ||
| 35 | return true, err | ||
| 36 | end | ||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | function persist.load_into_table(filename, tbl) | ||
| 49 | |||
| 50 | local result = tbl or {} | ||
| 51 | local globals = {} | ||
| 52 | local globals_mt = { | ||
| 53 | __index = function(_, k) | ||
| 54 | globals[k] = true | ||
| 55 | end, | ||
| 56 | } | ||
| 57 | local save_mt = getmetatable(result) | ||
| 58 | setmetatable(result, globals_mt) | ||
| 59 | |||
| 60 | local ok, err, errcode = persist.run_file(filename, result) | ||
| 61 | |||
| 62 | setmetatable(result, save_mt) | ||
| 63 | |||
| 64 | if not ok then | ||
| 65 | return nil, tostring(err), errcode | ||
| 66 | end | ||
| 67 | return result, globals | ||
| 68 | end | ||
| 69 | |||
| 70 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | local sysdetect = {} | ||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | |||
| 49 | |||
| 50 | |||
| 51 | |||
| 52 | |||
| 53 | |||
| 54 | |||
| 55 | |||
| 56 | |||
| 57 | |||
| 58 | |||
| 59 | local function hex(s) | ||
| 60 | return (s:gsub("$(..)", function(x) | ||
| 61 | return string.char(tonumber(x, 16)) | ||
| 62 | end)) | ||
| 63 | end | ||
| 64 | |||
| 65 | local function read_int8(fd) | ||
| 66 | if io.type(fd) == "closed file" then | ||
| 67 | return nil | ||
| 68 | end | ||
| 69 | local s = fd:read(1) | ||
| 70 | if not s then | ||
| 71 | fd:close() | ||
| 72 | return nil | ||
| 73 | end | ||
| 74 | return s:byte() | ||
| 75 | end | ||
| 76 | |||
| 77 | local function bytes2number(s, endian) | ||
| 78 | local r = 0 | ||
| 79 | if endian == "little" then | ||
| 80 | for i = #s, 1, -1 do | ||
| 81 | r = r * 256 + s:byte(i, i) | ||
| 82 | end | ||
| 83 | else | ||
| 84 | for i = 1, #s do | ||
| 85 | r = r * 256 + s:byte(i, i) | ||
| 86 | end | ||
| 87 | end | ||
| 88 | return r | ||
| 89 | end | ||
| 90 | |||
| 91 | local function read(fd, bytes, endian) | ||
| 92 | if io.type(fd) == "closed file" then | ||
| 93 | return nil | ||
| 94 | end | ||
| 95 | local s = fd:read(bytes) | ||
| 96 | if not s then | ||
| 97 | fd:close() | ||
| 98 | return nil | ||
| 99 | end | ||
| 100 | return bytes2number(s, endian) | ||
| 101 | end | ||
| 102 | |||
| 103 | local function read_int32le(fd) | ||
| 104 | return read(fd, 4, "little") | ||
| 105 | end | ||
| 106 | |||
| 107 | |||
| 108 | |||
| 109 | |||
| 110 | |||
| 111 | |||
| 112 | |||
| 113 | |||
| 114 | |||
| 115 | |||
| 116 | |||
| 117 | |||
| 118 | |||
| 119 | |||
| 120 | |||
| 121 | |||
| 122 | |||
| 123 | |||
| 124 | |||
| 125 | |||
| 126 | |||
| 127 | |||
| 128 | |||
| 129 | |||
| 130 | |||
| 131 | |||
| 132 | |||
| 133 | |||
| 134 | |||
| 135 | |||
| 136 | |||
| 137 | |||
| 138 | |||
| 139 | |||
| 140 | |||
| 141 | |||
| 142 | |||
| 143 | |||
| 144 | |||
| 145 | |||
| 146 | |||
| 147 | |||
| 148 | local endians = { | ||
| 149 | [0x01] = "little", | ||
| 150 | [0x02] = "big", | ||
| 151 | } | ||
| 152 | |||
| 153 | local e_osabi = { | ||
| 154 | [0x00] = "sysv", | ||
| 155 | [0x01] = "hpux", | ||
| 156 | [0x02] = "netbsd", | ||
| 157 | [0x03] = "linux", | ||
| 158 | [0x04] = "hurd", | ||
| 159 | [0x06] = "solaris", | ||
| 160 | [0x07] = "aix", | ||
| 161 | [0x08] = "irix", | ||
| 162 | [0x09] = "freebsd", | ||
| 163 | [0x0c] = "openbsd", | ||
| 164 | } | ||
| 165 | |||
| 166 | local e_machines = { | ||
| 167 | [0x02] = "sparc", | ||
| 168 | [0x03] = "x86", | ||
| 169 | [0x08] = "mips", | ||
| 170 | [0x0f] = "hppa", | ||
| 171 | [0x12] = "sparcv8", | ||
| 172 | [0x14] = "ppc", | ||
| 173 | [0x15] = "ppc64", | ||
| 174 | [0x16] = "s390", | ||
| 175 | [0x28] = "arm", | ||
| 176 | [0x2a] = "superh", | ||
| 177 | [0x2b] = "sparcv9", | ||
| 178 | [0x32] = "ia_64", | ||
| 179 | [0x3E] = "x86_64", | ||
| 180 | [0xB6] = "alpha", | ||
| 181 | [0xB7] = "aarch64", | ||
| 182 | [0xF3] = "riscv64", | ||
| 183 | [0x9026] = "alpha", | ||
| 184 | } | ||
| 185 | |||
| 186 | local SHT_NOTE = 7 | ||
| 187 | |||
| 188 | local function read_elf_section_headers(fd, hdr) | ||
| 189 | local endian = endians[hdr.endian] | ||
| 190 | local word = hdr.word | ||
| 191 | |||
| 192 | local strtab_offset | ||
| 193 | local sections = {} | ||
| 194 | local secarray = {} | ||
| 195 | for i = 0, hdr.e_shnum - 1 do | ||
| 196 | fd:seek("set", hdr.e_shoff + (i * hdr.e_shentsize)) | ||
| 197 | local section = {} | ||
| 198 | section.sh_name_off = read(fd, 4, endian) | ||
| 199 | section.sh_type = read(fd, 4, endian) | ||
| 200 | section.sh_flags = read(fd, word, endian) | ||
| 201 | section.sh_addr = read(fd, word, endian) | ||
| 202 | section.sh_offset = read(fd, word, endian) | ||
| 203 | section.sh_size = read(fd, word, endian) | ||
| 204 | section.sh_link = read(fd, 4, endian) | ||
| 205 | section.sh_info = read(fd, 4, endian) | ||
| 206 | if section.sh_type == SHT_NOTE then | ||
| 207 | fd:seek("set", section.sh_offset) | ||
| 208 | section.namesz = read(fd, 4, endian) | ||
| 209 | section.descsz = read(fd, 4, endian) | ||
| 210 | section.type = read(fd, 4, endian) | ||
| 211 | section.namedata = fd:read(section.namesz):gsub("%z.*", "") | ||
| 212 | section.descdata = fd:read(section.descsz) | ||
| 213 | elseif i == hdr.e_shstrndx then | ||
| 214 | strtab_offset = section.sh_offset | ||
| 215 | end | ||
| 216 | table.insert(secarray, section) | ||
| 217 | end | ||
| 218 | if strtab_offset then | ||
| 219 | for _, section in ipairs(secarray) do | ||
| 220 | fd:seek("set", strtab_offset + section.sh_name_off) | ||
| 221 | section.name = fd:read(32):gsub("%z.*", "") | ||
| 222 | sections[section.name] = section | ||
| 223 | end | ||
| 224 | end | ||
| 225 | return sections | ||
| 226 | end | ||
| 227 | |||
| 228 | local function detect_elf_system(fd, hdr, sections) | ||
| 229 | local system = e_osabi[hdr.osabi] | ||
| 230 | local endian = endians[hdr.endian] | ||
| 231 | |||
| 232 | if system == "sysv" then | ||
| 233 | local abitag = sections[".note.ABI-tag"] | ||
| 234 | if abitag then | ||
| 235 | if abitag.namedata == "GNU" and abitag.type == 1 and | ||
| 236 | abitag.descdata:sub(0, 4) == "\0\0\0\0" then | ||
| 237 | return "linux" | ||
| 238 | end | ||
| 239 | elseif sections[".SUNW_version"] or | ||
| 240 | sections[".SUNW_signature"] then | ||
| 241 | return "solaris" | ||
| 242 | elseif sections[".note.netbsd.ident"] then | ||
| 243 | return "netbsd" | ||
| 244 | elseif sections[".note.openbsd.ident"] then | ||
| 245 | return "openbsd" | ||
| 246 | elseif sections[".note.tag"] and | ||
| 247 | sections[".note.tag"].namedata == "DragonFly" then | ||
| 248 | return "dragonfly" | ||
| 249 | end | ||
| 250 | |||
| 251 | local gnu_version_r = sections[".gnu.version_r"] | ||
| 252 | if gnu_version_r then | ||
| 253 | |||
| 254 | local dynstr = sections[".dynstr"].sh_offset | ||
| 255 | |||
| 256 | local idx = 0 | ||
| 257 | for _ = 0, gnu_version_r.sh_info - 1 do | ||
| 258 | fd:seek("set", gnu_version_r.sh_offset + idx) | ||
| 259 | assert(read(fd, 2, endian)) | ||
| 260 | local vn_cnt = read(fd, 2, endian) | ||
| 261 | local vn_file = read(fd, 4, endian) | ||
| 262 | local vn_next = read(fd, 2, endian) | ||
| 263 | |||
| 264 | fd:seek("set", dynstr + vn_file) | ||
| 265 | local libname = fd:read(64):gsub("%z.*", "") | ||
| 266 | |||
| 267 | if hdr.e_type == 0x03 and libname == "libroot.so" then | ||
| 268 | return "haiku" | ||
| 269 | elseif libname:match("linux") then | ||
| 270 | return "linux" | ||
| 271 | end | ||
| 272 | |||
| 273 | idx = idx + (vn_next * (vn_cnt + 1)) | ||
| 274 | end | ||
| 275 | end | ||
| 276 | |||
| 277 | local procfile = io.open("/proc/sys/kernel/ostype") | ||
| 278 | if procfile then | ||
| 279 | local version = procfile:read(6) | ||
| 280 | procfile:close() | ||
| 281 | if version == "Linux\n" then | ||
| 282 | return "linux" | ||
| 283 | end | ||
| 284 | end | ||
| 285 | end | ||
| 286 | |||
| 287 | return system | ||
| 288 | end | ||
| 289 | |||
| 290 | local function read_elf_header(fd) | ||
| 291 | local hdr = {} | ||
| 292 | |||
| 293 | hdr.bits = read_int8(fd) | ||
| 294 | hdr.endian = read_int8(fd) | ||
| 295 | hdr.elf_version = read_int8(fd) | ||
| 296 | if hdr.elf_version ~= 1 then | ||
| 297 | return nil | ||
| 298 | end | ||
| 299 | hdr.osabi = read_int8(fd) | ||
| 300 | if not hdr.osabi then | ||
| 301 | return nil | ||
| 302 | end | ||
| 303 | |||
| 304 | local endian = endians[hdr.endian] | ||
| 305 | fd:seek("set", 0x10) | ||
| 306 | hdr.e_type = read(fd, 2, endian) | ||
| 307 | local machine = read(fd, 2, endian) | ||
| 308 | local processor = e_machines[machine] or "unknown" | ||
| 309 | if endian == "little" and processor == "ppc64" then | ||
| 310 | processor = "ppc64le" | ||
| 311 | end | ||
| 312 | |||
| 313 | local elfversion = read(fd, 4, endian) | ||
| 314 | if elfversion ~= 1 then | ||
| 315 | return nil | ||
| 316 | end | ||
| 317 | |||
| 318 | local word = (hdr.bits == 1) and 4 or 8 | ||
| 319 | hdr.word = word | ||
| 320 | |||
| 321 | hdr.e_entry = read(fd, word, endian) | ||
| 322 | hdr.e_phoff = read(fd, word, endian) | ||
| 323 | hdr.e_shoff = read(fd, word, endian) | ||
| 324 | hdr.e_flags = read(fd, 4, endian) | ||
| 325 | hdr.e_ehsize = read(fd, 2, endian) | ||
| 326 | hdr.e_phentsize = read(fd, 2, endian) | ||
| 327 | hdr.e_phnum = read(fd, 2, endian) | ||
| 328 | hdr.e_shentsize = read(fd, 2, endian) | ||
| 329 | hdr.e_shnum = read(fd, 2, endian) | ||
| 330 | hdr.e_shstrndx = read(fd, 2, endian) | ||
| 331 | |||
| 332 | return hdr, processor | ||
| 333 | end | ||
| 334 | |||
| 335 | local function detect_elf(fd) | ||
| 336 | local hdr, processor = read_elf_header(fd) | ||
| 337 | if not hdr then | ||
| 338 | return nil | ||
| 339 | end | ||
| 340 | local sections = read_elf_section_headers(fd, hdr) | ||
| 341 | local system = detect_elf_system(fd, hdr, sections) | ||
| 342 | return system, processor | ||
| 343 | end | ||
| 344 | |||
| 345 | |||
| 346 | |||
| 347 | |||
| 348 | |||
| 349 | local mach_l64 = { | ||
| 350 | [7] = "x86_64", | ||
| 351 | [12] = "aarch64", | ||
| 352 | } | ||
| 353 | |||
| 354 | local mach_b64 = { | ||
| 355 | [0] = "ppc64", | ||
| 356 | } | ||
| 357 | |||
| 358 | local mach_l32 = { | ||
| 359 | [7] = "x86", | ||
| 360 | [12] = "arm", | ||
| 361 | } | ||
| 362 | |||
| 363 | local mach_b32 = { | ||
| 364 | [0] = "ppc", | ||
| 365 | } | ||
| 366 | |||
| 367 | local function detect_mach(magic, fd) | ||
| 368 | if not magic then | ||
| 369 | return nil | ||
| 370 | end | ||
| 371 | |||
| 372 | if magic == hex("$CA$FE$BA$BE") then | ||
| 373 | |||
| 374 | fd:seek("set", 0x12) | ||
| 375 | local offs = read_int8(fd) | ||
| 376 | if not offs then | ||
| 377 | return nil | ||
| 378 | end | ||
| 379 | fd:seek("set", offs * 256) | ||
| 380 | magic = fd:read(4) | ||
| 381 | return detect_mach(magic, fd) | ||
| 382 | end | ||
| 383 | |||
| 384 | local cputype = read_int8(fd) | ||
| 385 | |||
| 386 | if magic == hex("$CF$FA$ED$FE") then | ||
| 387 | return "macosx", mach_l64[cputype] or "unknown" | ||
| 388 | elseif magic == hex("$FE$ED$CF$FA") then | ||
| 389 | return "macosx", mach_b64[cputype] or "unknown" | ||
| 390 | elseif magic == hex("$CE$FA$ED$FE") then | ||
| 391 | return "macosx", mach_l32[cputype] or "unknown" | ||
| 392 | elseif magic == hex("$FE$ED$FA$CE") then | ||
| 393 | return "macosx", mach_b32[cputype] or "unknown" | ||
| 394 | end | ||
| 395 | end | ||
| 396 | |||
| 397 | |||
| 398 | |||
| 399 | |||
| 400 | |||
| 401 | local pe_machine = { | ||
| 402 | [0x8664] = "x86_64", | ||
| 403 | [0x01c0] = "arm", | ||
| 404 | [0x01c4] = "armv7l", | ||
| 405 | [0xaa64] = "arm64", | ||
| 406 | [0x014c] = "x86", | ||
| 407 | } | ||
| 408 | |||
| 409 | local function detect_pe(fd) | ||
| 410 | fd:seek("set", 60) | ||
| 411 | local peoffset = read_int32le(fd) | ||
| 412 | if not peoffset then | ||
| 413 | return nil | ||
| 414 | end | ||
| 415 | local system = "windows" | ||
| 416 | fd:seek("set", peoffset + 4) | ||
| 417 | local machine = read(fd, 2, "little") | ||
| 418 | local processor = pe_machine[machine] | ||
| 419 | |||
| 420 | local rdata_pos_s = fd:read(736):match(".rdata%z%z............(....)") | ||
| 421 | if rdata_pos_s then | ||
| 422 | local rdata_pos = bytes2number(rdata_pos_s, "little") | ||
| 423 | fd:seek("set", rdata_pos) | ||
| 424 | local data = fd:read(512) | ||
| 425 | if data:match("cygwin") or data:match("cyggcc") then | ||
| 426 | system = "cygwin" | ||
| 427 | end | ||
| 428 | end | ||
| 429 | |||
| 430 | return system, processor or "unknown" | ||
| 431 | end | ||
| 432 | |||
| 433 | |||
| 434 | |||
| 435 | |||
| 436 | |||
| 437 | function sysdetect.detect_file(file) | ||
| 438 | local fd = io.open(file, "rb") | ||
| 439 | if not fd then | ||
| 440 | return nil | ||
| 441 | end | ||
| 442 | local magic = fd:read(4) | ||
| 443 | if magic == hex("$7FELF") then | ||
| 444 | return detect_elf(fd) | ||
| 445 | end | ||
| 446 | if magic == hex("MZ$90$00") then | ||
| 447 | return detect_pe(fd) | ||
| 448 | end | ||
| 449 | return detect_mach(magic, fd) | ||
| 450 | end | ||
| 451 | |||
| 452 | local cache_system | ||
| 453 | local cache_processor | ||
| 454 | |||
| 455 | function sysdetect.detect(input_file) | ||
| 456 | local dirsep = package.config:sub(1, 1) | ||
| 457 | local files | ||
| 458 | |||
| 459 | if input_file then | ||
| 460 | files = { input_file } | ||
| 461 | else | ||
| 462 | if cache_system then | ||
| 463 | return cache_system, cache_processor | ||
| 464 | end | ||
| 465 | |||
| 466 | local PATHsep | ||
| 467 | local interp = arg and arg[-1] | ||
| 468 | if dirsep == "/" then | ||
| 469 | |||
| 470 | files = { | ||
| 471 | "/bin/sh", | ||
| 472 | "/proc/self/exe", | ||
| 473 | } | ||
| 474 | PATHsep = ":" | ||
| 475 | else | ||
| 476 | |||
| 477 | local systemroot = os.getenv("SystemRoot") | ||
| 478 | files = { | ||
| 479 | systemroot .. "\\system32\\notepad.exe", | ||
| 480 | systemroot .. "\\explorer.exe", | ||
| 481 | } | ||
| 482 | if interp and not interp:lower():match("exe$") then | ||
| 483 | interp = interp .. ".exe" | ||
| 484 | end | ||
| 485 | PATHsep = ";" | ||
| 486 | end | ||
| 487 | if interp then | ||
| 488 | if interp:match(dirsep) then | ||
| 489 | |||
| 490 | table.insert(files, 1, interp) | ||
| 491 | else | ||
| 492 | for d in (os.getenv("PATH") or ""):gmatch("[^" .. PATHsep .. "]+") do | ||
| 493 | table.insert(files, d .. dirsep .. interp) | ||
| 494 | end | ||
| 495 | end | ||
| 496 | end | ||
| 497 | end | ||
| 498 | for _, f in ipairs(files) do | ||
| 499 | local system, processor = sysdetect.detect_file(f) | ||
| 500 | if system then | ||
| 501 | cache_system = system | ||
| 502 | cache_processor = processor | ||
| 503 | return system, processor | ||
| 504 | end | ||
| 505 | end | ||
| 506 | end | ||
| 507 | |||
| 508 | 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 @@ | |||
| 1 | |||
| 2 | |||
| 3 | local query = {Query = {}, } | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | 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 @@ | |||
| 1 | |||
| 2 | |||
| 3 | local result = {Result = {}, } | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | 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 @@ | |||
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | local rockspec = {Description = {}, Source = {}, Test = {}, Dependencies = {}, Hooks = {}, Deploy = {}, Rockspec = {}, } | ||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | |||
| 49 | |||
| 50 | |||
| 51 | |||
| 52 | |||
| 53 | |||
| 54 | |||
| 55 | |||
| 56 | |||
| 57 | |||
| 58 | |||
| 59 | |||
| 60 | |||
| 61 | |||
| 62 | |||
| 63 | |||
| 64 | |||
| 65 | |||
| 66 | |||
| 67 | |||
| 68 | |||
| 69 | |||
| 70 | |||
| 71 | |||
| 72 | |||
| 73 | |||
| 74 | |||
| 75 | |||
| 76 | |||
| 77 | |||
| 78 | 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 @@ | |||
| 1 | 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 | ||
| 2 | local util = {} | ||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | local dir_sep = package.config:sub(1, 1) | ||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | function util.popen_read(cmd, spec) | ||
| 20 | local tmpfile = (dir_sep == "\\") and | ||
| 21 | (os.getenv("TMP") .. "/luarocks-" .. tostring(math.floor(math.random() * 10000))) or | ||
| 22 | os.tmpname() | ||
| 23 | os.execute(cmd .. " > " .. tmpfile) | ||
| 24 | local fd = io.open(tmpfile, "rb") | ||
| 25 | if not fd then | ||
| 26 | os.remove(tmpfile) | ||
| 27 | return "" | ||
| 28 | end | ||
| 29 | local out = fd:read(spec or "*l") | ||
| 30 | fd:close() | ||
| 31 | os.remove(tmpfile) | ||
| 32 | return out or "" | ||
| 33 | end | ||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | |||
| 49 | |||
| 50 | |||
| 51 | |||
| 52 | function util.show_table(t, tname, top_indent) | ||
| 53 | local cart | ||
| 54 | local autoref | ||
| 55 | |||
| 56 | local function is_empty_table(tbl) return next(tbl) == nil end | ||
| 57 | |||
| 58 | local function basic_serialize(o) | ||
| 59 | local so = tostring(o) | ||
| 60 | if type(o) == "function" then | ||
| 61 | local info = debug and debug.getinfo(o, "S") | ||
| 62 | if not info then | ||
| 63 | return ("%q"):format(so) | ||
| 64 | end | ||
| 65 | |||
| 66 | if info.what == "C" then | ||
| 67 | return ("%q"):format(so .. ", C function") | ||
| 68 | else | ||
| 69 | |||
| 70 | return ("%q"):format(so .. ", defined in (" .. info.linedefined .. "-" .. info.lastlinedefined .. ")" .. info.source) | ||
| 71 | end | ||
| 72 | elseif type(o) == "number" then | ||
| 73 | return so | ||
| 74 | else | ||
| 75 | return ("%q"):format(so) | ||
| 76 | end | ||
| 77 | end | ||
| 78 | |||
| 79 | local function add_to_cart(value, name, indent, saved, field) | ||
| 80 | indent = indent or "" | ||
| 81 | saved = saved or {} | ||
| 82 | field = field or name | ||
| 83 | |||
| 84 | cart = cart .. indent .. field | ||
| 85 | |||
| 86 | if not (type(value) == "table") then | ||
| 87 | cart = cart .. " = " .. basic_serialize(value) .. ";\n" | ||
| 88 | else | ||
| 89 | if saved[value] then | ||
| 90 | cart = cart .. " = {}; -- " .. saved[value] .. " (self reference)\n" | ||
| 91 | autoref = autoref .. name .. " = " .. saved[value] .. ";\n" | ||
| 92 | else | ||
| 93 | saved[value] = name | ||
| 94 | if is_empty_table(value) then | ||
| 95 | cart = cart .. " = {};\n" | ||
| 96 | else | ||
| 97 | cart = cart .. " = {\n" | ||
| 98 | for k, v in pairs(value) do | ||
| 99 | k = basic_serialize(k) | ||
| 100 | local fname = ("%s[%s]"):format(name, k) | ||
| 101 | field = ("[%s]"):format(k) | ||
| 102 | |||
| 103 | add_to_cart(v, fname, indent .. " ", saved, field) | ||
| 104 | end | ||
| 105 | cart = cart .. indent .. "};\n" | ||
| 106 | end | ||
| 107 | end | ||
| 108 | end | ||
| 109 | end | ||
| 110 | |||
| 111 | tname = tname or "__unnamed__" | ||
| 112 | if not (type(t) == "table") then | ||
| 113 | return tname .. " = " .. basic_serialize(t) | ||
| 114 | end | ||
| 115 | cart, autoref = "", "" | ||
| 116 | add_to_cart(t, tname, top_indent) | ||
| 117 | return cart .. autoref | ||
| 118 | end | ||
| 119 | |||
| 120 | |||
| 121 | |||
| 122 | |||
| 123 | |||
| 124 | |||
| 125 | function util.matchquote(s) | ||
| 126 | return (s:gsub("[?%-+*%[%].%%()$^]", "%%%1")) | ||
| 127 | end | ||
| 128 | |||
| 129 | |||
| 130 | |||
| 131 | |||
| 132 | |||
| 133 | function util.deep_merge(dst, src) | ||
| 134 | for k, v in pairs(src) do | ||
| 135 | if type(v) == "table" then | ||
| 136 | local dstk = dst[k] | ||
| 137 | if dstk == nil then | ||
| 138 | dst[k] = {} | ||
| 139 | dstk = dst[k] | ||
| 140 | end | ||
| 141 | if type(dstk) == "table" then | ||
| 142 | util.deep_merge(dstk, v) | ||
| 143 | else | ||
| 144 | dst[k] = v | ||
| 145 | end | ||
| 146 | else | ||
| 147 | dst[k] = v | ||
| 148 | end | ||
| 149 | end | ||
| 150 | end | ||
| 151 | |||
| 152 | |||
| 153 | |||
| 154 | |||
| 155 | |||
| 156 | function util.deep_merge_under(dst, src) | ||
| 157 | for k, v in pairs(src) do | ||
| 158 | if type(v) == "table" then | ||
| 159 | local dstk = dst[k] | ||
| 160 | if dstk == nil then | ||
| 161 | dst[k] = {} | ||
| 162 | dstk = dst[k] | ||
| 163 | end | ||
| 164 | if type(dstk) == "table" then | ||
| 165 | util.deep_merge_under(dstk, v) | ||
| 166 | end | ||
| 167 | elseif dst[k] == nil then | ||
| 168 | dst[k] = v | ||
| 169 | end | ||
| 170 | end | ||
| 171 | end | ||
| 172 | |||
| 173 | |||
| 174 | |||
| 175 | function util.split_string(str, delim, maxNb) | ||
| 176 | |||
| 177 | if string.find(str, delim) == nil then | ||
| 178 | return { str } | ||
| 179 | end | ||
| 180 | if maxNb == nil or maxNb < 1 then | ||
| 181 | maxNb = 0 | ||
| 182 | end | ||
| 183 | local result = {} | ||
| 184 | local pat = "(.-)" .. delim .. "()" | ||
| 185 | local nb = 0 | ||
| 186 | local lastPos | ||
| 187 | for part, pos in string.gmatch(str, pat) do | ||
| 188 | nb = nb + 1 | ||
| 189 | result[nb] = part | ||
| 190 | lastPos = tonumber(pos) | ||
| 191 | if nb == maxNb then break end | ||
| 192 | end | ||
| 193 | |||
| 194 | if nb ~= maxNb then | ||
| 195 | result[nb + 1] = string.sub(str, lastPos) | ||
| 196 | end | ||
| 197 | return result | ||
| 198 | end | ||
| 199 | |||
| 200 | |||
| 201 | |||
| 202 | |||
| 203 | |||
| 204 | |||
| 205 | |||
| 206 | |||
| 207 | |||
| 208 | |||
| 209 | |||
| 210 | function util.cleanup_path(list, sep, lua_version, keep_first) | ||
| 211 | |||
| 212 | list = list:gsub(dir_sep, "/") | ||
| 213 | |||
| 214 | local parts = util.split_string(list, sep) | ||
| 215 | local final, entries = {}, {} | ||
| 216 | local start, stop, step | ||
| 217 | |||
| 218 | if keep_first then | ||
| 219 | start, stop, step = 1, #parts, 1 | ||
| 220 | else | ||
| 221 | start, stop, step = #parts, 1, -1 | ||
| 222 | end | ||
| 223 | |||
| 224 | for i = start, stop, step do | ||
| 225 | local part = parts[i]:gsub("//", "/") | ||
| 226 | if lua_version then | ||
| 227 | part = part:gsub("/lua/([%d.]+)/", function(part_version) | ||
| 228 | if part_version:sub(1, #lua_version) ~= lua_version then | ||
| 229 | return "/lua/" .. lua_version .. "/" | ||
| 230 | end | ||
| 231 | end) | ||
| 232 | end | ||
| 233 | if not entries[part] then | ||
| 234 | local at = keep_first and #final + 1 or 1 | ||
| 235 | table.insert(final, at, part) | ||
| 236 | entries[part] = true | ||
| 237 | end | ||
| 238 | end | ||
| 239 | |||
| 240 | return (table.concat(final, sep):gsub("/", dir_sep)) | ||
| 241 | end | ||
| 242 | |||
| 243 | |||
| 244 | |||
| 245 | |||
| 246 | function util.keys(tbl) | ||
| 247 | local ks = {} | ||
| 248 | for k, _ in pairs(tbl) do | ||
| 249 | table.insert(ks, k) | ||
| 250 | end | ||
| 251 | return ks | ||
| 252 | end | ||
| 253 | |||
| 254 | |||
| 255 | function util.printerr(...) | ||
| 256 | io.stderr:write(table.concat({ ... }, "\t")) | ||
| 257 | io.stderr:write("\n") | ||
| 258 | end | ||
| 259 | |||
| 260 | |||
| 261 | |||
| 262 | function util.warning(msg) | ||
| 263 | util.printerr("Warning: " .. msg) | ||
| 264 | end | ||
| 265 | |||
| 266 | |||
| 267 | local function default_sort(a, b) | ||
| 268 | local ta = type(a) | ||
| 269 | local tb = type(b) | ||
| 270 | if ta == "number" and tb == "number" then | ||
| 271 | return tonumber(a) < tonumber(b) | ||
| 272 | elseif ta == "number" then | ||
| 273 | return true | ||
| 274 | elseif tb == "number" then | ||
| 275 | return false | ||
| 276 | else | ||
| 277 | return tostring(a) < tostring(b) | ||
| 278 | end | ||
| 279 | end | ||
| 280 | |||
| 281 | |||
| 282 | |||
| 283 | |||
| 284 | |||
| 285 | |||
| 286 | |||
| 287 | |||
| 288 | |||
| 289 | |||
| 290 | |||
| 291 | function util.sortedpairs(tbl, sort_by) | ||
| 292 | local keys = util.keys(tbl) | ||
| 293 | local sub_orders = nil | ||
| 294 | |||
| 295 | if sort_by == nil then | ||
| 296 | table.sort(keys, default_sort) | ||
| 297 | elseif type(sort_by) == "function" then | ||
| 298 | table.sort(keys, sort_by) | ||
| 299 | else | ||
| 300 | |||
| 301 | |||
| 302 | sub_orders = sort_by.sub_orders | ||
| 303 | |||
| 304 | local seen_ordered_key = {} | ||
| 305 | |||
| 306 | local my_ordered_keys = {} | ||
| 307 | |||
| 308 | for _, key in ipairs(sort_by) do | ||
| 309 | if tbl[key] then | ||
| 310 | seen_ordered_key[key] = true | ||
| 311 | table.insert(my_ordered_keys, key) | ||
| 312 | end | ||
| 313 | end | ||
| 314 | |||
| 315 | table.sort(keys, default_sort) | ||
| 316 | |||
| 317 | for _, key in ipairs(keys) do | ||
| 318 | if not seen_ordered_key[key] then | ||
| 319 | table.insert(my_ordered_keys, key) | ||
| 320 | end | ||
| 321 | end | ||
| 322 | |||
| 323 | keys = my_ordered_keys | ||
| 324 | end | ||
| 325 | |||
| 326 | local i = 1 | ||
| 327 | return function() | ||
| 328 | local key = keys[i] | ||
| 329 | i = i + 1 | ||
| 330 | return key, tbl[key], sub_orders and sub_orders[key] | ||
| 331 | end | ||
| 332 | end | ||
| 333 | |||
| 334 | 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 @@ | |||
| 1 | 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 = {} | ||
| 2 | |||
| 3 | |||
| 4 | local util = require("luarocks.core.util") | ||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | local deltas = { | ||
| 10 | dev = 120000000, | ||
| 11 | scm = 110000000, | ||
| 12 | cvs = 100000000, | ||
| 13 | rc = -1000, | ||
| 14 | pre = -10000, | ||
| 15 | beta = -100000, | ||
| 16 | alpha = -1000000, | ||
| 17 | } | ||
| 18 | |||
| 19 | local version_mt = { | ||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | __eq = function(v1, v2) | ||
| 28 | if #v1 ~= #v2 then | ||
| 29 | return false | ||
| 30 | end | ||
| 31 | for i = 1, #v1 do | ||
| 32 | if v1[i] ~= v2[i] then | ||
| 33 | return false | ||
| 34 | end | ||
| 35 | end | ||
| 36 | if v1.revision and v2.revision then | ||
| 37 | return (v1.revision == v2.revision) | ||
| 38 | end | ||
| 39 | return true | ||
| 40 | end, | ||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | __lt = function(v1, v2) | ||
| 49 | for i = 1, math.max(#v1, #v2) do | ||
| 50 | local v1i, v2i = v1[i] or 0, v2[i] or 0 | ||
| 51 | if v1i ~= v2i then | ||
| 52 | return (v1i < v2i) | ||
| 53 | end | ||
| 54 | end | ||
| 55 | if v1.revision and v2.revision then | ||
| 56 | return (v1.revision < v2.revision) | ||
| 57 | end | ||
| 58 | return false | ||
| 59 | end, | ||
| 60 | |||
| 61 | |||
| 62 | |||
| 63 | __le = function(v1, v2) | ||
| 64 | return not (v2 < v1) | ||
| 65 | end, | ||
| 66 | |||
| 67 | |||
| 68 | |||
| 69 | __tostring = function(v) | ||
| 70 | return v.string | ||
| 71 | end, | ||
| 72 | } | ||
| 73 | |||
| 74 | local version_cache = {} | ||
| 75 | setmetatable(version_cache, { | ||
| 76 | __mode = "kv", | ||
| 77 | }) | ||
| 78 | |||
| 79 | |||
| 80 | |||
| 81 | |||
| 82 | |||
| 83 | |||
| 84 | |||
| 85 | |||
| 86 | |||
| 87 | |||
| 88 | |||
| 89 | |||
| 90 | function vers.parse_version(vstring) | ||
| 91 | if not vstring then return nil end | ||
| 92 | |||
| 93 | local cached = version_cache[vstring] | ||
| 94 | if cached then | ||
| 95 | return cached | ||
| 96 | end | ||
| 97 | |||
| 98 | local version = {} | ||
| 99 | local i = 1 | ||
| 100 | |||
| 101 | local function add_token(number) | ||
| 102 | version[i] = version[i] and version[i] + number / 100000 or number | ||
| 103 | i = i + 1 | ||
| 104 | end | ||
| 105 | |||
| 106 | |||
| 107 | local v = vstring:match("^%s*(.*)%s*$") | ||
| 108 | version.string = v | ||
| 109 | |||
| 110 | local main, revision = v:match("(.*)%-(%d+)$") | ||
| 111 | if revision then | ||
| 112 | v = main | ||
| 113 | version.revision = tonumber(revision) | ||
| 114 | end | ||
| 115 | while #v > 0 do | ||
| 116 | |||
| 117 | local token, rest = v:match("^(%d+)[%.%-%_]*(.*)") | ||
| 118 | if token then | ||
| 119 | add_token(tonumber(token)) | ||
| 120 | else | ||
| 121 | |||
| 122 | token, rest = v:match("^(%a+)[%.%-%_]*(.*)") | ||
| 123 | if not token then | ||
| 124 | util.warning("version number '" .. v .. "' could not be parsed.") | ||
| 125 | version[i] = 0 | ||
| 126 | break | ||
| 127 | end | ||
| 128 | version[i] = deltas[token] or (token:byte() / 1000) | ||
| 129 | end | ||
| 130 | v = rest | ||
| 131 | end | ||
| 132 | setmetatable(version, version_mt) | ||
| 133 | version_cache[vstring] = version | ||
| 134 | return version | ||
| 135 | end | ||
| 136 | |||
| 137 | |||
| 138 | |||
| 139 | |||
| 140 | |||
| 141 | function vers.compare_versions(a, b) | ||
| 142 | if a == b then | ||
| 143 | return false | ||
| 144 | end | ||
| 145 | return vers.parse_version(b) < vers.parse_version(a) | ||
| 146 | end | ||
| 147 | |||
| 148 | |||
| 149 | |||
| 150 | |||
| 151 | |||
| 152 | |||
| 153 | |||
| 154 | |||
| 155 | |||
| 156 | |||
| 157 | |||
| 158 | |||
| 159 | |||
| 160 | local function partial_match(input_version, input_requested) | ||
| 161 | |||
| 162 | local version, requested | ||
| 163 | |||
| 164 | if not (type(input_version) == "table") then version = vers.parse_version(input_version) | ||
| 165 | else version = input_version end | ||
| 166 | if not (type(input_requested) == "table") then requested = vers.parse_version(input_requested) | ||
| 167 | else requested = input_requested end | ||
| 168 | if not (type(version) == "table") or not (type(requested) == "table") then return false end | ||
| 169 | |||
| 170 | for i, ri in ipairs(requested) do | ||
| 171 | local vi = version[i] or 0 | ||
| 172 | if ri ~= vi then return false end | ||
| 173 | end | ||
| 174 | if requested.revision then | ||
| 175 | return requested.revision == version.revision | ||
| 176 | end | ||
| 177 | return true | ||
| 178 | end | ||
| 179 | |||
| 180 | |||
| 181 | |||
| 182 | |||
| 183 | |||
| 184 | |||
| 185 | function vers.match_constraints(version, constraints) | ||
| 186 | local ok = true | ||
| 187 | setmetatable(version, version_mt) | ||
| 188 | for _, constr in ipairs(constraints) do | ||
| 189 | local constr_version, constr_op = constr.version, constr.op | ||
| 190 | local cv | ||
| 191 | if type(constr_version) == "string" then | ||
| 192 | cv = vers.parse_version(constr_version) | ||
| 193 | constr.version = cv | ||
| 194 | else | ||
| 195 | cv = constr_version | ||
| 196 | end | ||
| 197 | setmetatable(cv, version_mt) | ||
| 198 | if constr_op == "==" then ok = version == cv | ||
| 199 | elseif constr_op == "~=" then ok = version ~= cv | ||
| 200 | elseif constr_op == ">" then ok = cv < version | ||
| 201 | elseif constr_op == "<" then ok = version < cv | ||
| 202 | elseif constr_op == ">=" then ok = cv <= version | ||
| 203 | elseif constr_op == "<=" then ok = version <= cv | ||
| 204 | elseif constr_op == "~>" then ok = partial_match(version, cv) | ||
| 205 | end | ||
| 206 | if not ok then break end | ||
| 207 | end | ||
| 208 | return ok | ||
| 209 | end | ||
| 210 | |||
| 211 | 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 @@ | |||
| 1 | local deplocks = {} | ||
| 2 | |||
| 3 | local fs = require("luarocks.fs") | ||
| 4 | local dir = require("luarocks.dir") | ||
| 5 | local util = require("luarocks.util") | ||
| 6 | local persist = require("luarocks.persist") | ||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | local deptable = {} | ||
| 11 | local deptable_mode = "start" | ||
| 12 | local deplock_abs_filename | ||
| 13 | local deplock_root_rock_name | ||
| 14 | |||
| 15 | function deplocks.init(root_rock_name, dirname) | ||
| 16 | if deptable_mode ~= "start" then | ||
| 17 | return | ||
| 18 | end | ||
| 19 | deptable_mode = "create" | ||
| 20 | |||
| 21 | local filename = dir.path(dirname, "luarocks.lock") | ||
| 22 | deplock_abs_filename = fs.absolute_name(filename) | ||
| 23 | deplock_root_rock_name = root_rock_name | ||
| 24 | |||
| 25 | deptable = {} | ||
| 26 | end | ||
| 27 | |||
| 28 | function deplocks.get_abs_filename(root_rock_name) | ||
| 29 | if root_rock_name == deplock_root_rock_name then | ||
| 30 | return deplock_abs_filename | ||
| 31 | end | ||
| 32 | end | ||
| 33 | |||
| 34 | function deplocks.load(root_rock_name, dirname) | ||
| 35 | if deptable_mode ~= "start" then | ||
| 36 | return true, nil | ||
| 37 | end | ||
| 38 | deptable_mode = "locked" | ||
| 39 | |||
| 40 | local filename = dir.path(dirname, "luarocks.lock") | ||
| 41 | local _, result, errcode = persist.run_file(filename, {}) | ||
| 42 | if errcode == "load" or errcode == "run" then | ||
| 43 | |||
| 44 | return nil, nil, "Could not read existing lockfile " .. filename | ||
| 45 | end | ||
| 46 | |||
| 47 | if errcode == "open" then | ||
| 48 | |||
| 49 | return true, nil | ||
| 50 | end | ||
| 51 | |||
| 52 | deplock_abs_filename = fs.absolute_name(filename) | ||
| 53 | deplock_root_rock_name = root_rock_name | ||
| 54 | |||
| 55 | deptable = result | ||
| 56 | return true, filename | ||
| 57 | end | ||
| 58 | |||
| 59 | function deplocks.add(depskey, name, version) | ||
| 60 | if deptable_mode == "locked" then | ||
| 61 | return | ||
| 62 | end | ||
| 63 | |||
| 64 | local dk = deptable[depskey] | ||
| 65 | if not dk then | ||
| 66 | dk = {} | ||
| 67 | deptable[depskey] = dk | ||
| 68 | end | ||
| 69 | |||
| 70 | if type(dk) == "table" and not dk[name] then | ||
| 71 | dk[name] = version | ||
| 72 | end | ||
| 73 | end | ||
| 74 | |||
| 75 | function deplocks.get(depskey, name) | ||
| 76 | local dk = deptable[depskey] | ||
| 77 | if not dk then | ||
| 78 | return nil | ||
| 79 | end | ||
| 80 | if type(dk) == "table" then | ||
| 81 | return dk[name] | ||
| 82 | else | ||
| 83 | return dk | ||
| 84 | end | ||
| 85 | end | ||
| 86 | |||
| 87 | function deplocks.write_file() | ||
| 88 | if deptable_mode ~= "create" then | ||
| 89 | return true | ||
| 90 | end | ||
| 91 | |||
| 92 | return persist.save_as_module(deplock_abs_filename, deptable) | ||
| 93 | end | ||
| 94 | |||
| 95 | |||
| 96 | function deplocks.proxy(depskey) | ||
| 97 | return setmetatable({}, { | ||
| 98 | __index = function(_, k) | ||
| 99 | return deplocks.get(depskey, k) | ||
| 100 | end, | ||
| 101 | __newindex = function(_, k, v) | ||
| 102 | return deplocks.add(depskey, k, v) | ||
| 103 | end, | ||
| 104 | }) | ||
| 105 | end | ||
| 106 | |||
| 107 | function deplocks.each(depskey) | ||
| 108 | return util.sortedpairs(deptable[depskey] or {}) | ||
| 109 | end | ||
| 110 | |||
| 111 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local deps = {} | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | local cfg = require("luarocks.core.cfg") | ||
| 8 | local manif = require("luarocks.manif") | ||
| 9 | local path = require("luarocks.path") | ||
| 10 | local dir = require("luarocks.dir") | ||
| 11 | local fun = require("luarocks.fun") | ||
| 12 | local util = require("luarocks.util") | ||
| 13 | local vers = require("luarocks.core.vers") | ||
| 14 | local queries = require("luarocks.queries") | ||
| 15 | local deplocks = require("luarocks.deplocks") | ||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | |||
| 49 | |||
| 50 | |||
| 51 | |||
| 52 | |||
| 53 | |||
| 54 | |||
| 55 | |||
| 56 | |||
| 57 | local function prepare_get_versions(deps_mode, rocks_provided, depskey, skip_set) | ||
| 58 | |||
| 59 | return function(dep) | ||
| 60 | local versions, locations | ||
| 61 | local provided = rocks_provided[dep.name] | ||
| 62 | if provided then | ||
| 63 | |||
| 64 | versions, locations = { provided }, {} | ||
| 65 | else | ||
| 66 | if deps_mode == "none" then | ||
| 67 | deps_mode = "one" | ||
| 68 | end | ||
| 69 | versions, locations = manif.get_versions(dep, deps_mode) | ||
| 70 | end | ||
| 71 | |||
| 72 | if skip_set and skip_set[dep.name] then | ||
| 73 | for i = #versions, 1, -1 do | ||
| 74 | local v = versions[i] | ||
| 75 | if skip_set[dep.name][v] then | ||
| 76 | table.remove(versions, i) | ||
| 77 | end | ||
| 78 | end | ||
| 79 | end | ||
| 80 | |||
| 81 | local lockversion = deplocks.get(depskey, dep.name) | ||
| 82 | |||
| 83 | return versions, locations, lockversion, provided ~= nil | ||
| 84 | end | ||
| 85 | end | ||
| 86 | |||
| 87 | |||
| 88 | |||
| 89 | |||
| 90 | |||
| 91 | |||
| 92 | |||
| 93 | |||
| 94 | |||
| 95 | |||
| 96 | |||
| 97 | |||
| 98 | |||
| 99 | local function match_dep(depq, | ||
| 100 | get_versions) | ||
| 101 | |||
| 102 | local versions, locations, lockversion, provided = get_versions(depq) | ||
| 103 | |||
| 104 | local latest_version | ||
| 105 | local latest_vstring | ||
| 106 | for _, vstring in ipairs(versions) do | ||
| 107 | local version = vers.parse_version(vstring) | ||
| 108 | if vers.match_constraints(version, depq.constraints) then | ||
| 109 | if not latest_version or version > latest_version then | ||
| 110 | latest_version = version | ||
| 111 | latest_vstring = vstring | ||
| 112 | end | ||
| 113 | end | ||
| 114 | end | ||
| 115 | |||
| 116 | if lockversion and not locations[lockversion] then | ||
| 117 | local latest_matching_msg = "" | ||
| 118 | if latest_vstring and latest_vstring ~= lockversion then | ||
| 119 | latest_matching_msg = " (latest matching is " .. latest_vstring .. ")" | ||
| 120 | end | ||
| 121 | util.printout("Forcing " .. depq.name .. " to pinned version " .. lockversion .. latest_matching_msg) | ||
| 122 | return nil, nil, queries.new(depq.name, depq.namespace, lockversion) | ||
| 123 | end | ||
| 124 | |||
| 125 | return latest_vstring, locations[latest_vstring], depq, provided | ||
| 126 | end | ||
| 127 | |||
| 128 | local function match_all_deps(dependencies, | ||
| 129 | get_versions) | ||
| 130 | |||
| 131 | local matched, missing, no_upgrade = {}, {}, {} | ||
| 132 | |||
| 133 | for _, depq in ipairs(dependencies) do | ||
| 134 | local found, _, provided | ||
| 135 | found, _, depq, provided = match_dep(depq, get_versions) | ||
| 136 | if found then | ||
| 137 | if not provided then | ||
| 138 | matched[depq] = { name = depq.name, version = found } | ||
| 139 | end | ||
| 140 | else | ||
| 141 | if depq.constraints and depq.constraints[1] and depq.constraints[1].no_upgrade then | ||
| 142 | no_upgrade[depq.name] = depq | ||
| 143 | else | ||
| 144 | missing[depq.name] = depq | ||
| 145 | end | ||
| 146 | end | ||
| 147 | end | ||
| 148 | return matched, missing, no_upgrade | ||
| 149 | end | ||
| 150 | |||
| 151 | |||
| 152 | |||
| 153 | |||
| 154 | |||
| 155 | |||
| 156 | |||
| 157 | |||
| 158 | |||
| 159 | |||
| 160 | |||
| 161 | |||
| 162 | |||
| 163 | |||
| 164 | function deps.match_deps(dependencies, rocks_provided, deps_mode, skip_set) | ||
| 165 | |||
| 166 | local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies", skip_set) | ||
| 167 | return match_all_deps(dependencies, get_versions) | ||
| 168 | end | ||
| 169 | |||
| 170 | local function rock_status(dep, get_versions) | ||
| 171 | local installed, _, _, provided = match_dep(dep, get_versions) | ||
| 172 | local installation_type = provided and "provided by VM" or "installed" | ||
| 173 | return installed and installed .. " " .. installation_type .. ": success" or "not installed" | ||
| 174 | end | ||
| 175 | |||
| 176 | |||
| 177 | |||
| 178 | |||
| 179 | |||
| 180 | |||
| 181 | |||
| 182 | |||
| 183 | |||
| 184 | |||
| 185 | function deps.report_missing_dependencies(name, version, dependencies, deps_mode, rocks_provided) | ||
| 186 | |||
| 187 | if deps_mode == "none" then | ||
| 188 | return | ||
| 189 | end | ||
| 190 | |||
| 191 | local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies") | ||
| 192 | |||
| 193 | local first_missing_dep = true | ||
| 194 | |||
| 195 | for _, depq in ipairs(dependencies) do | ||
| 196 | local found, _ | ||
| 197 | found, _, depq = match_dep(depq, get_versions) | ||
| 198 | if not found then | ||
| 199 | if first_missing_dep then | ||
| 200 | util.printout(("Missing dependencies for %s %s:"):format(name, version)) | ||
| 201 | first_missing_dep = false | ||
| 202 | end | ||
| 203 | |||
| 204 | util.printout((" %s (%s)"):format(tostring(depq), rock_status(depq, get_versions))) | ||
| 205 | end | ||
| 206 | end | ||
| 207 | end | ||
| 208 | |||
| 209 | function deps.fulfill_dependency(dep, deps_mode, rocks_provided, verify, depskey) | ||
| 210 | |||
| 211 | deps_mode = deps_mode or "all" | ||
| 212 | rocks_provided = rocks_provided or {} | ||
| 213 | |||
| 214 | local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey) | ||
| 215 | |||
| 216 | local found, where | ||
| 217 | found, where, dep = match_dep(dep, get_versions) | ||
| 218 | if found then | ||
| 219 | local tree_manifests = manif.load_rocks_tree_manifests(deps_mode) | ||
| 220 | manif.scan_dependencies(dep.name, found, tree_manifests, deplocks.proxy(depskey)) | ||
| 221 | return true, found, where | ||
| 222 | end | ||
| 223 | |||
| 224 | local search = require("luarocks.search") | ||
| 225 | |||
| 226 | local url, search_err = search.find_suitable_rock(dep) | ||
| 227 | if not url then | ||
| 228 | return nil, "Could not satisfy dependency " .. tostring(dep) .. ": " .. search_err | ||
| 229 | end | ||
| 230 | util.printout("Installing " .. url) | ||
| 231 | local install_args = { | ||
| 232 | rock = url, | ||
| 233 | deps_mode = deps_mode, | ||
| 234 | namespace = dep.namespace, | ||
| 235 | verify = verify, | ||
| 236 | } | ||
| 237 | local ok, install_err, errcode = deps.installer(install_args) | ||
| 238 | if not ok then | ||
| 239 | return nil, "Failed installing dependency: " .. url .. " - " .. install_err, errcode | ||
| 240 | end | ||
| 241 | |||
| 242 | found, where = match_dep(dep, get_versions) | ||
| 243 | if not found then | ||
| 244 | return nil, "Repository inconsistency detected (previously unfinished/corrupted installation?)" | ||
| 245 | end | ||
| 246 | return true, found, where | ||
| 247 | end | ||
| 248 | |||
| 249 | local function check_supported_platforms(rockspec) | ||
| 250 | if rockspec.supported_platforms and next(rockspec.supported_platforms) then | ||
| 251 | local all_negative = true | ||
| 252 | local supported = false | ||
| 253 | for _, plat in ipairs(rockspec.supported_platforms) do | ||
| 254 | local neg | ||
| 255 | neg, plat = plat:match("^(!?)(.*)") | ||
| 256 | if neg == "!" then | ||
| 257 | if cfg.is_platform(plat) then | ||
| 258 | return nil, "This rockspec for " .. rockspec.package .. " does not support " .. plat .. " platforms." | ||
| 259 | end | ||
| 260 | else | ||
| 261 | all_negative = false | ||
| 262 | if cfg.is_platform(plat) then | ||
| 263 | supported = true | ||
| 264 | break | ||
| 265 | end | ||
| 266 | end | ||
| 267 | end | ||
| 268 | if supported == false and not all_negative then | ||
| 269 | local plats = cfg.print_platforms() | ||
| 270 | return nil, "This rockspec for " .. rockspec.package .. " does not support " .. plats .. " platforms." | ||
| 271 | end | ||
| 272 | end | ||
| 273 | |||
| 274 | return true | ||
| 275 | end | ||
| 276 | |||
| 277 | |||
| 278 | |||
| 279 | |||
| 280 | |||
| 281 | |||
| 282 | |||
| 283 | |||
| 284 | |||
| 285 | |||
| 286 | |||
| 287 | |||
| 288 | |||
| 289 | function deps.fulfill_dependencies(rockspec, depskey, deps_mode, verify, deplock_dir) | ||
| 290 | local name = rockspec.name | ||
| 291 | local version = rockspec.version | ||
| 292 | local rocks_provided = rockspec.rocks_provided | ||
| 293 | |||
| 294 | local ok, filename, err = deplocks.load(name, deplock_dir or ".") | ||
| 295 | if filename then | ||
| 296 | util.printout("Using dependencies pinned in lockfile: " .. filename) | ||
| 297 | |||
| 298 | local get_versions = prepare_get_versions("none", rocks_provided, depskey) | ||
| 299 | local dnsnamestr, dversionstr | ||
| 300 | for dnsname, dversion in deplocks.each(depskey) do | ||
| 301 | if type(dnsname) == "string" then | ||
| 302 | dnsnamestr = dnsname | ||
| 303 | end | ||
| 304 | if type(dversion) == "string" then | ||
| 305 | dversionstr = dversion | ||
| 306 | end | ||
| 307 | local dname, dnamespace = util.split_namespace(dnsnamestr) | ||
| 308 | local depq = queries.new(dname, dnamespace, dversionstr) | ||
| 309 | |||
| 310 | util.printout(("%s %s is pinned to %s (%s)"):format( | ||
| 311 | name, version, tostring(depq), rock_status(depq, get_versions))) | ||
| 312 | |||
| 313 | local okfullfill, errfullfill = deps.fulfill_dependency(depq, "none", rocks_provided, verify, depskey) | ||
| 314 | if not okfullfill then | ||
| 315 | return nil, errfullfill | ||
| 316 | end | ||
| 317 | end | ||
| 318 | util.printout() | ||
| 319 | return true | ||
| 320 | elseif err then | ||
| 321 | util.warning(err) | ||
| 322 | end | ||
| 323 | |||
| 324 | ok, err = check_supported_platforms(rockspec) | ||
| 325 | if not ok then | ||
| 326 | return nil, err | ||
| 327 | end | ||
| 328 | |||
| 329 | deps.report_missing_dependencies(name, version, (rockspec)[depskey].queries, deps_mode, rocks_provided) | ||
| 330 | |||
| 331 | util.printout() | ||
| 332 | |||
| 333 | local get_versions = prepare_get_versions(deps_mode, rocks_provided, depskey) | ||
| 334 | for _, depq in ipairs((rockspec)[depskey].queries) do | ||
| 335 | |||
| 336 | util.printout(("%s %s depends on %s (%s)"):format( | ||
| 337 | name, version, tostring(depq), rock_status(depq, get_versions))) | ||
| 338 | |||
| 339 | local okfulfill, found_or_err, _ = deps.fulfill_dependency(depq, deps_mode, rocks_provided, verify, depskey) | ||
| 340 | if okfulfill then | ||
| 341 | deplocks.add(depskey, depq.name, found_or_err) | ||
| 342 | else | ||
| 343 | |||
| 344 | |||
| 345 | |||
| 346 | |||
| 347 | |||
| 348 | |||
| 349 | |||
| 350 | |||
| 351 | return nil, found_or_err | ||
| 352 | end | ||
| 353 | end | ||
| 354 | |||
| 355 | return true | ||
| 356 | end | ||
| 357 | |||
| 358 | |||
| 359 | |||
| 360 | |||
| 361 | |||
| 362 | |||
| 363 | |||
| 364 | |||
| 365 | local function deconstruct_pattern(file, pattern) | ||
| 366 | local depattern = "^" .. (pattern:gsub("%.", "%%."):gsub("%*", ".*"):gsub("?", "(.*)")) .. "$" | ||
| 367 | return (file:match(depattern)) | ||
| 368 | end | ||
| 369 | |||
| 370 | |||
| 371 | |||
| 372 | |||
| 373 | |||
| 374 | |||
| 375 | |||
| 376 | |||
| 377 | local function add_all_patterns(file, patterns, files) | ||
| 378 | for _, pattern in ipairs(patterns) do | ||
| 379 | table.insert(files, { #files + 1, (pattern:gsub("?", file)) }) | ||
| 380 | end | ||
| 381 | end | ||
| 382 | |||
| 383 | local function get_external_deps_dirs(mode) | ||
| 384 | local patterns = cfg.external_deps_patterns | ||
| 385 | local subdirs = cfg.external_deps_subdirs | ||
| 386 | if mode == "install" then | ||
| 387 | patterns = cfg.runtime_external_deps_patterns | ||
| 388 | subdirs = cfg.runtime_external_deps_subdirs | ||
| 389 | end | ||
| 390 | local dirs = { | ||
| 391 | BINDIR = { subdir = subdirs.bin, testfile = "program", pattern = patterns.bin }, | ||
| 392 | INCDIR = { subdir = subdirs.include, testfile = "header", pattern = patterns.include }, | ||
| 393 | LIBDIR = { subdir = subdirs.lib, testfile = "library", pattern = patterns.lib }, | ||
| 394 | } | ||
| 395 | if mode == "install" then | ||
| 396 | dirs.INCDIR = nil | ||
| 397 | end | ||
| 398 | return dirs | ||
| 399 | end | ||
| 400 | |||
| 401 | local function resolve_prefix(prefix, dirs) | ||
| 402 | if type(prefix) == "string" then | ||
| 403 | return prefix | ||
| 404 | elseif type(prefix) == "table" then | ||
| 405 | if prefix.bin then | ||
| 406 | dirs.BINDIR.subdir = prefix.bin | ||
| 407 | end | ||
| 408 | if prefix.include then | ||
| 409 | if dirs.INCDIR then | ||
| 410 | dirs.INCDIR.subdir = prefix.include | ||
| 411 | end | ||
| 412 | end | ||
| 413 | if prefix.lib then | ||
| 414 | dirs.LIBDIR.subdir = prefix.lib | ||
| 415 | end | ||
| 416 | return prefix.prefix | ||
| 417 | end | ||
| 418 | end | ||
| 419 | |||
| 420 | local function add_patterns_for_file(files, file, patterns) | ||
| 421 | |||
| 422 | if not (file:match("%.[a-z]+$") or file:match("%.[a-z]+%.")) then | ||
| 423 | add_all_patterns(file, patterns, files) | ||
| 424 | else | ||
| 425 | for _, pattern in ipairs(patterns) do | ||
| 426 | local matched = deconstruct_pattern(file, pattern) | ||
| 427 | if matched then | ||
| 428 | add_all_patterns(matched, patterns, files) | ||
| 429 | end | ||
| 430 | end | ||
| 431 | table.insert(files, { #files + 1, file }) | ||
| 432 | end | ||
| 433 | end | ||
| 434 | |||
| 435 | local function check_external_dependency_at( | ||
| 436 | prefix, | ||
| 437 | name, | ||
| 438 | ext_files, | ||
| 439 | vars, | ||
| 440 | dirs, | ||
| 441 | err_files, | ||
| 442 | cache) | ||
| 443 | |||
| 444 | local fs = require("luarocks.fs") | ||
| 445 | cache = cache or {} | ||
| 446 | |||
| 447 | for dirname, dirdata in util.sortedpairs(dirs) do | ||
| 448 | local paths | ||
| 449 | local path_var_value = vars[name .. "_" .. dirname] | ||
| 450 | local dirdatastr = dirdata.subdir | ||
| 451 | if path_var_value then | ||
| 452 | paths = { path_var_value } | ||
| 453 | elseif type(dirdatastr) == "table" then | ||
| 454 | paths = {} | ||
| 455 | for i, v in ipairs(dirdatastr) do | ||
| 456 | paths[i] = dir.path(prefix, v) | ||
| 457 | end | ||
| 458 | else | ||
| 459 | paths = { dir.path(prefix, dirdatastr) } | ||
| 460 | end | ||
| 461 | local file_or_files = ext_files[dirdata.testfile] | ||
| 462 | if file_or_files then | ||
| 463 | local files = {} | ||
| 464 | if type(file_or_files) == "string" then | ||
| 465 | add_patterns_for_file(files, file_or_files, dirdata.pattern) | ||
| 466 | elseif type(file_or_files) == "table" then | ||
| 467 | for _, f in ipairs(file_or_files) do | ||
| 468 | add_patterns_for_file(files, f, dirdata.pattern) | ||
| 469 | end | ||
| 470 | end | ||
| 471 | |||
| 472 | local found = false | ||
| 473 | table.sort(files, function(a, b) | ||
| 474 | if (not a[2]:match("%*")) and b[2]:match("%*") then | ||
| 475 | return true | ||
| 476 | elseif a[2]:match("%*") and (not b[2]:match("%*")) then | ||
| 477 | return false | ||
| 478 | else | ||
| 479 | return a[1] < b[1] | ||
| 480 | end | ||
| 481 | end) | ||
| 482 | for _, fa in ipairs(files) do | ||
| 483 | |||
| 484 | local f = fa[2] | ||
| 485 | |||
| 486 | if f:match("%.so$") or f:match("%.dylib$") or f:match("%.dll$") then | ||
| 487 | f = f:gsub("%.[^.]+$", "." .. cfg.external_lib_extension) | ||
| 488 | end | ||
| 489 | |||
| 490 | local pattern | ||
| 491 | if f:match("%*") then | ||
| 492 | pattern = "^" .. f:gsub("([-.+])", "%%%1"):gsub("%*", ".*") .. "$" | ||
| 493 | f = "matching " .. f | ||
| 494 | end | ||
| 495 | |||
| 496 | for _, d in ipairs(paths) do | ||
| 497 | if pattern then | ||
| 498 | if not cache[d] then | ||
| 499 | cache[d] = fs.list_dir(d) | ||
| 500 | end | ||
| 501 | local match = string.match | ||
| 502 | for _, entry in ipairs(cache[d]) do | ||
| 503 | if match(entry, pattern) then | ||
| 504 | found = true | ||
| 505 | break | ||
| 506 | end | ||
| 507 | end | ||
| 508 | else | ||
| 509 | found = fs.is_file(dir.path(d, f)) | ||
| 510 | end | ||
| 511 | if found then | ||
| 512 | dirdata.dir = d | ||
| 513 | dirdata.file = f | ||
| 514 | break | ||
| 515 | else | ||
| 516 | table.insert(err_files[dirdata.testfile], f .. " in " .. d) | ||
| 517 | end | ||
| 518 | end | ||
| 519 | if found then | ||
| 520 | break | ||
| 521 | end | ||
| 522 | end | ||
| 523 | if not found then | ||
| 524 | return nil, dirname, dirdata.testfile | ||
| 525 | end | ||
| 526 | else | ||
| 527 | |||
| 528 | |||
| 529 | |||
| 530 | dirdata.dir = paths[1] | ||
| 531 | for _, p in ipairs(paths) do | ||
| 532 | if fs.exists(p) then | ||
| 533 | dirdata.dir = p | ||
| 534 | break | ||
| 535 | end | ||
| 536 | end | ||
| 537 | end | ||
| 538 | end | ||
| 539 | |||
| 540 | for dirname, dirdata in pairs(dirs) do | ||
| 541 | vars[name .. "_" .. dirname] = dirdata.dir | ||
| 542 | vars[name .. "_" .. dirname .. "_FILE"] = dirdata.file | ||
| 543 | end | ||
| 544 | vars[name .. "_DIR"] = prefix | ||
| 545 | return true | ||
| 546 | end | ||
| 547 | |||
| 548 | local function check_external_dependency( | ||
| 549 | name, | ||
| 550 | ext_files, | ||
| 551 | vars, | ||
| 552 | mode, | ||
| 553 | cache) | ||
| 554 | local ok | ||
| 555 | local err_dirname | ||
| 556 | local err_testfile | ||
| 557 | local err_files = { program = {}, header = {}, library = {} } | ||
| 558 | |||
| 559 | local dirs = get_external_deps_dirs(mode) | ||
| 560 | |||
| 561 | local prefixes | ||
| 562 | if vars[name .. "_DIR"] then | ||
| 563 | prefixes = { vars[name .. "_DIR"] } | ||
| 564 | elseif vars.DEPS_DIR then | ||
| 565 | prefixes = { vars.DEPS_DIR } | ||
| 566 | else | ||
| 567 | prefixes = cfg.external_deps_dirs | ||
| 568 | end | ||
| 569 | |||
| 570 | for _, prefix in ipairs(prefixes) do | ||
| 571 | prefix = resolve_prefix(prefix, dirs) | ||
| 572 | if cfg.is_platform("mingw32") and name == "LUA" then | ||
| 573 | dirs.LIBDIR.pattern = fun.filter(util.deep_copy(dirs.LIBDIR.pattern), function(s) | ||
| 574 | return not s:match("%.a$") | ||
| 575 | end) | ||
| 576 | elseif cfg.is_platform("windows") and name == "LUA" then | ||
| 577 | dirs.LIBDIR.pattern = fun.filter(util.deep_copy(dirs.LIBDIR.pattern), function(s) | ||
| 578 | return not s:match("%.dll$") | ||
| 579 | end) | ||
| 580 | end | ||
| 581 | ok, err_dirname, err_testfile = check_external_dependency_at(prefix, name, ext_files, vars, dirs, err_files, cache) | ||
| 582 | if ok then | ||
| 583 | return true | ||
| 584 | end | ||
| 585 | end | ||
| 586 | |||
| 587 | return nil, err_dirname, err_testfile, err_files | ||
| 588 | end | ||
| 589 | |||
| 590 | function deps.autodetect_external_dependencies(build) | ||
| 591 | |||
| 592 | if not build or not (build).modules then | ||
| 593 | return nil | ||
| 594 | end | ||
| 595 | |||
| 596 | local extdeps = {} | ||
| 597 | local any = false | ||
| 598 | for _, data in pairs((build).modules) do | ||
| 599 | if type(data) == "table" and data.libraries then | ||
| 600 | local libraries | ||
| 601 | local librariesstr = data.libraries | ||
| 602 | if type(librariesstr) == "string" then | ||
| 603 | libraries = { librariesstr } | ||
| 604 | else | ||
| 605 | libraries = librariesstr | ||
| 606 | end | ||
| 607 | local incdirs = {} | ||
| 608 | local libdirs = {} | ||
| 609 | for _, lib in ipairs(libraries) do | ||
| 610 | local upper = lib:upper():gsub("%+", "P"):gsub("[^%w]", "_") | ||
| 611 | any = true | ||
| 612 | extdeps[upper] = { library = lib } | ||
| 613 | table.insert(incdirs, "$(" .. upper .. "_INCDIR)") | ||
| 614 | table.insert(libdirs, "$(" .. upper .. "_LIBDIR)") | ||
| 615 | end | ||
| 616 | if not data.incdirs then | ||
| 617 | data.incdirs = incdirs | ||
| 618 | end | ||
| 619 | if not data.libdirs then | ||
| 620 | data.libdirs = libdirs | ||
| 621 | end | ||
| 622 | end | ||
| 623 | end | ||
| 624 | return any and extdeps or nil | ||
| 625 | end | ||
| 626 | |||
| 627 | |||
| 628 | |||
| 629 | |||
| 630 | |||
| 631 | |||
| 632 | |||
| 633 | |||
| 634 | |||
| 635 | |||
| 636 | |||
| 637 | |||
| 638 | |||
| 639 | |||
| 640 | function deps.check_external_deps(rockspec, mode) | ||
| 641 | |||
| 642 | if not rockspec.external_dependencies then | ||
| 643 | rockspec.external_dependencies = deps.autodetect_external_dependencies(rockspec.build) | ||
| 644 | end | ||
| 645 | if not rockspec.external_dependencies then | ||
| 646 | return true | ||
| 647 | end | ||
| 648 | |||
| 649 | for name, ext_files in util.sortedpairs(rockspec.external_dependencies) do | ||
| 650 | local ok, err_dirname, err_testfile, err_files = check_external_dependency(name, ext_files, rockspec.variables, mode) | ||
| 651 | if not ok then | ||
| 652 | local lines = { "Could not find " .. err_testfile .. " file for " .. name } | ||
| 653 | |||
| 654 | local err_paths = {} | ||
| 655 | for _, err_file in ipairs(err_files[err_testfile]) do | ||
| 656 | if not err_paths[err_file] then | ||
| 657 | err_paths[err_file] = true | ||
| 658 | table.insert(lines, " No file " .. err_file) | ||
| 659 | end | ||
| 660 | end | ||
| 661 | |||
| 662 | 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.") | ||
| 663 | table.insert(lines, "Example: luarocks install " .. rockspec.name .. " " .. name .. "_DIR=/usr/local") | ||
| 664 | |||
| 665 | return nil, table.concat(lines, "\n"), "dependency" | ||
| 666 | end | ||
| 667 | end | ||
| 668 | return true | ||
| 669 | end | ||
| 670 | |||
| 671 | |||
| 672 | |||
| 673 | |||
| 674 | |||
| 675 | |||
| 676 | |||
| 677 | |||
| 678 | function deps.scan_deps(results, mdeps, name, version, deps_mode) | ||
| 679 | assert(not name:match("/")) | ||
| 680 | |||
| 681 | local fetch = require("luarocks.fetch") | ||
| 682 | |||
| 683 | if results[name] then | ||
| 684 | return | ||
| 685 | end | ||
| 686 | if not mdeps[name] then mdeps[name] = {} end | ||
| 687 | local mdn = mdeps[name] | ||
| 688 | local dependencies = mdn[version] | ||
| 689 | local rocks_provided | ||
| 690 | if not dependencies then | ||
| 691 | local rockspec = fetch.load_local_rockspec(path.rockspec_file(name, version), false) | ||
| 692 | if not rockspec then | ||
| 693 | return | ||
| 694 | end | ||
| 695 | dependencies = rockspec.dependencies.queries | ||
| 696 | rocks_provided = rockspec.rocks_provided | ||
| 697 | mdn[version] = dependencies | ||
| 698 | else | ||
| 699 | rocks_provided = util.get_rocks_provided() | ||
| 700 | end | ||
| 701 | |||
| 702 | local get_versions = prepare_get_versions(deps_mode, rocks_provided, "dependencies") | ||
| 703 | |||
| 704 | local matched = match_all_deps(dependencies, get_versions) | ||
| 705 | results[name] = version | ||
| 706 | for _, match in pairs(matched) do | ||
| 707 | deps.scan_deps(results, mdeps, match.name, match.version, deps_mode) | ||
| 708 | end | ||
| 709 | end | ||
| 710 | |||
| 711 | local function lua_h_exists(d, luaver) | ||
| 712 | local major, minor = luaver:match("(%d+)%.(%d+)") | ||
| 713 | local luanum = ("%s%02d"):format(major, tonumber(minor)) | ||
| 714 | |||
| 715 | local lua_h = dir.path(d, "lua.h") | ||
| 716 | local fd = io.open(lua_h) | ||
| 717 | if fd then | ||
| 718 | local data = fd:read("*a") | ||
| 719 | fd:close() | ||
| 720 | if data:match("LUA_VERSION_NUM%s*" .. tostring(luanum)) then | ||
| 721 | return d ~= nil | ||
| 722 | end | ||
| 723 | return nil, "Lua header lua.h found at " .. d .. " does not match Lua version " .. luaver .. ". You can use `luarocks config variables.LUA_INCDIR <path>` to set the correct location.", "dependency", 2 | ||
| 724 | end | ||
| 725 | |||
| 726 | 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 <path>` to set the correct location.", "dependency", 1 | ||
| 727 | end | ||
| 728 | |||
| 729 | local function find_lua_incdir(prefix, luaver, luajitver) | ||
| 730 | luajitver = luajitver and luajitver:gsub("%-.*", "") | ||
| 731 | local shortv = luaver:gsub("%.", "") | ||
| 732 | local incdirs = { | ||
| 733 | prefix .. "/include/lua/" .. luaver, | ||
| 734 | prefix .. "/include/lua" .. luaver, | ||
| 735 | prefix .. "/include/lua-" .. luaver, | ||
| 736 | prefix .. "/include/lua" .. shortv, | ||
| 737 | prefix .. "/include", | ||
| 738 | prefix, | ||
| 739 | luajitver and (prefix .. "/include/luajit-" .. (luajitver:match("^(%d+%.%d+)") or "")), | ||
| 740 | } | ||
| 741 | local errprio = 0 | ||
| 742 | local mainerr | ||
| 743 | for _, d in ipairs(incdirs) do | ||
| 744 | local ok, err, _, prio = lua_h_exists(d, luaver) | ||
| 745 | if ok then | ||
| 746 | return d | ||
| 747 | end | ||
| 748 | if prio > errprio then | ||
| 749 | mainerr = err | ||
| 750 | errprio = prio | ||
| 751 | end | ||
| 752 | end | ||
| 753 | |||
| 754 | |||
| 755 | return nil, mainerr | ||
| 756 | end | ||
| 757 | |||
| 758 | function deps.check_lua_incdir(vars) | ||
| 759 | if vars.LUA_INCDIR_OK == "ok" then | ||
| 760 | return true | ||
| 761 | end | ||
| 762 | |||
| 763 | local ljv = util.get_luajit_version() | ||
| 764 | |||
| 765 | if vars.LUA_INCDIR then | ||
| 766 | local ok, err = lua_h_exists(vars.LUA_INCDIR, cfg.lua_version) | ||
| 767 | if ok then | ||
| 768 | vars.LUA_INCDIR_OK = "ok" | ||
| 769 | end | ||
| 770 | return ok, err | ||
| 771 | end | ||
| 772 | |||
| 773 | if vars.LUA_DIR then | ||
| 774 | local d, err = find_lua_incdir(vars.LUA_DIR, cfg.lua_version, ljv) | ||
| 775 | if d then | ||
| 776 | vars.LUA_INCDIR = d | ||
| 777 | vars.LUA_INCDIR_OK = "ok" | ||
| 778 | return true | ||
| 779 | end | ||
| 780 | return nil, err | ||
| 781 | end | ||
| 782 | |||
| 783 | 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" | ||
| 784 | end | ||
| 785 | |||
| 786 | function deps.check_lua_libdir(vars) | ||
| 787 | if vars.LUA_LIBDIR_OK == "ok" then | ||
| 788 | return true | ||
| 789 | end | ||
| 790 | |||
| 791 | local fs = require("luarocks.fs") | ||
| 792 | local ljv = util.get_luajit_version() | ||
| 793 | |||
| 794 | if vars.LUA_LIBDIR and vars.LUALIB and fs.exists(dir.path(vars.LUA_LIBDIR, vars.LUALIB)) then | ||
| 795 | vars.LUA_LIBDIR_OK = "ok" | ||
| 796 | return true | ||
| 797 | end | ||
| 798 | |||
| 799 | local shortv = cfg.lua_version:gsub("%.", "") | ||
| 800 | local libnames = { | ||
| 801 | "lua" .. cfg.lua_version, | ||
| 802 | "lua" .. shortv, | ||
| 803 | "lua-" .. cfg.lua_version, | ||
| 804 | "lua-" .. shortv, | ||
| 805 | "lua", | ||
| 806 | } | ||
| 807 | if ljv then | ||
| 808 | table.insert(libnames, 1, "luajit-" .. cfg.lua_version) | ||
| 809 | table.insert(libnames, 2, "luajit") | ||
| 810 | end | ||
| 811 | local cache = {} | ||
| 812 | local save_LUA_INCDIR = vars.LUA_INCDIR | ||
| 813 | local ok, _, _, errfiles = check_external_dependency("LUA", { library = libnames }, vars, "build", cache) | ||
| 814 | vars.LUA_INCDIR = save_LUA_INCDIR | ||
| 815 | local err | ||
| 816 | if ok then | ||
| 817 | local filename = dir.path(vars.LUA_LIBDIR, vars.LUA_LIBDIR_FILE) | ||
| 818 | local fd = io.open(filename, "r") | ||
| 819 | if fd then | ||
| 820 | if not vars.LUA_LIBDIR_FILE:match((cfg.lua_version:gsub("%.", "%%.?"))) then | ||
| 821 | |||
| 822 | local txt = fd:read("*a") | ||
| 823 | ok = txt:find("Lua " .. cfg.lua_version, 1, true) or | ||
| 824 | txt:find("lua" .. (cfg.lua_version:gsub("%.", "")), 1, true) and | ||
| 825 | true | ||
| 826 | if not ok then | ||
| 827 | err = "Lua library at " .. filename .. " does not match Lua version " .. cfg.lua_version .. ". You can use `luarocks config variables.LUA_LIBDIR <path>` to set the correct location." | ||
| 828 | end | ||
| 829 | end | ||
| 830 | |||
| 831 | fd:close() | ||
| 832 | end | ||
| 833 | end | ||
| 834 | |||
| 835 | if ok then | ||
| 836 | vars.LUALIB = vars.LUA_LIBDIR_FILE | ||
| 837 | vars.LUA_LIBDIR_OK = "ok" | ||
| 838 | return true | ||
| 839 | else | ||
| 840 | err = err or "Failed finding the Lua library. You can use `luarocks config variables.LUA_LIBDIR <path>` to set the correct location." | ||
| 841 | return nil, err, "dependency", errfiles | ||
| 842 | end | ||
| 843 | end | ||
| 844 | |||
| 845 | function deps.get_deps_mode(args) | ||
| 846 | return args.deps_mode or cfg.deps_mode | ||
| 847 | end | ||
| 848 | |||
| 849 | |||
| 850 | |||
| 851 | |||
| 852 | |||
| 853 | |||
| 854 | |||
| 855 | function deps.check_dependencies(repo, deps_mode) | ||
| 856 | local rocks_dir = path.rocks_dir(repo or cfg.root_dir) | ||
| 857 | if deps_mode == "none" then deps_mode = cfg.deps_mode end | ||
| 858 | |||
| 859 | local manifest = manif.load_manifest(rocks_dir) | ||
| 860 | if not manifest then | ||
| 861 | return | ||
| 862 | end | ||
| 863 | |||
| 864 | for name, versions in util.sortedpairs(manifest.repository) do | ||
| 865 | for version, version_entries in util.sortedpairs(versions, vers.compare_versions) do | ||
| 866 | for _, entry in ipairs(version_entries) do | ||
| 867 | if entry.arch == "installed" then | ||
| 868 | if manifest.dependencies[name] and manifest.dependencies[name][version] then | ||
| 869 | deps.report_missing_dependencies(name, version, manifest.dependencies[name][version], deps_mode, util.get_rocks_provided()) | ||
| 870 | end | ||
| 871 | end | ||
| 872 | end | ||
| 873 | end | ||
| 874 | end | ||
| 875 | end | ||
| 876 | |||
| 877 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local dir = {} | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | local core = require("luarocks.core.dir") | ||
| 10 | |||
| 11 | dir.path = core.path | ||
| 12 | dir.split_url = core.split_url | ||
| 13 | dir.normalize = core.normalize | ||
| 14 | |||
| 15 | local dir_sep = package.config:sub(1, 1) | ||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | function dir.base_name(pathname) | ||
| 22 | |||
| 23 | local b | ||
| 24 | b = pathname:gsub("[/\\]", "/") | ||
| 25 | b = b:gsub("/*$", "") | ||
| 26 | b = b:match(".*[/\\]([^/\\]*)") | ||
| 27 | b = b or pathname | ||
| 28 | |||
| 29 | return b | ||
| 30 | end | ||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | function dir.dir_name(pathname) | ||
| 38 | |||
| 39 | local d | ||
| 40 | d = pathname:gsub("[/\\]", "/") | ||
| 41 | d = d:gsub("/*$", "") | ||
| 42 | d = d:match("(.*)[/]+[^/]*") | ||
| 43 | d = d or "" | ||
| 44 | d = d:gsub("/", dir_sep) | ||
| 45 | |||
| 46 | return d | ||
| 47 | end | ||
| 48 | |||
| 49 | |||
| 50 | |||
| 51 | function dir.is_basic_protocol(protocol) | ||
| 52 | return protocol == "http" or protocol == "https" or protocol == "ftp" or protocol == "file" | ||
| 53 | end | ||
| 54 | |||
| 55 | function dir.deduce_base_dir(url) | ||
| 56 | |||
| 57 | local known_exts = {} | ||
| 58 | for _, ext in ipairs({ "zip", "git", "tgz", "tar", "gz", "bz2" }) do | ||
| 59 | known_exts[ext] = "" | ||
| 60 | end | ||
| 61 | local base = dir.base_name(url) | ||
| 62 | return (base:gsub("%.([^.]*)$", known_exts):gsub("%.tar", "")) | ||
| 63 | end | ||
| 64 | |||
| 65 | 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 @@ | |||
| 1 | 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 = {} | ||
| 2 | |||
| 3 | |||
| 4 | local path = require("luarocks.path") | ||
| 5 | local fetch = require("luarocks.fetch") | ||
| 6 | local search = require("luarocks.search") | ||
| 7 | local queries = require("luarocks.queries") | ||
| 8 | local fs = require("luarocks.fs") | ||
| 9 | local dir = require("luarocks.dir") | ||
| 10 | local util = require("luarocks.util") | ||
| 11 | |||
| 12 | local function get_file(filename) | ||
| 13 | local protocol, pathname = dir.split_url(filename) | ||
| 14 | if protocol == "file" then | ||
| 15 | local ok, err = fs.copy(pathname, fs.current_dir(), "read") | ||
| 16 | if ok then | ||
| 17 | return pathname | ||
| 18 | else | ||
| 19 | return nil, err | ||
| 20 | end | ||
| 21 | else | ||
| 22 | |||
| 23 | local ok, err = fetch.fetch_url(filename) | ||
| 24 | return ok, err | ||
| 25 | end | ||
| 26 | end | ||
| 27 | |||
| 28 | function download.download_all(arch, name, namespace, version) | ||
| 29 | local substring = (name == "") | ||
| 30 | local query = queries.new(name, namespace, version, substring, arch) | ||
| 31 | local search_err | ||
| 32 | |||
| 33 | local results = search.search_repos(query) | ||
| 34 | local has_result = false | ||
| 35 | local all_ok = true | ||
| 36 | local any_err = "" | ||
| 37 | for name, result in pairs(results) do | ||
| 38 | for version, items in pairs(result) do | ||
| 39 | for _, item in ipairs(items) do | ||
| 40 | |||
| 41 | if item.arch ~= "installed" then | ||
| 42 | has_result = true | ||
| 43 | local filename = path.make_url(item.repo, name, version, item.arch) | ||
| 44 | local ok, err = get_file(filename) | ||
| 45 | if not ok then | ||
| 46 | all_ok = false | ||
| 47 | any_err = any_err .. "\n" .. err | ||
| 48 | end | ||
| 49 | end | ||
| 50 | end | ||
| 51 | end | ||
| 52 | end | ||
| 53 | |||
| 54 | if has_result then | ||
| 55 | return all_ok, any_err | ||
| 56 | end | ||
| 57 | |||
| 58 | local rock = util.format_rock_name(name, namespace, version) | ||
| 59 | return nil, "Could not find a result named " .. rock .. (search_err and ": " .. search_err or ".") | ||
| 60 | end | ||
| 61 | |||
| 62 | function download.download_file(arch, name, namespace, version, check_lua_versions) | ||
| 63 | local query = queries.new(name, namespace, version, false, arch) | ||
| 64 | local search_err | ||
| 65 | |||
| 66 | local url | ||
| 67 | url, search_err = search.find_rock_checking_lua_versions(query, check_lua_versions) | ||
| 68 | if url then | ||
| 69 | return get_file(url) | ||
| 70 | end | ||
| 71 | |||
| 72 | local rock = util.format_rock_name(name, namespace, version) | ||
| 73 | return nil, "Could not find a result named " .. rock .. (search_err and ": " .. search_err or ".") | ||
| 74 | end | ||
| 75 | |||
| 76 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local fetch = {Fetch = {}, } | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | local fs = require("luarocks.fs") | ||
| 19 | local dir = require("luarocks.dir") | ||
| 20 | local rockspecs = require("luarocks.rockspecs") | ||
| 21 | local signing = require("luarocks.signing") | ||
| 22 | local persist = require("luarocks.persist") | ||
| 23 | local util = require("luarocks.util") | ||
| 24 | local cfg = require("luarocks.core.cfg") | ||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | function fetch.fetch_caching(url, mirroring) | ||
| 49 | local repo_url, filename = url:match("^(.*)/([^/]+)$") | ||
| 50 | local name = repo_url:gsub("[/:]", "_") | ||
| 51 | local cache_dir = dir.path(cfg.local_cache, name) | ||
| 52 | local ok = fs.make_dir(cache_dir) | ||
| 53 | |||
| 54 | local cachefile = dir.path(cache_dir, filename) | ||
| 55 | local checkfile = cachefile .. ".check" | ||
| 56 | |||
| 57 | if (fs.file_age(checkfile) < 10 or | ||
| 58 | cfg.aggressive_cache and (not name:match("^manifest"))) and fs.exists(cachefile) then | ||
| 59 | |||
| 60 | return cachefile, nil, nil, true | ||
| 61 | end | ||
| 62 | |||
| 63 | local lock, errlock | ||
| 64 | if ok then | ||
| 65 | lock, errlock = fs.lock_access(cache_dir) | ||
| 66 | end | ||
| 67 | |||
| 68 | if not (ok and lock) then | ||
| 69 | cfg.local_cache = fs.make_temp_dir("local_cache") | ||
| 70 | if not cfg.local_cache then | ||
| 71 | return nil, "Failed creating temporary local_cache directory" | ||
| 72 | end | ||
| 73 | cache_dir = dir.path(cfg.local_cache, name) | ||
| 74 | ok = fs.make_dir(cache_dir) | ||
| 75 | if not ok then | ||
| 76 | return nil, "Failed creating temporary cache directory " .. cache_dir | ||
| 77 | end | ||
| 78 | lock = fs.lock_access(cache_dir) | ||
| 79 | end | ||
| 80 | |||
| 81 | local file, err, errcode, from_cache = fetch.fetch_url(url, cachefile, true, mirroring) | ||
| 82 | if not file then | ||
| 83 | fs.unlock_access(lock) | ||
| 84 | return nil, err or "Failed downloading " .. url, errcode | ||
| 85 | end | ||
| 86 | |||
| 87 | local fd, erropen = io.open(checkfile, "wb") | ||
| 88 | if erropen then | ||
| 89 | fs.unlock_access(lock) | ||
| 90 | return nil, erropen | ||
| 91 | end | ||
| 92 | fd:write("!") | ||
| 93 | fd:close() | ||
| 94 | |||
| 95 | fs.unlock_access(lock) | ||
| 96 | return file, nil, nil, from_cache | ||
| 97 | end | ||
| 98 | |||
| 99 | local function ensure_trailing_slash(url) | ||
| 100 | return (url:gsub("/*$", "/")) | ||
| 101 | end | ||
| 102 | |||
| 103 | local function is_url_relative_to_rocks_servers(url, servers) | ||
| 104 | for _, item in ipairs(servers) do | ||
| 105 | if type(item) == "table" then | ||
| 106 | for i, s in ipairs(item) do | ||
| 107 | local base = ensure_trailing_slash(s) | ||
| 108 | if string.find(url, base, 1, true) == 1 then | ||
| 109 | return i, url:sub(#base + 1), item | ||
| 110 | end | ||
| 111 | end | ||
| 112 | end | ||
| 113 | end | ||
| 114 | end | ||
| 115 | |||
| 116 | local function download_with_mirrors(url, filename, cache, servers) | ||
| 117 | local idx, rest, mirrors = is_url_relative_to_rocks_servers(url, servers) | ||
| 118 | |||
| 119 | if not idx then | ||
| 120 | |||
| 121 | return fs.download(url, filename, cache) | ||
| 122 | end | ||
| 123 | |||
| 124 | |||
| 125 | local err = "\n" | ||
| 126 | for i = idx, #mirrors do | ||
| 127 | local try_url = ensure_trailing_slash(mirrors[i]) .. rest | ||
| 128 | if i > idx then | ||
| 129 | util.warning("Failed downloading. Attempting mirror at " .. try_url) | ||
| 130 | end | ||
| 131 | local name, _, _, from_cache = fs.download(try_url, filename, cache) | ||
| 132 | if name then | ||
| 133 | return name, nil, nil, from_cache | ||
| 134 | else | ||
| 135 | err = err .. name .. "\n" | ||
| 136 | end | ||
| 137 | end | ||
| 138 | |||
| 139 | return nil, err, "network" | ||
| 140 | end | ||
| 141 | |||
| 142 | |||
| 143 | |||
| 144 | |||
| 145 | |||
| 146 | |||
| 147 | |||
| 148 | |||
| 149 | |||
| 150 | |||
| 151 | |||
| 152 | |||
| 153 | |||
| 154 | |||
| 155 | |||
| 156 | |||
| 157 | |||
| 158 | |||
| 159 | |||
| 160 | |||
| 161 | |||
| 162 | |||
| 163 | |||
| 164 | |||
| 165 | function fetch.fetch_url(url, filename, cache, mirroring) | ||
| 166 | |||
| 167 | local protocol, pathname = dir.split_url(url) | ||
| 168 | if protocol == "file" then | ||
| 169 | local fullname = fs.absolute_name(pathname) | ||
| 170 | if not fs.exists(fullname) then | ||
| 171 | local hint = (not pathname:match("^/")) and | ||
| 172 | (" - note that given path in rockspec is not absolute: " .. url) or | ||
| 173 | "" | ||
| 174 | return nil, "Local file not found: " .. fullname .. hint | ||
| 175 | end | ||
| 176 | filename = filename or dir.base_name(pathname) | ||
| 177 | local dstname = fs.absolute_name(dir.path(".", filename)) | ||
| 178 | local ok, err | ||
| 179 | if fullname == dstname then | ||
| 180 | ok = true | ||
| 181 | else | ||
| 182 | ok, err = fs.copy(fullname, dstname) | ||
| 183 | end | ||
| 184 | if ok then | ||
| 185 | return dstname | ||
| 186 | else | ||
| 187 | return nil, "Failed copying local file " .. fullname .. " to " .. dstname .. ": " .. err | ||
| 188 | end | ||
| 189 | elseif dir.is_basic_protocol(protocol) then | ||
| 190 | local name, err, err_code, from_cache | ||
| 191 | if mirroring ~= "no_mirror" then | ||
| 192 | name, err, err_code, from_cache = download_with_mirrors(url, filename, cache, cfg.rocks_servers) | ||
| 193 | else | ||
| 194 | name, err, err_code, from_cache = fs.download(url, filename, cache) | ||
| 195 | end | ||
| 196 | if not name then | ||
| 197 | return nil, "Failed downloading " .. url .. (err and " - " .. err or ""), err_code | ||
| 198 | end | ||
| 199 | return name, nil, nil, from_cache | ||
| 200 | else | ||
| 201 | return nil, "Unsupported protocol " .. protocol | ||
| 202 | end | ||
| 203 | end | ||
| 204 | |||
| 205 | |||
| 206 | |||
| 207 | |||
| 208 | |||
| 209 | |||
| 210 | |||
| 211 | |||
| 212 | |||
| 213 | |||
| 214 | |||
| 215 | |||
| 216 | function fetch.fetch_url_at_temp_dir(url, tmpname, filename, cache) | ||
| 217 | filename = filename or dir.base_name(url) | ||
| 218 | |||
| 219 | local protocol, pathname = dir.split_url(url) | ||
| 220 | if protocol == "file" then | ||
| 221 | if fs.exists(pathname) then | ||
| 222 | return pathname, dir.dir_name(fs.absolute_name(pathname)) | ||
| 223 | else | ||
| 224 | return nil, "File not found: " .. pathname | ||
| 225 | end | ||
| 226 | else | ||
| 227 | local temp_dir, errmake = fs.make_temp_dir(tmpname) | ||
| 228 | if not temp_dir then | ||
| 229 | return nil, "Failed creating temporary directory " .. tmpname .. ": " .. errmake | ||
| 230 | end | ||
| 231 | util.schedule_function(fs.delete, temp_dir) | ||
| 232 | local ok, errchange = fs.change_dir(temp_dir) | ||
| 233 | if not ok then return nil, errchange end | ||
| 234 | |||
| 235 | local file, err, errcode | ||
| 236 | |||
| 237 | if cache then | ||
| 238 | local cachefile | ||
| 239 | cachefile, err, errcode = fetch.fetch_caching(url) | ||
| 240 | |||
| 241 | if cachefile then | ||
| 242 | file = dir.path(temp_dir, filename) | ||
| 243 | fs.copy(cachefile, file) | ||
| 244 | end | ||
| 245 | end | ||
| 246 | |||
| 247 | if not file then | ||
| 248 | file, err, errcode = fetch.fetch_url(url, filename, cache) | ||
| 249 | end | ||
| 250 | |||
| 251 | fs.pop_dir() | ||
| 252 | if not file then | ||
| 253 | return nil, "Error fetching file: " .. err, errcode | ||
| 254 | end | ||
| 255 | |||
| 256 | return file, temp_dir | ||
| 257 | end | ||
| 258 | end | ||
| 259 | |||
| 260 | |||
| 261 | |||
| 262 | |||
| 263 | |||
| 264 | |||
| 265 | |||
| 266 | |||
| 267 | |||
| 268 | |||
| 269 | |||
| 270 | |||
| 271 | |||
| 272 | function fetch.find_base_dir(file, temp_dir, src_url, src_dir) | ||
| 273 | local ok, err = fs.change_dir(temp_dir) | ||
| 274 | if not ok then return nil, err end | ||
| 275 | fs.unpack_archive(file) | ||
| 276 | |||
| 277 | if not src_dir then | ||
| 278 | local rockspec = { | ||
| 279 | source = { | ||
| 280 | file = file, | ||
| 281 | dir = src_dir, | ||
| 282 | url = src_url, | ||
| 283 | }, | ||
| 284 | } | ||
| 285 | ok, err = fetch.find_rockspec_source_dir(rockspec, ".") | ||
| 286 | if ok then | ||
| 287 | src_dir = rockspec.source.dir | ||
| 288 | end | ||
| 289 | end | ||
| 290 | |||
| 291 | local inferred_dir = src_dir or dir.deduce_base_dir(src_url) | ||
| 292 | local found_dir = nil | ||
| 293 | if fs.exists(inferred_dir) then | ||
| 294 | found_dir = inferred_dir | ||
| 295 | else | ||
| 296 | util.printerr("Directory " .. inferred_dir .. " not found") | ||
| 297 | local files = fs.list_dir() | ||
| 298 | if files then | ||
| 299 | table.sort(files) | ||
| 300 | for _, filename in ipairs(files) do | ||
| 301 | if fs.is_dir(filename) then | ||
| 302 | util.printerr("Found " .. filename) | ||
| 303 | found_dir = filename | ||
| 304 | break | ||
| 305 | end | ||
| 306 | end | ||
| 307 | end | ||
| 308 | end | ||
| 309 | fs.pop_dir() | ||
| 310 | return inferred_dir, found_dir | ||
| 311 | end | ||
| 312 | |||
| 313 | |||
| 314 | local function fetch_and_verify_signature_for(url, filename, tmpdir) | ||
| 315 | local sig_url = signing.signature_url(url) | ||
| 316 | local sig_file, errfetch, errcode = fetch.fetch_url_at_temp_dir(sig_url, tmpdir) | ||
| 317 | if not sig_file then | ||
| 318 | return nil, "Could not fetch signature file for verification: " .. errfetch, errcode | ||
| 319 | end | ||
| 320 | |||
| 321 | local ok, err = signing.verify_signature(filename, sig_file) | ||
| 322 | if not ok then | ||
| 323 | return nil, "Failed signature verification: " .. err | ||
| 324 | end | ||
| 325 | |||
| 326 | return fs.absolute_name(sig_file) | ||
| 327 | end | ||
| 328 | |||
| 329 | |||
| 330 | |||
| 331 | |||
| 332 | |||
| 333 | |||
| 334 | |||
| 335 | |||
| 336 | |||
| 337 | |||
| 338 | function fetch.fetch_and_unpack_rock(url, dest, verify) | ||
| 339 | |||
| 340 | local name = dir.base_name(url):match("(.*)%.[^.]*%.rock") | ||
| 341 | local tmpname = "luarocks-rock-" .. name | ||
| 342 | |||
| 343 | local rock_file, err, errcode = fetch.fetch_url_at_temp_dir(url, tmpname, nil, true) | ||
| 344 | if not rock_file then | ||
| 345 | return nil, "Could not fetch rock file: " .. err, errcode | ||
| 346 | end | ||
| 347 | |||
| 348 | local sig_file | ||
| 349 | if verify then | ||
| 350 | sig_file, err = fetch_and_verify_signature_for(url, rock_file, tmpname) | ||
| 351 | if err then | ||
| 352 | return nil, err | ||
| 353 | end | ||
| 354 | end | ||
| 355 | |||
| 356 | rock_file = fs.absolute_name(rock_file) | ||
| 357 | |||
| 358 | local unpack_dir | ||
| 359 | if dest then | ||
| 360 | unpack_dir = dest | ||
| 361 | local ok, errmake = fs.make_dir(unpack_dir) | ||
| 362 | if not ok then | ||
| 363 | return nil, "Failed unpacking rock file: " .. errmake | ||
| 364 | end | ||
| 365 | else | ||
| 366 | unpack_dir, err = fs.make_temp_dir(name) | ||
| 367 | if not unpack_dir then | ||
| 368 | return nil, "Failed creating temporary dir: " .. err | ||
| 369 | end | ||
| 370 | end | ||
| 371 | if not dest then | ||
| 372 | util.schedule_function(fs.delete, unpack_dir) | ||
| 373 | end | ||
| 374 | local ok, errchange = fs.change_dir(unpack_dir) | ||
| 375 | if not ok then return nil, errchange end | ||
| 376 | ok, err = fs.unzip(rock_file) | ||
| 377 | if not ok then | ||
| 378 | return nil, "Failed unpacking rock file: " .. rock_file .. ": " .. err | ||
| 379 | end | ||
| 380 | if sig_file then | ||
| 381 | ok, err = fs.copy(sig_file, ".") | ||
| 382 | if not ok then | ||
| 383 | return nil, "Failed copying signature file" | ||
| 384 | end | ||
| 385 | end | ||
| 386 | fs.pop_dir() | ||
| 387 | return unpack_dir | ||
| 388 | end | ||
| 389 | |||
| 390 | |||
| 391 | |||
| 392 | |||
| 393 | |||
| 394 | |||
| 395 | |||
| 396 | |||
| 397 | function fetch.load_local_rockspec(rel_filename, quick) | ||
| 398 | local abs_filename = fs.absolute_name(rel_filename) | ||
| 399 | |||
| 400 | local basename = dir.base_name(abs_filename) | ||
| 401 | if basename ~= "rockspec" then | ||
| 402 | if not basename:match("(.*)%-[^-]*%-[0-9]*") then | ||
| 403 | return nil, "Expected filename in format 'name-version-revision.rockspec'." | ||
| 404 | end | ||
| 405 | end | ||
| 406 | |||
| 407 | local tbl, err = persist.load_into_table(abs_filename) | ||
| 408 | if not tbl and type(err) == "string" then | ||
| 409 | return nil, "Could not load rockspec file " .. abs_filename .. " (" .. err .. ")" | ||
| 410 | end | ||
| 411 | |||
| 412 | local rockspec, errrock = rockspecs.from_persisted_table(abs_filename, tbl, err, quick) | ||
| 413 | if not rockspec then | ||
| 414 | return nil, abs_filename .. ": " .. errrock | ||
| 415 | end | ||
| 416 | |||
| 417 | local name_version = rockspec.package:lower() .. "-" .. rockspec.version | ||
| 418 | if basename ~= "rockspec" and basename ~= name_version .. ".rockspec" then | ||
| 419 | return nil, "Inconsistency between rockspec filename (" .. basename .. ") and its contents (" .. name_version .. ".rockspec)." | ||
| 420 | end | ||
| 421 | |||
| 422 | return rockspec | ||
| 423 | end | ||
| 424 | |||
| 425 | |||
| 426 | |||
| 427 | |||
| 428 | |||
| 429 | |||
| 430 | |||
| 431 | |||
| 432 | |||
| 433 | |||
| 434 | |||
| 435 | function fetch.load_rockspec(url, location, verify) | ||
| 436 | |||
| 437 | local name | ||
| 438 | local basename = dir.base_name(url) | ||
| 439 | if basename == "rockspec" then | ||
| 440 | name = "rockspec" | ||
| 441 | else | ||
| 442 | name = basename:match("(.*)%.rockspec") | ||
| 443 | if not name then | ||
| 444 | return nil, "Filename '" .. url .. "' does not look like a rockspec." | ||
| 445 | end | ||
| 446 | end | ||
| 447 | |||
| 448 | local tmpname = "luarocks-rockspec-" .. name | ||
| 449 | local filename, err, errcode, ok | ||
| 450 | if location then | ||
| 451 | ok, err = fs.change_dir(location) | ||
| 452 | if not ok then return nil, err end | ||
| 453 | filename, err = fetch.fetch_url(url) | ||
| 454 | fs.pop_dir() | ||
| 455 | else | ||
| 456 | filename, err, errcode = fetch.fetch_url_at_temp_dir(url, tmpname, nil, true) | ||
| 457 | end | ||
| 458 | if not filename then | ||
| 459 | return nil, err, errcode | ||
| 460 | end | ||
| 461 | |||
| 462 | if verify then | ||
| 463 | local _, errfetch = fetch_and_verify_signature_for(url, filename, tmpname) | ||
| 464 | if err then | ||
| 465 | return nil, errfetch | ||
| 466 | end | ||
| 467 | end | ||
| 468 | |||
| 469 | return fetch.load_local_rockspec(filename) | ||
| 470 | end | ||
| 471 | |||
| 472 | |||
| 473 | |||
| 474 | |||
| 475 | |||
| 476 | |||
| 477 | |||
| 478 | |||
| 479 | |||
| 480 | |||
| 481 | function fetch.get_sources(rockspec, extract, dest_dir) | ||
| 482 | |||
| 483 | local url = rockspec.source.url | ||
| 484 | local name = rockspec.name .. "-" .. rockspec.version | ||
| 485 | local filename = rockspec.source.file | ||
| 486 | local source_file, store_dir | ||
| 487 | local ok, err, errcode | ||
| 488 | if dest_dir then | ||
| 489 | ok, err = fs.change_dir(dest_dir) | ||
| 490 | if not ok then return nil, err, "dest_dir" end | ||
| 491 | source_file, err, errcode = fetch.fetch_url(url, filename) | ||
| 492 | fs.pop_dir() | ||
| 493 | store_dir = dest_dir | ||
| 494 | else | ||
| 495 | source_file, store_dir, errcode = fetch.fetch_url_at_temp_dir(url, "luarocks-source-" .. name, filename) | ||
| 496 | end | ||
| 497 | if not source_file then | ||
| 498 | return nil, err or store_dir, errcode | ||
| 499 | end | ||
| 500 | if rockspec.source.md5 then | ||
| 501 | if not fs.check_md5(source_file, rockspec.source.md5) then | ||
| 502 | return nil, "MD5 check for " .. filename .. " has failed.", "md5" | ||
| 503 | end | ||
| 504 | end | ||
| 505 | if extract then | ||
| 506 | ok, err = fs.change_dir(store_dir) | ||
| 507 | if not ok then return nil, err end | ||
| 508 | ok, err = fs.unpack_archive(rockspec.source.file) | ||
| 509 | if not ok then return nil, err end | ||
| 510 | ok, err = fetch.find_rockspec_source_dir(rockspec, ".") | ||
| 511 | if not ok then return nil, err end | ||
| 512 | fs.pop_dir() | ||
| 513 | end | ||
| 514 | return source_file, store_dir | ||
| 515 | end | ||
| 516 | |||
| 517 | function fetch.find_rockspec_source_dir(rockspec, store_dir) | ||
| 518 | local ok, err = fs.change_dir(store_dir) | ||
| 519 | if not ok then return nil, err end | ||
| 520 | |||
| 521 | local file_count, dir_count = 0, 0 | ||
| 522 | local found_dir | ||
| 523 | |||
| 524 | if rockspec.source.dir and fs.exists(rockspec.source.dir) then | ||
| 525 | ok, err = true, nil | ||
| 526 | elseif rockspec.source.file and rockspec.source.dir then | ||
| 527 | ok, err = nil, "Directory " .. rockspec.source.dir .. " not found inside archive " .. rockspec.source.file | ||
| 528 | elseif not rockspec.source.dir_set then | ||
| 529 | |||
| 530 | local name = dir.base_name(rockspec.source.file or rockspec.source.url or "") | ||
| 531 | |||
| 532 | if name:match("%.lua$") or name:match("%.c$") then | ||
| 533 | if fs.is_file(name) then | ||
| 534 | rockspec.source.dir = "." | ||
| 535 | ok, err = true, nil | ||
| 536 | end | ||
| 537 | end | ||
| 538 | |||
| 539 | if not rockspec.source.dir then | ||
| 540 | for file in fs.dir() do | ||
| 541 | file_count = file_count + 1 | ||
| 542 | if fs.is_dir(file) then | ||
| 543 | dir_count = dir_count + 1 | ||
| 544 | found_dir = file | ||
| 545 | end | ||
| 546 | end | ||
| 547 | |||
| 548 | if dir_count == 1 then | ||
| 549 | rockspec.source.dir = found_dir | ||
| 550 | ok, err = true, nil | ||
| 551 | else | ||
| 552 | ok, err = nil, "Could not determine source directory from rock contents (" .. tostring(file_count) .. " file(s), " .. tostring(dir_count) .. " dir(s))" | ||
| 553 | end | ||
| 554 | end | ||
| 555 | else | ||
| 556 | ok, err = nil, "Could not determine source directory, please set source.dir in rockspec." | ||
| 557 | end | ||
| 558 | |||
| 559 | fs.pop_dir() | ||
| 560 | |||
| 561 | assert(rockspec.source.dir or not ok) | ||
| 562 | return ok, err | ||
| 563 | end | ||
| 564 | |||
| 565 | |||
| 566 | |||
| 567 | |||
| 568 | |||
| 569 | |||
| 570 | |||
| 571 | |||
| 572 | |||
| 573 | |||
| 574 | function fetch.fetch_sources(rockspec, extract, dest_dir) | ||
| 575 | |||
| 576 | |||
| 577 | |||
| 578 | if rockspec.source.url:match("^git://github%.com/") or | ||
| 579 | rockspec.source.url:match("^git://www%.github%.com/") then | ||
| 580 | rockspec.source.url = rockspec.source.url:gsub("^git://", "git+https://") | ||
| 581 | rockspec.source.protocol = "git+https" | ||
| 582 | end | ||
| 583 | |||
| 584 | local protocol = rockspec.source.protocol | ||
| 585 | local ok, err, proto | ||
| 586 | |||
| 587 | if dir.is_basic_protocol(protocol) then | ||
| 588 | proto = fetch | ||
| 589 | else | ||
| 590 | ok, proto = pcall(require, "luarocks.fetch." .. protocol:gsub("[+-]", "_")) | ||
| 591 | if not ok then | ||
| 592 | return nil, "Unknown protocol " .. protocol | ||
| 593 | end | ||
| 594 | end | ||
| 595 | |||
| 596 | if cfg.only_sources_from and | ||
| 597 | rockspec.source.pathname and | ||
| 598 | #rockspec.source.pathname > 0 then | ||
| 599 | if #cfg.only_sources_from == 0 then | ||
| 600 | return nil, "Can't download " .. rockspec.source.url .. " -- download from remote servers disabled" | ||
| 601 | elseif rockspec.source.pathname:find(cfg.only_sources_from, 1, true) ~= 1 then | ||
| 602 | return nil, "Can't download " .. rockspec.source.url .. " -- only downloading from " .. cfg.only_sources_from | ||
| 603 | end | ||
| 604 | end | ||
| 605 | |||
| 606 | local source_file, store_dir = proto.get_sources(rockspec, extract, dest_dir) | ||
| 607 | if not source_file then return nil, store_dir end | ||
| 608 | |||
| 609 | ok, err = fetch.find_rockspec_source_dir(rockspec, store_dir) | ||
| 610 | if not ok then return nil, err, "source.dir", source_file, store_dir end | ||
| 611 | |||
| 612 | return source_file, store_dir | ||
| 613 | end | ||
| 614 | |||
| 615 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local cvs = {} | ||
| 4 | |||
| 5 | |||
| 6 | local fs = require("luarocks.fs") | ||
| 7 | local dir = require("luarocks.dir") | ||
| 8 | local util = require("luarocks.util") | ||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | function cvs.get_sources(rockspec, extract, dest_dir) | ||
| 20 | local cvs_cmd = rockspec.variables.CVS | ||
| 21 | local ok, err_msg = fs.is_tool_available(cvs_cmd, "CVS") | ||
| 22 | if not ok then | ||
| 23 | return nil, err_msg | ||
| 24 | end | ||
| 25 | |||
| 26 | local name_version = rockspec.name .. "-" .. rockspec.version | ||
| 27 | local module = rockspec.source.module or dir.base_name(rockspec.source.url) | ||
| 28 | local command = { cvs_cmd, "-d" .. rockspec.source.pathname, "export", module } | ||
| 29 | if rockspec.source.tag then | ||
| 30 | table.insert(command, 4, "-r") | ||
| 31 | table.insert(command, 5, rockspec.source.tag) | ||
| 32 | end | ||
| 33 | local store_dir | ||
| 34 | if not dest_dir then | ||
| 35 | store_dir = fs.make_temp_dir(name_version) | ||
| 36 | if not store_dir then | ||
| 37 | return nil, "Failed creating temporary directory." | ||
| 38 | end | ||
| 39 | util.schedule_function(fs.delete, store_dir) | ||
| 40 | else | ||
| 41 | store_dir = dest_dir | ||
| 42 | end | ||
| 43 | local okchange, err = fs.change_dir(store_dir) | ||
| 44 | if not okchange then return nil, err end | ||
| 45 | if not fs.execute(_tl_table_unpack(command)) then | ||
| 46 | return nil, "Failed fetching files from CVS." | ||
| 47 | end | ||
| 48 | fs.pop_dir() | ||
| 49 | return module, store_dir | ||
| 50 | end | ||
| 51 | |||
| 52 | |||
| 53 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local git = {} | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | local fs = require("luarocks.fs") | ||
| 8 | local dir = require("luarocks.dir") | ||
| 9 | local vers = require("luarocks.core.vers") | ||
| 10 | local util = require("luarocks.util") | ||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | local cached_git_version | ||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | local function git_version(git_cmd) | ||
| 21 | if not cached_git_version then | ||
| 22 | local version_line = io.popen(fs.Q(git_cmd) .. ' --version'):read() | ||
| 23 | local version_string = version_line:match('%d-%.%d+%.?%d*') | ||
| 24 | cached_git_version = vers.parse_version(version_string) | ||
| 25 | end | ||
| 26 | |||
| 27 | return cached_git_version | ||
| 28 | end | ||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | local function git_is_at_least(git_cmd, version) | ||
| 35 | return git_version(git_cmd) >= vers.parse_version(version) | ||
| 36 | end | ||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | local function git_can_clone_by_tag(git_cmd) | ||
| 45 | return git_is_at_least(git_cmd, "1.7.10") | ||
| 46 | end | ||
| 47 | |||
| 48 | |||
| 49 | |||
| 50 | |||
| 51 | |||
| 52 | local function git_supports_shallow_submodules(git_cmd) | ||
| 53 | return git_is_at_least(git_cmd, "1.8.4") | ||
| 54 | end | ||
| 55 | |||
| 56 | |||
| 57 | |||
| 58 | |||
| 59 | local function git_supports_shallow_recommendations(git_cmd) | ||
| 60 | return git_is_at_least(git_cmd, "2.10.0") | ||
| 61 | end | ||
| 62 | |||
| 63 | local function git_identifier(git_cmd, ver) | ||
| 64 | if not (ver:match("^dev%-%d+$") or ver:match("^scm%-%d+$")) then | ||
| 65 | return nil | ||
| 66 | end | ||
| 67 | local pd = io.popen(fs.command_at(fs.current_dir(), fs.Q(git_cmd) .. " log --pretty=format:%ai_%h -n 1")) | ||
| 68 | if not pd then | ||
| 69 | return nil | ||
| 70 | end | ||
| 71 | local date_hash = pd:read("*l") | ||
| 72 | pd:close() | ||
| 73 | if not date_hash then | ||
| 74 | return nil | ||
| 75 | end | ||
| 76 | local date, time, _tz, hash = date_hash:match("([^%s]+) ([^%s]+) ([^%s]+)_([^%s]+)") | ||
| 77 | date = date:gsub("%-", "") | ||
| 78 | time = time:gsub(":", "") | ||
| 79 | return date .. "." .. time .. "." .. hash | ||
| 80 | end | ||
| 81 | |||
| 82 | |||
| 83 | |||
| 84 | |||
| 85 | |||
| 86 | |||
| 87 | |||
| 88 | |||
| 89 | function git.get_sources(rockspec, extract, dest_dir, depth) | ||
| 90 | |||
| 91 | local git_cmd = rockspec.variables.GIT | ||
| 92 | local name_version = rockspec.name .. "-" .. rockspec.version | ||
| 93 | local module = dir.base_name(rockspec.source.url) | ||
| 94 | |||
| 95 | module = module:gsub("%.git$", "") | ||
| 96 | |||
| 97 | local ok_available, err_msg = fs.is_tool_available(git_cmd, "Git") | ||
| 98 | if not ok_available then | ||
| 99 | return nil, err_msg | ||
| 100 | end | ||
| 101 | |||
| 102 | local store_dir | ||
| 103 | if not dest_dir then | ||
| 104 | store_dir = fs.make_temp_dir(name_version) | ||
| 105 | if not store_dir then | ||
| 106 | return nil, "Failed creating temporary directory." | ||
| 107 | end | ||
| 108 | util.schedule_function(fs.delete, store_dir) | ||
| 109 | else | ||
| 110 | store_dir = dest_dir | ||
| 111 | end | ||
| 112 | store_dir = fs.absolute_name(store_dir) | ||
| 113 | local ok, err = fs.change_dir(store_dir) | ||
| 114 | if not ok then return nil, err end | ||
| 115 | |||
| 116 | local command = { fs.Q(git_cmd), "clone", depth or "--depth=1", rockspec.source.url, module } | ||
| 117 | local tag_or_branch = rockspec.source.tag or rockspec.source.branch | ||
| 118 | |||
| 119 | |||
| 120 | if tag_or_branch == "master" then tag_or_branch = nil end | ||
| 121 | if tag_or_branch then | ||
| 122 | if git_can_clone_by_tag(git_cmd) then | ||
| 123 | |||
| 124 | |||
| 125 | table.insert(command, 3, "--branch=" .. tag_or_branch) | ||
| 126 | end | ||
| 127 | end | ||
| 128 | if not fs.execute(_tl_table_unpack(command)) then | ||
| 129 | return nil, "Failed cloning git repository." | ||
| 130 | end | ||
| 131 | ok, err = fs.change_dir(module) | ||
| 132 | if not ok then return nil, err end | ||
| 133 | if tag_or_branch and not git_can_clone_by_tag() then | ||
| 134 | if not fs.execute(fs.Q(git_cmd), "checkout", tag_or_branch) then | ||
| 135 | return nil, 'Failed to check out the "' .. tag_or_branch .. '" tag or branch.' | ||
| 136 | end | ||
| 137 | end | ||
| 138 | |||
| 139 | |||
| 140 | if rockspec:format_is_at_least("3.0") then | ||
| 141 | command = { fs.Q(git_cmd), "submodule", "update", "--init", "--recursive" } | ||
| 142 | |||
| 143 | if git_supports_shallow_recommendations(git_cmd) then | ||
| 144 | table.insert(command, 5, "--recommend-shallow") | ||
| 145 | elseif git_supports_shallow_submodules(git_cmd) then | ||
| 146 | |||
| 147 | table.insert(command, 5, "--depth=1") | ||
| 148 | end | ||
| 149 | |||
| 150 | if not fs.execute(_tl_table_unpack(command)) then | ||
| 151 | return nil, 'Failed to fetch submodules.' | ||
| 152 | end | ||
| 153 | end | ||
| 154 | |||
| 155 | if not rockspec.source.tag then | ||
| 156 | rockspec.source.identifier = git_identifier(git_cmd, rockspec.version) | ||
| 157 | end | ||
| 158 | |||
| 159 | fs.delete(dir.path(store_dir, module, ".git")) | ||
| 160 | fs.delete(dir.path(store_dir, module, ".gitignore")) | ||
| 161 | fs.pop_dir() | ||
| 162 | fs.pop_dir() | ||
| 163 | return module, store_dir | ||
| 164 | end | ||
| 165 | |||
| 166 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local git_file = {} | ||
| 4 | |||
| 5 | |||
| 6 | local git = require("luarocks.fetch.git") | ||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | function git_file.get_sources(rockspec, extract, dest_dir) | ||
| 18 | rockspec.source.url = rockspec.source.url:gsub("^git.file://", "") | ||
| 19 | return git.get_sources(rockspec, extract, dest_dir) | ||
| 20 | end | ||
| 21 | |||
| 22 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | local git_http = {} | ||
| 11 | |||
| 12 | |||
| 13 | local git = require("luarocks.fetch.git") | ||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | function git_http.get_sources(rockspec, extract, dest_dir) | ||
| 25 | rockspec.source.url = rockspec.source.url:gsub("^git.", "") | ||
| 26 | return git.get_sources(rockspec, extract, dest_dir, "--") | ||
| 27 | end | ||
| 28 | |||
| 29 | 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 @@ | |||
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | local git_ssh = {} | ||
| 11 | |||
| 12 | |||
| 13 | local git = require("luarocks.fetch.git") | ||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | function git_ssh.get_sources(rockspec, extract, dest_dir) | ||
| 25 | rockspec.source.url = rockspec.source.url:gsub("^git.", "") | ||
| 26 | |||
| 27 | |||
| 28 | if rockspec.source.url:match("^ssh://[^/]+:[^%d]") then | ||
| 29 | rockspec.source.url = rockspec.source.url:gsub("^ssh://", "") | ||
| 30 | end | ||
| 31 | |||
| 32 | return git.get_sources(rockspec, extract, dest_dir, "--") | ||
| 33 | end | ||
| 34 | |||
| 35 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local hg = {} | ||
| 4 | |||
| 5 | |||
| 6 | local fs = require("luarocks.fs") | ||
| 7 | local dir = require("luarocks.dir") | ||
| 8 | local util = require("luarocks.util") | ||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | function hg.get_sources(rockspec, extract, dest_dir) | ||
| 20 | |||
| 21 | local hg_cmd = rockspec.variables.HG | ||
| 22 | local ok_available, err_msg = fs.is_tool_available(hg_cmd, "Mercurial") | ||
| 23 | if not ok_available then | ||
| 24 | return nil, err_msg | ||
| 25 | end | ||
| 26 | |||
| 27 | local name_version = rockspec.name .. "-" .. rockspec.version | ||
| 28 | |||
| 29 | local url = rockspec.source.url:gsub("^hg://", "") | ||
| 30 | |||
| 31 | local module = dir.base_name(url) | ||
| 32 | |||
| 33 | local command = { hg_cmd, "clone", url, module } | ||
| 34 | local tag_or_branch = rockspec.source.tag or rockspec.source.branch | ||
| 35 | if tag_or_branch then | ||
| 36 | command = { hg_cmd, "clone", "--rev", tag_or_branch, url, module } | ||
| 37 | end | ||
| 38 | local store_dir | ||
| 39 | if not dest_dir then | ||
| 40 | store_dir = fs.make_temp_dir(name_version) | ||
| 41 | if not store_dir then | ||
| 42 | return nil, "Failed creating temporary directory." | ||
| 43 | end | ||
| 44 | util.schedule_function(fs.delete, store_dir) | ||
| 45 | else | ||
| 46 | store_dir = dest_dir | ||
| 47 | end | ||
| 48 | local ok, err = fs.change_dir(store_dir) | ||
| 49 | if not ok then return nil, err end | ||
| 50 | if not fs.execute(_tl_table_unpack(command)) then | ||
| 51 | return nil, "Failed cloning hg repository." | ||
| 52 | end | ||
| 53 | ok, err = fs.change_dir(module) | ||
| 54 | if not ok then return nil, err end | ||
| 55 | |||
| 56 | fs.delete(dir.path(store_dir, module, ".hg")) | ||
| 57 | fs.delete(dir.path(store_dir, module, ".hgignore")) | ||
| 58 | fs.pop_dir() | ||
| 59 | fs.pop_dir() | ||
| 60 | return module, store_dir | ||
| 61 | end | ||
| 62 | |||
| 63 | |||
| 64 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | local hg_http = {} | ||
| 9 | |||
| 10 | |||
| 11 | local hg = require("luarocks.fetch.hg") | ||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | function hg_http.get_sources(rockspec, extract, dest_dir) | ||
| 23 | rockspec.source.url = rockspec.source.url:gsub("^hg.", "") | ||
| 24 | return hg.get_sources(rockspec, extract, dest_dir) | ||
| 25 | end | ||
| 26 | |||
| 27 | 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 @@ | |||
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 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 @@ | |||
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local sscm = {} | ||
| 4 | |||
| 5 | |||
| 6 | local fs = require("luarocks.fs") | ||
| 7 | local dir = require("luarocks.dir") | ||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | function sscm.get_sources(rockspec, extract, dest_dir) | ||
| 19 | |||
| 20 | local sscm_cmd = rockspec.variables.SSCM | ||
| 21 | local module = rockspec.source.module or dir.base_name(rockspec.source.url) | ||
| 22 | local branch, repository = string.match(rockspec.source.pathname, "^([^/]*)/(.*)") | ||
| 23 | if not branch or not repository then | ||
| 24 | return nil, "Error retrieving branch and repository from rockspec." | ||
| 25 | end | ||
| 26 | |||
| 27 | local working_dir | ||
| 28 | local tmp = io.popen(string.format(sscm_cmd .. [[ property "/" -d -b%s -p%s]], branch, repository)) | ||
| 29 | for line in tmp:lines() do | ||
| 30 | |||
| 31 | working_dir = string.match(line, "Working directory:[%s]*(.*)%c$") | ||
| 32 | if working_dir then break end | ||
| 33 | end | ||
| 34 | tmp:close() | ||
| 35 | if not working_dir then | ||
| 36 | return nil, "Error retrieving working directory from SSCM." | ||
| 37 | end | ||
| 38 | if not fs.execute(sscm_cmd, "get", "*", "-e", "-r", "-b" .. branch, "-p" .. repository, "-tmodify", "-wreplace") then | ||
| 39 | return nil, "Failed fetching files from SSCM." | ||
| 40 | end | ||
| 41 | |||
| 42 | return module, working_dir | ||
| 43 | end | ||
| 44 | |||
| 45 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local svn = {} | ||
| 4 | |||
| 5 | |||
| 6 | local fs = require("luarocks.fs") | ||
| 7 | local dir = require("luarocks.dir") | ||
| 8 | local util = require("luarocks.util") | ||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | function svn.get_sources(rockspec, extract, dest_dir) | ||
| 20 | |||
| 21 | local svn_cmd = rockspec.variables.SVN | ||
| 22 | local ok, err_msg = fs.is_tool_available(svn_cmd, "Subversion") | ||
| 23 | if not ok then | ||
| 24 | return nil, err_msg | ||
| 25 | end | ||
| 26 | |||
| 27 | local name_version = rockspec.name .. "-" .. rockspec.version | ||
| 28 | local module = rockspec.source.module or dir.base_name(rockspec.source.url) | ||
| 29 | local url = rockspec.source.url:gsub("^svn://", "") | ||
| 30 | local command = { svn_cmd, "checkout", url, module } | ||
| 31 | if rockspec.source.tag then | ||
| 32 | table.insert(command, 5, "-r") | ||
| 33 | table.insert(command, 6, rockspec.source.tag) | ||
| 34 | end | ||
| 35 | local store_dir | ||
| 36 | if not dest_dir then | ||
| 37 | store_dir = fs.make_temp_dir(name_version) | ||
| 38 | if not store_dir then | ||
| 39 | return nil, "Failed creating temporary directory." | ||
| 40 | end | ||
| 41 | util.schedule_function(fs.delete, store_dir) | ||
| 42 | else | ||
| 43 | store_dir = dest_dir | ||
| 44 | end | ||
| 45 | local okchange, err = fs.change_dir(store_dir) | ||
| 46 | if not okchange then return nil, err end | ||
| 47 | if not fs.execute(_tl_table_unpack(command)) then | ||
| 48 | return nil, "Failed fetching files from Subversion." | ||
| 49 | end | ||
| 50 | okchange, err = fs.change_dir(module) | ||
| 51 | if not okchange then return nil, err end | ||
| 52 | for _, d in ipairs(fs.find(".")) do | ||
| 53 | if dir.base_name(d) == ".svn" then | ||
| 54 | fs.delete(dir.path(store_dir, module, d)) | ||
| 55 | end | ||
| 56 | end | ||
| 57 | fs.pop_dir() | ||
| 58 | fs.pop_dir() | ||
| 59 | return module, store_dir | ||
| 60 | end | ||
| 61 | |||
| 62 | |||
| 63 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local fun = {} | ||
| 4 | |||
| 5 | |||
| 6 | function fun.concat(xs, ys) | ||
| 7 | local rs = {} | ||
| 8 | local n = #xs | ||
| 9 | for i = 1, n do | ||
| 10 | rs[i] = xs[i] | ||
| 11 | end | ||
| 12 | for i = 1, #ys do | ||
| 13 | rs[i + n] = ys[i] | ||
| 14 | end | ||
| 15 | |||
| 16 | return rs | ||
| 17 | end | ||
| 18 | |||
| 19 | function fun.contains(xs, v) | ||
| 20 | for _, x in ipairs(xs) do | ||
| 21 | if v == x then | ||
| 22 | return true | ||
| 23 | end | ||
| 24 | end | ||
| 25 | return false | ||
| 26 | end | ||
| 27 | |||
| 28 | function fun.map(xs, f) | ||
| 29 | local rs = {} | ||
| 30 | for i = 1, #xs do | ||
| 31 | rs[i] = f(xs[i]) | ||
| 32 | end | ||
| 33 | return rs | ||
| 34 | end | ||
| 35 | |||
| 36 | function fun.filter(xs, f) | ||
| 37 | local rs = {} | ||
| 38 | for i = 1, #xs do | ||
| 39 | local v = xs[i] | ||
| 40 | if f(v) then | ||
| 41 | rs[#rs + 1] = v | ||
| 42 | end | ||
| 43 | end | ||
| 44 | return rs | ||
| 45 | end | ||
| 46 | |||
| 47 | function fun.reverse_in(t) | ||
| 48 | for i = 1, math.floor(#t / 2) do | ||
| 49 | local m, n = i, #t - i + 1 | ||
| 50 | local a, b = t[m], t[n] | ||
| 51 | t[m] = b | ||
| 52 | t[n] = a | ||
| 53 | end | ||
| 54 | return t | ||
| 55 | end | ||
| 56 | |||
| 57 | function fun.sort_in(t, f) | ||
| 58 | table.sort(t, f) | ||
| 59 | return t | ||
| 60 | end | ||
| 61 | |||
| 62 | function fun.flip(f) | ||
| 63 | return function(a, b) | ||
| 64 | return f(b, a) | ||
| 65 | end | ||
| 66 | end | ||
| 67 | |||
| 68 | function fun.find(xs, f) | ||
| 69 | if type(xs) == "table" then | ||
| 70 | for _, x in ipairs(xs) do | ||
| 71 | local r = f(x) | ||
| 72 | if r then | ||
| 73 | return r | ||
| 74 | end | ||
| 75 | end | ||
| 76 | else | ||
| 77 | for x in xs do | ||
| 78 | local r = f(x) | ||
| 79 | if r then | ||
| 80 | return r | ||
| 81 | end | ||
| 82 | end | ||
| 83 | end | ||
| 84 | end | ||
| 85 | |||
| 86 | |||
| 87 | function fun.partial(f, ...) | ||
| 88 | local n = select("#", ...) | ||
| 89 | if n == 1 then | ||
| 90 | local a = ... | ||
| 91 | return function(...) | ||
| 92 | return f(a, ...) | ||
| 93 | end | ||
| 94 | elseif n == 2 then | ||
| 95 | local a, b = ... | ||
| 96 | return function(...) | ||
| 97 | return f(a, b, ...) | ||
| 98 | end | ||
| 99 | else | ||
| 100 | local pargs = { n = n, ... } | ||
| 101 | return function(...) | ||
| 102 | local m = select("#", ...) | ||
| 103 | local fargs = { ... } | ||
| 104 | local args = {} | ||
| 105 | for i = 1, n do | ||
| 106 | args[i] = pargs[i] | ||
| 107 | end | ||
| 108 | for i = 1, m do | ||
| 109 | args[i + n] = fargs[i] | ||
| 110 | end | ||
| 111 | return f(_tl_table_unpack(args, 1, n + m)) | ||
| 112 | end | ||
| 113 | end | ||
| 114 | end | ||
| 115 | |||
| 116 | function fun.memoize(fn) | ||
| 117 | local memory = setmetatable({}, { __mode = "k" }) | ||
| 118 | local errors = setmetatable({}, { __mode = "k" }) | ||
| 119 | local NIL = {} | ||
| 120 | return function(a) | ||
| 121 | if memory[a] then | ||
| 122 | if memory[a] == NIL then | ||
| 123 | return nil, errors[a] | ||
| 124 | end | ||
| 125 | return memory[a] | ||
| 126 | end | ||
| 127 | local ret1, ret2 = fn(a) | ||
| 128 | if ret1 then | ||
| 129 | memory[a] = ret1 | ||
| 130 | else | ||
| 131 | memory[a] = NIL | ||
| 132 | errors[a] = ret2 | ||
| 133 | end | ||
| 134 | return ret1, ret2 | ||
| 135 | end | ||
| 136 | end | ||
| 137 | |||
| 138 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | local loaders = package.loaders or package.searchers | ||
| 9 | local require, ipairs, table, type, next, tostring, error = | ||
| 10 | require, ipairs, table, type, next, tostring, error | ||
| 11 | |||
| 12 | local loader = {} | ||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | local is_clean = not package.loaded["luarocks.core.cfg"] | ||
| 18 | |||
| 19 | |||
| 20 | local cfg = require("luarocks.core.cfg") | ||
| 21 | local cfg_ok, _err = cfg.init() | ||
| 22 | if cfg_ok then | ||
| 23 | cfg.init_package_paths() | ||
| 24 | end | ||
| 25 | |||
| 26 | local path = require("luarocks.core.path") | ||
| 27 | local manif = require("luarocks.core.manif") | ||
| 28 | local vers = require("luarocks.core.vers") | ||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | |||
| 49 | |||
| 50 | |||
| 51 | |||
| 52 | |||
| 53 | |||
| 54 | local temporary_global = false | ||
| 55 | local status, luarocks_value = pcall(function() | ||
| 56 | return luarocks | ||
| 57 | end) | ||
| 58 | if status and luarocks_value then | ||
| 59 | |||
| 60 | |||
| 61 | |||
| 62 | luarocks.loader = loader | ||
| 63 | else | ||
| 64 | |||
| 65 | |||
| 66 | |||
| 67 | |||
| 68 | |||
| 69 | local info = debug and debug.getinfo(2, "nS") | ||
| 70 | if info and info.what == "C" and not info.name then | ||
| 71 | luarocks = { loader = loader } | ||
| 72 | temporary_global = true | ||
| 73 | |||
| 74 | |||
| 75 | end | ||
| 76 | end | ||
| 77 | |||
| 78 | |||
| 79 | |||
| 80 | |||
| 81 | |||
| 82 | loader.context = {} | ||
| 83 | |||
| 84 | |||
| 85 | |||
| 86 | |||
| 87 | |||
| 88 | |||
| 89 | function loader.add_context(name, version) | ||
| 90 | if temporary_global then | ||
| 91 | |||
| 92 | |||
| 93 | luarocks = nil | ||
| 94 | temporary_global = false | ||
| 95 | end | ||
| 96 | |||
| 97 | local tree_manifests = manif.load_rocks_tree_manifests() | ||
| 98 | if not tree_manifests then | ||
| 99 | return | ||
| 100 | end | ||
| 101 | |||
| 102 | manif.scan_dependencies(name, version, tree_manifests, loader.context) | ||
| 103 | end | ||
| 104 | |||
| 105 | |||
| 106 | |||
| 107 | |||
| 108 | |||
| 109 | |||
| 110 | |||
| 111 | local function sort_versions(a, b) | ||
| 112 | return a.version > b.version | ||
| 113 | end | ||
| 114 | |||
| 115 | |||
| 116 | |||
| 117 | |||
| 118 | |||
| 119 | |||
| 120 | |||
| 121 | |||
| 122 | |||
| 123 | |||
| 124 | |||
| 125 | |||
| 126 | |||
| 127 | |||
| 128 | |||
| 129 | |||
| 130 | local function call_other_loaders(module, name, version, module_name) | ||
| 131 | for _, a_loader in ipairs(loaders) do | ||
| 132 | if a_loader ~= loader.luarocks_loader then | ||
| 133 | local results = { a_loader(module_name) } | ||
| 134 | local f = results[1] | ||
| 135 | if type(f) == "function" then | ||
| 136 | if #results == 2 then | ||
| 137 | return f, results[2] | ||
| 138 | else | ||
| 139 | return f | ||
| 140 | end | ||
| 141 | end | ||
| 142 | end | ||
| 143 | end | ||
| 144 | return "Failed loading module " .. module .. " in LuaRocks rock " .. name .. " " .. version | ||
| 145 | end | ||
| 146 | |||
| 147 | |||
| 148 | |||
| 149 | |||
| 150 | |||
| 151 | |||
| 152 | |||
| 153 | |||
| 154 | |||
| 155 | |||
| 156 | |||
| 157 | |||
| 158 | |||
| 159 | |||
| 160 | |||
| 161 | local function add_providers(providers, entries, tree, module, filter_name) | ||
| 162 | for i, entry in ipairs(entries) do | ||
| 163 | local name, version = entry:match("^([^/]*)/(.*)$") | ||
| 164 | |||
| 165 | local file_name = tree.manifest.repository[name][version][1].modules[module] | ||
| 166 | if type(file_name) ~= "string" then | ||
| 167 | error("Invalid data in manifest file for module " .. tostring(module) .. " (invalid data for " .. tostring(name) .. " " .. tostring(version) .. ")") | ||
| 168 | end | ||
| 169 | |||
| 170 | file_name = filter_name(file_name, name, version, tree.tree, i) | ||
| 171 | |||
| 172 | if loader.context[name] == version then | ||
| 173 | return name, version, file_name | ||
| 174 | end | ||
| 175 | |||
| 176 | table.insert(providers, { | ||
| 177 | name = name, | ||
| 178 | version = vers.parse_version(version), | ||
| 179 | module_name = file_name, | ||
| 180 | tree = tree, | ||
| 181 | }) | ||
| 182 | end | ||
| 183 | end | ||
| 184 | |||
| 185 | |||
| 186 | |||
| 187 | |||
| 188 | |||
| 189 | |||
| 190 | |||
| 191 | |||
| 192 | |||
| 193 | |||
| 194 | |||
| 195 | |||
| 196 | |||
| 197 | local function select_module(module, filter_name) | ||
| 198 | |||
| 199 | local tree_manifests = manif.load_rocks_tree_manifests() | ||
| 200 | if not tree_manifests then | ||
| 201 | return nil | ||
| 202 | end | ||
| 203 | |||
| 204 | local providers = {} | ||
| 205 | local initmodule | ||
| 206 | for _, tree in ipairs(tree_manifests) do | ||
| 207 | local entries = tree.manifest.modules[module] | ||
| 208 | if entries then | ||
| 209 | local n, v, f = add_providers(providers, entries, tree, module, filter_name) | ||
| 210 | if n then | ||
| 211 | return n, v, f | ||
| 212 | end | ||
| 213 | else | ||
| 214 | initmodule = initmodule or module .. ".init" | ||
| 215 | entries = tree.manifest.modules[initmodule] | ||
| 216 | if entries then | ||
| 217 | local n, v, f = add_providers(providers, entries, tree, initmodule, filter_name) | ||
| 218 | if n then | ||
| 219 | return n, v, f | ||
| 220 | end | ||
| 221 | end | ||
| 222 | end | ||
| 223 | end | ||
| 224 | |||
| 225 | if next(providers) then | ||
| 226 | table.sort(providers, sort_versions) | ||
| 227 | local first = providers[1] | ||
| 228 | return first.name, first.version.string, first.module_name | ||
| 229 | end | ||
| 230 | end | ||
| 231 | |||
| 232 | |||
| 233 | |||
| 234 | |||
| 235 | |||
| 236 | |||
| 237 | |||
| 238 | |||
| 239 | |||
| 240 | |||
| 241 | |||
| 242 | |||
| 243 | local function filter_module_name(file_name, name, version, _tree, i) | ||
| 244 | if i > 1 then | ||
| 245 | file_name = path.versioned_name(file_name, "", name, version) | ||
| 246 | end | ||
| 247 | return path.path_to_module(file_name) | ||
| 248 | end | ||
| 249 | |||
| 250 | |||
| 251 | |||
| 252 | |||
| 253 | |||
| 254 | |||
| 255 | |||
| 256 | |||
| 257 | |||
| 258 | local function pick_module(module) | ||
| 259 | return select_module(module, filter_module_name) | ||
| 260 | end | ||
| 261 | |||
| 262 | |||
| 263 | |||
| 264 | |||
| 265 | |||
| 266 | |||
| 267 | |||
| 268 | |||
| 269 | |||
| 270 | |||
| 271 | |||
| 272 | |||
| 273 | |||
| 274 | function loader.which(module, where) | ||
| 275 | where = where or "l" | ||
| 276 | if where:match("l") then | ||
| 277 | local rock_name, rock_version, file_name = select_module(module, path.which_i) | ||
| 278 | if rock_name then | ||
| 279 | local fd = io.open(file_name) | ||
| 280 | if fd then | ||
| 281 | fd:close() | ||
| 282 | return file_name, rock_name, rock_version, "l" | ||
| 283 | end | ||
| 284 | end | ||
| 285 | end | ||
| 286 | if where:match("p") then | ||
| 287 | local modpath = module:gsub("%.", "/") | ||
| 288 | for _, v in ipairs({ package.path, package.cpath }) do | ||
| 289 | for p in v:gmatch("([^;]+)") do | ||
| 290 | local file_name = p:gsub("%?", modpath) | ||
| 291 | local fd = io.open(file_name) | ||
| 292 | if fd then | ||
| 293 | fd:close() | ||
| 294 | return file_name, v, nil, "p" | ||
| 295 | end | ||
| 296 | end | ||
| 297 | end | ||
| 298 | end | ||
| 299 | end | ||
| 300 | |||
| 301 | |||
| 302 | |||
| 303 | |||
| 304 | |||
| 305 | |||
| 306 | |||
| 307 | |||
| 308 | |||
| 309 | |||
| 310 | |||
| 311 | |||
| 312 | |||
| 313 | function loader.luarocks_loader(module) | ||
| 314 | local name, version, module_name = pick_module(module) | ||
| 315 | if not name then | ||
| 316 | return "No LuaRocks module found for " .. module | ||
| 317 | else | ||
| 318 | loader.add_context(name, version) | ||
| 319 | return call_other_loaders(module, name, version, module_name) | ||
| 320 | end | ||
| 321 | end | ||
| 322 | |||
| 323 | table.insert(loaders, 1, loader.luarocks_loader) | ||
| 324 | |||
| 325 | if is_clean then | ||
| 326 | for modname, _ in pairs(package.loaded) do | ||
| 327 | if modname:match("^luarocks%.") then | ||
| 328 | package.loaded[modname] = nil | ||
| 329 | end | ||
| 330 | end | ||
| 331 | end | ||
| 332 | |||
| 333 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | local manif = {} | ||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | local core = require("luarocks.core.manif") | ||
| 16 | local persist = require("luarocks.persist") | ||
| 17 | local fetch = require("luarocks.fetch") | ||
| 18 | local dir = require("luarocks.dir") | ||
| 19 | local fs = require("luarocks.fs") | ||
| 20 | local cfg = require("luarocks.core.cfg") | ||
| 21 | local path = require("luarocks.path") | ||
| 22 | local util = require("luarocks.util") | ||
| 23 | local queries = require("luarocks.queries") | ||
| 24 | local type_manifest = require("luarocks.type.manifest") | ||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | manif.cache_manifest = core.cache_manifest | ||
| 32 | manif.load_rocks_tree_manifests = core.load_rocks_tree_manifests | ||
| 33 | manif.scan_dependencies = core.scan_dependencies | ||
| 34 | |||
| 35 | manif.rock_manifest_cache = {} | ||
| 36 | |||
| 37 | local function check_manifest(repo_url, manifest, globals) | ||
| 38 | local ok, err = type_manifest.check(manifest, globals) | ||
| 39 | if not ok then | ||
| 40 | core.cache_manifest(repo_url, cfg.lua_version, nil) | ||
| 41 | return nil, "Error checking manifest: " .. err, "type" | ||
| 42 | end | ||
| 43 | return manifest | ||
| 44 | end | ||
| 45 | |||
| 46 | local postprocess_dependencies | ||
| 47 | do | ||
| 48 | local postprocess_check = setmetatable({}, { __mode = "k" }) | ||
| 49 | postprocess_dependencies = function(manifest) | ||
| 50 | if postprocess_check[manifest] then | ||
| 51 | return | ||
| 52 | end | ||
| 53 | if manifest.dependencies then | ||
| 54 | for _, versions in pairs(manifest.dependencies) do | ||
| 55 | for _, entries in pairs(versions) do | ||
| 56 | for k, v in ipairs(entries) do | ||
| 57 | entries[k] = queries.from_persisted_table(v) | ||
| 58 | end | ||
| 59 | end | ||
| 60 | end | ||
| 61 | end | ||
| 62 | postprocess_check[manifest] = true | ||
| 63 | end | ||
| 64 | end | ||
| 65 | |||
| 66 | function manif.load_rock_manifest(name, version, root) | ||
| 67 | assert(not name:match("/")) | ||
| 68 | |||
| 69 | local name_version = name .. "/" .. version | ||
| 70 | if manif.rock_manifest_cache[name_version] then | ||
| 71 | return manif.rock_manifest_cache[name_version].rock_manifest | ||
| 72 | end | ||
| 73 | local pathname = path.rock_manifest_file(name, version, root) | ||
| 74 | local rock_manifest = persist.load_into_table(pathname) | ||
| 75 | if not rock_manifest then | ||
| 76 | return nil, "rock_manifest file not found for " .. name .. " " .. version .. " - not a LuaRocks tree?" | ||
| 77 | end | ||
| 78 | manif.rock_manifest_cache[name_version] = rock_manifest | ||
| 79 | return rock_manifest.rock_manifest | ||
| 80 | end | ||
| 81 | |||
| 82 | |||
| 83 | |||
| 84 | |||
| 85 | |||
| 86 | |||
| 87 | |||
| 88 | |||
| 89 | |||
| 90 | |||
| 91 | function manif.load_manifest(repo_url, lua_version, versioned_only) | ||
| 92 | lua_version = lua_version or cfg.lua_version | ||
| 93 | |||
| 94 | local cached_manifest = core.get_cached_manifest(repo_url, lua_version) | ||
| 95 | if cached_manifest then | ||
| 96 | postprocess_dependencies(cached_manifest) | ||
| 97 | return cached_manifest | ||
| 98 | end | ||
| 99 | |||
| 100 | local filenames = { | ||
| 101 | "manifest-" .. lua_version .. ".zip", | ||
| 102 | "manifest-" .. lua_version, | ||
| 103 | not versioned_only and "manifest" or nil, | ||
| 104 | } | ||
| 105 | |||
| 106 | local protocol, repodir = dir.split_url(repo_url) | ||
| 107 | local pathname, from_cache | ||
| 108 | if protocol == "file" then | ||
| 109 | for _, filename in ipairs(filenames) do | ||
| 110 | pathname = dir.path(repodir, filename) | ||
| 111 | if fs.exists(pathname) then | ||
| 112 | break | ||
| 113 | end | ||
| 114 | end | ||
| 115 | else | ||
| 116 | local err, errcode | ||
| 117 | for _, filename in ipairs(filenames) do | ||
| 118 | pathname, err, errcode, from_cache = fetch.fetch_caching(dir.path(repo_url, filename), "no_mirror") | ||
| 119 | if pathname then | ||
| 120 | break | ||
| 121 | end | ||
| 122 | end | ||
| 123 | if not pathname then | ||
| 124 | return nil, err, errcode | ||
| 125 | end | ||
| 126 | end | ||
| 127 | if pathname:match(".*%.zip$") then | ||
| 128 | pathname = fs.absolute_name(pathname) | ||
| 129 | local nozip = pathname:match("(.*)%.zip$") | ||
| 130 | if not from_cache then | ||
| 131 | local dirname = dir.dir_name(pathname) | ||
| 132 | fs.change_dir(dirname) | ||
| 133 | fs.delete(nozip) | ||
| 134 | local ok, err = fs.unzip(pathname) | ||
| 135 | fs.pop_dir() | ||
| 136 | if not ok then | ||
| 137 | fs.delete(pathname) | ||
| 138 | fs.delete(pathname .. ".timestamp") | ||
| 139 | return nil, "Failed extracting manifest file: " .. err | ||
| 140 | end | ||
| 141 | end | ||
| 142 | pathname = nozip | ||
| 143 | end | ||
| 144 | local manifest, err, errcode = core.manifest_loader(pathname, repo_url, lua_version) | ||
| 145 | if not manifest and type(err) == "string" then | ||
| 146 | return nil, err, errcode | ||
| 147 | end | ||
| 148 | |||
| 149 | postprocess_dependencies(manifest) | ||
| 150 | return check_manifest(repo_url, manifest, err) | ||
| 151 | end | ||
| 152 | |||
| 153 | |||
| 154 | |||
| 155 | |||
| 156 | |||
| 157 | function manif.get_provided_item(deploy_type, file_path) | ||
| 158 | local item_type = deploy_type == "bin" and "command" or "module" | ||
| 159 | local item_name = item_type == "command" and file_path or path.path_to_module(file_path) | ||
| 160 | return item_type, item_name | ||
| 161 | end | ||
| 162 | |||
| 163 | local function get_providers(item_type, item_name, repo) | ||
| 164 | local rocks_dir = path.rocks_dir(repo or cfg.root_dir) | ||
| 165 | local manifest = manif.load_manifest(rocks_dir) | ||
| 166 | return manifest and (manifest)[item_type .. "s"][item_name] | ||
| 167 | end | ||
| 168 | |||
| 169 | |||
| 170 | |||
| 171 | |||
| 172 | |||
| 173 | |||
| 174 | |||
| 175 | |||
| 176 | function manif.get_current_provider(item_type, item_name, repo) | ||
| 177 | local providers = get_providers(item_type, item_name, repo) | ||
| 178 | if providers then | ||
| 179 | return providers[1]:match("([^/]*)/([^/]*)") | ||
| 180 | end | ||
| 181 | end | ||
| 182 | |||
| 183 | function manif.get_next_provider(item_type, item_name, repo) | ||
| 184 | local providers = get_providers(item_type, item_name, repo) | ||
| 185 | if providers and providers[2] then | ||
| 186 | return providers[2]:match("([^/]*)/([^/]*)") | ||
| 187 | end | ||
| 188 | end | ||
| 189 | |||
| 190 | |||
| 191 | |||
| 192 | |||
| 193 | |||
| 194 | |||
| 195 | |||
| 196 | |||
| 197 | |||
| 198 | function manif.get_versions(dep, deps_mode) | ||
| 199 | |||
| 200 | local name = dep.name | ||
| 201 | local namespace = dep.namespace | ||
| 202 | |||
| 203 | local version_set = {} | ||
| 204 | path.map_trees(deps_mode, function(tree) | ||
| 205 | local manifest = manif.load_manifest(path.rocks_dir(tree)) | ||
| 206 | |||
| 207 | if manifest and manifest.repository[name] then | ||
| 208 | for version in pairs(manifest.repository[name]) do | ||
| 209 | if dep.namespace then | ||
| 210 | local ns_file = path.rock_namespace_file(name, version, tree) | ||
| 211 | local fd = io.open(ns_file, "r") | ||
| 212 | if fd then | ||
| 213 | local ns = fd:read("*a") | ||
| 214 | fd:close() | ||
| 215 | if ns == namespace then | ||
| 216 | version_set[version] = tree | ||
| 217 | end | ||
| 218 | end | ||
| 219 | else | ||
| 220 | version_set[version] = tree | ||
| 221 | end | ||
| 222 | end | ||
| 223 | end | ||
| 224 | end) | ||
| 225 | |||
| 226 | return util.keys(version_set), version_set | ||
| 227 | end | ||
| 228 | |||
| 229 | 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 @@ | |||
| 1 | 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 | ||
| 2 | local writer = {} | ||
| 3 | |||
| 4 | |||
| 5 | local cfg = require("luarocks.core.cfg") | ||
| 6 | local search = require("luarocks.search") | ||
| 7 | local repos = require("luarocks.repos") | ||
| 8 | local deps = require("luarocks.deps") | ||
| 9 | local vers = require("luarocks.core.vers") | ||
| 10 | local fs = require("luarocks.fs") | ||
| 11 | local util = require("luarocks.util") | ||
| 12 | local dir = require("luarocks.dir") | ||
| 13 | local fetch = require("luarocks.fetch") | ||
| 14 | local path = require("luarocks.path") | ||
| 15 | local persist = require("luarocks.persist") | ||
| 16 | local manif = require("luarocks.manif") | ||
| 17 | local queries = require("luarocks.queries") | ||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | local function store_package_items(storage, name, version, items) | ||
| 38 | assert(not name:match("/")) | ||
| 39 | |||
| 40 | local package_identifier = name .. "/" .. version | ||
| 41 | |||
| 42 | for item_name, _ in pairs(items) do | ||
| 43 | if not storage[item_name] then | ||
| 44 | storage[item_name] = {} | ||
| 45 | end | ||
| 46 | |||
| 47 | table.insert(storage[item_name], package_identifier) | ||
| 48 | end | ||
| 49 | end | ||
| 50 | |||
| 51 | |||
| 52 | |||
| 53 | |||
| 54 | |||
| 55 | |||
| 56 | |||
| 57 | |||
| 58 | local function remove_package_items(storage, name, version, items) | ||
| 59 | assert(not name:match("/")) | ||
| 60 | |||
| 61 | local package_identifier = name .. "/" .. version | ||
| 62 | |||
| 63 | for item_name, _path in pairs(items) do | ||
| 64 | local key = item_name | ||
| 65 | local all_identifiers = storage[key] | ||
| 66 | if not all_identifiers then | ||
| 67 | key = key .. ".init" | ||
| 68 | all_identifiers = storage[key] | ||
| 69 | end | ||
| 70 | |||
| 71 | if all_identifiers then | ||
| 72 | for i, identifier in ipairs(all_identifiers) do | ||
| 73 | if identifier == package_identifier then | ||
| 74 | table.remove(all_identifiers, i) | ||
| 75 | break | ||
| 76 | end | ||
| 77 | end | ||
| 78 | |||
| 79 | if #all_identifiers == 0 then | ||
| 80 | storage[key] = nil | ||
| 81 | end | ||
| 82 | else | ||
| 83 | util.warning("Cannot find entry for " .. item_name .. " in manifest -- corrupted manifest?") | ||
| 84 | end | ||
| 85 | end | ||
| 86 | end | ||
| 87 | |||
| 88 | |||
| 89 | |||
| 90 | |||
| 91 | |||
| 92 | |||
| 93 | |||
| 94 | |||
| 95 | |||
| 96 | local function update_dependencies(manifest, deps_mode) | ||
| 97 | |||
| 98 | if not manifest.dependencies then manifest.dependencies = {} end | ||
| 99 | local mdeps = manifest.dependencies | ||
| 100 | |||
| 101 | for pkg, versions in pairs(manifest.repository) do | ||
| 102 | for version, repositories in pairs(versions) do | ||
| 103 | for _, repo in ipairs(repositories) do | ||
| 104 | if repo.arch == "installed" then | ||
| 105 | local rd = {} | ||
| 106 | repo.dependencies = rd | ||
| 107 | deps.scan_deps(rd, mdeps, pkg, version, deps_mode) | ||
| 108 | rd[pkg] = nil | ||
| 109 | end | ||
| 110 | end | ||
| 111 | end | ||
| 112 | end | ||
| 113 | end | ||
| 114 | |||
| 115 | |||
| 116 | |||
| 117 | |||
| 118 | |||
| 119 | |||
| 120 | |||
| 121 | |||
| 122 | |||
| 123 | |||
| 124 | local function sort_pkgs(a, b) | ||
| 125 | local na, va = a:match("(.*)/(.*)$") | ||
| 126 | local nb, vb = b:match("(.*)/(.*)$") | ||
| 127 | |||
| 128 | return (na == nb) and vers.compare_versions(va, vb) or na < nb | ||
| 129 | end | ||
| 130 | |||
| 131 | |||
| 132 | |||
| 133 | |||
| 134 | local function sort_package_matching_table(tbl) | ||
| 135 | |||
| 136 | if next(tbl) then | ||
| 137 | for item, pkgs in pairs(tbl) do | ||
| 138 | if #pkgs > 1 then | ||
| 139 | table.sort(pkgs, sort_pkgs) | ||
| 140 | |||
| 141 | local prev = nil | ||
| 142 | local i = 1 | ||
| 143 | while pkgs[i] do | ||
| 144 | local curr = pkgs[i] | ||
| 145 | if curr == prev then | ||
| 146 | table.remove(pkgs, i) | ||
| 147 | else | ||
| 148 | prev = curr | ||
| 149 | i = i + 1 | ||
| 150 | end | ||
| 151 | end | ||
| 152 | end | ||
| 153 | end | ||
| 154 | end | ||
| 155 | end | ||
| 156 | |||
| 157 | |||
| 158 | |||
| 159 | |||
| 160 | |||
| 161 | |||
| 162 | |||
| 163 | local function filter_by_lua_version(manifest, lua_version_str, repodir, cache) | ||
| 164 | |||
| 165 | cache = cache or {} | ||
| 166 | local lua_version = vers.parse_version(lua_version_str) | ||
| 167 | for pkg, versions in pairs(manifest.repository) do | ||
| 168 | local to_remove = {} | ||
| 169 | for version, repositories in pairs(versions) do | ||
| 170 | for _, repo in ipairs(repositories) do | ||
| 171 | if repo.arch == "rockspec" then | ||
| 172 | local pathname = dir.path(repodir, pkg .. "-" .. version .. ".rockspec") | ||
| 173 | local rockspec = cache[pathname] | ||
| 174 | local err | ||
| 175 | if not rockspec then | ||
| 176 | rockspec, err = fetch.load_local_rockspec(pathname, true) | ||
| 177 | end | ||
| 178 | if rockspec then | ||
| 179 | cache[pathname] = rockspec | ||
| 180 | for _, dep in ipairs(rockspec.dependencies.queries) do | ||
| 181 | if dep.name == "lua" then | ||
| 182 | if not vers.match_constraints(lua_version, dep.constraints) then | ||
| 183 | table.insert(to_remove, version) | ||
| 184 | end | ||
| 185 | break | ||
| 186 | end | ||
| 187 | end | ||
| 188 | else | ||
| 189 | util.printerr("Error loading rockspec for " .. pkg .. " " .. version .. ": " .. err) | ||
| 190 | end | ||
| 191 | end | ||
| 192 | end | ||
| 193 | end | ||
| 194 | if next(to_remove) then | ||
| 195 | for _, incompat in ipairs(to_remove) do | ||
| 196 | versions[incompat] = nil | ||
| 197 | end | ||
| 198 | if not next(versions) then | ||
| 199 | manifest.repository[pkg] = nil | ||
| 200 | end | ||
| 201 | end | ||
| 202 | end | ||
| 203 | end | ||
| 204 | |||
| 205 | |||
| 206 | |||
| 207 | |||
| 208 | |||
| 209 | |||
| 210 | local function store_results(results, manifest) | ||
| 211 | |||
| 212 | for name, versions in pairs(results) do | ||
| 213 | local pkgtable = manifest.repository[name] or {} | ||
| 214 | for version, entries in pairs(versions) do | ||
| 215 | local versiontable = {} | ||
| 216 | for _, entry in ipairs(entries) do | ||
| 217 | local entrytable = {} | ||
| 218 | entrytable.arch = entry.arch | ||
| 219 | if entry.arch == "installed" then | ||
| 220 | local rock_manifest, err = manif.load_rock_manifest(name, version) | ||
| 221 | if not rock_manifest then return nil, err end | ||
| 222 | |||
| 223 | entrytable.modules = repos.package_modules(name, version) | ||
| 224 | store_package_items(manifest.modules, name, version, entrytable.modules) | ||
| 225 | entrytable.commands = repos.package_commands(name, version) | ||
| 226 | store_package_items(manifest.commands, name, version, entrytable.commands) | ||
| 227 | end | ||
| 228 | table.insert(versiontable, entrytable) | ||
| 229 | end | ||
| 230 | pkgtable[version] = versiontable | ||
| 231 | end | ||
| 232 | manifest.repository[name] = pkgtable | ||
| 233 | end | ||
| 234 | sort_package_matching_table(manifest.modules) | ||
| 235 | sort_package_matching_table(manifest.commands) | ||
| 236 | return true | ||
| 237 | end | ||
| 238 | |||
| 239 | |||
| 240 | |||
| 241 | |||
| 242 | |||
| 243 | |||
| 244 | |||
| 245 | local function save_table(where, name, tbl) | ||
| 246 | assert(not name:match("/")) | ||
| 247 | |||
| 248 | local filename = dir.path(where, name) | ||
| 249 | local ok, err = persist.save_from_table(filename .. ".tmp", tbl) | ||
| 250 | if ok then | ||
| 251 | ok, err = fs.replace_file(filename, filename .. ".tmp") | ||
| 252 | end | ||
| 253 | return ok, err | ||
| 254 | end | ||
| 255 | |||
| 256 | function writer.make_rock_manifest(name, version) | ||
| 257 | local install_dir = path.install_dir(name, version) | ||
| 258 | local tree = {} | ||
| 259 | for _, file in ipairs(fs.find(install_dir)) do | ||
| 260 | local full_path = dir.path(install_dir, file) | ||
| 261 | local walk = tree | ||
| 262 | local last | ||
| 263 | local last_name | ||
| 264 | local next | ||
| 265 | for filename in file:gmatch("[^\\/]+") do | ||
| 266 | next = walk[filename] | ||
| 267 | if not next then | ||
| 268 | next = {} | ||
| 269 | walk[filename] = next | ||
| 270 | end | ||
| 271 | last = walk | ||
| 272 | last_name = filename | ||
| 273 | assert(type(next) == "table") | ||
| 274 | walk = next | ||
| 275 | end | ||
| 276 | if fs.is_file(full_path) then | ||
| 277 | |||
| 278 | local sum, err = fs.get_md5(full_path) | ||
| 279 | if not sum then | ||
| 280 | return nil, "Failed producing checksum: " .. tostring(err) | ||
| 281 | end | ||
| 282 | last[last_name] = sum | ||
| 283 | end | ||
| 284 | end | ||
| 285 | local rock_manifest = { rock_manifest = tree } | ||
| 286 | manif.rock_manifest_cache[name .. "/" .. version] = rock_manifest | ||
| 287 | save_table(install_dir, "rock_manifest", rock_manifest) | ||
| 288 | return true | ||
| 289 | end | ||
| 290 | |||
| 291 | |||
| 292 | |||
| 293 | |||
| 294 | |||
| 295 | |||
| 296 | |||
| 297 | function writer.make_namespace_file(name, version, namespace) | ||
| 298 | assert(not name:match("/")) | ||
| 299 | if not namespace then | ||
| 300 | return true | ||
| 301 | end | ||
| 302 | local fd, err = io.open(path.rock_namespace_file(name, version), "w") | ||
| 303 | if not fd then | ||
| 304 | return nil, err | ||
| 305 | end | ||
| 306 | local ok, err = fd:write(namespace) | ||
| 307 | if not ok then | ||
| 308 | return nil, err | ||
| 309 | end | ||
| 310 | fd:close() | ||
| 311 | return true | ||
| 312 | end | ||
| 313 | |||
| 314 | |||
| 315 | |||
| 316 | |||
| 317 | |||
| 318 | |||
| 319 | |||
| 320 | |||
| 321 | |||
| 322 | |||
| 323 | |||
| 324 | function writer.make_manifest(repo, deps_mode, remote) | ||
| 325 | |||
| 326 | if deps_mode == "none" then deps_mode = cfg.deps_mode end | ||
| 327 | |||
| 328 | if not fs.is_dir(repo) then | ||
| 329 | return nil, "Cannot access repository at " .. repo | ||
| 330 | end | ||
| 331 | |||
| 332 | local query = queries.all("any") | ||
| 333 | local results = search.disk_search(repo, query) | ||
| 334 | local manifest = { repository = {}, modules = {}, commands = {} } | ||
| 335 | |||
| 336 | manif.cache_manifest(repo, nil, manifest) | ||
| 337 | |||
| 338 | local ok, err = store_results(results, manifest) | ||
| 339 | if not ok then return nil, err end | ||
| 340 | |||
| 341 | if remote then | ||
| 342 | local cache = {} | ||
| 343 | for luaver in util.lua_versions() do | ||
| 344 | local vmanifest = { repository = {}, modules = {}, commands = {} } | ||
| 345 | ok, err = store_results(results, vmanifest) | ||
| 346 | filter_by_lua_version(vmanifest, luaver, repo, cache) | ||
| 347 | if not cfg.no_manifest then | ||
| 348 | save_table(repo, "manifest-" .. luaver, vmanifest) | ||
| 349 | end | ||
| 350 | end | ||
| 351 | else | ||
| 352 | update_dependencies(manifest, deps_mode) | ||
| 353 | end | ||
| 354 | |||
| 355 | if cfg.no_manifest then | ||
| 356 | |||
| 357 | return true | ||
| 358 | end | ||
| 359 | return save_table(repo, "manifest", manifest) | ||
| 360 | end | ||
| 361 | |||
| 362 | |||
| 363 | |||
| 364 | |||
| 365 | |||
| 366 | |||
| 367 | |||
| 368 | |||
| 369 | |||
| 370 | |||
| 371 | |||
| 372 | |||
| 373 | function writer.add_to_manifest(name, version, repo, deps_mode) | ||
| 374 | assert(not name:match("/")) | ||
| 375 | local rocks_dir = path.rocks_dir(repo or cfg.root_dir) | ||
| 376 | |||
| 377 | if deps_mode == "none" then deps_mode = cfg.deps_mode end | ||
| 378 | |||
| 379 | local manifest, err = manif.load_manifest(rocks_dir) | ||
| 380 | if not manifest then | ||
| 381 | util.printerr("No existing manifest. Attempting to rebuild...") | ||
| 382 | |||
| 383 | |||
| 384 | |||
| 385 | return writer.make_manifest(rocks_dir, deps_mode) | ||
| 386 | end | ||
| 387 | |||
| 388 | local results = { [name] = { [version] = { { arch = "installed", repo = rocks_dir } } } } | ||
| 389 | |||
| 390 | local ok | ||
| 391 | ok, err = store_results(results, manifest) | ||
| 392 | if not ok then return nil, err end | ||
| 393 | |||
| 394 | update_dependencies(manifest, deps_mode) | ||
| 395 | |||
| 396 | if cfg.no_manifest then | ||
| 397 | return true | ||
| 398 | end | ||
| 399 | return save_table(rocks_dir, "manifest", manifest) | ||
| 400 | end | ||
| 401 | |||
| 402 | |||
| 403 | |||
| 404 | |||
| 405 | |||
| 406 | |||
| 407 | |||
| 408 | |||
| 409 | |||
| 410 | |||
| 411 | |||
| 412 | |||
| 413 | function writer.remove_from_manifest(name, version, repo, deps_mode) | ||
| 414 | assert(not name:match("/")) | ||
| 415 | local rocks_dir = path.rocks_dir(repo or cfg.root_dir) | ||
| 416 | |||
| 417 | if deps_mode == "none" then deps_mode = cfg.deps_mode end | ||
| 418 | |||
| 419 | local manifest, err = manif.load_manifest(rocks_dir) | ||
| 420 | if not manifest then | ||
| 421 | util.printerr("No existing manifest. Attempting to rebuild...") | ||
| 422 | |||
| 423 | |||
| 424 | return writer.make_manifest(rocks_dir, deps_mode) | ||
| 425 | end | ||
| 426 | |||
| 427 | local package_entry = manifest.repository[name] | ||
| 428 | if package_entry == nil or package_entry[version] == nil then | ||
| 429 | |||
| 430 | return true | ||
| 431 | end | ||
| 432 | |||
| 433 | local version_entry = package_entry[version][1] | ||
| 434 | if not version_entry then | ||
| 435 | |||
| 436 | return writer.make_manifest(rocks_dir, deps_mode) | ||
| 437 | end | ||
| 438 | |||
| 439 | remove_package_items(manifest.modules, name, version, version_entry.modules) | ||
| 440 | remove_package_items(manifest.commands, name, version, version_entry.commands) | ||
| 441 | |||
| 442 | package_entry[version] = nil | ||
| 443 | manifest.dependencies[name][version] = nil | ||
| 444 | |||
| 445 | if not next(package_entry) then | ||
| 446 | |||
| 447 | manifest.repository[name] = nil | ||
| 448 | manifest.dependencies[name] = nil | ||
| 449 | end | ||
| 450 | |||
| 451 | update_dependencies(manifest, deps_mode) | ||
| 452 | |||
| 453 | if cfg.no_manifest then | ||
| 454 | return true | ||
| 455 | end | ||
| 456 | return save_table(rocks_dir, "manifest", manifest) | ||
| 457 | end | ||
| 458 | |||
| 459 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local pack = {} | ||
| 4 | |||
| 5 | |||
| 6 | local queries = require("luarocks.queries") | ||
| 7 | local path = require("luarocks.path") | ||
| 8 | local repos = require("luarocks.repos") | ||
| 9 | local fetch = require("luarocks.fetch") | ||
| 10 | local fs = require("luarocks.fs") | ||
| 11 | local cfg = require("luarocks.core.cfg") | ||
| 12 | local util = require("luarocks.util") | ||
| 13 | local dir = require("luarocks.dir") | ||
| 14 | local manif = require("luarocks.manif") | ||
| 15 | local search = require("luarocks.search") | ||
| 16 | local signing = require("luarocks.signing") | ||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | function pack.pack_source_rock(rockspec_file) | ||
| 32 | |||
| 33 | local rockspec, errload = fetch.load_rockspec(rockspec_file) | ||
| 34 | if errload then | ||
| 35 | return nil, "Error loading rockspec: " .. errload | ||
| 36 | end | ||
| 37 | rockspec_file = rockspec.local_abs_filename | ||
| 38 | |||
| 39 | local name_version = rockspec.name .. "-" .. rockspec.version | ||
| 40 | local rock_file = fs.absolute_name(name_version .. ".src.rock") | ||
| 41 | |||
| 42 | local temp_dir, err = fs.make_temp_dir("pack-" .. name_version) | ||
| 43 | if not temp_dir then | ||
| 44 | return nil, "Failed creating temporary directory: " .. err | ||
| 45 | end | ||
| 46 | util.schedule_function(fs.delete, temp_dir) | ||
| 47 | |||
| 48 | local source_file, source_dir = fetch.fetch_sources(rockspec, true, temp_dir) | ||
| 49 | if not source_file then | ||
| 50 | return nil, source_dir | ||
| 51 | end | ||
| 52 | local ok, errchange = fs.change_dir(source_dir) | ||
| 53 | if not ok then return nil, errchange end | ||
| 54 | |||
| 55 | fs.delete(rock_file) | ||
| 56 | fs.copy(rockspec_file, source_dir, "read") | ||
| 57 | ok, err = fs.zip(rock_file, dir.base_name(rockspec_file), dir.base_name(source_file)) | ||
| 58 | if not ok then | ||
| 59 | return nil, "Failed packing " .. rock_file .. " - " .. err | ||
| 60 | end | ||
| 61 | fs.pop_dir() | ||
| 62 | |||
| 63 | return rock_file | ||
| 64 | end | ||
| 65 | |||
| 66 | local function copy_back_files(name, version, file_tree, deploy_dir, pack_dir, perms) | ||
| 67 | local ok, err = fs.make_dir(pack_dir) | ||
| 68 | if not ok then return nil, err end | ||
| 69 | for file, sub in pairs(file_tree) do | ||
| 70 | local source = dir.path(deploy_dir, file) | ||
| 71 | local target = dir.path(pack_dir, file) | ||
| 72 | if type(sub) == "table" then | ||
| 73 | ok, err = copy_back_files(name, version, sub, source, target) | ||
| 74 | if not ok then return nil, err end | ||
| 75 | else | ||
| 76 | local versioned = path.versioned_name(source, deploy_dir, name, version) | ||
| 77 | if fs.exists(versioned) then | ||
| 78 | fs.copy(versioned, target, perms) | ||
| 79 | else | ||
| 80 | fs.copy(source, target, perms) | ||
| 81 | end | ||
| 82 | end | ||
| 83 | end | ||
| 84 | return true | ||
| 85 | end | ||
| 86 | |||
| 87 | |||
| 88 | |||
| 89 | |||
| 90 | |||
| 91 | |||
| 92 | function pack.pack_installed_rock(query, tree) | ||
| 93 | |||
| 94 | local name, version, repo, repo_url = search.pick_installed_rock(query, tree) | ||
| 95 | if not name then | ||
| 96 | return nil, version | ||
| 97 | end | ||
| 98 | |||
| 99 | local root = path.root_from_rocks_dir(repo_url) | ||
| 100 | local prefix = path.install_dir(name, version, root) | ||
| 101 | if not fs.exists(prefix) then | ||
| 102 | return nil, "'" .. name .. " " .. version .. "' does not seem to be an installed rock." | ||
| 103 | end | ||
| 104 | |||
| 105 | local rock_manifest, err = manif.load_rock_manifest(name, version, root) | ||
| 106 | if not rock_manifest then return nil, err end | ||
| 107 | |||
| 108 | local name_version = name .. "-" .. version | ||
| 109 | local rock_file = fs.absolute_name(name_version .. "." .. cfg.arch .. ".rock") | ||
| 110 | |||
| 111 | local temp_dir = fs.make_temp_dir("pack") | ||
| 112 | fs.copy_contents(prefix, temp_dir) | ||
| 113 | |||
| 114 | local is_binary = false | ||
| 115 | if rock_manifest.lib then | ||
| 116 | local ok, err = copy_back_files(name, version, (rock_manifest.lib), path.deploy_lib_dir(repo), dir.path(temp_dir, "lib"), "exec") | ||
| 117 | if not ok then return nil, "Failed copying back files: " .. err end | ||
| 118 | is_binary = true | ||
| 119 | end | ||
| 120 | if rock_manifest.lua then | ||
| 121 | local ok, err = copy_back_files(name, version, (rock_manifest.lua), path.deploy_lua_dir(repo), dir.path(temp_dir, "lua"), "read") | ||
| 122 | if not ok then return nil, "Failed copying back files: " .. err end | ||
| 123 | end | ||
| 124 | |||
| 125 | local ok, err = fs.change_dir(temp_dir) | ||
| 126 | if not ok then return nil, err end | ||
| 127 | if not is_binary and not repos.has_binaries(name, version) then | ||
| 128 | rock_file = rock_file:gsub("%." .. cfg.arch:gsub("%-", "%%-") .. "%.", ".all.") | ||
| 129 | end | ||
| 130 | fs.delete(rock_file) | ||
| 131 | ok, err = fs.zip(rock_file, _tl_table_unpack(fs.list_dir())) | ||
| 132 | if not ok then | ||
| 133 | return nil, "Failed packing " .. rock_file .. " - " .. err | ||
| 134 | end | ||
| 135 | fs.pop_dir() | ||
| 136 | fs.delete(temp_dir) | ||
| 137 | return rock_file | ||
| 138 | end | ||
| 139 | |||
| 140 | function pack.report_and_sign_local_file(file, err, sign) | ||
| 141 | if err then | ||
| 142 | return nil, err | ||
| 143 | end | ||
| 144 | local sigfile | ||
| 145 | if sign then | ||
| 146 | sigfile, err = signing.sign_file(file) | ||
| 147 | util.printout() | ||
| 148 | end | ||
| 149 | util.printout("Packed: " .. file) | ||
| 150 | if sigfile then | ||
| 151 | util.printout("Signature stored in: " .. sigfile) | ||
| 152 | end | ||
| 153 | if err then | ||
| 154 | return nil, err | ||
| 155 | end | ||
| 156 | return true | ||
| 157 | end | ||
| 158 | |||
| 159 | function pack.pack_binary_rock(name, namespace, version, sign, cmd) | ||
| 160 | |||
| 161 | |||
| 162 | |||
| 163 | |||
| 164 | |||
| 165 | |||
| 166 | |||
| 167 | |||
| 168 | local temp_dir, err = fs.make_temp_dir("luarocks-build-pack-" .. dir.base_name(name)) | ||
| 169 | if not temp_dir then | ||
| 170 | return nil, "Failed creating temporary directory: " .. err | ||
| 171 | end | ||
| 172 | util.schedule_function(fs.delete, temp_dir) | ||
| 173 | |||
| 174 | path.use_tree(temp_dir) | ||
| 175 | local ok, err = cmd() | ||
| 176 | if not ok then | ||
| 177 | return nil, err | ||
| 178 | end | ||
| 179 | local rname, rversion = path.parse_name(name) | ||
| 180 | if not rname then | ||
| 181 | rname, rversion = name, version | ||
| 182 | end | ||
| 183 | local query = queries.new(rname, namespace, rversion) | ||
| 184 | local file, err = pack.pack_installed_rock(query, temp_dir) | ||
| 185 | return pack.report_and_sign_local_file(file, err, sign) | ||
| 186 | end | ||
| 187 | |||
| 188 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | local cfg = require("luarocks.core.cfg") | ||
| 7 | local core = require("luarocks.core.path") | ||
| 8 | local dir = require("luarocks.dir") | ||
| 9 | local util = require("luarocks.core.util") | ||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | local path = {} | ||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | path.rocks_dir = core.rocks_dir | ||
| 24 | path.versioned_name = core.versioned_name | ||
| 25 | path.path_to_module = core.path_to_module | ||
| 26 | path.deploy_lua_dir = core.deploy_lua_dir | ||
| 27 | path.deploy_lib_dir = core.deploy_lib_dir | ||
| 28 | path.map_trees = core.map_trees | ||
| 29 | path.rocks_tree_to_string = core.rocks_tree_to_string | ||
| 30 | |||
| 31 | function path.root_dir(tree) | ||
| 32 | if type(tree) == "string" then | ||
| 33 | return tree | ||
| 34 | else | ||
| 35 | return tree.root | ||
| 36 | end | ||
| 37 | end | ||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | function path.rockspec_name_from_rock(rock_name) | ||
| 43 | local base_name = dir.base_name(rock_name) | ||
| 44 | return base_name:match("(.*)%.[^.]*.rock") .. ".rockspec" | ||
| 45 | end | ||
| 46 | |||
| 47 | function path.root_from_rocks_dir(rocks_dir) | ||
| 48 | return rocks_dir:match("(.*)" .. util.matchquote(cfg.rocks_subdir) .. ".*$") | ||
| 49 | end | ||
| 50 | |||
| 51 | function path.deploy_bin_dir(tree) | ||
| 52 | return dir.path(path.root_dir(tree), "bin") | ||
| 53 | end | ||
| 54 | |||
| 55 | function path.manifest_file(tree) | ||
| 56 | return dir.path(path.rocks_dir(tree), "manifest") | ||
| 57 | end | ||
| 58 | |||
| 59 | |||
| 60 | |||
| 61 | |||
| 62 | |||
| 63 | |||
| 64 | function path.versions_dir(name, tree) | ||
| 65 | assert(not name:match("/")) | ||
| 66 | return dir.path(path.rocks_dir(tree), name) | ||
| 67 | end | ||
| 68 | |||
| 69 | |||
| 70 | |||
| 71 | |||
| 72 | |||
| 73 | |||
| 74 | |||
| 75 | function path.install_dir(name, version, tree) | ||
| 76 | assert(not name:match("/")) | ||
| 77 | return dir.path(path.rocks_dir(tree), name, version) | ||
| 78 | end | ||
| 79 | |||
| 80 | |||
| 81 | |||
| 82 | |||
| 83 | |||
| 84 | |||
| 85 | |||
| 86 | function path.rockspec_file(name, version, tree) | ||
| 87 | assert(not name:match("/")) | ||
| 88 | return dir.path(path.rocks_dir(tree), name, version, name .. "-" .. version .. ".rockspec") | ||
| 89 | end | ||
| 90 | |||
| 91 | |||
| 92 | |||
| 93 | |||
| 94 | |||
| 95 | |||
| 96 | |||
| 97 | function path.rock_manifest_file(name, version, tree) | ||
| 98 | assert(not name:match("/")) | ||
| 99 | return dir.path(path.rocks_dir(tree), name, version, "rock_manifest") | ||
| 100 | end | ||
| 101 | |||
| 102 | |||
| 103 | |||
| 104 | |||
| 105 | |||
| 106 | |||
| 107 | |||
| 108 | function path.rock_namespace_file(name, version, tree) | ||
| 109 | assert(not name:match("/")) | ||
| 110 | return dir.path(path.rocks_dir(tree), name, version, "rock_namespace") | ||
| 111 | end | ||
| 112 | |||
| 113 | |||
| 114 | |||
| 115 | |||
| 116 | |||
| 117 | |||
| 118 | |||
| 119 | function path.lib_dir(name, version, tree) | ||
| 120 | assert(not name:match("/")) | ||
| 121 | return dir.path(path.rocks_dir(tree), name, version, "lib") | ||
| 122 | end | ||
| 123 | |||
| 124 | |||
| 125 | |||
| 126 | |||
| 127 | |||
| 128 | |||
| 129 | |||
| 130 | function path.lua_dir(name, version, tree) | ||
| 131 | assert(not name:match("/")) | ||
| 132 | return dir.path(path.rocks_dir(tree), name, version, "lua") | ||
| 133 | end | ||
| 134 | |||
| 135 | |||
| 136 | |||
| 137 | |||
| 138 | |||
| 139 | |||
| 140 | |||
| 141 | function path.doc_dir(name, version, tree) | ||
| 142 | assert(not name:match("/")) | ||
| 143 | return dir.path(path.rocks_dir(tree), name, version, "doc") | ||
| 144 | end | ||
| 145 | |||
| 146 | |||
| 147 | |||
| 148 | |||
| 149 | |||
| 150 | |||
| 151 | |||
| 152 | function path.conf_dir(name, version, tree) | ||
| 153 | assert(not name:match("/")) | ||
| 154 | return dir.path(path.rocks_dir(tree), name, version, "conf") | ||
| 155 | end | ||
| 156 | |||
| 157 | |||
| 158 | |||
| 159 | |||
| 160 | |||
| 161 | |||
| 162 | |||
| 163 | |||
| 164 | function path.bin_dir(name, version, tree) | ||
| 165 | assert(not name:match("/")) | ||
| 166 | return dir.path(path.rocks_dir(tree), name, version, "bin") | ||
| 167 | end | ||
| 168 | |||
| 169 | |||
| 170 | |||
| 171 | |||
| 172 | |||
| 173 | |||
| 174 | function path.parse_name(file_name) | ||
| 175 | if file_name:match("%.rock$") then | ||
| 176 | return dir.base_name(file_name):match("(.*)-([^-]+-%d+)%.([^.]+)%.rock$") | ||
| 177 | else | ||
| 178 | return dir.base_name(file_name):match("(.*)-([^-]+-%d+)%.(rockspec)") | ||
| 179 | end | ||
| 180 | end | ||
| 181 | |||
| 182 | |||
| 183 | |||
| 184 | |||
| 185 | |||
| 186 | |||
| 187 | |||
| 188 | function path.make_url(pathname, name, version, arch) | ||
| 189 | assert(not name:match("/")) | ||
| 190 | local filename = name .. "-" .. version | ||
| 191 | if arch == "installed" then | ||
| 192 | filename = dir.path(name, version, filename .. ".rockspec") | ||
| 193 | elseif arch == "rockspec" then | ||
| 194 | filename = filename .. ".rockspec" | ||
| 195 | else | ||
| 196 | filename = filename .. "." .. arch .. ".rock" | ||
| 197 | end | ||
| 198 | return dir.path(pathname, filename) | ||
| 199 | end | ||
| 200 | |||
| 201 | |||
| 202 | |||
| 203 | |||
| 204 | |||
| 205 | function path.module_to_path(mod) | ||
| 206 | return (mod:gsub("[^.]*$", ""):gsub("%.", "/")) | ||
| 207 | end | ||
| 208 | |||
| 209 | function path.use_tree(tree) | ||
| 210 | cfg.root_dir = tree | ||
| 211 | cfg.rocks_dir = path.rocks_dir(tree) | ||
| 212 | cfg.deploy_bin_dir = path.deploy_bin_dir(tree) | ||
| 213 | cfg.deploy_lua_dir = path.deploy_lua_dir(tree) | ||
| 214 | cfg.deploy_lib_dir = path.deploy_lib_dir(tree) | ||
| 215 | end | ||
| 216 | |||
| 217 | function path.add_to_package_paths(tree) | ||
| 218 | package.path = dir.path(path.deploy_lua_dir(tree), "?.lua") .. ";" .. | ||
| 219 | dir.path(path.deploy_lua_dir(tree), "?/init.lua") .. ";" .. | ||
| 220 | package.path | ||
| 221 | package.cpath = dir.path(path.deploy_lib_dir(tree), "?." .. cfg.lib_extension) .. ";" .. | ||
| 222 | package.cpath | ||
| 223 | end | ||
| 224 | |||
| 225 | |||
| 226 | |||
| 227 | |||
| 228 | |||
| 229 | |||
| 230 | function path.read_namespace(name, version, tree) | ||
| 231 | assert(not name:match("/")) | ||
| 232 | |||
| 233 | local namespace | ||
| 234 | local fd = io.open(path.rock_namespace_file(name, version, tree), "r") | ||
| 235 | if fd then | ||
| 236 | namespace = fd:read("*a") | ||
| 237 | fd:close() | ||
| 238 | end | ||
| 239 | return namespace | ||
| 240 | end | ||
| 241 | |||
| 242 | function path.package_paths(deps_mode) | ||
| 243 | local lpaths = {} | ||
| 244 | local lcpaths = {} | ||
| 245 | path.map_trees(deps_mode, function(tree) | ||
| 246 | local root = path.root_dir(tree) | ||
| 247 | table.insert(lpaths, dir.path(root, cfg.lua_modules_path, "?.lua")) | ||
| 248 | table.insert(lpaths, dir.path(root, cfg.lua_modules_path, "?/init.lua")) | ||
| 249 | table.insert(lcpaths, dir.path(root, cfg.lib_modules_path, "?." .. cfg.lib_extension)) | ||
| 250 | end) | ||
| 251 | return table.concat(lpaths, ";"), table.concat(lcpaths, ";") | ||
| 252 | end | ||
| 253 | |||
| 254 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local persist = {} | ||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | local core = require("luarocks.core.persist") | ||
| 15 | local util = require("luarocks.util") | ||
| 16 | local dir = require("luarocks.dir") | ||
| 17 | local fs = require("luarocks.fs") | ||
| 18 | local cfg = require("luarocks.core.cfg") | ||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | persist.run_file = core.run_file | ||
| 29 | persist.load_into_table = core.load_into_table | ||
| 30 | |||
| 31 | local write_table | ||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | function persist.write_value(out, v, level, sub_order) | ||
| 42 | if type(v) == "table" then | ||
| 43 | level = level or 0 | ||
| 44 | write_table(out, v, level + 1, sub_order) | ||
| 45 | elseif type(v) == "string" then | ||
| 46 | if v:match("[\r\n]") then | ||
| 47 | local open, close = "[[", "]]" | ||
| 48 | local equals = 0 | ||
| 49 | local v_with_bracket = v .. "]" | ||
| 50 | while v_with_bracket:find(close, 1, true) do | ||
| 51 | equals = equals + 1 | ||
| 52 | local eqs = ("="):rep(equals) | ||
| 53 | open, close = "[" .. eqs .. "[", "]" .. eqs .. "]" | ||
| 54 | end | ||
| 55 | out:write(open .. "\n" .. v .. close) | ||
| 56 | else | ||
| 57 | out:write(("%q"):format(v)) | ||
| 58 | end | ||
| 59 | else | ||
| 60 | out:write(tostring(v)) | ||
| 61 | end | ||
| 62 | end | ||
| 63 | |||
| 64 | local is_valid_plain_key | ||
| 65 | do | ||
| 66 | local keywords = { | ||
| 67 | ["and"] = true, | ||
| 68 | ["break"] = true, | ||
| 69 | ["do"] = true, | ||
| 70 | ["else"] = true, | ||
| 71 | ["elseif"] = true, | ||
| 72 | ["end"] = true, | ||
| 73 | ["false"] = true, | ||
| 74 | ["for"] = true, | ||
| 75 | ["function"] = true, | ||
| 76 | ["goto"] = true, | ||
| 77 | ["if"] = true, | ||
| 78 | ["in"] = true, | ||
| 79 | ["local"] = true, | ||
| 80 | ["nil"] = true, | ||
| 81 | ["not"] = true, | ||
| 82 | ["or"] = true, | ||
| 83 | ["repeat"] = true, | ||
| 84 | ["return"] = true, | ||
| 85 | ["then"] = true, | ||
| 86 | ["true"] = true, | ||
| 87 | ["until"] = true, | ||
| 88 | ["while"] = true, | ||
| 89 | } | ||
| 90 | function is_valid_plain_key(k) | ||
| 91 | return k:match("^[a-zA-Z_][a-zA-Z0-9_]*$") and | ||
| 92 | not keywords[k] | ||
| 93 | end | ||
| 94 | end | ||
| 95 | |||
| 96 | local function write_table_key_assignment(out, k, level) | ||
| 97 | if type(k) == "string" and is_valid_plain_key(k) then | ||
| 98 | out:write(k) | ||
| 99 | else | ||
| 100 | out:write("[") | ||
| 101 | persist.write_value(out, k, level) | ||
| 102 | out:write("]") | ||
| 103 | end | ||
| 104 | |||
| 105 | out:write(" = ") | ||
| 106 | end | ||
| 107 | |||
| 108 | |||
| 109 | |||
| 110 | |||
| 111 | |||
| 112 | |||
| 113 | |||
| 114 | |||
| 115 | write_table = function(out, tbl, level, sort_by) | ||
| 116 | out:write("{") | ||
| 117 | local sep = "\n" | ||
| 118 | local indentation = " " | ||
| 119 | local indent = true | ||
| 120 | local i = 1 | ||
| 121 | for k, v, sub_order in util.sortedpairs(tbl, sort_by) do | ||
| 122 | out:write(sep) | ||
| 123 | if indent then | ||
| 124 | for _ = 1, level do out:write(indentation) end | ||
| 125 | end | ||
| 126 | |||
| 127 | if type(k) == "number" then | ||
| 128 | i = i + 1 | ||
| 129 | else | ||
| 130 | write_table_key_assignment(out, k, level) | ||
| 131 | end | ||
| 132 | |||
| 133 | persist.write_value(out, v, level, sub_order) | ||
| 134 | if type(v) == "number" then | ||
| 135 | sep = ", " | ||
| 136 | indent = false | ||
| 137 | else | ||
| 138 | sep = ",\n" | ||
| 139 | indent = true | ||
| 140 | end | ||
| 141 | end | ||
| 142 | if sep ~= "\n" then | ||
| 143 | out:write("\n") | ||
| 144 | for _ = 1, level - 1 do out:write(indentation) end | ||
| 145 | end | ||
| 146 | out:write("}") | ||
| 147 | end | ||
| 148 | |||
| 149 | |||
| 150 | |||
| 151 | |||
| 152 | |||
| 153 | |||
| 154 | local function write_table_as_assignments(out, tbl, sort_by) | ||
| 155 | for k, v, sub_order in util.sortedpairs(tbl, sort_by) do | ||
| 156 | if not (type(k) == "string" and is_valid_plain_key(k)) then | ||
| 157 | return nil, "cannot store '" .. tostring(k) .. "' as a plain key." | ||
| 158 | end | ||
| 159 | out:write(k .. " = ") | ||
| 160 | persist.write_value(out, v, 0, sub_order) | ||
| 161 | out:write("\n") | ||
| 162 | end | ||
| 163 | return true | ||
| 164 | end | ||
| 165 | |||
| 166 | |||
| 167 | |||
| 168 | |||
| 169 | local function write_table_as_table(out, tbl) | ||
| 170 | out:write("return {\n") | ||
| 171 | for k, v, sub_order in util.sortedpairs(tbl) do | ||
| 172 | out:write(" ") | ||
| 173 | write_table_key_assignment(out, k, 1) | ||
| 174 | persist.write_value(out, v, 1, sub_order) | ||
| 175 | out:write(",\n") | ||
| 176 | end | ||
| 177 | out:write("}\n") | ||
| 178 | end | ||
| 179 | |||
| 180 | |||
| 181 | |||
| 182 | |||
| 183 | |||
| 184 | |||
| 185 | |||
| 186 | |||
| 187 | function persist.save_from_table_to_string(tbl, sort_by) | ||
| 188 | local out = { buffer = {} } | ||
| 189 | function out:write(data) table.insert(self.buffer, data) end | ||
| 190 | local ok, err = write_table_as_assignments(out, tbl, sort_by) | ||
| 191 | if not ok then | ||
| 192 | return nil, err | ||
| 193 | end | ||
| 194 | return table.concat(out.buffer) | ||
| 195 | end | ||
| 196 | |||
| 197 | |||
| 198 | |||
| 199 | |||
| 200 | |||
| 201 | |||
| 202 | |||
| 203 | |||
| 204 | |||
| 205 | |||
| 206 | function persist.save_from_table(filename, tbl, sort_by) | ||
| 207 | local prefix = dir.dir_name(filename) | ||
| 208 | fs.make_dir(prefix) | ||
| 209 | local out = io.open(filename, "w") | ||
| 210 | if not out then | ||
| 211 | return nil, "Cannot create file at " .. filename | ||
| 212 | end | ||
| 213 | local ok, err = write_table_as_assignments(out, tbl, sort_by) | ||
| 214 | out:close() | ||
| 215 | if not ok then | ||
| 216 | return nil, err | ||
| 217 | end | ||
| 218 | return true | ||
| 219 | end | ||
| 220 | |||
| 221 | |||
| 222 | |||
| 223 | |||
| 224 | |||
| 225 | |||
| 226 | |||
| 227 | |||
| 228 | |||
| 229 | function persist.save_as_module(filename, tbl) | ||
| 230 | local out = io.open(filename, "w") | ||
| 231 | if not out then | ||
| 232 | return nil, "Cannot create file at " .. filename | ||
| 233 | end | ||
| 234 | write_table_as_table(out, tbl) | ||
| 235 | out:close() | ||
| 236 | return true | ||
| 237 | end | ||
| 238 | |||
| 239 | function persist.load_config_file_if_basic(filename, config) | ||
| 240 | local env = { | ||
| 241 | home = config.home, | ||
| 242 | } | ||
| 243 | local result, _, errcode = persist.load_into_table(filename, env) | ||
| 244 | if errcode == "load" or errcode == "run" then | ||
| 245 | |||
| 246 | return nil, "Could not read existing config file " .. filename | ||
| 247 | end | ||
| 248 | |||
| 249 | local tbl | ||
| 250 | if errcode == "open" then | ||
| 251 | |||
| 252 | tbl = {} | ||
| 253 | else | ||
| 254 | tbl = result | ||
| 255 | tbl.home = nil | ||
| 256 | end | ||
| 257 | |||
| 258 | return tbl | ||
| 259 | end | ||
| 260 | |||
| 261 | function persist.save_default_lua_version(prefix, lua_version) | ||
| 262 | local ok, err_makedir = fs.make_dir(prefix) | ||
| 263 | if not ok then | ||
| 264 | return nil, err_makedir | ||
| 265 | end | ||
| 266 | local fd, err_open = io.open(dir.path(prefix, "default-lua-version.lua"), "w") | ||
| 267 | if not fd then | ||
| 268 | return nil, err_open | ||
| 269 | end | ||
| 270 | fd:write('return "' .. lua_version .. '"\n') | ||
| 271 | fd:close() | ||
| 272 | return true | ||
| 273 | end | ||
| 274 | |||
| 275 | 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 @@ | |||
| 1 | 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 | ||
| 2 | local queries = {} | ||
| 3 | |||
| 4 | |||
| 5 | local vers = require("luarocks.core.vers") | ||
| 6 | local util = require("luarocks.util") | ||
| 7 | local cfg = require("luarocks.core.cfg") | ||
| 8 | |||
| 9 | local query = require("luarocks.core.types.query") | ||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | local query_mt = {} | ||
| 16 | |||
| 17 | query_mt.__index = query.Query | ||
| 18 | |||
| 19 | |||
| 20 | query.Query.arch = { | ||
| 21 | src = true, | ||
| 22 | all = true, | ||
| 23 | rockspec = true, | ||
| 24 | installed = true, | ||
| 25 | |||
| 26 | } | ||
| 27 | |||
| 28 | |||
| 29 | query.Query.substring = false | ||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | local function arch_to_table(input) | ||
| 34 | if type(input) == "table" then | ||
| 35 | return input | ||
| 36 | elseif type(input) == "string" then | ||
| 37 | local arch = {} | ||
| 38 | for a in input:gmatch("[%w_-]+") do | ||
| 39 | arch[a] = true | ||
| 40 | end | ||
| 41 | return arch | ||
| 42 | end | ||
| 43 | end | ||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | |||
| 49 | |||
| 50 | |||
| 51 | |||
| 52 | |||
| 53 | |||
| 54 | function queries.new(name, namespace, version, substring, arch, operator) | ||
| 55 | |||
| 56 | operator = operator or "==" | ||
| 57 | |||
| 58 | local self = { | ||
| 59 | name = name, | ||
| 60 | namespace = namespace, | ||
| 61 | constraints = {}, | ||
| 62 | substring = substring, | ||
| 63 | arch = arch_to_table(arch), | ||
| 64 | } | ||
| 65 | if version then | ||
| 66 | table.insert(self.constraints, { op = operator, version = vers.parse_version(version) }) | ||
| 67 | end | ||
| 68 | |||
| 69 | query.Query.arch[cfg.arch] = true | ||
| 70 | return setmetatable(self, query_mt) | ||
| 71 | end | ||
| 72 | |||
| 73 | |||
| 74 | |||
| 75 | function queries.all(arch) | ||
| 76 | |||
| 77 | return queries.new("", nil, nil, true, arch) | ||
| 78 | end | ||
| 79 | |||
| 80 | do | ||
| 81 | local parse_constraints | ||
| 82 | do | ||
| 83 | local parse_constraint | ||
| 84 | do | ||
| 85 | local operators = { | ||
| 86 | ["=="] = "==", | ||
| 87 | ["~="] = "~=", | ||
| 88 | [">"] = ">", | ||
| 89 | ["<"] = "<", | ||
| 90 | [">="] = ">=", | ||
| 91 | ["<="] = "<=", | ||
| 92 | ["~>"] = "~>", | ||
| 93 | |||
| 94 | [""] = "==", | ||
| 95 | ["="] = "==", | ||
| 96 | ["!="] = "~=", | ||
| 97 | } | ||
| 98 | |||
| 99 | |||
| 100 | |||
| 101 | |||
| 102 | |||
| 103 | |||
| 104 | |||
| 105 | |||
| 106 | |||
| 107 | parse_constraint = function(input) | ||
| 108 | |||
| 109 | local no_upgrade, op, versionstr, rest = input:match("^(@?)([<>=~!]*)%s*([%w%.%_%-]+)[%s,]*(.*)") | ||
| 110 | local _op = operators[op] | ||
| 111 | local version = vers.parse_version(versionstr) | ||
| 112 | if not _op then | ||
| 113 | return nil, "Encountered bad constraint operator: '" .. tostring(op) .. "' in '" .. input .. "'" | ||
| 114 | end | ||
| 115 | if not version then | ||
| 116 | return nil, "Could not parse version from constraint: '" .. input .. "'" | ||
| 117 | end | ||
| 118 | return { op = _op, version = version, no_upgrade = no_upgrade == "@" and true or nil }, rest | ||
| 119 | end | ||
| 120 | end | ||
| 121 | |||
| 122 | |||
| 123 | |||
| 124 | |||
| 125 | |||
| 126 | |||
| 127 | |||
| 128 | |||
| 129 | |||
| 130 | parse_constraints = function(input) | ||
| 131 | |||
| 132 | local constraints, oinput = {}, input | ||
| 133 | local constraint | ||
| 134 | while #input > 0 do | ||
| 135 | constraint, input = parse_constraint(input) | ||
| 136 | if constraint then | ||
| 137 | table.insert(constraints, constraint) | ||
| 138 | else | ||
| 139 | return nil, "Failed to parse constraint '" .. tostring(oinput) .. "' with error: " .. input | ||
| 140 | end | ||
| 141 | end | ||
| 142 | return constraints | ||
| 143 | end | ||
| 144 | end | ||
| 145 | |||
| 146 | |||
| 147 | |||
| 148 | |||
| 149 | |||
| 150 | function queries.from_dep_string(depstr) | ||
| 151 | |||
| 152 | local ns_name, rest = depstr:match("^%s*([a-zA-Z0-9%.%-%_]*/?[a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)") | ||
| 153 | if not ns_name then | ||
| 154 | return nil, "failed to extract dependency name from '" .. depstr .. "'" | ||
| 155 | end | ||
| 156 | |||
| 157 | ns_name = ns_name:lower() | ||
| 158 | |||
| 159 | local constraints, err = parse_constraints(rest) | ||
| 160 | if not constraints then | ||
| 161 | return nil, err | ||
| 162 | end | ||
| 163 | |||
| 164 | local name, namespace = util.split_namespace(ns_name) | ||
| 165 | |||
| 166 | local self = { | ||
| 167 | name = name, | ||
| 168 | namespace = namespace, | ||
| 169 | constraints = constraints, | ||
| 170 | } | ||
| 171 | |||
| 172 | query.Query.arch[cfg.arch] = true | ||
| 173 | return setmetatable(self, query_mt) | ||
| 174 | end | ||
| 175 | end | ||
| 176 | |||
| 177 | function queries.from_persisted_table(tbl) | ||
| 178 | query.Query.arch[cfg.arch] = true | ||
| 179 | return setmetatable(tbl, query_mt) | ||
| 180 | end | ||
| 181 | |||
| 182 | |||
| 183 | |||
| 184 | |||
| 185 | |||
| 186 | function query_mt.__tostring(self) | ||
| 187 | local out = {} | ||
| 188 | if self.namespace then | ||
| 189 | table.insert(out, self.namespace) | ||
| 190 | table.insert(out, "/") | ||
| 191 | end | ||
| 192 | table.insert(out, self.name) | ||
| 193 | |||
| 194 | if #self.constraints > 0 then | ||
| 195 | local pretty = {} | ||
| 196 | for _, c in ipairs(self.constraints) do | ||
| 197 | local v = tostring(c.version) | ||
| 198 | if c.op == "==" then | ||
| 199 | table.insert(pretty, v) | ||
| 200 | else | ||
| 201 | table.insert(pretty, c.op .. " " .. v) | ||
| 202 | end | ||
| 203 | end | ||
| 204 | table.insert(out, " ") | ||
| 205 | table.insert(out, table.concat(pretty, ", ")) | ||
| 206 | end | ||
| 207 | |||
| 208 | return table.concat(out) | ||
| 209 | end | ||
| 210 | |||
| 211 | 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 @@ | |||
| 1 | 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 | ||
| 2 | local remove = {} | ||
| 3 | |||
| 4 | |||
| 5 | local search = require("luarocks.search") | ||
| 6 | local deps = require("luarocks.deps") | ||
| 7 | local fetch = require("luarocks.fetch") | ||
| 8 | local repos = require("luarocks.repos") | ||
| 9 | local repo_writer = require("luarocks.repo_writer") | ||
| 10 | local path = require("luarocks.path") | ||
| 11 | local util = require("luarocks.util") | ||
| 12 | local cfg = require("luarocks.core.cfg") | ||
| 13 | local manif = require("luarocks.manif") | ||
| 14 | local queries = require("luarocks.queries") | ||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | local function check_dependents(name, versions, deps_mode) | ||
| 25 | local dependents = {} | ||
| 26 | |||
| 27 | local skip_set = {} | ||
| 28 | skip_set[name] = {} | ||
| 29 | for version, _ in pairs(versions) do | ||
| 30 | skip_set[name][version] = true | ||
| 31 | end | ||
| 32 | |||
| 33 | local local_rocks = {} | ||
| 34 | local query_all = queries.all() | ||
| 35 | search.local_manifest_search(local_rocks, cfg.rocks_dir, query_all) | ||
| 36 | local_rocks[name] = nil | ||
| 37 | for rock_name, rock_versions in pairs(local_rocks) do | ||
| 38 | for rock_version, _ in pairs(rock_versions) do | ||
| 39 | local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version)) | ||
| 40 | if rockspec then | ||
| 41 | local _, missing = deps.match_deps(rockspec.dependencies.queries, rockspec.rocks_provided, deps_mode, skip_set) | ||
| 42 | if missing[name] then | ||
| 43 | table.insert(dependents, { name = rock_name, version = rock_version }) | ||
| 44 | end | ||
| 45 | end | ||
| 46 | end | ||
| 47 | end | ||
| 48 | |||
| 49 | return dependents | ||
| 50 | end | ||
| 51 | |||
| 52 | |||
| 53 | |||
| 54 | |||
| 55 | |||
| 56 | |||
| 57 | |||
| 58 | |||
| 59 | local function delete_versions(name, versions, deps_mode) | ||
| 60 | |||
| 61 | for version, _ in pairs(versions) do | ||
| 62 | util.printout("Removing " .. name .. " " .. version .. "...") | ||
| 63 | local ok, err = repo_writer.delete_version(name, version, deps_mode) | ||
| 64 | if not ok then return nil, err end | ||
| 65 | end | ||
| 66 | |||
| 67 | return true | ||
| 68 | end | ||
| 69 | |||
| 70 | function remove.remove_search_results(results, name, deps_mode, force, fast) | ||
| 71 | local versions = results[name] | ||
| 72 | |||
| 73 | local version = next(versions) | ||
| 74 | local second = next(versions, version) | ||
| 75 | |||
| 76 | local dependents = {} | ||
| 77 | if not fast then | ||
| 78 | util.printout("Checking stability of dependencies in the absence of") | ||
| 79 | util.printout(name .. " " .. table.concat((util.keys(versions)), ", ") .. "...") | ||
| 80 | util.printout() | ||
| 81 | dependents = check_dependents(name, versions, deps_mode) | ||
| 82 | end | ||
| 83 | |||
| 84 | if #dependents > 0 then | ||
| 85 | if force or fast then | ||
| 86 | util.printerr("The following packages may be broken by this forced removal:") | ||
| 87 | for _, dependent in ipairs(dependents) do | ||
| 88 | util.printerr(dependent.name .. " " .. dependent.version) | ||
| 89 | end | ||
| 90 | util.printerr() | ||
| 91 | else | ||
| 92 | if not second then | ||
| 93 | util.printerr("Will not remove " .. name .. " " .. version .. ".") | ||
| 94 | util.printerr("Removing it would break dependencies for: ") | ||
| 95 | else | ||
| 96 | util.printerr("Will not remove installed versions of " .. name .. ".") | ||
| 97 | util.printerr("Removing them would break dependencies for: ") | ||
| 98 | end | ||
| 99 | for _, dependent in ipairs(dependents) do | ||
| 100 | util.printerr(dependent.name .. " " .. dependent.version) | ||
| 101 | end | ||
| 102 | util.printerr() | ||
| 103 | util.printerr("Use --force to force removal (warning: this may break modules).") | ||
| 104 | return nil, "Failed removing." | ||
| 105 | end | ||
| 106 | end | ||
| 107 | |||
| 108 | local ok, err = delete_versions(name, versions, deps_mode) | ||
| 109 | if not ok then return nil, err end | ||
| 110 | |||
| 111 | util.printout("Removal successful.") | ||
| 112 | return true | ||
| 113 | end | ||
| 114 | |||
| 115 | function remove.remove_other_versions(name, version, force, fast) | ||
| 116 | local results = {} | ||
| 117 | local query = queries.new(name, nil, version, false, nil, "~=") | ||
| 118 | search.local_manifest_search(results, cfg.rocks_dir, query) | ||
| 119 | local warn | ||
| 120 | if results[name] then | ||
| 121 | local ok, err = remove.remove_search_results(results, name, cfg.deps_mode, force, fast) | ||
| 122 | if not ok then | ||
| 123 | warn = err | ||
| 124 | end | ||
| 125 | end | ||
| 126 | |||
| 127 | if not fast then | ||
| 128 | |||
| 129 | |||
| 130 | local rock_manifest, load_err = manif.load_rock_manifest(name, version) | ||
| 131 | local ok, err = repos.check_everything_is_installed(name, version, rock_manifest, cfg.root_dir, false) | ||
| 132 | if not ok then | ||
| 133 | return nil, err | ||
| 134 | end | ||
| 135 | end | ||
| 136 | |||
| 137 | return true, nil, warn | ||
| 138 | end | ||
| 139 | |||
| 140 | 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 @@ | |||
| 1 | local repo_writer = {} | ||
| 2 | |||
| 3 | |||
| 4 | local fs = require("luarocks.fs") | ||
| 5 | local path = require("luarocks.path") | ||
| 6 | local repos = require("luarocks.repos") | ||
| 7 | local writer = require("luarocks.manif.writer") | ||
| 8 | |||
| 9 | function repo_writer.deploy_files(name, version, wrap_bin_scripts, deps_mode, namespace) | ||
| 10 | local ok, err | ||
| 11 | |||
| 12 | if not fs.exists(path.rock_manifest_file(name, version)) then | ||
| 13 | ok, err = writer.make_rock_manifest(name, version) | ||
| 14 | if err then | ||
| 15 | return nil, err | ||
| 16 | end | ||
| 17 | end | ||
| 18 | |||
| 19 | if namespace then | ||
| 20 | ok, err = writer.make_namespace_file(name, version, namespace) | ||
| 21 | if not ok then | ||
| 22 | return nil, err | ||
| 23 | end | ||
| 24 | end | ||
| 25 | |||
| 26 | ok, err = repos.deploy_local_files(name, version, wrap_bin_scripts, deps_mode) | ||
| 27 | if not ok then | ||
| 28 | return nil, err | ||
| 29 | end | ||
| 30 | |||
| 31 | ok, err = writer.add_to_manifest(name, version, nil, deps_mode) | ||
| 32 | return ok, err | ||
| 33 | end | ||
| 34 | |||
| 35 | function repo_writer.delete_version(name, version, deps_mode, quick) | ||
| 36 | local ok, err, op = repos.delete_local_version(name, version, deps_mode, quick) | ||
| 37 | |||
| 38 | if op == "remove" then | ||
| 39 | local rok, rerr = writer.remove_from_manifest(name, version, nil, deps_mode) | ||
| 40 | if ok and not rok then | ||
| 41 | ok, err = rok, rerr | ||
| 42 | end | ||
| 43 | end | ||
| 44 | |||
| 45 | return ok, err | ||
| 46 | end | ||
| 47 | |||
| 48 | function repo_writer.refresh_manifest(rocks_dir) | ||
| 49 | return writer.make_manifest(rocks_dir, "one") | ||
| 50 | end | ||
| 51 | |||
| 52 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local repos = {Op = {}, Paths = {}, } | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | local fs = require("luarocks.fs") | ||
| 20 | local path = require("luarocks.path") | ||
| 21 | local cfg = require("luarocks.core.cfg") | ||
| 22 | local util = require("luarocks.util") | ||
| 23 | local dir = require("luarocks.dir") | ||
| 24 | local manif = require("luarocks.manif") | ||
| 25 | local vers = require("luarocks.core.vers") | ||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | |||
| 49 | |||
| 50 | |||
| 51 | |||
| 52 | |||
| 53 | |||
| 54 | |||
| 55 | |||
| 56 | |||
| 57 | |||
| 58 | local function get_installed_versions(name) | ||
| 59 | assert(not name:match("/")) | ||
| 60 | |||
| 61 | local dirs = fs.list_dir(path.versions_dir(name)) | ||
| 62 | return (dirs and #dirs > 0) and dirs or nil | ||
| 63 | end | ||
| 64 | |||
| 65 | |||
| 66 | |||
| 67 | |||
| 68 | |||
| 69 | |||
| 70 | |||
| 71 | function repos.is_installed(name, version) | ||
| 72 | assert(not name:match("/")) | ||
| 73 | |||
| 74 | return fs.is_dir(path.install_dir(name, version)) | ||
| 75 | end | ||
| 76 | |||
| 77 | function repos.recurse_rock_manifest_entry(entry, action) | ||
| 78 | if entry == nil then | ||
| 79 | return true | ||
| 80 | end | ||
| 81 | |||
| 82 | local function do_recurse_rock_manifest_entry(tree, parent_path) | ||
| 83 | |||
| 84 | for file, sub in pairs(tree) do | ||
| 85 | local sub_path = (parent_path and (parent_path .. "/") or "") .. file | ||
| 86 | local ok, err | ||
| 87 | |||
| 88 | if type(sub) == "table" then | ||
| 89 | ok, err = do_recurse_rock_manifest_entry(sub, sub_path) | ||
| 90 | else | ||
| 91 | ok, err = action(sub_path) | ||
| 92 | end | ||
| 93 | |||
| 94 | if err then return nil, err end | ||
| 95 | end | ||
| 96 | return true | ||
| 97 | end | ||
| 98 | return do_recurse_rock_manifest_entry(entry) | ||
| 99 | end | ||
| 100 | |||
| 101 | local function store_package_data(result, rock_manifest, deploy_type) | ||
| 102 | if rock_manifest[deploy_type] then | ||
| 103 | repos.recurse_rock_manifest_entry(rock_manifest[deploy_type], function(file_path) | ||
| 104 | local _, item_name = manif.get_provided_item(deploy_type, file_path) | ||
| 105 | result[item_name] = file_path | ||
| 106 | return true | ||
| 107 | end) | ||
| 108 | end | ||
| 109 | end | ||
| 110 | |||
| 111 | |||
| 112 | |||
| 113 | |||
| 114 | |||
| 115 | |||
| 116 | |||
| 117 | |||
| 118 | |||
| 119 | |||
| 120 | function repos.package_modules(name, version) | ||
| 121 | assert(not name:match("/")) | ||
| 122 | |||
| 123 | local result = {} | ||
| 124 | local rock_manifest = manif.load_rock_manifest(name, version) | ||
| 125 | if not rock_manifest then return result end | ||
| 126 | store_package_data(result, rock_manifest, "lib") | ||
| 127 | store_package_data(result, rock_manifest, "lua") | ||
| 128 | return result | ||
| 129 | end | ||
| 130 | |||
| 131 | |||
| 132 | |||
| 133 | |||
| 134 | |||
| 135 | |||
| 136 | |||
| 137 | |||
| 138 | |||
| 139 | |||
| 140 | function repos.package_commands(name, version) | ||
| 141 | assert(not name:match("/")) | ||
| 142 | |||
| 143 | local result = {} | ||
| 144 | local rock_manifest = manif.load_rock_manifest(name, version) | ||
| 145 | if not rock_manifest then return result end | ||
| 146 | store_package_data(result, rock_manifest, "bin") | ||
| 147 | return result | ||
| 148 | end | ||
| 149 | |||
| 150 | |||
| 151 | |||
| 152 | |||
| 153 | |||
| 154 | |||
| 155 | |||
| 156 | function repos.has_binaries(name, version) | ||
| 157 | assert(not name:match("/")) | ||
| 158 | |||
| 159 | local entries = manif.load_rock_manifest(name, version) | ||
| 160 | if not entries then | ||
| 161 | return false | ||
| 162 | end | ||
| 163 | local bin = entries["bin"] | ||
| 164 | if type(bin) == "table" then | ||
| 165 | for bin_name, md5 in pairs(bin) do | ||
| 166 | |||
| 167 | if fs.is_actual_binary(dir.path(cfg.deploy_bin_dir, bin_name)) then | ||
| 168 | return true | ||
| 169 | end | ||
| 170 | end | ||
| 171 | end | ||
| 172 | return false | ||
| 173 | end | ||
| 174 | |||
| 175 | function repos.run_hook(rockspec, hook_name) | ||
| 176 | |||
| 177 | local hooks = rockspec.hooks | ||
| 178 | if not hooks then | ||
| 179 | return true | ||
| 180 | end | ||
| 181 | |||
| 182 | if cfg.hooks_enabled == false then | ||
| 183 | return nil, "This rockspec contains hooks, which are blocked by the 'hooks_enabled' setting in your LuaRocks configuration." | ||
| 184 | end | ||
| 185 | |||
| 186 | if not hooks.substituted_variables then | ||
| 187 | util.variable_substitutions(hooks, rockspec.variables) | ||
| 188 | hooks.substituted_variables = true | ||
| 189 | end | ||
| 190 | local hook = (hooks)[hook_name] | ||
| 191 | if hook then | ||
| 192 | util.printout(hook) | ||
| 193 | if not fs.execute(hook) then | ||
| 194 | return nil, "Failed running " .. hook_name .. " hook." | ||
| 195 | end | ||
| 196 | end | ||
| 197 | return true | ||
| 198 | end | ||
| 199 | |||
| 200 | function repos.should_wrap_bin_scripts(rockspec) | ||
| 201 | |||
| 202 | if cfg.wrap_bin_scripts then | ||
| 203 | return cfg.wrap_bin_scripts | ||
| 204 | end | ||
| 205 | if rockspec.deploy and rockspec.deploy.wrap_bin_scripts == false then | ||
| 206 | return false | ||
| 207 | end | ||
| 208 | return true | ||
| 209 | end | ||
| 210 | |||
| 211 | local function find_suffixed(file, suffix) | ||
| 212 | local filenames = { file } | ||
| 213 | if suffix and suffix ~= "" then | ||
| 214 | table.insert(filenames, 1, file .. suffix) | ||
| 215 | end | ||
| 216 | |||
| 217 | for _, filename in ipairs(filenames) do | ||
| 218 | if fs.exists(filename) then | ||
| 219 | return filename | ||
| 220 | end | ||
| 221 | end | ||
| 222 | |||
| 223 | return nil, table.concat(filenames, ", ") .. " not found" | ||
| 224 | end | ||
| 225 | |||
| 226 | local function check_suffix(filename, suffix) | ||
| 227 | local suffixed_filename, err = find_suffixed(filename, suffix) | ||
| 228 | if not suffixed_filename then | ||
| 229 | return "" | ||
| 230 | end | ||
| 231 | return suffixed_filename:sub(#filename + 1) | ||
| 232 | end | ||
| 233 | |||
| 234 | |||
| 235 | |||
| 236 | |||
| 237 | |||
| 238 | |||
| 239 | |||
| 240 | local function get_deploy_paths(name, version, deploy_type, file_path, repo) | ||
| 241 | |||
| 242 | repo = repo or cfg.root_dir | ||
| 243 | local deploy_dir = (path)["deploy_" .. deploy_type .. "_dir"](repo) | ||
| 244 | local non_versioned = dir.path(deploy_dir, file_path) | ||
| 245 | local versioned = path.versioned_name(non_versioned, deploy_dir, name, version) | ||
| 246 | return { nv = non_versioned, v = versioned } | ||
| 247 | end | ||
| 248 | |||
| 249 | local function check_spot_if_available(name, version, deploy_type, file_path) | ||
| 250 | local item_type, item_name = manif.get_provided_item(deploy_type, file_path) | ||
| 251 | local cur_name, cur_version = manif.get_current_provider(item_type, item_name) | ||
| 252 | |||
| 253 | |||
| 254 | |||
| 255 | |||
| 256 | if not cur_name and deploy_type == "lua" and item_name:match("%.init$") then | ||
| 257 | cur_name, cur_version = manif.get_current_provider(item_type, (item_name:gsub("%.init$", ""))) | ||
| 258 | end | ||
| 259 | |||
| 260 | if (not cur_name) or | ||
| 261 | (name < cur_name) or | ||
| 262 | (name == cur_name and (version == cur_version or | ||
| 263 | vers.compare_versions(version, cur_version))) then | ||
| 264 | return "nv", cur_name, cur_version, item_name | ||
| 265 | else | ||
| 266 | |||
| 267 | return "v", cur_name, cur_version, item_name | ||
| 268 | end | ||
| 269 | end | ||
| 270 | |||
| 271 | local function backup_existing(should_backup, target) | ||
| 272 | if not should_backup then | ||
| 273 | fs.delete(target) | ||
| 274 | return | ||
| 275 | end | ||
| 276 | if fs.exists(target) then | ||
| 277 | local backup = target | ||
| 278 | repeat | ||
| 279 | backup = backup .. "~" | ||
| 280 | until not fs.exists(backup) | ||
| 281 | |||
| 282 | util.warning(target .. " is not tracked by this installation of LuaRocks. Moving it to " .. backup) | ||
| 283 | local move_ok, move_err = os.rename(target, backup) | ||
| 284 | if not move_ok then | ||
| 285 | return nil, move_err | ||
| 286 | end | ||
| 287 | return backup | ||
| 288 | end | ||
| 289 | end | ||
| 290 | |||
| 291 | local function prepare_op_install() | ||
| 292 | local mkdirs = {} | ||
| 293 | local rmdirs = {} | ||
| 294 | |||
| 295 | local function memoize_mkdir(d) | ||
| 296 | if mkdirs[d] then | ||
| 297 | return true | ||
| 298 | end | ||
| 299 | local ok, err = fs.make_dir(d) | ||
| 300 | if not ok then | ||
| 301 | return nil, err | ||
| 302 | end | ||
| 303 | mkdirs[d] = true | ||
| 304 | return true | ||
| 305 | end | ||
| 306 | |||
| 307 | local function op_install(op) | ||
| 308 | local ok, err = memoize_mkdir(dir.dir_name(op.dst)) | ||
| 309 | if not ok then | ||
| 310 | return nil, err | ||
| 311 | end | ||
| 312 | |||
| 313 | local backup, err = backup_existing(op.backup, op.dst) | ||
| 314 | if err then | ||
| 315 | return nil, err | ||
| 316 | end | ||
| 317 | if backup then | ||
| 318 | op.backup_file = backup | ||
| 319 | end | ||
| 320 | |||
| 321 | ok, err = op.fn(op.src, op.dst, op.backup) | ||
| 322 | if not ok then | ||
| 323 | return nil, err | ||
| 324 | end | ||
| 325 | |||
| 326 | rmdirs[dir.dir_name(op.src)] = true | ||
| 327 | return true | ||
| 328 | end | ||
| 329 | |||
| 330 | local function done_op_install() | ||
| 331 | for d, _ in pairs(rmdirs) do | ||
| 332 | fs.remove_dir_tree_if_empty(d) | ||
| 333 | end | ||
| 334 | end | ||
| 335 | |||
| 336 | return op_install, done_op_install | ||
| 337 | end | ||
| 338 | |||
| 339 | local function rollback_install(op) | ||
| 340 | fs.delete(op.dst) | ||
| 341 | if op.backup_file then | ||
| 342 | os.rename(op.backup_file, op.dst) | ||
| 343 | end | ||
| 344 | fs.remove_dir_tree_if_empty(dir.dir_name(op.dst)) | ||
| 345 | return true | ||
| 346 | end | ||
| 347 | |||
| 348 | local function op_rename(op) | ||
| 349 | if op.suffix then | ||
| 350 | local suffix = check_suffix(op.src, op.suffix) | ||
| 351 | op.src = op.src .. suffix | ||
| 352 | op.dst = op.dst .. suffix | ||
| 353 | end | ||
| 354 | |||
| 355 | if fs.exists(op.src) then | ||
| 356 | fs.make_dir(dir.dir_name(op.dst)) | ||
| 357 | fs.delete(op.dst) | ||
| 358 | local ok, err = os.rename(op.src, op.dst) | ||
| 359 | fs.remove_dir_tree_if_empty(dir.dir_name(op.src)) | ||
| 360 | return ok, err | ||
| 361 | else | ||
| 362 | return true | ||
| 363 | end | ||
| 364 | end | ||
| 365 | |||
| 366 | local function rollback_rename(op) | ||
| 367 | return op_rename({ src = op.dst, dst = op.src }) | ||
| 368 | end | ||
| 369 | |||
| 370 | local function prepare_op_delete() | ||
| 371 | local deletes = {} | ||
| 372 | local rmdirs = {} | ||
| 373 | |||
| 374 | local function done_op_delete() | ||
| 375 | for _, f in ipairs(deletes) do | ||
| 376 | os.remove(f) | ||
| 377 | end | ||
| 378 | |||
| 379 | for d, _ in pairs(rmdirs) do | ||
| 380 | fs.remove_dir_tree_if_empty(d) | ||
| 381 | end | ||
| 382 | end | ||
| 383 | |||
| 384 | local function op_delete(op) | ||
| 385 | if op.suffix then | ||
| 386 | local suffix = check_suffix(op.name, op.suffix) | ||
| 387 | op.name = op.name .. suffix | ||
| 388 | end | ||
| 389 | |||
| 390 | table.insert(deletes, op.name) | ||
| 391 | |||
| 392 | rmdirs[dir.dir_name(op.name)] = true | ||
| 393 | end | ||
| 394 | |||
| 395 | return op_delete, done_op_delete | ||
| 396 | end | ||
| 397 | |||
| 398 | local function rollback_ops(ops, op_fn, n) | ||
| 399 | for i = 1, n do | ||
| 400 | op_fn(ops[i]) | ||
| 401 | end | ||
| 402 | end | ||
| 403 | |||
| 404 | |||
| 405 | function repos.check_everything_is_installed(name, version, rock_manifest, repo, accept_versioned) | ||
| 406 | local missing = {} | ||
| 407 | local suffix = cfg.wrapper_suffix or "" | ||
| 408 | for _, category in ipairs({ "bin", "lua", "lib" }) do | ||
| 409 | if rock_manifest[category] then | ||
| 410 | repos.recurse_rock_manifest_entry(rock_manifest[category], function(file_path) | ||
| 411 | local paths = get_deploy_paths(name, version, category, file_path, repo) | ||
| 412 | if category == "bin" then | ||
| 413 | if (fs.exists(paths.nv) or fs.exists(paths.nv .. suffix)) or | ||
| 414 | (accept_versioned and (fs.exists(paths.v) or fs.exists(paths.v .. suffix))) then | ||
| 415 | return | ||
| 416 | end | ||
| 417 | else | ||
| 418 | if fs.exists(paths.nv) or (accept_versioned and fs.exists(paths.v)) then | ||
| 419 | return | ||
| 420 | end | ||
| 421 | end | ||
| 422 | table.insert(missing, paths.nv) | ||
| 423 | end) | ||
| 424 | end | ||
| 425 | end | ||
| 426 | if #missing > 0 then | ||
| 427 | return nil, "failed deploying files. " .. | ||
| 428 | "The following files were not installed:\n" .. | ||
| 429 | table.concat(missing, "\n") | ||
| 430 | end | ||
| 431 | return true | ||
| 432 | end | ||
| 433 | |||
| 434 | |||
| 435 | |||
| 436 | |||
| 437 | |||
| 438 | |||
| 439 | |||
| 440 | |||
| 441 | function repos.deploy_local_files(name, version, wrap_bin_scripts, deps_mode) | ||
| 442 | assert(not name:match("/")) | ||
| 443 | |||
| 444 | local rock_manifest, load_err = manif.load_rock_manifest(name, version) | ||
| 445 | if not rock_manifest then return nil, load_err end | ||
| 446 | |||
| 447 | local repo = cfg.root_dir | ||
| 448 | local renames = {} | ||
| 449 | local installs = {} | ||
| 450 | |||
| 451 | local function install_binary(source, target) | ||
| 452 | if wrap_bin_scripts and fs.is_lua(source) then | ||
| 453 | return fs.wrap_script(source, target, deps_mode, name, version) | ||
| 454 | else | ||
| 455 | return fs.copy_binary(source, target) | ||
| 456 | end | ||
| 457 | end | ||
| 458 | |||
| 459 | local function move_lua(source, target) | ||
| 460 | return fs.move(source, target, "read") | ||
| 461 | end | ||
| 462 | |||
| 463 | local function move_lib(source, target) | ||
| 464 | return fs.move(source, target, "exec") | ||
| 465 | end | ||
| 466 | |||
| 467 | if rock_manifest.bin then | ||
| 468 | local source_dir = path.bin_dir(name, version) | ||
| 469 | repos.recurse_rock_manifest_entry(rock_manifest.bin, function(file_path) | ||
| 470 | local source = dir.path(source_dir, file_path) | ||
| 471 | local paths = get_deploy_paths(name, version, "bin", file_path, repo) | ||
| 472 | local mode, cur_name, cur_version = check_spot_if_available(name, version, "bin", file_path) | ||
| 473 | |||
| 474 | if mode == "nv" and cur_name then | ||
| 475 | local cur_paths = get_deploy_paths(cur_name, cur_version, "bin", file_path, repo) | ||
| 476 | table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v, suffix = cfg.wrapper_suffix }) | ||
| 477 | end | ||
| 478 | local target = mode == "nv" and paths.nv or paths.v | ||
| 479 | local backup = name ~= cur_name or version ~= cur_version | ||
| 480 | if wrap_bin_scripts and fs.is_lua(source) then | ||
| 481 | target = target .. (cfg.wrapper_suffix or "") | ||
| 482 | end | ||
| 483 | table.insert(installs, { fn = install_binary, src = source, dst = target, backup = backup }) | ||
| 484 | end) | ||
| 485 | end | ||
| 486 | |||
| 487 | if rock_manifest.lua then | ||
| 488 | local source_dir = path.lua_dir(name, version) | ||
| 489 | repos.recurse_rock_manifest_entry(rock_manifest.lua, function(file_path) | ||
| 490 | local source = dir.path(source_dir, file_path) | ||
| 491 | local paths = get_deploy_paths(name, version, "lua", file_path, repo) | ||
| 492 | local mode, cur_name, cur_version = check_spot_if_available(name, version, "lua", file_path) | ||
| 493 | |||
| 494 | if mode == "nv" and cur_name then | ||
| 495 | local cur_paths = get_deploy_paths(cur_name, cur_version, "lua", file_path, repo) | ||
| 496 | table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v }) | ||
| 497 | cur_paths = get_deploy_paths(cur_name, cur_version, "lib", file_path:gsub("%.lua$", "." .. cfg.lib_extension), repo) | ||
| 498 | table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v }) | ||
| 499 | end | ||
| 500 | local target = mode == "nv" and paths.nv or paths.v | ||
| 501 | local backup = name ~= cur_name or version ~= cur_version | ||
| 502 | table.insert(installs, { fn = move_lua, src = source, dst = target, backup = backup }) | ||
| 503 | end) | ||
| 504 | end | ||
| 505 | |||
| 506 | if rock_manifest.lib then | ||
| 507 | local source_dir = path.lib_dir(name, version) | ||
| 508 | repos.recurse_rock_manifest_entry(rock_manifest.lib, function(file_path) | ||
| 509 | local source = dir.path(source_dir, file_path) | ||
| 510 | local paths = get_deploy_paths(name, version, "lib", file_path, repo) | ||
| 511 | local mode, cur_name, cur_version = check_spot_if_available(name, version, "lib", file_path) | ||
| 512 | |||
| 513 | if mode == "nv" and cur_name then | ||
| 514 | local cur_paths = get_deploy_paths(cur_name, cur_version, "lua", file_path:gsub("%.[^.]+$", ".lua"), repo) | ||
| 515 | table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v }) | ||
| 516 | cur_paths = get_deploy_paths(cur_name, cur_version, "lib", file_path, repo) | ||
| 517 | table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v }) | ||
| 518 | end | ||
| 519 | local target = mode == "nv" and paths.nv or paths.v | ||
| 520 | local backup = name ~= cur_name or version ~= cur_version | ||
| 521 | table.insert(installs, { fn = move_lib, src = source, dst = target, backup = backup }) | ||
| 522 | end) | ||
| 523 | end | ||
| 524 | |||
| 525 | for i, op in ipairs(renames) do | ||
| 526 | local ok, err = op_rename(op) | ||
| 527 | if not ok then | ||
| 528 | rollback_ops(renames, rollback_rename, i - 1) | ||
| 529 | return nil, err | ||
| 530 | end | ||
| 531 | end | ||
| 532 | local op_install, done_op_install = prepare_op_install() | ||
| 533 | for i, op in ipairs(installs) do | ||
| 534 | local ok, err = op_install(op) | ||
| 535 | if not ok then | ||
| 536 | rollback_ops(installs, rollback_install, i - 1) | ||
| 537 | rollback_ops(renames, rollback_rename, #renames) | ||
| 538 | return nil, err | ||
| 539 | end | ||
| 540 | end | ||
| 541 | done_op_install() | ||
| 542 | |||
| 543 | local ok, err = repos.check_everything_is_installed(name, version, rock_manifest, repo, true) | ||
| 544 | if not ok then | ||
| 545 | return nil, err | ||
| 546 | end | ||
| 547 | |||
| 548 | return true | ||
| 549 | end | ||
| 550 | |||
| 551 | local function add_to_double_checks(double_checks, name, version) | ||
| 552 | double_checks[name] = double_checks[name] or {} | ||
| 553 | double_checks[name][version] = true | ||
| 554 | end | ||
| 555 | |||
| 556 | local function double_check_all(double_checks, repo) | ||
| 557 | local errs = {} | ||
| 558 | for next_name, versions in pairs(double_checks) do | ||
| 559 | for next_version in pairs(versions) do | ||
| 560 | local rock_manifest, load_err = manif.load_rock_manifest(next_name, next_version) | ||
| 561 | local ok, err = repos.check_everything_is_installed(next_name, next_version, rock_manifest, repo, true) | ||
| 562 | if not ok then | ||
| 563 | table.insert(errs, err) | ||
| 564 | end | ||
| 565 | end | ||
| 566 | end | ||
| 567 | if next(errs) then | ||
| 568 | return nil, table.concat(errs, "\n") | ||
| 569 | end | ||
| 570 | return true | ||
| 571 | end | ||
| 572 | |||
| 573 | |||
| 574 | |||
| 575 | |||
| 576 | |||
| 577 | |||
| 578 | |||
| 579 | |||
| 580 | |||
| 581 | |||
| 582 | |||
| 583 | function repos.delete_local_version(name, version, deps_mode, quick) | ||
| 584 | assert(not name:match("/")) | ||
| 585 | |||
| 586 | local rock_manifest, load_err = manif.load_rock_manifest(name, version) | ||
| 587 | if not rock_manifest then | ||
| 588 | if not quick then | ||
| 589 | return nil, "rock_manifest file not found for " .. name .. " " .. version .. " - removed entry from the manifest", "remove" | ||
| 590 | end | ||
| 591 | return nil, load_err, "fail" | ||
| 592 | end | ||
| 593 | |||
| 594 | local repo = cfg.root_dir | ||
| 595 | local renames = {} | ||
| 596 | local deletes = {} | ||
| 597 | |||
| 598 | local double_checks = {} | ||
| 599 | |||
| 600 | if rock_manifest.bin then | ||
| 601 | repos.recurse_rock_manifest_entry(rock_manifest.bin, function(file_path) | ||
| 602 | local paths = get_deploy_paths(name, version, "bin", file_path, repo) | ||
| 603 | local mode, cur_name, cur_version, item_name = check_spot_if_available(name, version, "bin", file_path) | ||
| 604 | if mode == "v" then | ||
| 605 | table.insert(deletes, { name = paths.v, suffix = cfg.wrapper_suffix }) | ||
| 606 | else | ||
| 607 | table.insert(deletes, { name = paths.nv, suffix = cfg.wrapper_suffix }) | ||
| 608 | |||
| 609 | local next_name, next_version = manif.get_next_provider("command", item_name) | ||
| 610 | if next_name then | ||
| 611 | add_to_double_checks(double_checks, next_name, next_version) | ||
| 612 | local next_paths = get_deploy_paths(next_name, next_version, "bin", file_path, repo) | ||
| 613 | table.insert(renames, { src = next_paths.v, dst = next_paths.nv, suffix = cfg.wrapper_suffix }) | ||
| 614 | end | ||
| 615 | end | ||
| 616 | end) | ||
| 617 | end | ||
| 618 | |||
| 619 | if rock_manifest.lua then | ||
| 620 | repos.recurse_rock_manifest_entry(rock_manifest.lua, function(file_path) | ||
| 621 | local paths = get_deploy_paths(name, version, "lua", file_path, repo) | ||
| 622 | local mode, cur_name, cur_version, item_name = check_spot_if_available(name, version, "lua", file_path) | ||
| 623 | if mode == "v" then | ||
| 624 | table.insert(deletes, { name = paths.v }) | ||
| 625 | else | ||
| 626 | table.insert(deletes, { name = paths.nv }) | ||
| 627 | |||
| 628 | local next_name, next_version = manif.get_next_provider("module", item_name) | ||
| 629 | if next_name then | ||
| 630 | add_to_double_checks(double_checks, next_name, next_version) | ||
| 631 | local next_lua_paths = get_deploy_paths(next_name, next_version, "lua", file_path, repo) | ||
| 632 | table.insert(renames, { src = next_lua_paths.v, dst = next_lua_paths.nv }) | ||
| 633 | local next_lib_paths = get_deploy_paths(next_name, next_version, "lib", file_path:gsub("%.[^.]+$", ".lua"), repo) | ||
| 634 | table.insert(renames, { src = next_lib_paths.v, dst = next_lib_paths.nv }) | ||
| 635 | end | ||
| 636 | end | ||
| 637 | end) | ||
| 638 | end | ||
| 639 | |||
| 640 | if rock_manifest.lib then | ||
| 641 | repos.recurse_rock_manifest_entry(rock_manifest.lib, function(file_path) | ||
| 642 | local paths = get_deploy_paths(name, version, "lib", file_path, repo) | ||
| 643 | local mode, cur_name, cur_version, item_name = check_spot_if_available(name, version, "lib", file_path) | ||
| 644 | if mode == "v" then | ||
| 645 | table.insert(deletes, { name = paths.v }) | ||
| 646 | else | ||
| 647 | table.insert(deletes, { name = paths.nv }) | ||
| 648 | |||
| 649 | local next_name, next_version = manif.get_next_provider("module", item_name) | ||
| 650 | if next_name then | ||
| 651 | add_to_double_checks(double_checks, next_name, next_version) | ||
| 652 | local next_lua_paths = get_deploy_paths(next_name, next_version, "lua", file_path:gsub("%.[^.]+$", ".lua"), repo) | ||
| 653 | table.insert(renames, { src = next_lua_paths.v, dst = next_lua_paths.nv }) | ||
| 654 | local next_lib_paths = get_deploy_paths(next_name, next_version, "lib", file_path, repo) | ||
| 655 | table.insert(renames, { src = next_lib_paths.v, dst = next_lib_paths.nv }) | ||
| 656 | end | ||
| 657 | end | ||
| 658 | end) | ||
| 659 | end | ||
| 660 | |||
| 661 | local op_delete, done_op_delete = prepare_op_delete() | ||
| 662 | for _, op in ipairs(deletes) do | ||
| 663 | op_delete(op) | ||
| 664 | end | ||
| 665 | done_op_delete() | ||
| 666 | |||
| 667 | if not quick then | ||
| 668 | for _, op in ipairs(renames) do | ||
| 669 | op_rename(op) | ||
| 670 | end | ||
| 671 | |||
| 672 | local ok, err = double_check_all(double_checks, repo) | ||
| 673 | if not ok then | ||
| 674 | return nil, err, "fail" | ||
| 675 | end | ||
| 676 | end | ||
| 677 | |||
| 678 | fs.delete(path.install_dir(name, version)) | ||
| 679 | if not get_installed_versions(name) then | ||
| 680 | fs.delete(dir.path(cfg.rocks_dir, name)) | ||
| 681 | end | ||
| 682 | |||
| 683 | if quick then | ||
| 684 | return true, nil, "ok" | ||
| 685 | end | ||
| 686 | |||
| 687 | return true, nil, "remove" | ||
| 688 | end | ||
| 689 | |||
| 690 | 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 @@ | |||
| 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 @@ | |||
| 1 | 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 = {} | ||
| 2 | |||
| 3 | |||
| 4 | local vers = require("luarocks.core.vers") | ||
| 5 | local util = require("luarocks.util") | ||
| 6 | |||
| 7 | |||
| 8 | local result = require("luarocks.core.types.result") | ||
| 9 | |||
| 10 | |||
| 11 | local result_mt = {} | ||
| 12 | |||
| 13 | result_mt.__index = result.Result | ||
| 14 | |||
| 15 | function results.new(name, version, repo, arch, namespace) | ||
| 16 | |||
| 17 | assert(not name:match("/")) | ||
| 18 | |||
| 19 | |||
| 20 | if not namespace then | ||
| 21 | name, namespace = util.split_namespace(name) | ||
| 22 | end | ||
| 23 | |||
| 24 | local self = { | ||
| 25 | name = name, | ||
| 26 | version = version, | ||
| 27 | namespace = namespace, | ||
| 28 | arch = arch, | ||
| 29 | repo = repo, | ||
| 30 | } | ||
| 31 | |||
| 32 | return setmetatable(self, result_mt) | ||
| 33 | end | ||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | local function match_name(query, name) | ||
| 43 | if query.substring then | ||
| 44 | return name:find(query.name, 0, true) and true or false | ||
| 45 | else | ||
| 46 | return name == query.name | ||
| 47 | end | ||
| 48 | end | ||
| 49 | |||
| 50 | |||
| 51 | |||
| 52 | |||
| 53 | function result.Result:satisfies(query) | ||
| 54 | return match_name(query, self.name) and | ||
| 55 | (query.arch[self.arch] or query.arch["any"]) and | ||
| 56 | ((not query.namespace) or (query.namespace == self.namespace)) and | ||
| 57 | (vers.match_constraints(vers.parse_version(self.version), query.constraints)) | ||
| 58 | end | ||
| 59 | |||
| 60 | 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 @@ | |||
| 1 | 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 | ||
| 2 | local rockspecs = {} | ||
| 3 | |||
| 4 | |||
| 5 | local cfg = require("luarocks.core.cfg") | ||
| 6 | local dir = require("luarocks.dir") | ||
| 7 | local path = require("luarocks.path") | ||
| 8 | local queries = require("luarocks.queries") | ||
| 9 | local type_rockspec = require("luarocks.type.rockspec") | ||
| 10 | local util = require("luarocks.util") | ||
| 11 | local vers = require("luarocks.core.vers") | ||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | local vendored_build_type_set = { | ||
| 19 | ["builtin"] = true, | ||
| 20 | ["cmake"] = true, | ||
| 21 | ["command"] = true, | ||
| 22 | ["make"] = true, | ||
| 23 | ["module"] = true, | ||
| 24 | ["none"] = true, | ||
| 25 | } | ||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | local function platform_overrides(tbl) | ||
| 41 | |||
| 42 | if not tbl then return end | ||
| 43 | |||
| 44 | local tblp = tbl.platforms | ||
| 45 | |||
| 46 | if type(tblp) == "table" then | ||
| 47 | for platform in cfg.each_platform() do | ||
| 48 | local platform_tbl = tblp[platform] | ||
| 49 | if type(platform_tbl) == "table" then | ||
| 50 | util.deep_merge(tbl, platform_tbl) | ||
| 51 | end | ||
| 52 | end | ||
| 53 | end | ||
| 54 | tbl.platforms = nil | ||
| 55 | end | ||
| 56 | |||
| 57 | local function convert_dependencies(dependencies) | ||
| 58 | local qs = {} | ||
| 59 | for i = 1, #dependencies do | ||
| 60 | local parsed, err = queries.from_dep_string(dependencies[i]) | ||
| 61 | if not parsed then | ||
| 62 | return nil, "Parse error processing dependency '" .. dependencies[i] .. "': " .. tostring(err) | ||
| 63 | end | ||
| 64 | qs[i] = parsed | ||
| 65 | end | ||
| 66 | dependencies.queries = qs | ||
| 67 | return true | ||
| 68 | end | ||
| 69 | |||
| 70 | |||
| 71 | |||
| 72 | |||
| 73 | |||
| 74 | local function configure_paths(rockspec) | ||
| 75 | local vars = {} | ||
| 76 | for k, v in pairs(cfg.variables) do | ||
| 77 | vars[k] = v | ||
| 78 | end | ||
| 79 | local name, version = rockspec.name, rockspec.version | ||
| 80 | vars.PREFIX = path.install_dir(name, version) | ||
| 81 | vars.LUADIR = path.lua_dir(name, version) | ||
| 82 | vars.LIBDIR = path.lib_dir(name, version) | ||
| 83 | vars.CONFDIR = path.conf_dir(name, version) | ||
| 84 | vars.BINDIR = path.bin_dir(name, version) | ||
| 85 | vars.DOCDIR = path.doc_dir(name, version) | ||
| 86 | rockspec.variables = vars | ||
| 87 | end | ||
| 88 | |||
| 89 | function rockspecs.from_persisted_table(filename, rockspec, globals, quick) | ||
| 90 | |||
| 91 | if rockspec.rockspec_format then | ||
| 92 | if vers.compare_versions(rockspec.rockspec_format, type_rockspec.rockspec_format) then | ||
| 93 | return nil, "Rockspec format " .. rockspec.rockspec_format .. " is not supported, please upgrade LuaRocks." | ||
| 94 | end | ||
| 95 | end | ||
| 96 | |||
| 97 | if not quick then | ||
| 98 | local ok, err = type_rockspec.check(rockspec, globals or {}) | ||
| 99 | if not ok then | ||
| 100 | return nil, err | ||
| 101 | end | ||
| 102 | end | ||
| 103 | |||
| 104 | |||
| 105 | |||
| 106 | |||
| 107 | |||
| 108 | do | ||
| 109 | local parsed_format = vers.parse_version(rockspec.rockspec_format or "1.0") | ||
| 110 | rockspec.format_is_at_least = function(self, version) | ||
| 111 | return parsed_format >= vers.parse_version(version) | ||
| 112 | end | ||
| 113 | end | ||
| 114 | |||
| 115 | platform_overrides(rockspec.build) | ||
| 116 | platform_overrides(rockspec.dependencies) | ||
| 117 | platform_overrides(rockspec.build_dependencies) | ||
| 118 | platform_overrides(rockspec.test_dependencies) | ||
| 119 | platform_overrides(rockspec.external_dependencies) | ||
| 120 | platform_overrides(rockspec.source) | ||
| 121 | platform_overrides(rockspec.hooks) | ||
| 122 | platform_overrides(rockspec.test) | ||
| 123 | |||
| 124 | rockspec.name = rockspec.package:lower() | ||
| 125 | |||
| 126 | local protocol, pathname = dir.split_url(rockspec.source.url) | ||
| 127 | if dir.is_basic_protocol(protocol) then | ||
| 128 | rockspec.source.file = rockspec.source.file or dir.base_name(rockspec.source.url) | ||
| 129 | end | ||
| 130 | rockspec.source.protocol, rockspec.source.pathname = protocol, pathname | ||
| 131 | |||
| 132 | |||
| 133 | if rockspec.source.cvs_module then rockspec.source.module = rockspec.source.cvs_module end | ||
| 134 | if rockspec.source.cvs_tag then rockspec.source.tag = rockspec.source.cvs_tag end | ||
| 135 | |||
| 136 | rockspec.local_abs_filename = filename | ||
| 137 | rockspec.source.dir_set = rockspec.source.dir ~= nil | ||
| 138 | rockspec.source.dir = rockspec.source.dir or rockspec.source.module | ||
| 139 | |||
| 140 | rockspec.rocks_provided = util.get_rocks_provided(rockspec) | ||
| 141 | |||
| 142 | rockspec.dependencies = rockspec.dependencies or {} | ||
| 143 | rockspec.build_dependencies = rockspec.build_dependencies or {} | ||
| 144 | rockspec.test_dependencies = rockspec.test_dependencies or {} | ||
| 145 | for _, d in ipairs({ rockspec.dependencies, rockspec.build_dependencies, rockspec.test_dependencies }) do | ||
| 146 | local _, err = convert_dependencies(d) | ||
| 147 | if err then | ||
| 148 | return nil, err | ||
| 149 | end | ||
| 150 | end | ||
| 151 | |||
| 152 | if rockspec.build and | ||
| 153 | rockspec.build.type and | ||
| 154 | not vendored_build_type_set[rockspec.build.type] then | ||
| 155 | local build_pkg_name = "luarocks-build-" .. rockspec.build.type | ||
| 156 | if not rockspec.build_dependencies then | ||
| 157 | rockspec.build_dependencies = {} | ||
| 158 | end | ||
| 159 | |||
| 160 | local found = false | ||
| 161 | for _, dep in ipairs(rockspec.build_dependencies.queries) do | ||
| 162 | if dep.name == build_pkg_name then | ||
| 163 | found = true | ||
| 164 | break | ||
| 165 | end | ||
| 166 | end | ||
| 167 | |||
| 168 | if not found then | ||
| 169 | local query, errfromdep = queries.from_dep_string(build_pkg_name) | ||
| 170 | if errfromdep then | ||
| 171 | return nil, "Invalid dependency in rockspec: " .. errfromdep | ||
| 172 | end | ||
| 173 | table.insert(rockspec.build_dependencies.queries, query) | ||
| 174 | end | ||
| 175 | end | ||
| 176 | |||
| 177 | if not quick then | ||
| 178 | configure_paths(rockspec) | ||
| 179 | end | ||
| 180 | |||
| 181 | return rockspec | ||
| 182 | end | ||
| 183 | |||
| 184 | 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 @@ | |||
| 1 | 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 = {} | ||
| 2 | |||
| 3 | local dir = require("luarocks.dir") | ||
| 4 | local path = require("luarocks.path") | ||
| 5 | local manif = require("luarocks.manif") | ||
| 6 | local vers = require("luarocks.core.vers") | ||
| 7 | local cfg = require("luarocks.core.cfg") | ||
| 8 | local util = require("luarocks.util") | ||
| 9 | local queries = require("luarocks.queries") | ||
| 10 | local results = require("luarocks.results") | ||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | function search.store_result(result_tree, result) | ||
| 24 | |||
| 25 | local name = result.name | ||
| 26 | local version = result.version | ||
| 27 | |||
| 28 | if not result_tree[name] then result_tree[name] = {} end | ||
| 29 | if not result_tree[name][version] then result_tree[name][version] = {} end | ||
| 30 | table.insert(result_tree[name][version], { | ||
| 31 | arch = result.arch, | ||
| 32 | repo = result.repo, | ||
| 33 | namespace = result.namespace, | ||
| 34 | }) | ||
| 35 | end | ||
| 36 | |||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | |||
| 45 | |||
| 46 | local function store_if_match(result_tree, result, query) | ||
| 47 | |||
| 48 | if result:satisfies(query) then | ||
| 49 | search.store_result(result_tree, result) | ||
| 50 | end | ||
| 51 | end | ||
| 52 | |||
| 53 | |||
| 54 | |||
| 55 | |||
| 56 | |||
| 57 | |||
| 58 | |||
| 59 | |||
| 60 | |||
| 61 | |||
| 62 | function search.disk_search(repo, query, result_tree) | ||
| 63 | |||
| 64 | local fs = require("luarocks.fs") | ||
| 65 | |||
| 66 | if not result_tree then | ||
| 67 | result_tree = {} | ||
| 68 | end | ||
| 69 | |||
| 70 | for name in fs.dir(repo) do | ||
| 71 | local pathname = dir.path(repo, name) | ||
| 72 | local rname, rversion, rarch = path.parse_name(name) | ||
| 73 | |||
| 74 | if rname and (pathname:match(".rockspec$") or pathname:match(".rock$")) then | ||
| 75 | local result = results.new(rname, rversion, repo, rarch) | ||
| 76 | store_if_match(result_tree, result, query) | ||
| 77 | elseif fs.is_dir(pathname) then | ||
| 78 | for version in fs.dir(pathname) do | ||
| 79 | if version:match("-%d+$") then | ||
| 80 | local namespace = path.read_namespace(name, version, repo) | ||
| 81 | local result = results.new(name, version, repo, "installed", namespace) | ||
| 82 | store_if_match(result_tree, result, query) | ||
| 83 | end | ||
| 84 | end | ||
| 85 | end | ||
| 86 | end | ||
| 87 | return result_tree | ||
| 88 | end | ||
| 89 | |||
| 90 | |||
| 91 | |||
| 92 | |||
| 93 | |||
| 94 | |||
| 95 | |||
| 96 | |||
| 97 | |||
| 98 | |||
| 99 | |||
| 100 | local function manifest_search(result_tree, repo, query, lua_version, is_local) | ||
| 101 | |||
| 102 | |||
| 103 | if (not is_local) and query.namespace then | ||
| 104 | repo = repo .. "/manifests/" .. query.namespace | ||
| 105 | end | ||
| 106 | |||
| 107 | local manifest, err, errcode = manif.load_manifest(repo, lua_version, not is_local) | ||
| 108 | if not manifest then | ||
| 109 | return nil, err, errcode | ||
| 110 | end | ||
| 111 | for name, versions in pairs(manifest.repository) do | ||
| 112 | for version, items in pairs(versions) do | ||
| 113 | local namespace = is_local and path.read_namespace(name, version, repo) or query.namespace | ||
| 114 | for _, item in ipairs(items) do | ||
| 115 | local result = results.new(name, version, repo, item.arch, namespace) | ||
| 116 | store_if_match(result_tree, result, query) | ||
| 117 | end | ||
| 118 | end | ||
| 119 | end | ||
| 120 | return true | ||
| 121 | end | ||
| 122 | |||
| 123 | local function remote_manifest_search(result_tree, repo, query, lua_version) | ||
| 124 | return manifest_search(result_tree, repo, query, lua_version, false) | ||
| 125 | end | ||
| 126 | |||
| 127 | function search.local_manifest_search(result_tree, repo, query, lua_version) | ||
| 128 | return manifest_search(result_tree, repo, query, lua_version, true) | ||
| 129 | end | ||
| 130 | |||
| 131 | |||
| 132 | |||
| 133 | |||
| 134 | |||
| 135 | |||
| 136 | |||
| 137 | function search.search_repos(query, lua_version) | ||
| 138 | |||
| 139 | local result_tree = {} | ||
| 140 | local repo = {} | ||
| 141 | for _, repostr in ipairs(cfg.rocks_servers) do | ||
| 142 | if type(repostr) == "string" then | ||
| 143 | repo = { repostr } | ||
| 144 | else | ||
| 145 | repo = repostr | ||
| 146 | end | ||
| 147 | for _, mirror in ipairs(repo) do | ||
| 148 | if not cfg.disabled_servers[mirror] then | ||
| 149 | local protocol, pathname = dir.split_url(mirror) | ||
| 150 | if protocol == "file" then | ||
| 151 | mirror = pathname | ||
| 152 | end | ||
| 153 | local ok, err, errcode = remote_manifest_search(result_tree, mirror, query, lua_version) | ||
| 154 | if errcode == "network" then | ||
| 155 | cfg.disabled_servers[mirror] = true | ||
| 156 | end | ||
| 157 | if ok then | ||
| 158 | break | ||
| 159 | else | ||
| 160 | util.warning("Failed searching manifest: " .. err) | ||
| 161 | if errcode == "downloader" then | ||
| 162 | break | ||
| 163 | end | ||
| 164 | end | ||
| 165 | end | ||
| 166 | end | ||
| 167 | end | ||
| 168 | |||
| 169 | local provided_repo = "provided by VM or rocks_provided" | ||
| 170 | for name, version in pairs(util.get_rocks_provided()) do | ||
| 171 | local result = results.new(name, version, provided_repo, "installed") | ||
| 172 | store_if_match(result_tree, result, query) | ||
| 173 | end | ||
| 174 | return result_tree | ||
| 175 | end | ||
| 176 | |||
| 177 | |||
| 178 | |||
| 179 | |||
| 180 | |||
| 181 | |||
| 182 | |||
| 183 | local function pick_latest_version(name, versions) | ||
| 184 | assert(not name:match("/")) | ||
| 185 | |||
| 186 | local vtables = {} | ||
| 187 | for v, _ in pairs(versions) do | ||
| 188 | table.insert(vtables, vers.parse_version(v)) | ||
| 189 | end | ||
| 190 | table.sort(vtables) | ||
| 191 | local version = vtables[#vtables].string | ||
| 192 | local items = versions[version] | ||
| 193 | if items then | ||
| 194 | local pick = 1 | ||
| 195 | for i, item in ipairs(items) do | ||
| 196 | if (item.arch == 'src' and items[pick].arch == 'rockspec') or | ||
| 197 | (item.arch ~= 'src' and item.arch ~= 'rockspec') then | ||
| 198 | pick = i | ||
| 199 | end | ||
| 200 | end | ||
| 201 | return path.make_url(items[pick].repo, name, version, items[pick].arch) | ||
| 202 | end | ||
| 203 | return nil | ||
| 204 | end | ||
| 205 | |||
| 206 | |||
| 207 | |||
| 208 | |||
| 209 | local function supported_lua_versions(query) | ||
| 210 | local result_tree = {} | ||
| 211 | |||
| 212 | for lua_version in util.lua_versions() do | ||
| 213 | if lua_version ~= cfg.lua_version then | ||
| 214 | util.printout("Checking for Lua " .. lua_version .. "...") | ||
| 215 | if search.search_repos(query, lua_version)[query.name] then | ||
| 216 | table.insert(result_tree, lua_version) | ||
| 217 | end | ||
| 218 | end | ||
| 219 | end | ||
| 220 | |||
| 221 | return result_tree | ||
| 222 | end | ||
| 223 | |||
| 224 | |||
| 225 | |||
| 226 | |||
| 227 | |||
| 228 | |||
| 229 | function search.find_suitable_rock(query) | ||
| 230 | |||
| 231 | local rocks_provided = util.get_rocks_provided() | ||
| 232 | |||
| 233 | if rocks_provided[query.name] then | ||
| 234 | |||
| 235 | return nil, "Rock " .. query.name .. " " .. rocks_provided[query.name] .. | ||
| 236 | " is already provided by VM or via 'rocks_provided' in the config file.", "provided" | ||
| 237 | end | ||
| 238 | |||
| 239 | local result_tree = search.search_repos(query) | ||
| 240 | local first_rock = next(result_tree) | ||
| 241 | if not first_rock then | ||
| 242 | return nil, "No results matching query were found for Lua " .. cfg.lua_version .. ".", "notfound" | ||
| 243 | elseif next(result_tree, first_rock) then | ||
| 244 | |||
| 245 | return nil, "Several rocks matched query.", "manyfound" | ||
| 246 | else | ||
| 247 | return pick_latest_version(query.name, result_tree[first_rock]) | ||
| 248 | end | ||
| 249 | end | ||
| 250 | |||
| 251 | function search.find_rock_checking_lua_versions(query, check_lua_versions) | ||
| 252 | local url, err, errcode = search.find_suitable_rock(query) | ||
| 253 | if url then | ||
| 254 | return url | ||
| 255 | end | ||
| 256 | |||
| 257 | if errcode == "notfound" then | ||
| 258 | local add | ||
| 259 | if check_lua_versions then | ||
| 260 | util.printout(query.name .. " not found for Lua " .. cfg.lua_version .. ".") | ||
| 261 | util.printout("Checking if available for other Lua versions...") | ||
| 262 | |||
| 263 | |||
| 264 | local lua_versions = supported_lua_versions(query) | ||
| 265 | |||
| 266 | if #lua_versions ~= 0 then | ||
| 267 | |||
| 268 | for i, lua_version in ipairs(lua_versions) do | ||
| 269 | lua_versions[i] = "Lua " .. lua_version | ||
| 270 | end | ||
| 271 | |||
| 272 | local versions_message = "only " .. table.concat(lua_versions, " and ") .. | ||
| 273 | " but not Lua " .. cfg.lua_version .. "." | ||
| 274 | |||
| 275 | if #query.constraints == 0 then | ||
| 276 | add = query.name .. " supports " .. versions_message | ||
| 277 | elseif #query.constraints == 1 and query.constraints[1].op == "==" then | ||
| 278 | local queryversion = tostring(query.constraints[1].version) | ||
| 279 | add = query.name .. " " .. queryversion .. " supports " .. versions_message | ||
| 280 | else | ||
| 281 | add = "Matching " .. query.name .. " versions support " .. versions_message | ||
| 282 | end | ||
| 283 | else | ||
| 284 | add = query.name .. " is not available for any Lua versions." | ||
| 285 | end | ||
| 286 | else | ||
| 287 | add = "To check if it is available for other Lua versions, use --check-lua-versions." | ||
| 288 | end | ||
| 289 | err = err .. "\n" .. add | ||
| 290 | end | ||
| 291 | |||
| 292 | return nil, err | ||
| 293 | end | ||
| 294 | |||
| 295 | function search.find_src_or_rockspec(name, namespace, version, check_lua_versions) | ||
| 296 | local query = queries.new(name, namespace, version, false, "src|rockspec") | ||
| 297 | local url, err = search.find_rock_checking_lua_versions(query, check_lua_versions) | ||
| 298 | if not url then | ||
| 299 | return nil, "Could not find a result named " .. tostring(query) .. ": " .. err | ||
| 300 | end | ||
| 301 | return url | ||
| 302 | end | ||
| 303 | |||
| 304 | |||
| 305 | |||
| 306 | |||
| 307 | function search.print_result_tree(result_tree, porcelain) | ||
| 308 | |||
| 309 | if porcelain then | ||
| 310 | for packagestr, versions in util.sortedpairs(result_tree) do | ||
| 311 | for version, repos in util.sortedpairs(versions, vers.compare_versions) do | ||
| 312 | for _, repo in ipairs(repos) do | ||
| 313 | local nrepo = dir.normalize(repo.repo) | ||
| 314 | util.printout(packagestr, version, repo.arch, nrepo, repo.namespace) | ||
| 315 | end | ||
| 316 | end | ||
| 317 | end | ||
| 318 | return | ||
| 319 | end | ||
| 320 | |||
| 321 | for packagestr, versions in util.sortedpairs(result_tree) do | ||
| 322 | local namespaces = {} | ||
| 323 | for version, repos in util.sortedpairs(versions, vers.compare_versions) do | ||
| 324 | for _, repo in ipairs(repos) do | ||
| 325 | local key = repo.namespace or "" | ||
| 326 | local list = namespaces[key] or {} | ||
| 327 | namespaces[key] = list | ||
| 328 | |||
| 329 | repo.repo = dir.normalize(repo.repo) | ||
| 330 | table.insert(list, " " .. version .. " (" .. repo.arch .. ") - " .. path.root_dir(repo.repo)) | ||
| 331 | end | ||
| 332 | end | ||
| 333 | for key, list in util.sortedpairs(namespaces) do | ||
| 334 | util.printout(key == "" and packagestr or key .. "/" .. packagestr) | ||
| 335 | for _, line in ipairs(list) do | ||
| 336 | util.printout(line) | ||
| 337 | end | ||
| 338 | util.printout() | ||
| 339 | end | ||
| 340 | end | ||
| 341 | end | ||
| 342 | |||
| 343 | function search.pick_installed_rock(query, given_tree) | ||
| 344 | |||
| 345 | local result_tree = {} | ||
| 346 | local tree_map = {} | ||
| 347 | local trees = cfg.rocks_trees | ||
| 348 | if given_tree then | ||
| 349 | trees = { given_tree } | ||
| 350 | end | ||
| 351 | for _, tree in ipairs(trees) do | ||
| 352 | local rocks_dir = path.rocks_dir(tree) | ||
| 353 | tree_map[rocks_dir] = tree | ||
| 354 | search.local_manifest_search(result_tree, rocks_dir, query) | ||
| 355 | end | ||
| 356 | if not next(result_tree) then | ||
| 357 | return nil, "cannot find package " .. tostring(query) .. "\nUse 'list' to find installed rocks." | ||
| 358 | end | ||
| 359 | |||
| 360 | if not result_tree[query.name] and next(result_tree, next(result_tree)) then | ||
| 361 | local out = { "multiple installed packages match the name '" .. tostring(query) .. "':\n\n" } | ||
| 362 | for name, _ in util.sortedpairs(result_tree) do | ||
| 363 | table.insert(out, " " .. name .. "\n") | ||
| 364 | end | ||
| 365 | table.insert(out, "\nPlease specify a single rock.\n") | ||
| 366 | return nil, table.concat(out) | ||
| 367 | end | ||
| 368 | |||
| 369 | local repo_url | ||
| 370 | |||
| 371 | local name, versions | ||
| 372 | if result_tree[query.name] then | ||
| 373 | name, versions = query.name, result_tree[query.name] | ||
| 374 | else | ||
| 375 | name, versions = util.sortedpairs(result_tree)() | ||
| 376 | end | ||
| 377 | |||
| 378 | local version, repositories = util.sortedpairs(versions, vers.compare_versions)() | ||
| 379 | for _, rp in ipairs(repositories) do repo_url = rp.repo end | ||
| 380 | |||
| 381 | local repo = tree_map[repo_url] | ||
| 382 | return name, version, repo, repo_url | ||
| 383 | end | ||
| 384 | |||
| 385 | 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 @@ | |||
| 1 | local signing = {} | ||
| 2 | |||
| 3 | local cfg = require("luarocks.core.cfg") | ||
| 4 | local fs = require("luarocks.fs") | ||
| 5 | |||
| 6 | local function get_gpg() | ||
| 7 | local vars = cfg.variables | ||
| 8 | local gpg = vars.GPG | ||
| 9 | local gpg_ok, err = fs.is_tool_available(gpg, "gpg") | ||
| 10 | if not gpg_ok then | ||
| 11 | return nil, err | ||
| 12 | end | ||
| 13 | return gpg | ||
| 14 | end | ||
| 15 | |||
| 16 | function signing.signature_url(url) | ||
| 17 | return url .. ".asc" | ||
| 18 | end | ||
| 19 | |||
| 20 | function signing.sign_file(file) | ||
| 21 | local gpg, err = get_gpg() | ||
| 22 | if not gpg then | ||
| 23 | return nil, err | ||
| 24 | end | ||
| 25 | |||
| 26 | local sigfile = file .. ".asc" | ||
| 27 | if fs.execute(gpg, "--armor", "--output", sigfile, "--detach-sign", file) then | ||
| 28 | return sigfile | ||
| 29 | else | ||
| 30 | return nil, "failed running " .. gpg .. " to sign " .. file | ||
| 31 | end | ||
| 32 | end | ||
| 33 | |||
| 34 | function signing.verify_signature(file, sigfile) | ||
| 35 | local gpg, err = get_gpg() | ||
| 36 | if not gpg then | ||
| 37 | return nil, err | ||
| 38 | end | ||
| 39 | |||
| 40 | if fs.execute(gpg, "--verify", sigfile, file) then | ||
| 41 | return true | ||
| 42 | else | ||
| 43 | return nil, "GPG returned a verification error" | ||
| 44 | end | ||
| 45 | |||
| 46 | end | ||
| 47 | |||
| 48 | 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 @@ | |||
| 1 | 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 = {} | ||
| 2 | |||
| 3 | |||
| 4 | local fetch = require("luarocks.fetch") | ||
| 5 | local deps = require("luarocks.deps") | ||
| 6 | local util = require("luarocks.util") | ||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | local test_types = { | ||
| 15 | "busted", | ||
| 16 | "command", | ||
| 17 | } | ||
| 18 | |||
| 19 | local test_modules = {} | ||
| 20 | local typetomod = {} | ||
| 21 | local modtotype = {} | ||
| 22 | |||
| 23 | for _, test_type in ipairs(test_types) do | ||
| 24 | local mod | ||
| 25 | if test_type == "command" then | ||
| 26 | mod = require("luarocks.test.command") | ||
| 27 | elseif test_type == "busted" then | ||
| 28 | mod = require("luarocks.test.busted") | ||
| 29 | end | ||
| 30 | table.insert(test_modules, mod) | ||
| 31 | typetomod[test_type] = mod | ||
| 32 | modtotype[mod] = test_type | ||
| 33 | end | ||
| 34 | |||
| 35 | local function get_test_type(rockspec) | ||
| 36 | if rockspec.test and rockspec.test.type then | ||
| 37 | return rockspec.test.type | ||
| 38 | end | ||
| 39 | |||
| 40 | for _, test_module in ipairs(test_modules) do | ||
| 41 | if test_module.detect_type() then | ||
| 42 | return modtotype[test_module] | ||
| 43 | end | ||
| 44 | end | ||
| 45 | |||
| 46 | return nil, "could not detect test type -- no test suite for " .. rockspec.package .. "?" | ||
| 47 | end | ||
| 48 | |||
| 49 | |||
| 50 | function test.run_test_suite(rockspec_arg, test_type, args, prepare) | ||
| 51 | local rockspec | ||
| 52 | if type(rockspec_arg) == "string" then | ||
| 53 | local err, errcode | ||
| 54 | rockspec, err, errcode = fetch.load_rockspec(rockspec_arg) | ||
| 55 | if err then | ||
| 56 | return nil, err, errcode | ||
| 57 | end | ||
| 58 | else | ||
| 59 | rockspec = rockspec_arg | ||
| 60 | end | ||
| 61 | |||
| 62 | if not test_type then | ||
| 63 | local err | ||
| 64 | test_type, err = get_test_type(rockspec) | ||
| 65 | if not test_type then | ||
| 66 | return nil, err | ||
| 67 | end | ||
| 68 | end | ||
| 69 | |||
| 70 | local all_deps = { | ||
| 71 | "dependencies", | ||
| 72 | "build_dependencies", | ||
| 73 | "test_dependencies", | ||
| 74 | } | ||
| 75 | for _, dep_kind in ipairs(all_deps) do | ||
| 76 | if (rockspec)[dep_kind] and next((rockspec)[dep_kind]) ~= nil then | ||
| 77 | local _, err, errcode = deps.fulfill_dependencies(rockspec, dep_kind, "all") | ||
| 78 | if err then | ||
| 79 | return nil, err, errcode | ||
| 80 | end | ||
| 81 | end | ||
| 82 | end | ||
| 83 | |||
| 84 | local pok, test_mod = pcall(require, "luarocks.test." .. test_type) | ||
| 85 | if not pok then | ||
| 86 | return nil, "failed loading test execution module luarocks.test." .. test_type | ||
| 87 | end | ||
| 88 | |||
| 89 | if prepare then | ||
| 90 | if test_type == "busted" then | ||
| 91 | return test_mod.run_tests(rockspec.test, { "--version" }) | ||
| 92 | else | ||
| 93 | return true | ||
| 94 | end | ||
| 95 | else | ||
| 96 | local flags = rockspec.test and rockspec.test.flags | ||
| 97 | if type(flags) == "table" then | ||
| 98 | util.variable_substitutions(flags, rockspec.variables) | ||
| 99 | |||
| 100 | |||
| 101 | for i = 1, #flags do | ||
| 102 | table.insert(args, i, flags[i]) | ||
| 103 | end | ||
| 104 | end | ||
| 105 | |||
| 106 | return test_mod.run_tests(rockspec.test, args) | ||
| 107 | end | ||
| 108 | end | ||
| 109 | |||
| 110 | 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 @@ | |||
| 1 | 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 | ||
| 2 | local busted = {} | ||
| 3 | |||
| 4 | |||
| 5 | local fs = require("luarocks.fs") | ||
| 6 | local deps = require("luarocks.deps") | ||
| 7 | local path = require("luarocks.path") | ||
| 8 | local dir = require("luarocks.dir") | ||
| 9 | local queries = require("luarocks.queries") | ||
| 10 | local install = require("luarocks.cmd.install") | ||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | function busted.detect_type() | ||
| 15 | if fs.exists(".busted") then | ||
| 16 | return true | ||
| 17 | end | ||
| 18 | return false | ||
| 19 | end | ||
| 20 | |||
| 21 | function busted.run_tests(test, args) | ||
| 22 | if not test then | ||
| 23 | test = {} | ||
| 24 | end | ||
| 25 | |||
| 26 | local ok, bustedver, where = deps.fulfill_dependency(queries.new("busted"), nil, nil, nil, "test_dependencies") | ||
| 27 | if not ok then | ||
| 28 | return nil, bustedver | ||
| 29 | end | ||
| 30 | |||
| 31 | local busted_exe | ||
| 32 | if test.busted_executable then | ||
| 33 | busted_exe = test.busted_executable | ||
| 34 | else | ||
| 35 | busted_exe = dir.path(path.root_dir(where), "bin", "busted") | ||
| 36 | |||
| 37 | |||
| 38 | local busted_bat = dir.path(path.root_dir(where), "bin", "busted.bat") | ||
| 39 | |||
| 40 | if not fs.exists(busted_exe) and not fs.exists(busted_bat) then | ||
| 41 | return nil, "'busted' executable failed to be installed" | ||
| 42 | end | ||
| 43 | end | ||
| 44 | |||
| 45 | local err | ||
| 46 | ok, err = fs.execute(busted_exe, _tl_table_unpack(args)) | ||
| 47 | if ok then | ||
| 48 | return true | ||
| 49 | else | ||
| 50 | return nil, err or "test suite failed." | ||
| 51 | end | ||
| 52 | end | ||
| 53 | |||
| 54 | |||
| 55 | 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 @@ | |||
| 1 | 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 | ||
| 2 | local command = {} | ||
| 3 | |||
| 4 | |||
| 5 | local fs = require("luarocks.fs") | ||
| 6 | local cfg = require("luarocks.core.cfg") | ||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | function command.detect_type() | ||
| 11 | if fs.exists("test.lua") then | ||
| 12 | return true | ||
| 13 | end | ||
| 14 | return false | ||
| 15 | end | ||
| 16 | |||
| 17 | function command.run_tests(test, args) | ||
| 18 | if not test then | ||
| 19 | test = { | ||
| 20 | script = "test.lua", | ||
| 21 | } | ||
| 22 | end | ||
| 23 | |||
| 24 | if not test.script and not test.command then | ||
| 25 | test.script = "test.lua" | ||
| 26 | end | ||
| 27 | |||
| 28 | local ok | ||
| 29 | |||
| 30 | if test.script then | ||
| 31 | local test_script = test.script | ||
| 32 | if not (type(test_script) == "string") then | ||
| 33 | return nil, "Malformed rockspec: 'script' expects a string" | ||
| 34 | end | ||
| 35 | if not fs.exists(test.script) then | ||
| 36 | return nil, "Test script " .. test.script .. " does not exist" | ||
| 37 | end | ||
| 38 | local lua = fs.Q(cfg.variables["LUA"]) | ||
| 39 | ok = fs.execute(lua, test.script, _tl_table_unpack(args)) | ||
| 40 | elseif test.command then | ||
| 41 | local test_command = test.command | ||
| 42 | if not (type(test_command) == "string") then | ||
| 43 | return nil, "Malformed rockspec: 'command' expects a string" | ||
| 44 | end | ||
| 45 | ok = fs.execute(test.command, _tl_table_unpack(args)) | ||
| 46 | end | ||
| 47 | |||
| 48 | if ok then | ||
| 49 | return true | ||
| 50 | else | ||
| 51 | return nil, "tests failed with non-zero exit code" | ||
| 52 | end | ||
| 53 | end | ||
| 54 | |||
| 55 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | local patch = {Lineends = {}, Hunk = {}, File = {}, Files = {}, } | ||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | local fs = require("luarocks.fs") | ||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | |||
| 49 | |||
| 50 | |||
| 51 | local debugmode = false | ||
| 52 | local function debug(_) end | ||
| 53 | local function info(_) end | ||
| 54 | local function warning(s) io.stderr:write(s .. '\n') end | ||
| 55 | |||
| 56 | |||
| 57 | local function startswith(s, s2) | ||
| 58 | return s:sub(1, #s2) == s2 | ||
| 59 | end | ||
| 60 | |||
| 61 | |||
| 62 | local function endswith(s, s2) | ||
| 63 | return #s >= #s2 and s:sub(#s - #s2 + 1) == s2 | ||
| 64 | end | ||
| 65 | |||
| 66 | |||
| 67 | local function endlstrip(s) | ||
| 68 | return s:gsub('[\r\n]+$', '') | ||
| 69 | end | ||
| 70 | |||
| 71 | |||
| 72 | local function table_copy(t) | ||
| 73 | local t2 = {} | ||
| 74 | for k, v in pairs(t) do t2[k] = v end | ||
| 75 | return t2 | ||
| 76 | end | ||
| 77 | |||
| 78 | local function exists(filename) | ||
| 79 | local fh = io.open(filename) | ||
| 80 | local result = fh ~= nil | ||
| 81 | if fh then fh:close() end | ||
| 82 | return result | ||
| 83 | end | ||
| 84 | local function isfile() return true end | ||
| 85 | |||
| 86 | local function string_as_file(s) | ||
| 87 | return { | ||
| 88 | at = 0, | ||
| 89 | str = s, | ||
| 90 | len = #s, | ||
| 91 | eof = false, | ||
| 92 | read = function(self, n) | ||
| 93 | if self.eof then return nil end | ||
| 94 | local chunk = self.str:sub(self.at, self.at + n - 1) | ||
| 95 | self.at = self.at + n | ||
| 96 | if self.at > self.len then | ||
| 97 | self.eof = true | ||
| 98 | end | ||
| 99 | return chunk | ||
| 100 | end, | ||
| 101 | close = function(self) | ||
| 102 | self.eof = true | ||
| 103 | end, | ||
| 104 | } | ||
| 105 | end | ||
| 106 | |||
| 107 | |||
| 108 | |||
| 109 | |||
| 110 | |||
| 111 | |||
| 112 | |||
| 113 | |||
| 114 | |||
| 115 | |||
| 116 | local function file_lines(f) | ||
| 117 | local CHUNK_SIZE = 1024 | ||
| 118 | local buffer = "" | ||
| 119 | local pos_beg = 1 | ||
| 120 | return function() | ||
| 121 | local pos, chars | ||
| 122 | while 1 do | ||
| 123 | pos, chars = buffer:match('()([\r\n].)', pos_beg) | ||
| 124 | if pos or not f then | ||
| 125 | break | ||
| 126 | elseif f then | ||
| 127 | local chunk = f:read(CHUNK_SIZE) | ||
| 128 | if chunk then | ||
| 129 | buffer = buffer:sub(pos_beg) .. chunk | ||
| 130 | pos_beg = 1 | ||
| 131 | else | ||
| 132 | f = nil | ||
| 133 | end | ||
| 134 | end | ||
| 135 | end | ||
| 136 | local posi = math.tointeger(pos) | ||
| 137 | if not posi then | ||
| 138 | posi = #buffer | ||
| 139 | elseif chars == '\r\n' then | ||
| 140 | posi = posi + 1 | ||
| 141 | end | ||
| 142 | local line = buffer:sub(pos_beg, posi) | ||
| 143 | pos_beg = posi + 1 | ||
| 144 | if #line > 0 then | ||
| 145 | return line | ||
| 146 | end | ||
| 147 | end | ||
| 148 | end | ||
| 149 | |||
| 150 | local function match_linerange(line) | ||
| 151 | local m1, m2, m3, m4 = line:match("^@@ %-(%d+),(%d+) %+(%d+),(%d+)") | ||
| 152 | if not m1 then m1, m3, m4 = line:match("^@@ %-(%d+) %+(%d+),(%d+)") end | ||
| 153 | if not m1 then m1, m2, m3 = line:match("^@@ %-(%d+),(%d+) %+(%d+)") end | ||
| 154 | if not m1 then m1, m3 = line:match("^@@ %-(%d+) %+(%d+)") end | ||
| 155 | return m1, m2, m3, m4 | ||
| 156 | end | ||
| 157 | |||
| 158 | local function match_epoch(str) | ||
| 159 | return str:match("[^0-9]1969[^0-9]") or str:match("[^0-9]1970[^0-9]") | ||
| 160 | end | ||
| 161 | |||
| 162 | function patch.read_patch(filename, data) | ||
| 163 | |||
| 164 | local state = 'header' | ||
| 165 | |||
| 166 | |||
| 167 | |||
| 168 | |||
| 169 | |||
| 170 | |||
| 171 | local all_ok = true | ||
| 172 | local lineends = { lf = 0, crlf = 0, cr = 0 } | ||
| 173 | local files = { source = {}, target = {}, epoch = {}, hunks = {}, fileends = {}, hunkends = {} } | ||
| 174 | local nextfileno = 0 | ||
| 175 | local nexthunkno = 0 | ||
| 176 | |||
| 177 | |||
| 178 | |||
| 179 | local hunkinfo = { | ||
| 180 | startsrc = nil, linessrc = nil, starttgt = nil, linestgt = nil, | ||
| 181 | invalid = false, text = {}, | ||
| 182 | } | ||
| 183 | local hunkactual = { linessrc = nil, linestgt = nil } | ||
| 184 | |||
| 185 | info(string.format("reading patch %s", filename)) | ||
| 186 | |||
| 187 | local fp | ||
| 188 | if data then | ||
| 189 | fp = string_as_file(data) | ||
| 190 | else | ||
| 191 | fp = filename == '-' and io.stdin or assert(io.open(filename, "rb")) | ||
| 192 | end | ||
| 193 | local lineno = 0 | ||
| 194 | |||
| 195 | for line in file_lines(fp) do | ||
| 196 | lineno = lineno + 1 | ||
| 197 | if state == 'header' then | ||
| 198 | if startswith(line, "--- ") then | ||
| 199 | state = 'filenames' | ||
| 200 | end | ||
| 201 | |||
| 202 | end | ||
| 203 | if state == 'hunkbody' then | ||
| 204 | |||
| 205 | |||
| 206 | if line:match("^[\r\n]*$") then | ||
| 207 | |||
| 208 | line = " " .. line | ||
| 209 | end | ||
| 210 | |||
| 211 | |||
| 212 | if line:match("^[- +\\]") then | ||
| 213 | |||
| 214 | local he = files.hunkends[nextfileno] | ||
| 215 | if endswith(line, "\r\n") then | ||
| 216 | he.crlf = he.crlf + 1 | ||
| 217 | elseif endswith(line, "\n") then | ||
| 218 | he.lf = he.lf + 1 | ||
| 219 | elseif endswith(line, "\r") then | ||
| 220 | he.cr = he.cr + 1 | ||
| 221 | end | ||
| 222 | if startswith(line, "-") then | ||
| 223 | hunkactual.linessrc = hunkactual.linessrc + 1 | ||
| 224 | elseif startswith(line, "+") then | ||
| 225 | hunkactual.linestgt = hunkactual.linestgt + 1 | ||
| 226 | elseif startswith(line, "\\") then | ||
| 227 | |||
| 228 | else | ||
| 229 | hunkactual.linessrc = hunkactual.linessrc + 1 | ||
| 230 | hunkactual.linestgt = hunkactual.linestgt + 1 | ||
| 231 | end | ||
| 232 | table.insert(hunkinfo.text, line) | ||
| 233 | |||
| 234 | else | ||
| 235 | warning(string.format("invalid hunk no.%d at %d for target file %s", | ||
| 236 | nexthunkno, lineno, files.target[nextfileno])) | ||
| 237 | |||
| 238 | table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) | ||
| 239 | files.hunks[nextfileno][nexthunkno].invalid = true | ||
| 240 | all_ok = false | ||
| 241 | state = 'hunkskip' | ||
| 242 | end | ||
| 243 | |||
| 244 | |||
| 245 | if hunkactual.linessrc > hunkinfo.linessrc or | ||
| 246 | hunkactual.linestgt > hunkinfo.linestgt then | ||
| 247 | |||
| 248 | warning(string.format("extra hunk no.%d lines at %d for target %s", | ||
| 249 | nexthunkno, lineno, files.target[nextfileno])) | ||
| 250 | |||
| 251 | table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) | ||
| 252 | files.hunks[nextfileno][nexthunkno].invalid = true | ||
| 253 | state = 'hunkskip' | ||
| 254 | elseif hunkinfo.linessrc == hunkactual.linessrc and | ||
| 255 | hunkinfo.linestgt == hunkactual.linestgt then | ||
| 256 | |||
| 257 | table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) | ||
| 258 | state = 'hunkskip' | ||
| 259 | |||
| 260 | |||
| 261 | local ends = files.hunkends[nextfileno] | ||
| 262 | if (ends.cr ~= 0 and 1 or 0) + (ends.crlf ~= 0 and 1 or 0) + | ||
| 263 | (ends.lf ~= 0 and 1 or 0) > 1 then | ||
| 264 | |||
| 265 | warning(string.format("inconsistent line ends in patch hunks for %s", | ||
| 266 | files.source[nextfileno])) | ||
| 267 | end | ||
| 268 | end | ||
| 269 | |||
| 270 | end | ||
| 271 | |||
| 272 | if state == 'hunkskip' then | ||
| 273 | if match_linerange(line) then | ||
| 274 | state = 'hunkhead' | ||
| 275 | elseif startswith(line, "--- ") then | ||
| 276 | state = 'filenames' | ||
| 277 | if debugmode and #files.source > 0 then | ||
| 278 | debug(string.format("- %2d hunks for %s", #files.hunks[nextfileno], | ||
| 279 | files.source[nextfileno])) | ||
| 280 | end | ||
| 281 | end | ||
| 282 | |||
| 283 | end | ||
| 284 | local advance | ||
| 285 | if state == 'filenames' then | ||
| 286 | if startswith(line, "--- ") then | ||
| 287 | if files.source[nextfileno] then | ||
| 288 | all_ok = false | ||
| 289 | warning(string.format("skipping invalid patch for %s", | ||
| 290 | files.source[nextfileno + 1])) | ||
| 291 | table.remove(files.source, nextfileno + 1) | ||
| 292 | |||
| 293 | |||
| 294 | end | ||
| 295 | |||
| 296 | |||
| 297 | |||
| 298 | local match, rest = line:match("^%-%-%- ([^ \t\r\n]+)(.*)") | ||
| 299 | if not match then | ||
| 300 | all_ok = false | ||
| 301 | warning(string.format("skipping invalid filename at line %d", lineno + 1)) | ||
| 302 | state = 'header' | ||
| 303 | else | ||
| 304 | if match_epoch(rest) then | ||
| 305 | files.epoch[nextfileno + 1] = true | ||
| 306 | end | ||
| 307 | table.insert(files.source, match) | ||
| 308 | end | ||
| 309 | elseif not startswith(line, "+++ ") then | ||
| 310 | if files.source[nextfileno] then | ||
| 311 | all_ok = false | ||
| 312 | warning(string.format("skipping invalid patch with no target for %s", | ||
| 313 | files.source[nextfileno + 1])) | ||
| 314 | table.remove(files.source, nextfileno + 1) | ||
| 315 | else | ||
| 316 | |||
| 317 | warning("skipping invalid target patch") | ||
| 318 | end | ||
| 319 | state = 'header' | ||
| 320 | else | ||
| 321 | if files.target[nextfileno] then | ||
| 322 | all_ok = false | ||
| 323 | warning(string.format("skipping invalid patch - double target at line %d", | ||
| 324 | lineno + 1)) | ||
| 325 | table.remove(files.source, nextfileno + 1) | ||
| 326 | table.remove(files.target, nextfileno + 1) | ||
| 327 | nextfileno = nextfileno - 1 | ||
| 328 | |||
| 329 | |||
| 330 | state = 'header' | ||
| 331 | else | ||
| 332 | |||
| 333 | |||
| 334 | |||
| 335 | local re_filename = "^%+%+%+ ([^ \t\r\n]+)(.*)$" | ||
| 336 | local match, rest = line:match(re_filename) | ||
| 337 | if not match then | ||
| 338 | all_ok = false | ||
| 339 | warning(string.format( | ||
| 340 | "skipping invalid patch - no target filename at line %d", | ||
| 341 | lineno + 1)) | ||
| 342 | state = 'header' | ||
| 343 | else | ||
| 344 | table.insert(files.target, match) | ||
| 345 | nextfileno = nextfileno + 1 | ||
| 346 | if match_epoch(rest) then | ||
| 347 | files.epoch[nextfileno] = true | ||
| 348 | end | ||
| 349 | nexthunkno = 0 | ||
| 350 | table.insert(files.hunks, {}) | ||
| 351 | table.insert(files.hunkends, table_copy(lineends)) | ||
| 352 | table.insert(files.fileends, table_copy(lineends)) | ||
| 353 | state = 'hunkhead' | ||
| 354 | advance = true | ||
| 355 | end | ||
| 356 | end | ||
| 357 | end | ||
| 358 | |||
| 359 | end | ||
| 360 | if not advance and state == 'hunkhead' then | ||
| 361 | local m1, m2, m3, m4 = match_linerange(line) | ||
| 362 | if not m1 then | ||
| 363 | if not files.hunks[nextfileno - 1] then | ||
| 364 | all_ok = false | ||
| 365 | warning(string.format("skipping invalid patch with no hunks for file %s", | ||
| 366 | files.target[nextfileno])) | ||
| 367 | end | ||
| 368 | state = 'header' | ||
| 369 | else | ||
| 370 | hunkinfo.startsrc = math.tointeger(m1) | ||
| 371 | hunkinfo.linessrc = math.tointeger(m2) or 1 | ||
| 372 | hunkinfo.starttgt = math.tointeger(m3) | ||
| 373 | hunkinfo.linestgt = math.tointeger(m4) or 1 | ||
| 374 | hunkinfo.invalid = false | ||
| 375 | hunkinfo.text = {} | ||
| 376 | |||
| 377 | hunkactual.linessrc = 0 | ||
| 378 | hunkactual.linestgt = 0 | ||
| 379 | |||
| 380 | state = 'hunkbody' | ||
| 381 | nexthunkno = nexthunkno + 1 | ||
| 382 | end | ||
| 383 | |||
| 384 | end | ||
| 385 | end | ||
| 386 | if state ~= 'hunkskip' then | ||
| 387 | warning(string.format("patch file incomplete - %s", filename)) | ||
| 388 | all_ok = false | ||
| 389 | |||
| 390 | else | ||
| 391 | |||
| 392 | if debugmode and #files.source > 0 then | ||
| 393 | debug(string.format("- %2d hunks for %s", #files.hunks[nextfileno], | ||
| 394 | files.source[nextfileno])) | ||
| 395 | end | ||
| 396 | end | ||
| 397 | |||
| 398 | local sum = 0; for _, hset in ipairs(files.hunks) do sum = sum + #hset end | ||
| 399 | info(string.format("total files: %d total hunks: %d", #files.source, sum)) | ||
| 400 | fp:close() | ||
| 401 | return files, all_ok | ||
| 402 | end | ||
| 403 | |||
| 404 | local function find_hunk(file, h, hno) | ||
| 405 | for fuzz = 0, 2 do | ||
| 406 | local lineno = h.startsrc | ||
| 407 | for i = 0, #file do | ||
| 408 | local found = true | ||
| 409 | local location = lineno | ||
| 410 | for l, hline in ipairs(h.text) do | ||
| 411 | if l > fuzz then | ||
| 412 | |||
| 413 | if startswith(hline, " ") or startswith(hline, "-") then | ||
| 414 | local line = file[lineno] | ||
| 415 | lineno = lineno + 1 | ||
| 416 | if not line or #line == 0 then | ||
| 417 | found = false | ||
| 418 | break | ||
| 419 | end | ||
| 420 | if endlstrip(line) ~= endlstrip(hline:sub(2)) then | ||
| 421 | found = false | ||
| 422 | break | ||
| 423 | end | ||
| 424 | end | ||
| 425 | end | ||
| 426 | end | ||
| 427 | if found then | ||
| 428 | local offset = location - h.startsrc - fuzz | ||
| 429 | if offset ~= 0 then | ||
| 430 | warning(string.format("Hunk %d found at offset %d%s...", hno, offset, fuzz == 0 and "" or string.format(" (fuzz %d)", fuzz))) | ||
| 431 | end | ||
| 432 | h.startsrc = location | ||
| 433 | h.starttgt = h.starttgt + offset | ||
| 434 | for _ = 1, fuzz do | ||
| 435 | table.remove(h.text, 1) | ||
| 436 | table.remove(h.text, #h.text) | ||
| 437 | end | ||
| 438 | return true | ||
| 439 | end | ||
| 440 | lineno = i | ||
| 441 | end | ||
| 442 | end | ||
| 443 | return false | ||
| 444 | end | ||
| 445 | |||
| 446 | local function load_file(filename) | ||
| 447 | local fp = assert(io.open(filename)) | ||
| 448 | local file = {} | ||
| 449 | local readline = file_lines(fp) | ||
| 450 | while true do | ||
| 451 | local line = readline() | ||
| 452 | if not line then break end | ||
| 453 | table.insert(file, line) | ||
| 454 | end | ||
| 455 | fp:close() | ||
| 456 | return file | ||
| 457 | end | ||
| 458 | |||
| 459 | local function find_hunks(file, hunks) | ||
| 460 | for hno, h in ipairs(hunks) do | ||
| 461 | find_hunk(file, h, hno) | ||
| 462 | end | ||
| 463 | end | ||
| 464 | |||
| 465 | local function check_patched(file, hunks) | ||
| 466 | local lineno = 1 | ||
| 467 | local _, err = pcall(function() | ||
| 468 | if #file == 0 then | ||
| 469 | error('nomatch', 0) | ||
| 470 | end | ||
| 471 | for hno, h in ipairs(hunks) do | ||
| 472 | |||
| 473 | if #file < h.starttgt then | ||
| 474 | error('nomatch', 0) | ||
| 475 | end | ||
| 476 | lineno = h.starttgt | ||
| 477 | for _, hline in ipairs(h.text) do | ||
| 478 | |||
| 479 | if not startswith(hline, "-") and not startswith(hline, "\\") then | ||
| 480 | local line = file[lineno] | ||
| 481 | lineno = lineno + 1 | ||
| 482 | if #line == 0 then | ||
| 483 | error('nomatch', 0) | ||
| 484 | end | ||
| 485 | if endlstrip(line) ~= endlstrip(hline:sub(2)) then | ||
| 486 | warning(string.format("file is not patched - failed hunk: %d", hno)) | ||
| 487 | error('nomatch', 0) | ||
| 488 | end | ||
| 489 | end | ||
| 490 | end | ||
| 491 | end | ||
| 492 | end) | ||
| 493 | |||
| 494 | return err ~= 'nomatch' | ||
| 495 | end | ||
| 496 | |||
| 497 | local function patch_hunks(srcname, tgtname, hunks) | ||
| 498 | local src = assert(io.open(srcname, "rb")) | ||
| 499 | local tgt = assert(io.open(tgtname, "wb")) | ||
| 500 | |||
| 501 | local src_readline = file_lines(src) | ||
| 502 | |||
| 503 | |||
| 504 | |||
| 505 | |||
| 506 | |||
| 507 | |||
| 508 | |||
| 509 | local srclineno = 1 | ||
| 510 | local lineends = { ['\n'] = 0, ['\r\n'] = 0, ['\r'] = 0 } | ||
| 511 | for hno, h in ipairs(hunks) do | ||
| 512 | debug(string.format("processing hunk %d for file %s", hno, tgtname)) | ||
| 513 | |||
| 514 | while srclineno < h.startsrc do | ||
| 515 | local line = src_readline() | ||
| 516 | |||
| 517 | if endswith(line, "\r\n") then | ||
| 518 | lineends["\r\n"] = lineends["\r\n"] + 1 | ||
| 519 | elseif endswith(line, "\n") then | ||
| 520 | lineends["\n"] = lineends["\n"] + 1 | ||
| 521 | elseif endswith(line, "\r") then | ||
| 522 | lineends["\r"] = lineends["\r"] + 1 | ||
| 523 | end | ||
| 524 | tgt:write(line) | ||
| 525 | srclineno = srclineno + 1 | ||
| 526 | end | ||
| 527 | |||
| 528 | for _, hline in ipairs(h.text) do | ||
| 529 | |||
| 530 | if startswith(hline, "-") or startswith(hline, "\\") then | ||
| 531 | src_readline() | ||
| 532 | srclineno = srclineno + 1 | ||
| 533 | else | ||
| 534 | if not startswith(hline, "+") then | ||
| 535 | src_readline() | ||
| 536 | srclineno = srclineno + 1 | ||
| 537 | end | ||
| 538 | local line2write = hline:sub(2) | ||
| 539 | |||
| 540 | local sum = 0 | ||
| 541 | for _, v in pairs(lineends) do if v > 0 then sum = sum + 1 end end | ||
| 542 | if sum == 1 then | ||
| 543 | local newline | ||
| 544 | for k, v in pairs(lineends) do if v ~= 0 then newline = k end end | ||
| 545 | tgt:write(endlstrip(line2write) .. newline) | ||
| 546 | else | ||
| 547 | tgt:write(line2write) | ||
| 548 | end | ||
| 549 | end | ||
| 550 | end | ||
| 551 | end | ||
| 552 | for line in src_readline do | ||
| 553 | tgt:write(line) | ||
| 554 | end | ||
| 555 | tgt:close() | ||
| 556 | src:close() | ||
| 557 | return true | ||
| 558 | end | ||
| 559 | |||
| 560 | local function strip_dirs(filename, strip) | ||
| 561 | if strip == nil then return filename end | ||
| 562 | for _ = 1, strip do | ||
| 563 | filename = filename:gsub("^[^/]*/", "") | ||
| 564 | end | ||
| 565 | return filename | ||
| 566 | end | ||
| 567 | |||
| 568 | local function write_new_file(filename, hunk) | ||
| 569 | local fh = io.open(filename, "wb") | ||
| 570 | if not fh then return false end | ||
| 571 | for _, hline in ipairs(hunk.text) do | ||
| 572 | local c = hline:sub(1, 1) | ||
| 573 | if c ~= "+" and c ~= "-" and c ~= " " then | ||
| 574 | return false, "malformed patch" | ||
| 575 | end | ||
| 576 | fh:write(hline:sub(2)) | ||
| 577 | end | ||
| 578 | fh:close() | ||
| 579 | return true | ||
| 580 | end | ||
| 581 | |||
| 582 | local function patch_file(source, target, epoch, hunks, strip, create_delete) | ||
| 583 | local create_file = false | ||
| 584 | if create_delete then | ||
| 585 | local is_src_epoch = epoch and #hunks == 1 and hunks[1].startsrc == 0 and hunks[1].linessrc == 0 | ||
| 586 | if is_src_epoch or source == "/dev/null" then | ||
| 587 | info(string.format("will create %s", target)) | ||
| 588 | create_file = true | ||
| 589 | end | ||
| 590 | end | ||
| 591 | if create_file then | ||
| 592 | return write_new_file(fs.absolute_name(strip_dirs(target, strip)), hunks[1]) | ||
| 593 | end | ||
| 594 | source = strip_dirs(source, strip) | ||
| 595 | local f2patch = source | ||
| 596 | if not exists(f2patch) then | ||
| 597 | f2patch = strip_dirs(target, strip) | ||
| 598 | f2patch = fs.absolute_name(f2patch) | ||
| 599 | if not exists(f2patch) then | ||
| 600 | warning(string.format("source/target file does not exist\n--- %s\n+++ %s", | ||
| 601 | source, f2patch)) | ||
| 602 | return false | ||
| 603 | end | ||
| 604 | end | ||
| 605 | |||
| 606 | if not isfile() then | ||
| 607 | warning(string.format("not a file - %s", f2patch)) | ||
| 608 | return false | ||
| 609 | end | ||
| 610 | |||
| 611 | source = f2patch | ||
| 612 | |||
| 613 | |||
| 614 | local file = load_file(source) | ||
| 615 | local hunkno = 1 | ||
| 616 | local hunk = hunks[hunkno] | ||
| 617 | local hunkfind = {} | ||
| 618 | local validhunks = 0 | ||
| 619 | local canpatch = false | ||
| 620 | local hunklineno | ||
| 621 | if not file then | ||
| 622 | return nil, "failed reading file " .. source | ||
| 623 | end | ||
| 624 | |||
| 625 | if create_delete then | ||
| 626 | if epoch and #hunks == 1 and hunks[1].starttgt == 0 and hunks[1].linestgt == 0 then | ||
| 627 | local ok = os.remove(source) | ||
| 628 | if not ok then | ||
| 629 | return false | ||
| 630 | end | ||
| 631 | info(string.format("successfully removed %s", source)) | ||
| 632 | return true | ||
| 633 | end | ||
| 634 | end | ||
| 635 | |||
| 636 | find_hunks(file, hunks) | ||
| 637 | |||
| 638 | local function process_line(line, lineno) | ||
| 639 | if not hunk or lineno < hunk.startsrc then | ||
| 640 | return false | ||
| 641 | end | ||
| 642 | if lineno == hunk.startsrc then | ||
| 643 | hunkfind = {} | ||
| 644 | for _, x in ipairs(hunk.text) do | ||
| 645 | if x:sub(1, 1) == ' ' or x:sub(1, 1) == '-' then | ||
| 646 | hunkfind[#hunkfind + 1] = endlstrip(x:sub(2)) | ||
| 647 | end | ||
| 648 | end | ||
| 649 | hunklineno = 1 | ||
| 650 | |||
| 651 | |||
| 652 | end | ||
| 653 | |||
| 654 | if lineno < hunk.startsrc + #hunkfind - 1 then | ||
| 655 | if endlstrip(line) == hunkfind[hunklineno] then | ||
| 656 | hunklineno = hunklineno + 1 | ||
| 657 | else | ||
| 658 | debug(string.format("hunk no.%d doesn't match source file %s", | ||
| 659 | hunkno, source)) | ||
| 660 | |||
| 661 | hunkno = hunkno + 1 | ||
| 662 | if hunkno <= #hunks then | ||
| 663 | hunk = hunks[hunkno] | ||
| 664 | return false | ||
| 665 | else | ||
| 666 | return true | ||
| 667 | end | ||
| 668 | end | ||
| 669 | end | ||
| 670 | |||
| 671 | if lineno == hunk.startsrc + #hunkfind - 1 then | ||
| 672 | debug(string.format("file %s hunk no.%d -- is ready to be patched", | ||
| 673 | source, hunkno)) | ||
| 674 | hunkno = hunkno + 1 | ||
| 675 | validhunks = validhunks + 1 | ||
| 676 | if hunkno <= #hunks then | ||
| 677 | hunk = hunks[hunkno] | ||
| 678 | else | ||
| 679 | if validhunks == #hunks then | ||
| 680 | |||
| 681 | canpatch = true | ||
| 682 | return true | ||
| 683 | end | ||
| 684 | end | ||
| 685 | end | ||
| 686 | return false | ||
| 687 | end | ||
| 688 | |||
| 689 | local done = false | ||
| 690 | for lineno, line in ipairs(file) do | ||
| 691 | done = process_line(line, lineno) | ||
| 692 | if done then | ||
| 693 | break | ||
| 694 | end | ||
| 695 | end | ||
| 696 | if not done then | ||
| 697 | if hunkno <= #hunks and not create_file then | ||
| 698 | warning(string.format("premature end of source file %s at hunk %d", | ||
| 699 | source, hunkno)) | ||
| 700 | return false | ||
| 701 | end | ||
| 702 | end | ||
| 703 | if validhunks < #hunks then | ||
| 704 | if check_patched(file, hunks) then | ||
| 705 | warning(string.format("already patched %s", source)) | ||
| 706 | elseif not create_file then | ||
| 707 | warning(string.format("source file is different - %s", source)) | ||
| 708 | return false | ||
| 709 | end | ||
| 710 | end | ||
| 711 | if not canpatch then | ||
| 712 | return true | ||
| 713 | end | ||
| 714 | local backupname = source .. ".orig" | ||
| 715 | if exists(backupname) then | ||
| 716 | warning(string.format("can't backup original file to %s - aborting", | ||
| 717 | backupname)) | ||
| 718 | return false | ||
| 719 | end | ||
| 720 | local ok = os.rename(source, backupname) | ||
| 721 | if not ok then | ||
| 722 | warning(string.format("failed backing up %s when patching", source)) | ||
| 723 | return false | ||
| 724 | end | ||
| 725 | patch_hunks(backupname, source, hunks) | ||
| 726 | info(string.format("successfully patched %s", source)) | ||
| 727 | os.remove(backupname) | ||
| 728 | return true | ||
| 729 | end | ||
| 730 | |||
| 731 | function patch.apply_patch(the_patch, strip, create_delete) | ||
| 732 | local all_ok = true | ||
| 733 | local total = #the_patch.source | ||
| 734 | for fileno, source in ipairs(the_patch.source) do | ||
| 735 | local target = the_patch.target[fileno] | ||
| 736 | local hunks = the_patch.hunks[fileno] | ||
| 737 | local epoch = the_patch.epoch[fileno] | ||
| 738 | info(string.format("processing %d/%d:\t %s", fileno, total, source)) | ||
| 739 | local ok = patch_file(source, target, epoch, hunks, strip, create_delete) | ||
| 740 | all_ok = all_ok and ok | ||
| 741 | end | ||
| 742 | |||
| 743 | return all_ok | ||
| 744 | end | ||
| 745 | |||
| 746 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | local tar = {Header = {}, } | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | local fs = require("luarocks.fs") | ||
| 25 | local dir = require("luarocks.dir") | ||
| 26 | local fun = require("luarocks.fun") | ||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | local blocksize = 512 | ||
| 31 | |||
| 32 | local function get_typeflag(flag) | ||
| 33 | if flag == "0" or flag == "\0" then return "file" | ||
| 34 | elseif flag == "1" then return "link" | ||
| 35 | elseif flag == "2" then return "symlink" | ||
| 36 | elseif flag == "3" then return "character" | ||
| 37 | elseif flag == "4" then return "block" | ||
| 38 | elseif flag == "5" then return "directory" | ||
| 39 | elseif flag == "6" then return "fifo" | ||
| 40 | elseif flag == "7" then return "contiguous" | ||
| 41 | elseif flag == "x" then return "next file" | ||
| 42 | elseif flag == "g" then return "global extended header" | ||
| 43 | elseif flag == "L" then return "long name" | ||
| 44 | elseif flag == "K" then return "long link name" | ||
| 45 | end | ||
| 46 | return "unknown" | ||
| 47 | end | ||
| 48 | |||
| 49 | local function octal_to_number(octal) | ||
| 50 | local exp = 0 | ||
| 51 | local number = 0 | ||
| 52 | octal = octal:gsub("%s", "") | ||
| 53 | for i = #octal, 1, -1 do | ||
| 54 | local digit = math.tointeger(octal:sub(i, i)) | ||
| 55 | if not digit then | ||
| 56 | break | ||
| 57 | end | ||
| 58 | number = number + (digit * math.tointeger(8 ^ exp)) | ||
| 59 | exp = exp + 1 | ||
| 60 | end | ||
| 61 | return number | ||
| 62 | end | ||
| 63 | |||
| 64 | local function checksum_header(block) | ||
| 65 | local sum = 256 | ||
| 66 | |||
| 67 | if block:byte(1) == 0 then | ||
| 68 | return 0 | ||
| 69 | end | ||
| 70 | |||
| 71 | for i = 1, 148 do | ||
| 72 | local b = block:byte(i) or 0 | ||
| 73 | sum = sum + b | ||
| 74 | end | ||
| 75 | for i = 157, 500 do | ||
| 76 | local b = block:byte(i) or 0 | ||
| 77 | sum = sum + b | ||
| 78 | end | ||
| 79 | |||
| 80 | return sum | ||
| 81 | end | ||
| 82 | |||
| 83 | local function nullterm(s) | ||
| 84 | return s:match("^[^%z]*") | ||
| 85 | end | ||
| 86 | |||
| 87 | local function read_header_block(block) | ||
| 88 | local header = {} | ||
| 89 | header.name = nullterm(block:sub(1, 100)) | ||
| 90 | header.mode = nullterm(block:sub(101, 108)):gsub(" ", "") | ||
| 91 | header.uid = octal_to_number(nullterm(block:sub(109, 116))) | ||
| 92 | header.gid = octal_to_number(nullterm(block:sub(117, 124))) | ||
| 93 | header.size = octal_to_number(nullterm(block:sub(125, 136))) | ||
| 94 | header.mtime = octal_to_number(nullterm(block:sub(137, 148))) | ||
| 95 | header.chksum = octal_to_number(nullterm(block:sub(149, 156))) | ||
| 96 | header.typeflag = get_typeflag(block:sub(157, 157)) | ||
| 97 | header.linkname = nullterm(block:sub(158, 257)) | ||
| 98 | header.magic = block:sub(258, 263) | ||
| 99 | header.version = block:sub(264, 265) | ||
| 100 | header.uname = nullterm(block:sub(266, 297)) | ||
| 101 | header.gname = nullterm(block:sub(298, 329)) | ||
| 102 | header.devmajor = octal_to_number(nullterm(block:sub(330, 337))) | ||
| 103 | header.devminor = octal_to_number(nullterm(block:sub(338, 345))) | ||
| 104 | header.prefix = block:sub(346, 500) | ||
| 105 | |||
| 106 | |||
| 107 | |||
| 108 | |||
| 109 | |||
| 110 | |||
| 111 | |||
| 112 | if header.typeflag == "unknown" then | ||
| 113 | if checksum_header(block) ~= header.chksum then | ||
| 114 | return false, "Failed header checksum" | ||
| 115 | end | ||
| 116 | end | ||
| 117 | return header | ||
| 118 | end | ||
| 119 | |||
| 120 | function tar.untar(filename, destdir) | ||
| 121 | |||
| 122 | local tar_handle = io.open(filename, "rb") | ||
| 123 | if not tar_handle then return nil, "Error opening file " .. filename end | ||
| 124 | |||
| 125 | local long_name, long_link_name | ||
| 126 | local ok, err | ||
| 127 | local make_dir = fun.memoize(fs.make_dir) | ||
| 128 | while true do | ||
| 129 | local block | ||
| 130 | repeat | ||
| 131 | block = tar_handle:read(blocksize) | ||
| 132 | until (not block) or block:byte(1) > 0 | ||
| 133 | if not block then break end | ||
| 134 | if #block < blocksize then | ||
| 135 | ok, err = nil, "Invalid block size -- corrupted file?" | ||
| 136 | break | ||
| 137 | end | ||
| 138 | |||
| 139 | local headerp | ||
| 140 | headerp, err = read_header_block(block) | ||
| 141 | if not headerp then | ||
| 142 | ok = false | ||
| 143 | break | ||
| 144 | end | ||
| 145 | local header = headerp | ||
| 146 | local file_data = "" | ||
| 147 | if header.size > 0 then | ||
| 148 | local nread = math.ceil(header.size / blocksize) * blocksize | ||
| 149 | file_data = tar_handle:read(header.size) | ||
| 150 | if nread > header.size then | ||
| 151 | tar_handle:seek("cur", nread - header.size) | ||
| 152 | end | ||
| 153 | end | ||
| 154 | |||
| 155 | if header.typeflag == "long name" then | ||
| 156 | long_name = nullterm(file_data) | ||
| 157 | elseif header.typeflag == "long link name" then | ||
| 158 | long_link_name = nullterm(file_data) | ||
| 159 | else | ||
| 160 | if long_name then | ||
| 161 | header.name = long_name | ||
| 162 | long_name = nil | ||
| 163 | end | ||
| 164 | if long_link_name then | ||
| 165 | header.name = long_link_name | ||
| 166 | long_link_name = nil | ||
| 167 | end | ||
| 168 | end | ||
| 169 | local pathname = dir.path(destdir, header.name) | ||
| 170 | pathname = fs.absolute_name(pathname) | ||
| 171 | if header.typeflag == "directory" then | ||
| 172 | ok, err = make_dir(pathname) | ||
| 173 | if not ok then | ||
| 174 | break | ||
| 175 | end | ||
| 176 | elseif header.typeflag == "file" then | ||
| 177 | local dirname = dir.dir_name(pathname) | ||
| 178 | if dirname ~= "" then | ||
| 179 | ok, err = make_dir(dirname) | ||
| 180 | if not ok then | ||
| 181 | break | ||
| 182 | end | ||
| 183 | end | ||
| 184 | local file_handle | ||
| 185 | file_handle, err = io.open(pathname, "wb") | ||
| 186 | if not file_handle then | ||
| 187 | ok = nil | ||
| 188 | break | ||
| 189 | end | ||
| 190 | file_handle:write(file_data) | ||
| 191 | file_handle:close() | ||
| 192 | fs.set_time(pathname, header.mtime) | ||
| 193 | if header.mode:match("[75]") then | ||
| 194 | fs.set_permissions(pathname, "exec", "all") | ||
| 195 | else | ||
| 196 | fs.set_permissions(pathname, "read", "all") | ||
| 197 | end | ||
| 198 | end | ||
| 199 | |||
| 200 | |||
| 201 | |||
| 202 | |||
| 203 | |||
| 204 | |||
| 205 | end | ||
| 206 | tar_handle:close() | ||
| 207 | return ok, err | ||
| 208 | end | ||
| 209 | |||
| 210 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | local zip = {ZipHandle = {}, LocalFileHeader = {}, Zip = {}, } | ||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | |||
| 49 | local zlib = require("zlib") | ||
| 50 | local fs = require("luarocks.fs") | ||
| 51 | local fun = require("luarocks.fun") | ||
| 52 | local dir = require("luarocks.dir") | ||
| 53 | |||
| 54 | |||
| 55 | |||
| 56 | |||
| 57 | |||
| 58 | local function shr(n, m) | ||
| 59 | return math.floor(n / 2 ^ m) | ||
| 60 | end | ||
| 61 | |||
| 62 | local function shl(n, m) | ||
| 63 | return (n * 2 ^ m) | ||
| 64 | end | ||
| 65 | |||
| 66 | local function lowbits(n, m) | ||
| 67 | return (n % 2 ^ m) | ||
| 68 | end | ||
| 69 | |||
| 70 | local function mode_to_windowbits(mode) | ||
| 71 | if mode == "gzip" then | ||
| 72 | return 31 | ||
| 73 | elseif mode == "zlib" then | ||
| 74 | return 0 | ||
| 75 | elseif mode == "raw" then | ||
| 76 | return -15 | ||
| 77 | end | ||
| 78 | end | ||
| 79 | |||
| 80 | |||
| 81 | |||
| 82 | local zlib_compress | ||
| 83 | local zlib_uncompress | ||
| 84 | local zlib_crc32 | ||
| 85 | if zlib._VERSION:match("^lua%-zlib") then | ||
| 86 | function zlib_compress(data, mode) | ||
| 87 | return (zlib.deflate(6, mode_to_windowbits(mode))(data, "finish")) | ||
| 88 | end | ||
| 89 | |||
| 90 | function zlib_uncompress(data, mode) | ||
| 91 | return (zlib.inflate(mode_to_windowbits(mode))(data)) | ||
| 92 | end | ||
| 93 | |||
| 94 | function zlib_crc32(data) | ||
| 95 | return zlib.crc32()(data) | ||
| 96 | end | ||
| 97 | elseif zlib._VERSION:match("^lzlib") then | ||
| 98 | function zlib_compress(data, mode) | ||
| 99 | return zlib.compress(data, -1, nil, mode_to_windowbits(mode)) | ||
| 100 | end | ||
| 101 | |||
| 102 | function zlib_uncompress(data, mode) | ||
| 103 | return zlib.decompress(data, mode_to_windowbits(mode)) | ||
| 104 | end | ||
| 105 | |||
| 106 | function zlib_crc32(data) | ||
| 107 | return zlib.crc32(zlib.crc32(), data) | ||
| 108 | end | ||
| 109 | else | ||
| 110 | error("unknown zlib library", 0) | ||
| 111 | end | ||
| 112 | |||
| 113 | local function number_to_lestring(number, nbytes) | ||
| 114 | local out = {} | ||
| 115 | for _ = 1, nbytes do | ||
| 116 | local byte = number % 256 | ||
| 117 | table.insert(out, string.char(byte)) | ||
| 118 | number = (number - byte) / 256 | ||
| 119 | end | ||
| 120 | return table.concat(out) | ||
| 121 | end | ||
| 122 | |||
| 123 | local function lestring_to_number(str) | ||
| 124 | local n = 0 | ||
| 125 | local bytes = { string.byte(str, 1, #str) } | ||
| 126 | for b = 1, #str do | ||
| 127 | n = n + shl(bytes[b], (b - 1) * 8) | ||
| 128 | end | ||
| 129 | return math.floor(n) | ||
| 130 | end | ||
| 131 | |||
| 132 | local LOCAL_FILE_HEADER_SIGNATURE = number_to_lestring(0x04034b50, 4) | ||
| 133 | local DATA_DESCRIPTOR_SIGNATURE = number_to_lestring(0x08074b50, 4) | ||
| 134 | local CENTRAL_DIRECTORY_SIGNATURE = number_to_lestring(0x02014b50, 4) | ||
| 135 | local END_OF_CENTRAL_DIR_SIGNATURE = number_to_lestring(0x06054b50, 4) | ||
| 136 | |||
| 137 | |||
| 138 | |||
| 139 | |||
| 140 | |||
| 141 | local function zipwriter_open_new_file_in_zip(self, filename) | ||
| 142 | if self.in_open_file then | ||
| 143 | self:close_file_in_zip() | ||
| 144 | return nil | ||
| 145 | end | ||
| 146 | local lfh = {} | ||
| 147 | self.local_file_header = lfh | ||
| 148 | lfh.last_mod_file_time = 0 | ||
| 149 | lfh.last_mod_file_date = 0 | ||
| 150 | lfh.file_name_length = #filename | ||
| 151 | lfh.extra_field_length = 0 | ||
| 152 | lfh.file_name = filename:gsub("\\", "/") | ||
| 153 | lfh.external_attr = shl(493, 16) | ||
| 154 | self.in_open_file = true | ||
| 155 | return true | ||
| 156 | end | ||
| 157 | |||
| 158 | |||
| 159 | |||
| 160 | |||
| 161 | |||
| 162 | local function zipwriter_write_file_in_zip(self, data) | ||
| 163 | if not self.in_open_file then | ||
| 164 | return nil | ||
| 165 | end | ||
| 166 | local lfh = self.local_file_header | ||
| 167 | local compressed = zlib_compress(data, "raw") | ||
| 168 | lfh.crc32 = zlib_crc32(data) | ||
| 169 | lfh.compressed_size = #compressed | ||
| 170 | lfh.uncompressed_size = #data | ||
| 171 | self.data = compressed | ||
| 172 | return true | ||
| 173 | end | ||
| 174 | |||
| 175 | |||
| 176 | |||
| 177 | |||
| 178 | local function zipwriter_close_file_in_zip(self) | ||
| 179 | local zh = self.ZipHandle | ||
| 180 | |||
| 181 | if not self.in_open_file then | ||
| 182 | return nil | ||
| 183 | end | ||
| 184 | |||
| 185 | |||
| 186 | local lfh = self.local_file_header | ||
| 187 | lfh.offset = zh:seek() | ||
| 188 | zh:write(LOCAL_FILE_HEADER_SIGNATURE) | ||
| 189 | zh:write(number_to_lestring(20, 2)) | ||
| 190 | zh:write(number_to_lestring(4, 2)) | ||
| 191 | zh:write(number_to_lestring(8, 2)) | ||
| 192 | zh:write(number_to_lestring(lfh.last_mod_file_time, 2)) | ||
| 193 | zh:write(number_to_lestring(lfh.last_mod_file_date, 2)) | ||
| 194 | zh:write(number_to_lestring(lfh.crc32, 4)) | ||
| 195 | zh:write(number_to_lestring(lfh.compressed_size, 4)) | ||
| 196 | zh:write(number_to_lestring(lfh.uncompressed_size, 4)) | ||
| 197 | zh:write(number_to_lestring(lfh.file_name_length, 2)) | ||
| 198 | zh:write(number_to_lestring(lfh.extra_field_length, 2)) | ||
| 199 | zh:write(lfh.file_name) | ||
| 200 | |||
| 201 | |||
| 202 | zh:write(self.data) | ||
| 203 | |||
| 204 | |||
| 205 | zh:write(DATA_DESCRIPTOR_SIGNATURE) | ||
| 206 | zh:write(number_to_lestring(lfh.crc32, 4)) | ||
| 207 | zh:write(number_to_lestring(lfh.compressed_size, 4)) | ||
| 208 | zh:write(number_to_lestring(lfh.uncompressed_size, 4)) | ||
| 209 | |||
| 210 | table.insert(self.files, lfh) | ||
| 211 | self.in_open_file = false | ||
| 212 | |||
| 213 | return true | ||
| 214 | end | ||
| 215 | |||
| 216 | |||
| 217 | |||
| 218 | local function zipwriter_add(self, file) | ||
| 219 | local fin | ||
| 220 | local ok, err = self:open_new_file_in_zip(file) | ||
| 221 | if not ok then | ||
| 222 | err = "error in opening " .. file .. " in zipfile" | ||
| 223 | else | ||
| 224 | fin = io.open(fs.absolute_name(file), "rb") | ||
| 225 | if not fin then | ||
| 226 | ok = false | ||
| 227 | err = "error opening " .. file .. " for reading" | ||
| 228 | end | ||
| 229 | end | ||
| 230 | if ok then | ||
| 231 | local data = fin:read("*a") | ||
| 232 | if not data then | ||
| 233 | err = "error reading " .. file | ||
| 234 | ok = false | ||
| 235 | else | ||
| 236 | ok = self:write_file_in_zip(data) | ||
| 237 | if not ok then | ||
| 238 | err = "error in writing " .. file .. " in the zipfile" | ||
| 239 | end | ||
| 240 | end | ||
| 241 | end | ||
| 242 | if fin then | ||
| 243 | fin:close() | ||
| 244 | end | ||
| 245 | if ok then | ||
| 246 | ok = self:close_file_in_zip() | ||
| 247 | if not ok then | ||
| 248 | err = "error in writing " .. file .. " in the zipfile" | ||
| 249 | end | ||
| 250 | end | ||
| 251 | return ok == true, err | ||
| 252 | end | ||
| 253 | |||
| 254 | |||
| 255 | |||
| 256 | |||
| 257 | local function zipwriter_close(self) | ||
| 258 | local zh = self.ZipHandle | ||
| 259 | |||
| 260 | local central_directory_offset = zh:seek() | ||
| 261 | |||
| 262 | local size_of_central_directory = 0 | ||
| 263 | |||
| 264 | for _, lfh in ipairs(self.files) do | ||
| 265 | zh:write(CENTRAL_DIRECTORY_SIGNATURE) | ||
| 266 | zh:write(number_to_lestring(3, 2)) | ||
| 267 | zh:write(number_to_lestring(20, 2)) | ||
| 268 | zh:write(number_to_lestring(0, 2)) | ||
| 269 | zh:write(number_to_lestring(8, 2)) | ||
| 270 | zh:write(number_to_lestring(lfh.last_mod_file_time, 2)) | ||
| 271 | zh:write(number_to_lestring(lfh.last_mod_file_date, 2)) | ||
| 272 | zh:write(number_to_lestring(lfh.crc32, 4)) | ||
| 273 | zh:write(number_to_lestring(lfh.compressed_size, 4)) | ||
| 274 | zh:write(number_to_lestring(lfh.uncompressed_size, 4)) | ||
| 275 | zh:write(number_to_lestring(lfh.file_name_length, 2)) | ||
| 276 | zh:write(number_to_lestring(lfh.extra_field_length, 2)) | ||
| 277 | zh:write(number_to_lestring(0, 2)) | ||
| 278 | zh:write(number_to_lestring(0, 2)) | ||
| 279 | zh:write(number_to_lestring(0, 2)) | ||
| 280 | zh:write(number_to_lestring(lfh.external_attr, 4)) | ||
| 281 | zh:write(number_to_lestring(lfh.offset, 4)) | ||
| 282 | zh:write(lfh.file_name) | ||
| 283 | size_of_central_directory = size_of_central_directory + 46 + lfh.file_name_length | ||
| 284 | end | ||
| 285 | |||
| 286 | |||
| 287 | zh:write(END_OF_CENTRAL_DIR_SIGNATURE) | ||
| 288 | zh:write(number_to_lestring(0, 2)) | ||
| 289 | zh:write(number_to_lestring(0, 2)) | ||
| 290 | zh:write(number_to_lestring(#self.files, 2)) | ||
| 291 | zh:write(number_to_lestring(#self.files, 2)) | ||
| 292 | zh:write(number_to_lestring(size_of_central_directory, 4)) | ||
| 293 | zh:write(number_to_lestring(central_directory_offset, 4)) | ||
| 294 | zh:write(number_to_lestring(0, 2)) | ||
| 295 | zh:close() | ||
| 296 | |||
| 297 | return true | ||
| 298 | end | ||
| 299 | |||
| 300 | |||
| 301 | |||
| 302 | |||
| 303 | function zip.new_zipwriter(name) | ||
| 304 | |||
| 305 | local zw = {} | ||
| 306 | |||
| 307 | zw.ZipHandle = io.open(fs.absolute_name(name), "wb") | ||
| 308 | if not zw.ZipHandle then | ||
| 309 | return nil | ||
| 310 | end | ||
| 311 | zw.files = {} | ||
| 312 | zw.in_open_file = false | ||
| 313 | |||
| 314 | zw.add = zipwriter_add | ||
| 315 | zw.close = zipwriter_close | ||
| 316 | zw.open_new_file_in_zip = zipwriter_open_new_file_in_zip | ||
| 317 | zw.write_file_in_zip = zipwriter_write_file_in_zip | ||
| 318 | zw.close_file_in_zip = zipwriter_close_file_in_zip | ||
| 319 | |||
| 320 | return zw | ||
| 321 | end | ||
| 322 | |||
| 323 | |||
| 324 | |||
| 325 | |||
| 326 | |||
| 327 | |||
| 328 | |||
| 329 | function zip.zip(zipfile, ...) | ||
| 330 | local zw = zip.new_zipwriter(zipfile) | ||
| 331 | if not zw then | ||
| 332 | return nil, "error opening " .. zipfile | ||
| 333 | end | ||
| 334 | |||
| 335 | local args = _tl_table_pack(...) | ||
| 336 | local ok, err | ||
| 337 | for i = 1, args.n do | ||
| 338 | local file = args[i] | ||
| 339 | if fs.is_dir(file) then | ||
| 340 | for _, entry in ipairs(fs.find(file)) do | ||
| 341 | local fullname = dir.path(file, entry) | ||
| 342 | if fs.is_file(fullname) then | ||
| 343 | ok, err = zw:add(fullname) | ||
| 344 | if not ok then break end | ||
| 345 | end | ||
| 346 | end | ||
| 347 | else | ||
| 348 | ok, err = zw:add(file) | ||
| 349 | if not ok then break end | ||
| 350 | end | ||
| 351 | end | ||
| 352 | |||
| 353 | zw:close() | ||
| 354 | return ok, err | ||
| 355 | end | ||
| 356 | |||
| 357 | |||
| 358 | local function ziptime_to_luatime(ztime, zdate) | ||
| 359 | local date = { | ||
| 360 | year = shr(zdate, 9) + 1980, | ||
| 361 | month = shr(lowbits(zdate, 9), 5), | ||
| 362 | day = lowbits(zdate, 5), | ||
| 363 | hour = shr(ztime, 11), | ||
| 364 | min = shr(lowbits(ztime, 11), 5), | ||
| 365 | sec = lowbits(ztime, 5) * 2, | ||
| 366 | } | ||
| 367 | |||
| 368 | if date.month == 0 then date.month = 1 end | ||
| 369 | if date.day == 0 then date.day = 1 end | ||
| 370 | |||
| 371 | return date | ||
| 372 | end | ||
| 373 | |||
| 374 | local function read_file_in_zip(zh, cdr) | ||
| 375 | local sig = zh:read(4) | ||
| 376 | if sig ~= LOCAL_FILE_HEADER_SIGNATURE then | ||
| 377 | return nil, "failed reading Local File Header signature" | ||
| 378 | end | ||
| 379 | |||
| 380 | |||
| 381 | |||
| 382 | zh:seek("cur", 22) | ||
| 383 | local file_name_length = lestring_to_number(zh:read(2)) | ||
| 384 | local extra_field_length = lestring_to_number(zh:read(2)) | ||
| 385 | zh:read(file_name_length) | ||
| 386 | zh:read(extra_field_length) | ||
| 387 | |||
| 388 | local data = zh:read(cdr.compressed_size) | ||
| 389 | |||
| 390 | local uncompressed | ||
| 391 | if cdr.compression_method == 8 then | ||
| 392 | uncompressed = zlib_uncompress(data, "raw") | ||
| 393 | elseif cdr.compression_method == 0 then | ||
| 394 | uncompressed = data | ||
| 395 | else | ||
| 396 | return nil, "unknown compression method " .. cdr.compression_method | ||
| 397 | end | ||
| 398 | |||
| 399 | if #uncompressed ~= cdr.uncompressed_size then | ||
| 400 | return nil, "uncompressed size doesn't match" | ||
| 401 | end | ||
| 402 | if cdr.crc32 ~= zlib_crc32(uncompressed) then | ||
| 403 | return nil, "crc32 failed (expected " .. cdr.crc32 .. ") - data: " .. uncompressed | ||
| 404 | end | ||
| 405 | |||
| 406 | return uncompressed | ||
| 407 | end | ||
| 408 | |||
| 409 | local function process_end_of_central_dir(zh) | ||
| 410 | local at, errend = zh:seek("end", -22) | ||
| 411 | if not at then | ||
| 412 | return nil, errend | ||
| 413 | end | ||
| 414 | |||
| 415 | while true do | ||
| 416 | local sig = zh:read(4) | ||
| 417 | if sig == END_OF_CENTRAL_DIR_SIGNATURE then | ||
| 418 | break | ||
| 419 | end | ||
| 420 | at = at - 1 | ||
| 421 | local at1 = zh:seek("set", at) | ||
| 422 | if at1 ~= at then | ||
| 423 | return nil, "Could not find End of Central Directory signature" | ||
| 424 | end | ||
| 425 | end | ||
| 426 | |||
| 427 | |||
| 428 | |||
| 429 | |||
| 430 | |||
| 431 | zh:seek("cur", 6) | ||
| 432 | |||
| 433 | local central_directory_entries = lestring_to_number(zh:read(2)) | ||
| 434 | |||
| 435 | |||
| 436 | zh:seek("cur", 4) | ||
| 437 | |||
| 438 | local central_directory_offset = lestring_to_number(zh:read(4)) | ||
| 439 | |||
| 440 | return central_directory_entries, central_directory_offset | ||
| 441 | end | ||
| 442 | |||
| 443 | local function process_central_dir(zh, cd_entries) | ||
| 444 | |||
| 445 | local files = {} | ||
| 446 | |||
| 447 | for i = 1, cd_entries do | ||
| 448 | local sig = zh:read(4) | ||
| 449 | if sig ~= CENTRAL_DIRECTORY_SIGNATURE then | ||
| 450 | return nil, "failed reading Central Directory signature" | ||
| 451 | end | ||
| 452 | |||
| 453 | local cdr = {} | ||
| 454 | files[i] = cdr | ||
| 455 | |||
| 456 | cdr.version_made_by = lestring_to_number(zh:read(2)) | ||
| 457 | cdr.version_needed = lestring_to_number(zh:read(2)) | ||
| 458 | cdr.bitflag = lestring_to_number(zh:read(2)) | ||
| 459 | cdr.compression_method = lestring_to_number(zh:read(2)) | ||
| 460 | cdr.last_mod_file_time = lestring_to_number(zh:read(2)) | ||
| 461 | cdr.last_mod_file_date = lestring_to_number(zh:read(2)) | ||
| 462 | cdr.last_mod_luatime = ziptime_to_luatime(cdr.last_mod_file_time, cdr.last_mod_file_date) | ||
| 463 | cdr.crc32 = lestring_to_number(zh:read(4)) | ||
| 464 | cdr.compressed_size = lestring_to_number(zh:read(4)) | ||
| 465 | cdr.uncompressed_size = lestring_to_number(zh:read(4)) | ||
| 466 | cdr.file_name_length = lestring_to_number(zh:read(2)) | ||
| 467 | cdr.extra_field_length = lestring_to_number(zh:read(2)) | ||
| 468 | cdr.file_comment_length = lestring_to_number(zh:read(2)) | ||
| 469 | cdr.disk_number_start = lestring_to_number(zh:read(2)) | ||
| 470 | cdr.internal_attr = lestring_to_number(zh:read(2)) | ||
| 471 | cdr.external_attr = lestring_to_number(zh:read(4)) | ||
| 472 | cdr.offset = lestring_to_number(zh:read(4)) | ||
| 473 | cdr.file_name = zh:read(cdr.file_name_length) | ||
| 474 | cdr.extra_field = zh:read(cdr.extra_field_length) | ||
| 475 | cdr.file_comment = zh:read(cdr.file_comment_length) | ||
| 476 | end | ||
| 477 | return files | ||
| 478 | end | ||
| 479 | |||
| 480 | |||
| 481 | |||
| 482 | |||
| 483 | |||
| 484 | function zip.unzip(zipfile) | ||
| 485 | zipfile = fs.absolute_name(zipfile) | ||
| 486 | local zh, erropen = io.open(zipfile, "rb") | ||
| 487 | if not zh then | ||
| 488 | return nil, erropen | ||
| 489 | end | ||
| 490 | |||
| 491 | local cd_entries, cd_offset = process_end_of_central_dir(zh) | ||
| 492 | if type(cd_offset) == "string" then | ||
| 493 | return nil, cd_offset | ||
| 494 | end | ||
| 495 | |||
| 496 | local okseek, errseek = zh:seek("set", cd_offset) | ||
| 497 | if not okseek then | ||
| 498 | return nil, errseek | ||
| 499 | end | ||
| 500 | |||
| 501 | local files, errproc = process_central_dir(zh, cd_entries) | ||
| 502 | if not files then | ||
| 503 | return nil, errproc | ||
| 504 | end | ||
| 505 | |||
| 506 | for _, cdr in ipairs(files) do | ||
| 507 | local file = cdr.file_name | ||
| 508 | if file:sub(#file) == "/" then | ||
| 509 | local okmake, errmake = fs.make_dir(dir.path(fs.current_dir(), file)) | ||
| 510 | if not okmake then | ||
| 511 | return nil, errmake | ||
| 512 | end | ||
| 513 | else | ||
| 514 | local base = dir.dir_name(file) | ||
| 515 | if base ~= "" then | ||
| 516 | base = dir.path(fs.current_dir(), base) | ||
| 517 | if not fs.is_dir(base) then | ||
| 518 | local okmake, errmake = fs.make_dir(base) | ||
| 519 | if not okmake then | ||
| 520 | return nil, errmake | ||
| 521 | end | ||
| 522 | end | ||
| 523 | end | ||
| 524 | |||
| 525 | local okseek2, errseek2 = zh:seek("set", cdr.offset) | ||
| 526 | if not okseek2 then | ||
| 527 | return nil, errseek2 | ||
| 528 | end | ||
| 529 | |||
| 530 | local contents, err = read_file_in_zip(zh, cdr) | ||
| 531 | if not contents then | ||
| 532 | return nil, err | ||
| 533 | end | ||
| 534 | local pathname = dir.path(fs.current_dir(), file) | ||
| 535 | local wf, erropen2 = io.open(pathname, "wb") | ||
| 536 | if not wf then | ||
| 537 | zh:close() | ||
| 538 | return nil, erropen2 | ||
| 539 | end | ||
| 540 | wf:write(contents) | ||
| 541 | wf:close() | ||
| 542 | |||
| 543 | if cdr.external_attr > 0 then | ||
| 544 | fs.set_permissions(pathname, "exec", "all") | ||
| 545 | else | ||
| 546 | fs.set_permissions(pathname, "read", "all") | ||
| 547 | end | ||
| 548 | fs.set_time(pathname, cdr.last_mod_luatime) | ||
| 549 | end | ||
| 550 | end | ||
| 551 | zh:close() | ||
| 552 | return true | ||
| 553 | end | ||
| 554 | |||
| 555 | function zip.gzip(input_filename, output_filename) | ||
| 556 | |||
| 557 | if not output_filename then | ||
| 558 | output_filename = input_filename .. ".gz" | ||
| 559 | end | ||
| 560 | |||
| 561 | local fn = fun.partial(fun.flip(zlib_compress), "gzip") | ||
| 562 | return fs.filter_file(fn, input_filename, output_filename) | ||
| 563 | end | ||
| 564 | |||
| 565 | function zip.gunzip(input_filename, output_filename) | ||
| 566 | |||
| 567 | if not output_filename then | ||
| 568 | output_filename = input_filename:gsub("%.gz$", "") | ||
| 569 | end | ||
| 570 | |||
| 571 | local fn = fun.partial(fun.flip(zlib_uncompress), "gzip") | ||
| 572 | return fs.filter_file(fn, input_filename, output_filename) | ||
| 573 | end | ||
| 574 | |||
| 575 | 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 @@ | |||
| 1 | local type_manifest = {} | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | local type_check = require("luarocks.type_check") | ||
| 7 | |||
| 8 | local manifest_formats = type_check.declare_schemas({ | ||
| 9 | ["3.0"] = { | ||
| 10 | fields = { | ||
| 11 | repository = { | ||
| 12 | _mandatory = true, | ||
| 13 | |||
| 14 | _any = { | ||
| 15 | |||
| 16 | _any = { | ||
| 17 | |||
| 18 | _any = { | ||
| 19 | fields = { | ||
| 20 | arch = { _type = "string", _mandatory = true }, | ||
| 21 | modules = { _any = { _type = "string" } }, | ||
| 22 | commands = { _any = { _type = "string" } }, | ||
| 23 | dependencies = { _any = { _type = "string" } }, | ||
| 24 | |||
| 25 | }, | ||
| 26 | }, | ||
| 27 | }, | ||
| 28 | }, | ||
| 29 | }, | ||
| 30 | modules = { | ||
| 31 | _mandatory = true, | ||
| 32 | |||
| 33 | _any = { | ||
| 34 | |||
| 35 | _any = { _type = "string" }, | ||
| 36 | }, | ||
| 37 | }, | ||
| 38 | commands = { | ||
| 39 | _mandatory = true, | ||
| 40 | |||
| 41 | _any = { | ||
| 42 | |||
| 43 | _any = { _type = "string" }, | ||
| 44 | }, | ||
| 45 | }, | ||
| 46 | dependencies = { | ||
| 47 | |||
| 48 | _any = { | ||
| 49 | |||
| 50 | _any = { | ||
| 51 | |||
| 52 | _any = { | ||
| 53 | fields = { | ||
| 54 | name = { _type = "string" }, | ||
| 55 | namespace = { _type = "string" }, | ||
| 56 | constraints = { | ||
| 57 | _any = { | ||
| 58 | fields = { | ||
| 59 | no_upgrade = { _type = "boolean" }, | ||
| 60 | op = { _type = "string" }, | ||
| 61 | version = { | ||
| 62 | fields = { | ||
| 63 | string = { _type = "string" }, | ||
| 64 | }, | ||
| 65 | _any = { _type = "number" }, | ||
| 66 | }, | ||
| 67 | }, | ||
| 68 | }, | ||
| 69 | }, | ||
| 70 | }, | ||
| 71 | }, | ||
| 72 | }, | ||
| 73 | }, | ||
| 74 | }, | ||
| 75 | }, | ||
| 76 | }, | ||
| 77 | }) | ||
| 78 | |||
| 79 | |||
| 80 | |||
| 81 | |||
| 82 | |||
| 83 | |||
| 84 | |||
| 85 | function type_manifest.check(manifest, globals) | ||
| 86 | local format = manifest_formats["3.0"] | ||
| 87 | local ok, err = type_check.check_undeclared_globals(globals, format) | ||
| 88 | if not ok then return nil, err end | ||
| 89 | return type_check.type_check_table("3.0", manifest, format, "") | ||
| 90 | end | ||
| 91 | |||
| 92 | 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 @@ | |||
| 1 | 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 = {} | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | local type_check = require("luarocks.type_check") | ||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | type_rockspec.rockspec_format = "3.0" | ||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | local rockspec_formats, versions = type_check.declare_schemas({ | ||
| 30 | ["1.0"] = { | ||
| 31 | fields = { | ||
| 32 | rockspec_format = { _type = "string" }, | ||
| 33 | package = { _type = "string", _mandatory = true }, | ||
| 34 | version = { _type = "string", _pattern = "[%w.]+-[%d]+", _mandatory = true }, | ||
| 35 | description = { | ||
| 36 | fields = { | ||
| 37 | summary = { _type = "string" }, | ||
| 38 | detailed = { _type = "string" }, | ||
| 39 | homepage = { _type = "string" }, | ||
| 40 | license = { _type = "string" }, | ||
| 41 | maintainer = { _type = "string" }, | ||
| 42 | }, | ||
| 43 | }, | ||
| 44 | dependencies = { | ||
| 45 | fields = { | ||
| 46 | platforms = type_check.MAGIC_PLATFORMS, | ||
| 47 | }, | ||
| 48 | _any = { | ||
| 49 | _type = "string", | ||
| 50 | _name = "a valid dependency string", | ||
| 51 | _pattern = "%s*([a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)", | ||
| 52 | }, | ||
| 53 | }, | ||
| 54 | supported_platforms = { | ||
| 55 | _any = { _type = "string" }, | ||
| 56 | }, | ||
| 57 | external_dependencies = { | ||
| 58 | fields = { | ||
| 59 | platforms = type_check.MAGIC_PLATFORMS, | ||
| 60 | }, | ||
| 61 | _any = { | ||
| 62 | fields = { | ||
| 63 | program = { _type = "string" }, | ||
| 64 | header = { _type = "string" }, | ||
| 65 | library = { _type = "string" }, | ||
| 66 | }, | ||
| 67 | }, | ||
| 68 | }, | ||
| 69 | source = { | ||
| 70 | _mandatory = true, | ||
| 71 | fields = { | ||
| 72 | platforms = type_check.MAGIC_PLATFORMS, | ||
| 73 | url = { _type = "string", _mandatory = true }, | ||
| 74 | md5 = { _type = "string" }, | ||
| 75 | file = { _type = "string" }, | ||
| 76 | dir = { _type = "string" }, | ||
| 77 | tag = { _type = "string" }, | ||
| 78 | branch = { _type = "string" }, | ||
| 79 | module = { _type = "string" }, | ||
| 80 | cvs_tag = { _type = "string" }, | ||
| 81 | cvs_module = { _type = "string" }, | ||
| 82 | }, | ||
| 83 | }, | ||
| 84 | build = { | ||
| 85 | fields = { | ||
| 86 | platforms = type_check.MAGIC_PLATFORMS, | ||
| 87 | type = { _type = "string" }, | ||
| 88 | install = { | ||
| 89 | fields = { | ||
| 90 | lua = { | ||
| 91 | _more = true, | ||
| 92 | }, | ||
| 93 | lib = { | ||
| 94 | _more = true, | ||
| 95 | }, | ||
| 96 | conf = { | ||
| 97 | _more = true, | ||
| 98 | }, | ||
| 99 | bin = { | ||
| 100 | _more = true, | ||
| 101 | }, | ||
| 102 | }, | ||
| 103 | }, | ||
| 104 | copy_directories = { | ||
| 105 | _any = { _type = "string" }, | ||
| 106 | }, | ||
| 107 | }, | ||
| 108 | _more = true, | ||
| 109 | _mandatory = true, | ||
| 110 | }, | ||
| 111 | hooks = { | ||
| 112 | fields = { | ||
| 113 | platforms = type_check.MAGIC_PLATFORMS, | ||
| 114 | post_install = { _type = "string" }, | ||
| 115 | }, | ||
| 116 | }, | ||
| 117 | }, | ||
| 118 | }, | ||
| 119 | |||
| 120 | ["1.1"] = { | ||
| 121 | fields = { | ||
| 122 | deploy = { | ||
| 123 | fields = { | ||
| 124 | wrap_bin_scripts = { _type = "boolean" }, | ||
| 125 | }, | ||
| 126 | }, | ||
| 127 | }, | ||
| 128 | }, | ||
| 129 | |||
| 130 | ["3.0"] = { | ||
| 131 | fields = { | ||
| 132 | description = { | ||
| 133 | fields = { | ||
| 134 | labels = { | ||
| 135 | _any = { _type = "string" }, | ||
| 136 | }, | ||
| 137 | issues_url = { _type = "string" }, | ||
| 138 | }, | ||
| 139 | }, | ||
| 140 | dependencies = { | ||
| 141 | _any = { | ||
| 142 | _pattern = "%s*([a-zA-Z0-9%.%-%_]*/?[a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)", | ||
| 143 | }, | ||
| 144 | }, | ||
| 145 | build_dependencies = { | ||
| 146 | fields = { | ||
| 147 | platforms = type_check.MAGIC_PLATFORMS, | ||
| 148 | }, | ||
| 149 | _any = { | ||
| 150 | _type = "string", | ||
| 151 | _name = "a valid dependency string", | ||
| 152 | _pattern = "%s*([a-zA-Z0-9%.%-%_]*/?[a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)", | ||
| 153 | }, | ||
| 154 | }, | ||
| 155 | test_dependencies = { | ||
| 156 | fields = { | ||
| 157 | platforms = type_check.MAGIC_PLATFORMS, | ||
| 158 | }, | ||
| 159 | _any = { | ||
| 160 | _type = "string", | ||
| 161 | _name = "a valid dependency string", | ||
| 162 | _pattern = "%s*([a-zA-Z0-9%.%-%_]*/?[a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)", | ||
| 163 | }, | ||
| 164 | }, | ||
| 165 | build = { | ||
| 166 | _mandatory = false, | ||
| 167 | }, | ||
| 168 | test = { | ||
| 169 | fields = { | ||
| 170 | platforms = type_check.MAGIC_PLATFORMS, | ||
| 171 | type = { _type = "string" }, | ||
| 172 | }, | ||
| 173 | _more = true, | ||
| 174 | }, | ||
| 175 | }, | ||
| 176 | }, | ||
| 177 | }) | ||
| 178 | |||
| 179 | |||
| 180 | |||
| 181 | |||
| 182 | |||
| 183 | |||
| 184 | |||
| 185 | |||
| 186 | |||
| 187 | type_rockspec.order = { | ||
| 188 | "rockspec_format", | ||
| 189 | "package", | ||
| 190 | "version", | ||
| 191 | "source", | ||
| 192 | "description", | ||
| 193 | "supported_platforms", | ||
| 194 | "dependencies", | ||
| 195 | "build_dependencies", | ||
| 196 | "external_dependencies", | ||
| 197 | "build", | ||
| 198 | "test_dependencies", | ||
| 199 | "test", | ||
| 200 | "hooks", | ||
| 201 | sub_orders = { | ||
| 202 | ["source"] = { "url", "tag", "branch", "md5" }, | ||
| 203 | ["description"] = { "summary", "detailed", "homepage", "license" }, | ||
| 204 | ["build"] = { "type", "modules", "copy_directories", "platforms" }, | ||
| 205 | ["test"] = { "type" }, | ||
| 206 | }, | ||
| 207 | } | ||
| 208 | |||
| 209 | local function check_rockspec_using_version(rockspec, globals, version) | ||
| 210 | local schema = rockspec_formats[version] | ||
| 211 | if not schema then | ||
| 212 | return nil, "unknown rockspec format " .. version | ||
| 213 | end | ||
| 214 | local ok, err = type_check.check_undeclared_globals(globals, schema) | ||
| 215 | if ok then | ||
| 216 | ok, err = type_check.type_check_table(version, rockspec, schema, "") | ||
| 217 | end | ||
| 218 | if ok then | ||
| 219 | return true | ||
| 220 | else | ||
| 221 | return nil, err | ||
| 222 | end | ||
| 223 | end | ||
| 224 | |||
| 225 | |||
| 226 | |||
| 227 | |||
| 228 | |||
| 229 | |||
| 230 | |||
| 231 | function type_rockspec.check(rockspec, globals) | ||
| 232 | |||
| 233 | local version = rockspec.rockspec_format or "1.0" | ||
| 234 | local ok, err = check_rockspec_using_version(rockspec, globals, version) | ||
| 235 | if ok then | ||
| 236 | return true | ||
| 237 | end | ||
| 238 | |||
| 239 | |||
| 240 | |||
| 241 | |||
| 242 | local found = false | ||
| 243 | for _, v in ipairs(versions) do | ||
| 244 | if not found then | ||
| 245 | if v == version then | ||
| 246 | found = true | ||
| 247 | end | ||
| 248 | else | ||
| 249 | local v_ok = check_rockspec_using_version(rockspec, globals, v) | ||
| 250 | if v_ok then | ||
| 251 | return nil, err .. " (using rockspec format " .. version .. " -- " .. | ||
| 252 | [[adding 'rockspec_format = "]] .. v .. [["' to the rockspec ]] .. | ||
| 253 | [[will fix this)]] | ||
| 254 | end | ||
| 255 | end | ||
| 256 | end | ||
| 257 | |||
| 258 | return nil, err .. " (using rockspec format " .. version .. ")" | ||
| 259 | end | ||
| 260 | |||
| 261 | 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 @@ | |||
| 1 | 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 | ||
| 2 | local type_check = {TableSchema = {}, } | ||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | local cfg = require("luarocks.core.cfg") | ||
| 20 | local fun = require("luarocks.fun") | ||
| 21 | local util = require("luarocks.util") | ||
| 22 | local vers = require("luarocks.core.vers") | ||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | type_check.MAGIC_PLATFORMS = {} | ||
| 30 | |||
| 31 | do | ||
| 32 | local function fill_in_version(tbl, version) | ||
| 33 | |||
| 34 | if not tbl.fields then | ||
| 35 | return | ||
| 36 | end | ||
| 37 | |||
| 38 | for _, v in pairs(tbl.fields) do | ||
| 39 | if type(v) == "table" then | ||
| 40 | if v._version == nil then | ||
| 41 | v._version = version | ||
| 42 | end | ||
| 43 | fill_in_version(v) | ||
| 44 | end | ||
| 45 | end | ||
| 46 | end | ||
| 47 | |||
| 48 | local function expand_magic_platforms(tbl) | ||
| 49 | for k, v in pairs(tbl.fields) do | ||
| 50 | if v == type_check.MAGIC_PLATFORMS then | ||
| 51 | tbl.fields[k] = { | ||
| 52 | _any = util.deep_copy(tbl), | ||
| 53 | } | ||
| 54 | tbl.fields[k]._any.fields[k] = nil | ||
| 55 | expand_magic_platforms(v) | ||
| 56 | end | ||
| 57 | end | ||
| 58 | end | ||
| 59 | |||
| 60 | |||
| 61 | |||
| 62 | |||
| 63 | |||
| 64 | |||
| 65 | function type_check.declare_schemas(inputs) | ||
| 66 | local schemas = {} | ||
| 67 | local parent_version | ||
| 68 | |||
| 69 | local versions = fun.reverse_in(fun.sort_in(util.keys(inputs), vers.compare_versions)) | ||
| 70 | |||
| 71 | for _, version in ipairs(versions) do | ||
| 72 | local schema = inputs[version] | ||
| 73 | if parent_version then | ||
| 74 | local copy = util.deep_copy(schemas[parent_version]) | ||
| 75 | util.deep_merge(copy, schema) | ||
| 76 | schema = copy | ||
| 77 | end | ||
| 78 | fill_in_version(schema, version) | ||
| 79 | expand_magic_platforms(schema) | ||
| 80 | parent_version = version | ||
| 81 | schemas[version] = schema | ||
| 82 | end | ||
| 83 | |||
| 84 | return schemas, versions | ||
| 85 | end | ||
| 86 | end | ||
| 87 | |||
| 88 | |||
| 89 | |||
| 90 | local function check_version(version, typetbl, context) | ||
| 91 | local typetbl_version = typetbl._version or "1.0" | ||
| 92 | if vers.compare_versions(typetbl_version, version) then | ||
| 93 | if context == "" then | ||
| 94 | return nil, "Invalid rockspec_format version number in rockspec? Please fix rockspec accordingly." | ||
| 95 | else | ||
| 96 | return nil, context .. " is not supported in rockspec format " .. version .. " (requires version " .. typetbl_version .. "), please fix the rockspec_format field accordingly." | ||
| 97 | end | ||
| 98 | end | ||
| 99 | return true | ||
| 100 | end | ||
| 101 | |||
| 102 | |||
| 103 | |||
| 104 | |||
| 105 | |||
| 106 | |||
| 107 | |||
| 108 | |||
| 109 | |||
| 110 | |||
| 111 | |||
| 112 | |||
| 113 | |||
| 114 | |||
| 115 | |||
| 116 | local function type_check_item(version, item, typetbl, context) | ||
| 117 | |||
| 118 | if typetbl._version and typetbl._version ~= "1.0" then | ||
| 119 | local ok, err = check_version(version, typetbl, context) | ||
| 120 | if not ok then | ||
| 121 | return nil, err | ||
| 122 | end | ||
| 123 | end | ||
| 124 | |||
| 125 | local expected_type = typetbl._type or "table" | ||
| 126 | |||
| 127 | if expected_type == "number" then | ||
| 128 | if not tonumber(item) then | ||
| 129 | return nil, "Type mismatch on field " .. context .. ": expected a number" | ||
| 130 | end | ||
| 131 | elseif expected_type == "string" then | ||
| 132 | if not (type(item) == "string") then | ||
| 133 | return nil, "Type mismatch on field " .. context .. ": expected a string, got " .. type(item) | ||
| 134 | end | ||
| 135 | local pattern = typetbl._pattern | ||
| 136 | if pattern then | ||
| 137 | if not item:match("^" .. pattern .. "$") then | ||
| 138 | local what = typetbl._name or ("'" .. pattern .. "'") | ||
| 139 | return nil, "Type mismatch on field " .. context .. ": invalid value '" .. item .. "' does not match " .. what | ||
| 140 | end | ||
| 141 | end | ||
| 142 | elseif expected_type == "table" then | ||
| 143 | if not (type(item) == "table") then | ||
| 144 | return nil, "Type mismatch on field " .. context .. ": expected a table" | ||
| 145 | else | ||
| 146 | return type_check.type_check_table(version, item, typetbl, context) | ||
| 147 | end | ||
| 148 | elseif type(item) ~= expected_type then | ||
| 149 | return nil, "Type mismatch on field " .. context .. ": expected " .. expected_type | ||
| 150 | end | ||
| 151 | return true | ||
| 152 | end | ||
| 153 | |||
| 154 | local function mkfield(context, field) | ||
| 155 | if context == "" then | ||
| 156 | return tostring(field) | ||
| 157 | elseif type(field) == "string" then | ||
| 158 | return context .. "." .. field | ||
| 159 | else | ||
| 160 | return context .. "[" .. tostring(field) .. "]" | ||
| 161 | end | ||
| 162 | end | ||
| 163 | |||
| 164 | |||
| 165 | |||
| 166 | |||
| 167 | |||
| 168 | |||
| 169 | |||
| 170 | |||
| 171 | |||
| 172 | |||
| 173 | |||
| 174 | |||
| 175 | |||
| 176 | |||
| 177 | |||
| 178 | |||
| 179 | |||
| 180 | |||
| 181 | |||
| 182 | |||
| 183 | |||
| 184 | |||
| 185 | |||
| 186 | function type_check.type_check_table(version, tbl, typetbl, context) | ||
| 187 | |||
| 188 | local ok, err = check_version(version, typetbl, context) | ||
| 189 | if not ok then | ||
| 190 | return nil, err | ||
| 191 | end | ||
| 192 | |||
| 193 | if not typetbl.fields then | ||
| 194 | |||
| 195 | return true | ||
| 196 | end | ||
| 197 | |||
| 198 | for k, v in pairs(tbl) do | ||
| 199 | local t = typetbl.fields[tostring(k)] or typetbl._any | ||
| 200 | if t then | ||
| 201 | ok, err = type_check_item(version, v, t, mkfield(context, k)) | ||
| 202 | if not ok then return nil, err end | ||
| 203 | elseif typetbl._more then | ||
| 204 | |||
| 205 | else | ||
| 206 | if not cfg.accept_unknown_fields then | ||
| 207 | return nil, "Unknown field " .. tostring(k) | ||
| 208 | end | ||
| 209 | end | ||
| 210 | end | ||
| 211 | |||
| 212 | for k, v in pairs(typetbl.fields) do | ||
| 213 | if k:sub(1, 1) ~= "_" and v._mandatory then | ||
| 214 | if not tbl[k] then | ||
| 215 | return nil, "Mandatory field " .. mkfield(context, k) .. " is missing." | ||
| 216 | end | ||
| 217 | end | ||
| 218 | end | ||
| 219 | return true | ||
| 220 | end | ||
| 221 | |||
| 222 | function type_check.check_undeclared_globals(globals, typetbl) | ||
| 223 | local undeclared = {} | ||
| 224 | for glob, _ in pairs(globals) do | ||
| 225 | if not (typetbl.fields[glob] or typetbl.fields["MUST_" .. glob]) then | ||
| 226 | table.insert(undeclared, glob) | ||
| 227 | end | ||
| 228 | end | ||
| 229 | if #undeclared == 1 then | ||
| 230 | return nil, "Unknown variable: " .. undeclared[1] | ||
| 231 | elseif #undeclared > 1 then | ||
| 232 | return nil, "Unknown variables: " .. table.concat(undeclared, ", ") | ||
| 233 | end | ||
| 234 | return true | ||
| 235 | end | ||
| 236 | |||
| 237 | 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 @@ | |||
| 1 | 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 = {}, } | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | local cfg = require("luarocks.core.cfg") | ||
| 22 | local fs = require("luarocks.fs") | ||
| 23 | local dir = require("luarocks.dir") | ||
| 24 | local util = require("luarocks.util") | ||
| 25 | local persist = require("luarocks.persist") | ||
| 26 | local multipart = require("luarocks.upload.multipart") | ||
| 27 | local json = require("luarocks.vendor.dkjson") | ||
| 28 | local dir_sep = package.config:sub(1, 1) | ||
| 29 | |||
| 30 | |||
| 31 | local Api = api.Api | ||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | local function upload_config_file() | ||
| 39 | if not cfg.config_files.user.file then | ||
| 40 | return nil | ||
| 41 | end | ||
| 42 | return (cfg.config_files.user.file:gsub("[\\/][^\\/]+$", dir_sep .. "upload_config.lua")) | ||
| 43 | end | ||
| 44 | |||
| 45 | function api.Api:load_config() | ||
| 46 | local upload_conf = upload_config_file() | ||
| 47 | if not upload_conf then return nil end | ||
| 48 | local config = persist.load_into_table(upload_conf) | ||
| 49 | return config | ||
| 50 | end | ||
| 51 | |||
| 52 | function api.Api:save_config() | ||
| 53 | |||
| 54 | local res, errraw = self:raw_method("status") | ||
| 55 | if not res then | ||
| 56 | return nil, errraw | ||
| 57 | end | ||
| 58 | local reserrors = res.errors | ||
| 59 | if type(reserrors) == "table" then | ||
| 60 | return nil, ("Server error: " .. tostring(reserrors[1])) | ||
| 61 | end | ||
| 62 | local upload_conf = upload_config_file() | ||
| 63 | if not upload_conf then return nil end | ||
| 64 | local ok, errmake = fs.make_dir(dir.dir_name(upload_conf)) | ||
| 65 | if not ok then | ||
| 66 | return nil, errmake | ||
| 67 | end | ||
| 68 | persist.save_from_table(upload_conf, self.config) | ||
| 69 | fs.set_permissions(upload_conf, "read", "user") | ||
| 70 | return true | ||
| 71 | end | ||
| 72 | |||
| 73 | function api.Api:check_version() | ||
| 74 | if not self._server_tool_version then | ||
| 75 | local tool_version = cfg.upload.tool_version | ||
| 76 | local res, err = self:request(tostring(self.config.server) .. "/api/tool_version", { | ||
| 77 | current = tool_version, | ||
| 78 | }) | ||
| 79 | if not res then | ||
| 80 | return nil, err | ||
| 81 | end | ||
| 82 | if not res.version then | ||
| 83 | return nil, "failed to fetch tool version" | ||
| 84 | end | ||
| 85 | self._server_tool_version = tostring(res.version) | ||
| 86 | if res.force_update then | ||
| 87 | return nil, "Your upload client is too out of date to continue, please upgrade LuaRocks." | ||
| 88 | end | ||
| 89 | if res.version ~= tool_version then | ||
| 90 | util.warning("your LuaRocks is out of date, consider upgrading.") | ||
| 91 | end | ||
| 92 | end | ||
| 93 | return true | ||
| 94 | end | ||
| 95 | |||
| 96 | function api.Api:method(path, ...) | ||
| 97 | local res, err = self:raw_method(path, ...) | ||
| 98 | if not res then | ||
| 99 | return nil, err | ||
| 100 | end | ||
| 101 | local reserrors = res.errors | ||
| 102 | if type(reserrors) == "table" then | ||
| 103 | if reserrors[1] == "Invalid key" then | ||
| 104 | return nil, reserrors[1] .. " (use the --api-key flag to change)" | ||
| 105 | end | ||
| 106 | local msg = table.concat(reserrors, ", ") | ||
| 107 | return nil, "API Failed: " .. msg | ||
| 108 | end | ||
| 109 | return res | ||
| 110 | end | ||
| 111 | |||
| 112 | function api.Api:raw_method(path, ...) | ||
| 113 | self:check_version() | ||
| 114 | local url = tostring(self.config.server) .. "/api/" .. tostring(cfg.upload.api_version) .. "/" .. tostring(self.config.key) .. "/" .. path | ||
| 115 | return self:request(url, ...) | ||
| 116 | end | ||
| 117 | |||
| 118 | local function encode_query_string(t, sep) | ||
| 119 | if sep == nil then | ||
| 120 | sep = "&" | ||
| 121 | end | ||
| 122 | local i = 0 | ||
| 123 | local buf = {} | ||
| 124 | for k, v in pairs(t) do | ||
| 125 | local ks, vs | ||
| 126 | local vf = v | ||
| 127 | if type(vf) == "table" then | ||
| 128 | ks, vs = k, vf:content() | ||
| 129 | else | ||
| 130 | ks, vs = k, vf | ||
| 131 | end | ||
| 132 | buf[i + 1] = multipart.url_escape(ks) | ||
| 133 | buf[i + 2] = "=" | ||
| 134 | buf[i + 3] = multipart.url_escape(vs) | ||
| 135 | buf[i + 4] = sep | ||
| 136 | i = i + 4 | ||
| 137 | end | ||
| 138 | buf[i] = nil | ||
| 139 | return table.concat(buf) | ||
| 140 | end | ||
| 141 | |||
| 142 | local function redact_api_url(url) | ||
| 143 | local urls = tostring(url) | ||
| 144 | return (urls:gsub(".*/api/[^/]+/[^/]+", "")) or "" | ||
| 145 | end | ||
| 146 | |||
| 147 | local ltn12_ok, ltn12 = pcall(require, "ltn12") | ||
| 148 | if not ltn12_ok then | ||
| 149 | |||
| 150 | api.Api.request = function(self, url, params, post_params) | ||
| 151 | local vars = cfg.variables | ||
| 152 | |||
| 153 | if fs.which_tool("downloader") == "wget" then | ||
| 154 | local curl_ok, err = fs.is_tool_available(vars.CURL, "curl") | ||
| 155 | if not curl_ok then | ||
| 156 | return nil, err | ||
| 157 | end | ||
| 158 | end | ||
| 159 | |||
| 160 | if not self.config.key then | ||
| 161 | return nil, "Must have API key before performing any actions." | ||
| 162 | end | ||
| 163 | if params and next(params) then | ||
| 164 | url = url .. ("?" .. encode_query_string(params)) | ||
| 165 | end | ||
| 166 | local method = "GET" | ||
| 167 | local out | ||
| 168 | local tmpfile = fs.tmpname() | ||
| 169 | if post_params then | ||
| 170 | method = "POST" | ||
| 171 | local curl_cmd = vars.CURL .. " " .. vars.CURLNOCERTFLAG .. " -f -L --silent --user-agent \"" .. cfg.user_agent .. " via curl\" " | ||
| 172 | for k, v in pairs(post_params) do | ||
| 173 | local var | ||
| 174 | if type(v) == "table" then | ||
| 175 | var = "@" .. v.fname | ||
| 176 | else | ||
| 177 | var = v | ||
| 178 | end | ||
| 179 | curl_cmd = curl_cmd .. "--form \"" .. k .. "=" .. var .. "\" " | ||
| 180 | end | ||
| 181 | if cfg.connection_timeout and cfg.connection_timeout > 0 then | ||
| 182 | curl_cmd = curl_cmd .. "--connect-timeout " .. tonumber(cfg.connection_timeout) .. " " | ||
| 183 | end | ||
| 184 | local ok = fs.execute_string(curl_cmd .. fs.Q(url) .. " -o " .. fs.Q(tmpfile)) | ||
| 185 | if not ok then | ||
| 186 | return nil, "API failure: " .. redact_api_url(url) | ||
| 187 | end | ||
| 188 | else | ||
| 189 | local name, err = fs.download(url, tmpfile) | ||
| 190 | if not name then | ||
| 191 | return nil, "API failure: " .. tostring(err) .. " - " .. redact_api_url(url) | ||
| 192 | end | ||
| 193 | end | ||
| 194 | |||
| 195 | local tmpfd = io.open(tmpfile) | ||
| 196 | if not tmpfd then | ||
| 197 | os.remove(tmpfile) | ||
| 198 | return nil, "API failure reading temporary file - " .. redact_api_url(url) | ||
| 199 | end | ||
| 200 | out = tmpfd:read("*a") | ||
| 201 | tmpfd:close() | ||
| 202 | os.remove(tmpfile) | ||
| 203 | |||
| 204 | if self.debug then | ||
| 205 | util.printout("[" .. tostring(method) .. " via curl] " .. redact_api_url(url) .. " ... ") | ||
| 206 | end | ||
| 207 | |||
| 208 | return json.decode(out) | ||
| 209 | end | ||
| 210 | |||
| 211 | else | ||
| 212 | |||
| 213 | local warned_luasec = false | ||
| 214 | |||
| 215 | api.Api.request = function(self, url, params, post_params) | ||
| 216 | local server = tostring(self.config.server) | ||
| 217 | |||
| 218 | local http_ok, http | ||
| 219 | local via = "luasocket" | ||
| 220 | if server:match("^https://") then | ||
| 221 | http_ok, http = pcall(require, "ssl.https") | ||
| 222 | if http_ok then | ||
| 223 | via = "luasec" | ||
| 224 | else | ||
| 225 | if not warned_luasec then | ||
| 226 | util.printerr("LuaSec is not available; using plain HTTP. Install 'luasec' to enable HTTPS.") | ||
| 227 | warned_luasec = true | ||
| 228 | end | ||
| 229 | http_ok, http = pcall(require, "socket.http") | ||
| 230 | url = url:gsub("^https", "http") | ||
| 231 | via = "luasocket" | ||
| 232 | end | ||
| 233 | else | ||
| 234 | http_ok, http = pcall(require, "socket.http") | ||
| 235 | end | ||
| 236 | if not http_ok then | ||
| 237 | return nil, "Failed loading socket library!" | ||
| 238 | end | ||
| 239 | |||
| 240 | if not self.config.key then | ||
| 241 | return nil, "Must have API key before performing any actions." | ||
| 242 | end | ||
| 243 | local body | ||
| 244 | local headers = {} | ||
| 245 | if params and next(params) then | ||
| 246 | url = url .. ("?" .. encode_query_string(params)) | ||
| 247 | end | ||
| 248 | if post_params then | ||
| 249 | local boundary | ||
| 250 | body, boundary = multipart.encode(post_params) | ||
| 251 | headers["Content-length"] = tostring(#body) | ||
| 252 | headers["Content-type"] = "multipart/form-data; boundary=" .. tostring(boundary) | ||
| 253 | end | ||
| 254 | local method = post_params and "POST" or "GET" | ||
| 255 | if self.debug then | ||
| 256 | util.printout("[" .. tostring(method) .. " via " .. via .. "] " .. redact_api_url(url) .. " ... ") | ||
| 257 | end | ||
| 258 | local out = {} | ||
| 259 | local _, status = http.request({ | ||
| 260 | url = url, | ||
| 261 | headers = headers, | ||
| 262 | method = method, | ||
| 263 | sink = ltn12.sink.table(out), | ||
| 264 | source = body and ltn12.source.string(body), | ||
| 265 | }) | ||
| 266 | if self.debug then | ||
| 267 | util.printout(tostring(status)) | ||
| 268 | end | ||
| 269 | local pok, ret = pcall(json.decode, table.concat(out)) | ||
| 270 | if pok and ret then | ||
| 271 | return ret | ||
| 272 | end | ||
| 273 | return nil, "API returned " .. tostring(status) .. " - " .. redact_api_url(url) | ||
| 274 | end | ||
| 275 | |||
| 276 | end | ||
| 277 | |||
| 278 | function api.new(args) | ||
| 279 | local self = {} | ||
| 280 | setmetatable(self, { __index = Api }) | ||
| 281 | self.config = self:load_config() or {} | ||
| 282 | self.config.server = args.server or self.config.server or cfg.upload.server | ||
| 283 | self.config.version = self.config.version or cfg.upload.version | ||
| 284 | self.config.key = args.temp_key or args.api_key or self.config.key | ||
| 285 | self.debug = args.debug | ||
| 286 | if not self.config.key then | ||
| 287 | return nil, "You need an API key to upload rocks.\n" .. | ||
| 288 | "Navigate to " .. self.config.server .. "/settings to get a key\n" .. | ||
| 289 | "and then pass it through the --api-key=<key> flag." | ||
| 290 | end | ||
| 291 | if args.api_key then | ||
| 292 | local ok, err = self:save_config() | ||
| 293 | if not ok then | ||
| 294 | return nil, err | ||
| 295 | end | ||
| 296 | end | ||
| 297 | return self | ||
| 298 | end | ||
| 299 | |||
| 300 | 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 @@ | |||
| 1 | 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 | ||
| 2 | local multipart = {File = {}, } | ||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | local File = multipart.File | ||
| 18 | |||
| 19 | |||
| 20 | function multipart.url_escape(s) | ||
| 21 | return (string.gsub(s, "([^A-Za-z0-9_])", function(c) | ||
| 22 | return string.format("%%%02x", string.byte(c)) | ||
| 23 | end)) | ||
| 24 | end | ||
| 25 | |||
| 26 | function multipart.File:mime() | ||
| 27 | if not self.mimetype then | ||
| 28 | local mimetypes_ok, mimetypes = pcall(require, "mimetypes") | ||
| 29 | if mimetypes_ok then | ||
| 30 | self.mimetype = mimetypes.guess(self.fname) | ||
| 31 | end | ||
| 32 | self.mimetype = self.mimetype or "application/octet-stream" | ||
| 33 | end | ||
| 34 | return self.mimetype | ||
| 35 | end | ||
| 36 | |||
| 37 | function multipart.File:content() | ||
| 38 | local fd = io.open(self.fname, "rb") | ||
| 39 | if not fd then | ||
| 40 | return nil, "Failed to open file: " .. self.fname | ||
| 41 | end | ||
| 42 | local data = fd:read("*a") | ||
| 43 | fd:close() | ||
| 44 | return data | ||
| 45 | end | ||
| 46 | |||
| 47 | local function rand_string(len) | ||
| 48 | local shuffled = {} | ||
| 49 | for i = 1, len do | ||
| 50 | local r = math.random(97, 122) | ||
| 51 | if math.random() >= 0.5 then | ||
| 52 | r = r - 32 | ||
| 53 | end | ||
| 54 | shuffled[i] = r | ||
| 55 | end | ||
| 56 | return string.char(_tl_table_unpack(shuffled)) | ||
| 57 | end | ||
| 58 | |||
| 59 | |||
| 60 | |||
| 61 | |||
| 62 | |||
| 63 | |||
| 64 | |||
| 65 | |||
| 66 | |||
| 67 | function multipart.encode(params) | ||
| 68 | local tuples = {} | ||
| 69 | for k, v in pairs(params) do | ||
| 70 | if type(k) == "string" then | ||
| 71 | table.insert(tuples, { k, v }) | ||
| 72 | end | ||
| 73 | end | ||
| 74 | local chunks = {} | ||
| 75 | for _, tuple in ipairs(tuples) do | ||
| 76 | local k, v = _tl_table_unpack(tuple) | ||
| 77 | k = multipart.url_escape(k) | ||
| 78 | local buffer = { 'Content-Disposition: form-data; name="' .. k .. '"' } | ||
| 79 | local content | ||
| 80 | if type(v) == "table" then | ||
| 81 | buffer[1] = buffer[1] .. ('; filename="' .. v.fname:gsub(".*[/\\]", "") .. '"') | ||
| 82 | table.insert(buffer, "Content-type: " .. v:mime()) | ||
| 83 | content = v:content() | ||
| 84 | else | ||
| 85 | content = v | ||
| 86 | end | ||
| 87 | table.insert(buffer, "") | ||
| 88 | table.insert(buffer, content) | ||
| 89 | table.insert(chunks, table.concat(buffer, "\r\n")) | ||
| 90 | end | ||
| 91 | local boundary | ||
| 92 | while not boundary do | ||
| 93 | boundary = "Boundary" .. rand_string(16) | ||
| 94 | for _, chunk in ipairs(chunks) do | ||
| 95 | if chunk:find(boundary) then | ||
| 96 | boundary = nil | ||
| 97 | break | ||
| 98 | end | ||
| 99 | end | ||
| 100 | end | ||
| 101 | local inner = "\r\n--" .. boundary .. "\r\n" | ||
| 102 | return table.concat({ "--", boundary, "\r\n", | ||
| 103 | table.concat(chunks, inner), | ||
| 104 | "\r\n", "--", boundary, "--", "\r\n", }), boundary | ||
| 105 | end | ||
| 106 | |||
| 107 | function multipart.new_file(fname, mime) | ||
| 108 | local self = {} | ||
| 109 | |||
| 110 | setmetatable(self, { __index = File }) | ||
| 111 | |||
| 112 | self.fname = fname | ||
| 113 | self.mimetype = mime | ||
| 114 | return self | ||
| 115 | end | ||
| 116 | |||
| 117 | 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 @@ | |||
| 1 | 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 | ||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | local core = require("luarocks.core.util") | ||
| 8 | local cfg = require("luarocks.core.cfg") | ||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | local util = {Fn = {}, } | ||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | util.cleanup_path = core.cleanup_path | ||
| 33 | util.split_string = core.split_string | ||
| 34 | util.sortedpairs = core.sortedpairs | ||
| 35 | util.deep_merge = core.deep_merge | ||
| 36 | util.deep_merge_under = core.deep_merge_under | ||
| 37 | util.popen_read = core.popen_read | ||
| 38 | util.show_table = core.show_table | ||
| 39 | util.printerr = core.printerr | ||
| 40 | util.warning = core.warning | ||
| 41 | util.keys = core.keys | ||
| 42 | util.matchquote = core.matchquote | ||
| 43 | |||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | |||
| 49 | |||
| 50 | local scheduled_functions = {} | ||
| 51 | |||
| 52 | |||
| 53 | |||
| 54 | |||
| 55 | |||
| 56 | |||
| 57 | |||
| 58 | |||
| 59 | function util.schedule_function(f, ...) | ||
| 60 | local item = { fn = f, args = _tl_table_pack(...) } | ||
| 61 | table.insert(scheduled_functions, item) | ||
| 62 | return item | ||
| 63 | end | ||
| 64 | |||
| 65 | |||
| 66 | |||
| 67 | |||
| 68 | |||
| 69 | function util.remove_scheduled_function(item) | ||
| 70 | for k, v in ipairs(scheduled_functions) do | ||
| 71 | if v == item then | ||
| 72 | table.remove(scheduled_functions, k) | ||
| 73 | return | ||
| 74 | end | ||
| 75 | end | ||
| 76 | end | ||
| 77 | |||
| 78 | |||
| 79 | |||
| 80 | |||
| 81 | |||
| 82 | |||
| 83 | function util.run_scheduled_functions() | ||
| 84 | local fs = require("luarocks.fs") | ||
| 85 | if fs.change_dir_to_root then | ||
| 86 | fs.change_dir_to_root() | ||
| 87 | end | ||
| 88 | for i = #scheduled_functions, 1, -1 do | ||
| 89 | local item = scheduled_functions[i] | ||
| 90 | item.fn(_tl_table_unpack(item.args, 1, item.args.n)) | ||
| 91 | end | ||
| 92 | end | ||
| 93 | |||
| 94 | local var_format_pattern = "%$%((%a[%a%d_]+)%)" | ||
| 95 | |||
| 96 | |||
| 97 | |||
| 98 | |||
| 99 | |||
| 100 | |||
| 101 | |||
| 102 | |||
| 103 | |||
| 104 | |||
| 105 | |||
| 106 | function util.warn_if_not_used(var_defs, needed_set, msg) | ||
| 107 | local seen = {} | ||
| 108 | for _, val in pairs(var_defs) do | ||
| 109 | for used in val:gmatch(var_format_pattern) do | ||
| 110 | seen[used] = true | ||
| 111 | end | ||
| 112 | end | ||
| 113 | for var, _ in pairs(needed_set) do | ||
| 114 | if not seen[var] then | ||
| 115 | util.warning(msg:format(var)) | ||
| 116 | end | ||
| 117 | end | ||
| 118 | end | ||
| 119 | |||
| 120 | |||
| 121 | |||
| 122 | |||
| 123 | local function warn_failed_matches(line) | ||
| 124 | local any_failed = false | ||
| 125 | if line:match(var_format_pattern) then | ||
| 126 | for unmatched in line:gmatch(var_format_pattern) do | ||
| 127 | util.warning("unmatched variable " .. unmatched) | ||
| 128 | any_failed = true | ||
| 129 | end | ||
| 130 | end | ||
| 131 | return any_failed | ||
| 132 | end | ||
| 133 | |||
| 134 | |||
| 135 | |||
| 136 | |||
| 137 | |||
| 138 | |||
| 139 | |||
| 140 | |||
| 141 | |||
| 142 | function util.variable_substitutions(tbl, vars) | ||
| 143 | |||
| 144 | local updated = {} | ||
| 145 | for k, v in pairs(tbl) do | ||
| 146 | if type(v) == "string" then | ||
| 147 | updated[k] = string.gsub(v, var_format_pattern, vars) | ||
| 148 | if warn_failed_matches(updated[k]) then | ||
| 149 | updated[k] = updated[k]:gsub(var_format_pattern, "") | ||
| 150 | end | ||
| 151 | end | ||
| 152 | end | ||
| 153 | for k, v in pairs(updated) do | ||
| 154 | tbl[k] = v | ||
| 155 | end | ||
| 156 | end | ||
| 157 | |||
| 158 | function util.lua_versions(sort) | ||
| 159 | local versions = { "5.1", "5.2", "5.3", "5.4" } | ||
| 160 | local i = 0 | ||
| 161 | if sort == "descending" then | ||
| 162 | i = #versions + 1 | ||
| 163 | return function() | ||
| 164 | i = i - 1 | ||
| 165 | return versions[i] | ||
| 166 | end | ||
| 167 | else | ||
| 168 | return function() | ||
| 169 | i = i + 1 | ||
| 170 | return versions[i] | ||
| 171 | end | ||
| 172 | end | ||
| 173 | end | ||
| 174 | |||
| 175 | function util.lua_path_variables() | ||
| 176 | local lpath_var = "LUA_PATH" | ||
| 177 | local lcpath_var = "LUA_CPATH" | ||
| 178 | |||
| 179 | local lv = cfg.lua_version:gsub("%.", "_") | ||
| 180 | if lv ~= "5_1" then | ||
| 181 | if os.getenv("LUA_PATH_" .. lv) then | ||
| 182 | lpath_var = "LUA_PATH_" .. lv | ||
| 183 | end | ||
| 184 | if os.getenv("LUA_CPATH_" .. lv) then | ||
| 185 | lcpath_var = "LUA_CPATH_" .. lv | ||
| 186 | end | ||
| 187 | end | ||
| 188 | return lpath_var, lcpath_var | ||
| 189 | end | ||
| 190 | |||
| 191 | function util.starts_with(s, prefix) | ||
| 192 | return s:sub(1, #prefix) == prefix | ||
| 193 | end | ||
| 194 | |||
| 195 | |||
| 196 | function util.printout(...) | ||
| 197 | io.stdout:write(table.concat({ ... }, "\t")) | ||
| 198 | io.stdout:write("\n") | ||
| 199 | end | ||
| 200 | |||
| 201 | function util.title(msg, porcelain, underline) | ||
| 202 | if porcelain then return end | ||
| 203 | util.printout() | ||
| 204 | util.printout(msg) | ||
| 205 | util.printout((underline or "-"):rep(#msg)) | ||
| 206 | util.printout() | ||
| 207 | end | ||
| 208 | |||
| 209 | function util.this_program(default) | ||
| 210 | local i = 1 | ||
| 211 | local last, cur = default, default | ||
| 212 | while i do | ||
| 213 | local dbg = debug and debug.getinfo(i, "S") | ||
| 214 | if not dbg then break end | ||
| 215 | last = cur | ||
| 216 | cur = dbg.source | ||
| 217 | i = i + 1 | ||
| 218 | end | ||
| 219 | local prog = last:sub(1, 1) == "@" and last:sub(2) or last | ||
| 220 | |||
| 221 | |||
| 222 | local lrdir, binpath = prog:match("^(.*)/lib/luarocks/rocks%-[0-9.]*/[^/]+/[^/]+(/bin/[^/]+)$") | ||
| 223 | if lrdir then | ||
| 224 | |||
| 225 | return lrdir .. binpath | ||
| 226 | end | ||
| 227 | |||
| 228 | return prog | ||
| 229 | end | ||
| 230 | |||
| 231 | function util.format_rock_name(name, namespace, version) | ||
| 232 | return (namespace and namespace .. "/" or "") .. name .. (version and " " .. version or "") | ||
| 233 | end | ||
| 234 | |||
| 235 | function util.deps_mode_option(parser, program) | ||
| 236 | |||
| 237 | parser:option("--deps-mode", "How to handle dependencies. Four modes are supported:\n" .. | ||
| 238 | "* all - use all trees from the rocks_trees list for finding dependencies\n" .. | ||
| 239 | "* one - use only the current tree (possibly set with --tree)\n" .. | ||
| 240 | "* order - use trees based on order (use the current tree and all " .. | ||
| 241 | "trees below it on the rocks_trees list)\n" .. | ||
| 242 | "* none - ignore dependencies altogether.\n" .. | ||
| 243 | "The default mode may be set with the deps_mode entry in the configuration file.\n" .. | ||
| 244 | 'The current default is "' .. cfg.deps_mode .. '".\n' .. | ||
| 245 | "Type '" .. util.this_program(program or "luarocks") .. "' with no " .. | ||
| 246 | "arguments to see your list of rocks trees."): | ||
| 247 | argname("<mode>"): | ||
| 248 | choices({ "all", "one", "order", "none" }) | ||
| 249 | parser:flag("--nodeps"):hidden(true) | ||
| 250 | end | ||
| 251 | |||
| 252 | function util.see_help(command, program) | ||
| 253 | return "See '" .. util.this_program(program or "luarocks") .. ' help' .. (command and " " .. command or "") .. "'." | ||
| 254 | end | ||
| 255 | |||
| 256 | function util.see_also(text) | ||
| 257 | local see_also = "See also:\n" | ||
| 258 | if text then | ||
| 259 | see_also = see_also .. text .. "\n" | ||
| 260 | end | ||
| 261 | return see_also .. " '" .. util.this_program("luarocks") .. " help' for general options and configuration." | ||
| 262 | end | ||
| 263 | |||
| 264 | function util.announce_install(rockspec) | ||
| 265 | local path = require("luarocks.path") | ||
| 266 | |||
| 267 | local suffix = "" | ||
| 268 | if rockspec.description and rockspec.description.license then | ||
| 269 | suffix = " (license: " .. rockspec.description.license .. ")" | ||
| 270 | end | ||
| 271 | |||
| 272 | util.printout(rockspec.name .. " " .. rockspec.version .. " is now installed in " .. path.root_dir(cfg.root_dir) .. suffix) | ||
| 273 | util.printout() | ||
| 274 | end | ||
| 275 | |||
| 276 | |||
| 277 | |||
| 278 | |||
| 279 | |||
| 280 | |||
| 281 | |||
| 282 | local function collect_rockspecs(versions, paths, unnamed_paths, subdir) | ||
| 283 | local fs = require("luarocks.fs") | ||
| 284 | local dir = require("luarocks.dir") | ||
| 285 | local path = require("luarocks.path") | ||
| 286 | local vers = require("luarocks.core.vers") | ||
| 287 | if fs.is_dir(subdir) then | ||
| 288 | for file in fs.dir(subdir) do | ||
| 289 | file = dir.path(subdir, file) | ||
| 290 | |||
| 291 | if file:match("rockspec$") and fs.is_file(file) then | ||
| 292 | local rock, version = path.parse_name(file) | ||
| 293 | |||
| 294 | if rock then | ||
| 295 | if not versions[rock] or vers.compare_versions(version, versions[rock]) then | ||
| 296 | versions[rock] = version | ||
| 297 | paths[rock] = file | ||
| 298 | end | ||
| 299 | else | ||
| 300 | table.insert(unnamed_paths, file) | ||
| 301 | end | ||
| 302 | end | ||
| 303 | end | ||
| 304 | end | ||
| 305 | end | ||
| 306 | |||
| 307 | |||
| 308 | |||
| 309 | function util.get_default_rockspec() | ||
| 310 | |||
| 311 | local versions = {} | ||
| 312 | local paths = {} | ||
| 313 | local unnamed_paths = {} | ||
| 314 | |||
| 315 | collect_rockspecs(versions, paths, unnamed_paths, ".") | ||
| 316 | collect_rockspecs(versions, paths, unnamed_paths, "rockspec") | ||
| 317 | collect_rockspecs(versions, paths, unnamed_paths, "rockspecs") | ||
| 318 | |||
| 319 | if #unnamed_paths > 0 then | ||
| 320 | |||
| 321 | |||
| 322 | if #unnamed_paths > 1 then | ||
| 323 | return nil, "Please specify which rockspec file to use." | ||
| 324 | else | ||
| 325 | return unnamed_paths[1] | ||
| 326 | end | ||
| 327 | else | ||
| 328 | local fs = require("luarocks.fs") | ||
| 329 | local dir = require("luarocks.dir") | ||
| 330 | local basename = dir.base_name(fs.current_dir()) | ||
| 331 | |||
| 332 | if paths[basename] then | ||
| 333 | return paths[basename] | ||
| 334 | end | ||
| 335 | |||
| 336 | local rock = next(versions) | ||
| 337 | |||
| 338 | if rock then | ||
| 339 | |||
| 340 | if next(versions, rock) then | ||
| 341 | return nil, "Please specify which rockspec file to use." | ||
| 342 | else | ||
| 343 | return paths[rock] | ||
| 344 | end | ||
| 345 | else | ||
| 346 | return nil, "Argument missing: please specify a rockspec to use on current directory." | ||
| 347 | end | ||
| 348 | end | ||
| 349 | end | ||
| 350 | |||
| 351 | |||
| 352 | |||
| 353 | |||
| 354 | function util.LQ(s) | ||
| 355 | return ("%q"):format(s) | ||
| 356 | end | ||
| 357 | |||
| 358 | |||
| 359 | |||
| 360 | |||
| 361 | function util.split_namespace(ns_name) | ||
| 362 | local p1, p2 = ns_name:match("^([^/]+)/([^/]+)$") | ||
| 363 | if p1 then | ||
| 364 | return p2, p1 | ||
| 365 | end | ||
| 366 | return ns_name | ||
| 367 | end | ||
| 368 | |||
| 369 | |||
| 370 | function util.namespaced_name_action(args, target, ns_name) | ||
| 371 | |||
| 372 | if not ns_name then | ||
| 373 | return | ||
| 374 | end | ||
| 375 | |||
| 376 | if ns_name:match("%.rockspec$") or ns_name:match("%.rock$") then | ||
| 377 | args[target] = ns_name | ||
| 378 | else | ||
| 379 | local name, namespace = util.split_namespace(ns_name) | ||
| 380 | args[target] = name:lower() | ||
| 381 | if namespace then | ||
| 382 | args.namespace = namespace:lower() | ||
| 383 | end | ||
| 384 | end | ||
| 385 | end | ||
| 386 | |||
| 387 | function util.deep_copy(tbl) | ||
| 388 | local copy = {} | ||
| 389 | for k, v in pairs(tbl) do | ||
| 390 | if type(v) == "table" then | ||
| 391 | copy[k] = util.deep_copy(v) | ||
| 392 | else | ||
| 393 | copy[k] = v | ||
| 394 | end | ||
| 395 | end | ||
| 396 | return copy | ||
| 397 | end | ||
| 398 | |||
| 399 | |||
| 400 | |||
| 401 | |||
| 402 | function util.exists(file) | ||
| 403 | local fd, _, code = io.open(file, "r") | ||
| 404 | if code == 13 then | ||
| 405 | |||
| 406 | |||
| 407 | return true | ||
| 408 | end | ||
| 409 | if fd then | ||
| 410 | fd:close() | ||
| 411 | return true | ||
| 412 | end | ||
| 413 | return false | ||
| 414 | end | ||
| 415 | |||
| 416 | function util.lua_is_wrapper(interp) | ||
| 417 | local fd, err = io.open(interp, "r") | ||
| 418 | if not fd then | ||
| 419 | return nil, err | ||
| 420 | end | ||
| 421 | local data | ||
| 422 | data, err = fd:read(1000) | ||
| 423 | fd:close() | ||
| 424 | if not data then | ||
| 425 | return nil, err | ||
| 426 | end | ||
| 427 | return not not data:match("LUAROCKS_SYSCONFDIR") | ||
| 428 | end | ||
| 429 | |||
| 430 | do | ||
| 431 | local function Q(pathname) | ||
| 432 | if pathname:match("^.:") then | ||
| 433 | return pathname:sub(1, 2) .. '"' .. pathname:sub(3) .. '"' | ||
| 434 | end | ||
| 435 | return '"' .. pathname .. '"' | ||
| 436 | end | ||
| 437 | |||
| 438 | function util.check_lua_version(lua, luaver) | ||
| 439 | if not util.exists(lua) then | ||
| 440 | return nil | ||
| 441 | end | ||
| 442 | local lv = util.popen_read(Q(lua) .. ' -e "io.write(_VERSION:sub(5))"') | ||
| 443 | if lv == "" then | ||
| 444 | return nil | ||
| 445 | end | ||
| 446 | if luaver and luaver ~= lv then | ||
| 447 | return nil | ||
| 448 | end | ||
| 449 | return lv | ||
| 450 | end | ||
| 451 | |||
| 452 | function util.get_luajit_version() | ||
| 453 | if cfg.cache.luajit_version_checked then | ||
| 454 | return cfg.cache.luajit_version | ||
| 455 | end | ||
| 456 | cfg.cache.luajit_version_checked = true | ||
| 457 | |||
| 458 | if not cfg.variables.LUA then | ||
| 459 | return nil | ||
| 460 | end | ||
| 461 | |||
| 462 | local ljv | ||
| 463 | if cfg.lua_version == "5.1" then | ||
| 464 | |||
| 465 | ljv = util.popen_read(Q(cfg.variables.LUA) .. ' -e "io.write(tostring(jit and jit.version:gsub([[^%S+ (%S+).*]], [[%1]])))"') | ||
| 466 | if ljv == "nil" then | ||
| 467 | ljv = nil | ||
| 468 | end | ||
| 469 | end | ||
| 470 | cfg.cache.luajit_version = ljv | ||
| 471 | return ljv | ||
| 472 | end | ||
| 473 | |||
| 474 | local find_lua_bindir | ||
| 475 | do | ||
| 476 | local exe_suffix = (package.config:sub(1, 1) == "\\" and ".exe" or "") | ||
| 477 | |||
| 478 | local function insert_lua_variants(names, luaver) | ||
| 479 | local variants = { | ||
| 480 | "lua" .. luaver .. exe_suffix, | ||
| 481 | "lua" .. luaver:gsub("%.", "") .. exe_suffix, | ||
| 482 | "lua-" .. luaver .. exe_suffix, | ||
| 483 | "lua-" .. luaver:gsub("%.", "") .. exe_suffix, | ||
| 484 | } | ||
| 485 | for _, name in ipairs(variants) do | ||
| 486 | table.insert(names, name) | ||
| 487 | end | ||
| 488 | end | ||
| 489 | |||
| 490 | find_lua_bindir = function(prefix, luaver, verbose) | ||
| 491 | local names = {} | ||
| 492 | if luaver then | ||
| 493 | insert_lua_variants(names, luaver) | ||
| 494 | else | ||
| 495 | for v in util.lua_versions("descending") do | ||
| 496 | insert_lua_variants(names, v) | ||
| 497 | end | ||
| 498 | end | ||
| 499 | if luaver == "5.1" or not luaver then | ||
| 500 | table.insert(names, "luajit" .. exe_suffix) | ||
| 501 | end | ||
| 502 | table.insert(names, "lua" .. exe_suffix) | ||
| 503 | |||
| 504 | local tried = {} | ||
| 505 | local dir_sep = package.config:sub(1, 1) | ||
| 506 | for _, d in ipairs({ prefix .. dir_sep .. "bin", prefix }) do | ||
| 507 | for _, name in ipairs(names) do | ||
| 508 | local lua = d .. dir_sep .. name | ||
| 509 | local is_wrapper, err = util.lua_is_wrapper(lua) | ||
| 510 | if is_wrapper == false then | ||
| 511 | local lv = util.check_lua_version(lua, luaver) | ||
| 512 | if lv then | ||
| 513 | return lua, d, lv | ||
| 514 | end | ||
| 515 | elseif is_wrapper == true or err == nil then | ||
| 516 | table.insert(tried, lua) | ||
| 517 | else | ||
| 518 | table.insert(tried, string.format("%-13s (%s)", lua, err)) | ||
| 519 | end | ||
| 520 | end | ||
| 521 | end | ||
| 522 | local interp = luaver and | ||
| 523 | ("Lua " .. luaver .. " interpreter") or | ||
| 524 | "Lua interpreter" | ||
| 525 | return nil, interp .. " not found at " .. prefix .. "\n" .. | ||
| 526 | (verbose and "Tried:\t" .. table.concat(tried, "\n\t") or "") | ||
| 527 | end | ||
| 528 | end | ||
| 529 | |||
| 530 | function util.find_lua(prefix, luaver, verbose) | ||
| 531 | local lua, bindir | ||
| 532 | lua, bindir, luaver = find_lua_bindir(prefix, luaver, verbose) | ||
| 533 | if not lua then | ||
| 534 | return nil, bindir | ||
| 535 | end | ||
| 536 | |||
| 537 | return { | ||
| 538 | lua_version = luaver, | ||
| 539 | lua = lua, | ||
| 540 | lua_dir = prefix, | ||
| 541 | lua_bindir = bindir, | ||
| 542 | } | ||
| 543 | end | ||
| 544 | end | ||
| 545 | |||
| 546 | |||
| 547 | |||
| 548 | |||
| 549 | |||
| 550 | |||
| 551 | |||
| 552 | |||
| 553 | |||
| 554 | function util.get_rocks_provided(rockspec) | ||
| 555 | |||
| 556 | if not rockspec and cfg.cache.rocks_provided then | ||
| 557 | return cfg.cache.rocks_provided | ||
| 558 | end | ||
| 559 | |||
| 560 | local rocks_provided = {} | ||
| 561 | |||
| 562 | local lv = cfg.lua_version | ||
| 563 | |||
| 564 | rocks_provided["lua"] = lv .. "-1" | ||
| 565 | |||
| 566 | if lv == "5.2" then | ||
| 567 | rocks_provided["bit32"] = lv .. "-1" | ||
| 568 | end | ||
| 569 | |||
| 570 | if lv == "5.3" or lv == "5.4" then | ||
| 571 | rocks_provided["utf8"] = lv .. "-1" | ||
| 572 | end | ||
| 573 | |||
| 574 | if lv == "5.1" then | ||
| 575 | local ljv = util.get_luajit_version() | ||
| 576 | if ljv then | ||
| 577 | rocks_provided["luabitop"] = ljv .. "-1" | ||
| 578 | if (not rockspec) or rockspec:format_is_at_least("3.0") then | ||
| 579 | rocks_provided["luajit"] = ljv .. "-1" | ||
| 580 | end | ||
| 581 | end | ||
| 582 | end | ||
| 583 | |||
| 584 | if cfg.rocks_provided then | ||
| 585 | util.deep_merge_under(rocks_provided, cfg.rocks_provided) | ||
| 586 | end | ||
| 587 | |||
| 588 | if not rockspec then | ||
| 589 | cfg.cache.rocks_provided = rocks_provided | ||
| 590 | end | ||
| 591 | |||
| 592 | return rocks_provided | ||
| 593 | end | ||
| 594 | |||
| 595 | function util.remove_doc_dir(name, version) | ||
| 596 | local path = require("luarocks.path") | ||
| 597 | local fs = require("luarocks.fs") | ||
| 598 | local dir = require("luarocks.dir") | ||
| 599 | |||
| 600 | local install_dir = path.install_dir(name, version) | ||
| 601 | for _, f in ipairs(fs.list_dir(install_dir)) do | ||
| 602 | local doc_dirs = { "doc", "docs" } | ||
| 603 | for _, d in ipairs(doc_dirs) do | ||
| 604 | if f == d then | ||
| 605 | fs.delete(dir.path(install_dir, f)) | ||
| 606 | end | ||
| 607 | end | ||
| 608 | end | ||
| 609 | end | ||
| 610 | |||
| 611 | return util | ||
