Mike Frysinger | f1ba7ad | 2022-09-12 05:42:57 -0400 | [diff] [blame] | 1 | # Copyright 2010 The ChromiumOS Authors |
Jim Hebert | 91c052c | 2011-03-11 11:00:53 -0800 | [diff] [blame] | 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
Alex Klein | 9e7b29e | 2023-04-11 16:10:31 -0600 | [diff] [blame] | 5 | """Command to extract the dependency tree for a given package. |
Mike Frysinger | 012371e | 2019-01-03 03:42:32 -0500 | [diff] [blame] | 6 | |
| 7 | This produces JSON output for other tools to process. |
| 8 | """ |
Don Garrett | 25f309a | 2014-03-19 14:02:12 -0700 | [diff] [blame] | 9 | |
Chris McDonald | d2fa616 | 2019-07-30 15:30:58 -0600 | [diff] [blame] | 10 | from __future__ import absolute_import |
Mike Frysinger | 383367e | 2014-09-16 15:06:17 -0400 | [diff] [blame] | 11 | |
Mike Frysinger | a942aee | 2020-03-20 03:53:37 -0400 | [diff] [blame] | 12 | import sys |
Don Garrett | f8bf784 | 2014-03-20 17:03:42 -0700 | [diff] [blame] | 13 | |
Mike Frysinger | 06a51c8 | 2021-04-06 11:39:17 -0400 | [diff] [blame] | 14 | from chromite.lib import build_target_lib |
Jim Hebert | cf870d7 | 2013-06-12 15:33:34 -0700 | [diff] [blame] | 15 | from chromite.lib import commandline |
Chris McDonald | 59650c3 | 2021-07-20 15:29:28 -0600 | [diff] [blame] | 16 | from chromite.lib.depgraph import DepGraphGenerator |
Alex Klein | 18a60af | 2020-06-11 12:08:47 -0600 | [diff] [blame] | 17 | from chromite.lib.parser import package_info |
Alex Klein | 73eba21 | 2021-09-09 11:43:33 -0600 | [diff] [blame] | 18 | from chromite.utils import pformat |
Mike Frysinger | cc83883 | 2014-05-24 13:10:30 -0400 | [diff] [blame] | 19 | |
Chris McDonald | d8a7f11 | 2019-11-01 10:35:07 -0600 | [diff] [blame] | 20 | |
Mike Frysinger | a994fc1 | 2023-08-29 11:40:29 -0400 | [diff] [blame^] | 21 | def FlattenDepTree(deptree, pkgtable=None, parentcpv=None): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 22 | """Simplify dependency json. |
Don Garrett | 25f309a | 2014-03-19 14:02:12 -0700 | [diff] [blame] | 23 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 24 | Turn something like this (the parallel_emerge DepsTree format): |
| 25 | { |
| 26 | "app-admin/eselect-1.2.9": { |
Jim Hebert | 91c052c | 2011-03-11 11:00:53 -0800 | [diff] [blame] | 27 | "action": "merge", |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 28 | "deps": { |
| 29 | "sys-apps/coreutils-7.5-r1": { |
| 30 | "action": "merge", |
| 31 | "deps": {}, |
| 32 | "deptype": "runtime" |
| 33 | }, |
| 34 | ... |
| 35 | } |
| 36 | } |
Jim Hebert | 91c052c | 2011-03-11 11:00:53 -0800 | [diff] [blame] | 37 | } |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 38 | ...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 Hong | 7f01e9a | 2014-10-23 11:01:57 -0700 | [diff] [blame] | 59 | |
Alex Klein | 9e7b29e | 2023-04-11 16:10:31 -0600 | [diff] [blame] | 60 | Args: |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 61 | deptree: The dependency tree. |
| 62 | pkgtable: The package table to update. If None, create a new one. |
| 63 | parentcpv: The parent CPV. |
Yu-Ju Hong | 7f01e9a | 2014-10-23 11:01:57 -0700 | [diff] [blame] | 64 | |
Alex Klein | 9e7b29e | 2023-04-11 16:10:31 -0600 | [diff] [blame] | 65 | Returns: |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 66 | 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 Klein | 7bd88b1 | 2023-05-19 15:39:55 -0600 | [diff] [blame] | 72 | pkg_info = package_info.parse(cpv) |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 73 | pkgtable[cpv] = { |
| 74 | "deps": [], |
| 75 | "rev_deps": [], |
Alex Klein | 7bd88b1 | 2023-05-19 15:39:55 -0600 | [diff] [blame] | 76 | "name": pkg_info.package, |
| 77 | "category": pkg_info.category, |
| 78 | "version": pkg_info.vr, |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 79 | "full_name": cpv, |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 80 | "action": record["action"], |
| 81 | } |
Yu-Ju Hong | 7f01e9a | 2014-10-23 11:01:57 -0700 | [diff] [blame] | 82 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 83 | # 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 Frysinger | a994fc1 | 2023-08-29 11:40:29 -0400 | [diff] [blame^] | 90 | FlattenDepTree(record["deps"], pkgtable=pkgtable, parentcpv=cpv) |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 91 | # 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 Hebert | 91c052c | 2011-03-11 11:00:53 -0800 | [diff] [blame] | 95 | |
| 96 | |
Chris McDonald | e69db66 | 2018-11-15 12:50:18 -0700 | [diff] [blame] | 97 | def ParseArgs(argv): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 98 | """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 McDonald | e69db66 | 2018-11-15 12:50:18 -0700 | [diff] [blame] | 103 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 104 | parser.add_argument( |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 105 | "--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 Hebert | 91c052c | 2011-03-11 11:00:53 -0800 | [diff] [blame] | 111 | |
Chris McDonald | e69db66 | 2018-11-15 12:50:18 -0700 | [diff] [blame] | 112 | |
Ned Nguyen | e16dcfb | 2019-03-22 10:36:05 -0600 | [diff] [blame] | 113 | def FilterObsoleteDeps(package_deps): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 114 | """Remove all the packages that are to be uninstalled from |package_deps|. |
Ned Nguyen | e16dcfb | 2019-03-22 10:36:05 -0600 | [diff] [blame] | 115 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 116 | Returns: |
Alex Klein | 9e7b29e | 2023-04-11 16:10:31 -0600 | [diff] [blame] | 117 | None since this method mutates |package_deps| directly. |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 118 | """ |
| 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 Nguyen | e16dcfb | 2019-03-22 10:36:05 -0600 | [diff] [blame] | 129 | |
| 130 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 131 | def ExtractDeps( |
| 132 | sysroot, |
| 133 | package_list, |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 134 | include_bdepend=True, |
| 135 | backtrack=True, |
| 136 | ): |
| 137 | """Returns the set of dependencies for the packages in package_list. |
Ned Nguyen | dd3e09f | 2019-03-14 18:54:03 -0600 | [diff] [blame] | 138 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 139 | 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 Nguyen | dd3e09f | 2019-03-14 18:54:03 -0600 | [diff] [blame] | 143 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 144 | Args: |
Alex Klein | 9e7b29e | 2023-04-11 16:10:31 -0600 | [diff] [blame] | 145 | 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 Klein | 9e7b29e | 2023-04-11 16:10:31 -0600 | [diff] [blame] | 150 | 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 Nguyen | dd3e09f | 2019-03-14 18:54:03 -0600 | [diff] [blame] | 157 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 158 | Returns: |
Mike Frysinger | a994fc1 | 2023-08-29 11:40:29 -0400 | [diff] [blame^] | 159 | A JSON-izable object. |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 160 | """ |
| 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 Hebert | 91c052c | 2011-03-11 11:00:53 -0800 | [diff] [blame] | 168 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 169 | deps = DepGraphGenerator() |
| 170 | deps.Initialize(lib_argv) |
Chris McDonald | d8a7f11 | 2019-11-01 10:35:07 -0600 | [diff] [blame] | 171 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 172 | deps_tree, _deps_info, bdeps_tree = deps.GenDependencyTree() |
| 173 | trees = (deps_tree, bdeps_tree) |
Chris McDonald | d8a7f11 | 2019-11-01 10:35:07 -0600 | [diff] [blame] | 174 | |
Mike Frysinger | a994fc1 | 2023-08-29 11:40:29 -0400 | [diff] [blame^] | 175 | flattened_trees = tuple(FlattenDepTree(x) for x in trees) |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 176 | |
Alex Klein | 9e7b29e | 2023-04-11 16:10:31 -0600 | [diff] [blame] | 177 | # 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 Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 179 | # 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 Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 185 | return flattened_trees |
Chris McDonald | e69db66 | 2018-11-15 12:50:18 -0700 | [diff] [blame] | 186 | |
| 187 | |
| 188 | def main(argv): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 189 | opts = ParseArgs(argv) |
Chris McDonald | e69db66 | 2018-11-15 12:50:18 -0700 | [diff] [blame] | 190 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 191 | 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 Hong | f48d398 | 2014-10-30 16:12:16 -0700 | [diff] [blame] | 195 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 196 | pformat.json( |
| 197 | deps_list, fp=opts.output_path if opts.output_path else sys.stdout |
| 198 | ) |