blob: 59cfaa4885ac870dac21ba4b62d748f649321fc2 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2010 The ChromiumOS Authors
Jim Hebert91c052c2011-03-11 11:00:53 -08002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Alex Klein9e7b29e2023-04-11 16:10:31 -06005"""Command to extract the dependency tree for a given package.
Mike Frysinger012371e2019-01-03 03:42:32 -05006
7This produces JSON output for other tools to process.
8"""
Don Garrett25f309a2014-03-19 14:02:12 -07009
Chris McDonaldd2fa6162019-07-30 15:30:58 -060010from __future__ import absolute_import
Mike Frysinger383367e2014-09-16 15:06:17 -040011
Mike Frysingera942aee2020-03-20 03:53:37 -040012import sys
Don Garrettf8bf7842014-03-20 17:03:42 -070013
Mike Frysinger06a51c82021-04-06 11:39:17 -040014from chromite.lib import build_target_lib
Jim Hebertcf870d72013-06-12 15:33:34 -070015from chromite.lib import commandline
Chris McDonald59650c32021-07-20 15:29:28 -060016from chromite.lib.depgraph import DepGraphGenerator
Alex Klein18a60af2020-06-11 12:08:47 -060017from chromite.lib.parser import package_info
Alex Klein73eba212021-09-09 11:43:33 -060018from chromite.utils import pformat
Mike Frysingercc838832014-05-24 13:10:30 -040019
Chris McDonaldd8a7f112019-11-01 10:35:07 -060020
Mike Frysingera994fc12023-08-29 11:40:29 -040021def FlattenDepTree(deptree, pkgtable=None, parentcpv=None):
Alex Klein1699fab2022-09-08 08:46:06 -060022 """Simplify dependency json.
Don Garrett25f309a2014-03-19 14:02:12 -070023
Alex Klein1699fab2022-09-08 08:46:06 -060024 Turn something like this (the parallel_emerge DepsTree format):
25 {
26 "app-admin/eselect-1.2.9": {
Jim Hebert91c052c2011-03-11 11:00:53 -080027 "action": "merge",
Alex Klein1699fab2022-09-08 08:46:06 -060028 "deps": {
29 "sys-apps/coreutils-7.5-r1": {
30 "action": "merge",
31 "deps": {},
32 "deptype": "runtime"
33 },
34 ...
35 }
36 }
Jim Hebert91c052c2011-03-11 11:00:53 -080037 }
Alex Klein1699fab2022-09-08 08:46:06 -060038 ...into something like this (the cros_extract_deps format):
39 {
40 "app-admin/eselect-1.2.9": {
41 "deps": ["coreutils-7.5-r1"],
42 "rev_deps": [],
43 "name": "eselect",
44 "category": "app-admin",
45 "version": "1.2.9",
46 "full_name": "app-admin/eselect-1.2.9",
47 "action": "merge"
48 },
49 "sys-apps/coreutils-7.5-r1": {
50 "deps": [],
51 "rev_deps": ["app-admin/eselect-1.2.9"],
52 "name": "coreutils",
53 "category": "sys-apps",
54 "version": "7.5-r1",
55 "full_name": "sys-apps/coreutils-7.5-r1",
56 "action": "merge"
57 }
58 }
Yu-Ju Hong7f01e9a2014-10-23 11:01:57 -070059
Alex Klein9e7b29e2023-04-11 16:10:31 -060060 Args:
Alex Klein1699fab2022-09-08 08:46:06 -060061 deptree: The dependency tree.
62 pkgtable: The package table to update. If None, create a new one.
63 parentcpv: The parent CPV.
Yu-Ju Hong7f01e9a2014-10-23 11:01:57 -070064
Alex Klein9e7b29e2023-04-11 16:10:31 -060065 Returns:
Alex Klein1699fab2022-09-08 08:46:06 -060066 A flattened dependency tree.
67 """
68 if pkgtable is None:
69 pkgtable = {}
70 for cpv, record in deptree.items():
71 if cpv not in pkgtable:
Alex Klein7bd88b12023-05-19 15:39:55 -060072 pkg_info = package_info.parse(cpv)
Alex Klein1699fab2022-09-08 08:46:06 -060073 pkgtable[cpv] = {
74 "deps": [],
75 "rev_deps": [],
Alex Klein7bd88b12023-05-19 15:39:55 -060076 "name": pkg_info.package,
77 "category": pkg_info.category,
78 "version": pkg_info.vr,
Alex Klein1699fab2022-09-08 08:46:06 -060079 "full_name": cpv,
Alex Klein1699fab2022-09-08 08:46:06 -060080 "action": record["action"],
81 }
Yu-Ju Hong7f01e9a2014-10-23 11:01:57 -070082
Alex Klein1699fab2022-09-08 08:46:06 -060083 # If we have a parent, that is a rev_dep for the current package.
84 if parentcpv:
85 pkgtable[cpv]["rev_deps"].append(parentcpv)
86 # If current package has any deps, record those.
87 for childcpv in record["deps"]:
88 pkgtable[cpv]["deps"].append(childcpv)
89 # Visit the subtree recursively as well.
Mike Frysingera994fc12023-08-29 11:40:29 -040090 FlattenDepTree(record["deps"], pkgtable=pkgtable, parentcpv=cpv)
Alex Klein1699fab2022-09-08 08:46:06 -060091 # Sort 'deps' & 'rev_deps' alphabetically to make them more readable.
92 pkgtable[cpv]["deps"].sort()
93 pkgtable[cpv]["rev_deps"].sort()
94 return pkgtable
Jim Hebert91c052c2011-03-11 11:00:53 -080095
96
Chris McDonalde69db662018-11-15 12:50:18 -070097def ParseArgs(argv):
Alex Klein1699fab2022-09-08 08:46:06 -060098 """Parse command line arguments."""
99 parser = commandline.ArgumentParser(description=__doc__)
100 target = parser.add_mutually_exclusive_group()
101 target.add_argument("--sysroot", type="path", help="Path to the sysroot.")
102 target.add_argument("--board", help="Board name.")
Chris McDonalde69db662018-11-15 12:50:18 -0700103
Alex Klein1699fab2022-09-08 08:46:06 -0600104 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600105 "--output-path", default=None, help="Write output to the given path."
106 )
107 parser.add_argument("pkgs", nargs="*")
108 opts = parser.parse_args(argv)
109 opts.Freeze()
110 return opts
Jim Hebert91c052c2011-03-11 11:00:53 -0800111
Chris McDonalde69db662018-11-15 12:50:18 -0700112
Ned Nguyene16dcfb2019-03-22 10:36:05 -0600113def FilterObsoleteDeps(package_deps):
Alex Klein1699fab2022-09-08 08:46:06 -0600114 """Remove all the packages that are to be uninstalled from |package_deps|.
Ned Nguyene16dcfb2019-03-22 10:36:05 -0600115
Alex Klein1699fab2022-09-08 08:46:06 -0600116 Returns:
Alex Klein9e7b29e2023-04-11 16:10:31 -0600117 None since this method mutates |package_deps| directly.
Alex Klein1699fab2022-09-08 08:46:06 -0600118 """
119 obsolete_package_deps = []
120 for k, v in package_deps.items():
121 if v["action"] in ("merge", "nomerge"):
122 continue
123 elif v["action"] == "uninstall":
124 obsolete_package_deps.append(k)
125 else:
126 assert False, "Unrecognized action. Package dep data: %s" % v
127 for p in obsolete_package_deps:
128 del package_deps[p]
Ned Nguyene16dcfb2019-03-22 10:36:05 -0600129
130
Alex Klein1699fab2022-09-08 08:46:06 -0600131def ExtractDeps(
132 sysroot,
133 package_list,
Alex Klein1699fab2022-09-08 08:46:06 -0600134 include_bdepend=True,
135 backtrack=True,
136):
137 """Returns the set of dependencies for the packages in package_list.
Ned Nguyendd3e09f2019-03-14 18:54:03 -0600138
Alex Klein1699fab2022-09-08 08:46:06 -0600139 For calculating dependencies graph, this should only consider packages
140 that are DEPENDS, RDEPENDS, or BDEPENDS. Essentially, this should answer the
141 question "which are all the packages which changing them may change the
142 execution of any binaries produced by packages in |package_list|."
Ned Nguyendd3e09f2019-03-14 18:54:03 -0600143
Alex Klein1699fab2022-09-08 08:46:06 -0600144 Args:
Alex Klein9e7b29e2023-04-11 16:10:31 -0600145 sysroot: the path (string) to the root directory into which the package
146 is pretend to be merged. This value is also used for setting
147 PORTAGE_CONFIGROOT.
148 package_list: the list of packages (CP string) to extract their
149 dependencies from.
Alex Klein9e7b29e2023-04-11 16:10:31 -0600150 include_bdepend: Controls whether BDEPEND packages that would be
151 installed to BROOT (usually "/" instead of ROOT) are included in the
152 output.
153 backtrack: Setting to False disables backtracking in Portage's
154 dependency solver. If the highest available version of dependencies
155 doesn't produce a solvable graph Portage will give up and return an
156 error instead of trying other candidates.
Ned Nguyendd3e09f2019-03-14 18:54:03 -0600157
Alex Klein1699fab2022-09-08 08:46:06 -0600158 Returns:
Mike Frysingera994fc12023-08-29 11:40:29 -0400159 A JSON-izable object.
Alex Klein1699fab2022-09-08 08:46:06 -0600160 """
161 lib_argv = ["--quiet", "--pretend", "--emptytree"]
162 if include_bdepend:
163 lib_argv += ["--include-bdepend"]
164 if not backtrack:
165 lib_argv += ["--backtrack=0"]
166 lib_argv += ["--sysroot=%s" % sysroot]
167 lib_argv.extend(package_list)
Jim Hebert91c052c2011-03-11 11:00:53 -0800168
Alex Klein1699fab2022-09-08 08:46:06 -0600169 deps = DepGraphGenerator()
170 deps.Initialize(lib_argv)
Chris McDonaldd8a7f112019-11-01 10:35:07 -0600171
Alex Klein1699fab2022-09-08 08:46:06 -0600172 deps_tree, _deps_info, bdeps_tree = deps.GenDependencyTree()
173 trees = (deps_tree, bdeps_tree)
Chris McDonaldd8a7f112019-11-01 10:35:07 -0600174
Mike Frysingera994fc12023-08-29 11:40:29 -0400175 flattened_trees = tuple(FlattenDepTree(x) for x in trees)
Alex Klein1699fab2022-09-08 08:46:06 -0600176
Alex Klein9e7b29e2023-04-11 16:10:31 -0600177 # Workaround: since emerge doesn't honor the --emptytree flag, for now we
178 # need to manually filter out packages that are obsolete (meant to be
Alex Klein1699fab2022-09-08 08:46:06 -0600179 # uninstalled by emerge)
180 # TODO(crbug.com/938605): remove this work around once
181 # https://bugs.gentoo.org/681308 is addressed.
182 for tree in flattened_trees:
183 FilterObsoleteDeps(tree)
184
Alex Klein1699fab2022-09-08 08:46:06 -0600185 return flattened_trees
Chris McDonalde69db662018-11-15 12:50:18 -0700186
187
188def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600189 opts = ParseArgs(argv)
Chris McDonalde69db662018-11-15 12:50:18 -0700190
Alex Klein1699fab2022-09-08 08:46:06 -0600191 sysroot = opts.sysroot or build_target_lib.get_default_sysroot_path(
192 opts.board
193 )
194 deps_list, _ = ExtractDeps(sysroot, opts.pkgs, opts.format)
Yu-Ju Hongf48d3982014-10-30 16:12:16 -0700195
Alex Klein1699fab2022-09-08 08:46:06 -0600196 pformat.json(
197 deps_list, fp=opts.output_path if opts.output_path else sys.stdout
198 )