+ (match (find-packages-by-name name)
+ (()
+ '())
+ ((matches ...)
+ ;; Return the subset of MATCHES with the higher version number.
+ (let ((highest (package-version (first matches))))
+ (take-while (lambda (p)
+ (string=? (package-version p) highest))
+ matches))))))
+
+;; Prevent Guile 3 from inlining this procedure so we can mock it in tests.
+(set! find-best-packages-by-name find-best-packages-by-name)
+
+(define (generate-package-cache directory)
+ "Generate under DIRECTORY a cache of all the available packages.
+
+The primary purpose of the cache is to speed up package lookup by name such
+that we don't have to traverse and load all the package modules, thereby also
+reducing the memory footprint."
+ (define cache-file
+ (string-append directory %package-cache-file))
+
+ (define expand-cache
+ (match-lambda*
+ (((module symbol variable) (result . seen))
+ (let ((package (variable-ref variable)))
+ (if (or (vhash-assq package seen)
+ (hidden-package? package))
+ (cons result seen)
+ (cons (cons `#(,(package-name package)
+ ,(package-version package)
+ ,(module-name module)
+ ,symbol
+ ,(package-outputs package)
+ ,(->bool (supported-package? package))
+ ,(->bool (package-superseded package))
+ ,@(let ((loc (package-location package)))
+ (if loc
+ `(,(location-file loc)
+ ,(location-line loc)
+ ,(location-column loc))
+ '(#f #f #f))))
+ result)
+ (vhash-consq package #t seen)))))))
+
+ (define entry-key
+ (match-lambda
+ ((module symbol variable)
+ (let ((value (variable-ref variable)))
+ (string-append (package-name value) (package-version value)
+ (object->string module)
+ (symbol->string symbol))))))
+
+ (define (entry<? a b)
+ (string<? (entry-key a) (entry-key b)))
+
+ (define variables
+ ;; First sort variables so that 'expand-cache' later dismisses
+ ;; already-seen package objects in a deterministic fashion.
+ (sort
+ (fold-module-public-variables* (lambda (module symbol variable lst)
+ (let ((value (false-if-exception
+ (variable-ref variable))))
+ (if (package? value)
+ (cons (list module symbol variable)
+ lst)
+ lst)))
+ '()
+ (all-modules (%package-module-path)
+ #:warn
+ warn-about-load-error))
+ entry<?))
+
+ (define exp
+ (first (fold expand-cache (cons '() vlist-null) variables)))
+
+ (mkdir-p (dirname cache-file))
+ (call-with-output-file cache-file
+ (lambda (port)
+ ;; Store the cache as a '.go' file. This makes loading fast and reduces
+ ;; heap usage since some of the static data is directly mmapped.
+ (put-bytevector port
+ (compile `'(,@exp)
+ #:to 'bytecode
+ #:opts '(#:to-file? #t)))))
+ cache-file)