summaryrefslogtreecommitdiff
path: root/xmake/core/sandbox/modules/import.lua
blob: 234e7692580b02eed01a2694b23b73f332ad5db0 (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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
--!The Make-like Build Utility based on Lua
--
-- Licensed to the Apache Software Foundation (ASF) under one
-- or more contributor license agreements.  See the NOTICE file
-- distributed with this work for additional information
-- regarding copyright ownership.  The ASF licenses this file
-- to you under the Apache License, Version 2.0 (the
-- "License"); you may not use this file except in compliance
-- with the License.  You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
-- 
-- Copyright (C) 2015 - 2017, TBOOX Open Source Group.
--
-- @author      ruki
-- @file        import.lua
--

-- define module
local sandbox_import = sandbox_import or {}

-- load modules
local os        = require("base/os")
local path      = require("base/path")
local utils     = require("base/utils")
local table     = require("base/table")
local string    = require("base/string")
local sandbox   = require("sandbox/sandbox")
local raise     = require("sandbox/modules/raise")

-- get module name
function sandbox_import._modulename(name)

    -- check
    assert(name)

    -- find modulename
    local i = name:find_last(".", true)
    if i then
        name = name:sub(i + 1)
    end

    -- get it
    return name
end

-- load module from file
function sandbox_import._loadfile(filepath, instance)

    -- check
    assert(filepath)

    -- load module script
    local script, errors = loadfile(filepath)
    if not script then
        return nil, errors
    end

    -- with sandbox?
    if instance then

        -- fork a new sandbox for this script
        instance, errors = instance:fork(script, path.directory(filepath))
        if not instance then
            return nil, errors
        end

        -- import module
        local result, errors = instance:import()
        if not result then
            return nil, errors
        end
        
        -- ok
        return result, instance:script()
    end

    -- load module without sandbox
    local ok, result = xpcall(script, debug.traceback)
    if not ok then
        return nil, result
    end

    -- ok?
    return result, script
end

-- find module
function sandbox_import._find(dir, name)

    -- check
    assert(dir and name)

    -- replace "package.module" => "package/module"
    name = (name:gsub("%.", "/"))
    assert(name)

    -- load the single module?
    local module = nil
    if os.isfile(path.join(dir, name .. ".lua")) then

        -- ok
        return true

    -- load modules
    elseif os.isdir(path.join(dir, name)) then

        -- ok
        return true

    end

    -- not found
    return false

end

-- load module
function sandbox_import._load(dir, name, instance)

    -- check
    assert(dir and name)

    -- replace "package.module" => "package/module"
    name = (name:gsub("%.", "/"))
    assert(name)

    -- load the single module?
    local module = nil
    local script = nil
    if os.isfile(path.join(dir, name .. ".lua")) then

        -- load module
        local result, errors = sandbox_import._loadfile(path.join(dir, name .. ".lua"), instance)
        if not result then
            return nil, errors
        end

        -- save module
        module = result

        -- save script
        script = errors

    -- load modules
    elseif os.isdir(path.join(dir, name)) then

        -- get modulefiles
        local moduleroot = path.join(path.join(dir, name))
        local modulefiles = os.match(path.join(moduleroot, "**.lua"))
        if modulefiles then
            for _, modulefile in ipairs(modulefiles) do

                -- load module
                local result, errors = sandbox_import._loadfile(modulefile, instance)
                if not result then
                    return nil, errors
                end

                -- get the module path
                local modulepath = path.relative(modulefile, moduleroot)
                if not modulepath then
                    return nil, string.format("cannot get the path for module: %s", name)
                end

                -- init the root module
                module = module or {}

                -- save script
                script = errors

                -- save module
                local scope = module
                for _, modulename in ipairs(path.split(modulepath)) do
                    
                    -- is end?
                    local pos = modulename:find(".lua", 1, true)
                    if pos then

                        -- get the module name
                        modulename = modulename:sub(1, pos - 1)
                        assert(modulename)

                        -- save module
                        scope[modulename] = result

                    -- is scope?
                    else
 
                        -- enter submodule
                        scope[modulename] = scope[modulename] or {}
                        scope = scope[modulename]
                    end
                end
            end
        end
    end
    
    -- this module not found?
    if not module then
        return nil, string.format("module: %s not found!", name)
    end

    -- return it
    return module, script
end

-- import module
--
-- .e.g 
--
-- import("core.platform")
-- => platform
-- 
-- import("core.platform", {alias = "p"})
-- => p
--
-- import("core")
-- => core
-- => core.platform
---
-- import("core", {rootdir = "/scripts"})
-- => core
-- => core.platform
--
-- import("core.platform", {inherit = true})
-- => inherit the all interfaces of core.platform to the current scope
--
-- @note the polymiorphism is not supported for import.inherit mode now.
--
function sandbox_import.import(name, args)

    -- check
    assert(name)

    -- the arguments
    args = args or {}

    -- get the parent scope
    local scope_parent = getfenv(2)
    assert(scope_parent)

    -- get module name
    local modulename = sandbox_import._modulename(name)
    if not modulename then
        raise("cannot get module name for %s", name)
    end

    -- the imported name
    local imported_name = args.alias or modulename

    -- this module has been imported?
    local module = rawget(scope_parent, imported_name)
    if module ~= nil then
        return module
    end

    -- get the current sandbox instance
    local instance = sandbox.instance()
    assert(instance)

    -- the root directory for this sandbox script
    local rootdir = args.rootdir or instance:rootdir()
    assert(rootdir)

    -- load module
    local module = nil
    local errors = nil
    if sandbox_import._find(rootdir, name) then
        -- load module from the sandbox root directory 
        module, errors = sandbox_import._load(rootdir, name, instance)
    else
        -- load module from the sandbox core directory
        module, errors = sandbox_import._load(path.join(xmake._CORE_DIR, "sandbox/modules/import"), name)
    end

    -- check
    if not module then
        raise("cannot import module: %s, %s", name, errors)
    end

    -- get module script
    local script = errors

    -- inherit?
    if args.inherit then
 
        -- inherit this module into the parent scope
        table.inherit2(scope_parent, module)

        -- import as super module
        imported_name = "_super"

        -- public the script scope for the super module
        --
        -- we can access the all scope members of _super in the child module
        --
        -- .e.g
        --
        -- import("core.platform.xxx", {inherit = true})
        --
        -- print(_super._g)
        -- 
        if script ~= nil then
            setmetatable(module, {  __index = function (tbl, key)
                                        local val = rawget(tbl, key)
                                        if val == nil then
                                            val = rawget(getfenv(script), key)
                                        end
                                        return val
                                    end})

        end

    end

    -- import this module into the parent scope
    scope_parent[imported_name] = module

    -- return it
    return module
end

-- load module
return sandbox_import.import