Commit 7a4a744d authored by Dimitri Fontaine's avatar Dimitri Fontaine

Simplify `el-get-install' and dependencies management.

This refactoring branch will need some testing at some point…
parent dbd44bdf
......@@ -90,115 +90,5 @@ in the topological ordering (i.e., the first value)."
all-sorted-p
(unless all-sorted-p
entries)))))))
;;
;; Support for tracking package states
;;
(defvar el-get-pkg-state
(make-hash-table)
"A hash mapping el-get package name symbols to their installation states")
(defun el-get-package-state (package)
"Return the installation state of PACKAGE.
- nil indicates that installation of the package has not been requested
- 'installing indicates that the package's installation is in progress
- 'init indicates that the package has been initialized
- ('error . <data>) indicates that there was an installation error"
(gethash (el-get-as-symbol package) el-get-pkg-state))
(defun el-get-currently-installing-p (package)
(eq (el-get-package-state package) 'installing))
(defun el-get-currently-installing-packages ()
"Return the packages that are currently installing"
(loop
for pkg being the hash-keys of el-get-pkg-state
if (el-get-currently-installing-p pkg)
collect pkg))
(defun el-get-set-package-state (package state)
"Set the installation state of PACKAGE to STATE"
(puthash (el-get-as-symbol package) state el-get-pkg-state))
(defun el-get-mark-initialized (package)
"Record the fact that the given PACKAGE has been initialized."
(el-get-set-package-state package 'init))
(add-hook 'el-get-post-init-hooks 'el-get-mark-initialized)
(defun el-get-mark-removed (package)
"Record the fact that the given PACKAGE has been initialized."
(el-get-set-package-state package nil))
(add-hook 'el-get-post-remove-hooks 'el-get-mark-removed)
(defun el-get-mark-failed (package info)
"Record the fact that the given PACKAGE has failed to install
for reasons described in INFO."
(el-get-verbose-message "el-get-mark-failed: %s %s" package info)
(el-get-set-package-state package `(error ,info)))
(add-hook 'el-get-post-error-hooks 'el-get-mark-failed)
;;
;; generic one-shot event support
;;
(defvar el-get-generic-event-tasks (make-hash-table :test 'equal)
"A hash mapping event triggers to lists of functions to be called")
(defun el-get-generic-event-occurred (event &optional data)
"Fire all tasks added for the given EVENT (a hash key), passing DATA."
(let (tasks)
(while (setq tasks (gethash event el-get-generic-event-tasks))
(puthash event (cdr tasks) el-get-generic-event-tasks)
(ignore-errors (funcall (car tasks) data)))))
(defun el-get-add-generic-event-task (event task)
"Set up TASK to be called when EVENT (a hash key) occurs."
(puthash event (cons task (gethash event el-get-generic-event-tasks))
el-get-generic-event-tasks))
(defun el-get-clear-generic-event-tasks (event)
"Clear all tasks waiting on EVENT (a hash key)"
(remhash event el-get-generic-event-tasks))
;;
;; fire events for completion of el-get's init, install, and update
;; phases (and for errors).
;;
(defun el-get-event-id (package action)
(list (el-get-as-symbol package) (intern (format "el-get-%s" action))))
(defun el-get-event-occurred (package action &optional data)
"Handle the completion of ACTION on PACKAGE (both symbols),
passing DATA"
;; If this action finalizes the package state, first cancel other
;; final actions
(let* ((final-actions '(init error))
(found (position action final-actions)))
(when found
(el-get-clear-generic-event-tasks
(el-get-event-id package (elt final-actions (- 1 found))))))
;; Now fire off the generic event
(el-get-generic-event-occurred (el-get-event-id package action) data))
;; Install hooks that generate events
(dolist (action '(init install update error))
(add-hook (intern (format "el-get-post-%s-hooks" action))
`(lambda (p &optional data) (el-get-event-occurred p ',action data))))
(defun el-get-package-initialized-p (package)
(eq (el-get-package-state package) 'init))
(defun el-get-dependency-installed (package dependency)
"Install the given PACKAGE (a symbol) iff all its dependencies
are now installed"
(when (every 'el-get-package-initialized-p
(el-get-dependencies package))
(el-get-do-install package)))
(defun el-get-dependency-error (package dependency data)
"Mark PACKAGE as having failed installation due to a failure to
install DEPENDENCY, with error information DATA"
(el-get-mark-failed package (list dependency data)))
(provide 'el-get-depends)
(provide 'el-get-dependencies)
......@@ -146,19 +146,18 @@
(require 'el-get-core)
(require 'el-get-autoloads)
(require 'el-get-build)
(require 'el-get-byte-compile)
(require 'el-get-core)
(require 'el-get-custom)
(require 'el-get)
(require 'el-get-install)
(require 'el-get-list-depends)
(require 'el-get-list-packages)
(require 'el-get-methods)
(require 'el-get-notify)
(require 'el-get-recipes)
(require 'el-get-status)
(require 'el-get-core) ; core facilities used everywhere
(require 'el-get-custom) ; user tweaks and `el-get-sources'
(require 'el-get-methods) ; support for `el-get-methods', backends
(require 'el-get-recipes) ; support for dealing with recipes
(require 'el-get-status) ; support for dealing with status
(require 'el-get-build) ; building packages
(require 'el-get-byte-compile) ; byte compiling in a subprocess
(require 'el-get-dependencies) ; topological-sort of package dep graph
(require 'el-get-notify) ; notification support (dbus, growl...)
(require 'el-get-list-packages) ; menu and `el-get-describe' facilities
(require 'el-get-autoloads) ; to be removed^W cleaned up next
(defgroup el-get nil "el-get customization group"
:group 'convenience)
......@@ -251,57 +250,8 @@ force their evaluation on some packages only."
(defvar el-get-install-info (or (executable-find "ginstall-info")
(executable-find "install-info")))
(defun el-get-install (package)
"Cause the named PACKAGE to be installed after all of its
dependencies (if any).
PACKAGE may be either a string or the corresponding symbol."
(interactive (list (el-get-read-package-name "Install")))
(if (el-get-package-is-installed package)
(message "el-get: `%s' package is already installed" package)
(condition-case err
(let* ((psym (el-get-as-symbol package))
(pname (symbol-name psym)))
;; don't do anything if it's already installed or in progress
(unless (memq (el-get-package-state psym) '(init installing))
;; Remember that we're working on it
(el-get-set-package-state psym 'installing)
(let ((non-installed-dependencies
(remove-if 'el-get-package-initialized-p
(el-get-dependencies psym))))
;;
;; demand all non-installed dependencies with appropriate
;; handlers in place to trigger installation of this package
;;
(dolist (dep non-installed-dependencies)
;; set up a handler that will install `package' when all
;; its dependencies are installed
(el-get-add-generic-event-task
(el-get-event-id dep 'init)
`(lambda (data)
(el-get-mark-initialized ',dep)
(el-get-dependency-installed ',psym ',dep)))
;; set up a handler that will cancel installation of
;; `package' if installing the dependency fails
(el-get-add-generic-event-task
(el-get-event-id dep 'error)
`(lambda (data)
(el-get-set-package-state ',dep (list 'error data))
(el-get-dependency-error ',psym ',dep data)))
(el-get-install dep))
(unless non-installed-dependencies
(el-get-do-install psym)))))
((debug error)
(el-get-installation-failed package err)))))
(defvar el-get-next-packages nil
"List of packages to install next, used when dealing with dependencies.")
(defun el-get-installation-failed (package signal-data)
"Run all the failure hooks for PACKAGE and `signal' the car and cdr of SIGNAL-DATA."
......@@ -381,6 +331,7 @@ which defaults to the first element in `el-get-recipe-path'."
(let (pdir (el-get-package-directory package))
(funcall func))))
(defun el-get-init (package)
"Make the named PACKAGE available for use.
......@@ -472,6 +423,34 @@ called by `el-get' (usually at startup) for each installed package."
(debug error
(el-get-installation-failed package err))))
(defun el-get-install (package)
"Cause the named PACKAGE to be installed after all of its
dependencies (if any).
PACKAGE may be either a string or the corresponding symbol."
(interactive (list (el-get-read-package-name "Install")))
(if (el-get-package-is-installed package)
(message "el-get: `%s' package is already installed" package)
(let* ((packages (el-get-dependencies package)))
(when (cdr packages)
;; tweak el-get-post-install-hooks to install remaining packages
;; once the first is installed
(setq el-get-next-packages (cdr packages))
(add-hook 'el-get-post-install-hooks 'el-get-install-next-packages))
(el-get-do-install package))))
(defun el-get-install-next-packages ()
"Run as part of `el-get-post-init-hooks' when dealing with dependencies."
(let ((package (pop el-get-next-packages)))
(if package
(if (el-get-package-is-installed package)
(message "el-get: `%s' package is already installed" package)
(el-get-do-install package))
;; no more packages to install in the dependency walk, clean up
(remove-hook 'el-get-post-init-hooks 'el-get-install-next-packages))))
(defun el-get-post-install-build (package)
"Function to call after building the package while installing it."
(el-get-invalidate-autoloads package)
......@@ -518,6 +497,7 @@ called by `el-get' (usually at startup) for each installed package."
(funcall install package url 'el-get-post-install)
(message "el-get install %s" package))))
(defun el-get-post-update (package)
"Post update PACKAGE. This will get run by a sentinel."
(let* ((source (el-get-package-def package))
......@@ -557,6 +537,7 @@ called by `el-get' (usually at startup) for each installed package."
(expand-file-name ".." (file-name-directory el-get-script))))
(el-get-update "el-get")))
(defun el-get-post-remove (package)
"Run the post-remove hooks for PACKAGE."
(let* ((hooks (el-get-method (el-get-package-method package) :remove-hook)))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment