style.scm 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2021-2022 Ludovic Courtès <ludo@gnu.org>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. ;;; Commentary:
  19. ;;;
  20. ;;; This script updates package definitions so they use the "simplified" style
  21. ;;; for input lists, as in:
  22. ;;;
  23. ;;; (package
  24. ;;; ;; ...
  25. ;;; (inputs (list foo bar baz)))
  26. ;;;
  27. ;;; Code:
  28. (define-module (guix scripts style)
  29. #:autoload (gnu packages) (specification->package fold-packages)
  30. #:use-module (guix scripts)
  31. #:use-module ((guix scripts build) #:select (%standard-build-options))
  32. #:use-module (guix combinators)
  33. #:use-module (guix ui)
  34. #:use-module (guix packages)
  35. #:use-module (guix utils)
  36. #:use-module (guix i18n)
  37. #:use-module (guix diagnostics)
  38. #:use-module (ice-9 control)
  39. #:use-module (ice-9 match)
  40. #:use-module (ice-9 rdelim)
  41. #:use-module (ice-9 vlist)
  42. #:use-module (srfi srfi-1)
  43. #:use-module (srfi srfi-9)
  44. #:use-module (srfi srfi-26)
  45. #:use-module (srfi srfi-37)
  46. #:export (pretty-print-with-comments
  47. read-with-comments
  48. canonicalize-comment
  49. guix-style))
  50. ;;;
  51. ;;; Comment-preserving reader.
  52. ;;;
  53. ;; A comment.
  54. (define-record-type <comment>
  55. (comment str margin?)
  56. comment?
  57. (str comment->string)
  58. (margin? comment-margin?))
  59. (define (read-with-comments port)
  60. "Like 'read', but include <comment> objects when they're encountered."
  61. ;; Note: Instead of implementing this functionality in 'read' proper, which
  62. ;; is the best approach long-term, this code is a later on top of 'read',
  63. ;; such that we don't have to rely on a specific Guile version.
  64. (let loop ((blank-line? #t)
  65. (return (const 'unbalanced)))
  66. (match (read-char port)
  67. ((? eof-object? eof)
  68. eof) ;oops!
  69. (chr
  70. (cond ((eqv? chr #\newline)
  71. (loop #t return))
  72. ((char-set-contains? char-set:whitespace chr)
  73. (loop blank-line? return))
  74. ((memv chr '(#\( #\[))
  75. (let/ec return
  76. (let liip ((lst '()))
  77. (liip (cons (loop (match lst
  78. (((? comment?) . _) #t)
  79. (_ #f))
  80. (lambda ()
  81. (return (reverse lst))))
  82. lst)))))
  83. ((memv chr '(#\) #\]))
  84. (return))
  85. ((eq? chr #\')
  86. (list 'quote (loop #f return)))
  87. ((eq? chr #\`)
  88. (list 'quasiquote (loop #f return)))
  89. ((eq? chr #\,)
  90. (list (match (peek-char port)
  91. (#\@
  92. (read-char port)
  93. 'unquote-splicing)
  94. (_
  95. 'unquote))
  96. (loop #f return)))
  97. ((eqv? chr #\;)
  98. (unread-char chr port)
  99. (comment (read-line port 'concat)
  100. (not blank-line?)))
  101. (else
  102. (unread-char chr port)
  103. (read port)))))))
  104. ;;;
  105. ;;; Comment-preserving pretty-printer.
  106. ;;;
  107. (define-syntax vhashq
  108. (syntax-rules (quote)
  109. ((_) vlist-null)
  110. ((_ (key (quote (lst ...))) rest ...)
  111. (vhash-consq key '(lst ...) (vhashq rest ...)))
  112. ((_ (key value) rest ...)
  113. (vhash-consq key '((() . value)) (vhashq rest ...)))))
  114. (define %special-forms
  115. ;; Forms that are indented specially. The number is meant to be understood
  116. ;; like Emacs' 'scheme-indent-function' symbol property. When given an
  117. ;; alist instead of a number, the alist gives "context" in which the symbol
  118. ;; is a special form; for instance, context (modify-phases) means that the
  119. ;; symbol must appear within a (modify-phases ...) expression.
  120. (vhashq
  121. ('begin 1)
  122. ('lambda 2)
  123. ('lambda* 2)
  124. ('match-lambda 1)
  125. ('match-lambda* 2)
  126. ('define 2)
  127. ('define* 2)
  128. ('define-public 2)
  129. ('define*-public 2)
  130. ('define-syntax 2)
  131. ('define-syntax-rule 2)
  132. ('define-module 2)
  133. ('define-gexp-compiler 2)
  134. ('let 2)
  135. ('let* 2)
  136. ('letrec 2)
  137. ('letrec* 2)
  138. ('match 2)
  139. ('when 2)
  140. ('unless 2)
  141. ('package 1)
  142. ('origin 1)
  143. ('operating-system 1)
  144. ('modify-inputs 2)
  145. ('modify-phases 2)
  146. ('add-after '(((modify-phases) . 3)))
  147. ('add-before '(((modify-phases) . 3)))
  148. ('replace '(((modify-phases) . 2))) ;different from 'modify-inputs'
  149. ('substitute* 2)
  150. ('substitute-keyword-arguments 2)
  151. ('call-with-input-file 2)
  152. ('call-with-output-file 2)
  153. ('with-output-to-file 2)
  154. ('with-input-from-file 2)))
  155. (define %newline-forms
  156. ;; List heads that must be followed by a newline. The second argument is
  157. ;; the context in which they must appear. This is similar to a special form
  158. ;; of 1, except that indent is 1 instead of 2 columns.
  159. (vhashq
  160. ('arguments '(package))
  161. ('sha256 '(origin source package))
  162. ('base32 '(sha256 origin))
  163. ('git-reference '(uri origin source))
  164. ('search-paths '(package))
  165. ('native-search-paths '(package))
  166. ('search-path-specification '())))
  167. (define (prefix? candidate lst)
  168. "Return true if CANDIDATE is a prefix of LST."
  169. (let loop ((candidate candidate)
  170. (lst lst))
  171. (match candidate
  172. (() #t)
  173. ((head1 . rest1)
  174. (match lst
  175. (() #f)
  176. ((head2 . rest2)
  177. (and (equal? head1 head2)
  178. (loop rest1 rest2))))))))
  179. (define (special-form-lead symbol context)
  180. "If SYMBOL is a special form in the given CONTEXT, return its number of
  181. arguments; otherwise return #f. CONTEXT is a stack of symbols lexically
  182. surrounding SYMBOL."
  183. (match (vhash-assq symbol %special-forms)
  184. (#f #f)
  185. ((_ . alist)
  186. (any (match-lambda
  187. ((prefix . level)
  188. (and (prefix? prefix context) (- level 1))))
  189. alist))))
  190. (define (newline-form? symbol context)
  191. "Return true if parenthesized expressions starting with SYMBOL must be
  192. followed by a newline."
  193. (match (vhash-assq symbol %newline-forms)
  194. (#f #f)
  195. ((_ . prefix)
  196. (prefix? prefix context))))
  197. (define (escaped-string str)
  198. "Return STR with backslashes and double quotes escaped. Everything else, in
  199. particular newlines, is left as is."
  200. (list->string
  201. `(#\"
  202. ,@(string-fold-right (lambda (chr lst)
  203. (match chr
  204. (#\" (cons* #\\ #\" lst))
  205. (#\\ (cons* #\\ #\\ lst))
  206. (_ (cons chr lst))))
  207. '()
  208. str)
  209. #\")))
  210. (define (string-width str)
  211. "Return the \"width\" of STR--i.e., the width of the longest line of STR."
  212. (apply max (map string-length (string-split str #\newline))))
  213. (define (canonicalize-comment c)
  214. "Canonicalize comment C, ensuring it has the \"right\" number of leading
  215. semicolons."
  216. (let ((line (string-trim-both
  217. (string-trim (comment->string c) (char-set #\;)))))
  218. (comment (string-append
  219. (if (comment-margin? c)
  220. ";"
  221. (if (string-null? line)
  222. ";;" ;no trailing space
  223. ";; "))
  224. line "\n")
  225. (comment-margin? c))))
  226. (define* (pretty-print-with-comments port obj
  227. #:key
  228. (format-comment identity)
  229. (indent 0)
  230. (max-width 78)
  231. (long-list 5))
  232. "Pretty-print OBJ to PORT, attempting to at most MAX-WIDTH character columns
  233. and assuming the current column is INDENT. Comments present in OBJ are
  234. included in the output.
  235. Lists longer than LONG-LIST are written as one element per line. Comments are
  236. passed through FORMAT-COMMENT before being emitted; a useful value for
  237. FORMAT-COMMENT is 'canonicalize-comment'."
  238. (let loop ((indent indent)
  239. (column indent)
  240. (delimited? #t) ;true if comes after a delimiter
  241. (context '()) ;list of "parent" symbols
  242. (obj obj))
  243. (define (print-sequence context indent column lst delimited?)
  244. (define long?
  245. (> (length lst) long-list))
  246. (let print ((lst lst)
  247. (first? #t)
  248. (delimited? delimited?)
  249. (column column))
  250. (match lst
  251. (()
  252. column)
  253. ((item . tail)
  254. (define newline?
  255. ;; Insert a newline if ITEM is itself a list, or if TAIL is long,
  256. ;; but only if ITEM is not the first item. Also insert a newline
  257. ;; before a keyword.
  258. (and (or (pair? item) long?
  259. (and (keyword? item)
  260. (not (eq? item #:allow-other-keys))))
  261. (not first?) (not delimited?)
  262. (not (comment? item))))
  263. (when newline?
  264. (newline port)
  265. (display (make-string indent #\space) port))
  266. (let ((column (if newline? indent column)))
  267. (print tail #f
  268. (comment? item)
  269. (loop indent column
  270. (or newline? delimited?)
  271. context
  272. item)))))))
  273. (define (sequence-would-protrude? indent lst)
  274. ;; Return true if elements of LST written at INDENT would protrude
  275. ;; beyond MAX-WIDTH. This is implemented as a cheap test with false
  276. ;; negatives to avoid actually rendering all of LST.
  277. (find (match-lambda
  278. ((? string? str)
  279. (>= (+ (string-width str) 2 indent) max-width))
  280. ((? symbol? symbol)
  281. (>= (+ (string-width (symbol->string symbol)) indent)
  282. max-width))
  283. ((? boolean?)
  284. (>= (+ 2 indent) max-width))
  285. (()
  286. (>= (+ 2 indent) max-width))
  287. (_ ;don't know
  288. #f))
  289. lst))
  290. (define (special-form? head)
  291. (special-form-lead head context))
  292. (match obj
  293. ((? comment? comment)
  294. (if (comment-margin? comment)
  295. (begin
  296. (display " " port)
  297. (display (comment->string (format-comment comment))
  298. port))
  299. (begin
  300. ;; When already at the beginning of a line, for example because
  301. ;; COMMENT follows a margin comment, no need to emit a newline.
  302. (unless (= column indent)
  303. (newline port)
  304. (display (make-string indent #\space) port))
  305. (display (comment->string (format-comment comment))
  306. port)))
  307. (display (make-string indent #\space) port)
  308. indent)
  309. (('quote lst)
  310. (unless delimited? (display " " port))
  311. (display "'" port)
  312. (loop indent (+ column (if delimited? 1 2)) #t context lst))
  313. (('quasiquote lst)
  314. (unless delimited? (display " " port))
  315. (display "`" port)
  316. (loop indent (+ column (if delimited? 1 2)) #t context lst))
  317. (('unquote lst)
  318. (unless delimited? (display " " port))
  319. (display "," port)
  320. (loop indent (+ column (if delimited? 1 2)) #t context lst))
  321. (('unquote-splicing lst)
  322. (unless delimited? (display " " port))
  323. (display ",@" port)
  324. (loop indent (+ column (if delimited? 2 3)) #t context lst))
  325. (('gexp lst)
  326. (unless delimited? (display " " port))
  327. (display "#~" port)
  328. (loop indent (+ column (if delimited? 2 3)) #t context lst))
  329. (('ungexp obj)
  330. (unless delimited? (display " " port))
  331. (display "#$" port)
  332. (loop indent (+ column (if delimited? 2 3)) #t context obj))
  333. (('ungexp-native obj)
  334. (unless delimited? (display " " port))
  335. (display "#+" port)
  336. (loop indent (+ column (if delimited? 2 3)) #t context obj))
  337. (('ungexp-splicing lst)
  338. (unless delimited? (display " " port))
  339. (display "#$@" port)
  340. (loop indent (+ column (if delimited? 3 4)) #t context lst))
  341. (('ungexp-native-splicing lst)
  342. (unless delimited? (display " " port))
  343. (display "#+@" port)
  344. (loop indent (+ column (if delimited? 3 4)) #t context lst))
  345. (((? special-form? head) arguments ...)
  346. ;; Special-case 'let', 'lambda', 'modify-inputs', etc. so the second
  347. ;; and following arguments are less indented.
  348. (let* ((lead (special-form-lead head context))
  349. (context (cons head context))
  350. (head (symbol->string head))
  351. (total (length arguments)))
  352. (unless delimited? (display " " port))
  353. (display "(" port)
  354. (display head port)
  355. (unless (zero? lead)
  356. (display " " port))
  357. ;; Print the first LEAD arguments.
  358. (let* ((indent (+ column 2
  359. (if delimited? 0 1)))
  360. (column (+ column 1
  361. (if (zero? lead) 0 1)
  362. (if delimited? 0 1)
  363. (string-length head)))
  364. (initial-indent column))
  365. (define new-column
  366. (let inner ((n lead)
  367. (arguments (take arguments (min lead total)))
  368. (column column))
  369. (if (zero? n)
  370. (begin
  371. (newline port)
  372. (display (make-string indent #\space) port)
  373. indent)
  374. (match arguments
  375. (() column)
  376. ((head . tail)
  377. (inner (- n 1) tail
  378. (loop initial-indent column
  379. (= n lead)
  380. context
  381. head)))))))
  382. ;; Print the remaining arguments.
  383. (let ((column (print-sequence
  384. context indent new-column
  385. (drop arguments (min lead total))
  386. #t)))
  387. (display ")" port)
  388. (+ column 1)))))
  389. ((head tail ...)
  390. (let* ((overflow? (>= column max-width))
  391. (column (if overflow?
  392. (+ indent 1)
  393. (+ column (if delimited? 1 2))))
  394. (newline? (newline-form? head context))
  395. (context (cons head context)))
  396. (if overflow?
  397. (begin
  398. (newline port)
  399. (display (make-string indent #\space) port))
  400. (unless delimited? (display " " port)))
  401. (display "(" port)
  402. (let* ((new-column (loop column column #t context head))
  403. (indent (if (or (>= new-column max-width)
  404. (not (symbol? head))
  405. (sequence-would-protrude?
  406. (+ new-column 1) tail)
  407. newline?)
  408. column
  409. (+ new-column 1))))
  410. (when newline?
  411. ;; Insert a newline right after HEAD.
  412. (newline port)
  413. (display (make-string indent #\space) port))
  414. (let ((column
  415. (print-sequence context indent
  416. (if newline? indent new-column)
  417. tail newline?)))
  418. (display ")" port)
  419. (+ column 1)))))
  420. (_
  421. (let* ((str (if (string? obj)
  422. (escaped-string obj)
  423. (object->string obj)))
  424. (len (string-width str)))
  425. (if (and (> (+ column 1 len) max-width)
  426. (not delimited?))
  427. (begin
  428. (newline port)
  429. (display (make-string indent #\space) port)
  430. (display str port)
  431. (+ indent len))
  432. (begin
  433. (unless delimited? (display " " port))
  434. (display str port)
  435. (+ column (if delimited? 0 1) len))))))))
  436. (define (object->string* obj indent . args)
  437. (call-with-output-string
  438. (lambda (port)
  439. (apply pretty-print-with-comments port obj
  440. #:indent indent
  441. args))))
  442. ;;;
  443. ;;; Simplifying input expressions.
  444. ;;;
  445. (define (label-matches? label name)
  446. "Return true if LABEL matches NAME, a package name."
  447. (or (string=? label name)
  448. (and (string-prefix? "python-" label)
  449. (string-prefix? "python2-" name)
  450. (string=? (string-drop label (string-length "python-"))
  451. (string-drop name (string-length "python2-"))))))
  452. (define* (simplify-inputs location package str inputs
  453. #:key (label-matches? label-matches?))
  454. "Simplify the inputs field of PACKAGE (a string) at LOCATION; its current
  455. value is INPUTS the corresponding source code is STR. Return a string to
  456. replace STR."
  457. (define (simplify-input-expression return)
  458. (match-lambda
  459. ((label ('unquote symbol)) symbol)
  460. ((label ('unquote symbol) output)
  461. (list 'quasiquote
  462. (list (list 'unquote symbol) output)))
  463. (_
  464. ;; Expression doesn't look like a simple input.
  465. (warning location (G_ "~a: complex expression, \
  466. bailing out~%")
  467. package)
  468. (return str))))
  469. (define (simplify-input exp input return)
  470. (define package* package)
  471. (match input
  472. ((or ((? string? label) (? package? package))
  473. ((? string? label) (? package? package)
  474. (? string?)))
  475. ;; If LABEL doesn't match PACKAGE's name, then simplifying would incur
  476. ;; a rebuild, and perhaps it would break build-side code relying on
  477. ;; this specific label.
  478. (if (label-matches? label (package-name package))
  479. ((simplify-input-expression return) exp)
  480. (begin
  481. (warning location (G_ "~a: input label \
  482. '~a' does not match package name, bailing out~%")
  483. package* label)
  484. (return str))))
  485. (_
  486. (warning location (G_ "~a: non-trivial input, \
  487. bailing out~%")
  488. package*)
  489. (return str))))
  490. (define (simplify-expressions exp inputs return)
  491. ;; Simplify the expressions in EXP, which correspond to INPUTS, and return
  492. ;; a list of expressions. Call RETURN with a string when bailing out.
  493. (let loop ((result '())
  494. (exp exp)
  495. (inputs inputs))
  496. (match exp
  497. (((? comment? head) . rest)
  498. (loop (cons head result) rest inputs))
  499. ((head . rest)
  500. (match inputs
  501. ((input . inputs)
  502. ;; HEAD (an sexp) and INPUT (an input tuple) are correlated.
  503. (loop (cons (simplify-input head input return) result)
  504. rest inputs))
  505. (()
  506. ;; If EXP and INPUTS have a different length, that
  507. ;; means EXP is a non-trivial input list, for example
  508. ;; with input-splicing, conditionals, etc.
  509. (warning location (G_ "~a: input expression is too short~%")
  510. package)
  511. (return str))))
  512. (()
  513. ;; It's possible for EXP to contain fewer elements than INPUTS, for
  514. ;; example in the case of input splicing. No bailout here. (XXX)
  515. (reverse result)))))
  516. (define inputs-exp
  517. (call-with-input-string str read-with-comments))
  518. (match inputs-exp
  519. (('list _ ...) ;already done
  520. str)
  521. (('modify-inputs _ ...) ;already done
  522. str)
  523. (('quasiquote ;prepending inputs
  524. (exp ...
  525. ('unquote-splicing
  526. ((and symbol (or 'package-inputs 'package-native-inputs
  527. 'package-propagated-inputs))
  528. arg))))
  529. (let/ec return
  530. (object->string*
  531. (let ((things (simplify-expressions exp inputs return)))
  532. `(modify-inputs (,symbol ,arg)
  533. (prepend ,@things)))
  534. (location-column location))))
  535. (('quasiquote ;replacing an input
  536. ((and exp ((? string? to-delete) ('unquote replacement)))
  537. ('unquote-splicing
  538. ('alist-delete (? string? to-delete)
  539. ((and symbol
  540. (or 'package-inputs 'package-native-inputs
  541. 'package-propagated-inputs))
  542. arg)))))
  543. (let/ec return
  544. (object->string*
  545. (let ((things (simplify-expressions (list exp)
  546. (list (car inputs))
  547. return)))
  548. `(modify-inputs (,symbol ,arg)
  549. (replace ,to-delete ,replacement)))
  550. (location-column location))))
  551. (('quasiquote ;removing an input
  552. (exp ...
  553. ('unquote-splicing
  554. ('alist-delete (? string? to-delete)
  555. ((and symbol
  556. (or 'package-inputs 'package-native-inputs
  557. 'package-propagated-inputs))
  558. arg)))))
  559. (let/ec return
  560. (object->string*
  561. (let ((things (simplify-expressions exp inputs return)))
  562. `(modify-inputs (,symbol ,arg)
  563. (delete ,to-delete)
  564. (prepend ,@things)))
  565. (location-column location))))
  566. (('fold 'alist-delete ;removing several inputs
  567. ((and symbol
  568. (or 'package-inputs 'package-native-inputs
  569. 'package-propagated-inputs))
  570. arg)
  571. ('quote ((? string? to-delete) ...)))
  572. (object->string*
  573. `(modify-inputs (,symbol ,arg)
  574. (delete ,@to-delete))
  575. (location-column location)))
  576. (('quasiquote ;removing several inputs and adding others
  577. (exp ...
  578. ('unquote-splicing
  579. ('fold 'alist-delete
  580. ((and symbol
  581. (or 'package-inputs 'package-native-inputs
  582. 'package-propagated-inputs))
  583. arg)
  584. ('quote ((? string? to-delete) ...))))))
  585. (let/ec return
  586. (object->string*
  587. (let ((things (simplify-expressions exp inputs return)))
  588. `(modify-inputs (,symbol ,arg)
  589. (delete ,@to-delete)
  590. (prepend ,@things)))
  591. (location-column location))))
  592. (('quasiquote (exp ...))
  593. (let/ec return
  594. (object->string*
  595. `(list ,@(simplify-expressions exp inputs return))
  596. (location-column location))))
  597. (_
  598. (warning location (G_ "~a: unsupported input style, \
  599. bailing out~%")
  600. package)
  601. str)))
  602. (define (edit-expression/dry-run properties rewrite-string)
  603. "Like 'edit-expression' but display what would be edited without actually
  604. doing it."
  605. (edit-expression properties
  606. (lambda (str)
  607. (unless (string=? (rewrite-string str) str)
  608. (info (source-properties->location properties)
  609. (G_ "would be edited~%")))
  610. str)))
  611. (define (absolute-location loc)
  612. "Replace the file name in LOC by an absolute location."
  613. (location (if (string-prefix? "/" (location-file loc))
  614. (location-file loc)
  615. (search-path %load-path (location-file loc)))
  616. (location-line loc)
  617. (location-column loc)))
  618. (define* (simplify-package-inputs package
  619. #:key (policy 'silent)
  620. (edit-expression edit-expression))
  621. "Edit the source code of PACKAGE to simplify its inputs field if needed.
  622. POLICY is a symbol that defines whether to simplify inputs; it can one of
  623. 'silent (change only if the resulting derivation is the same), 'safe (change
  624. only if semantics are known to be unaffected), and 'always (fearlessly
  625. simplify inputs!). Call EDIT-EXPRESSION to actually edit the source of
  626. PACKAGE."
  627. (for-each (lambda (field-name field)
  628. (match (field package)
  629. (()
  630. #f)
  631. (inputs
  632. (match (package-field-location package field-name)
  633. (#f
  634. ;; If the location of FIELD-NAME is not found, it may be
  635. ;; that PACKAGE inherits from another package.
  636. #f)
  637. (location
  638. (edit-expression
  639. (location->source-properties (absolute-location location))
  640. (lambda (str)
  641. (define matches?
  642. (match policy
  643. ('silent
  644. ;; Simplify inputs only when the label matches
  645. ;; perfectly, such that the resulting derivation
  646. ;; is unchanged.
  647. label-matches?)
  648. ('safe
  649. ;; If PACKAGE has no arguments, labels are known
  650. ;; to have no effect: this is a "safe" change, but
  651. ;; it may change the derivation.
  652. (if (null? (package-arguments package))
  653. (const #t)
  654. label-matches?))
  655. ('always
  656. ;; Assume it's gonna be alright.
  657. (const #t))))
  658. (simplify-inputs location
  659. (package-name package)
  660. str inputs
  661. #:label-matches? matches?))))))))
  662. '(inputs native-inputs propagated-inputs)
  663. (list package-inputs package-native-inputs
  664. package-propagated-inputs)))
  665. ;;;
  666. ;;; Formatting package definitions.
  667. ;;;
  668. (define* (format-package-definition package
  669. #:key policy
  670. (edit-expression edit-expression))
  671. "Reformat the definition of PACKAGE."
  672. (unless (package-definition-location package)
  673. (leave (package-location package)
  674. (G_ "no definition location for package ~a~%")
  675. (package-full-name package)))
  676. (edit-expression
  677. (location->source-properties
  678. (absolute-location (package-definition-location package)))
  679. (lambda (str)
  680. (let ((exp (call-with-input-string str
  681. read-with-comments)))
  682. (object->string* exp
  683. (location-column
  684. (package-definition-location package))
  685. #:format-comment canonicalize-comment)))))
  686. (define (package-location<? p1 p2)
  687. "Return true if P1's location is \"before\" P2's."
  688. (let ((loc1 (package-location p1))
  689. (loc2 (package-location p2)))
  690. (and loc1 loc2
  691. (if (string=? (location-file loc1) (location-file loc2))
  692. (< (location-line loc1) (location-line loc2))
  693. (string<? (location-file loc1) (location-file loc2))))))
  694. ;;;
  695. ;;; Options.
  696. ;;;
  697. (define %options
  698. ;; Specification of the command-line options.
  699. (list (find (lambda (option)
  700. (member "load-path" (option-names option)))
  701. %standard-build-options)
  702. (option '(#\n "dry-run") #f #f
  703. (lambda (opt name arg result)
  704. (alist-cons 'dry-run? #t result)))
  705. (option '(#\e "expression") #t #f
  706. (lambda (opt name arg result)
  707. (alist-cons 'expression arg result)))
  708. (option '(#\S "styling") #t #f
  709. (lambda (opt name arg result)
  710. (alist-cons 'styling-procedure
  711. (match arg
  712. ("inputs" simplify-package-inputs)
  713. ("format" format-package-definition)
  714. (_ (leave (G_ "~a: unknown styling~%")
  715. arg)))
  716. result)))
  717. (option '("input-simplification") #t #f
  718. (lambda (opt name arg result)
  719. (let ((symbol (string->symbol arg)))
  720. (unless (memq symbol '(silent safe always))
  721. (leave (G_ "~a: invalid input simplification policy~%")
  722. arg))
  723. (alist-cons 'input-simplification-policy symbol
  724. result))))
  725. (option '(#\h "help") #f #f
  726. (lambda args
  727. (show-help)
  728. (exit 0)))
  729. (option '(#\V "version") #f #f
  730. (lambda args
  731. (show-version-and-exit "guix style")))))
  732. (define (show-help)
  733. (display (G_ "Usage: guix style [OPTION]... [PACKAGE]...
  734. Update package definitions to the latest style.\n"))
  735. (display (G_ "
  736. -S, --styling=RULE apply RULE, a styling rule"))
  737. (newline)
  738. (display (G_ "
  739. -n, --dry-run display files that would be edited but do nothing"))
  740. (display (G_ "
  741. -L, --load-path=DIR prepend DIR to the package module search path"))
  742. (display (G_ "
  743. -e, --expression=EXPR consider the package EXPR evaluates to"))
  744. (display (G_ "
  745. --input-simplification=POLICY
  746. follow POLICY for package input simplification, one
  747. of 'silent', 'safe', or 'always'"))
  748. (newline)
  749. (display (G_ "
  750. -h, --help display this help and exit"))
  751. (display (G_ "
  752. -V, --version display version information and exit"))
  753. (newline)
  754. (show-bug-report-information))
  755. (define %default-options
  756. ;; Alist of default option values.
  757. `((input-simplification-policy . silent)
  758. (styling-procedure . ,format-package-definition)))
  759. ;;;
  760. ;;; Entry point.
  761. ;;;
  762. (define-command (guix-style . args)
  763. (category packaging)
  764. (synopsis "update the style of package definitions")
  765. (define (parse-options)
  766. ;; Return the alist of option values.
  767. (parse-command-line args %options (list %default-options)
  768. #:build-options? #f))
  769. (let* ((opts (parse-options))
  770. (packages (filter-map (match-lambda
  771. (('argument . spec)
  772. (specification->package spec))
  773. (('expression . str)
  774. (read/eval str))
  775. (_ #f))
  776. opts))
  777. (edit (if (assoc-ref opts 'dry-run?)
  778. edit-expression/dry-run
  779. edit-expression))
  780. (style (assoc-ref opts 'styling-procedure))
  781. (policy (assoc-ref opts 'input-simplification-policy)))
  782. (with-error-handling
  783. (for-each (lambda (package)
  784. (style package #:policy policy
  785. #:edit-expression edit))
  786. ;; Sort package by source code location so that we start editing
  787. ;; files from the bottom and going upward. That way, the
  788. ;; 'location' field of <package> records is not invalidated as
  789. ;; we modify files.
  790. (sort (if (null? packages)
  791. (fold-packages cons '() #:select? (const #t))
  792. packages)
  793. (negate package-location<?))))))