profiles.scm 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org>
  4. ;;; Copyright © 2014, 2016 Alex Kost <alezost@gmail.com>
  5. ;;; Copyright © 2015 Mark H Weaver <mhw@netris.org>
  6. ;;; Copyright © 2015 Sou Bunnbu <iyzsong@gmail.com>
  7. ;;; Copyright © 2016 Ricardo Wurmus <rekado@elephly.net>
  8. ;;; Copyright © 2016 Chris Marusich <cmmarusich@gmail.com>
  9. ;;; Copyright © 2017 Huang Ying <huang.ying.caritas@gmail.com>
  10. ;;; Copyright © 2017 Maxim Cournoyer <maxim.cournoyer@gmail.com>
  11. ;;;
  12. ;;; This file is part of GNU Guix.
  13. ;;;
  14. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  15. ;;; under the terms of the GNU General Public License as published by
  16. ;;; the Free Software Foundation; either version 3 of the License, or (at
  17. ;;; your option) any later version.
  18. ;;;
  19. ;;; GNU Guix is distributed in the hope that it will be useful, but
  20. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  21. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. ;;; GNU General Public License for more details.
  23. ;;;
  24. ;;; You should have received a copy of the GNU General Public License
  25. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  26. (define-module (guix profiles)
  27. #:use-module ((guix utils) #:hide (package-name->name+version))
  28. #:use-module ((guix build utils)
  29. #:select (package-name->name+version))
  30. #:use-module (guix records)
  31. #:use-module (guix packages)
  32. #:use-module (guix derivations)
  33. #:use-module (guix search-paths)
  34. #:use-module (guix gexp)
  35. #:use-module (guix modules)
  36. #:use-module (guix monads)
  37. #:use-module (guix store)
  38. #:use-module (guix sets)
  39. #:use-module (ice-9 vlist)
  40. #:use-module (ice-9 match)
  41. #:use-module (ice-9 regex)
  42. #:use-module (ice-9 ftw)
  43. #:use-module (ice-9 format)
  44. #:use-module (srfi srfi-1)
  45. #:use-module (srfi srfi-9)
  46. #:use-module (srfi srfi-11)
  47. #:use-module (srfi srfi-19)
  48. #:use-module (srfi srfi-26)
  49. #:use-module (srfi srfi-34)
  50. #:use-module (srfi srfi-35)
  51. #:export (&profile-error
  52. profile-error?
  53. profile-error-profile
  54. &profile-not-found-error
  55. profile-not-found-error?
  56. &profile-collistion-error
  57. profile-collision-error?
  58. profile-collision-error-entry
  59. profile-collision-error-conflict
  60. &missing-generation-error
  61. missing-generation-error?
  62. missing-generation-error-generation
  63. manifest make-manifest
  64. manifest?
  65. manifest-entries
  66. manifest-transitive-entries
  67. <manifest-entry> ; FIXME: eventually make it internal
  68. manifest-entry
  69. manifest-entry?
  70. manifest-entry-name
  71. manifest-entry-version
  72. manifest-entry-output
  73. manifest-entry-item
  74. manifest-entry-dependencies
  75. manifest-entry-search-paths
  76. manifest-entry-parent
  77. manifest-pattern
  78. manifest-pattern?
  79. manifest-pattern-name
  80. manifest-pattern-version
  81. manifest-pattern-output
  82. manifest-remove
  83. manifest-add
  84. manifest-lookup
  85. manifest-installed?
  86. manifest-matching-entries
  87. manifest-transaction
  88. manifest-transaction?
  89. manifest-transaction-install
  90. manifest-transaction-remove
  91. manifest-transaction-install-entry
  92. manifest-transaction-remove-pattern
  93. manifest-transaction-null?
  94. manifest-transaction-removal-candidate?
  95. manifest-perform-transaction
  96. manifest-transaction-effects
  97. profile-manifest
  98. package->manifest-entry
  99. packages->manifest
  100. ca-certificate-bundle
  101. %default-profile-hooks
  102. profile-derivation
  103. generation-number
  104. generation-numbers
  105. profile-generations
  106. relative-generation-spec->number
  107. relative-generation
  108. previous-generation-number
  109. generation-time
  110. generation-file-name
  111. switch-to-generation
  112. roll-back
  113. delete-generation))
  114. ;;; Commentary:
  115. ;;;
  116. ;;; Tools to create and manipulate profiles---i.e., the representation of a
  117. ;;; set of installed packages.
  118. ;;;
  119. ;;; Code:
  120. ;;;
  121. ;;; Condition types.
  122. ;;;
  123. (define-condition-type &profile-error &error
  124. profile-error?
  125. (profile profile-error-profile))
  126. (define-condition-type &profile-not-found-error &profile-error
  127. profile-not-found-error?)
  128. (define-condition-type &profile-collision-error &error
  129. profile-collision-error?
  130. (entry profile-collision-error-entry) ;<manifest-entry>
  131. (conflict profile-collision-error-conflict)) ;<manifest-entry>
  132. (define-condition-type &missing-generation-error &profile-error
  133. missing-generation-error?
  134. (generation missing-generation-error-generation))
  135. ;;;
  136. ;;; Manifests.
  137. ;;;
  138. (define-record-type <manifest>
  139. (manifest entries)
  140. manifest?
  141. (entries manifest-entries)) ; list of <manifest-entry>
  142. ;; Convenient alias, to avoid name clashes.
  143. (define make-manifest manifest)
  144. (define-record-type* <manifest-entry> manifest-entry
  145. make-manifest-entry
  146. manifest-entry?
  147. (name manifest-entry-name) ; string
  148. (version manifest-entry-version) ; string
  149. (output manifest-entry-output ; string
  150. (default "out"))
  151. (item manifest-entry-item) ; package | store path
  152. (dependencies manifest-entry-dependencies ; <manifest-entry>*
  153. (default '()))
  154. (search-paths manifest-entry-search-paths ; search-path-specification*
  155. (default '()))
  156. (parent manifest-entry-parent ; promise (#f | <manifest-entry>)
  157. (default (delay #f))))
  158. (define-record-type* <manifest-pattern> manifest-pattern
  159. make-manifest-pattern
  160. manifest-pattern?
  161. (name manifest-pattern-name) ; string
  162. (version manifest-pattern-version ; string | #f
  163. (default #f))
  164. (output manifest-pattern-output ; string | #f
  165. (default "out")))
  166. (define (manifest-transitive-entries manifest)
  167. "Return the entries of MANIFEST along with their propagated inputs,
  168. recursively."
  169. (let loop ((entries (manifest-entries manifest))
  170. (result '())
  171. (visited (set))) ;compare with 'equal?'
  172. (match entries
  173. (()
  174. (reverse result))
  175. ((head . tail)
  176. (if (set-contains? visited head)
  177. (loop tail result visited)
  178. (loop (append (manifest-entry-dependencies head)
  179. tail)
  180. (cons head result)
  181. (set-insert head visited)))))))
  182. (define (profile-manifest profile)
  183. "Return the PROFILE's manifest."
  184. (let ((file (string-append profile "/manifest")))
  185. (if (file-exists? file)
  186. (call-with-input-file file read-manifest)
  187. (manifest '()))))
  188. (define (manifest-entry-lookup manifest)
  189. "Return a lookup procedure for the entries of MANIFEST. The lookup
  190. procedure takes two arguments: the entry name and output."
  191. (define mapping
  192. (let loop ((entries (manifest-entries manifest))
  193. (mapping vlist-null))
  194. (fold (lambda (entry result)
  195. (vhash-cons (cons (manifest-entry-name entry)
  196. (manifest-entry-output entry))
  197. entry
  198. (loop (manifest-entry-dependencies entry)
  199. result)))
  200. mapping
  201. entries)))
  202. (lambda (name output)
  203. (match (vhash-assoc (cons name output) mapping)
  204. ((_ . entry) entry)
  205. (#f #f))))
  206. (define* (lower-manifest-entry entry system #:key target)
  207. "Lower ENTRY for SYSTEM and TARGET such that its 'item' field is a store
  208. file name."
  209. (let ((item (manifest-entry-item entry)))
  210. (if (string? item)
  211. (with-monad %store-monad
  212. (return entry))
  213. (mlet %store-monad ((drv (lower-object item system
  214. #:target target))
  215. (output -> (manifest-entry-output entry)))
  216. (return (manifest-entry
  217. (inherit entry)
  218. (item (derivation->output-path drv output))))))))
  219. (define* (check-for-collisions manifest system #:key target)
  220. "Check whether the entries of MANIFEST conflict with one another; raise a
  221. '&profile-collision-error' when a conflict is encountered."
  222. (define lookup
  223. (manifest-entry-lookup manifest))
  224. (with-monad %store-monad
  225. (foldm %store-monad
  226. (lambda (entry result)
  227. (match (lookup (manifest-entry-name entry)
  228. (manifest-entry-output entry))
  229. ((? manifest-entry? second) ;potential conflict
  230. (mlet %store-monad ((first (lower-manifest-entry entry system
  231. #:target
  232. target))
  233. (second (lower-manifest-entry second system
  234. #:target
  235. target)))
  236. (if (string=? (manifest-entry-item first)
  237. (manifest-entry-item second))
  238. (return result)
  239. (raise (condition
  240. (&profile-collision-error
  241. (entry first)
  242. (conflict second)))))))
  243. (#f ;no conflict
  244. (return result))))
  245. #t
  246. (manifest-transitive-entries manifest))))
  247. (define* (package->manifest-entry package #:optional (output "out")
  248. #:key (parent (delay #f)))
  249. "Return a manifest entry for the OUTPUT of package PACKAGE."
  250. ;; For each dependency, keep a promise pointing to its "parent" entry.
  251. (letrec* ((deps (map (match-lambda
  252. ((label package)
  253. (package->manifest-entry package
  254. #:parent (delay entry)))
  255. ((label package output)
  256. (package->manifest-entry package output
  257. #:parent (delay entry))))
  258. (package-propagated-inputs package)))
  259. (entry (manifest-entry
  260. (name (package-name package))
  261. (version (package-version package))
  262. (output output)
  263. (item package)
  264. (dependencies (delete-duplicates deps))
  265. (search-paths
  266. (package-transitive-native-search-paths package))
  267. (parent parent))))
  268. entry))
  269. (define (packages->manifest packages)
  270. "Return a list of manifest entries, one for each item listed in PACKAGES.
  271. Elements of PACKAGES can be either package objects or package/string tuples
  272. denoting a specific output of a package."
  273. (manifest
  274. (map (match-lambda
  275. ((package output)
  276. (package->manifest-entry package output))
  277. ((? package? package)
  278. (package->manifest-entry package)))
  279. packages)))
  280. (define (manifest->gexp manifest)
  281. "Return a representation of MANIFEST as a gexp."
  282. (define (entry->gexp entry)
  283. (match entry
  284. (($ <manifest-entry> name version output (? string? path)
  285. (deps ...) (search-paths ...))
  286. #~(#$name #$version #$output #$path
  287. (propagated-inputs #$(map entry->gexp deps))
  288. (search-paths #$(map search-path-specification->sexp
  289. search-paths))))
  290. (($ <manifest-entry> name version output (? package? package)
  291. (deps ...) (search-paths ...))
  292. #~(#$name #$version #$output
  293. (ungexp package (or output "out"))
  294. (propagated-inputs #$(map entry->gexp deps))
  295. (search-paths #$(map search-path-specification->sexp
  296. search-paths))))))
  297. (match manifest
  298. (($ <manifest> (entries ...))
  299. #~(manifest (version 3)
  300. (packages #$(map entry->gexp entries))))))
  301. (define (find-package name version)
  302. "Return a package from the distro matching NAME and possibly VERSION. This
  303. procedure is here for backward-compatibility and will eventually vanish."
  304. (define find-best-packages-by-name ;break abstractions
  305. (module-ref (resolve-interface '(gnu packages))
  306. 'find-best-packages-by-name))
  307. ;; Use 'find-best-packages-by-name' and not 'find-packages-by-name'; the
  308. ;; former traverses the module tree only once and then allows for efficient
  309. ;; access via a vhash.
  310. (match (find-best-packages-by-name name version)
  311. ((p _ ...) p)
  312. (_
  313. (match (find-best-packages-by-name name #f)
  314. ((p _ ...) p)
  315. (_ #f)))))
  316. (define (sexp->manifest sexp)
  317. "Parse SEXP as a manifest."
  318. (define (infer-search-paths name version)
  319. ;; Infer the search path specifications for NAME-VERSION by looking up a
  320. ;; same-named package in the distro. Useful for the old manifest formats
  321. ;; that did not store search path info.
  322. (let ((package (find-package name version)))
  323. (if package
  324. (package-native-search-paths package)
  325. '())))
  326. (define (infer-dependency item parent)
  327. ;; Return a <manifest-entry> for ITEM.
  328. (let-values (((name version)
  329. (package-name->name+version
  330. (store-path-package-name item))))
  331. (manifest-entry
  332. (name name)
  333. (version version)
  334. (item item)
  335. (parent parent))))
  336. (define* (sexp->manifest-entry sexp #:optional (parent (delay #f)))
  337. (match sexp
  338. ((name version output path
  339. ('propagated-inputs deps)
  340. ('search-paths search-paths)
  341. extra-stuff ...)
  342. ;; For each of DEPS, keep a promise pointing to ENTRY.
  343. (letrec* ((deps* (map (cut sexp->manifest-entry <> (delay entry))
  344. deps))
  345. (entry (manifest-entry
  346. (name name)
  347. (version version)
  348. (output output)
  349. (item path)
  350. (dependencies deps*)
  351. (search-paths (map sexp->search-path-specification
  352. search-paths))
  353. (parent parent))))
  354. entry))))
  355. (match sexp
  356. (('manifest ('version 0)
  357. ('packages ((name version output path) ...)))
  358. (manifest
  359. (map (lambda (name version output path)
  360. (manifest-entry
  361. (name name)
  362. (version version)
  363. (output output)
  364. (item path)
  365. (search-paths (infer-search-paths name version))))
  366. name version output path)))
  367. ;; Version 1 adds a list of propagated inputs to the
  368. ;; name/version/output/path tuples.
  369. (('manifest ('version 1)
  370. ('packages ((name version output path deps) ...)))
  371. (manifest
  372. (map (lambda (name version output path deps)
  373. ;; Up to Guix 0.7 included, dependencies were listed as ("gmp"
  374. ;; "/gnu/store/...-gmp") for instance. Discard the 'label' in
  375. ;; such lists.
  376. (let ((deps (match deps
  377. (((labels directories) ...)
  378. directories)
  379. ((directories ...)
  380. directories))))
  381. (letrec* ((deps* (map (cute infer-dependency <> (delay entry))
  382. deps))
  383. (entry (manifest-entry
  384. (name name)
  385. (version version)
  386. (output output)
  387. (item path)
  388. (dependencies deps*)
  389. (search-paths
  390. (infer-search-paths name version)))))
  391. entry)))
  392. name version output path deps)))
  393. ;; Version 2 adds search paths and is slightly more verbose.
  394. (('manifest ('version 2 minor-version ...)
  395. ('packages ((name version output path
  396. ('propagated-inputs deps)
  397. ('search-paths search-paths)
  398. extra-stuff ...)
  399. ...)))
  400. (manifest
  401. (map (lambda (name version output path deps search-paths)
  402. (letrec* ((deps* (map (cute infer-dependency <> (delay entry))
  403. deps))
  404. (entry (manifest-entry
  405. (name name)
  406. (version version)
  407. (output output)
  408. (item path)
  409. (dependencies deps*)
  410. (search-paths
  411. (map sexp->search-path-specification
  412. search-paths)))))
  413. entry))
  414. name version output path deps search-paths)))
  415. ;; Version 3 represents DEPS as full-blown manifest entries.
  416. (('manifest ('version 3 minor-version ...)
  417. ('packages (entries ...)))
  418. (manifest (map sexp->manifest-entry entries)))
  419. (_
  420. (raise (condition
  421. (&message (message "unsupported manifest format")))))))
  422. (define (read-manifest port)
  423. "Return the packages listed in MANIFEST."
  424. (sexp->manifest (read port)))
  425. (define (entry-predicate pattern)
  426. "Return a procedure that returns #t when passed a manifest entry that
  427. matches NAME/OUTPUT/VERSION. OUTPUT and VERSION may be #f, in which case they
  428. are ignored."
  429. (match pattern
  430. (($ <manifest-pattern> name version output)
  431. (match-lambda
  432. (($ <manifest-entry> entry-name entry-version entry-output)
  433. (and (string=? entry-name name)
  434. (or (not entry-output) (not output)
  435. (string=? entry-output output))
  436. (or (not version)
  437. (string=? entry-version version))))))))
  438. (define (manifest-remove manifest patterns)
  439. "Remove entries for each of PATTERNS from MANIFEST. Each item in PATTERNS
  440. must be a manifest-pattern."
  441. (define (remove-entry pattern lst)
  442. (remove (entry-predicate pattern) lst))
  443. (make-manifest (fold remove-entry
  444. (manifest-entries manifest)
  445. patterns)))
  446. (define (manifest-add manifest entries)
  447. "Add a list of manifest ENTRIES to MANIFEST and return new manifest.
  448. Remove MANIFEST entries that have the same name and output as ENTRIES."
  449. (define (same-entry? entry name output)
  450. (match entry
  451. (($ <manifest-entry> entry-name _ entry-output _ ...)
  452. (and (equal? name entry-name)
  453. (equal? output entry-output)))))
  454. (make-manifest
  455. (append entries
  456. (fold (lambda (entry result)
  457. (match entry
  458. (($ <manifest-entry> name _ out _ ...)
  459. (filter (negate (cut same-entry? <> name out))
  460. result))))
  461. (manifest-entries manifest)
  462. entries))))
  463. (define (manifest-lookup manifest pattern)
  464. "Return the first item of MANIFEST that matches PATTERN, or #f if there is
  465. no match.."
  466. (find (entry-predicate pattern)
  467. (manifest-entries manifest)))
  468. (define (manifest-installed? manifest pattern)
  469. "Return #t if MANIFEST has an entry matching PATTERN (a manifest-pattern),
  470. #f otherwise."
  471. (->bool (manifest-lookup manifest pattern)))
  472. (define (manifest-matching-entries manifest patterns)
  473. "Return all the entries of MANIFEST that match one of the PATTERNS."
  474. (define predicates
  475. (map entry-predicate patterns))
  476. (define (matches? entry)
  477. (any (lambda (pred)
  478. (pred entry))
  479. predicates))
  480. (filter matches? (manifest-entries manifest)))
  481. ;;;
  482. ;;; Manifest transactions.
  483. ;;;
  484. (define-record-type* <manifest-transaction> manifest-transaction
  485. make-manifest-transaction
  486. manifest-transaction?
  487. (install manifest-transaction-install ; list of <manifest-entry>
  488. (default '()))
  489. (remove manifest-transaction-remove ; list of <manifest-pattern>
  490. (default '())))
  491. (define (manifest-transaction-install-entry entry transaction)
  492. "Augment TRANSACTION's set of installed packages with ENTRY, a
  493. <manifest-entry>."
  494. (manifest-transaction
  495. (inherit transaction)
  496. (install
  497. (cons entry (manifest-transaction-install transaction)))))
  498. (define (manifest-transaction-remove-pattern pattern transaction)
  499. "Add PATTERN to TRANSACTION's list of packages to remove."
  500. (manifest-transaction
  501. (inherit transaction)
  502. (remove
  503. (cons pattern (manifest-transaction-remove transaction)))))
  504. (define (manifest-transaction-null? transaction)
  505. "Return true if TRANSACTION has no effect---i.e., it neither installs nor
  506. remove software."
  507. (match transaction
  508. (($ <manifest-transaction> () ()) #t)
  509. (($ <manifest-transaction> _ _) #f)))
  510. (define (manifest-transaction-removal-candidate? entry transaction)
  511. "Return true if ENTRY is a candidate for removal in TRANSACTION."
  512. (any (lambda (pattern)
  513. ((entry-predicate pattern) entry))
  514. (manifest-transaction-remove transaction)))
  515. (define (manifest-transaction-effects manifest transaction)
  516. "Compute the effect of applying TRANSACTION to MANIFEST. Return 4 values:
  517. the list of packages that would be removed, installed, upgraded, or downgraded
  518. when applying TRANSACTION to MANIFEST. Upgrades are represented as pairs
  519. where the head is the entry being upgraded and the tail is the entry that will
  520. replace it."
  521. (define (manifest-entry->pattern entry)
  522. (manifest-pattern
  523. (name (manifest-entry-name entry))
  524. (output (manifest-entry-output entry))))
  525. (let loop ((input (manifest-transaction-install transaction))
  526. (install '())
  527. (upgrade '())
  528. (downgrade '()))
  529. (match input
  530. (()
  531. (let ((remove (manifest-transaction-remove transaction)))
  532. (values (manifest-matching-entries manifest remove)
  533. (reverse install) (reverse upgrade) (reverse downgrade))))
  534. ((entry rest ...)
  535. ;; Check whether installing ENTRY corresponds to the installation of a
  536. ;; new package or to an upgrade.
  537. ;; XXX: When the exact same output directory is installed, we're not
  538. ;; really upgrading anything. Add a check for that case.
  539. (let* ((pattern (manifest-entry->pattern entry))
  540. (previous (manifest-lookup manifest pattern))
  541. (newer? (and previous
  542. (version>=? (manifest-entry-version entry)
  543. (manifest-entry-version previous)))))
  544. (loop rest
  545. (if previous install (cons entry install))
  546. (if (and previous newer?)
  547. (alist-cons previous entry upgrade)
  548. upgrade)
  549. (if (and previous (not newer?))
  550. (alist-cons previous entry downgrade)
  551. downgrade)))))))
  552. (define (manifest-perform-transaction manifest transaction)
  553. "Perform TRANSACTION on MANIFEST and return the new manifest."
  554. (let ((install (manifest-transaction-install transaction))
  555. (remove (manifest-transaction-remove transaction)))
  556. (manifest-add (manifest-remove manifest remove)
  557. install)))
  558. ;;;
  559. ;;; Profiles.
  560. ;;;
  561. (define (manifest-inputs manifest)
  562. "Return a list of <gexp-input> objects for MANIFEST."
  563. (define entry->input
  564. (match-lambda
  565. (($ <manifest-entry> name version output thing deps)
  566. ;; THING may be a package or a file name. In the latter case, assume
  567. ;; it's already valid.
  568. (cons (gexp-input thing output)
  569. (append-map entry->input deps)))))
  570. (append-map entry->input (manifest-entries manifest)))
  571. (define* (manifest-lookup-package manifest name #:optional version)
  572. "Return as a monadic value the first package or store path referenced by
  573. MANIFEST that is named NAME and optionally has the given VERSION prefix, or #f
  574. if not found."
  575. ;; Return as a monadic value the package or store path referenced by the
  576. ;; manifest ENTRY, or #f if not referenced.
  577. (define (entry-lookup-package entry)
  578. (define (find-among-inputs inputs)
  579. (find (lambda (input)
  580. (and (package? input)
  581. (equal? name (package-name input))
  582. (if version
  583. (string-prefix? version (package-version input))
  584. #t)))
  585. inputs))
  586. (define (find-among-store-items items)
  587. (find (lambda (item)
  588. (let-values (((name* version*)
  589. (package-name->name+version
  590. (store-path-package-name item))))
  591. (and (string=? name name*)
  592. (if version
  593. (string-prefix? version version*)
  594. #t))))
  595. items))
  596. (with-monad %store-monad
  597. (match (manifest-entry-item entry)
  598. ((? package? package)
  599. (match (cons (list (package-name package) package)
  600. (package-transitive-inputs package))
  601. (((labels inputs . _) ...)
  602. (return (find-among-inputs inputs)))))
  603. ((? string? item)
  604. (mlet %store-monad ((refs (references* item)))
  605. (return (find-among-store-items refs)))))))
  606. (anym %store-monad
  607. entry-lookup-package (manifest-entries manifest)))
  608. (define (info-dir-file manifest)
  609. "Return a derivation that builds the 'dir' file for all the entries of
  610. MANIFEST."
  611. (define texinfo ;lazy reference
  612. (module-ref (resolve-interface '(gnu packages texinfo)) 'texinfo))
  613. (define gzip ;lazy reference
  614. (module-ref (resolve-interface '(gnu packages compression)) 'gzip))
  615. (define build
  616. (with-imported-modules '((guix build utils))
  617. #~(begin
  618. (use-modules (guix build utils)
  619. (srfi srfi-1) (srfi srfi-26)
  620. (ice-9 ftw))
  621. (define (info-file? file)
  622. (or (string-suffix? ".info" file)
  623. (string-suffix? ".info.gz" file)))
  624. (define (info-files top)
  625. (let ((infodir (string-append top "/share/info")))
  626. (map (cut string-append infodir "/" <>)
  627. (or (scandir infodir info-file?) '()))))
  628. (define (install-info info)
  629. (setenv "PATH" (string-append #+gzip "/bin")) ;for info.gz files
  630. (zero?
  631. (system* (string-append #+texinfo "/bin/install-info") "--silent"
  632. info (string-append #$output "/share/info/dir"))))
  633. (mkdir-p (string-append #$output "/share/info"))
  634. (exit (every install-info
  635. (append-map info-files
  636. '#$(manifest-inputs manifest)))))))
  637. (gexp->derivation "info-dir" build
  638. #:local-build? #t
  639. #:substitutable? #f))
  640. (define (ghc-package-cache-file manifest)
  641. "Return a derivation that builds the GHC 'package.cache' file for all the
  642. entries of MANIFEST, or #f if MANIFEST does not have any GHC packages."
  643. (define ghc ;lazy reference
  644. (module-ref (resolve-interface '(gnu packages haskell)) 'ghc))
  645. (define build
  646. (with-imported-modules '((guix build utils))
  647. #~(begin
  648. (use-modules (guix build utils)
  649. (srfi srfi-1) (srfi srfi-26)
  650. (ice-9 ftw))
  651. (define ghc-name-version
  652. (let* ((base (basename #+ghc)))
  653. (string-drop base
  654. (+ 1 (string-index base #\-)))))
  655. (define db-subdir
  656. (string-append "lib/" ghc-name-version "/package.conf.d"))
  657. (define db-dir
  658. (string-append #$output "/" db-subdir))
  659. (define (conf-files top)
  660. (let ((db (string-append top "/" db-subdir)))
  661. (if (file-exists? db)
  662. (find-files db "\\.conf$")
  663. '())))
  664. (define (copy-conf-file conf)
  665. (let ((base (basename conf)))
  666. (copy-file conf (string-append db-dir "/" base))))
  667. (system* (string-append #+ghc "/bin/ghc-pkg") "init" db-dir)
  668. (for-each copy-conf-file
  669. (append-map conf-files
  670. (delete-duplicates
  671. '#$(manifest-inputs manifest))))
  672. (let ((success
  673. (zero?
  674. (system* (string-append #+ghc "/bin/ghc-pkg") "recache"
  675. (string-append "--package-db=" db-dir)))))
  676. (for-each delete-file (find-files db-dir "\\.conf$"))
  677. (exit success)))))
  678. (with-monad %store-monad
  679. ;; Don't depend on GHC when there's nothing to do.
  680. (if (any (cut string-prefix? "ghc" <>)
  681. (map manifest-entry-name (manifest-entries manifest)))
  682. (gexp->derivation "ghc-package-cache" build
  683. #:local-build? #t
  684. #:substitutable? #f)
  685. (return #f))))
  686. (define (ca-certificate-bundle manifest)
  687. "Return a derivation that builds a single-file bundle containing the CA
  688. certificates in the /etc/ssl/certs sub-directories of the packages in
  689. MANIFEST. Single-file bundles are required by programs such as Git and Lynx."
  690. ;; See <http://lists.gnu.org/archive/html/guix-devel/2015-02/msg00429.html>
  691. ;; for a discussion.
  692. (define glibc-utf8-locales ;lazy reference
  693. (module-ref (resolve-interface '(gnu packages base)) 'glibc-utf8-locales))
  694. (define build
  695. (with-imported-modules '((guix build utils))
  696. #~(begin
  697. (use-modules (guix build utils)
  698. (rnrs io ports)
  699. (srfi srfi-1)
  700. (srfi srfi-26)
  701. (ice-9 ftw)
  702. (ice-9 match))
  703. (define (pem-file? file)
  704. (string-suffix? ".pem" file))
  705. (define (ca-files top)
  706. (let ((cert-dir (string-append top "/etc/ssl/certs")))
  707. (map (cut string-append cert-dir "/" <>)
  708. (or (scandir cert-dir pem-file?) '()))))
  709. (define (concatenate-files files result)
  710. "Make RESULT the concatenation of all of FILES."
  711. (define (dump file port)
  712. (display (call-with-input-file file get-string-all)
  713. port)
  714. (newline port)) ;required, see <https://bugs.debian.org/635570>
  715. (call-with-output-file result
  716. (lambda (port)
  717. (for-each (cut dump <> port) files))))
  718. ;; Some file names in the NSS certificates are UTF-8 encoded so
  719. ;; install a UTF-8 locale.
  720. (setenv "LOCPATH"
  721. (string-append #+glibc-utf8-locales "/lib/locale/"
  722. #+(package-version glibc-utf8-locales)))
  723. (setlocale LC_ALL "en_US.utf8")
  724. (match (append-map ca-files '#$(manifest-inputs manifest))
  725. (()
  726. ;; Since there are no CA files, just create an empty directory. Do
  727. ;; not create the etc/ssl/certs sub-directory, since that would
  728. ;; wrongfully lead to a message about 'SSL_CERT_DIR' needing to be
  729. ;; defined.
  730. (mkdir #$output)
  731. #t)
  732. ((ca-files ...)
  733. (let ((result (string-append #$output "/etc/ssl/certs")))
  734. (mkdir-p result)
  735. (concatenate-files ca-files
  736. (string-append result
  737. "/ca-certificates.crt"))
  738. #t))))))
  739. (gexp->derivation "ca-certificate-bundle" build
  740. #:local-build? #t
  741. #:substitutable? #f))
  742. (define (gtk-icon-themes manifest)
  743. "Return a derivation that unions all icon themes from manifest entries and
  744. creates the GTK+ 'icon-theme.cache' file for each theme."
  745. (define gtk+ ; lazy reference
  746. (module-ref (resolve-interface '(gnu packages gtk)) 'gtk+))
  747. (mlet %store-monad ((%gtk+ (manifest-lookup-package manifest "gtk+"))
  748. ;; XXX: Can't use gtk-update-icon-cache corresponding
  749. ;; to the gtk+ referenced by 'manifest'. Because
  750. ;; '%gtk+' can be either a package or store path, and
  751. ;; there's no way to get the "bin" output for the later.
  752. (gtk-update-icon-cache
  753. -> #~(string-append #+gtk+:bin
  754. "/bin/gtk-update-icon-cache")))
  755. (define build
  756. (with-imported-modules '((guix build utils)
  757. (guix build union)
  758. (guix build profiles)
  759. (guix search-paths)
  760. (guix records))
  761. #~(begin
  762. (use-modules (guix build utils)
  763. (guix build union)
  764. (guix build profiles)
  765. (srfi srfi-26)
  766. (ice-9 ftw))
  767. (let* ((destdir (string-append #$output "/share/icons"))
  768. (icondirs (filter file-exists?
  769. (map (cut string-append <> "/share/icons")
  770. '#$(manifest-inputs manifest)))))
  771. ;; Union all the icons.
  772. (mkdir-p (string-append #$output "/share"))
  773. (union-build destdir icondirs
  774. #:log-port (%make-void-port "w"))
  775. ;; Update the 'icon-theme.cache' file for each icon theme.
  776. (for-each
  777. (lambda (theme)
  778. (let ((dir (string-append destdir "/" theme)))
  779. ;; Occasionally DESTDIR contains plain files, such as
  780. ;; "abiword_48.png". Ignore these.
  781. (when (file-is-directory? dir)
  782. (ensure-writable-directory dir)
  783. (system* #+gtk-update-icon-cache "-t" dir "--quiet"))))
  784. (scandir destdir (negate (cut member <> '("." "..")))))))))
  785. ;; Don't run the hook when there's nothing to do.
  786. (if %gtk+
  787. (gexp->derivation "gtk-icon-themes" build
  788. #:local-build? #t
  789. #:substitutable? #f)
  790. (return #f))))
  791. (define (gtk-im-modules manifest)
  792. "Return a derivation that builds the cache files for input method modules
  793. for both major versions of GTK+."
  794. (mlet %store-monad ((gtk+ (manifest-lookup-package manifest "gtk+" "3"))
  795. (gtk+-2 (manifest-lookup-package manifest "gtk+" "2")))
  796. (define (build gtk gtk-version query)
  797. (let ((major (string-take gtk-version 1)))
  798. (with-imported-modules '((guix build utils)
  799. (guix build union)
  800. (guix build profiles)
  801. (guix search-paths)
  802. (guix records))
  803. #~(begin
  804. (use-modules (guix build utils)
  805. (guix build union)
  806. (guix build profiles)
  807. (ice-9 popen)
  808. (srfi srfi-1)
  809. (srfi srfi-26))
  810. (let* ((prefix (string-append "/lib/gtk-" #$major ".0/"
  811. #$gtk-version))
  812. (destdir (string-append #$output prefix))
  813. (moddirs (cons (string-append #$gtk prefix "/immodules")
  814. (filter file-exists?
  815. (map (cut string-append <> prefix "/immodules")
  816. '#$(manifest-inputs manifest)))))
  817. (modules (append-map (cut find-files <> "\\.so$")
  818. moddirs)))
  819. ;; Generate a new immodules cache file.
  820. (mkdir-p (string-append #$output prefix))
  821. (let ((pipe (apply open-pipe* OPEN_READ #$query modules))
  822. (outfile (string-append #$output prefix
  823. "/immodules-gtk" #$major ".cache")))
  824. (dynamic-wind
  825. (const #t)
  826. (lambda ()
  827. (call-with-output-file outfile
  828. (lambda (out)
  829. (while (not (eof-object? (peek-char pipe)))
  830. (write-char (read-char pipe) out))))
  831. #t)
  832. (lambda ()
  833. (close-pipe pipe)))))))))
  834. ;; Don't run the hook when there's nothing to do.
  835. (let* ((pkg-gtk+ (module-ref ; lazy reference
  836. (resolve-interface '(gnu packages gtk)) 'gtk+))
  837. (gexp #~(begin
  838. #$(if gtk+
  839. (build
  840. gtk+ "3.0.0"
  841. ;; Use 'gtk-query-immodules-3.0' from the 'bin'
  842. ;; output of latest gtk+ package.
  843. #~(string-append
  844. #$pkg-gtk+:bin "/bin/gtk-query-immodules-3.0"))
  845. #t)
  846. #$(if gtk+-2
  847. (build
  848. gtk+-2 "2.10.0"
  849. #~(string-append
  850. #$gtk+-2 "/bin/gtk-query-immodules-2.0"))
  851. #t))))
  852. (if (or gtk+ gtk+-2)
  853. (gexp->derivation "gtk-im-modules" gexp
  854. #:local-build? #t
  855. #:substitutable? #f)
  856. (return #f)))))
  857. (define (xdg-desktop-database manifest)
  858. "Return a derivation that builds the @file{mimeinfo.cache} database from
  859. desktop files. It's used to query what applications can handle a given
  860. MIME type."
  861. (define desktop-file-utils ; lazy reference
  862. (module-ref (resolve-interface '(gnu packages freedesktop))
  863. 'desktop-file-utils))
  864. (mlet %store-monad ((glib
  865. (manifest-lookup-package
  866. manifest "glib")))
  867. (define build
  868. (with-imported-modules '((guix build utils)
  869. (guix build union))
  870. #~(begin
  871. (use-modules (srfi srfi-26)
  872. (guix build utils)
  873. (guix build union))
  874. (let* ((destdir (string-append #$output "/share/applications"))
  875. (appdirs (filter file-exists?
  876. (map (cut string-append <>
  877. "/share/applications")
  878. '#$(manifest-inputs manifest))))
  879. (update-desktop-database (string-append
  880. #+desktop-file-utils
  881. "/bin/update-desktop-database")))
  882. (mkdir-p (string-append #$output "/share"))
  883. (union-build destdir appdirs
  884. #:log-port (%make-void-port "w"))
  885. (exit (zero? (system* update-desktop-database destdir)))))))
  886. ;; Don't run the hook when 'glib' is not referenced.
  887. (if glib
  888. (gexp->derivation "xdg-desktop-database" build
  889. #:local-build? #t
  890. #:substitutable? #f)
  891. (return #f))))
  892. (define (xdg-mime-database manifest)
  893. "Return a derivation that builds the @file{mime.cache} database from manifest
  894. entries. It's used to query the MIME type of a given file."
  895. (define shared-mime-info ; lazy reference
  896. (module-ref (resolve-interface '(gnu packages gnome)) 'shared-mime-info))
  897. (mlet %store-monad ((glib
  898. (manifest-lookup-package
  899. manifest "glib")))
  900. (define build
  901. (with-imported-modules '((guix build utils)
  902. (guix build union))
  903. #~(begin
  904. (use-modules (srfi srfi-26)
  905. (guix build utils)
  906. (guix build union))
  907. (let* ((datadir (string-append #$output "/share"))
  908. (destdir (string-append datadir "/mime"))
  909. (pkgdirs (filter file-exists?
  910. (map (cut string-append <>
  911. "/share/mime/packages")
  912. (cons #+shared-mime-info
  913. '#$(manifest-inputs manifest)))))
  914. (update-mime-database (string-append
  915. #+shared-mime-info
  916. "/bin/update-mime-database")))
  917. (mkdir-p destdir)
  918. (union-build (string-append destdir "/packages") pkgdirs
  919. #:log-port (%make-void-port "w"))
  920. (setenv "XDG_DATA_HOME" datadir)
  921. (exit (zero? (system* update-mime-database destdir)))))))
  922. ;; Don't run the hook when there are no GLib based applications.
  923. (if glib
  924. (gexp->derivation "xdg-mime-database" build
  925. #:local-build? #t
  926. #:substitutable? #f)
  927. (return #f))))
  928. ;; Several font packages may install font files into same directory, so
  929. ;; fonts.dir and fonts.scale file should be generated here, instead of in
  930. ;; packages.
  931. (define (fonts-dir-file manifest)
  932. "Return a derivation that builds the @file{fonts.dir} and @file{fonts.scale}
  933. files for the fonts of the @var{manifest} entries."
  934. (define mkfontscale
  935. (module-ref (resolve-interface '(gnu packages xorg)) 'mkfontscale))
  936. (define mkfontdir
  937. (module-ref (resolve-interface '(gnu packages xorg)) 'mkfontdir))
  938. (define build
  939. #~(begin
  940. (use-modules (srfi srfi-26)
  941. (guix build utils)
  942. (guix build union))
  943. (let ((fonts-dirs (filter file-exists?
  944. (map (cut string-append <>
  945. "/share/fonts")
  946. '#$(manifest-inputs manifest)))))
  947. (mkdir #$output)
  948. (if (null? fonts-dirs)
  949. (exit #t)
  950. (let* ((share-dir (string-append #$output "/share"))
  951. (fonts-dir (string-append share-dir "/fonts"))
  952. (mkfontscale (string-append #+mkfontscale
  953. "/bin/mkfontscale"))
  954. (mkfontdir (string-append #+mkfontdir
  955. "/bin/mkfontdir"))
  956. (empty-file? (lambda (filename)
  957. (call-with-ascii-input-file filename
  958. (lambda (p)
  959. (eqv? #\0 (read-char p))))))
  960. (fonts-dir-file "fonts.dir")
  961. (fonts-scale-file "fonts.scale"))
  962. (mkdir-p share-dir)
  963. ;; Create all sub-directories, because we may create fonts.dir
  964. ;; and fonts.scale files in the sub-directories.
  965. (union-build fonts-dir fonts-dirs
  966. #:log-port (%make-void-port "w")
  967. #:create-all-directories? #t)
  968. (let ((directories (find-files fonts-dir
  969. (lambda (file stat)
  970. (eq? 'directory (stat:type stat)))
  971. #:directories? #t)))
  972. (for-each (lambda (dir)
  973. (with-directory-excursion dir
  974. (when (file-exists? fonts-scale-file)
  975. (delete-file fonts-scale-file))
  976. (when (file-exists? fonts-dir-file)
  977. (delete-file fonts-dir-file))
  978. (unless (and (zero? (system* mkfontscale))
  979. (zero? (system* mkfontdir)))
  980. (exit #f))
  981. (when (and (file-exists? fonts-scale-file)
  982. (empty-file? fonts-scale-file))
  983. (delete-file fonts-scale-file))
  984. (when (and (file-exists? fonts-dir-file)
  985. (empty-file? fonts-dir-file))
  986. (delete-file fonts-dir-file))))
  987. directories)))))))
  988. (gexp->derivation "fonts-dir" build
  989. #:modules '((guix build utils)
  990. (guix build union)
  991. (srfi srfi-26))
  992. #:local-build? #t
  993. #:substitutable? #f))
  994. (define (manual-database manifest)
  995. "Return a derivation that builds the manual page database (\"mandb\") for
  996. the entries in MANIFEST."
  997. (define gdbm-ffi
  998. (module-ref (resolve-interface '(gnu packages guile))
  999. 'guile-gdbm-ffi))
  1000. (define zlib
  1001. (module-ref (resolve-interface '(gnu packages compression)) 'zlib))
  1002. (define config.scm
  1003. (scheme-file "config.scm"
  1004. #~(begin
  1005. (define-module (guix config)
  1006. #:export (%libz))
  1007. (define %libz
  1008. #+(file-append zlib "/lib/libz")))))
  1009. (define modules
  1010. (cons `((guix config) => ,config.scm)
  1011. (delete '(guix config)
  1012. (source-module-closure `((guix build utils)
  1013. (guix man-db))))))
  1014. (define build
  1015. (with-imported-modules modules
  1016. #~(begin
  1017. (add-to-load-path (string-append #$gdbm-ffi "/share/guile/site/"
  1018. (effective-version)))
  1019. (use-modules (guix man-db)
  1020. (guix build utils)
  1021. (srfi srfi-1)
  1022. (srfi srfi-19))
  1023. (define (compute-entries)
  1024. (append-map (lambda (directory)
  1025. (let ((man (string-append directory "/share/man")))
  1026. (if (directory-exists? man)
  1027. (mandb-entries man)
  1028. '())))
  1029. '#$(manifest-inputs manifest)))
  1030. (define man-directory
  1031. (string-append #$output "/share/man"))
  1032. (mkdir-p man-directory)
  1033. (format #t "Creating manual page database...~%")
  1034. (force-output)
  1035. (let* ((start (current-time))
  1036. (entries (compute-entries))
  1037. (_ (write-mandb-database (string-append man-directory
  1038. "/index.db")
  1039. entries))
  1040. (duration (time-difference (current-time) start)))
  1041. (format #t "~a entries processed in ~,1f s~%"
  1042. (length entries)
  1043. (+ (time-second duration)
  1044. (* (time-nanosecond duration) (expt 10 -9))))
  1045. (force-output)))))
  1046. (gexp->derivation "manual-database" build
  1047. ;; Work around GDBM 1.13 issue whereby uninitialized bytes
  1048. ;; get written to disk:
  1049. ;; <https://debbugs.gnu.org/cgi/bugreport.cgi?bug=29654#23>.
  1050. #:env-vars `(("MALLOC_PERTURB_" . "1"))
  1051. #:local-build? #t))
  1052. (define %default-profile-hooks
  1053. ;; This is the list of derivation-returning procedures that are called by
  1054. ;; default when making a non-empty profile.
  1055. (list info-dir-file
  1056. manual-database
  1057. fonts-dir-file
  1058. ghc-package-cache-file
  1059. ca-certificate-bundle
  1060. gtk-icon-themes
  1061. gtk-im-modules
  1062. xdg-desktop-database
  1063. xdg-mime-database))
  1064. (define* (profile-derivation manifest
  1065. #:key
  1066. (hooks %default-profile-hooks)
  1067. (locales? #t)
  1068. (allow-collisions? #f)
  1069. system target)
  1070. "Return a derivation that builds a profile (aka. 'user environment') with
  1071. the given MANIFEST. The profile includes additional derivations returned by
  1072. the monadic procedures listed in HOOKS--such as an Info 'dir' file, etc.
  1073. Unless ALLOW-COLLISIONS? is true, a '&profile-collision-error' is raised if
  1074. entries in MANIFEST collide (for instance if there are two same-name packages
  1075. with a different version number.)
  1076. When LOCALES? is true, the build is performed under a UTF-8 locale; this adds
  1077. a dependency on the 'glibc-utf8-locales' package.
  1078. When TARGET is true, it must be a GNU triplet, and the packages in MANIFEST
  1079. are cross-built for TARGET."
  1080. (mlet* %store-monad ((system (if system
  1081. (return system)
  1082. (current-system)))
  1083. (ok? (if allow-collisions?
  1084. (return #t)
  1085. (check-for-collisions manifest system
  1086. #:target target)))
  1087. (extras (if (null? (manifest-entries manifest))
  1088. (return '())
  1089. (sequence %store-monad
  1090. (map (lambda (hook)
  1091. (hook manifest))
  1092. hooks)))))
  1093. (define inputs
  1094. (append (filter-map (lambda (drv)
  1095. (and (derivation? drv)
  1096. (gexp-input drv)))
  1097. extras)
  1098. (manifest-inputs manifest)))
  1099. (define glibc-utf8-locales ;lazy reference
  1100. (module-ref (resolve-interface '(gnu packages base))
  1101. 'glibc-utf8-locales))
  1102. (define set-utf8-locale
  1103. ;; Some file names (e.g., in 'nss-certs') are UTF-8 encoded so
  1104. ;; install a UTF-8 locale.
  1105. #~(begin
  1106. (setenv "LOCPATH"
  1107. #$(file-append glibc-utf8-locales "/lib/locale/"
  1108. (package-version glibc-utf8-locales)))
  1109. (setlocale LC_ALL "en_US.utf8")))
  1110. (define builder
  1111. (with-imported-modules '((guix build profiles)
  1112. (guix build union)
  1113. (guix build utils)
  1114. (guix search-paths)
  1115. (guix records))
  1116. #~(begin
  1117. (use-modules (guix build profiles)
  1118. (guix search-paths)
  1119. (srfi srfi-1))
  1120. (setvbuf (current-output-port) _IOLBF)
  1121. (setvbuf (current-error-port) _IOLBF)
  1122. #+(if locales? set-utf8-locale #t)
  1123. (define search-paths
  1124. ;; Search paths of MANIFEST's packages, converted back to their
  1125. ;; record form.
  1126. (map sexp->search-path-specification
  1127. (delete-duplicates
  1128. '#$(map search-path-specification->sexp
  1129. (append-map manifest-entry-search-paths
  1130. (manifest-entries manifest))))))
  1131. (build-profile #$output '#$inputs
  1132. #:manifest '#$(manifest->gexp manifest)
  1133. #:search-paths search-paths))))
  1134. (gexp->derivation "profile" builder
  1135. #:system system
  1136. #:target target
  1137. ;; Don't complain about _IO* on Guile 2.2.
  1138. #:env-vars '(("GUILE_WARN_DEPRECATED" . "no"))
  1139. ;; Not worth offloading.
  1140. #:local-build? #t
  1141. ;; Disable substitution because it would trigger a
  1142. ;; connection to the substitute server, which is likely
  1143. ;; to have no substitute to offer.
  1144. #:substitutable? #f)))
  1145. (define (profile-regexp profile)
  1146. "Return a regular expression that matches PROFILE's name and number."
  1147. (make-regexp (string-append "^" (regexp-quote (basename profile))
  1148. "-([0-9]+)")))
  1149. (define (generation-number profile)
  1150. "Return PROFILE's number or 0. An absolute file name must be used."
  1151. (or (and=> (false-if-exception (regexp-exec (profile-regexp profile)
  1152. (basename (readlink profile))))
  1153. (compose string->number (cut match:substring <> 1)))
  1154. 0))
  1155. (define (generation-numbers profile)
  1156. "Return the sorted list of generation numbers of PROFILE, or '(0) if no
  1157. former profiles were found."
  1158. (match (scandir (dirname profile)
  1159. (cute regexp-exec (profile-regexp profile) <>))
  1160. (#f ; no profile directory
  1161. '(0))
  1162. (() ; no profiles
  1163. '(0))
  1164. ((profiles ...) ; former profiles around
  1165. (sort (map (compose string->number
  1166. (cut match:substring <> 1)
  1167. (cute regexp-exec (profile-regexp profile) <>))
  1168. profiles)
  1169. <))))
  1170. (define (profile-generations profile)
  1171. "Return a list of PROFILE's generations."
  1172. (let ((generations (generation-numbers profile)))
  1173. (if (equal? generations '(0))
  1174. '()
  1175. generations)))
  1176. (define (relative-generation-spec->number profile spec)
  1177. "Return PROFILE's generation specified by SPEC, which is a string. The SPEC
  1178. may be a N, -N, or +N, where N is a number. If the spec is N, then the number
  1179. returned is N. If it is -N, then the number returned is the profile's current
  1180. generation number minus N. If it is +N, then the number returned is the
  1181. profile's current generation number plus N. Return #f if there is no such
  1182. generation."
  1183. (let ((number (string->number spec)))
  1184. (and number
  1185. (case (string-ref spec 0)
  1186. ((#\+ #\-)
  1187. (relative-generation profile number))
  1188. (else (if (memv number (profile-generations profile))
  1189. number
  1190. #f))))))
  1191. (define* (relative-generation profile shift #:optional
  1192. (current (generation-number profile)))
  1193. "Return PROFILE's generation shifted from the CURRENT generation by SHIFT.
  1194. SHIFT is a positive or negative number.
  1195. Return #f if there is no such generation."
  1196. (let* ((abs-shift (abs shift))
  1197. (numbers (profile-generations profile))
  1198. (from-current (memq current
  1199. (if (negative? shift)
  1200. (reverse numbers)
  1201. numbers))))
  1202. (and from-current
  1203. (< abs-shift (length from-current))
  1204. (list-ref from-current abs-shift))))
  1205. (define* (previous-generation-number profile #:optional
  1206. (number (generation-number profile)))
  1207. "Return the number of the generation before generation NUMBER of
  1208. PROFILE, or 0 if none exists. It could be NUMBER - 1, but it's not the
  1209. case when generations have been deleted (there are \"holes\")."
  1210. (or (relative-generation profile -1 number)
  1211. 0))
  1212. (define (generation-file-name profile generation)
  1213. "Return the file name for PROFILE's GENERATION."
  1214. (format #f "~a-~a-link" profile generation))
  1215. (define (generation-time profile number)
  1216. "Return the creation time of a generation in the UTC format."
  1217. (make-time time-utc 0
  1218. (stat:ctime (stat (generation-file-name profile number)))))
  1219. (define (link-to-empty-profile store generation)
  1220. "Link GENERATION, a string, to the empty profile. An error is raised if
  1221. that fails."
  1222. (let* ((drv (run-with-store store
  1223. (profile-derivation (manifest '())
  1224. #:locales? #f)))
  1225. (prof (derivation->output-path drv "out")))
  1226. (build-derivations store (list drv))
  1227. (switch-symlinks generation prof)))
  1228. (define (switch-to-generation profile number)
  1229. "Atomically switch PROFILE to the generation NUMBER. Return the number of
  1230. the generation that was current before switching."
  1231. (let ((current (generation-number profile))
  1232. (generation (generation-file-name profile number)))
  1233. (cond ((not (file-exists? profile))
  1234. (raise (condition (&profile-not-found-error
  1235. (profile profile)))))
  1236. ((not (file-exists? generation))
  1237. (raise (condition (&missing-generation-error
  1238. (profile profile)
  1239. (generation number)))))
  1240. (else
  1241. (switch-symlinks profile generation)
  1242. current))))
  1243. (define (switch-to-previous-generation profile)
  1244. "Atomically switch PROFILE to the previous generation. Return the former
  1245. generation number and the current one."
  1246. (let ((previous (previous-generation-number profile)))
  1247. (values (switch-to-generation profile previous)
  1248. previous)))
  1249. (define (roll-back store profile)
  1250. "Roll back to the previous generation of PROFILE. Return the number of the
  1251. generation that was current before switching and the new generation number."
  1252. (let* ((number (generation-number profile))
  1253. (previous-number (previous-generation-number profile number))
  1254. (previous-generation (generation-file-name profile previous-number)))
  1255. (cond ((not (file-exists? profile)) ;invalid profile
  1256. (raise (condition (&profile-not-found-error
  1257. (profile profile)))))
  1258. ((zero? number) ;empty profile
  1259. (values number number))
  1260. ((or (zero? previous-number) ;going to emptiness
  1261. (not (file-exists? previous-generation)))
  1262. (link-to-empty-profile store previous-generation)
  1263. (switch-to-previous-generation profile))
  1264. (else ;anything else
  1265. (switch-to-previous-generation profile)))))
  1266. (define (delete-generation store profile number)
  1267. "Delete generation with NUMBER from PROFILE. Return the file name of the
  1268. generation that has been deleted, or #f if nothing was done (for instance
  1269. because the NUMBER is zero.)"
  1270. (define (delete-and-return)
  1271. (let ((generation (generation-file-name profile number)))
  1272. (delete-file generation)
  1273. generation))
  1274. (let* ((current-number (generation-number profile))
  1275. (previous-number (previous-generation-number profile number))
  1276. (previous-generation (generation-file-name profile previous-number)))
  1277. (cond ((zero? number) #f) ;do not delete generation 0
  1278. ((and (= number current-number)
  1279. (not (file-exists? previous-generation)))
  1280. (link-to-empty-profile store previous-generation)
  1281. (switch-to-previous-generation profile)
  1282. (delete-and-return))
  1283. ((= number current-number)
  1284. (roll-back store profile)
  1285. (delete-and-return))
  1286. (else
  1287. (delete-and-return)))))
  1288. ;;; profiles.scm ends here