summaryrefslogtreecommitdiff
path: root/xmake/core/base/utils.lua
blob: a1070a6a946f908eda68c8f6e23ba38fe10119a0 (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
333
--!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, TBOOX Open Source Group.
--
-- @author      ruki
-- @file        utils.lua
--

-- define module
local utils = utils or {}

-- load modules
local option = require("base/option")
local colors = require("base/colors")
local string = require("base/string")
local log    = require("base/log")
local io     = require("base/io")
local dump   = require("base/dump")
local text   = require("base/text")

-- save original interfaces
utils._bin2c = utils._bin2c or utils.bin2c

-- dump values
function utils.dump(...)
    if option.get("quiet") then
        return ...
    end

    local diagnosis = option.get("diagnosis")

    -- show caller info
    if diagnosis then
        local info = debug.getinfo(2)
        local line = info.currentline
        if not line or line < 0 then line = info.linedefined end
        io.write(string.format("dump from %s %s:%s\n", info.name or "<anonymous>", info.source, line))
    end

    local values = table.pack(...)
    if values.n == 0 then
        return
    end

    if values.n == 1 then
        dump(values[1], "", diagnosis)
    else
        for i = 1, values.n do
            dump(values[i], string.format("%2d: ", i), diagnosis)
        end
    end

    return table.unpack(values, 1, values.n)
end

-- print string with newline
function utils._print(...)

    -- print it if not quiet
    if not option.get("quiet") then
        local values = {...}
        for i, v in ipairs(values) do
            -- dump basic type
            if type(v) == "string" or type(v) == "boolean" or type(v) == "number" then
                io.write(tostring(v))
            -- dump table
            elseif type(v) == "table" then
                dump(v)
            else
                io.write("<" .. tostring(v) .. ">")
            end
            if i ~= #values then
                io.write(" ")
            end
        end
        io.write('\n')
    end
end

-- print string without newline
function utils._iowrite(...)

    -- print it if not quiet
    if not option.get("quiet") then
        io.write(...)
    end
end

-- decode errors if errors is encoded table string
function utils._decode_errors(errors)
    if not errors then
        return
    end
    local _, pos = errors:find("[@encode(error)]: ", 1, true)
    if pos then
        -- strip traceback (maybe from coroutine.resume)
        local errs = errors:sub(pos + 1)
        local stack = nil
        local stackpos = errs:find("}\nstack traceback:", 1, true)
        if stackpos and stackpos > 1 then
            stack = errs:sub(stackpos + 2)
            errs  = errs:sub(1, stackpos)
        end
        errors, errs = errs:deserialize()
        if not errors then
            errors = errs
        end
        if type(errors) == "table" then
            if stack then
                errors._stack = stack
            end
            setmetatable(errors,
            {
                __tostring = function (self)
                    local result = self.errors
                    if not result then
                        result = string.serialize(self, {strip = true, indent = false})
                    end
                    result = result or ""
                    if self._stack then
                        result = result .. "\n" .. self._stack
                    end
                    return result
                end,
                __concat = function (self, other)
                    return tostring(self) .. tostring(other)
                end
            })
        end
        return errors
    end
end

-- print format string with newline
function utils.print(format, ...)
    assert(format)
    local message = string.tryformat(format, ...)
    utils._print(message)
    log:printv(message)
end

-- print format string without newline
function utils.printf(format, ...)
    assert(format)
    local message = string.tryformat(format, ...)
    utils._iowrite(message)
    log:write(message)
end

-- print format string and colors with newline
function utils.cprint(format, ...)
    assert(format)
    local message = string.tryformat(format, ...)
    utils._print(colors.translate(message))
    if log:file() then
        log:printv(colors.ignore(message))
    end
end

-- print format string and colors without newline
function utils.cprintf(format, ...)
    assert(format)
    local message = string.tryformat(format, ...)
    utils._iowrite(colors.translate(message))
    if log:file() then
        log:write(colors.ignore(message))
    end
end

-- print the verbose information
function utils.vprint(format, ...)
    if (option.get("verbose") or option.get("diagnosis")) and format ~= nil then
        utils.print(format, ...)
    end
end

-- print the verbose information without newline
function utils.vprintf(format, ...)
    if (option.get("verbose") or option.get("diagnosis")) and format ~= nil then
        utils.printf(format, ...)
    end
end

-- print the error information
function utils.error(format, ...)
    if format ~= nil then
        local errors = string.tryformat(format, ...)
        local decoded_errors = utils._decode_errors(errors)
        if decoded_errors then
            errors = tostring(decoded_errors)
        end
        utils.cprint("${bright color.error}${text.error}: ${clear}" .. errors)
        log:flush()
    end
end

-- add warning message
function utils.warning(format, ...)
    if option.get("quiet") then
        return
    end

    -- format message
    local args = table.pack(...)
    local msg = (args.n > 0 and string.tryformat(format, ...) or format)

    -- init warnings
    local warnings = utils._WARNINGS
    if not warnings then
        warnings = {}
        utils._WARNINGS = warnings
    end

    -- add warning msg
    table.insert(warnings, msg)
end

-- show warnings
function utils.show_warnings()
    local warnings = utils._WARNINGS
    if warnings then
        for idx, msg in ipairs(table.unique(warnings)) do
            if not option.get("verbose") and idx > 1 then
                utils.cprint("${bright color.warning}${text.warning}: ${color.warning}add -v for getting more warnings ..")
                break
            end
            utils.cprint("${bright color.warning}${text.warning}: ${color.warning}%s", msg)
        end
    end
end

-- try to call script
function utils.trycall(script, traceback, ...)
    return xpcall(script, function (errors)

            -- get traceback
            traceback = traceback or debug.traceback

            -- decode it if errors is encoded table string
            local decoded_errors = utils._decode_errors(errors)
            if decoded_errors then
                return decoded_errors
            end
            return traceback(errors)
        end, ...)
end

-- get confirm result
--
-- @code
-- if utils.confirm({description = "xmake.lua not found, try generating it", default = true}) then
--    TODO
-- end
-- @endcode
--
function utils.confirm(opt)

    -- init options
    opt = opt or {}

    -- get default
    local default = opt.default
    if default == nil then
        default = false
    end

    -- get description
    local description = opt.description or ""

    -- get confirm result
    local result = option.get("yes") or option.get("confirm")
    if type(result) == "string" then
        result = result:lower()
        if result == "d" or result == "def" then
            result = default
        else
            result = nil
        end
    end

    -- get user confirm
    if result == nil then

        -- show tips
        if type(description) == "function" then
            description()
        else
            utils.cprint("${bright color.warning}note: ${clear}%s (pass -y or --confirm=y/n/d to skip confirm)?", description)
        end

        -- get answer
        if opt.answer then
            result = opt.answer()
        else
            utils.cprint("please input: ${bright}%s${clear} (y/n)", default and "y" or "n")
            io.flush()
            result = option.boolean((io.read() or "false"):trim())
            if type(result) ~= "boolean" then
                result = default
            end
        end
    end
    return result
end

function utils.table(data, opt)
    utils.printf(text.table(data, opt))
end

function utils.vtable(data, opt)
    utils.vprintf(text.table(data, opt))
end

-- generate c/c++ code from the binary file
function utils.bin2c(binaryfile, outputfile, opt)
    opt = opt or {}
    return utils._bin2c(binaryfile, outputfile, opt.linewidth or 32, opt.nozeroend or false)
end

-- return module
return utils