parameterization.patch 53 KB

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