Subject: [bug#28251] [PATCH 1/3] packages: Add package->code.

From: [email-masked] (Ludovic Courtès)
Date: Fri, 01 Sep 2017 17:55:27 +0200

Ricardo Wurmus <[email-masked]> skribis:

> * guix/packages.scm (package->code): New procedure.

We’ll need tests for this.  :-)

I would move it to (guix import utils) or a new (guix import print)
module or similar (which will also allow us to use ‘factorize-uri’).  In
my mind, eventually all importers will produce a <package> object
directly, and so this will be a core part of the import machinery.
Since it’s not a crucial component, I would prefer to have it out of
(guix packages) though.

> +;; FIXME: the quasiquoted arguments field may contain embedded package
> +;; objects, e.g. in #:disallowed-references; they will just be printed with
> +;; their usual #<package ...> representation, not as variable names.

Not sure how to solve that; maybe we can ignore for now.

> +(define (package->code package)
> +  "Return an S-expression representing the source code that produces PACKAGE
> +when evaluated."

Like you wrote, it would be nice to also return a spec of modules in
scope, like:

  ((gnu packages r)
   ((guix licenses) #:prefix license:))

WDYT?

That way, we can eventually change ‘guix import’ to systematically print
both the ‘define-module’ clause and the package definition.

> +  ;; The module in which the package PKG is defined
> +  (define (package-module-name pkg)
> +    (map string->symbol
> +         (string-split (string-drop-right
> +                        (location-file (package-location pkg)) 4)
> +                       #\/)))
> +
> +  ;; Return the first candidate variable name that is bound to VAL.
> +  ;; TODO: avoid '%pkg-config
> +  (define (variable-name val mod)
> +    (let ((candidates (filter identity
> +                              (module-map
> +                               (lambda (sym var)
> +                                 (if (equal? val (variable-ref var)) sym #f))
> +                               (resolve-interface mod)))))
> +      (if (null? candidates) #f (car candidates))))
> +

I think we should compare values with ‘eq?’ (usually we’re concerned
with pointer identity of records), and also use ‘module-for-each’ +
‘let/ec’ to avoid building a list for nothing, like:

--8<---------------cut here---------------start------------->8---
scheme@(guile-user)> (match (let/ec return
			      (module-for-each (lambda (sym var)
						 (if (eq? + (variable-ref var))
						     (return sym)
						     #f))
					       the-scm-module))
		       ((? symbol? sym) sym)
		       (_ #f))
$17 = +
--8<---------------cut here---------------end--------------->8---

> +  ;; Print either license variable name or the code for a license object
> +  (define (print-license lic)

Nitpick: I’d rename all the ‘print-*’ procedures to ‘*->code’.

> +  (match package
> +    (($ <package> name version source build-system

If we move this to (guix import …), we can no longer match on the
record, but that’s not necessarily a bad thing anyway.  :-)

> +        ,@(let ((args (arguments)))
> +            (if (null? args) '()
> +                `((arguments ,(list 'quasiquote (arguments))))))
> +        ,@(if (equal? outputs '("out")) '()
> +              `((outputs (list ,@outputs))))
> +        ,@(let ((pkgs (native-inputs)))
> +            (if (null? pkgs) '()
> +                `((native-inputs ,(print-package-lists pkgs)))))
> +        ,@(let ((pkgs (inputs)))
> +            (if (null? pkgs) '()
> +                `((inputs ,(print-package-lists pkgs)))))
> +        ,@(let ((pkgs (propagated-inputs)))
> +            (if (null? pkgs) '()
> +                `((propagated-inputs ,(print-package-lists pkgs)))))

‘match’!  :-)

This looks pretty cool already!

Thanks,
Ludo’.