parameters.scm 45 KB

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