mirror of
https://git.in.rschanz.org/ryan77627/guix.git
synced 2025-01-11 21:59:08 -05:00
style: Add 'arguments' styling rule.
* guix/scripts/style.scm (unquote->ungexp, gexpify-argument-value) (quote-argument-value, gexpify-argument-tail) (gexpify-package-arguments): New procedures. (%gexp-keywords): New variable. (%options): Add "arguments" case for 'styling-procedure. (show-stylings): Update. * tests/style.scm ("gexpify arguments, already gexpified") ("gexpify arguments, non-gexp arguments, margin comment") ("gexpify arguments, phases and flags") ("gexpify arguments, append arguments") ("gexpify arguments, substitute-keyword-arguments") ("gexpify arguments, append substitute-keyword-arguments"): New tests. * doc/guix.texi (package Reference): For 'arguments', add compatibility note and link to 'guix style'. (Invoking guix style): Document the 'arguments' styling rule.
This commit is contained in:
parent
c1007786fd
commit
ba5da5125a
3 changed files with 359 additions and 1 deletions
|
@ -7785,6 +7785,24 @@ The exact set of supported keywords depends on the build system
|
||||||
@code{#:phases}. The @code{#:phases} keyword in particular lets you
|
@code{#:phases}. The @code{#:phases} keyword in particular lets you
|
||||||
modify the set of build phases for your package (@pxref{Build Phases}).
|
modify the set of build phases for your package (@pxref{Build Phases}).
|
||||||
|
|
||||||
|
@quotation Compatibility Note
|
||||||
|
Until version 1.3.0, the @code{arguments} field would typically use
|
||||||
|
@code{quote} (@code{'}) or @code{quasiquote} (@code{`}) and no
|
||||||
|
G-expressions, like so:
|
||||||
|
|
||||||
|
@lisp
|
||||||
|
(package
|
||||||
|
;; several fields omitted
|
||||||
|
(arguments ;old-style quoted arguments
|
||||||
|
'(#:tests? #f
|
||||||
|
#:configure-flags '("--enable-frobbing"))))
|
||||||
|
@end lisp
|
||||||
|
|
||||||
|
To convert from that style to the one shown above, you can run
|
||||||
|
@code{guix style -S arguments @var{package}} (@pxref{Invoking guix
|
||||||
|
style}).
|
||||||
|
@end quotation
|
||||||
|
|
||||||
@item @code{inputs} (default: @code{'()})
|
@item @code{inputs} (default: @code{'()})
|
||||||
@itemx @code{native-inputs} (default: @code{'()})
|
@itemx @code{native-inputs} (default: @code{'()})
|
||||||
@itemx @code{propagated-inputs} (default: @code{'()})
|
@itemx @code{propagated-inputs} (default: @code{'()})
|
||||||
|
@ -14709,6 +14727,39 @@ Rewriting is done in a conservative way: preserving comments and bailing
|
||||||
out if it cannot make sense of the code that appears in an inputs field.
|
out if it cannot make sense of the code that appears in an inputs field.
|
||||||
The @option{--input-simplification} option described below provides
|
The @option{--input-simplification} option described below provides
|
||||||
fine-grain control over when inputs should be simplified.
|
fine-grain control over when inputs should be simplified.
|
||||||
|
|
||||||
|
@item arguments
|
||||||
|
Rewrite package arguments to use G-expressions (@pxref{G-Expressions}).
|
||||||
|
For example, consider this package definition:
|
||||||
|
|
||||||
|
@lisp
|
||||||
|
(define-public my-package
|
||||||
|
(package
|
||||||
|
;; @dots{}
|
||||||
|
(arguments ;old-style quoted arguments
|
||||||
|
'(#:make-flags '("V=1")
|
||||||
|
#:phases (modify-phases %standard-phases
|
||||||
|
(delete 'build))))))
|
||||||
|
@end lisp
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
Running @command{guix style -S arguments} on this package would rewrite
|
||||||
|
its @code{arguments} field like to:
|
||||||
|
|
||||||
|
@lisp
|
||||||
|
(define-public my-package
|
||||||
|
(package
|
||||||
|
;; @dots{}
|
||||||
|
(arguments
|
||||||
|
(list #:make-flags #~'("V=1")
|
||||||
|
#:phases #~(modify-phases %standard-phases
|
||||||
|
(delete 'build))))))
|
||||||
|
@end lisp
|
||||||
|
|
||||||
|
Note that changes made by the @code{arguments} rule do not entail a
|
||||||
|
rebuild of the affected packages. Furthermore, if a package definition
|
||||||
|
happens to be using G-expressions already, @command{guix style} leaves
|
||||||
|
it unchanged.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
@item --list-stylings
|
@item --list-stylings
|
||||||
|
|
|
@ -41,6 +41,7 @@ (define-module (guix scripts style)
|
||||||
#:use-module (ice-9 match)
|
#:use-module (ice-9 match)
|
||||||
#:use-module (srfi srfi-1)
|
#:use-module (srfi srfi-1)
|
||||||
#:use-module (srfi srfi-9)
|
#:use-module (srfi srfi-9)
|
||||||
|
#:use-module (srfi srfi-26)
|
||||||
#:use-module (srfi srfi-34)
|
#:use-module (srfi srfi-34)
|
||||||
#:use-module (srfi srfi-37)
|
#:use-module (srfi srfi-37)
|
||||||
#:export (guix-style))
|
#:export (guix-style))
|
||||||
|
@ -302,6 +303,174 @@ (define matches?
|
||||||
(list package-inputs package-native-inputs
|
(list package-inputs package-native-inputs
|
||||||
package-propagated-inputs)))
|
package-propagated-inputs)))
|
||||||
|
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Gexpifying package arguments.
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(define (unquote->ungexp value)
|
||||||
|
"Replace 'unquote' and 'unquote-splicing' in VALUE with their gexp
|
||||||
|
counterpart."
|
||||||
|
;; Replace 'unquote only on the first quasiquotation level.
|
||||||
|
(let loop ((value value)
|
||||||
|
(quotation 1))
|
||||||
|
(match value
|
||||||
|
(('unquote x)
|
||||||
|
(if (= quotation 1)
|
||||||
|
`(ungexp ,x)
|
||||||
|
value))
|
||||||
|
(('unquote-splicing x)
|
||||||
|
(if (= quotation 1)
|
||||||
|
`(ungexp-splicing x)
|
||||||
|
value))
|
||||||
|
(('quasiquote x)
|
||||||
|
(list 'quasiquote (loop x (+ quotation 1))))
|
||||||
|
(('quote x)
|
||||||
|
(list 'quote (loop x (+ quotation 1))))
|
||||||
|
((lst ...)
|
||||||
|
(map (cut loop <> quotation) lst))
|
||||||
|
(x x))))
|
||||||
|
|
||||||
|
(define (gexpify-argument-value value quotation)
|
||||||
|
"Turn VALUE, an sexp, into its gexp equivalent. QUOTATION is a symbol that
|
||||||
|
indicates in what quotation context VALUE is to be interpreted: 'quasiquote,
|
||||||
|
'quote, or 'none."
|
||||||
|
(match quotation
|
||||||
|
('none
|
||||||
|
(match value
|
||||||
|
(('quasiquote value)
|
||||||
|
(gexpify-argument-value value 'quasiquote))
|
||||||
|
(('quote value)
|
||||||
|
(gexpify-argument-value value 'quote))
|
||||||
|
(value value)))
|
||||||
|
('quote
|
||||||
|
`(gexp ,value))
|
||||||
|
('quasiquote
|
||||||
|
`(gexp ,(unquote->ungexp value)))))
|
||||||
|
|
||||||
|
(define (quote-argument-value value quotation)
|
||||||
|
"Quote VALUE, an sexp. QUOTATION is a symbol that indicates in what
|
||||||
|
quotation context VALUE is to be interpreted: 'quasiquote, 'quote, or 'none."
|
||||||
|
(define (self-quoting? x)
|
||||||
|
(or (boolean? x) (number? x) (string? x) (char? x)
|
||||||
|
(keyword? x)))
|
||||||
|
|
||||||
|
(match quotation
|
||||||
|
('none
|
||||||
|
(match value
|
||||||
|
(('quasiquote value)
|
||||||
|
(quote-argument-value value 'quasiquote))
|
||||||
|
(('quote value)
|
||||||
|
(quote-argument-value value 'quote))
|
||||||
|
(value value)))
|
||||||
|
('quote
|
||||||
|
(if (self-quoting? value)
|
||||||
|
value
|
||||||
|
(list 'quote value)))
|
||||||
|
('quasiquote
|
||||||
|
(match value
|
||||||
|
(('unquote x) x)
|
||||||
|
((? self-quoting? x) x)
|
||||||
|
(_ (list 'quasiquote value))))))
|
||||||
|
|
||||||
|
(define %gexp-keywords
|
||||||
|
;; Package argument keywords that must be followed by a gexp.
|
||||||
|
'(#:phases #:configure-flags #:make-flags #:strip-flags))
|
||||||
|
|
||||||
|
(define (gexpify-argument-tail sexp)
|
||||||
|
"Gexpify SEXP, an unquoted argument tail."
|
||||||
|
(match sexp
|
||||||
|
(('substitute-keyword-arguments lst clauses ...)
|
||||||
|
`(substitute-keyword-arguments ,lst
|
||||||
|
,@(map (match-lambda
|
||||||
|
((((? keyword? keyword) identifier) body)
|
||||||
|
`((,keyword ,identifier)
|
||||||
|
,(if (memq keyword %gexp-keywords)
|
||||||
|
(gexpify-argument-value body 'none)
|
||||||
|
(quote-argument-value body 'none))))
|
||||||
|
((((? keyword? keyword) identifier default) body)
|
||||||
|
`((,keyword ,identifier
|
||||||
|
,(if (memq keyword %gexp-keywords)
|
||||||
|
(gexpify-argument-value default 'none)
|
||||||
|
(quote-argument-value default 'none)))
|
||||||
|
,(if (memq keyword %gexp-keywords)
|
||||||
|
(gexpify-argument-value body 'none)
|
||||||
|
(quote-argument-value body 'none))))
|
||||||
|
(clause clause))
|
||||||
|
clauses)))
|
||||||
|
(_ sexp)))
|
||||||
|
|
||||||
|
(define* (gexpify-package-arguments package
|
||||||
|
#:key
|
||||||
|
(policy 'none)
|
||||||
|
(edit-expression edit-expression))
|
||||||
|
"Rewrite the 'arguments' field of PACKAGE to use gexps where applicable."
|
||||||
|
(define (gexpify location str)
|
||||||
|
(match (call-with-input-string str read-with-comments)
|
||||||
|
((rest ...)
|
||||||
|
(let ((blanks (take-while blank? rest))
|
||||||
|
(value (drop-while blank? rest)))
|
||||||
|
(define-values (quotation arguments tail)
|
||||||
|
(match value
|
||||||
|
(('quote (arguments ...)) (values 'quote arguments '()))
|
||||||
|
(('quasiquote (arguments ... ('unquote-splicing tail)))
|
||||||
|
(values 'quasiquote arguments tail))
|
||||||
|
(('quasiquote (arguments ...)) (values 'quasiquote arguments '()))
|
||||||
|
(('list arguments ...) (values 'none arguments '()))
|
||||||
|
(arguments (values 'none '() arguments))))
|
||||||
|
|
||||||
|
(define (append-tail sexp)
|
||||||
|
(if (null? tail)
|
||||||
|
sexp
|
||||||
|
(let ((tail (gexpify-argument-tail tail)))
|
||||||
|
(if (null? arguments)
|
||||||
|
tail
|
||||||
|
`(append ,sexp ,tail)))))
|
||||||
|
|
||||||
|
(let/ec return
|
||||||
|
(object->string*
|
||||||
|
(append-tail
|
||||||
|
`(list ,@(let loop ((arguments arguments)
|
||||||
|
(result '()))
|
||||||
|
(match arguments
|
||||||
|
(() (reverse result))
|
||||||
|
(((? keyword? keyword) value rest ...)
|
||||||
|
(when (eq? quotation 'none)
|
||||||
|
(match value
|
||||||
|
(('gexp _) ;already gexpified
|
||||||
|
(return str))
|
||||||
|
(_ #f)))
|
||||||
|
|
||||||
|
(loop rest
|
||||||
|
(cons* (if (memq keyword %gexp-keywords)
|
||||||
|
(gexpify-argument-value value
|
||||||
|
quotation)
|
||||||
|
(quote-argument-value value quotation))
|
||||||
|
keyword result)))
|
||||||
|
(((? blank? blank) rest ...)
|
||||||
|
(loop rest (cons blank result)))
|
||||||
|
(_
|
||||||
|
;; Something like: ,@(package-arguments xyz).
|
||||||
|
(warning location
|
||||||
|
(G_ "unsupported argument style; \
|
||||||
|
bailing out~%"))
|
||||||
|
(return str))))))
|
||||||
|
(location-column location)))))
|
||||||
|
(_
|
||||||
|
(warning location
|
||||||
|
(G_ "unsupported argument field; bailing out~%"))
|
||||||
|
str)))
|
||||||
|
|
||||||
|
(unless (null? (package-arguments package))
|
||||||
|
(match (package-field-location package 'arguments)
|
||||||
|
(#f
|
||||||
|
#f)
|
||||||
|
(location
|
||||||
|
(edit-expression
|
||||||
|
(location->source-properties (absolute-location location))
|
||||||
|
(lambda (str)
|
||||||
|
(gexpify location str)))))))
|
||||||
|
|
||||||
|
|
||||||
;;;
|
;;;
|
||||||
;;; Formatting package definitions.
|
;;; Formatting package definitions.
|
||||||
|
@ -379,6 +548,7 @@ (define %options
|
||||||
(alist-cons 'styling-procedure
|
(alist-cons 'styling-procedure
|
||||||
(match arg
|
(match arg
|
||||||
("inputs" simplify-package-inputs)
|
("inputs" simplify-package-inputs)
|
||||||
|
("arguments" gexpify-package-arguments)
|
||||||
("format" format-package-definition)
|
("format" format-package-definition)
|
||||||
(_ (leave (G_ "~a: unknown styling~%")
|
(_ (leave (G_ "~a: unknown styling~%")
|
||||||
arg)))
|
arg)))
|
||||||
|
@ -407,7 +577,8 @@ (define %options
|
||||||
(define (show-stylings)
|
(define (show-stylings)
|
||||||
(display (G_ "Available styling rules:\n"))
|
(display (G_ "Available styling rules:\n"))
|
||||||
(display (G_ "- format: Format the given package definition(s)\n"))
|
(display (G_ "- format: Format the given package definition(s)\n"))
|
||||||
(display (G_ "- inputs: Rewrite package inputs to the “new style”\n")))
|
(display (G_ "- inputs: Rewrite package inputs to the “new style”\n"))
|
||||||
|
(display (G_ "- arguments: Rewrite package arguments to G-expressions\n")))
|
||||||
|
|
||||||
(define (show-help)
|
(define (show-help)
|
||||||
(display (G_ "Usage: guix style [OPTION]... [PACKAGE]...
|
(display (G_ "Usage: guix style [OPTION]... [PACKAGE]...
|
||||||
|
|
136
tests/style.scm
136
tests/style.scm
|
@ -386,6 +386,142 @@ (define file
|
||||||
(list (package-inputs (@ (my-packages) my-coreutils))
|
(list (package-inputs (@ (my-packages) my-coreutils))
|
||||||
(read-package-field (@ (my-packages) my-coreutils) 'inputs 4)))))
|
(read-package-field (@ (my-packages) my-coreutils) 'inputs 4)))))
|
||||||
|
|
||||||
|
(test-assert "gexpify arguments, already gexpified"
|
||||||
|
(call-with-test-package '((arguments
|
||||||
|
(list #:configure-flags #~'("--help"))))
|
||||||
|
(lambda (directory)
|
||||||
|
(define file
|
||||||
|
(string-append directory "/my-packages.scm"))
|
||||||
|
(define (fingerprint file)
|
||||||
|
(let ((stat (stat file)))
|
||||||
|
(list (stat:mtime stat) (stat:size stat))))
|
||||||
|
(define before
|
||||||
|
(fingerprint file))
|
||||||
|
|
||||||
|
(system* "guix" "style" "-L" directory "my-coreutils"
|
||||||
|
"-S" "arguments")
|
||||||
|
|
||||||
|
(equal? (fingerprint file) before))))
|
||||||
|
|
||||||
|
(test-equal "gexpify arguments, non-gexp arguments, margin comment"
|
||||||
|
(list (list #:tests? #f #:test-target "check")
|
||||||
|
"\
|
||||||
|
(arguments (list #:tests? #f ;no tests
|
||||||
|
#:test-target \"check\"))\n")
|
||||||
|
(call-with-test-package '((arguments
|
||||||
|
'(#:tests? #f
|
||||||
|
#:test-target "check")))
|
||||||
|
(lambda (directory)
|
||||||
|
(define file
|
||||||
|
(string-append directory "/my-packages.scm"))
|
||||||
|
|
||||||
|
(substitute* file
|
||||||
|
(("#:tests\\? #f" all)
|
||||||
|
(string-append all " ;no tests\n")))
|
||||||
|
|
||||||
|
(system* "guix" "style" "-L" directory "my-coreutils"
|
||||||
|
"-S" "arguments")
|
||||||
|
|
||||||
|
(load file)
|
||||||
|
(list (package-arguments (@ (my-packages) my-coreutils))
|
||||||
|
(read-package-field (@ (my-packages) my-coreutils) 'arguments 2)))))
|
||||||
|
|
||||||
|
(test-equal "gexpify arguments, phases and flags"
|
||||||
|
"\
|
||||||
|
(list #:tests? #f
|
||||||
|
#:configure-flags #~'(\"--fast\")
|
||||||
|
#:make-flags #~(list (string-append \"CC=\"
|
||||||
|
#$(cc-for-target)))
|
||||||
|
#:phases #~(modify-phases %standard-phases
|
||||||
|
;; Line comment.
|
||||||
|
whatever)))\n"
|
||||||
|
(call-with-test-package '((arguments
|
||||||
|
`(#:tests? #f
|
||||||
|
#:configure-flags '("--fast")
|
||||||
|
#:make-flags
|
||||||
|
(list (string-append "CC=" ,(cc-for-target)))
|
||||||
|
#:phases (modify-phases %standard-phases
|
||||||
|
whatever))))
|
||||||
|
(lambda (directory)
|
||||||
|
(define file
|
||||||
|
(string-append directory "/my-packages.scm"))
|
||||||
|
|
||||||
|
(substitute* file
|
||||||
|
(("whatever")
|
||||||
|
"\n;; Line comment.
|
||||||
|
whatever"))
|
||||||
|
(system* "guix" "style" "-L" directory "my-coreutils"
|
||||||
|
"-S" "arguments")
|
||||||
|
|
||||||
|
(load file)
|
||||||
|
(read-package-field (@ (my-packages) my-coreutils) 'arguments 7))))
|
||||||
|
|
||||||
|
(test-equal "gexpify arguments, append arguments"
|
||||||
|
"\
|
||||||
|
(append (list #:tests? #f
|
||||||
|
#:configure-flags #~'(\"--fast\"))
|
||||||
|
(package-arguments coreutils)))\n"
|
||||||
|
(call-with-test-package '((arguments
|
||||||
|
`(#:tests? #f
|
||||||
|
#:configure-flags '("--fast")
|
||||||
|
,@(package-arguments coreutils))))
|
||||||
|
(lambda (directory)
|
||||||
|
(define file
|
||||||
|
(string-append directory "/my-packages.scm"))
|
||||||
|
|
||||||
|
(system* "guix" "style" "-L" directory "my-coreutils"
|
||||||
|
"-S" "arguments")
|
||||||
|
|
||||||
|
(load file)
|
||||||
|
(read-package-field (@ (my-packages) my-coreutils) 'arguments 3))))
|
||||||
|
|
||||||
|
(test-equal "gexpify arguments, substitute-keyword-arguments"
|
||||||
|
"\
|
||||||
|
(substitute-keyword-arguments (package-arguments coreutils)
|
||||||
|
((#:tests? _ #f)
|
||||||
|
#t)
|
||||||
|
((#:make-flags flags
|
||||||
|
#~'())
|
||||||
|
#~(cons \"-DXYZ=yes\"
|
||||||
|
#$flags))))\n"
|
||||||
|
(call-with-test-package '((arguments
|
||||||
|
(substitute-keyword-arguments
|
||||||
|
(package-arguments coreutils)
|
||||||
|
((#:tests? _ #f) #t)
|
||||||
|
((#:make-flags flags ''())
|
||||||
|
`(cons "-DXYZ=yes" ,flags)))))
|
||||||
|
(lambda (directory)
|
||||||
|
(define file
|
||||||
|
(string-append directory "/my-packages.scm"))
|
||||||
|
|
||||||
|
(system* "guix" "style" "-L" directory "my-coreutils"
|
||||||
|
"-S" "arguments")
|
||||||
|
|
||||||
|
(load file)
|
||||||
|
(read-package-field (@ (my-packages) my-coreutils) 'arguments 7))))
|
||||||
|
|
||||||
|
(test-equal "gexpify arguments, append substitute-keyword-arguments"
|
||||||
|
"\
|
||||||
|
(append (list #:tests? #f)
|
||||||
|
(substitute-keyword-arguments (package-arguments coreutils)
|
||||||
|
((#:make-flags flags)
|
||||||
|
#~(append `(\"-n\" ,%output)
|
||||||
|
#$flags)))))\n"
|
||||||
|
(call-with-test-package '((arguments
|
||||||
|
`(#:tests? #f
|
||||||
|
,@(substitute-keyword-arguments
|
||||||
|
(package-arguments coreutils)
|
||||||
|
((#:make-flags flags)
|
||||||
|
`(append `("-n" ,%output) ,flags))))))
|
||||||
|
(lambda (directory)
|
||||||
|
(define file
|
||||||
|
(string-append directory "/my-packages.scm"))
|
||||||
|
|
||||||
|
(system* "guix" "style" "-L" directory "my-coreutils"
|
||||||
|
"-S" "arguments")
|
||||||
|
|
||||||
|
(load file)
|
||||||
|
(read-package-field (@ (my-packages) my-coreutils) 'arguments 5))))
|
||||||
|
|
||||||
(test-end)
|
(test-end)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue