summaryrefslogtreecommitdiff
path: root/xmake/modules/utils/run_script.lua
blob: e6f8469f6efc5c42344cd1ad06acd256f22a8bfe (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
--!A cross-platform build utility based on Lua
--
-- Licensed 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-present, Xmake Open Source Community.
--
-- @author      ruki
-- @file        run.lua
--

-- imports
import("core.base.option")
import("core.base.thread")
import("core.sandbox.module")

-- print verbose log
function _print_vlog(script_type, script_name, args, opt)
    if not opt.verbose then
        return
    end
    cprintf("running %s ${underline}%s${reset}", script_type, script_name)
    if args.n > 0 then
        print(" with args:")
        if not opt.diagnosis then
            for i = 1, args.n do
                print("  - " .. todisplay(args[i]))
            end
        else
            utils.dump(table.unpack(args, 1, args.n))
        end
    else
        print(".")
    end
end

function _is_callable(func)
    if type(func) == "function" then
        return true
    elseif type(func) == "table" then
        local meta = debug.getmetatable(func)
        if meta and meta.__call then
            return true
        end
    end
end

function _run_script(script, args, opt)

    local func
    local printresult = false
    local script_type, script_name

    -- import and run script
    if path.extension(script) == ".lua" and os.isfile(script) then

        -- run the given lua script file (xmake lua /tmp/script.lua)
        script_type, script_name = "given lua script file", path.relative(script)
        func = import(path.basename(script), {rootdir = path.directory(script), anonymous = true})
    elseif os.isfile(path.join(os.programdir(), "plugins", "lua", "scripts", script .. ".lua")) then

        -- run builtin lua script (xmake lua echo "hello xmake")
        script_type, script_name = "builtin lua script", script
        func = import("scripts." .. script, {anonymous = true, rootdir = path.join(os.programdir(), "plugins", "lua")})
    else

        -- attempt to find the builtin module
        local object = nil
        for _, name in ipairs(script:split("%.")) do
            object = object and object[name] or module.get(name)
            if not object then
                break
            end
        end
        if object then
            -- run builtin modules (xmake lua core.xxx.xxx)
            script_type, script_name = "builtin module", script
            func = object
        else
            -- run imported modules (xmake lua core.xxx.xxx)
            script_type, script_name = "imported module", script
            func = import(script, {anonymous = true})
        end
        printresult = true
    end

    -- print verbose log
    _print_vlog(script_type or "script", script_name or "", args, opt)

    -- dump func() result
    if _is_callable(func) then
        local result = table.pack(func(table.unpack(args, 1, args.n)))
        if printresult and result and result.n ~= 0 then
            utils.dump(table.unpack(result, 1, result.n))
        end
    else
        -- dump variables directly
        utils.dump(func)
    end
end

function _run_commanad(command, args, opt)
    local tmpfile = os.tmpfile() .. ".lua"
    io.writefile(tmpfile, "function main(...)\n" .. command .. "\nend")
    _run_script(tmpfile, args, opt)
end

function _get_args(opt)
    opt = opt or {}

    -- get arguments
    local args = opt.arguments or {}
    args.n = #args

    -- get deserialize tag
    local deserialize = opt.deserialize
    if not deserialize then
        return args
    end
    deserialize = tostring(deserialize)

    -- deserialize prefixed arguments
    for i, value in ipairs(args) do
        if value:startswith(deserialize) then
            local v, err = string.deserialize(value:sub(#deserialize + 1))
            if err then
                raise(err)
            else
                args[i] = v
            end
        end
    end
    return args
end

function _run(script, opt)
    opt = opt or {}

    if opt.quiet then
        option.save()
        option.set("quiet", true, {force = true})
    end

    local oldir
    if xmake.in_main_thread() then
        local curdir = opt.curdir or os.workingdir()
        oldir = os.cd(curdir)
    end
    if opt.command then
        _run_commanad(script, _get_args(opt), opt)
    else
        _run_script(script, _get_args(opt), opt)
    end
    if oldir then
        os.cd(oldir)
    end

    if opt.quiet then
        option.restore()
    end
end

function _run_in_thread(script, opt)
    import("utils.run_script", {anonymous = true})(script, opt)
end

-- run lua script
--
-- @param script      the script file or string or module name
--                    e.g. /tmp/test.lua, "print("hello")", "utils.bin2c"
-- @param opt         the options
--                     - curdir      the currect directory
--                     - command     run script as command
--                     - deserialize deserialize arguments starts with given prefix
--                     - arguments   the script arguments
--                     - thread      run script in a new native thread
--                     - quiet       enable quiet output
--                     - verbose     enable verbose output
--                     - diagnosis   enable diagnosis output
function main(script, opt)
    opt = opt or {}
    if opt.thread then
        local argv
        for _, arg in ipairs(opt.arguments) do
            argv = argv or {}
            if path.instance_of(arg) then
                arg = tostring(arg)
            end
            table.insert(argv, arg)
        end
        local thread_opt = {
            curdir = opt.curdir,
            command = opt.command,
            deserialize = opt.deserialize,
            arguments = argv,
            quiet = opt.quiet,
            verbose = opt.verbose,
            diagnosis = opt.diagnosis
        }
        local t = thread.start_named("utils.run_script", _run_in_thread, script, thread_opt)
        t:wait(-1)
    else
        _run(script, opt)
    end
end