diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/util-lib.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/util-lib.lua | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/util-lib.lua b/Master/texmf-dist/tex/context/base/util-lib.lua new file mode 100644 index 00000000000..fde1f987e7f --- /dev/null +++ b/Master/texmf-dist/tex/context/base/util-lib.lua @@ -0,0 +1,233 @@ +if not modules then modules = { } end modules ['util-lib'] = { + version = 1.001, + comment = "companion to luat-lib.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files", +} + +-- This is experimental code for Hans and Luigi. Don't depend on it! There +-- will be a plain variant. + +--[[ + +The problem with library bindings is manyfold. They are of course platform +dependent and while a binary with its directly related libraries are often +easy to maintain and load, additional libraries can each have their demands. + +One important aspect is that loading additional libraries from within the +loaded one is also operating system dependent. There can be shared libraries +elsewhere on the system and as there can be multiple libraries with the same +name but different usage and versioning there can be clashes. So there has to +be some logic in where to look for these sublibraries. + +We found out that for instance on windows libraries are by default sought on +the parents path and then on the binary paths and these of course can be in +an out of our control, thereby enlarging the changes on a clash. A rather +safe solution for that to load the library on the path where it sits. + +Another aspect is initialization. When you ask for a library t.e.x it will +try to initialize luaopen_t_e_x no matter if such an inializer is present. +However, because loading is configurable and in the case of luatex is already +partly under out control, this is easy to deal with. We only have to make +sure that we inform the loader that the library has been loaded so that +it won't load it twice. + +In swiglib we have chosen for a clear organization and although one can use +variants normally in the tex directory structure predictability is more or +less the standard. For instance: + +.../tex/texmf-mswin/bin/lib/luatex/lua/swiglib/mysql/core.dll +.../tex/texmf-mswin/bin/lib/luajittex/lua/swiglib/mysql/core.dll +.../tex/texmf-mswin/bin/lib/luatex/context/lua/swiglib/mysql/core.dll +.../tex/texmf-mswin/bin/lib/swiglib/lua/mysql/core.dll +.../tex/texmf-mswin/bin/lib/swiglib/lua/mysql/5.6/core.dll + +The lookups are determined via an entry in texmfcnf.lua: + +CLUAINPUTS = ".;$SELFAUTOLOC/lib/{$engine,luatex}/lua//", + +A request for t.e.x is converted to t/e/x.dll or t/e/x.so depending on the +platform. Then we use the regular finder to locate the file in the tex +directory structure. Once located we goto the path where it sits, load the +file and return to the original path. We register as t.e.x in order to +prevent reloading and also because the base name is seldom unique. + +The main function is a big one and evolved out of experiments that Luigi +Scarso and I conducted when playing with variants of SwigLib. The function +locates the library using the context mkiv resolver that operates on the +tds tree and if that doesn't work out well, the normal clib path is used. + +The lookups is somewhat clever in the sense that it can deal with (optional) +versions and can fall back on non versioned alternatives if needed, either +or not using a wildcard lookup. + +This code is experimental and by providing a special abstract loader (called +swiglib) we can start using the libraries. + +]]-- + +-- seems to be clua in recent texlive + +local gsub, find = string.gsub, string.find +local pathpart, nameonly, joinfile = file.pathpart, file.nameonly, file.join +local findfile, findfiles = resolvers and resolvers.findfile, resolvers and resolvers.findfiles + +local loaded = package.loaded + +local report_swiglib = logs.reporter("swiglib") +local trace_swiglib = false trackers.register("resolvers.swiglib", function(v) trace_swiglib = v end) + +-- We can check if there are more that one component, and if not, we can +-- append 'core'. + +local function requireswiglib(required,version) + local library = loaded[required] + if library == nil then + -- initialize a few variables + local required_full = gsub(required,"%.","/") + local required_path = pathpart(required_full) + local required_base = nameonly(required_full) + local required_name = required_base .. "." .. os.libsuffix + local version = type(version) == "string" and version ~= "" and version or false + -- helper + local function check(locate,...) + local found_library = nil + if version then + local asked_library = joinfile(required_path,version,required_name) + if trace_swiglib then + report_swiglib("checking %s: %a","with version",asked_library) + end + found_library = locate(asked_library,...) + if not found_library or found_library == ""then + asked_library = joinfile(required_path,required_name) + if trace_swiglib then + report_swiglib("checking %s: %a","without version",asked_library) + end + found_library = locate(asked_library,...) + end + else + local asked_library = joinfile(required_path,required_name) + if trace_swiglib then + report_swiglib("checking %s: %a","without version",asked_library) + end + found_library = locate(asked_library,...) + end + return found_library and found_library ~= "" and found_library or false + end + -- check cnf spec using name and version + local found_library = findfile and check(findfile,"lib") + -- check cnf spec using wildcard + if findfiles and not found_library then + local asked_library = joinfile(required_path,".*",required_name) + if trace_swiglib then + report_swiglib("checking %s: %a","latest version",asked_library) + end + local list = findfiles(asked_library,"lib",true) + if list and #list > 0 then + table.sort(list) + found_library = list[#list] + end + end + -- check clib paths using name and version + if not found_library then + package.extraclibpath(environment.ownpath) + local paths = package.clibpaths() + for i=1,#paths do + local found_library = check(lfs.isfile) + if found_library then + break + end + end + end + -- load and initialize when found + if not found_library then + if trace_swiglib then + report_swiglib("not found: %a",asked_library) + end + library = false + else + local path = pathpart(found_library) + local base = nameonly(found_library) + dir.push(path) + if trace_swiglib then + report_swiglib("found: %a",found_library) + end + library = package.loadlib(found_library,"luaopen_" .. required_base) + if type(library) == "function" then + library = library() + else + library = false + end + dir.pop() + end + -- cache result + if not library then + report_swiglib("unknown: %a",required) + elseif trace_swiglib then + report_swiglib("stored: %a",required) + end + loaded[required] = library + else + report_swiglib("reused: %a",required) + end + return library +end + +--[[ + +For convenience we make the require loader function swiglib aware. Alternatively +we could put the specific loader in the global namespace. + +]]-- + +local savedrequire = require + +function require(name,version) + if find(name,"^swiglib%.") then + return requireswiglib(name,version) + else + return savedrequire(name) + end +end + +--[[ + +At the cost of some overhead we provide a specific loader so that we can keep +track of swiglib usage which is handy for development. In context this is the +recommended loader. + +]]-- + +local swiglibs = { } + +function swiglib(name,version) + local library = swiglibs[name] + if not library then + statistics.starttiming(swiglibs) + report_swiglib("loading %a",name) + library = requireswiglib("swiglib." .. name,version) + swiglibs[name] = library + statistics.stoptiming(swiglibs) + end + return library +end + +statistics.register("used swiglibs", function() + if next(swiglibs) then + return string.format("%s, initial load time %s seconds",table.concat(table.sortedkeys(swiglibs)," "),statistics.elapsedtime(swiglibs)) + end +end) + +--[[ + +So, we now have: + +local gm = require("swiglib.gmwand.core") +local gm = swiglib("gmwand.core") +local sq = swiglib("mysql.core") +local sq = swiglib("mysql.core","5.6") + +Watch out, the last one is less explicit and lacks the swiglib prefix. + +]]-- |