From e5d73251660f231dd15a14993d4042d9dc082c78 Mon Sep 17 00:00:00 2001 From: Yarny0 <41838844+Yarny0@users.noreply.github.com> Date: Thu, 22 Feb 2024 20:29:44 +0100 Subject: [PATCH 1/2] pythonCatchConflictsHook: avoid infinite recursion Albeit counter-intutive, the `propagatedBuildInputs` mechanism and the corresponding package files in `nix-support/propagated-build-inputs` can form a dependency cycle. This can happen if a package adds itself to this file, or if multiple outputs of one derivation reference each other. An example for this is the `patchPpdFilesHook`: In its mission to collect dependency packages with binaries that might be required by the dependent package to be created, it sometimes picks up the dependent package itself. This indicates that if a file of the dependent package is used, the package itself should also be installed. In the case of a multiple output package, it is also possible that two outputs depend on each other, creating a dependency cycle. Since commit 2651ddc7b0788932df9da7416ccd1618d76c11fe, the `find_packages` function in `catch_conflicts.py` recursively collects all `propagated-build-inputs` files. If it encounters a dependency cycle, it must not follow the cycle to avoid infinite recursion (and a stack overflow). The commit at hand adds a check so that the function skips over a package that it already encountered and processed earlier. This does not loosen the script's checks as the script still recursively collects all propagated build inputs. --- .../interpreters/python/catch_conflicts/catch_conflicts.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py b/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py index d4219192790b..ad679d9f9f99 100644 --- a/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py +++ b/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py @@ -57,7 +57,8 @@ def find_packages(store_path: Path, site_packages_path: str, parents: List[str]) with open(propagated_build_inputs, "r") as f: build_inputs: List[str] = f.read().strip().split(" ") for build_input in build_inputs: - find_packages(Path(build_input), site_packages_path, parents + [build_input]) + if build_input not in parents: + find_packages(Path(build_input), site_packages_path, parents + [build_input]) find_packages(out_path, site_packages_path, [f"this derivation: {out_path}"]) From b1bccc25ec523b1d4f6508ccb750b91bc07bb473 Mon Sep 17 00:00:00 2001 From: Yarny0 <41838844+Yarny0@users.noreply.github.com> Date: Fri, 23 Feb 2024 15:02:43 +0100 Subject: [PATCH 2/2] pythonCatchConflictsHook: test cyclic dependency Add test case where a package enlists itself as propagated build input. --- .../python/hooks/python-catch-conflicts-hook-tests.nix | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkgs/development/interpreters/python/hooks/python-catch-conflicts-hook-tests.nix b/pkgs/development/interpreters/python/hooks/python-catch-conflicts-hook-tests.nix index f3d9235799e0..cba1034e0963 100644 --- a/pkgs/development/interpreters/python/hooks/python-catch-conflicts-hook-tests.nix +++ b/pkgs/development/interpreters/python/hooks/python-catch-conflicts-hook-tests.nix @@ -78,6 +78,15 @@ in { ]; }; + # multi-output derivation with dependency on itself must not crash + cyclic-dependencies = + generatePythonPackage { + pname = "cyclic-dependencies"; + preFixup = '' + propagatedBuildInputs+=("$out") + ''; + }; + # Simplest test case that should trigger a conflict catches-simple-conflict = let # this build must fail due to conflicts