summaryrefslogtreecommitdiff
path: root/tex/context/base/util-lib.lua
blob: c987bdde3d84da081f000562a4c2d841cf7ebf9a (plain)
1
2
3
4
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
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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
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/swiglib/gmwand

]]--

local savedrequire = require
local loaded = package.loaded
local gsub, find = string.gsub, string.find

--[[

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.

]]--

local function requireswiglib(required)
    local library = loaded[required]
    if not library then
        local name = gsub(required,"%.","/") .. "." .. os.libsuffix
        local full = resolvers.findfile(name,"lib")
   --   local full = resolvers.findfile(name)
        if not full or full == "" then
            -- We can consider alternatives but we cannot load yet ... I
            -- need to extent l-lua with a helper if we really want that.
            --
            -- package.helpers.trace = true
            -- package.extraclibpath(environment.ownpath)
        end
        local path = file.pathpart(full)
        local base = file.nameonly(full)
        dir.push(path)
     -- if false then
     --     local savedlibrary = loaded[base]
     --     library = savedrequire(base)
     --     loaded[base] = savedlibrary
     -- else
            library = package.loadlib(full,"luaopen_" .. base)
            if type(library) == "function" then
                library = library()
            else
                -- some error
            end
     -- end
        dir.pop()
        loaded[required] = library
    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.

]]--

function require(name,...) -- this might disappear or change
    if find(name,"^swiglib%.") then
        return requireswiglib(name,...)
    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.

]]--

local report_swiglib = logs.reporter("swiglib")

local swiglibs = { }

function swiglib(name)
    local library = swiglibs[name]
    if not library then
        statistics.starttiming(swiglibs)
        report_swiglib("loading %a",name)
        library = requireswiglib("swiglib." .. name)
        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:

----- gm = requireswiglib("swiglib.gmwand.core") -- most bare method (not public in context)
local gm = require("swiglib.gmwand.core")        -- nicer integrated (maybe not in context)
local gm = swiglib("gmwand.core")                -- the context way

Watch out, the last one is less explicit and lacks the swiglib prefix.

]]--