1if not modules then modules = { } end modules ['util-lib'] = {
2 version = 1.001,
3 comment = "companion to luat-lib.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files",
7}
8
9
10
11
74
75local type = type
76local next = next
77local pcall = pcall
78local gsub = string.gsub
79local find = string.find
80local sort = table.sort
81local pathpart = file.pathpart
82local nameonly = file.nameonly
83local joinfile = file.join
84local removesuffix = file.removesuffix
85local addsuffix = file.addsuffix
86local findfile = resolvers.findfile
87local findfiles = resolvers.findfiles
88local expandpaths = resolvers.expandedpathlistfromvariable
89local qualifiedpath = file.is_qualified_path
90local isfile = lfs.isfile
91
92local done = false
93
94
95
96
97local function locate(required,version,trace,report,action)
98 if type(required) ~= "string" then
99 report("provide a proper library name")
100 return
101 end
102 if trace then
103 report("requiring library %a with version %a",required,version or "any")
104 end
105 local found_library = nil
106 local required_full = gsub(required,"%.","/")
107 local required_path = pathpart(required_full)
108 local required_base = nameonly(required_full)
109 if qualifiedpath(required) then
110
111 if isfile(addsuffix(required,os.libsuffix)) then
112 if trace then
113 report("qualified name %a found",required)
114 end
115 found_library = required
116 else
117 if trace then
118 report("qualified name %a not found",required)
119 end
120 end
121 else
122
123 local required_name = required_base .. "." .. os.libsuffix
124 local version = type(version) == "string" and version ~= "" and version or false
125
126 local engine = environment.ownmain or false
127
128 if trace and not done then
129 local list = expandpaths("lib")
130 for i=1,#list do
131 report("tds path %i: %s",i,list[i])
132 end
133 end
134
135 local function found(locate,asked_library,how,...)
136 if trace then
137 report("checking %s: %a",how,asked_library)
138 end
139 return locate(asked_library,...)
140 end
141 local function check(locate,...)
142 local found = nil
143 if version then
144 local asked_library = joinfile(required_path,version,required_name)
145 if trace then
146 report("checking %s: %a","with version",asked_library)
147 end
148 found = locate(asked_library,...)
149 end
150 if not found or found == "" then
151 local asked_library = joinfile(required_path,required_name)
152 if trace then
153 report("checking %s: %a","with version",asked_library)
154 end
155 found = locate(asked_library,...)
156 end
157 return found and found ~= "" and found or false
158 end
159
160
161
162 local function attempt(checkpattern)
163
164 if trace then
165 report("checking tds lib paths strictly")
166 end
167 local found = findfile and check(findfile,"lib")
168 if found and (not checkpattern or find(found,checkpattern)) then
169 return found
170 end
171
172 if trace then
173 report("checking tds lib paths with wildcard")
174 end
175 local asked_library = joinfile(required_path,".*",required_name)
176 if trace then
177 report("checking %s: %a","latest version",asked_library)
178 end
179 local list = findfiles(asked_library,"lib",true)
180 if list and #list > 0 then
181 sort(list)
182 local found = list[#list]
183 if found and (not checkpattern or find(found,checkpattern)) then
184 return found
185 end
186 end
187
188 if trace then
189 report("checking lib paths")
190 end
191 package.extralibpath(environment.ownpath)
192 local paths = package.libpaths()
193 local pattern = "/[^/]+%." .. os.libsuffix .. "$"
194 for i=1,#paths do
195 required_path = gsub(paths[i],pattern,"")
196 local found = check(lfs.isfound)
197 if type(found) == "string" and (not checkpattern or find(found,checkpattern)) then
198 return found
199 end
200 end
201 return false
202 end
203 if engine then
204 if trace then
205 report("attemp 1, engine %a",engine)
206 end
207 found_library = attempt("/"..engine.."/")
208 if not found_library then
209 if trace then
210 report("attemp 2, no engine",asked_library)
211 end
212 found_library = attempt()
213 end
214 else
215 found_library = attempt()
216 end
217 end
218
219 if not found_library then
220 if trace then
221 report("not found: %a",required)
222 end
223 library = false
224 else
225 if trace then
226 report("found: %a",found_library)
227 end
228 local result, message = action(found_library,required_base)
229 if result then
230 library = result
231 else
232 library = false
233 report("load error: message %a, library %a",tostring(message or "unknown"),found_library or "no library")
234 end
235 end
236 if trace then
237 if not library then
238 report("unknown library: %a",required)
239 else
240 report("stored library: %a",required)
241 end
242 end
243 return library or nil
244end
245
246resolvers.locatelib = locate
247
248
249
250do
251
252 local report_swiglib = logs.reporter("swiglib")
253 local trace_swiglib = false
254 local savedrequire = require
255 local loadedlibs = { }
256 local loadlib = package.loadlib
257
258 local pushdir = dir.push
259 local popdir = dir.pop
260
261 trackers.register("resolvers.swiglib", function(v) trace_swiglib = v end)
262
263 function requireswiglib(required,version)
264 local library = loadedlibs[library]
265 if library == nil then
266 local trace_swiglib = trace_swiglib or package.helpers.trace
267 library = locate(required,version,trace_swiglib,report_swiglib,function(name,base)
268 pushdir(pathpart(name))
269 local opener = "luaopen_" .. base
270 if trace_swiglib then
271 report_swiglib("opening: %a with %a",name,opener)
272 end
273 local library, message = loadlib(name,opener)
274 local libtype = type(library)
275 if libtype == "function" then
276 library = library()
277 else
278 report_swiglib("load error: %a returns %a, message %a, library %a",opener,libtype,(string.gsub(message or "no message","[%s]+$","")),found_library or "no library")
279 library = false
280 end
281 popdir()
282 return library
283 end)
284 loadedlibs[required] = library or false
285 end
286 return library
287 end
288
289
295
296 function require(name,version)
297 if find(name,"^swiglib%.") then
298 return requireswiglib(name,version)
299 else
300 return savedrequire(name)
301 end
302 end
303
304
311
312 local swiglibs = { }
313 local initializer = "core"
314
315 function swiglib(name,version)
316 local library = swiglibs[name]
317 if not library then
318 statistics.starttiming(swiglibs)
319 if trace_swiglib then
320 report_swiglib("loading %a",name)
321 end
322 if not find(name,"%." .. initializer .. "$") then
323 fullname = "swiglib." .. name .. "." .. initializer
324 else
325 fullname = "swiglib." .. name
326 end
327 library = requireswiglib(fullname,version)
328 swiglibs[name] = library
329 statistics.stoptiming(swiglibs)
330 end
331 return library
332 end
333
334 statistics.register("used swiglibs", function()
335 if next(swiglibs) then
336 return string.format("%s, initial load time %s seconds",table.concat(table.sortedkeys(swiglibs)," "),statistics.elapsedtime(swiglibs))
337 end
338 end)
339
340end
341
342if FFISUPPORTED and ffi and ffi.load then
343
344
349
350 local report_ffilib = logs.reporter("ffilib")
351 local trace_ffilib = false
352 local savedffiload = ffi.load
353
354
355
356
357
358
359 trackers.register("resolvers.ffilib", function(v) trace_ffilib = v end)
360
361
362
363
364
365 local loaded = { }
366
367 local function locateindeed(name)
368 name = removesuffix(name)
369 local l = loaded[name]
370 if l == nil then
371 local state, library = pcall(savedffiload,name)
372 if type(library) == "userdata" then
373 l = library
374 elseif type(state) == "userdata" then
375 l = state
376 else
377 l = false
378 end
379 loaded[name] = l
380 elseif trace_ffilib then
381 report_ffilib("reusing already loaded %a",name)
382 end
383 return l
384 end
385
386 local function getlist(required)
387 local list = directives.value("system.librarynames" )
388 if type(list) == "table" then
389 list = list[required]
390 if type(list) == "table" then
391 if trace then
392 report("using lookup list for library %a: % | t",required,list)
393 end
394 return list
395 end
396 end
397 return { required }
398 end
399
400 function ffilib(name,version)
401 name = removesuffix(name)
402 local l = loaded[name]
403 if l ~= nil then
404 if trace_ffilib then
405 report_ffilib("reusing already loaded %a",name)
406 end
407 return l
408 end
409 local list = getlist(name)
410 if version == "system" then
411 for i=1,#list do
412 local library = locateindeed(list[i])
413 if type(library) == "userdata" then
414 return library
415 end
416 end
417 else
418 for i=1,#list do
419 local library = locate(list[i],version,trace_ffilib,report_ffilib,locateindeed)
420 if type(library) == "userdata" then
421 return library
422 end
423 end
424 end
425 end
426
427 function ffi.load(name)
428 local list = getlist(name)
429 for i=1,#list do
430 local library = ffilib(list[i])
431 if type(library) == "userdata" then
432 return library
433 end
434 end
435 if trace_ffilib then
436 report_ffilib("trying to load %a using normal loader",name)
437 end
438
439 for i=1,#list do
440 local state, library = pcall(savedffiload,list[i])
441 if type(library) == "userdata" then
442 return library
443 elseif type(state) == "userdata" then
444 return library
445 end
446 end
447 end
448
449end
450
451
468
469do
470
471 local isfile = lfs.isfile
472 local report = logs.reporter("resolvers","lib")
473 local trace = false
474
475 trackers.register("resolvers.lib", function(v) trace = v end)
476
477 local function action(filename)
478 return isfile(filename) and filename or false
479 end
480
481 function resolvers.findlib(required)
482 local list = directives.value("system.librarynames" )
483 local only = nameonly(required)
484 if type(list) == "table" then
485 list = list[only]
486 if type(list) == "table" then
487 if trace then
488 report("using lookup list for library %a: % | t",only,list)
489 end
490 else
491 list = { only }
492 end
493 else
494 list = { only }
495 end
496 for i=1,#list do
497 local name = list[i]
498 local found = locate(name,false,trace,report,action)
499 if found then
500 return found
501 end
502 end
503 local getpaths = resolvers.expandedpathlistfromvariable
504 if getpaths then
505 local list = getpaths("PATH")
506 local base = addsuffix(only,os.libsuffix)
507 for i=1,#list do
508 local full = joinfile(list[i],base)
509 local found = locate(full,false,trace,report,action)
510 if found then
511 return found
512 end
513 end
514 end
515 end
516
517end
518 |