parameters.scm 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2023 Sarthak Shah <shahsarthakw@gmail.com>
  4. ;;;
  5. ;;; This file is part of GNU Guix.
  6. ;;;
  7. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  8. ;;; under the terms of the GNU General Public License as published by
  9. ;;; the Free Software Foundation; either version 3 of the License, or (at
  10. ;;; your option) any later version.
  11. ;;;
  12. ;;; GNU Guix is distributed in the hope that it will be useful, but
  13. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;;; GNU General Public License for more details.
  16. ;;;
  17. ;;; You should have received a copy of the GNU General Public License
  18. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  19. (define-module (guix parameters)
  20. #:use-module (guix diagnostics)
  21. #:use-module (guix i18n)
  22. #:use-module (guix packages)
  23. #:use-module (guix profiles)
  24. #:use-module (guix records)
  25. #:autoload (guix transformations) (options->transformation)
  26. #:use-module (srfi srfi-1)
  27. #:use-module (srfi srfi-13)
  28. #:use-module (srfi srfi-26)
  29. #:use-module (srfi srfi-34)
  30. #:use-module (srfi srfi-35)
  31. #:use-module (ice-9 hash-table)
  32. #:use-module (ice-9 match)
  33. #:use-module (ice-9 receive)
  34. ;? -> remove?
  35. #:export (parameter-type;
  36. package-parameter;
  37. parameter-spec;
  38. boolean-parameter-type;
  39. parameter-variant;
  40. parameter-variant-match;
  41. parameter-spec-property;?
  42. package-parameter-spec;
  43. package-parameter-alist;
  44. all-spec-parameters;
  45. all-spec-parameters-with-types;
  46. base-parameter-alist;
  47. parameter-process-list;
  48. package-override-plist;?
  49. parameter-spec-validate;
  50. package-resolve-parameter-list;
  51. %global-parameters;
  52. define-global-parameter;
  53. package-with-parameters;
  54. parameterize-package;
  55. apply-variants;
  56. parameter-spec-parameter-alist;
  57. parameter-if;
  58. parameter-match;
  59. parameter-match-case;
  60. parameter-modify-inputs;
  61. parameter-substitute-keyword-arguments;
  62. ))
  63. ;;; Commentary:
  64. ;;;
  65. ;;; This module provides a way to express high-level "package parameters",
  66. ;;; which allow users to customize how packages are built. Parameters are an
  67. ;;; interface that package developers define, where each parameter has a name
  68. ;;; and type. The user interface then converts parameter values from string
  69. ;;; to Scheme values and records them in the package properties.
  70. ;;;
  71. ;;; Package parameters are discoverable; their description is
  72. ;;; internationalized. The possible values of a parameter can be enumerated,
  73. ;;; and thus the Cartesian product of all possible parameter values for a
  74. ;;; package can be enumerated as well.
  75. ;;;
  76. ;;; Code:
  77. (define (give-me-a-symbol ex)
  78. "Take a string or symbol EX and return a symbol."
  79. (cond ((symbol? ex) ex)
  80. ((string? ex) (string->symbol ex))
  81. (else (raise (formatted-message
  82. (G_ "Not a symbol or a string: ~s")
  83. ex)))))
  84. (define-record-type* <parameter-type> parameter-type
  85. make-parameter-type
  86. parameter-type?
  87. this-parameter-type
  88. (name parameter-type-name
  89. (sanitize give-me-a-symbol))
  90. (accepted-values parameter-type-accepted-values)
  91. (negation parameter-type-negation
  92. (default (first (parameter-type-accepted-values this-parameter-type)))
  93. (thunked))
  94. (default parameter-type-default
  95. (default (match (parameter-type-accepted-values this-parameter-type)
  96. [(first second . rest)
  97. (if (not (parameter-type-negation this-parameter-type))
  98. first
  99. second)]
  100. [oth (raise (formatted-message
  101. (G_ "Bad accepted-values form: ~s")
  102. oth))]))
  103. (thunked))
  104. (description parameter-type-description
  105. (default "")))
  106. (define boolean-parameter-type
  107. (parameter-type
  108. (name 'boolean)
  109. (accepted-values '(off on))
  110. (description "Boolean Parameter Type")))
  111. ;; Package parameter interface.
  112. (define-record-type* <package-parameter> package-parameter
  113. make-package-parameter
  114. package-parameter?
  115. (name package-parameter-name
  116. (sanitize give-me-a-symbol))
  117. (type package-parameter-type
  118. (default boolean-parameter-type))
  119. (variants package-parameter-variants
  120. (default '())
  121. (sanitize sanitize-parametric-variants))
  122. (dependencies package-parameter-dependencies
  123. (default '())
  124. (sanitize dependency-sanitizer)
  125. (thunked))
  126. (predicate package-parameter-predicate
  127. (sanitize predicate-sanitizer)
  128. (default (const #f)))
  129. (description package-parameter-description (default "")))
  130. (define %global-parameters
  131. (alist->hash-table '()))
  132. ;; SANITIZERS
  133. (define (sanitize-parametric-variants ls)
  134. "Raise an error if LS is not a list."
  135. (cond ((list? ls) ls)
  136. (else (raise (formatted-message
  137. (G_ "Not a list: ~s")
  138. ls)))))
  139. (define (predicate-sanitizer p)
  140. (match p
  141. [(? procedure? p) p]
  142. [#t (and (warning
  143. (G_ "Please use (const #t) instead of #t!~%"))
  144. (const #t))]
  145. [#f (and (warning
  146. (G_ "Please use (const #f) instead of #f!~%"))
  147. (const #f))]
  148. [_ (raise (formatted-message
  149. (G_ "Not a predicate: ~s")
  150. p))]))
  151. ;; % USEFUL HELPER FUNCTIONS %
  152. (define (return-list lst)
  153. "Take a value LST, return LST if it a list and (list LST) otherwise."
  154. (if (list? lst)
  155. lst
  156. (list lst)))
  157. (define (append-everything . things)
  158. "Take a number of THINGS, and append them all."
  159. (apply append
  160. (map return-list things)))
  161. (define (get-parameter-sym psym)
  162. "If the argument is a cons cell, return the CAR otherwise return the argument."
  163. (match psym
  164. [(a . b) a]
  165. [a a]))
  166. (define* (merge-same-key lst #:optional (carry '()))
  167. "Merge the cells of LST with the same value in their CAR."
  168. (match lst
  169. [((a . b) . rest)
  170. (if (null? (filter (lambda (y) (equal? a (first y)))
  171. carry))
  172. (merge-same-key rest (cons (cons a b) carry))
  173. (merge-same-key rest (assq-set! carry
  174. a
  175. (append (assq-ref carry a) b))))]
  176. [() carry]))
  177. (define-syntax lambdize-lambdas
  178. (syntax-rules (:cruise)
  179. [(% :cruise x . rest)
  180. (if (keyword? x)
  181. (lambdize-lambdas x . rest)
  182. (cons x (lambdize-lambdas :cruise . rest)))]
  183. [(% :cruise) '()]
  184. [(% #:lambda fn . rest)
  185. (cons #:lambda
  186. (cons fn
  187. (lambdize-lambdas :cruise . rest)))]
  188. [(% x . rest)
  189. (cons 'x (lambdize-lambdas . rest))]
  190. [(%) '()]))
  191. (define-syntax parameter-variant
  192. (syntax-rules ()
  193. [(%) '()]
  194. [(% psym variants ...)
  195. (let ((parsed-variants
  196. (parse-keyword-list (lambdize-lambdas variants ...))))
  197. (map (cut cons <>
  198. parsed-variants)
  199. (return-list 'psym)))]))
  200. (define* (parse-keyword-list kw-lst)
  201. "Parses a list of keywords, KW-LST and returns an alist."
  202. (define (list-till-keyword lst)
  203. (receive (a b)
  204. (break keyword? lst)
  205. (cons a b)))
  206. (define* (break-keywords lst)
  207. (match lst
  208. [((? keyword? key) vals ..1)
  209. (match (list-till-keyword vals)
  210. [(first . rest)
  211. (cons (cons (keyword->symbol key)
  212. first)
  213. (break-keywords rest))])]
  214. [((? keyword? just-a-key)) ; (... #:key)
  215. (cons (cons (keyword->symbol just-a-key) '())
  216. '())]
  217. [(singleton) '()]
  218. [() '()]
  219. [_ (raise (formatted-message
  220. (G_ "Error trying to break keywords at ~a")
  221. lst))]))
  222. (merge-same-key (break-keywords kw-lst)))
  223. ;; The lock here is used to signal when merge-same-key is to be used
  224. ;; having a :lock means merge-same-key has been used further up the tree
  225. ;; note that :lock is not a keyword but a symbol, as we are using keywords elsewhere
  226. (define-syntax parameter-variant-match
  227. (syntax-rules (:lock)
  228. ((% :lock (x ...))
  229. (return-list
  230. (parameter-variant x ...)))
  231. ((% :lock (x ...) rest ...)
  232. (append
  233. (return-list (parameter-variant x ...))
  234. (parameter-variant-match :lock rest ...)))
  235. ((% rest ...)
  236. (map
  237. (match-lambda
  238. [(v . lst)
  239. (cons v
  240. (merge-same-key lst))])
  241. (merge-same-key
  242. (parameter-variant-match :lock rest ...))))))
  243. (define (local-sanitizer ls)
  244. "Sanitize a list of local parameters, LS."
  245. (if (list? ls)
  246. (map (lambda (val)
  247. (cond ((package-parameter? val) val)
  248. ((symbol? val) (package-parameter (name val)))
  249. ((string? val) (package-parameter (name (string->symbol val))))
  250. (else (raise (formatted-message
  251. (G_ "Not a parameter, symbol or string: ~s")
  252. val)))))
  253. ls)
  254. (raise (formatted-message
  255. (G_ "Spec's local field is not a list: ~s")
  256. ls))))
  257. (define* (variant-sanitizer lv)
  258. "Sanitize a list of variants."
  259. ;; #:yes -> use default variant
  260. ;; #:no -> don't use variant
  261. ;; #:special -> use variant in rest
  262. (define (sym->parameter psym)
  263. "Take a symbol PSYM and return the corresponding parameter."
  264. (or (find (lambda (g) (eqv? psym
  265. (package-parameter-name g)))
  266. lv)
  267. (hash-ref %global-parameters psym)
  268. (raise (formatted-message
  269. (G_ "sym->parameter: not a symbol: ~s")
  270. psym))))
  271. (define-macro (assq-override! asslst key val)
  272. `(set! ,asslst
  273. (assq-set! ,asslst ,key ,val)))
  274. (lambda (ls)
  275. (let ((triad (parse-keyword-list ls)))
  276. (if (find (lambda (g) (not (or (eqv? (first g) 'yes)
  277. (eqv? (first g) 'no)
  278. (eqv? (first g) 'special))))
  279. triad)
  280. (raise (formatted-message
  281. (G_ "Invalid keyword in use-variants: ~s")
  282. (first g))))
  283. (let ((vars-lst '()))
  284. (map
  285. (match-lambda
  286. [('yes rest ...)
  287. (map
  288. (lambda (p)
  289. (if (not (symbol? p))
  290. (raise (formatted-message
  291. (G_ "Not a symbol: ~s")
  292. p))
  293. (assq-override! vars-lst p #:yes)))
  294. rest)]
  295. [('no rest ...)
  296. (map
  297. (lambda (p)
  298. (if (not (symbol? p))
  299. (raise (formatted-message
  300. (G_ "Not a symbol: ~s")
  301. p))
  302. (assq-override! vars-lst p #:no)))
  303. rest)]
  304. [('special rest ...)
  305. (map
  306. (match-lambda
  307. [(a . b)
  308. (assq-override! vars-lst
  309. a
  310. b)])
  311. rest)]
  312. [_ (error "wrongly formatted use-variant!")])
  313. triad)
  314. (map
  315. (lambda (x)
  316. (match (assq-ref vars-lst (package-parameter-name x))
  317. [#f (assq-override! vars-lst
  318. (package-parameter-name x)
  319. (package-parameter-variants x))]
  320. [#:yes (assq-override! vars-lst
  321. (package-parameter-name x)
  322. (package-parameter-variants x))]
  323. [#:no #f] ; do nothing
  324. [varn (assq-override! vars-lst
  325. (package-parameter-name x)
  326. varn)]))
  327. lv)
  328. vars-lst))))
  329. (define (dependency-sanitizer deps)
  330. "Sanitize the dependency-list of a package-parameter."
  331. (unless (eqv? deps '())
  332. (if (not (list? deps))
  333. (raise (formatted-message
  334. (G_ "Dependencies not a list: ~s")
  335. deps)))
  336. (if (keyword? (first deps))
  337. (if (match (first deps)
  338. [#:package (and (warning
  339. (G_ "Package Dependencies are not supported!~%"))
  340. #t)]
  341. [#:parameter #t]
  342. [_ #f])
  343. (parse-keyword-list deps)
  344. (raise (formatted-message
  345. (G_ "Bad dependency keyword: ~s")
  346. (first deps))))
  347. (dependency-sanitizer (cons #:parameter deps)))))
  348. (define-record-type* <parameter-spec> parameter-spec
  349. make-parameter-spec
  350. parameter-spec?
  351. this-parameter-spec
  352. (local parameter-spec-local
  353. (default '())
  354. (sanitize local-sanitizer)
  355. (thunked))
  356. (defaults parameter-spec-defaults
  357. (default '())
  358. (thunked))
  359. (required parameter-spec-required
  360. (default '())
  361. (thunked))
  362. (optional parameter-spec-optional
  363. (default '())
  364. (thunked))
  365. (one-of parameter-spec-one-of
  366. (default '())
  367. (thunked))
  368. (combinations-with-substitutes
  369. parameter-spec-combinations-with-substitutes
  370. (default parameter-spec-defaults)
  371. (thunked))
  372. (use-variants parameter-spec-use-variants
  373. (default '())
  374. (sanitize (variant-sanitizer
  375. (parameter-spec-local this-parameter-spec)))
  376. (thunked))
  377. (parameter-alist parameter-spec-parameter-alist
  378. (default (base-parameter-alist this-parameter-spec))
  379. (thunked)))
  380. (define-syntax parameter-spec-property
  381. (syntax-rules ()
  382. [(parameter-spec-property body ...)
  383. (cons 'parameter-spec
  384. (parameter-spec body ...))]))
  385. (define (apply-variants pkg vars)
  386. "Apply a list of variants, VARS to the given package PKG."
  387. (define (exact-sub v)
  388. (match v
  389. [(lst ...) ; to traverse the tree
  390. (map exact-sub v)]
  391. [#:package-name
  392. (package-name pkg)]
  393. [#:package
  394. pkg]
  395. [#:parameter-value
  396. (match vars
  397. [((_ . rest) . others)
  398. rest])]
  399. [x x]))
  400. ;; substitute keywords - transforms
  401. (define* (substitute-keywords-for-transforms in #:optional (ret '()))
  402. (match in
  403. [(a . rest)
  404. (substitute-keywords-for-transforms
  405. rest
  406. (cons (exact-sub a) ret))]
  407. [() (match (reverse ret)
  408. [(a . rest)
  409. (cons a (string-join rest "="))])]))
  410. ;; substitute keywords
  411. (define* (substitute-keywords in #:optional (ret '()))
  412. (match in
  413. [(a . rest)
  414. (substitute-keywords
  415. a
  416. (cons (exact-sub a) ret))]
  417. [() (reverse ret)]))
  418. (match vars
  419. [(pcell (option optargs ...) . rest)
  420. (match option
  421. ['build-system
  422. ;; halt execution if it does not match
  423. (if (member (package-build-system the-package)
  424. optargs) ; will be a list of build systems
  425. (apply-variants pkg (cons pcell
  426. rest))
  427. pkg)]
  428. ['transform
  429. (apply-variants
  430. ((options->transformation
  431. (map substitute-keywords-for-transforms optargs))
  432. pkg)
  433. (cons pcell
  434. rest))]
  435. ['lambda
  436. (apply-variants
  437. (fold
  438. (lambda (fn pack)
  439. (case (first (procedure-minimum-arity fn))
  440. [(0) (fn)]
  441. [(1) (fn pack)]
  442. [(2) (fn pack (match pcell [(_ . rest) rest]))]
  443. [else (raise (formatted-message
  444. (G_ "Procedure ~s has invalid arity.")
  445. fn))]))
  446. pkg
  447. optargs)
  448. (cons pcell
  449. rest))]
  450. [oth
  451. (raise (formatted-message
  452. (G_ "Invalid Option: ")
  453. oth))])]
  454. [(pcell (option) . rest)
  455. (apply-variants pkg (cons pcell rest))]
  456. [(pcell) pkg]
  457. [_ (raise (formatted-message
  458. (G_ "Poorly formatted variant spec: ~s")
  459. vars))]))
  460. (define-syntax package-with-parameters
  461. (syntax-rules ()
  462. [(% spec body ...)
  463. (let* [(the-package-0 (package body ...))
  464. (the-package (package
  465. (inherit the-package-0)
  466. (replacement (package-replacement the-package-0))
  467. (location (package-location the-package-0))
  468. (properties
  469. (cons (cons 'parameter-spec
  470. spec)
  471. (package-properties the-package-0)))))]
  472. (parameterize-package the-package
  473. (parameter-spec-parameter-alist spec)
  474. #:force-parameterization? #t))]))
  475. (define* (parameterize-package the-initial-package
  476. the-initial-list
  477. #:key (force-parameterization? #f))
  478. "Evaluates THE-INITIAL-PACKAGE with the parameter-list THE-INITIAL-LIST."
  479. (define-macro (assq-override! asslst key val)
  480. `(set! ,asslst
  481. (assq-set! ,asslst ,key ,val)))
  482. (define smoothen
  483. (match-lambda
  484. [(a . #:off)
  485. (cons a
  486. (parameter-type-negation
  487. (package-parameter-type (parameter-spec-get-parameter spec a))))]
  488. [(a . #:default)
  489. (cons a
  490. (parameter-type-default
  491. (package-parameter-type (parameter-spec-get-parameter spec a))))]
  492. [cell cell]))
  493. (let* [(the-initial-spec
  494. (package-parameter-spec the-initial-package))
  495. (the-original-parameter-list
  496. (package-parameter-alist the-initial-package))
  497. (the-parameter-list
  498. (package-resolve-parameter-list the-initial-package
  499. the-initial-list))]
  500. ;; exit and return the same package if no impactful changes
  501. (if (and (not force-parameterization?)
  502. (null? (filter (match-lambda
  503. [(parameter-sym . parameter-value)
  504. (not (eqv? (assq-ref
  505. the-original-parameter-list
  506. parameter-sym)
  507. parameter-value))])
  508. the-parameter-list)))
  509. the-initial-package
  510. (let* [(the-spec ; this value gets called very often
  511. (parameter-spec
  512. (inherit the-initial-spec)
  513. (parameter-alist
  514. the-parameter-list)))
  515. (the-package
  516. (package
  517. (inherit the-initial-package)
  518. (replacement (package-replacement the-initial-package))
  519. (location (package-location the-initial-package))
  520. (properties (assq-set! (package-properties the-initial-package)
  521. 'parameter-spec
  522. the-spec))))
  523. (the-variants
  524. ;; first get list of normal variants (local, etc)
  525. ;; then match over use-variants
  526. ;; if rest #:yes, check the-parameter-list for val
  527. ;; if rest #:no, purge from prev list
  528. ;; if rest #:special, /replace/ value
  529. (let ((var-lst (parameter-spec-use-variants the-spec)))
  530. (map (match-lambda
  531. [(key . rest)
  532. (set! var-lst
  533. (assq-set! var-lst
  534. key
  535. (package-parameter-variants
  536. (parameter-spec-get-parameter the-spec key))))])
  537. (filter (lambda (x)
  538. ((package-parameter-predicate
  539. (parameter-spec-get-parameter
  540. the-spec
  541. (first x)))
  542. the-package))
  543. (filter
  544. (lambda (x)
  545. (not (assq-ref var-lst (first x)))) ; not in the variant-lst?
  546. the-parameter-list)))
  547. (map
  548. (match-lambda
  549. [(key . rest)
  550. (match rest
  551. [#:yes (assq-override! var-lst
  552. key
  553. (package-parameter-variants
  554. (parameter-spec-get-parameter the-spec key)))]
  555. [#:no (set! var-lst
  556. (assq-remove! var-lst
  557. key))]
  558. [_ #f])])
  559. var-lst)
  560. var-lst))
  561. (applicable-variants
  562. (map (match-lambda
  563. [(key . rest)
  564. (cons (cons key
  565. (assq-ref the-parameter-list key))
  566. (apply append
  567. (map (match-lambda
  568. [(_ . remaining)
  569. (return-list remaining)])
  570. rest)))])
  571. ;; does it have values?
  572. (filter (match-lambda
  573. [(_ . rest)
  574. (not (null? rest))])
  575. (map ;; get list of applicable values
  576. (match-lambda
  577. [(p . lst)
  578. (let ((absv (assq-ref the-parameter-list p))
  579. ;; if absv is -ve, only -ve values allowed
  580. ;; if absv is +ve, only +ve and _ allowed
  581. (negv (parameter-type-negation
  582. (package-parameter-type
  583. (parameter-spec-get-parameter the-spec p))))
  584. (defv (parameter-type-default
  585. (package-parameter-type
  586. (parameter-spec-get-parameter the-spec p)))))
  587. (cons p
  588. (filter
  589. (lambda (ls)
  590. (match (first ls)
  591. ['_ (not (eqv? absv negv))]
  592. [#:off (eqv? absv negv)]
  593. [#:default (eqv? absv defv)]
  594. [oth (eqv? absv oth)]))
  595. lst)))])
  596. (filter (lambda (x) (assq-ref the-parameter-list (first x)))
  597. the-variants)))))]
  598. (fold (lambda (vlst pack)
  599. (apply-variants pack vlst))
  600. the-package
  601. applicable-variants)))))
  602. (define (package-parameter-spec package)
  603. "Takes a package PACKAGE and returns its parameter-spec."
  604. (or (assq-ref (package-properties package) 'parameter-spec)
  605. (parameter-spec))) ; returns empty spec
  606. (define (package-parameter-alist package)
  607. "Takes a package PACKAGE and returns its parameter-list."
  608. (parameter-spec-parameter-alist
  609. (package-parameter-spec package)))
  610. ;;; PROCESSING PIPELINE
  611. ;; Convention:
  612. ;; Works on Parameters? -> parameter-spec/fun
  613. ;; Works on Parameter-Spec? -> parameter-spec/fun
  614. (define (parameter-spec-get-parameter pspec pcons)
  615. "Takes a parameter cell PCONS and returns the corresponding package-parameter."
  616. (let ((psym (get-parameter-sym pcons)))
  617. (or (find (lambda (x)
  618. (eqv? psym
  619. (package-parameter-name x)))
  620. (parameter-spec-local pspec))
  621. (hash-ref %global-parameters psym)
  622. (raise (formatted-message
  623. (G_ "Parameter not found: ~s")
  624. psym)))))
  625. (define (parameter-spec-negation-supported? pspec x)
  626. "Is negation supported for the given parameter X?"
  627. (let ((negv
  628. (parameter-type-negation (package-parameter-type (parameter-spec-get-parameter pspec x)))))
  629. (if negv
  630. negv
  631. '_)))
  632. (define (get-parameter-spec-dependencies pspec psym)
  633. "Get the dependencies of the corresponding parameter to a given parameter symbol, PSYM."
  634. (let ([p (parameter-spec-get-parameter pspec psym)])
  635. (return-list
  636. (assq-ref (package-parameter-dependencies p)
  637. 'parameter))))
  638. ;; 1. Fetching
  639. (define (base-parameter-alist pspec) ; returns base case
  640. "Returns the BASE-PARAMETER-ALIST for a given parameter-spec PSPEC."
  641. ;; '((a . psym) (b . #f) ...)
  642. (let* ((v1 (parameter-process-list ; returns funneled list
  643. (append-everything
  644. (parameter-spec-defaults pspec)
  645. (parameter-spec-required pspec))))
  646. (v2 (parameter-process-list
  647. (append-everything
  648. (apply append
  649. ;; XXX: change to a filter-map
  650. (filter (cut first <>)
  651. (map (cut get-parameter-spec-dependencies pspec <>)
  652. (return-list v1))))
  653. v1))))
  654. ;; funnel will signal duplication err
  655. ;; check if base case is valid
  656. (parameter-spec-validate pspec v2)
  657. v2))
  658. ;; 2. Processing
  659. ;; IMPORTANT CHANGE: Symbolic Negation no longer supported (psym!)
  660. (define (parameter-process-list lst)
  661. "Processes and formats a list of parameters, LST."
  662. (define (return-cell p)
  663. (match p
  664. [(a b) (cons a b)]
  665. [(a . b) p]
  666. [a (cons a '_)]))
  667. (define (funnel plst)
  668. (define* (group-values lst #:optional (carry '()))
  669. (match lst
  670. [((a . b) . rest)
  671. (let ((v (assq-ref carry a)))
  672. (group-values rest
  673. (assq-set! carry
  674. a
  675. (cons b
  676. (if v v '())))))]
  677. [() carry]
  678. [_ (raise (formatted-message
  679. (G_ "Poorly formatted assoc-list in group-values! ~s")
  680. lst))]))
  681. (define (figure-out psym p)
  682. (or (and (< (length p) 3)
  683. (or (and (eq? (length p) 1) (first p))
  684. (and (member '_ p)
  685. (first (delq '_ p)))))
  686. (raise (formatted-message
  687. (G_ "Too many values for a single parameter: ~s with ~s")
  688. psym p))))
  689. (map (match-lambda [(parameter . values)
  690. (cons parameter
  691. (figure-out parameter ; for the error message
  692. (delete-duplicates values)))])
  693. (group-values plst)))
  694. (funnel (map
  695. return-cell
  696. lst)))
  697. ;; 3. Overriding
  698. (define (all-spec-parameters pspec) ; for the UI
  699. "Returns all the parameters in a parameter-spec, PSPEC."
  700. ;; '(sym-a sym-b ...)
  701. (delete-duplicates
  702. (map get-parameter-sym ; we do not care about the values
  703. (append-everything ; works same as before
  704. (map package-parameter-name
  705. (parameter-spec-local pspec))
  706. (parameter-spec-defaults pspec)
  707. (parameter-spec-required pspec)
  708. ;; We are NOT pulling dependencies at this phase
  709. ;; They will not be influenced by the user parameter alist
  710. (filter (lambda (x) (not (eqv? x '_)))
  711. (apply append (parameter-spec-one-of pspec)))
  712. (parameter-spec-optional pspec)))))
  713. (define* (all-spec-parameters-with-types pspec #:key (show-booleans? #t))
  714. (if show-booleans?
  715. (map (lambda (x)
  716. (string-append
  717. (symbol->string x)
  718. ":"
  719. (symbol->string
  720. (parameter-type-name
  721. (package-parameter-type (parameter-spec-get-parameter pspec (cons x #f)))))))
  722. (all-spec-parameters pspec))
  723. (map (lambda (x)
  724. (string-append
  725. (symbol->string x)
  726. ((lambda (x)
  727. (if (eqv? x 'boolean)
  728. ""
  729. (string-append ":" (symbol->string x))))
  730. (parameter-type-name
  731. (package-parameter-type (parameter-spec-get-parameter pspec (cons x #f)))))))
  732. (all-spec-parameters pspec))))
  733. ;; Now we compare it against the PLIST
  734. ;; NOTE: This is the only instance where GLOBAL PARAMETERS may be used
  735. ;; Since referring to the package is not possible, we pass it instead of pspec
  736. (define (package-override-plist pkg plist)
  737. "Takes a package PKG and parameter-list PLIST and overrides PLIST according to the package."
  738. (let* ((pspec (package-parameter-spec pkg))
  739. (all-p (all-spec-parameters pspec))
  740. (filtered-plist (filter (match-lambda
  741. [(sym . rest)
  742. (or (member sym all-p)
  743. (and (hash-ref %global-parameters sym)
  744. ((package-parameter-predicate
  745. (hash-ref %global-parameters sym))
  746. pkg)))])
  747. (parameter-process-list plist)))
  748. (filtered-first (map first filtered-plist))
  749. (remaining-p (filter (lambda (x) (not (member x filtered-first)))
  750. all-p)))
  751. (append-everything filtered-plist
  752. (map (lambda (x) (if (parameter-spec-negation-supported? pspec x)
  753. (cons x #:off)
  754. (cons x '_)))
  755. remaining-p))))
  756. ;; 4. Funneling
  757. (define (override-spec-multi-match pspec plst)
  758. "Overrides various keyword values in the parameter-list PLST."
  759. (map
  760. (match-lambda
  761. [(a . '_)
  762. (cons a
  763. (match
  764. (parameter-type-accepted-values
  765. (package-parameter-type (parameter-spec-get-parameter pspec a)))
  766. [(_ . (val . rest)) val]))]
  767. [(a . #:off)
  768. (cons a
  769. (parameter-type-negation (package-parameter-type (parameter-spec-get-parameter pspec a))))]
  770. [(a . #:default)
  771. (cons a
  772. (parameter-type-default (package-parameter-type (parameter-spec-get-parameter pspec a))))]
  773. [cell cell])
  774. plst))
  775. ;; 5. Validation
  776. (define (parameter-spec-validate pspec plst)
  777. "Validates a parameter-list PLST against the parameter-spec PSPEC."
  778. (define (process-multi-list lst)
  779. (apply append
  780. (map (lambda (x)
  781. (parameter-process-list (list x)))
  782. (filter (lambda (x) (not (eqv? x '_)))
  783. lst))))
  784. ;; We want all tests to run
  785. (let ((works? #t))
  786. (define (m+eqv? new-val orig-val)
  787. (or (and (eqv? orig-val '_)
  788. (not (eqv? new-val #:off)))
  789. (eqv? orig-val new-val)))
  790. (define (throw+f sym vals)
  791. (raise (formatted-message
  792. (G_ "Parameter Validation Error: ~a with values ~s~%")
  793. sym vals))
  794. (set! works? #f))
  795. ;; first we check duplication
  796. ;; a bit unnecessary
  797. (define (validate/duplication)
  798. (let ((symlst (map first plst)))
  799. (unless (eqv? symlst (delete-duplicates symlst))
  800. (throw+f "Duplicates" plst))))
  801. ;; logic checking checks for:
  802. ;; - presence of required parameters
  803. ;; - 'one-of' conflicts
  804. ;; - dependency satisfaction
  805. (define (validate/logic)
  806. (map ; required
  807. (match-lambda
  808. [(psym . value)
  809. (unless
  810. (let ((new-val (assq-ref plst psym)))
  811. (m+eqv? (if (eqv?
  812. new-val
  813. (parameter-spec-negation-supported?
  814. pspec
  815. psym))
  816. #:off new-val)
  817. value))
  818. (throw+f "Unsatisfied Requirements" (cons psym value)))])
  819. (parameter-process-list ; cannot have duplicates here!
  820. (parameter-spec-required pspec)))
  821. (map ; one-of
  822. (lambda (ls)
  823. (unless
  824. (let ((satisfied (count
  825. (match-lambda
  826. [(psym . value)
  827. (let ((new-val (assq-ref plst psym)))
  828. (m+eqv?
  829. (if
  830. (eqv? new-val
  831. (parameter-spec-negation-supported?
  832. pspec
  833. psym))
  834. #:off new-val)
  835. value))])
  836. (process-multi-list ls)))) ; duplicates could happen!
  837. (or (= satisfied 1)
  838. (and (= satisfied 0)
  839. (eqv? (first ls) '_))))
  840. (throw+f "Unsatisfied One-Of" ls)))
  841. (parameter-spec-one-of pspec))
  842. (unless (not (member #f
  843. (return-list
  844. (map (lambda (x)
  845. (let ((deps (package-parameter-dependencies
  846. (parameter-spec-get-parameter pspec x))))
  847. (if deps
  848. (not
  849. (member
  850. #f
  851. (map
  852. (lambda (dep)
  853. ;; 0. restructure dep to a proper cell
  854. (match (first
  855. (parameter-process-list
  856. (return-list dep)))
  857. ;; 1. assq-ref
  858. [(psym . value)
  859. (m+eqv?
  860. (assq-ref plst psym)
  861. value)]))
  862. (return-list
  863. ;;; XXX: check for packages
  864. ;; not doable in the current state as the validator
  865. ;; does not take the entire package as an argument
  866. ;; the validator will have to be heavily modified
  867. (assq-ref deps 'parameter)))))
  868. #t)))
  869. ;; filter to check if parameter is not its negation
  870. (filter (match-lambda
  871. [(psym . value)
  872. (not (eqv? value
  873. (parameter-spec-negation-supported?
  874. pspec
  875. psym)))])
  876. plst)))))
  877. (throw+f "Bad dependencies!" plst)))
  878. (validate/duplication)
  879. (validate/logic)
  880. works?))
  881. ;; need pkg instead of pspec for override-spec
  882. (define (package-resolve-parameter-list pkg plst)
  883. "Resolves a parameter-list PLST against the package PKG."
  884. (let* ([pspec (package-parameter-spec pkg)]
  885. [proper-plst (override-spec-multi-match
  886. pspec
  887. (package-override-plist
  888. pkg
  889. (parameter-process-list plst)))])
  890. (if (parameter-spec-validate pspec proper-plst)
  891. proper-plst
  892. (base-parameter-alist pspec))))
  893. ;; %global-parameters: hash table containing global parameters ref'd by syms
  894. (define-syntax define-global-parameter
  895. (syntax-rules ()
  896. [(define-global-parameter parameter-definition)
  897. (let ((gp-val parameter-definition))
  898. (hash-set! %global-parameters
  899. (package-parameter-name gp-val)
  900. gp-val))]))
  901. (define-syntax parameter-inside?
  902. (syntax-rules ()
  903. [(% p pkg)
  904. (let ((plst
  905. (parameter-spec-parameter-alist
  906. (package-parameter-spec pkg))))
  907. (not
  908. (eqv? (or (assq-ref plst (first p))
  909. (error "Parameter not found!"))
  910. (parameter-type-negation
  911. (package-parameter-type
  912. (parameter-spec-get-parameter
  913. (package-parameter-spec pkg)
  914. p))))))]))
  915. (define-syntax parameter-if
  916. (syntax-rules ()
  917. [(parameter-if #:package pkg rest ...)
  918. (parameter-if-branches pkg rest ...)]
  919. [(parameter-if rest ...)
  920. (parameter-if-branches this-pkg rest ...)]))
  921. (define-syntax parameter-if-branches
  922. (syntax-rules ()
  923. [(parameter-if-branches pkg parameters exp)
  924. (parameter-if-driven pkg parameters exp '())]
  925. [(parameter-if-branches pkg parameters exp exp-else)
  926. (parameter-if-driven pkg parameters exp exp-else)]
  927. [(% anything ...)
  928. (raise (formatted-message
  929. (G_ "Poorly formatted parameter-if: ~s"
  930. '(parameter-if anything ...))))]))
  931. (define-syntax parameter-if-driven
  932. (syntax-rules ()
  933. [(parameter-if-driven pkg (#:all parameters ...) exp exp-else)
  934. (if (not (member
  935. #f
  936. (map (cut parameter-inside? <> pkg)
  937. (parameter-process-list '(parameters ...)))))
  938. exp
  939. exp-else)]
  940. [(parameter-if-driven pkg (parameters ...) exp exp-else)
  941. (if (member
  942. #t
  943. (map (cut parameter-inside? <> pkg)
  944. (parameter-process-list '(parameters ...))))
  945. exp
  946. exp-else)]))
  947. (define-syntax parameter-match
  948. (syntax-rules (_)
  949. [(% #:package pkg rest ...)
  950. (parameter-match-driven pkg rest ...)]
  951. [(% rest ...)
  952. (parameter-match-driven this-package rest ...)]))
  953. (define-syntax parameter-match-driven
  954. (syntax-rules (_)
  955. [(% pkg) '()]
  956. [(% pkg (_ clauses ...) rest ...) (begin (begin clauses ...) (parameter-match-driven pkg rest ...))]
  957. [(% pkg (parameters) rest ...) (parameter-match-driven pkg rest ...)]
  958. [(% pkg ((#:all parameters ...) clauses ...) rest ...)
  959. (begin
  960. (and (not (member #f (map (cut parameter-inside? <> pkg)
  961. (parameter-process-list '(parameters ...)))))
  962. (begin clauses ...))
  963. (parameter-match-driven pkg rest ...))]
  964. [(% pkg ((parameters ...) clauses ...) rest ...)
  965. (begin
  966. (and (member #t (map (cut parameter-inside? <> pkg)
  967. (parameter-process-list '(parameters ...))))
  968. (begin clauses ...))
  969. (parameter-match-driven pkg rest ...))]
  970. [(% pkg (parameter clauses ...) rest ...)
  971. (begin
  972. (and (parameter-inside? parameter pkg)
  973. (begin clauses ...))
  974. (parameter-match-driven pkg rest ...))]
  975. [(% pkg anything ...)
  976. (raise (formatted-message
  977. (G_ "Poorly formatted parameter-match: ~s"
  978. '(parameter-match anything ...))))]))
  979. (define-syntax parameter-match-case
  980. (syntax-rules (_)
  981. [(% #:package pkg rest ...)
  982. (parameter-match-case-driven pkg rest ...)]
  983. [(% rest ...)
  984. (parameter-match-case-driven this-package rest ...)]))
  985. (define-syntax parameter-match-case-driven
  986. (syntax-rules (_)
  987. [(% pkg) '()]
  988. [(% pkg (_ clauses ...) rest ...) (begin clauses ...)]
  989. [(% pkg (parameters) rest ...) (parameter-match-case-driven pkg rest ...)]
  990. [(% pkg ((#:all parameters ...) clauses ...) rest ...)
  991. (if (not (member #f (map (cut parameter-inside? <> pkg)
  992. (parameter-process-list '(parameters ...)))))
  993. (begin clauses ...)
  994. (parameter-match-case-driven pkg rest ...))]
  995. [(% pkg ((parameters ...) clauses ...) rest ...)
  996. (if (member #t (map (cut parameter-inside? <> pkg)
  997. (parameter-process-list '(parameters ...))))
  998. (begin clauses ...)
  999. (parameter-match-case-driven pkg rest ...))]
  1000. [(% pkg (parameter clauses ...) rest ...)
  1001. (if (parameter-inside? parameter pkg)
  1002. (begin clauses ...)
  1003. (parameter-match-case-driven pkg rest ...))]
  1004. [(% pkg anything ...)
  1005. (raise (formatted-message
  1006. (G_ "Poorly formatted parameter-match-case: ~s"
  1007. '(parameter-match-case anything ...))))]))
  1008. ;; modified to take the original package, similar to modify-inputs
  1009. (define-syntax parameter-modify-inputs
  1010. (syntax-rules (_ :lock prepend append delete replace)
  1011. [(% inputs) inputs]
  1012. [(% inputs :lock ())
  1013. inputs]
  1014. [(% inputs :lock (stuff ...))
  1015. (modify-inputs inputs stuff ...)]
  1016. [(% inputs :lock (stuff ...) (_ clauses ...) rest ...)
  1017. (parameter-modify-inputs inputs :lock (stuff ... clauses ...) rest ...)]
  1018. [(% inputs :lock stuff (parameters) rest ...)
  1019. (parameter-modify-inputs inputs :lock stuff rest ...)]
  1020. [(% inputs :lock (stuff ...) ((#:all parameters ...) clauses ...) rest ...)
  1021. (if (not (member #f (map (cut parameter-inside?
  1022. <> this-package)
  1023. (parameter-process-list '(parameters ...)))))
  1024. (parameter-modify-inputs inputs :lock
  1025. (stuff ... clauses ...)
  1026. rest ...)
  1027. (parameter-modify-inputs inputs :lock (stuff ...) rest ...))]
  1028. [(% inputs :lock (stuff ...) ((parameters ...) clauses ...) rest ...)
  1029. (if (member #t (map (cut parameter-inside?
  1030. <> this-package)
  1031. (parameter-process-list '(parameters ...))))
  1032. (parameter-modify-inputs inputs :lock
  1033. (stuff ... clauses ...)
  1034. rest ...)
  1035. (parameter-modify-inputs inputs :lock (stuff ...) rest ...))]
  1036. [(% inputs :lock (stuff ...) (parameter clauses ...) rest ...)
  1037. (if (parameter-inside? parameter
  1038. this-package)
  1039. (parameter-modify-inputs inputs :lock
  1040. (stuff ... clauses ...)
  1041. rest ...)
  1042. (parameter-modify-inputs inputs :lock (stuff ...) rest ...))]
  1043. [(% inputs rest ...)
  1044. (parameter-modify-inputs inputs :lock () rest ...)]
  1045. [(% . anything)
  1046. (raise (formatted-message (G_ "Poorly formatted parameter-modify-inputs: ~s" anything)))]))
  1047. (define-syntax parameter-substitute-keyword-arguments
  1048. (syntax-rules (_ :lock prepend append delete replace)
  1049. [(% arguments) arguments]
  1050. [(% arguments :lock ())
  1051. arguments]
  1052. [(% arguments :lock (stuff ...))
  1053. (substitute-keyword-arguments arguments stuff ...)]
  1054. [(% arguments :lock (stuff ...) (_ clauses ...) rest ...)
  1055. (parameter-substitute-keyword-arguments arguments :lock (stuff ... clauses ...) rest ...)]
  1056. [(% arguments :lock stuff (parameters) rest ...)
  1057. (parameter-substitute-keyword-arguments arguments :lock stuff rest ...)]
  1058. [(% arguments :lock (stuff ...) ((#:all parameters ...) clauses ...) rest ...)
  1059. (if (not (member #f (map (cut parameter-inside?
  1060. <> this-package)
  1061. (parameter-process-list '(parameters ...)))))
  1062. (parameter-substitute-keyword-arguments arguments :lock
  1063. (stuff ... clauses ...)
  1064. rest ...)
  1065. (parameter-substitute-keyword-arguments arguments :lock (stuff ...) rest ...))]
  1066. [(% arguments :lock (stuff ...) ((parameters ...) clauses ...) rest ...)
  1067. (if (member #t (map (cut parameter-inside?
  1068. <> this-package)
  1069. (parameter-process-list '(parameters ...))))
  1070. (parameter-substitute-keyword-arguments arguments :lock
  1071. (stuff ... clauses ...)
  1072. rest ...)
  1073. (parameter-substitute-keyword-arguments arguments :lock (stuff ...) rest ...))]
  1074. [(% arguments :lock (stuff ...) (parameter clauses ...) rest ...)
  1075. (if (parameter-inside? parameter
  1076. this-package)
  1077. (parameter-substitute-keyword-arguments arguments :lock
  1078. (stuff ... clauses ...)
  1079. rest ...)
  1080. (parameter-substitute-keyword-arguments arguments :lock (stuff ...) rest ...))]
  1081. [(% arguments rest ...)
  1082. (parameter-substitute-keyword-arguments arguments :lock () rest ...)]
  1083. [(% . anything)
  1084. (raise (formatted-message (G_ "Poorly formatted parameter-substitute-keyword-arguments: ~s" anything)))]))
  1085. ;; Some global parameters
  1086. (define-global-parameter
  1087. (package-parameter
  1088. (name 'static-lib)
  1089. (variants
  1090. (parameter-variant-match
  1091. (_ #:transform
  1092. (with-configure-flag #:package-name "=--disable-shared")
  1093. (with-configure-flag #:package-name "=--enable-static"))))
  1094. (predicate #t)))
  1095. (define-global-parameter
  1096. (package-parameter
  1097. (name 'tests)
  1098. (variants
  1099. (parameter-variant-match
  1100. (#:off #:transform (without-tests #:package-name))))
  1101. (description "Toggle for tests")
  1102. (predicate #t)))