From 08f0362fd876d8a0e7502a419aabcc1c91d91cb2 Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 14 Nov 2025 23:02:13 +0800 Subject: add json output for xmake show basic info --- xmake/plugins/show/info/basic.lua | 135 ++++++++++++++++++++++++++++---------- 1 file changed, 102 insertions(+), 33 deletions(-) diff --git a/xmake/plugins/show/info/basic.lua b/xmake/plugins/show/info/basic.lua index 440e6768a..a08b47d3a 100644 --- a/xmake/plugins/show/info/basic.lua +++ b/xmake/plugins/show/info/basic.lua @@ -21,51 +21,120 @@ -- imports import("core.base.option") import("core.base.global") +import("core.base.json") import("core.project.config") import("core.project.project") import("core.package.package") --- show basic info -function main() +function _show_xmake_info(opt, result) + local json_enabled = opt and opt.json + local info = { + version = tostring(xmake.version()), + host = {os = os.host(), arch = os.arch()}, + programdir = xmake.programdir(), + programfile = xmake.programfile(), + globaldir = global.directory(), + tmpdir = os.tmpdir(), + workingdir = os.workingdir(), + packagedir = package.installdir(), + packagedir_cache = package.cachedir() + } + if json_enabled then + result = result or {} + result.xmake = info + else + print("The information of xmake:") + cprint(" ${color.dump.string}version${clear}: %s", info.version) + cprint(" ${color.dump.string}host${clear}: %s/%s", info.host.os, info.host.arch) + cprint(" ${color.dump.string}programdir${clear}: %s", info.programdir) + cprint(" ${color.dump.string}programfile${clear}: %s", info.programfile) + cprint(" ${color.dump.string}globaldir${clear}: %s", info.globaldir) + cprint(" ${color.dump.string}tmpdir${clear}: %s", info.tmpdir) + cprint(" ${color.dump.string}workingdir${clear}: %s", info.workingdir) + cprint(" ${color.dump.string}packagedir${clear}: %s", info.packagedir) + cprint(" ${color.dump.string}packagedir(cache)${clear}: %s", info.packagedir_cache) + print("") + end + return result +end - -- get target - config.load() +function _show_project_info(opt, result) + local json_enabled = opt and opt.json + local projectfile = os.projectfile() + if not os.isfile(projectfile) then + return result + end - -- show xmake information - print("The information of xmake:") - cprint(" ${color.dump.string}version${clear}: %s", xmake.version()) - cprint(" ${color.dump.string}host${clear}: %s/%s", os.host(), os.arch()) - cprint(" ${color.dump.string}programdir${clear}: %s", xmake.programdir()) - cprint(" ${color.dump.string}programfile${clear}: %s", xmake.programfile()) - cprint(" ${color.dump.string}globaldir${clear}: %s", global.directory()) - cprint(" ${color.dump.string}tmpdir${clear}: %s", os.tmpdir()) - cprint(" ${color.dump.string}workingdir${clear}: %s", os.workingdir()) - cprint(" ${color.dump.string}packagedir${clear}: %s", package.installdir()) - cprint(" ${color.dump.string}packagedir(cache)${clear}: %s", package.cachedir()) - print("") + local info = { + configdir = config.directory(), + projectdir = os.projectdir(), + projectfile = projectfile + } + local name = project.name() + if name then + info.name = name + end + local project_version = project.version() + if project_version ~= nil then + info.version = tostring(project_version) + end + local plat = config.plat() + if plat then + info.plat = plat + end + local arch = config.arch() + if arch then + info.arch = arch + end + local mode = config.mode() + if mode then + info.mode = mode + end + local builddir = config.builddir() + if builddir then + info.builddir = builddir + end - local projectfile = os.projectfile() - if os.isfile(projectfile) then - print("The information of project: %s", project.name() and project.name() or "") - local version = project.version() - if version then - cprint(" ${color.dump.string}version${clear}: %s", version) + if json_enabled then + result = result or {} + result.project = info + else + print("The information of project: %s", info.name or "") + if info.version then + cprint(" ${color.dump.string}version${clear}: %s", info.version) end - if config.plat() then - cprint(" ${color.dump.string}plat${clear}: %s", config.plat()) + if info.plat then + cprint(" ${color.dump.string}plat${clear}: %s", info.plat) end - if config.arch() then - cprint(" ${color.dump.string}arch${clear}: %s", config.arch()) + if info.arch then + cprint(" ${color.dump.string}arch${clear}: %s", info.arch) end - if config.mode() then - cprint(" ${color.dump.string}mode${clear}: %s", config.mode()) + if info.mode then + cprint(" ${color.dump.string}mode${clear}: %s", info.mode) end - if config.builddir() then - cprint(" ${color.dump.string}builddir${clear}: %s", config.builddir()) + if info.builddir then + cprint(" ${color.dump.string}builddir${clear}: %s", info.builddir) end - cprint(" ${color.dump.string}configdir${clear}: %s", config.directory()) - cprint(" ${color.dump.string}projectdir${clear}: %s", os.projectdir()) - cprint(" ${color.dump.string}projectfile${clear}: %s", projectfile) + cprint(" ${color.dump.string}configdir${clear}: %s", info.configdir) + cprint(" ${color.dump.string}projectdir${clear}: %s", info.projectdir) + cprint(" ${color.dump.string}projectfile${clear}: %s", info.projectfile) print("") end + return result +end + +-- show basic info +function main() + + config.load() + + local opt = {json = option.get("json")} + local result = opt.json and {} or nil + + result = _show_xmake_info(opt, result) + result = _show_project_info(opt, result) + + if opt.json then + print(json.encode(result or {})) + end end -- cgit v1.3.1 From 4a865b7a441ff44104fe4309d424666a9d32a9df Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 14 Nov 2025 23:22:40 +0800 Subject: add json output for show target --- xmake/core/base/string.lua | 19 ++++ xmake/plugins/show/info/target.lua | 220 ++++++++++++++++++++++++++++--------- 2 files changed, 188 insertions(+), 51 deletions(-) diff --git a/xmake/core/base/string.lua b/xmake/core/base/string.lua index 2c7069cb9..658ca7345 100644 --- a/xmake/core/base/string.lua +++ b/xmake/core/base/string.lua @@ -238,6 +238,25 @@ function string.dump(object, deflate) return string.serialize(object, deflate) end +-- convert object to json-serializable form +function string.serializable(object, opt) + opt = opt or {} + local vtype = type(object) + if vtype == "table" then + local result = {} + for k, v in pairs(object) do + result[k] = string.serializable(v, opt) + end + return result + elseif vtype == "function" or vtype == "userdata" or vtype == "thread" then + if opt.stringify then + return opt.stringify(object) + end + return tostring(object) + end + return object +end + -- serialize to string from the given object -- -- @param opt serialize options diff --git a/xmake/plugins/show/info/target.lua b/xmake/plugins/show/info/target.lua index d5ddfbe76..5384a739d 100644 --- a/xmake/plugins/show/info/target.lua +++ b/xmake/plugins/show/info/target.lua @@ -20,34 +20,58 @@ -- imports import("core.base.option") +import("core.base.json") import("core.base.hashset") import("core.project.config") import("core.language.language") import("private.detect.check_targetname") --- get source info string -function _get_sourceinfo_str(target, name, item, opt) +-- get source info data +function _get_sourceinfo(target, name, item, opt) opt = opt or {} local sourceinfo = target:sourceinfo(name, item) + local tips = opt.tips if sourceinfo then - local tips = opt.tips - if tips then - tips = tips .. " -> " - end + return { + file = sourceinfo.file, + line = sourceinfo.line, + tips = tips + } + elseif tips then + return {tips = tips} + end +end + +-- format source info string with color +function _format_sourceinfo(sourceinfo) + if not sourceinfo then + return "" + end + local tips = sourceinfo.tips + if tips then + tips = tips .. " -> " + end + if sourceinfo.file then return string.format(" ${dim}-> %s%s:%s${clear}", tips or "", sourceinfo.file or "", sourceinfo.line or -1) - elseif opt.tips then - return string.format(" ${dim}-> %s${clear}", opt.tips) + elseif tips then + return string.format(" ${dim}-> %s${clear}", tips) end return "" end +-- get source info string +function _get_sourceinfo_str(target, name, item, opt) + return _format_sourceinfo(_get_sourceinfo(target, name, item, opt)) +end + -- get values from target options function _get_values_from_opts(target, name) local values = {} for _, opt_ in ipairs(target:orderopts()) do for _, value in ipairs(opt_:get(name)) do local tips = string.format("option(%s)", opt_:name()) - values[value] = _get_sourceinfo_str(opt_, name, value, {tips = tips}) + local sourceinfo = _get_sourceinfo(opt_, name, value, {tips = tips}) + values[value] = sourceinfo or {tips = tips} end end return values @@ -78,7 +102,8 @@ function _get_values_from_pkgs(target, name) local info = components[component_name] if info then for _, value in ipairs(info[name]) do - values[value] = string.format(" -> package(%s)", pkg:fullname()) + local tips = string.format("package(%s)", pkg:fullname()) + values[value] = {tips = tips} end else local components_str = table.concat(table.wrap(configinfo.components), ", ") @@ -90,12 +115,14 @@ function _get_values_from_pkgs(target, name) -- e.g. `add_packages("xxx", {links = "xxx"})` elseif configinfo and configinfo[name] then for _, value in ipairs(configinfo[name]) do - values[value] = _get_sourceinfo_str(target, "packages", pkg:name()) + local sourceinfo = _get_sourceinfo(target, "packages", pkg:name()) + values[value] = sourceinfo end else -- get values from the builtin package configs for _, value in ipairs(pkg:get(name)) do - values[value] = string.format(" -> package(%s)", pkg:fullname()) + local tips = string.format("package(%s)", pkg:fullname()) + values[value] = {tips = tips} end end end @@ -112,18 +139,21 @@ function _get_values_from_deps(target, name) local depinherit = target:extraconf("deps", dep:name(), "inherit") if depinherit == nil or depinherit then for _, value in ipairs(dep:get(name, {interface = true})) do - values[value] = string.format(" -> dep(%s)", dep:name()) + local tips = string.format("dep(%s)", dep:name()) + values[value] = {tips = tips} end local values_chunks = dep:get_from(name, "option::*", {interface = true}) for _, values_chunk in ipairs(values_chunks) do for _, value in ipairs(values_chunk) do - values[value] = string.format(" -> dep(%s) -> options", dep:name()) + local tips = string.format("dep(%s) -> options", dep:name()) + values[value] = {tips = tips} end end values_chunks = dep:get_from(name, "package::*", {interface = true}) for _, values_chunk in ipairs(values_chunks) do for _, value in ipairs(values_chunk) do - values[value] = string.format(" -> dep(%s) -> packages", dep:name()) + local tips = string.format("dep(%s) -> packages", dep:name()) + values[value] = {tips = tips} end end end @@ -131,27 +161,36 @@ function _get_values_from_deps(target, name) return values end --- show target information -function _show_target(target) - print("The information of target(%s):", target:name()) - cprint(" ${color.dump.string}at${clear}: %s", path.join(target:scriptdir(), "xmake.lua")) - cprint(" ${color.dump.string}kind${clear}: %s", target:kind()) +function _collect_target_info(target) + local info = { + name = target:name(), + at = path.join(target:scriptdir(), "xmake.lua"), + kind = target:kind() + } local targetfile = target:targetfile() if targetfile then - cprint(" ${color.dump.string}targetfile${clear}: %s", targetfile) + info.targetfile = targetfile end local deps = target:get("deps") if deps then - cprint(" ${color.dump.string}deps${clear}:") + local entries = {} for _, dep in ipairs(deps) do - cprint(" ${color.dump.reference}->${clear} %s%s", dep, _get_sourceinfo_str(target, "deps", dep)) + local entry = {name = dep, source = _get_sourceinfo(target, "deps", dep)} + table.insert(entries, entry) + end + if #entries > 0 then + info.deps = entries end end local rules = target:get("rules") if rules then - cprint(" ${color.dump.string}rules${clear}:") + local entries = {} for _, value in ipairs(rules) do - cprint(" ${color.dump.reference}->${clear} %s%s", value, _get_sourceinfo_str(target, "rules", value)) + local entry = {name = value, source = _get_sourceinfo(target, "rules", value)} + table.insert(entries, entry) + end + if #entries > 0 then + info.rules = entries end end local options = {} @@ -161,18 +200,25 @@ function _show_target(target) end end if #options > 0 then - cprint(" ${color.dump.string}options${clear}:") + local entries = {} for _, value in ipairs(options) do - cprint(" ${color.dump.reference}->${clear} %s%s", value, _get_sourceinfo_str(target, "options", value)) + table.insert(entries, {name = value, source = _get_sourceinfo(target, "options", value)}) + end + if #entries > 0 then + info.options = entries end end local packages = target:get("packages") if packages then - cprint(" ${color.dump.string}packages${clear}:") + local entries = {} for _, value in ipairs(packages) do - cprint(" ${color.dump.reference}->${clear} %s%s", value, _get_sourceinfo_str(target, "packages", value)) + table.insert(entries, {name = value, source = _get_sourceinfo(target, "packages", value)}) + end + if #entries > 0 then + info.packages = entries end end + info.api_entries = {} for _, apiname in ipairs(table.join(language.apis().values, language.apis().paths)) do if apiname:startswith("target.") then local valuename = apiname:split('.add_', {plain = true})[2] @@ -181,38 +227,44 @@ function _show_target(target) local values = table.unique(table.wrap(target:get(valuename))) if #values > 0 then for _, value in ipairs(values) do - table.insert(results, {value = value, sourceinfo = _get_sourceinfo_str(target, valuename, value)}) + local sourceinfo = _get_sourceinfo(target, valuename, value) + table.insert(results, {value = string.serializable(value), source = sourceinfo}) end end local values_from_opts = _get_values_from_opts(target, valuename) for value, sourceinfo in pairs(values_from_opts) do - table.insert(results, {value = value, sourceinfo = sourceinfo}) + table.insert(results, {value = string.serializable(value), source = sourceinfo}) end local values_from_pkgs = _get_values_from_pkgs(target, valuename) for value, sourceinfo in pairs(values_from_pkgs) do - table.insert(results, {value = value, sourceinfo = sourceinfo}) + table.insert(results, {value = string.serializable(value), source = sourceinfo}) end local values_from_deps = _get_values_from_deps(target, valuename) for value, sourceinfo in pairs(values_from_deps) do - table.insert(results, {value = value, sourceinfo = sourceinfo}) + table.insert(results, {value = string.serializable(value), source = sourceinfo}) end if #results > 0 then - cprint(" ${color.dump.string}%s${clear}:", valuename) + local entries = {} for _, result in ipairs(results) do - cprint(" ${color.dump.reference}->${clear} %s%s", result.value, result.sourceinfo) + table.insert(entries, result) end + info[valuename] = entries + table.insert(info.api_entries, {name = valuename, entries = entries}) end end end end local files = target:get("files") if files then - cprint(" ${color.dump.string}files${clear}:") + local entries = {} for _, file in ipairs(files) do if not file:startswith("__remove_") then - cprint(" ${color.dump.reference}->${clear} %s%s", file, _get_sourceinfo_str(target, "files", file)) + table.insert(entries, {path = file, source = _get_sourceinfo(target, "files", file)}) end end + if #entries > 0 then + info.files = entries + end end local sourcekinds = hashset.new() for _, sourcebatch in pairs(target:sourcebatches()) do @@ -223,25 +275,85 @@ function _show_target(target) for _, sourcekind in sourcekinds:keys() do local compinst = target:compiler(sourcekind) if compinst then - cprint(" ${color.dump.string}compiler (%s)${clear}: %s", sourcekind, compinst:program()) - cprint(" ${color.dump.reference}->${clear} %s", os.args(compinst:compflags())) + info.compilers = info.compilers or {} + table.insert(info.compilers, { + sourcekind = sourcekind, + program = compinst:program(), + flags = os.args(compinst:compflags()), + flags_with_target = os.args(compinst:compflags({target = target})) + }) end end local linker = targetfile and target:linker() if linker then - cprint(" ${color.dump.string}linker (%s)${clear}: %s", linker:kind(), linker:program()) - cprint(" ${color.dump.reference}->${clear} %s", os.args(linker:linkflags())) + info.linker = { + kind = linker:kind(), + program = linker:program(), + flags = os.args(linker:linkflags()), + flags_with_target = os.args(linker:linkflags({target = target})) + } end - for _, sourcekind in sourcekinds:keys() do - local compinst = target:compiler(sourcekind) - if compinst then - cprint(" ${color.dump.string}compflags (%s)${clear}:", sourcekind) - cprint(" ${color.dump.reference}->${clear} %s", os.args(compinst:compflags({target = target}))) + return info +end + +function _print_entries(label, items, formatter) + if not items or #items == 0 then + return + end + cprint(" ${color.dump.string}%s${clear}:", label) + for _, item in ipairs(items) do + local text, source = formatter(item) + cprint(" ${color.dump.reference}->${clear} %s%s", text, source or "") + end +end + +function _print_target_info(info) + print("The information of target(%s):", info.name) + cprint(" ${color.dump.string}at${clear}: %s", info.at) + cprint(" ${color.dump.string}kind${clear}: %s", info.kind) + if info.targetfile then + cprint(" ${color.dump.string}targetfile${clear}: %s", info.targetfile) + end + _print_entries("deps", info.deps, function(item) + return item.name, _format_sourceinfo(item.source) + end) + _print_entries("rules", info.rules, function(item) + return item.name, _format_sourceinfo(item.source) + end) + _print_entries("options", info.options, function(item) + return item.name, _format_sourceinfo(item.source) + end) + _print_entries("packages", info.packages, function(item) + return item.name, _format_sourceinfo(item.source) + end) + if info.api_entries then + for _, entry in ipairs(info.api_entries) do + _print_entries(entry.name, entry.entries, function(item) + local value = item.value + local source = _format_sourceinfo(item.source) + return value, source + end) end end - if linker then - cprint(" ${color.dump.string}linkflags (%s)${clear}:", linker:kind()) - cprint(" ${color.dump.reference}->${clear} %s", os.args(linker:linkflags({target = target}))) + if info.files then + _print_entries("files", info.files, function(item) + return item.path, _format_sourceinfo(item.source) + end) + end + if info.compilers then + for _, compiler in ipairs(info.compilers) do + cprint(" ${color.dump.string}compiler (%s)${clear}: %s", compiler.sourcekind, compiler.program) + end + for _, compiler in ipairs(info.compilers) do + cprint(" ${color.dump.string}compflags (%s)${clear}:", compiler.sourcekind) + cprint(" ${color.dump.reference}->${clear} %s", compiler.flags_with_target) + end + end + if info.linker then + cprint(" ${color.dump.string}linker (%s)${clear}: %s", info.linker.kind, info.linker.program) + cprint(" ${color.dump.reference}->${clear} %s", info.linker.flags) + cprint(" ${color.dump.string}linkflags (%s)${clear}:", info.linker.kind) + cprint(" ${color.dump.reference}->${clear} %s", info.linker.flags_with_target) end end @@ -249,8 +361,14 @@ function main(name) -- get target config.load() + local opt = {json = option.get("json")} local target = assert(check_targetname(name)) - -- show target information - _show_target(target) + local info = _collect_target_info(target) + if opt.json then + info.api_entries = nil + print(json.encode(string.serializable(info) or {})) + else + _print_target_info(info) + end end -- cgit v1.3.1 From 1da60acf10492426338a6802b537589361e8c6b6 Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 14 Nov 2025 23:24:59 +0800 Subject: improve to dump target --- xmake/core/base/string.lua | 19 ------------------- xmake/plugins/show/info/target.lua | 23 +++++++++++------------ 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/xmake/core/base/string.lua b/xmake/core/base/string.lua index 658ca7345..2c7069cb9 100644 --- a/xmake/core/base/string.lua +++ b/xmake/core/base/string.lua @@ -238,25 +238,6 @@ function string.dump(object, deflate) return string.serialize(object, deflate) end --- convert object to json-serializable form -function string.serializable(object, opt) - opt = opt or {} - local vtype = type(object) - if vtype == "table" then - local result = {} - for k, v in pairs(object) do - result[k] = string.serializable(v, opt) - end - return result - elseif vtype == "function" or vtype == "userdata" or vtype == "thread" then - if opt.stringify then - return opt.stringify(object) - end - return tostring(object) - end - return object -end - -- serialize to string from the given object -- -- @param opt serialize options diff --git a/xmake/plugins/show/info/target.lua b/xmake/plugins/show/info/target.lua index 5384a739d..e4787a8e2 100644 --- a/xmake/plugins/show/info/target.lua +++ b/xmake/plugins/show/info/target.lua @@ -175,8 +175,8 @@ function _collect_target_info(target) if deps then local entries = {} for _, dep in ipairs(deps) do - local entry = {name = dep, source = _get_sourceinfo(target, "deps", dep)} - table.insert(entries, entry) + local entry = {name = dep, source = _get_sourceinfo(target, "deps", dep)} + table.insert(entries, entry) end if #entries > 0 then info.deps = entries @@ -186,8 +186,8 @@ function _collect_target_info(target) if rules then local entries = {} for _, value in ipairs(rules) do - local entry = {name = value, source = _get_sourceinfo(target, "rules", value)} - table.insert(entries, entry) + local entry = {name = value, source = _get_sourceinfo(target, "rules", value)} + table.insert(entries, entry) end if #entries > 0 then info.rules = entries @@ -202,7 +202,7 @@ function _collect_target_info(target) if #options > 0 then local entries = {} for _, value in ipairs(options) do - table.insert(entries, {name = value, source = _get_sourceinfo(target, "options", value)}) + table.insert(entries, {name = value, source = _get_sourceinfo(target, "options", value)}) end if #entries > 0 then info.options = entries @@ -228,20 +228,20 @@ function _collect_target_info(target) if #values > 0 then for _, value in ipairs(values) do local sourceinfo = _get_sourceinfo(target, valuename, value) - table.insert(results, {value = string.serializable(value), source = sourceinfo}) + table.insert(results, {value = value, source = sourceinfo}) end end local values_from_opts = _get_values_from_opts(target, valuename) for value, sourceinfo in pairs(values_from_opts) do - table.insert(results, {value = string.serializable(value), source = sourceinfo}) + table.insert(results, {value = value, source = sourceinfo}) end local values_from_pkgs = _get_values_from_pkgs(target, valuename) for value, sourceinfo in pairs(values_from_pkgs) do - table.insert(results, {value = string.serializable(value), source = sourceinfo}) + table.insert(results, {value = value, source = sourceinfo}) end local values_from_deps = _get_values_from_deps(target, valuename) for value, sourceinfo in pairs(values_from_deps) do - table.insert(results, {value = string.serializable(value), source = sourceinfo}) + table.insert(results, {value = value, source = sourceinfo}) end if #results > 0 then local entries = {} @@ -329,9 +329,8 @@ function _print_target_info(info) if info.api_entries then for _, entry in ipairs(info.api_entries) do _print_entries(entry.name, entry.entries, function(item) - local value = item.value local source = _format_sourceinfo(item.source) - return value, source + return item.value, source end) end end @@ -367,7 +366,7 @@ function main(name) local info = _collect_target_info(target) if opt.json then info.api_entries = nil - print(json.encode(string.serializable(info) or {})) + print(json.encode(info or {})) else _print_target_info(info) end -- cgit v1.3.1 From 31cab9106eeb43897e641ae80d5557d3b24c35f3 Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 14 Nov 2025 23:28:02 +0800 Subject: add pretty support for json module --- tests/modules/json/test.lua | 20 ++++++++++++++ xmake/core/base/json.lua | 65 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 73 insertions(+), 12 deletions(-) diff --git a/tests/modules/json/test.lua b/tests/modules/json/test.lua index a09f0918a..b2e53481b 100755 --- a/tests/modules/json/test.lua +++ b/tests/modules/json/test.lua @@ -38,6 +38,16 @@ function test_json_encode(t) t:are_equal(json_encode({1, "2", {a = 1}}), '[1,"2",{"a":1}]') t:are_equal(json_encode({1, "2", {b = true}}), '[1,"2",{"b":true}]') t:are_equal(json_encode(json.mark_as_array({1, 0xa, 0xdeadbeef, 0xffffffff, -1})), '[1,10,3735928559,4294967295,-1]') + local pretty_expected = table.concat({ + "{", + " \"name\": \"xmake\",", + " \"targets\": [", + " \"foo\",", + " \"bar\"", + " ]", + "}" + }, "\n") + t:are_equal(json_encode({name = "xmake", targets = {"foo", "bar"}}, {pretty = true, indent = 4}), pretty_expected) end function test_pure_json_decode(t) @@ -59,4 +69,14 @@ function test_pure_json_encode(t) t:are_equal(json_pure_encode({1, "2", {a = 1}}), '[1,"2",{"a":1}]') t:are_equal(json_pure_encode({1, "2", {b = true}}), '[1,"2",{"b":true}]') t:are_equal(json_pure_encode(json.mark_as_array({1, 0xa, 0xdeadbeef, 0xffffffff, -1})), '[1,10,3735928559,4294967295,-1]') + local pretty_expected = table.concat({ + "{", + " \"name\": \"xmake\",", + " \"targets\": [", + " \"foo\",", + " \"bar\"", + " ]", + "}" + }, "\n") + t:are_equal(json.encode({name = "xmake", targets = {"foo", "bar"}}, {pure = true, pretty = true, indent = 4}), pretty_expected) end diff --git a/xmake/core/base/json.lua b/xmake/core/base/json.lua index c93d298a9..2ed363461 100644 --- a/xmake/core/base/json.lua +++ b/xmake/core/base/json.lua @@ -130,7 +130,14 @@ function json._pure_parse_num_val(str, pos) return val, pos + #num_str end -function json._pure_stringify(obj, as_key) +function json._pure_stringify(obj, level, as_key, opt) + opt = opt or {} + level = level or 0 + local pretty = opt.pretty + local indent_step = pretty and (opt.indent or 4) or nil + local newline = pretty and "\n" or "" + local curr_indent = indent_step and string.rep(" ", indent_step * level) or nil + local child_indent = indent_step and string.rep(" ", indent_step * (level + 1)) or nil local s = {} local kind = json._pure_kind_of(obj) if kind == "array" then @@ -138,9 +145,25 @@ function json._pure_stringify(obj, as_key) os.raise("can\'t encode array as key.") end s[#s + 1] = '[' - for i, val in ipairs(obj) do - if i > 1 then s[#s + 1] = ',' end - s[#s + 1] = json._pure_stringify(val) + local arrlen = #obj + if pretty and arrlen > 0 then + s[#s + 1] = newline + end + for idx = 1, arrlen do + if idx > 1 then + s[#s + 1] = ',' + if pretty then + s[#s + 1] = newline + end + end + if pretty then + s[#s + 1] = child_indent + end + s[#s + 1] = json._pure_stringify(obj[idx], level + 1, false, opt) + end + if pretty and arrlen > 0 then + s[#s + 1] = newline + s[#s + 1] = curr_indent end s[#s + 1] = ']' elseif kind == "table" then @@ -148,11 +171,26 @@ function json._pure_stringify(obj, as_key) os.raise("can\'t encode table as key.") end s[#s + 1] = '{' + local first = true for k, v in pairs(obj) do - if #s > 1 then s[#s + 1] = ',' end - s[#s + 1] = json._pure_stringify(k, true) + if not first then + s[#s + 1] = ',' + end + if pretty then + s[#s + 1] = newline + s[#s + 1] = child_indent + end + s[#s + 1] = json._pure_stringify(k, level + 1, true, opt) s[#s + 1] = ':' - s[#s + 1] = json._pure_stringify(v) + if pretty then + s[#s + 1] = ' ' + end + s[#s + 1] = json._pure_stringify(v, level + 1, false, opt) + first = false + end + if pretty and not first then + s[#s + 1] = newline + s[#s + 1] = curr_indent end s[#s + 1] = '}' elseif kind == "string" then @@ -237,7 +275,7 @@ end -- encode json string using pua lua function json._pure_encode(luatable, opt) - return json._pure_stringify(luatable) + return json._pure_stringify(luatable, 0, false, opt) end -- support empty array @@ -281,11 +319,14 @@ end -- @return the json string -- function json.encode(luatable, opt) - local encode = cjson and cjson.encode or json._pure_encode - if opt and opt.pure then - encode = json._pure_encode + local use_pure = not cjson or (opt and (opt.pure or opt.pretty)) + local encode = use_pure and json._pure_encode or cjson.encode + local ok, jsonstr_or_errors + if use_pure then + ok, jsonstr_or_errors = utils.trycall(encode, nil, luatable, opt) + else + ok, jsonstr_or_errors = utils.trycall(encode, nil, luatable) end - local ok, jsonstr_or_errors = utils.trycall(encode, nil, luatable) if not ok then return nil, string.format("encode json failed, %s", jsonstr_or_errors) end -- cgit v1.3.1 From e4617488a56abbd89c24be114e8f7ae36da289e8 Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 14 Nov 2025 23:29:05 +0800 Subject: enable pretty output for xmake show --- xmake/plugins/show/info/basic.lua | 11 +++++++++-- xmake/plugins/show/info/target.lua | 11 +++++++++-- xmake/plugins/show/showlist.lua | 6 +++++- xmake/plugins/show/xmake.lua | 1 + 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/xmake/plugins/show/info/basic.lua b/xmake/plugins/show/info/basic.lua index a08b47d3a..47b0f72e7 100644 --- a/xmake/plugins/show/info/basic.lua +++ b/xmake/plugins/show/info/basic.lua @@ -128,13 +128,20 @@ function main() config.load() - local opt = {json = option.get("json")} + local opt = { + json = option.get("json"), + pretty = option.get("pretty") + } local result = opt.json and {} or nil result = _show_xmake_info(opt, result) result = _show_project_info(opt, result) if opt.json then - print(json.encode(result or {})) + local json_opt + if opt.pretty then + json_opt = {pretty = true} + end + print(json.encode(result or {}, json_opt)) end end diff --git a/xmake/plugins/show/info/target.lua b/xmake/plugins/show/info/target.lua index e4787a8e2..9400ad2fe 100644 --- a/xmake/plugins/show/info/target.lua +++ b/xmake/plugins/show/info/target.lua @@ -360,13 +360,20 @@ function main(name) -- get target config.load() - local opt = {json = option.get("json")} + local opt = { + json = option.get("json"), + pretty = option.get("pretty") + } local target = assert(check_targetname(name)) local info = _collect_target_info(target) if opt.json then info.api_entries = nil - print(json.encode(info or {})) + local json_opt + if opt.pretty then + json_opt = {pretty = true} + end + print(json.encode(info or {}, json_opt)) else _print_target_info(info) end diff --git a/xmake/plugins/show/showlist.lua b/xmake/plugins/show/showlist.lua index 9dadb59c6..e82a3ab52 100644 --- a/xmake/plugins/show/showlist.lua +++ b/xmake/plugins/show/showlist.lua @@ -40,7 +40,11 @@ function _show_text(values) end function _show_json(values) - print(json.encode(values)) + local opt = {} + if option.get("pretty") then + opt.pretty = true + end + print(json.encode(values, opt)) end function main(values) diff --git a/xmake/plugins/show/xmake.lua b/xmake/plugins/show/xmake.lua index 475a771b8..331aeb468 100644 --- a/xmake/plugins/show/xmake.lua +++ b/xmake/plugins/show/xmake.lua @@ -31,6 +31,7 @@ task("show") end}, {'g', "group", "kv", nil, "Filter targets by the given group name."}, {nil, "json", "k", false, "Show information with json format."}, + {nil, "pretty", "k", false, "Enable pretty formatted json output."}, {'t', "target", "kv", nil, "Show the information of the given target.", values = function (complete, opt) return import("private.utils.complete_helper.targets")(complete, opt) -- cgit v1.3.1 From 4f6a4ba89e0138ea5b6b1b86b2599ad711e29ba0 Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 14 Nov 2025 23:29:52 +0800 Subject: fix show target --- xmake/plugins/show/info/target.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xmake/plugins/show/info/target.lua b/xmake/plugins/show/info/target.lua index 9400ad2fe..67f9c4a69 100644 --- a/xmake/plugins/show/info/target.lua +++ b/xmake/plugins/show/info/target.lua @@ -70,8 +70,7 @@ function _get_values_from_opts(target, name) for _, opt_ in ipairs(target:orderopts()) do for _, value in ipairs(opt_:get(name)) do local tips = string.format("option(%s)", opt_:name()) - local sourceinfo = _get_sourceinfo(opt_, name, value, {tips = tips}) - values[value] = sourceinfo or {tips = tips} + values[value] = _get_sourceinfo(opt_, name, value, {tips = tips}) end end return values @@ -342,6 +341,9 @@ function _print_target_info(info) if info.compilers then for _, compiler in ipairs(info.compilers) do cprint(" ${color.dump.string}compiler (%s)${clear}: %s", compiler.sourcekind, compiler.program) + if compiler.flags then + cprint(" ${color.dump.reference}->${clear} %s", compiler.flags) + end end for _, compiler in ipairs(info.compilers) do cprint(" ${color.dump.string}compflags (%s)${clear}:", compiler.sourcekind) -- cgit v1.3.1 From 8d9b5970ed3f44a84f5e7ed5f7f140e72a9f1e97 Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 14 Nov 2025 23:30:37 +0800 Subject: fix test --- tests/modules/json/test.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/modules/json/test.lua b/tests/modules/json/test.lua index b2e53481b..b0e88112b 100755 --- a/tests/modules/json/test.lua +++ b/tests/modules/json/test.lua @@ -3,12 +3,12 @@ import("core.base.json") local json_null = json.null local json_pure_null = json.purenull -function json_decode(jsonstr) - return json.decode(jsonstr) +function json_decode(jsonstr, opt) + return json.decode(jsonstr, opt) end -function json_encode(luatable) - return json.encode(luatable) +function json_encode(luatable, opt) + return json.encode(luatable, opt) end function json_pure_decode(jsonstr) -- cgit v1.3.1 From d066068a571d6e6515e56270374e96518d20eaf0 Mon Sep 17 00:00:00 2001 From: ruki Date: Fri, 14 Nov 2025 23:34:02 +0800 Subject: fix test --- tests/modules/json/test.lua | 14 +++++++++----- xmake/core/base/json.lua | 8 +++++++- xmake/plugins/show/info/basic.lua | 2 +- xmake/plugins/show/info/target.lua | 2 +- xmake/plugins/show/showlist.lua | 1 + 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/tests/modules/json/test.lua b/tests/modules/json/test.lua index b0e88112b..6bd89389c 100755 --- a/tests/modules/json/test.lua +++ b/tests/modules/json/test.lua @@ -11,12 +11,16 @@ function json_encode(luatable, opt) return json.encode(luatable, opt) end -function json_pure_decode(jsonstr) - return json.decode(jsonstr, {pure = true}) +function json_pure_decode(jsonstr, opt) + opt = opt or {} + opt.pure = true + return json.decode(jsonstr, opt) end -function json_pure_encode(luatable) - return json.encode(luatable, {pure = true}) +function json_pure_encode(luatable, opt) + opt = opt or {} + opt.pure = true + return json.encode(luatable, opt) end function test_json_decode(t) @@ -78,5 +82,5 @@ function test_pure_json_encode(t) " ]", "}" }, "\n") - t:are_equal(json.encode({name = "xmake", targets = {"foo", "bar"}}, {pure = true, pretty = true, indent = 4}), pretty_expected) + t:are_equal(json_pure_encode({name = "xmake", targets = {"foo", "bar"}}, {pretty = true, indent = 4}), pretty_expected) end diff --git a/xmake/core/base/json.lua b/xmake/core/base/json.lua index 2ed363461..760adefa0 100644 --- a/xmake/core/base/json.lua +++ b/xmake/core/base/json.lua @@ -24,6 +24,7 @@ local json = json or {} -- load modules local io = require("base/io") local os = require("base/os") +local table = require("base/table") local utils = require("base/utils") -- export null @@ -134,6 +135,10 @@ function json._pure_stringify(obj, level, as_key, opt) opt = opt or {} level = level or 0 local pretty = opt.pretty + local orderkeys = opt.orderkeys + if orderkeys == nil and pretty then + orderkeys = true + end local indent_step = pretty and (opt.indent or 4) or nil local newline = pretty and "\n" or "" local curr_indent = indent_step and string.rep(" ", indent_step * level) or nil @@ -172,7 +177,8 @@ function json._pure_stringify(obj, level, as_key, opt) end s[#s + 1] = '{' local first = true - for k, v in pairs(obj) do + local iter = orderkeys and table.orderpairs or pairs + for k, v in iter(obj) do if not first then s[#s + 1] = ',' end diff --git a/xmake/plugins/show/info/basic.lua b/xmake/plugins/show/info/basic.lua index 47b0f72e7..4ad14fe98 100644 --- a/xmake/plugins/show/info/basic.lua +++ b/xmake/plugins/show/info/basic.lua @@ -140,7 +140,7 @@ function main() if opt.json then local json_opt if opt.pretty then - json_opt = {pretty = true} + json_opt = {pretty = true, orderkeys = true} end print(json.encode(result or {}, json_opt)) end diff --git a/xmake/plugins/show/info/target.lua b/xmake/plugins/show/info/target.lua index 67f9c4a69..62d8b6ddd 100644 --- a/xmake/plugins/show/info/target.lua +++ b/xmake/plugins/show/info/target.lua @@ -373,7 +373,7 @@ function main(name) info.api_entries = nil local json_opt if opt.pretty then - json_opt = {pretty = true} + json_opt = {pretty = true, orderkeys = true} end print(json.encode(info or {}, json_opt)) else diff --git a/xmake/plugins/show/showlist.lua b/xmake/plugins/show/showlist.lua index e82a3ab52..205ae21ed 100644 --- a/xmake/plugins/show/showlist.lua +++ b/xmake/plugins/show/showlist.lua @@ -43,6 +43,7 @@ function _show_json(values) local opt = {} if option.get("pretty") then opt.pretty = true + opt.orderkeys = true end print(json.encode(values, opt)) end -- cgit v1.3.1