aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHisham Muhammad <hisham@gobolinux.org>2024-08-22 17:48:58 -0300
committerHisham Muhammad <hisham@gobolinux.org>2024-10-21 13:30:51 -0300
commitce5ed124744644d414121c88c94cc67f3437362c (patch)
treea408e5b1da5c7a2204e1839cf757ebd7a829d9d9 /src
parentd99f13f80e2b53a41d6d07d185fcef636f414c61 (diff)
downloadluarocks-ce5ed124744644d414121c88c94cc67f3437362c.tar.gz
luarocks-ce5ed124744644d414121c88c94cc67f3437362c.tar.bz2
luarocks-ce5ed124744644d414121c88c94cc67f3437362c.zip
Teal: convert luarocks.loader
Diffstat (limited to 'src')
-rw-r--r--src/luarocks/loader.tl248
1 files changed, 156 insertions, 92 deletions
diff --git a/src/luarocks/loader.tl b/src/luarocks/loader.tl
index 772fdfcb..527f1255 100644
--- a/src/luarocks/loader.tl
+++ b/src/luarocks/loader.tl
@@ -5,20 +5,20 @@
5-- used to load previous modules, so that the loader chooses versions 5-- used to load previous modules, so that the loader chooses versions
6-- that are declared to be compatible with the ones loaded earlier. 6-- that are declared to be compatible with the ones loaded earlier.
7 7
8-- luacheck: globals luarocks
9
10local loaders = package.loaders or package.searchers 8local loaders = package.loaders or package.searchers
11local require, ipairs, table, type, next, tostring, error = 9local require, ipairs, table, type, next, tostring, error =
12 require, ipairs, table, type, next, tostring, error 10 require, ipairs, table, type, next, tostring, error
13local unpack = unpack or table.unpack
14 11
15local loader = {} 12local record loader
13 context: {string: string}
14 luarocks_loader: function(string): LoaderFn | string, any
15end
16 16
17local is_clean = not package.loaded["luarocks.core.cfg"] 17local is_clean = not package.loaded["luarocks.core.cfg"]
18 18
19-- This loader module depends only on core modules. 19-- This loader module depends only on core modules.
20local cfg = require("luarocks.core.cfg") 20local cfg = require("luarocks.core.cfg")
21local cfg_ok, err = cfg.init() 21local cfg_ok, _err = cfg.init()
22if cfg_ok then 22if cfg_ok then
23 cfg.init_package_paths() 23 cfg.init_package_paths()
24end 24end
@@ -26,12 +26,35 @@ end
26local path = require("luarocks.core.path") 26local path = require("luarocks.core.path")
27local manif = require("luarocks.core.manif") 27local manif = require("luarocks.core.manif")
28local vers = require("luarocks.core.vers") 28local vers = require("luarocks.core.vers")
29local require = nil -- luacheck: ignore 411 29
30local type Version = require("luarocks.core.types.version").Version
31local type TreeManifest = require("luarocks.core.types.manifest").Tree_manifest
32local type Tree = require("luarocks.core.types.tree").Tree
33local type FilterFn = function(string, string, string, Tree, integer): string
34local type LoaderFn = function()
35
36local record Provider
37 name: string
38 version: Version
39 module_name: string
40 tree: TreeManifest
41end
42
30-------------------------------------------------------------------------------- 43--------------------------------------------------------------------------------
44-- Backwards compatibility
45--------------------------------------------------------------------------------
46
47local record LuaRocksGlobal
48 loader: loader
49end
50
51global luarocks: LuaRocksGlobal
31 52
32-- Workaround for wrappers produced by older versions of LuaRocks 53-- Workaround for wrappers produced by older versions of LuaRocks
33local temporary_global = false 54local temporary_global = false
34local status, luarocks_value = pcall(function() return luarocks end) 55local status, luarocks_value = pcall(function(): LuaRocksGlobal
56 return luarocks
57end)
35if status and luarocks_value then 58if status and luarocks_value then
36 -- The site_config.lua file generated by old versions uses module(), 59 -- The site_config.lua file generated by old versions uses module(),
37 -- so it produces a global `luarocks` table. Since we have the table, 60 -- so it produces a global `luarocks` table. Since we have the table,
@@ -52,16 +75,18 @@ else
52 end 75 end
53end 76end
54 77
78--------------------------------------------------------------------------------
79-- Context management
80--------------------------------------------------------------------------------
81
55loader.context = {} 82loader.context = {}
56 83
57--- Process the dependencies of a package to determine its dependency 84--- Process the dependencies of a package to determine its dependency
58-- chain for loading modules. 85-- chain for loading modules.
59-- @param name string: The name of an installed rock. 86--
60-- @param version string: The version of the rock, in string format 87-- @param name The name of an installed rock.
61function loader.add_context(name, version) 88-- @param version The version of the rock, in string format
62 -- assert(type(name) == "string") 89function loader.add_context(name: string, version: string)
63 -- assert(type(version) == "string")
64
65 if temporary_global then 90 if temporary_global then
66 -- The first thing a wrapper does is to call add_context. 91 -- The first thing a wrapper does is to call add_context.
67 -- From here on, it's safe to clean the global environment. 92 -- From here on, it's safe to clean the global environment.
@@ -71,17 +96,19 @@ function loader.add_context(name, version)
71 96
72 local tree_manifests = manif.load_rocks_tree_manifests() 97 local tree_manifests = manif.load_rocks_tree_manifests()
73 if not tree_manifests then 98 if not tree_manifests then
74 return nil 99 return
75 end 100 end
76 101
77 return manif.scan_dependencies(name, version, tree_manifests, loader.context) 102 manif.scan_dependencies(name, version, tree_manifests, loader.context)
78end 103end
79 104
80--- Internal sorting function. 105--- Internal sorting function.
81-- @param a table: A provider table. 106--
82-- @param b table: Another provider table. 107-- @param a A provider table.
83-- @return boolean: True if the version of a is greater than that of b. 108-- @param b Another provider table.
84local function sort_versions(a,b) 109--
110-- @return true if the version of a is greater than that of b.
111local function sort_versions(a: Provider, b: Provider): boolean
85 return a.version > b.version 112 return a.version > b.version
86end 113end
87 114
@@ -92,67 +119,94 @@ end
92-- the version 2.0.2 needs to be loaded and it is not the current 119-- the version 2.0.2 needs to be loaded and it is not the current
93-- version, the module requested for the other loaders will be 120-- version, the module requested for the other loaders will be
94-- "socket.core_2_0_2". 121-- "socket.core_2_0_2".
95-- @param module The module name requested by the user, such as "socket.core" 122--
96-- @param name The rock name, such as "luasocket" 123-- @param module The module name requested by the user, e.g. "socket.core"
97-- @param version The rock version, such as "2.0.2-1" 124-- @param name The rock name, such as "luasocket"
98-- @param module_name The actual module name, such as "socket.core" or "socket.core_2_0_2". 125-- @param version The rock version, such as "2.0.2-1"
99-- @return table or (nil, string): The module table as returned by some other loader, 126-- @param module_name Actual module name, such as "socket.core_2_0_2"
100-- or nil followed by an error message if no other loader managed to load the module. 127--
101local function call_other_loaders(module, name, version, module_name) 128-- @return The loader function returned or an error message.
129-- @return Additional loader data, if returned by the loader.
130local function call_other_loaders(module: string, name: string, version: string, module_name: string): LoaderFn | string, any
102 for _, a_loader in ipairs(loaders) do 131 for _, a_loader in ipairs(loaders) do
103 if a_loader ~= loader.luarocks_loader then 132 if a_loader ~= loader.luarocks_loader then
104 local results = { a_loader(module_name) } 133 local results: {any} = { a_loader(module_name) }
105 if type(results[1]) == "function" then 134 local f = results[1]
106 return unpack(results) 135 if f is LoaderFn then
136 if #results == 2 then
137 return f, results[2]
138 else
139 return f
140 end
107 end 141 end
108 end 142 end
109 end 143 end
110 return "Failed loading module "..module.." in LuaRocks rock "..name.." "..version 144 return "Failed loading module " .. module .. " in LuaRocks rock " .. name .. " " .. version
111end 145end
112 146
113local function add_providers(providers, entries, tree, module, filter_file_name) 147--- Find entries which provide the wanted module in the tree,
148-- and store them in the array of providers for later sorting.
149--
150-- @param providers The array of providers being accumulated into
151-- @param entries The packages which provide the module
152-- @param tree TreeManifest where filenames can be found.
153-- @param module The module name being looked up
154-- @param filter_name A filtering function to adjust the filename.
155--
156-- @return If the current LuaRocks loader context already resolved this
157-- dependency based on other dependencies, return the name of the module
158-- for immediate use.
159-- @return Version of the module for immediate use, if matched.
160-- @return File name of the module for immediate use, if matched.
161local function add_providers(providers: {Provider}, entries: {string}, tree: TreeManifest, module: string, filter_name: FilterFn): string, string, string
114 for i, entry in ipairs(entries) do 162 for i, entry in ipairs(entries) do
115 local name, version = entry:match("^([^/]*)/(.*)$") 163 local name, version = entry:match("^([^/]*)/(.*)$")
164
116 local file_name = tree.manifest.repository[name][version][1].modules[module] 165 local file_name = tree.manifest.repository[name][version][1].modules[module]
117 if type(file_name) ~= "string" then 166 if type(file_name) ~= "string" then
118 error("Invalid data in manifest file for module "..tostring(module).." (invalid data for "..tostring(name).." "..tostring(version)..")") 167 error("Invalid data in manifest file for module " .. tostring(module) .. " (invalid data for " .. tostring(name) .. " " .. tostring(version) .. ")")
119 end 168 end
120 file_name = filter_file_name(file_name, name, version, tree.tree, i) 169
170 file_name = filter_name(file_name, name, version, tree.tree, i)
171
121 if loader.context[name] == version then 172 if loader.context[name] == version then
122 return name, version, file_name 173 return name, version, file_name
123 end 174 end
124 version = vers.parse_version(version) 175
125 table.insert(providers, {name = name, version = version, module_name = file_name, tree = tree}) 176 table.insert(providers, {
177 name = name,
178 version = vers.parse_version(version),
179 module_name = file_name,
180 tree = tree
181 })
126 end 182 end
127end 183end
128 184
129--- Search for a module in the rocks trees 185--- Search for a module in the rocks trees.
130-- @param module string: module name (eg. "socket.core") 186--
131-- @param filter_file_name function(string, string, string, string, number): 187-- @param module module name (eg. "socket.core")
132-- a function that takes the module file name (eg "socket/core.so"), the rock name 188-- @param filter_name a function that takes the module file name
133-- (eg "luasocket"), the version (eg "2.0.2-1"), the path of the rocks tree 189-- (eg "socket/core.so"), the rock name (eg "luasocket"),
190-- the version (eg "2.0.2-1"), the path of the rocks tree
134-- (eg "/usr/local"), and the numeric index of the matching entry, so the 191-- (eg "/usr/local"), and the numeric index of the matching entry, so the
135-- filter function can know if the matching module was the first entry or not. 192-- filter function can know if the matching module was the first entry or not.
136-- @return string, string, string, (string or table): 193--
137-- * name of the rock containing the module (eg. "luasocket") 194-- @return name of the rock containing the module (eg. "luasocket")
138-- * version of the rock (eg. "2.0.2-1") 195-- @return version of the rock (eg. "2.0.2-1")
139-- * return value of filter_file_name 196-- @return return value of filter_name
140-- * tree of the module (string or table in `tree_manifests` format) 197local function select_module(module: string, filter_name: FilterFn): string, string, string
141local function select_module(module, filter_file_name)
142 --assert(type(module) == "string")
143 --assert(type(filter_module_name) == "function")
144 198
145 local tree_manifests = manif.load_rocks_tree_manifests() 199 local tree_manifests = manif.load_rocks_tree_manifests()
146 if not tree_manifests then 200 if not tree_manifests then
147 return nil 201 return nil
148 end 202 end
149 203
150 local providers = {} 204 local providers: {Provider} = {}
151 local initmodule 205 local initmodule: string
152 for _, tree in ipairs(tree_manifests) do 206 for _, tree in ipairs(tree_manifests) do
153 local entries = tree.manifest.modules[module] 207 local entries = tree.manifest.modules[module]
154 if entries then 208 if entries then
155 local n, v, f = add_providers(providers, entries, tree, module, filter_file_name) 209 local n, v, f = add_providers(providers, entries, tree, module, filter_name)
156 if n then 210 if n then
157 return n, v, f 211 return n, v, f
158 end 212 end
@@ -160,7 +214,7 @@ local function select_module(module, filter_file_name)
160 initmodule = initmodule or module .. ".init" 214 initmodule = initmodule or module .. ".init"
161 entries = tree.manifest.modules[initmodule] 215 entries = tree.manifest.modules[initmodule]
162 if entries then 216 if entries then
163 local n, v, f = add_providers(providers, entries, tree, initmodule, filter_file_name) 217 local n, v, f = add_providers(providers, entries, tree, initmodule, filter_name)
164 if n then 218 if n then
165 return n, v, f 219 return n, v, f
166 end 220 end
@@ -171,46 +225,53 @@ local function select_module(module, filter_file_name)
171 if next(providers) then 225 if next(providers) then
172 table.sort(providers, sort_versions) 226 table.sort(providers, sort_versions)
173 local first = providers[1] 227 local first = providers[1]
174 return first.name, first.version.string, first.module_name, first.tree 228 return first.name, first.version.string, first.module_name
175 end 229 end
176end 230end
177 231
178--- Search for a module 232--- Filter operation for adjusting the versioned names when multiple packages provide
179-- @param module string: module name (eg. "socket.core") 233-- the same file.
180-- @return string, string, string, (string or table): 234--
181-- * name of the rock containing the module (eg. "luasocket") 235-- @param file_name The original filename
182-- * version of the rock (eg. "2.0.2-1") 236-- @param name The rock name
183-- * name of the module (eg. "socket.core", or "socket.core_2_0_2" if file is stored versioned). 237-- @param version The rock version
184-- * tree of the module (string or table in `tree_manifests` format) 238-- @param _tree (unused)
185local function pick_module(module) 239-- @param i The priority index, to determine whether to version the name.
186 return 240--
187 select_module(module, function(file_name, name, version, tree, i) 241-- @return A filename, which may be plain or versioned.
188 if i > 1 then 242-- (eg. "socket.core", or "socket.core_2_0_2" if file is stored versioned).
189 file_name = path.versioned_name(file_name, "", name, version) 243local function filter_module_name(file_name: string, name: string, version: string, _tree: Tree, i: integer): string
190 end 244 if i > 1 then
191 return path.path_to_module(file_name) 245 file_name = path.versioned_name(file_name, "", name, version)
192 end) 246 end
247 return path.path_to_module(file_name)
248end
249
250--- Search for a module.
251--
252-- @param module name of the module (eg. "socket.core")
253--
254-- @return name of the rock containing the module (eg. "luasocket")
255-- @return version of the rock (eg. "2.0.2-1")
256-- @return name of the module (eg. "socket.core", or "socket.core_2_0_2" if file is stored versioned).
257-- @return tree of the module (string or table in `tree_manifests` format)
258local function pick_module(module: string): string, string, string, string | TreeManifest
259 return select_module(module, filter_module_name)
193end 260end
194 261
195--- Return the pathname of the file that would be loaded for a module. 262--- Return the pathname of the file that would be loaded for a module.
196-- @param module string: module name (eg. "socket.core") 263--
197-- @param where string: places to look for the module. If `where` contains 264-- @param module module name (eg. "socket.core")
265-- @param where places to look for the module. If `where` contains
198-- "l", it will search using the LuaRocks loader; if it contains "p", 266-- "l", it will search using the LuaRocks loader; if it contains "p",
199-- it will look in the filesystem using package.path and package.cpath. 267-- it will look in the filesystem using package.path and package.cpath.
200-- You can use both at the same time. 268-- You can use both at the same time.
201-- @return If successful, it will return four values. 269--
202-- * If found using the LuaRocks loader, it will return: 270-- @return If found, filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so"),
203-- * filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so"), 271-- @return If found via the loader, the rock name; otherwise, "path" or "cpath"
204-- * rock name 272-- @return If found via the loader, the rock version; otherwise, nil
205-- * rock version 273-- @return If found via the loader, "l"; if found via package.path or package.cpath, "p".
206-- * "l" to indicate the match comes from the loader. 274function loader.which(module: string, where?: string): string, string, string, string
207-- * If found scanning package.path and package.cpath, it will return:
208-- * filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so"),
209-- * "path" or "cpath"
210-- * nil
211-- * "p" to indicate the match comes from scanning package.path and cpath.
212-- If unsuccessful, nothing is returned.
213function loader.which(module, where)
214 where = where or "l" 275 where = where or "l"
215 if where:match("l") then 276 if where:match("l") then
216 local rock_name, rock_version, file_name = select_module(module, path.which_i) 277 local rock_name, rock_version, file_name = select_module(module, path.which_i)
@@ -224,8 +285,8 @@ function loader.which(module, where)
224 end 285 end
225 if where:match("p") then 286 if where:match("p") then
226 local modpath = module:gsub("%.", "/") 287 local modpath = module:gsub("%.", "/")
227 for _, v in ipairs({"path", "cpath"}) do 288 for _, v in ipairs({package.path, package.cpath}) do
228 for p in package[v]:gmatch("([^;]+)") do 289 for p in v:gmatch("([^;]+)") do
229 local file_name = p:gsub("%?", modpath) -- luacheck: ignore 421 290 local file_name = p:gsub("%?", modpath) -- luacheck: ignore 421
230 local fd = io.open(file_name) 291 local fd = io.open(file_name)
231 if fd then 292 if fd then
@@ -238,18 +299,21 @@ function loader.which(module, where)
238end 299end
239 300
240--- Package loader for LuaRocks support. 301--- Package loader for LuaRocks support.
241-- A module is searched in installed rocks that match the 302-- See <a href="http://www.lua.org/manual/5.4/manual.html#pdf-require">require()</a>
303-- in the Lua reference manual for details on the require() mechanism.
304-- The LuaRocks loader works by searching in installed rocks that match the
242-- current LuaRocks context. If module is not part of the 305-- current LuaRocks context. If module is not part of the
243-- context, or if a context has not yet been set, the module 306-- context, or if a context has not yet been set, the module
244-- in the package with the highest version is used. 307-- in the package with the highest version is used.
245-- @param module string: The module name, like in plain require(). 308--
246-- @return table: The module table (typically), like in plain 309-- @param module The module name, like in plain require().
247-- require(). See <a href="http://www.lua.org/manual/5.1/manual.html#pdf-require">require()</a> 310--
248-- in the Lua reference manual for details. 311-- @return A function which can load the module found,
249function loader.luarocks_loader(module) 312-- or a string with an error message.
313function loader.luarocks_loader(module: string): LoaderFn | string, any
250 local name, version, module_name = pick_module(module) 314 local name, version, module_name = pick_module(module)
251 if not name then 315 if not name then
252 return "No LuaRocks module found for "..module 316 return "No LuaRocks module found for " .. module
253 else 317 else
254 loader.add_context(name, version) 318 loader.add_context(name, version)
255 return call_other_loaders(module, name, version, module_name) 319 return call_other_loaders(module, name, version, module_name)