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 | |
parent | 259e0ca4855d775dc8ba61f6d30b1cc3b493ae61 (diff) | |
download | luarocks-ba518aa110d2fe84f6163c785da9fc7ab4ea9eae.tar.gz luarocks-ba518aa110d2fe84f6163c785da9fc7ab4ea9eae.tar.bz2 luarocks-ba518aa110d2fe84f6163c785da9fc7ab4ea9eae.zip |
Teal: add generated modules
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 | ||