summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2026-04-19 22:59:42 +0800
committerGitHub <[email protected]>2026-04-19 22:59:42 +0800
commit3a1b4e1719ba50b259afa27b450f06c39548003d (patch)
treed311536177e99c54120ba50e8ad429ecb65f682c
parent1813ff50ab7bd0cbddd2d7f9e510f7a2309fa4c9 (diff)
parentb8434770f9188e5871a5e3de8d09e6d01d17f2f4 (diff)
Merge pull request #7491 from xmake-io/xrepo
Add xrepo info --depgraph
-rw-r--r--xmake/actions/require/main.lua6
-rw-r--r--xmake/actions/require/xmake.lua11
-rw-r--r--xmake/modules/private/action/require/depgraph.lua178
-rw-r--r--xmake/modules/private/action/require/info.lua550
-rw-r--r--xmake/modules/private/xrepo/action/info.lua33
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