diff options
| author | ruki <[email protected]> | 2026-04-19 22:59:42 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-04-19 22:59:42 +0800 |
| commit | 3a1b4e1719ba50b259afa27b450f06c39548003d (patch) | |
| tree | d311536177e99c54120ba50e8ad429ecb65f682c | |
| parent | 1813ff50ab7bd0cbddd2d7f9e510f7a2309fa4c9 (diff) | |
| parent | b8434770f9188e5871a5e3de8d09e6d01d17f2f4 (diff) | |
Merge pull request #7491 from xmake-io/xrepo
Add xrepo info --depgraph
| -rw-r--r-- | xmake/actions/require/main.lua | 6 | ||||
| -rw-r--r-- | xmake/actions/require/xmake.lua | 11 | ||||
| -rw-r--r-- | xmake/modules/private/action/require/depgraph.lua | 178 | ||||
| -rw-r--r-- | xmake/modules/private/action/require/info.lua | 550 | ||||
| -rw-r--r-- | xmake/modules/private/xrepo/action/info.lua | 33 |
5 files changed, 585 insertions, 193 deletions
diff --git a/xmake/actions/require/main.lua b/xmake/actions/require/main.lua index 94965d810..c98ee039e 100644 --- a/xmake/actions/require/main.lua +++ b/xmake/actions/require/main.lua @@ -27,6 +27,7 @@ import("core.platform.platform") import("private.action.require.list") import("private.action.require.scan") import("private.action.require.info") +import("private.action.require.depgraph") import("private.action.require.fetch") import("private.action.require.clean") import("private.action.require.search") @@ -111,6 +112,11 @@ function main() info(option.get("requires")) + -- show the given package depgraph + elseif option.get("depgraph") then + + depgraph(option.get("requires")) + -- fetch the library info of the given packages elseif option.get("fetch") then diff --git a/xmake/actions/require/xmake.lua b/xmake/actions/require/xmake.lua index 77c56f784..3a0b15802 100644 --- a/xmake/actions/require/xmake.lua +++ b/xmake/actions/require/xmake.lua @@ -65,6 +65,17 @@ task("require") , {nil, "info", "k", nil, "Show the given package info.", "e.g.", " $ xmake require --info tbox" } + , {nil, "depgraph", "k", nil, "Show the dependency graph of the given packages.", + "e.g.", + " $ xmake require --depgraph libpng", + " $ xmake require --depgraph --format=json libpng", + " $ xmake require --depgraph --format=dot libpng" } + , {nil, "format", "kv", nil, "Set the output format.", + "e.g.", + " $ xmake require --info --format=json zlib", + " $ xmake require --depgraph --format=dot libpng", + "values: json (for --info/--depgraph), tree/dot (for --depgraph only)", + values = {"tree", "json", "dot"} } , {nil, "check", "k", nil, "Check whether the given package is supported", "e.g.", " $ xmake require --check tbox" } diff --git a/xmake/modules/private/action/require/depgraph.lua b/xmake/modules/private/action/require/depgraph.lua new file mode 100644 index 000000000..ba1fb9ff4 --- /dev/null +++ b/xmake/modules/private/action/require/depgraph.lua @@ -0,0 +1,178 @@ +--!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 depgraph.lua +-- + +-- imports +import("core.base.task") +import("core.base.option") +import("core.base.json") +import("core.project.project") +import("private.action.require.impl.package") +import("private.action.require.impl.repository") +import("private.action.require.impl.environment") +import("private.action.require.impl.utils.get_requires") + +-- collect single package entry with its direct dependencies +function _collect_package_entry(instance) + local deps = {} + local plaindeps = instance:plaindeps() + if plaindeps then + for _, dep in ipairs(plaindeps) do + table.insert(deps, dep:fullname()) + end + end + json.mark_as_array(deps) + return { + name = instance:fullname(), + version = instance:version_str(), + deps = deps + } +end + +-- collect the full dependency graph from loaded package instances +-- +-- returns a table with: +-- root_packages: packages that are not depended on by others +-- packages: all package entries with their direct deps +-- +function _collect_package_graph(instances) + local targets = {} + local roots = {} + local all_deps = {} + for _, instance in ipairs(instances) do + local entry = _collect_package_entry(instance) + table.insert(targets, entry) + for _, dep in ipairs(entry.deps) do + all_deps[dep] = true + end + end + for _, entry in ipairs(targets) do + if not all_deps[entry.name] then + table.insert(roots, entry.name) + end + end + json.mark_as_array(roots) + return { + root_packages = roots, + packages = targets + } +end + +-- print dependency tree recursively +-- +-- e.g. +-- libpng +-- \-- zlib +-- +-- already expanded subtrees are marked with (*) to avoid duplication +-- +function _print_dep_tree(packages_map, name, prefix, expanded) + expanded[name] = true + local entry = packages_map[name] + local deps = entry and entry.deps or {} + for i, dep in ipairs(deps) do + local is_last = (i == #deps) + local connector = is_last and "\\-- " or "|-- " + local next_prefix = prefix .. (is_last and " " or "| ") + local dep_entry = packages_map[dep] + local dep_deps = dep_entry and dep_entry.deps or {} + if expanded[dep] and #dep_deps > 0 then + cprint("%s%s${color.dump.reference}%s${clear} ${dim}(*)${clear}", prefix, connector, dep) + else + cprint("%s%s${color.dump.reference}%s${clear}", prefix, connector, dep) + _print_dep_tree(packages_map, dep, next_prefix, expanded) + end + end +end + +-- print the package dependency graph as a tree +function _print_package_graph(graph) + local packages_map = {} + for _, pkg in ipairs(graph.packages) do + packages_map[pkg.name] = pkg + end + local expanded = {} + for _, root in ipairs(graph.root_packages) do + cprint("${color.dump.string}%s${clear}", root) + _print_dep_tree(packages_map, root, "", expanded) + end +end + +-- print the package dependency graph in graphviz DOT format +-- +-- e.g. +-- digraph { +-- "zlib" +-- "libpng" -> "zlib" +-- } +-- +function _print_dot_graph(graph) + print("digraph {") + for _, pkg in ipairs(graph.packages) do + if #pkg.deps == 0 then + print(string.format(" \"%s\"", pkg.name)) + else + for _, dep in ipairs(pkg.deps) do + print(string.format(" \"%s\" -> \"%s\"", pkg.name, dep)) + end + end + end + print("}") +end + +-- show the given package dependency graph +-- +-- supported output formats (via --format): +-- tree (default): ASCII tree view +-- json: structured JSON output +-- dot: graphviz DOT format +-- +function main(requires_raw) + + -- get requires and extra config + local requires, requires_extra = get_requires(requires_raw) + if not requires or #requires == 0 then + return + end + + -- enter environment + environment.enter() + + -- pull all repositories first if not exists + if not repository.pulled() then + task.run("repo", {update = true}) + end + + -- load all packages and collect dependency graph + local instances = package.load_packages(requires, {requires_extra = requires_extra}) + local graph = _collect_package_graph(instances) + + -- output in the specified format + local format = option.get("format") or "tree" + if format == "json" then + print(json.encode(graph, {pretty = true, orderkeys = true})) + elseif format == "dot" then + _print_dot_graph(graph) + else + _print_package_graph(graph) + end + + -- leave environment + environment.leave() +end diff --git a/xmake/modules/private/action/require/info.lua b/xmake/modules/private/action/require/info.lua index 4ef702a2c..b0b1cc24b 100644 --- a/xmake/modules/private/action/require/info.lua +++ b/xmake/modules/private/action/require/info.lua @@ -21,6 +21,7 @@ -- imports import("core.base.task") import("core.base.option") +import("core.base.json") import("core.base.hashset") import("core.project.project") import("core.package.package", {alias = "core_package"}) @@ -63,74 +64,212 @@ function _info(instance) return info end --- show the given package info -function main(requires_raw) +-- collect package info as table for json output +function _collect_package_info(instance) + local info = {} + local requireinfo = instance:requireinfo() or {} + info.require = requireinfo.originstr + info.description = instance:get("description") + info.version = instance:version_str() + info.license = instance:get("license") - -- get requires and extra config - local requires_extra = nil - local requires, requires_extra = get_requires(requires_raw) - if not requires or #requires == 0 then - return + -- urls + local urls = instance:urls() + if urls and #urls > 0 then + info.urls = {} + json.mark_as_array(info.urls) + local schemes = instance:schemes_orderlist() + if schemes then + for _, scheme in ipairs(schemes) do + if not scheme:is_precompiled() then + local surls = scheme:urls() + if surls and #surls > 0 then + local scheme_name = scheme:is_default() and "default" or scheme:name() + local scheme_info = {name = scheme_name, urls = {}} + json.mark_as_array(scheme_info.urls) + for _, url in ipairs(surls) do + local url_entry = {url = filter.handle(url, instance)} + if git.asgiturl(url) then + local url_alias = scheme:url_alias(url) + url_entry.revision = scheme:revision(url_alias) or scheme:tag() or scheme:version_str() + else + url_entry.sourcehash = scheme:sourcehash(scheme:url_alias(url)) + end + table.insert(scheme_info.urls, url_entry) + end + table.insert(info.urls, scheme_info) + end + end + end + end end - -- enter environment - environment.enter() + -- repository + local repo = instance:repo() + if repo then + info.repo = {name = repo:name(), url = repo:url(), branch = repo:branch()} + end - -- pull all repositories first if not exists - if not repository.pulled() then - task.run("repo", {update = true}) + -- deps + local deps = instance:orderdeps() + if deps and #deps > 0 then + info.deps = {} + json.mark_as_array(info.deps) + for _, dep in ipairs(deps) do + local dep_requireinfo = dep:requireinfo() or {} + table.insert(info.deps, dep_requireinfo.originstr) + end end - -- show title - print("The package info of project:") + info.cachedir = instance:cachedir() + info.installdir = instance:installdir() + info.searchdirs = table.wrap(core_package.searchdirs()) + json.mark_as_array(info.searchdirs) - -- list all packages - for _, instance in ipairs(package.load_packages(requires, {requires_extra = requires_extra})) do + -- fetch info + local fetchinfo = instance:fetch() + if fetchinfo then + info.fetchinfo = {} + for name, finfo in pairs(fetchinfo) do + info.fetchinfo[name] = table.wrap(table.unwrap(finfo)) + end + end - -- show package name - local requireinfo = instance:requireinfo() or {} - cprint(" ${color.dump.string_quote}require${clear}(%s): ", requireinfo.originstr) + -- platforms + local platforms = {} + local on_install = instance:get("install") + if type(on_install) == "table" then + for plat, _ in pairs(on_install) do + table.insert(platforms, plat) + end + else + table.insert(platforms, "all") + end + json.mark_as_array(platforms) + info.platforms = platforms - -- show description - local description = instance:get("description") - if description then - cprint(" -> ${color.dump.string_quote}description${clear}: %s", description) + -- requires + info.requires = {plat = instance:plat(), arch = instance:arch()} + local configs_required = instance:configs() + if configs_required then + info.requires.configs = configs_required + end + + -- user configs + local configs_defined = instance:get("configs") + if configs_defined then + local configs = {} + local builtin_configs = {} + for _, conf in ipairs(configs_defined) do + local configs_extra = instance:extraconf("configs", conf) + if configs_extra then + local entry = {name = conf} + if configs_extra.description then + entry.description = configs_extra.description + end + if configs_extra.default ~= nil then + entry.default = configs_extra.default + end + if configs_extra.type then + entry.type = configs_extra.type + end + if configs_extra.values then + entry.values = configs_extra.values + end + if configs_extra.readonly then + entry.readonly = true + end + if configs_extra.builtin then + table.insert(builtin_configs, entry) + else + table.insert(configs, entry) + end + end + end + if #configs > 0 then + json.mark_as_array(configs) + info.configs = configs end + if #builtin_configs > 0 then + json.mark_as_array(builtin_configs) + info.builtin_configs = builtin_configs + end + end - -- show version - local version = instance:version_str() - if version then - cprint(" -> ${color.dump.string_quote}version${clear}: %s", version) + -- components + local components = instance:get("components") + if components then + info.components = {} + json.mark_as_array(info.components) + for _, comp in ipairs(components) do + local comp_info = {name = comp} + local plaindeps = instance:extraconf("components", comp, "deps") + if plaindeps then + comp_info.deps = table.wrap(plaindeps) + json.mark_as_array(comp_info.deps) + end + table.insert(info.components, comp_info) end + end - -- show license - local license = instance:get("license") - if license then - cprint(" -> ${color.dump.string_quote}license${clear}: %s", license) + -- references + local references = instance:references() + if references then + info.references = {} + json.mark_as_array(info.references) + for projectdir, refdate in pairs(references) do + table.insert(info.references, {dir = projectdir, date = refdate, exists = os.isdir(projectdir)}) end + end + return info +end + +-- print the given package info +function _print_package_info(instance) - -- show urls - local urls = instance:urls() - if urls and #urls > 0 then - cprint(" -> ${color.dump.string_quote}urls${clear}:") - local schemes = instance:schemes_orderlist() - if schemes then - for _, scheme in ipairs(schemes) do - if not scheme:is_precompiled() then - local urls = scheme:urls() - if urls and #urls > 0 then - local scheme_name = scheme:is_default() and "default" or scheme:name() - cprint(" -> ${magenta}%s${clear}:", scheme_name) - for _, url in ipairs(urls) do - print(" -> %s", filter.handle(url, instance)) - if git.asgiturl(url) then - local url_alias = scheme:url_alias(url) - cprint(" -> ${yellow}%s", scheme:revision(url_alias) or scheme:tag() or scheme:version_str()) - else - local sourcehash = scheme:sourcehash(scheme:url_alias(url)) - if sourcehash then - cprint(" -> ${yellow}%s", sourcehash) - end + -- show package name + local requireinfo = instance:requireinfo() or {} + cprint(" ${color.dump.string_quote}require${clear}(%s): ", requireinfo.originstr) + + -- show description + local description = instance:get("description") + if description then + cprint(" -> ${color.dump.string_quote}description${clear}: %s", description) + end + + -- show version + local version = instance:version_str() + if version then + cprint(" -> ${color.dump.string_quote}version${clear}: %s", version) + end + + -- show license + local license = instance:get("license") + if license then + cprint(" -> ${color.dump.string_quote}license${clear}: %s", license) + end + + -- show urls + local urls = instance:urls() + if urls and #urls > 0 then + cprint(" -> ${color.dump.string_quote}urls${clear}:") + local schemes = instance:schemes_orderlist() + if schemes then + for _, scheme in ipairs(schemes) do + if not scheme:is_precompiled() then + local urls = scheme:urls() + if urls and #urls > 0 then + local scheme_name = scheme:is_default() and "default" or scheme:name() + cprint(" -> ${magenta}%s${clear}:", scheme_name) + for _, url in ipairs(urls) do + print(" -> %s", filter.handle(url, instance)) + if git.asgiturl(url) then + local url_alias = scheme:url_alias(url) + cprint(" -> ${yellow}%s", scheme:revision(url_alias) or scheme:tag() or scheme:version_str()) + else + local sourcehash = scheme:sourcehash(scheme:url_alias(url)) + if sourcehash then + cprint(" -> ${yellow}%s", sourcehash) end end end @@ -138,173 +277,208 @@ function main(requires_raw) end end end + end - -- show repository - local repo = instance:repo() - if repo then - cprint(" -> ${color.dump.string_quote}repo${clear}: %s %s %s", repo:name(), repo:url(), repo:branch() or "") - end + -- show repository + local repo = instance:repo() + if repo then + cprint(" -> ${color.dump.string_quote}repo${clear}: %s %s %s", repo:name(), repo:url(), repo:branch() or "") + end - -- show deps - local deps = instance:orderdeps() - if deps and #deps > 0 then - cprint(" -> ${color.dump.string_quote}deps${clear}:") - for _, dep in ipairs(deps) do - requireinfo = dep:requireinfo() or {} - cprint(" -> %s", requireinfo.originstr) - end + -- show deps + local deps = instance:orderdeps() + if deps and #deps > 0 then + cprint(" -> ${color.dump.string_quote}deps${clear}:") + for _, dep in ipairs(deps) do + requireinfo = dep:requireinfo() or {} + cprint(" -> %s", requireinfo.originstr) end + end - -- show cache directory - cprint(" -> ${color.dump.string_quote}cachedir${clear}: %s", instance:cachedir()) + -- show cache directory + cprint(" -> ${color.dump.string_quote}cachedir${clear}: %s", instance:cachedir()) - -- show install directory - cprint(" -> ${color.dump.string_quote}installdir${clear}: %s", instance:installdir()) + -- show install directory + cprint(" -> ${color.dump.string_quote}installdir${clear}: %s", instance:installdir()) - -- show search directories and search names - cprint(" -> ${color.dump.string_quote}searchdirs${clear}: %s", table.concat(table.wrap(core_package.searchdirs()), path.envsep())) - local searchnames = hashset.new() - for _, url in ipairs(urls) do - local url = filter.handle(url, instance) - if git.checkurl(url) then - searchnames:insert(instance:name() .. archive.extension(url) .. " ${dim}(git)${clear}") - searchnames:insert(path.basename(url_filename(url)) .. " ${dim}(git)${clear}") - else - local extension = archive.extension(url) - if extension then - searchnames:insert(instance:name() .. "-" .. instance:version_str() .. extension) - end - searchnames:insert(url_filename(url)) + -- show search directories and search names + cprint(" -> ${color.dump.string_quote}searchdirs${clear}: %s", table.concat(table.wrap(core_package.searchdirs()), path.envsep())) + local searchnames = hashset.new() + for _, url in ipairs(urls) do + local url = filter.handle(url, instance) + if git.checkurl(url) then + searchnames:insert(instance:name() .. archive.extension(url) .. " ${dim}(git)${clear}") + searchnames:insert(path.basename(url_filename(url)) .. " ${dim}(git)${clear}") + else + local extension = archive.extension(url) + if extension then + searchnames:insert(instance:name() .. "-" .. instance:version_str() .. extension) + end + searchnames:insert(url_filename(url)) - -- match github name mangling https://github.com/xmake-io/xmake/issues/1343 - local github_name = url_filename.github_filename(url) - if github_name then - searchnames:insert(github_name) - end + -- match github name mangling https://github.com/xmake-io/xmake/issues/1343 + local github_name = url_filename.github_filename(url) + if github_name then + searchnames:insert(github_name) end end - cprint(" -> ${color.dump.string_quote}searchnames${clear}:") - for _, searchname in searchnames:keys() do - cprint(" -> %s", searchname) - end + end + cprint(" -> ${color.dump.string_quote}searchnames${clear}:") + for _, searchname in searchnames:keys() do + cprint(" -> %s", searchname) + end - -- show fetch info - cprint(" -> ${color.dump.string_quote}fetchinfo${clear}: %s", _info(instance)) - local fetchinfo = instance:fetch() - if fetchinfo then - for name, info in pairs(fetchinfo) do - local info = table.unwrap(info) - if type(info) ~= "table" then - info = tostring(info) - end - cprint(" -> ${color.dump.string_quote}%s${clear}: %s", name, table.concat(table.wrap(info), " ")) + -- show fetch info + cprint(" -> ${color.dump.string_quote}fetchinfo${clear}: %s", _info(instance)) + local fetchinfo = instance:fetch() + if fetchinfo then + for name, info in pairs(fetchinfo) do + local info = table.unwrap(info) + if type(info) ~= "table" then + info = tostring(info) end + cprint(" -> ${color.dump.string_quote}%s${clear}: %s", name, table.concat(table.wrap(info), " ")) end + end - -- show supported platforms - local platforms = {} - local on_install = instance:get("install") - if type(on_install) == "table" then - for plat, _ in pairs(on_install) do - table.insert(platforms, plat) - end - else - table.insert(platforms, "all") + -- show supported platforms + local platforms = {} + local on_install = instance:get("install") + if type(on_install) == "table" then + for plat, _ in pairs(on_install) do + table.insert(platforms, plat) end - cprint(" -> ${color.dump.string_quote}platforms${clear}: %s", table.concat(platforms, ", ")) + else + table.insert(platforms, "all") + end + cprint(" -> ${color.dump.string_quote}platforms${clear}: %s", table.concat(platforms, ", ")) - -- show requires - cprint(" -> ${color.dump.string_quote}requires${clear}:") - cprint(" -> ${cyan}plat${clear}: %s", instance:plat()) - cprint(" -> ${cyan}arch${clear}: %s", instance:arch()) - local configs_required = instance:configs() - if configs_required then - cprint(" -> ${cyan}configs${clear}:") - for name, value in pairs(configs_required) do - cprint(" -> %s: %s", name, value) - end + -- show requires + cprint(" -> ${color.dump.string_quote}requires${clear}:") + cprint(" -> ${cyan}plat${clear}: %s", instance:plat()) + cprint(" -> ${cyan}arch${clear}: %s", instance:arch()) + local configs_required = instance:configs() + if configs_required then + cprint(" -> ${cyan}configs${clear}:") + for name, value in pairs(configs_required) do + cprint(" -> %s: %s", name, value) end + end - -- show user configs - local configs_defined = instance:get("configs") - if configs_defined then - cprint(" -> ${color.dump.string_quote}configs${clear}:") - for _, conf in ipairs(configs_defined) do - local configs_extra = instance:extraconf("configs", conf) - if configs_extra and not configs_extra.builtin then - cprintf(" -> ${cyan}%s${clear}: ", conf) - if configs_extra.description then - printf(configs_extra.description) - end - if configs_extra.default ~= nil then - printf(" (default: %s)", configs_extra.default) - elseif configs_extra.type ~= nil and configs_extra.type ~= "string" then - printf(" (type: %s)", configs_extra.type) - end - if configs_extra.readonly then - printf(" (readonly)") - end - print("") - if configs_extra.values then - cprint(" -> values: %s", string.serialize(configs_extra.values, true)) - end + -- show user configs + local configs_defined = instance:get("configs") + if configs_defined then + cprint(" -> ${color.dump.string_quote}configs${clear}:") + for _, conf in ipairs(configs_defined) do + local configs_extra = instance:extraconf("configs", conf) + if configs_extra and not configs_extra.builtin then + cprintf(" -> ${cyan}%s${clear}: ", conf) + if configs_extra.description then + printf(configs_extra.description) + end + if configs_extra.default ~= nil then + printf(" (default: %s)", configs_extra.default) + elseif configs_extra.type ~= nil and configs_extra.type ~= "string" then + printf(" (type: %s)", configs_extra.type) + end + if configs_extra.readonly then + printf(" (readonly)") + end + print("") + if configs_extra.values then + cprint(" -> values: %s", string.serialize(configs_extra.values, true)) end end end + end - -- show builtin configs - local configs_defined = instance:get("configs") - if configs_defined then - cprint(" -> ${color.dump.string_quote}configs (builtin)${clear}:") - for _, conf in ipairs(configs_defined) do - local configs_extra = instance:extraconf("configs", conf) - if configs_extra and configs_extra.builtin then - cprintf(" -> ${cyan}%s${clear}: ", conf) - if configs_extra.description then - printf(configs_extra.description) - end - if configs_extra.default ~= nil then - printf(" (default: %s)", configs_extra.default) - elseif configs_extra.type ~= nil and configs_extra.type ~= "string" then - printf(" (type: %s)", configs_extra.type) - end - print("") - if configs_extra.values then - cprint(" -> values: %s", string.serialize(configs_extra.values, true)) - end + -- show builtin configs + local configs_defined = instance:get("configs") + if configs_defined then + cprint(" -> ${color.dump.string_quote}configs (builtin)${clear}:") + for _, conf in ipairs(configs_defined) do + local configs_extra = instance:extraconf("configs", conf) + if configs_extra and configs_extra.builtin then + cprintf(" -> ${cyan}%s${clear}: ", conf) + if configs_extra.description then + printf(configs_extra.description) + end + if configs_extra.default ~= nil then + printf(" (default: %s)", configs_extra.default) + elseif configs_extra.type ~= nil and configs_extra.type ~= "string" then + printf(" (type: %s)", configs_extra.type) + end + print("") + if configs_extra.values then + cprint(" -> values: %s", string.serialize(configs_extra.values, true)) end end end + end - -- show components - local components = instance:get("components") - if components then - cprint(" -> ${color.dump.string_quote}components${clear}: ") - for _, comp in ipairs(components) do - cprintf(" -> ${cyan}%s${clear}: ", comp) - local plaindeps = instance:extraconf("components", comp, "deps") - if plaindeps then - print("%s", table.concat(table.wrap(plaindeps), ", ")) - else - print("") - end + -- show components + local components = instance:get("components") + if components then + cprint(" -> ${color.dump.string_quote}components${clear}: ") + for _, comp in ipairs(components) do + cprintf(" -> ${cyan}%s${clear}: ", comp) + local plaindeps = instance:extraconf("components", comp, "deps") + if plaindeps then + print("%s", table.concat(table.wrap(plaindeps), ", ")) + else + print("") end end + end - -- show references - local references = instance:references() - if references then - cprint(" -> ${color.dump.string_quote}references${clear}:") - for projectdir, refdate in pairs(references) do - cprint(" -> %s: %s%s", refdate, projectdir, os.isdir(projectdir) and "" or " ${red}(not found)${clear}") - end + -- show references + local references = instance:references() + if references then + cprint(" -> ${color.dump.string_quote}references${clear}:") + for projectdir, refdate in pairs(references) do + cprint(" -> %s: %s%s", refdate, projectdir, os.isdir(projectdir) and "" or " ${red}(not found)${clear}") end + end + + -- end + print("") +end + +-- show the given package info +function main(requires_raw) + + -- get requires and extra config + local requires_extra = nil + local requires, requires_extra = get_requires(requires_raw) + if not requires or #requires == 0 then + return + end + + -- enter environment + environment.enter() - -- end - print("") + -- pull all repositories first if not exists + if not repository.pulled() then + task.run("repo", {update = true}) + end + + -- list all packages + local instances = package.load_packages(requires, {requires_extra = requires_extra}) + local format = option.get("format") + if format == "json" then + local results = {} + json.mark_as_array(results) + for _, instance in ipairs(instances) do + table.insert(results, _collect_package_info(instance)) + end + print(json.encode(results, {pretty = true, orderkeys = true})) + else + print("The package info of project:") + for _, instance in ipairs(instances) do + _print_package_info(instance) + end end -- leave environment environment.leave() end - diff --git a/xmake/modules/private/xrepo/action/info.lua b/xmake/modules/private/xrepo/action/info.lua index 8fa58fcdb..d7420533d 100644 --- a/xmake/modules/private/xrepo/action/info.lua +++ b/xmake/modules/private/xrepo/action/info.lua @@ -40,6 +40,17 @@ function menu_options() "e.g.", " - xrepo fetch --configs=\"runtimes='MD'\" zlib", " - xrepo fetch --configs=\"regex=true,thread=true\" boost"}, + {nil, "depgraph", "k", nil, "Show the dependency graph of the given packages.", + "e.g.", + " - xrepo info --depgraph libpng", + " - xrepo info --depgraph --format=json libpng", + " - xrepo info --depgraph --format=dot libpng"}, + {nil, "format", "kv", nil, "Set the output format.", + "e.g.", + " - xrepo info --format=json zlib", + " - xrepo info --depgraph --format=dot libpng", + "values: json (for --info/--depgraph), tree/dot (for --depgraph only)", + values = {"tree", "json", "dot"}}, {}, {nil, "packages", "vs", nil, "The packages list.", "e.g.", @@ -75,10 +86,13 @@ function _info_packages(packages) os.cd(workdir) end - -- do configure first + -- do configure first, use `-q` to suppress checking noise + -- unless `-vD` is enabled for diagnosis local config_argv = {"f", "-c"} if option.get("diagnosis") then table.insert(config_argv, "-vD") + else + table.insert(config_argv, "-q") end if option.get("plat") then table.insert(config_argv, "-p") @@ -88,20 +102,29 @@ function _info_packages(packages) table.insert(config_argv, "-a") table.insert(config_argv, option.get("arch")) end - local mode = option.get("mode") + local mode = option.get("mode") if mode then table.insert(config_argv, "-m") table.insert(config_argv, mode) end - local kind = option.get("kind") + local kind = option.get("kind") if kind then table.insert(config_argv, "-k") table.insert(config_argv, kind) end os.vrunv(os.programfile(), config_argv) - -- show info - local require_argv = {"require", "--info"} + -- show package info or dependency graph + local require_argv = {"require"} + if option.get("depgraph") then + table.insert(require_argv, "--depgraph") + else + table.insert(require_argv, "--info") + end + local format = option.get("format") + if format then + table.insert(require_argv, "--format=" .. format) + end if option.get("verbose") then table.insert(require_argv, "-v") end |
