diff --git a/README.md b/README.md
index 6bd9d93..0a0e610 100644
--- a/README.md
+++ b/README.md
@@ -41,21 +41,25 @@ section of this project:
### Making some room in your home directory
-It is recommended to use this in a new install, when your home directory is
-still pretty empty. Since your home directory will be made into a Guix profile,
-you first want to create a new directory for your user data, for instance as
-root:
+Your home directory will be completely taken over by Guix. In particular, when
+using the home manager, your home directory is entirely read-only. A read-only
+home directory is not very useful though, so users of the home manager will have
+to use a separate directory for their documents, caches and states. This is
+typically `/data/alice` for user alice.
+
+It is not required to set up that directory beforehand, but if you do, you will
+not be able to use the home manager until you have completely wiped-out your
+home directory (i.e. transfered it to the new directory). If the directory
+does not yet exist, your current home directory is automatically renamed to
+that directory, and the home manager starts working.
+
+Basically, you will run (as root):
```bash
-mkdir -p /data/alice
-chown alice: /data/alice
+mkdir /data
+mv /home/alice /data/alice
```
-if your user is named alice. Then, move all your data over to that directory.
-Do not transfer your configuration, it will be useless. That transfering of data
-is the reason why it's simpler to start with an empty home: there is no data to
-transfer ;)
-
Once that is done, some parts of your home directory will still have to be
read-write. This is mostly `~/.cache`, `~/.local` but also `~/.guix-profile` and
`~/.config/guix`. Inside your new data directory, create them like this, as your
@@ -66,6 +70,13 @@ cd /data/alice
mkdir-p .local/share .cache .config
```
+Since you have moved your entire home directory, make sure you can still access
+your own copy of guix and your user profile by (temporarily) setting your
+`$PATH` (make sure it starts with `/data/alice/.config/guix/current/bin`) and
+by sourcing the profile with `export GUIX_PROFILE=/data/alice/.guix-profile;
+source $GUIX_PROFILE/etc/profile`. You might also need to run `hash -r`
+(no output) for bash to clear all its memorized binary locations.
+
### Creating the first home generation
To create your first home configuration, you must create a configuration file.
@@ -74,8 +85,8 @@ For instance, create `/data/alice/.config/guix/home.scm`:
```scheme
(use-modules (home))
-(home "/data/alice"
- '())
+(home
+ (data-directory "/data/alice"))
```
This will generate a completely empty home, except for essential configurations,
@@ -87,23 +98,7 @@ To build your first generation of your home environment, run as your regular
user:
```bash
-guix package -p /var/guix/profiles/per-user/alice/home \
- -f /data/alice/.config/guix/home.scm
-```
-
-Still as your regular user, copy your `~/.config/guix` to your data directory.
-This will ensure you can still use Guix after you switch to the managed home
-profile:
-
-```bash
-cp -ar ~/.config/guix /data/alice/.config/
-```
-
-Finaly, switch to the managed home profile as root:
-
-```bash
-mv /home/alice{,.bak} # keep a backup in case something goes wrong
-ln -sv /var/guix/profiles/per-user/alice/home /home/alice
+guix home reconfigure /data/alice/.config/guix/home.scm
```
That's it!
@@ -121,4 +116,4 @@ over the world!
If you are less into code, we welcome contributions in the form of documentation,
translation, issues, reviews, tips and tricks. Do not hesitate to get in touch if
-you have an idea or want to help in any way!
\ No newline at end of file
+you have an idea or want to help in any way!
diff --git a/doc/README.md b/doc/README.md
index 1ade3a5..384294a 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -36,12 +36,14 @@ with "-home", you know that you can use it in the list of configurations, like
this:
```scheme
-(home "/data/alice"
- (list (something-home ...)
- (something-else-home ...)
- (other-stuff-home ...)
- (yet-another-config-home ...)
- ...))
+(home
+ (data-directory "/data/alice")
+ (configurations
+ (list (something-home ...)
+ (something-else-home ...)
+ (other-stuff-home ...)
+ (yet-another-config-home ...)
+ ...)))
```
#### Desktop and Window Managers
@@ -69,4 +71,4 @@ Unfortunately, that means you cannot configure pulseaudio through Guix. You can
also add a similar line to your desktop or window manager's configuration to
instruct it to start pulseaudio. However, pulseaudio sometimes crashes for non
obvious reasons, and no graphical program will be able to restart it automatically
-with a proper configuration.
\ No newline at end of file
+with a proper configuration.
diff --git a/doc/general.md b/doc/general.md
index f6abd1b..23fd91f 100644
--- a/doc/general.md
+++ b/doc/general.md
@@ -66,8 +66,8 @@ Build-side Utilities
The `(home build utils)` extends the utilities provided by `(guix build utils)`
with the following procedure:
-**Scheme Procedure**: (home-file outputs path ...)
+**Scheme Procedure**: (home-file output path ...)
-Return the complete path to the output of a package being defined by adding
-"/" between each component of _path_. _outputs_ is the content of `%build-output`
-in the package definition. This is not very useful for end users.
+Return the complete path to the output of a gexp being defined by adding
+"/" between each component of _path_. _output_ is the content of `#$output`
+in the package definition.
diff --git a/doc/install.md b/doc/install.md
index 16b8149..bda7456 100644
--- a/doc/install.md
+++ b/doc/install.md
@@ -28,21 +28,25 @@ Usage
### Making some room in your home directory
-It is recommended to use this in a new install, when your home directory is
-still pretty empty. Since your home directory will be made into a Guix profile,
-you first want to create a new directory for your user data, for instance as
-root:
+Your home directory will be completely taken over by Guix. In particular, when
+using the home manager, your home directory is entirely read-only. A read-only
+home directory is not very useful though, so users of the home manager will have
+to use a separate directory for their documents, caches and states. This is
+typically `/data/alice` for user alice.
+
+It is not required to set up that directory beforehand, but if you do, you will
+not be able to use the home manager until you have completely wiped-out your
+home directory (i.e. transfered it to the new directory). If the directory
+does not yet exist, your current home directory is automatically renamed to
+that directory, and the home manager starts working.
+
+Basically, you will run (as root):
```bash
-mkdir -p /data/alice
-chown alice: /data/alice
+mkdir /data
+mv /home/alice /data/alice
```
-if your user is named alice. Then, move all your data over to that directory.
-Do not transfer your configuration, it will be useless. That transfering of data
-is the reason why it's simpler to start with an empty home: there is no data to
-transfer ;)
-
Once that is done, some parts of your home directory will still have to be
read-write. This is mostly `~/.cache`, `~/.local` but also `~/.guix-profile` and
`~/.config/guix`. Inside your new data directory, create them like this, as your
@@ -53,6 +57,13 @@ cd /data/alice
mkdir-p .local/share .cache .config
```
+Since you have moved your entire home directory, make sure you can still access
+your own copy of guix and your user profile by (temporarily) setting your
+`$PATH` (make sure it starts with `/data/alice/.config/guix/current/bin`) and
+by sourcing the profile with `export GUIX_PROFILE=/data/alice/.guix-profile;
+source $GUIX_PROFILE/etc/profile`. You might also need to run `hash -r`
+(no output) for bash to clear all its memorized binary locations.
+
### Creating the first home generation
To create your first home configuration, you must create a configuration file.
@@ -61,8 +72,8 @@ For instance, create `/data/alice/.config/guix/home.scm`:
```scheme
(use-modules (home))
-(home "/data/alice"
- '())
+(home
+ (data-directory "/data/alice"))
```
This will generate a completely empty home, except for essential configurations,
@@ -74,23 +85,7 @@ To build your first generation of your home environment, run as your regular
user:
```bash
-guix package -p /var/guix/profiles/per-user/alice/home \
- -f /data/alice/.config/guix/home.scm
+guix home reconfigure /data/alice/.config/guix/home.scm
```
-Still as your regular user, copy your `~/.config/guix` to your data directory.
-This will ensure you can still use Guix after you switch to the managed home
-profile:
-
-```bash
-cp -ar ~/.config/guix /data/alice/.config/
-```
-
-Finaly, switch to the managed home profile as root:
-
-```bash
-mv /home/alice{,.bak} # keep a backup in case something goes wrong
-ln -sv /var/guix/profiles/per-user/alice/home /home/alice
-```
-
-That's it!
\ No newline at end of file
+That's it!
diff --git a/guix/scripts/home.scm b/guix/scripts/home.scm
index d4b54ea..76a7fe5 100644
--- a/guix/scripts/home.scm
+++ b/guix/scripts/home.scm
@@ -17,9 +17,21 @@
;;; along with GNU Guix. If not, see .
(define-module (guix scripts home)
+ #:use-module (guix derivations)
+ #:use-module (guix grafts)
+ #:use-module (guix monads)
+ #:use-module (guix packages)
+ #:use-module (guix profiles)
#:use-module (guix scripts)
+ #:use-module (guix scripts build)
+ #:use-module (guix status)
+ #:use-module (guix store)
#:use-module (guix ui)
#:use-module (guix utils)
+ #:use-module (home)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-26)
#:use-module (srfi srfi-37)
#:export (guix-home))
@@ -37,8 +49,16 @@
(show-version-and-exit "guix edit")))))
(define (show-help)
- (display (G_ "Usage: guix home CONFIG
-Manage a user home environment according to CONFIG\n"))
+ (display (G_ "Usage: guix home [OPTION ...] ACTION [ARG ...] [FILE]
+Manage a user home environment according to FILE and ACTION. Some actions
+support additional ARGs.\n"))
+ (display (G_ "The valid values for ACTION are:\n"))
+ (newline)
+ (display (G_ "\
+ reconfigure switch to or create a new home configuration\n"))
+ (display (G_ "\
+ build build the home configuration without installing anything\n"))
+ (show-build-options-help)
(display (G_ "
-h, --help display this help and exit"))
(display (G_ "
@@ -46,19 +66,276 @@ Manage a user home environment according to CONFIG\n"))
(newline)
(show-bug-report-information))
+(define %options
+ ;; Specifications of the command-line options.
+ (cons* (option '(#\h "help") #f #f
+ (lambda args
+ (show-help)
+ (exit 0)))
+ (option '(#\V "version") #f #f
+ (lambda args
+ (show-version-and-exit "guix system")))
+ (option '(#\e "expression") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'expression arg result)))
+ (option '(#\d "derivation") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'derivations-only? #t result)))
+ (option '("on-error") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'on-error (string->symbol arg)
+ result)))
+ (option '(#\t "file-system-type") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'file-system-type arg
+ result)))
+ (option '("image-size") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'image-size (size->number arg)
+ result)))
+ (option '(#\N "network") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'container-shared-network? #t result)))
+ (option '("no-bootloader" "no-grub") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'install-bootloader? #f result)))
+ (option '("full-boot") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'full-boot? #t result)))
+ (option '("skip-checks") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'skip-safety-checks? #t result)))
+
+ (option '("share") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'file-system-mapping
+ (specification->file-system-mapping arg #t)
+ result)))
+ (option '("expose") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'file-system-mapping
+ (specification->file-system-mapping arg #f)
+ result)))
+
+ (option '(#\n "dry-run") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'dry-run? #t (alist-cons 'graft? #f result))))
+ (option '(#\v "verbosity") #t #f
+ (lambda (opt name arg result)
+ (let ((level (string->number* arg)))
+ (alist-cons 'verbosity level
+ (alist-delete 'verbosity result)))))
+ (option '(#\s "system") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'system arg
+ (alist-delete 'system result eq?))))
+ (option '(#\r "root") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'gc-root arg result)))
+ %standard-build-options))
+
(define %default-options
- `((system . ,(%current-system))))
+ ;; Alist of default option values.
+ `((system . ,(%current-system))
+ (substitutes? . #t)
+ (build-hook? . #t)
+ (print-build-trace? . #t)
+ (print-extended-build-trace? . #t)
+ (multiplexed-build-output? . #t)
+ (graft? . #t)
+ (debug . 0)
+ (verbosity . #f) ;default
+ (file-system-type . "ext4")
+ (image-size . guess)
+ (install-bootloader? . #t)))
+
+;;;
+;;; Profiles
+;;;
+
+(define %user-module
+ ;; Module in which the machine description file is loaded.
+ (make-user-module '()))
+
+(define %home (getenv "HOME"))
+
+(define %current-home
+ (string-append %profile-directory "/home"))
+
+(define (ensure-home-profile data-directory)
+ "Ensures $HOME is a symlink to the profile. If it is not yet the case, move
+it to the @var{data-directory} directory, unless it already exists, in which case
+report an error."
+ (ensure-profile-directory)
+
+ (when %home %current-home
+ (let ((home (false-if-exception (lstat %home))))
+ (if home
+ (if (false-if-exception (lstat data-directory))
+ (rename-file %home data-directory)
+ (leav (G_ "Your $HOME directory (~a) is not a symlink to a profile,
+and it cannot be moved as ~a already exists on the filesystem.~%")
+ %home data-directory))
+ (symlink %current-home %home)))))
;;;
;;; Entry point.
;;;
+(define* (perform-action action home
+ #:key
+ dry-run? derivations-only?
+ use-substitutes?)
+ "Perform ACTION for HOME. When DERIVATIONS-ONLY? is true, print the
+derivation file name(s) without building anything."
+ (define println
+ (cut format #t "~a~%" <>))
+
+ (when (eq? action 'reconfigure)
+ (ensure-home-profile (home-data-directory home))
+ (maybe-suggest-running-guix-pull))
+
+ (with-store store
+ (let* ((drv (run-with-store store (home->derivation home)))
+ (profile (derivation->output-path drv)))
+ (show-what-to-build store (list drv)
+ #:use-substitutes? use-substitutes?
+ #:dry-run? dry-run?)
+
+ (unless (or dry-run? derivations-only?)
+ (begin
+ (build-derivations store (list drv))
+ (case action
+ ((reconfigure)
+ (newline)
+ (format #t (G_ "activating home...~%"))
+ (let ((number (generation-number %current-home))
+ (generation (generation-file-name %current-home number)))
+ (switch-symlinks generation profile)
+ (switch-symlinks %current-home generation)))
+ (else
+ (display profile)
+ (newline))))))))
+
+(define (process-action action args opts)
+ "Process ACTION, a sub-command, with the arguments are listed in ARGS.
+ACTION must be one of the sub-commands that takes an operating system
+declaration as an argument (a file name.) OPTS is the raw alist of options
+resulting from command-line parsing."
+ (define (ensure-home-configuration file-or-exp obj)
+ (unless (home? obj)
+ (leave (G_ "'~a' does not return a home configuration~%")
+ file-or-exp))
+ obj)
+
+ (let* ((file (match args
+ (() #f)
+ ((x . _) x)))
+ (expr (assoc-ref opts 'expression))
+ (system (assoc-ref opts 'system))
+ (home (ensure-home-configuration
+ (or file expr)
+ (cond
+ ((and expr file)
+ (leave
+ (G_ "both file and expression cannot be specified~%")))
+ (expr
+ (read/eval expr))
+ (file
+ (load* file %user-module
+ #:on-error (assoc-ref opts 'on-error)))
+ (else
+ (leave (G_ "no configuration specified~%"))))))
+
+ (dry? (assoc-ref opts 'dry-run?)))
+
+ (with-store store
+ (set-build-options-from-command-line store opts)
+
+ (set-guile-for-build (default-guile))
+
+ (case action
+ (else
+ (unless (eq? action 'build)
+ (warn-about-old-distro #:suggested-command
+ "guix home reconfigure"))
+
+ (perform-action action home
+ #:dry-run? dry?
+ #:derivations-only? (assoc-ref opts
+ 'derivations-only?)
+ #:use-substitutes? (assoc-ref opts 'substitutes?)))))
+ (warn-about-disk-space)))
+
+(define (resolve-subcommand name)
+ (let ((module (resolve-interface
+ `(guix scripts home ,(string->symbol name))))
+ (proc (string->symbol (string-append "guix-home-" name))))
+ (module-ref module proc)))
+
+(define (process-command command args opts)
+ "Process COMMAND, one of the 'guix system' sub-commands. ARGS is its
+argument list and OPTS is the option alist."
+ (case command
+ ;; The following commands do not need to use the store, and they do not need
+ ;; an operating system configuration file.
+ ;; The following commands need to use the store, but they do not need an
+ ;; operating system configuration file.
+ ;; The following commands need to use the store, and they also
+ ;; need an operating system configuration file.
+ (else (process-action command args opts))))
+
(define (guix-home . args)
+ (define (parse-sub-command arg result)
+ ;; Parse sub-command ARG and augment RESULT accordingly.
+ (if (assoc-ref result 'action)
+ (alist-cons 'argument arg result)
+ (let ((action (string->symbol arg)))
+ (case action
+ ((build reconfigure)
+ (alist-cons 'action action result))
+ (else (leave (G_ "~a: unknown action~%") action))))))
+
+ (define (match-pair car)
+ ;; Return a procedure that matches a pair with CAR.
+ (match-lambda
+ ((head . tail)
+ (and (eq? car head) tail))
+ (_ #f)))
+
+ (define (option-arguments opts)
+ ;; Extract the plain arguments from OPTS.
+ (let* ((args (reverse (filter-map (match-pair 'argument) opts)))
+ (count (length args))
+ (action (assoc-ref opts 'action))
+ (expr (assoc-ref opts 'expression)))
+ (define (fail)
+ (leave (G_ "wrong number of arguments for action '~a'~%")
+ action))
+
+ (unless action
+ (format (current-error-port)
+ (G_ "guix home: missing command name~%"))
+ (format (current-error-port)
+ (G_ "Try 'guix home --help' for more information.~%"))
+ (exit 1))
+
+ (case action
+ ((build reconfigure)
+ (unless (= count 1)
+ (fail))))
+ args))
+
(with-error-handling
(let* ((opts (parse-command-line args %options
(list %default-options)
- #:build-options? #f)))
- (format #t "Guix Home Test~%")))
- #t)
+ #:argument-handler
+ parse-sub-command))
+ (args (option-arguments opts))
+ (command (assoc-ref opts 'action)))
+ (parameterize ((%graft? (assoc-ref opts 'graft?)))
+ (with-status-verbosity (or (assoc-ref opts 'verbosity)
+ (if (eq? command 'build) 2 1))
+ (process-command command args opts))))))
;;; home.scm ends here
diff --git a/home.scm b/home.scm
index 0a3c03a..99ff98f 100644
--- a/home.scm
+++ b/home.scm
@@ -22,56 +22,71 @@
#:use-module (guix gexp)
#:use-module (guix licenses)
#:use-module (guix packages)
+ #:use-module (guix records)
+ #:use-module (ice-9 match)
#:use-module (home build utils)
- #:export (home
- use-home-modules))
+ #:export (use-home-modules
+ home
+ home?
+ home-data-directory
+ home-guix-symlink
+ home-guix-config-symlink
+ home-local-symlink
+ home-cache-symlink
+ home->derivation))
(define-syntax use-home-modules
(syntax-rules ()
((_ modules ...)
(use-modules (home modules) ...))))
-(define* (home basedir inputs #:key
- (guix-symlink (string-append basedir "/.guix-profile"))
- (guix-config-symlink (string-append basedir "/.config/guix"))
- (local-symlink (string-append basedir "/.local"))
- (cache-symlink (string-append basedir "/.cache")))
- (define union
- (computed-file "home"
+(define-record-type* home
+ make-home
+ home?
+ (data-directory home-data-directory)
+ (guix-symlink home-guix-symlink (thunked)
+ (default (string-append
+ (home-data-directory this-record)
+ "/.guix-profile")))
+ (guix-config-symlink home-guix-config-symlink (thunked)
+ (default (string-append
+ (home-data-directory this-record)
+ "/.config/guix")))
+ (local-symlink home-local-symlink (thunked)
+ (default (string-append
+ (home-data-directory this-record)
+ "/.local")))
+ (cache-symlink home-cache-symlink (thunked)
+ (default (string-append
+ (home-data-directory this-record)
+ "/.cache")))
+ (configurations home-configurations
+ (default '())))
+
+(define (home->derivation home)
+ (define builder
+ (with-imported-modules
+ '((guix build utils) (home build utils))
#~(begin
- (use-modules (guix build union))
- (union-build #$output '#$inputs))
- #:options
- '(#:local-build? #t
- #:modules ((guix build union)))))
- (package
- (name "guix-home")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments
- `(#:modules ((guix build utils) (home build utils))
- #:builder
- (begin
- (use-modules (guix build utils) (home build utils))
- (mkdir-p (home-file %outputs ".config"))
- ;; For guix
- (symlink ,guix-config-symlink (home-file %outputs ".config/guix"))
- (symlink ,guix-symlink (home-file %outputs ".guix-profile"))
- ;; symlink writeable directories
- (symlink ,local-symlink (home-file %outputs ".local"))
- (symlink ,cache-symlink (home-file %outputs ".cache"))
- ;; rest of the files
- (with-directory-excursion (assoc-ref %build-inputs "union")
- (for-each
- (lambda (f)
- (mkdir-p (home-file %outputs (dirname f)))
- (symlink (string-append (assoc-ref %build-inputs "union") "/" f)
- (home-file %outputs f)))
- (find-files "." ".*"))))))
- (inputs
- `(("union" ,union)))
- (home-page "")
- (synopsis "")
- (description "")
- (license gpl3+)))
\ No newline at end of file
+ (use-modules (guix build utils) (home build utils))
+ (mkdir-p (home-file #$output ".config"))
+ ;; For guix
+ (symlink #$(home-guix-config-symlink home) (home-file #$output ".config/guix"))
+ (symlink #$(home-guix-symlink home) (home-file #$output ".guix-profile"))
+ ;; symlink writeable directories
+ (symlink #$(home-local-symlink home) (home-file #$output ".local"))
+ (symlink #$(home-cache-symlink home) (home-file #$output ".cache"))
+ ;; rest of the files
+ (for-each
+ (lambda (config)
+ (with-directory-excursion config
+ (for-each
+ (lambda (f)
+ (mkdir-p (home-file #$output (dirname f)))
+ (symlink (home-file config f)
+ (home-file #$output f)))
+ (find-files "." "."))))
+ (list #$@(home-configurations home))))))
+ (gexp->derivation "home" builder
+ #:substitutable? #f
+ #:local-build? #t))
diff --git a/home/build/utils.scm b/home/build/utils.scm
index 19b66a9..832af28 100644
--- a/home/build/utils.scm
+++ b/home/build/utils.scm
@@ -18,5 +18,5 @@
(define-module (home build utils)
#:export (home-file))
-(define (home-file outputs . path)
- (apply string-append (assoc-ref outputs "out") "/" path))
\ No newline at end of file
+(define (home-file output . path)
+ (apply string-append output "/" path))