# Package dependency cycles

**URL:** <https://racket.discourse.group/t/package-dependency-cycles/1353>\
**Category:** Questions & Answers\
**Tags:** question, package\
**Created:** [October 6, 2022, 1:09am UTC](https://racket.discourse.group/t/package-dependency-cycles/1353 "2022-10-06T01:09:53Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![LiberalArtist](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/liberalartist/32/151_2.png) [@LiberalArtist](https://racket.discourse.group/u/LiberalArtist)\
**Post date:** [October 6, 2022, 1:09am UTC](https://racket.discourse.group/t/package-dependency-cycles/1353/1 "2022-10-06T01:09:53Z")

</div>

While working on support for Racket packages in Guix, I learned that packages can have cyclic dependencies. Cycles seem rare, but, since one cycle involves `base` and `racket-lib`, they have to be accounted for from the beginning. So, I'd like to check my tentative understanding of how package dependency cycles work in practice.

First, according to the script at the bottom of this post, these are all of the cycles in the main package catalog:

```scheme
;; Ignoring build-deps:
'(("base"
   "com-win32-i386"
   "com-win32-x86_64"
   "db-ppc-macosx"
   "db-win32-arm64"
   "db-win32-i386"
   "db-win32-x86_64"
   "db-x86_64-linux-natipkg"
   "racket-aarch64-macosx-3"
   "racket-i386-macosx-3"
   "racket-lib"
   "racket-ppc-macosx-3"
   "racket-win32-arm64-3"
   "racket-win32-i386-3"
   "racket-win32-x86_64-3"
   "racket-x86_64-linux-natipkg-3"
   "racket-x86_64-macosx-3")
  ("collections-lib" "functional-lib")
  ("drracket" "quickscript")
  ("deinprogramm-signature" "htdp-lib")
  ("stxparse-info" "subtemplate"))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Considering build-deps:
'(("base"
   "com-win32-i386"
   "com-win32-x86_64"
   "db-ppc-macosx"
   "db-win32-arm64"
   "db-win32-i386"
   "db-win32-x86_64"
   "db-x86_64-linux-natipkg"
   "racket-aarch64-macosx-3"
   "racket-i386-macosx-3"
   "racket-lib"
   "racket-ppc-macosx-3"
   "racket-win32-arm64-3"
   "racket-win32-i386-3"
   "racket-win32-x86_64-3"
   "racket-x86_64-linux-natipkg-3"
   "racket-x86_64-macosx-3")
  ("collections-lib" "functional-lib")
  ("drracket" "drracket-tool-doc" "quickscript")
  ("collections-doc" "functional-doc")
  ("graphite-doc" "graphite-tutorial")
  ("deinprogramm-signature" "htdp-lib")
  ("lens-common" "lens-data")
  ("peg-gen" "typed-peg")
  ("compatibility-doc"
   "data-doc"
   "db-doc"
   "distributed-places-doc"
   "draw-doc"
   "errortrace-doc"
   "future-visualizer"
   "gui-doc"
   "macro-debugger"
   "math-doc"
   "mzscheme-doc"
   "net-cookies-doc"
   "net-doc"
   "pict-doc"
   "planet-doc"
   "plot-doc"
   "profile-doc"
   "r5rs-doc"
   "r6rs-doc"
   "racket-doc"
   "rackunit-doc"
   "readline-doc"
   "scheme-doc"
   "scribble-doc"
   "simple-tree-text-markup-doc"
   "slideshow-doc"
   "srfi-doc"
   "string-constants-doc"
   "syntax-color-doc"
   "typed-racket-doc"
   "web-server-doc"
   "xrepl-doc")
  ("net-test" "racket-test")
  ("stxparse-info" "subtemplate")
  ("graphics" "w3s"))

```

(I'm not sure if this list reflects [Reduce racket-doc dependencies. by samth · Pull Request #3215 · racket/racket · GitHub](https://github.com/racket/racket/pull/3215) .)

Some questions:

1. For packages involved in a cycle through `deps` (as opposed to `build-deps` only), it seems like they always need to be built at the same time, and they will also always need to be installed together. Is that correct?
2. Likewise, do I understand correctly that packages involved in a cycle through `build-deps` need to be built at the same time?
3. Even if installing "built packages" in the sense of [5&nbsp;Source, Binary, and Built Packages](https://docs.racket-lang.org/pkg/strip.html), do packages involved in cycles through `build-deps` still all have to be installed together? It seems like they do—that only converting to "binary" or "binary library" packages would allow `build-deps` to be dropped—but that had not been my first guess.

Here is the script I used to look for cycles:

```scheme
#lang racket

(require pkg/lib
         file/unzip
         pkg-dep-draw/private/get-pkgs
         pkg-dep-draw/private/cliques
         racket/runtime-path)

(define-runtime-path archive-state.sqlite
  "archive-state.sqlite")
(define-runtime-path archive/
  "archive/")
;; this is a little convoluted to preserve deps vs build-deps
(unless (file-exists? (build-path archive/ "LOCK"))
  (parameterize ([current-pkg-lookup-version "8.6.0.14"])
    (pkg-catalog-archive archive/
                         '("https://pkgs.racket-lang.org")
                         #:package-exn-handler void
                         #:state-catalog archive-state.sqlite)))
(parameterize ([current-directory (build-path archive/ "pkgs")])
  (for ([pth (in-directory)]
        #:when (equal? #".zip" (path-get-extension pth)))
    (define dir
      (path-replace-extension pth #""))
    (unless (directory-exists? dir)
      (unzip pth
             (make-filesystem-entry-reader #:dest dir)))
    (define compiled-dir
      (build-path dir "compiled"))
    (when (directory-exists? compiled-dir)
      (delete-directory/files compiled-dir))))

(define-values (pkgs invert-pkgs)
  (get-pkgs #:srcs (list (cons 'dir (build-path archive/ "pkgs")))
            #:roots '()
            #:quiet #false
            #:no-build? #false))

(define-values (reps no-build-reps depths max-depth at-depth)
  (get-cliques pkgs))

(define (group-cycles reps)
  (for/fold ([cycles #hash()])
            ([{pkg rep} (in-hash reps)])
    (hash-update cycles
                 rep
                 (λ (members)
                   (hash-set members pkg #t))
                 (λ ()
                   (hash rep #t)))))

(define (show-cycles label reps)
  (displayln label)
  (pretty-print
   (hash-map (group-cycles reps)
             (λ (rep members)
               (hash-keys members 'ordered))
             'ordered)))

(show-cycles ";; Ignoring build-deps:" no-build-reps)
(displayln (make-string 80 #\;))
(show-cycles ";; Considering build-deps:" reps)

```

---

<div class="post-metadata">

**Author:** ![mflatt](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/mflatt/32/6_2.png) [@mflatt](https://racket.discourse.group/u/mflatt)\
**Post date:** [October 6, 2022, 3:51am UTC](https://racket.discourse.group/t/package-dependency-cycles/1353/2 "2022-10-06T03:51:59Z")

</div>

> [@LiberalArtist](#):
>
> Some questions:

"Yes" to all.

I was a little surprised to see all the native-library packages in a cycle with `"base"`. It makes some sense, but I'm not sure I noticed when I created the cycle. Maybe it would work and be better to reduce the native-library package dependencies to `"racket"`. Or maybe it doesn't matter.
