;;; system-packages.el --- functions to manage system packages -*- lexical-binding: t; -*- ;; Copyright (C) 2016-2019 Free Software Foundation, Inc. ;; Author: J. Alexander Branham ;; Maintainer: J. Alexander Branham ;; URL: https://gitlab.com/jabranham/system-packages ;; Package-Requires: ((emacs "24.3")) ;; Version: 1.0.11 ;; This file is part of GNU Emacs. ;;; License: ;; ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see ;; ;;; Commentary: ;; This is a package to manage installed system packages. Useful ;; functions include installing packages, removing packages, listing ;; installed packages, and others. ;; Helm users might also be interested in helm-system-packages.el ;; ;; Usage: ;; (require 'system-packages) ;; ;;; Code: (defgroup system-packages nil "Manages system packages" :tag "System Packages" :prefix "system-packages" :group 'packages) (defvar system-packages-supported-package-managers '( ;; guix (guix . ((default-sudo . nil) (install . "guix package -i") (search . "guix package -s") (uninstall . "guix package -r") (update . ("guix package --upgrade")) (clean-cache . "guix gc") (log . nil) (get-info . nil) (get-info-remote . nil) (list-files-provided-by . nil) (verify-all-packages . nil) (verify-all-dependencies . nil) (remove-orphaned . nil) (list-installed-packages . "guix package -I") (list-installed-packages-all . "guix package -I") (list-dependencies-of . nil) (noconfirm . nil))) ;; nix (nix . ((default-sudo . nil) (install . "nix-env -i") (search . "nix search") (uninstall . "nix-env -e") (update . ("nix-env -u" )) (clean-cache . "nix-collect-garbage") (log . nil) (get-info . nil) (get-info-remote . nil) (list-files-provided-by . nil) (verify-all-packages . nil) (verify-all-dependencies . nil) (remove-orphaned . nil) (list-installed-packages . "nix-env -q") (list-installed-packages-all . "nix-env -q") (list-dependencies-of . nil) (noconfirm . nil))) ;; Mac (brew . ((default-sudo . nil) (install . "brew install") (search . "brew search") (uninstall . "brew uninstall") (update . ("brew update" "brew upgrade")) (clean-cache . "brew cleanup") (log . nil) (get-info . nil) (get-info-remote . nil) (list-files-provided-by . "brew ls --verbose") (verify-all-packages . nil) (verify-all-dependencies . nil) (remove-orphaned . nil) (list-installed-packages . "brew list") (list-installed-packages-all . "brew list") (list-dependencies-of . "brew deps") (noconfirm . nil))) (port . ((default-sudo . t) (install . "port install") (search . "port search") (uninstall . "port uninstall") (update . ("port sync" "port upgrade outdated")) (clean-cache . "port clean --all") (log . "port log") (get-info . "port info") (get-info-remote . nil) (list-files-provided-by . "port contents") (verify-all-packages . nil) (verify-all-dependencies . nil) (remove-orphaned . "port uninstall leaves") (list-installed-packages . "port installed") (list-installed-packages-all . "port installed") (list-dependencies-of . "port deps") (noconfirm . nil))) ;; Arch-based systems (pacman . ((default-sudo . t) (install . "pacman -S") (search . "pacman -Ss") (uninstall . "pacman -Rns") (update . "pacman -Syu") (clean-cache . "pacman -Sc") (log . "cat /var/log/pacman.log") (get-info . "pacman -Qi") (get-info-remote . "pacman -Si") (list-files-provided-by . "pacman -Ql") (verify-all-packages . "pacman -Qkk") (verify-all-dependencies . "pacman -Dk") (remove-orphaned . "pacman -Rns $(pacman -Qtdq)") (list-installed-packages . "pacman -Qe") (list-installed-packages-all . "pacman -Q") (list-dependencies-of . "pacman -Qi") (noconfirm . "--noconfirm"))) ;; Debian (and Ubuntu) based systems (apt . ((default-sudo . t) (install . "apt-get install") (search . "apt-cache search") (uninstall . "apt-get --purge remove") (update . ("apt-get update" "apt-get upgrade")) (clean-cache . "apt-get clean") (log . "cat /var/log/dpkg.log") (get-info . "dpkg -s") (get-info-remote . "apt-cache show") (list-files-provided-by . "dpkg -L") (verify-all-packages . "debsums") (verify-all-dependencies . "apt-get check") (remove-orphaned . "apt-get autoremove") (list-installed-packages . "dpkg -l") (list-installed-packages-all . "dpkg -l") (list-dependencies-of . "apt-cache depends") (noconfirm . "-y"))) (aptitude . ((default-sudo . t) (install . "aptitude install") (search . "aptitude search") (uninstall . "aptitude remove") (update . ("apt update" "aptitude safe-upgrade")) (clean-cache . "aptitude clean") (log . "cat /var/log/dpkg.log") (get-info . "aptitude show") (get-info-remote . "aptitude show") (list-files-provided-by . "dpkg -L") (verify-all-packages . "debsums") (verify-all-dependencies . "apt-get check") (remove-orphaned . nil) ; aptitude does this automatically (list-installed-packages . "aptitude search '~i!~M'") (list-installed-packages-all . "aptitude search '~i!~M'") (list-dependencies-of . "apt-cache deps") (noconfirm . "-y"))) ;; Gentoo (emerge . ((default-sudo . t) (install . "emerge") (search . "emerge -S") (uninstall . "emerge -C") (update . "emerge -u world") (clean-cache . "eclean distfiles") (log . "cat /var/log/portage") (get-info . "emerge -pv") (get-info-remote . "emerge -S") (list-files-provided-by . "equery files") (verify-all-packages . "equery check") (verify-all-dependencies . "emerge -uDN world") (remove-orphaned . "emerge --depclean") (list-installed-packages . nil) (list-installed-packages-all . nil) (list-dependencies-of . "emerge -ep") (noconfirm . nil))) ;; openSUSE (zypper . ((default-sudo . t) (install . "zypper install") (search . "zypper search") (uninstall . "zypper remove") (update . "zypper update") (clean-cache . "zypper clean") (log . "cat /var/log/zypp/history") (get-info . "zypper info") (get-info-remote . "zypper info") (list-files-provided-by . "rpm -Ql") (verify-all-packages . "rpm -Va") (verify-all-dependencies . "zypper verify") (remove-orphaned . "zypper rm -u") (list-installed-packages . nil) (list-installed-packages-all . nil) (list-dependencies-of . "zypper info --requires") (noconfirm . nil))) ;; Fedora (dnf . ((default-sudo . t) (install . "dnf install") (search . "dnf search") (uninstall . "dnf remove") (update . ("dnf upgrade")) (clean-cache . "dnf clean all") (log . "dnf history") (get-info . "rpm -qi") (get-info-remote . "dnf info") (list-files-provided-by . "rpm -ql") (verify-all-packages . "rpm -Va") (verify-all-dependencies . "dnf repoquery --requires") (remove-orphaned . "dnf autoremove") (list-installed-packages . "dnf list --installed") (list-installed-packages-all . nil) (list-dependencies-of . "rpm -qR") (noconfirm . nil))) ;; RedHat derivatives (yum . ((default-sudo . t) (install . "yum install") (search . "yum search") (uninstall . "yum remove") (update . "yum update") (clean-cache . "yum clean expire-cache") (log . "cat /var/log/yum.log") (get-info . "yum info") (get-info-remote . "repoquery --plugins -i") (list-files-provided-by . "rpm -ql") (verify-all-packages) (verify-all-dependencies) (remove-orphaned . "yum autoremove") (list-installed-packages . "yum list installed") (list-installed-packages-all . "yum list installed") (list-dependencies-of . "yum deplist") (noconfirm . "-y"))) ;; Void ;; xbps is the name of the package manager, but that doesn't appear as an ;; executable, so let's just call it xbps-install: (xbps-install . ((default-sudo . t) (install . "xbps-install") (search . "xbps-query -Rs") (uninstall . "xbps-remove -R") (update . ("xbps-install -Su")) (clean-cache . "xbps-remove -O") (log . nil) (get-info . "xbps-query") (get-info-remote . "xbps-query -R") (list-files-provided-by . "xbps-query -f") (verify-all-packages . nil) (verify-all-dependencies . "xbps-pkgdb -a") (remove-orphaned . "dnf autoremove") (list-installed-packages . "xbps-query -l ") (list-installed-packages-all . "xbps-query -l ") (list-dependencies-of . "xbps-query -x") (noconfirm . nil)))) "An alist of package manager commands. The key is the package manager and values (usually) commands.") (put 'system-packages-supported-package-managers 'risky-local-variable t) (define-obsolete-variable-alias 'system-packages-packagemanager 'system-packages-package-manager "2017-12-25") (defcustom system-packages-package-manager (let ((managers system-packages-supported-package-managers) manager) (while managers (progn (setq manager (pop managers)) (if (executable-find (symbol-name (car manager))) (setq managers nil) (setq manager nil)))) (car manager)) "Symbol containing the package manager to use. See `system-packages-supported-package-managers' for a list of supported software. Tries to be smart about selecting the default. If you change this value, you may also want to change `system-packages-use-sudo'." :type '(choice (const guix) (const nix-env) (const brew) (const port) (const pacman) (const apt) (const aptitude) (const emerge) (const zypper) (const dnf) (const xbps-install))) (define-obsolete-variable-alias 'system-packages-usesudo 'system-packages-use-sudo "2017-12-25") (defcustom system-packages-use-sudo (cdr (assoc 'default-sudo (cdr (assoc system-packages-package-manager system-packages-supported-package-managers)))) "If non-nil, system-packages uses sudo for appropriate commands. Tries to be smart for selecting the default." :type 'boolean) (defcustom system-packages-noconfirm nil "If non-nil, bypass prompts asking the user to confirm package upgrades." :type 'boolean) (defun system-packages-get-command (action &optional pack args) "Return a command to run as a string. ACTION should be in `system-packages-supported-package-managers' (e.g. 'install). PACK is used to operate on a specific package, and ARGS is a way of passing additional arguments to the package manager." (let ((command (cdr (assoc action (cdr (assoc system-packages-package-manager system-packages-supported-package-managers))))) (noconfirm (when system-packages-noconfirm (cdr (assoc 'noconfirm (cdr (assoc system-packages-package-manager system-packages-supported-package-managers))))))) (unless command (error (format "%S not supported in %S" action system-packages-package-manager))) (unless (listp command) (setq command (list command))) (setq command (mapconcat #'identity command " && ")) (setq command (mapconcat #'identity (list command pack) " ")) (when noconfirm (setq args (concat args (and pack " ") noconfirm))) (concat command args))) (defun system-packages--run-command (action &optional pack args) "Run a command asynchronously using the system's package manager. See `system-packages-get-command' for how to use ACTION, PACK, and ARGS." (let ((command (system-packages-get-command action pack args)) (default-directory (if system-packages-use-sudo "/sudo::" default-directory)) (inhibit-read-only t)) (async-shell-command command "*system-packages*"))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; functions on named packages ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;###autoload (defun system-packages-install (pack &optional args) "Install system packages. Use the package manager from `system-packages-package-manager' to install PACK. You may use ARGS to pass options to the package manger." (interactive "sPackage to install: ") (system-packages--run-command 'install pack args)) ;;;###autoload (defun system-packages-ensure (pack &optional args) "Ensure PACK is installed on system. Search for PACK with `system-packages-package-installed-p', and install the package if not found. Use ARGS to pass options to the package manager." (interactive "sPackage to ensure is present: ") (if (system-packages-package-installed-p pack) t (system-packages-install pack args))) ;;;###autoload (defalias 'system-packages-package-installed-p #'executable-find "Return t if PACK is installed. Currently an alias for `executable-find', so it will give wrong results if the package and executable names are different.") ;;;###autoload (defun system-packages-search (pack &optional args) "Search for system packages. Use the package manager named in `system-packages-package-manager' to search for PACK. You may use ARGS to pass options to the package manager." (interactive "sSearch string: ") (system-packages--run-command 'search pack args)) ;;;###autoload (defun system-packages-uninstall (pack &optional args) "Uninstall system packages. Uses the package manager named in `system-packages-package-manager' to uninstall PACK. You may use ARGS to pass options to the package manager." (interactive "sWhat package to uninstall: ") (system-packages--run-command 'uninstall pack args)) ;;;###autoload (defun system-packages-list-dependencies-of (pack &optional args) "List the dependencies of PACK. You may use ARGS to pass options to the package manager." (interactive "sWhat package to list dependencies of: ") (system-packages--run-command 'list-dependencies-of pack args)) ;;;###autoload (defun system-packages-get-info (pack &optional args) "Display local package information of PACK. With a prefix argument, display remote package information. You may use ARGS to pass options to the package manager." (interactive "sWhat package to list info for: ") (if current-prefix-arg (system-packages--run-command 'get-info-remote pack args) (system-packages--run-command 'get-info pack args))) ;;;###autoload (defun system-packages-list-files-provided-by (pack &optional args) "List the files provided by PACK. You may use ARGS to pass options to the package manager." (interactive "sWhat package to list provided files of: ") (system-packages--run-command 'list-files-provided-by pack args)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; functions that don't take a named package ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;###autoload (defun system-packages-update (&optional args) "Update system packages. Use the package manager `system-packages-package-manager'. You may use ARGS to pass options to the package manger." (interactive) (system-packages--run-command 'update nil args)) ;;;###autoload (defun system-packages-remove-orphaned (&optional args) "Remove orphaned packages. Uses the package manager named in `system-packages-package-manager'. You may use ARGS to pass options to the package manger." (interactive) (system-packages--run-command 'remove-orphaned nil args)) ;;;###autoload (defun system-packages-list-installed-packages (all &optional args) "List explicitly installed packages. Uses the package manager named in `system-packages-package-manager'. With \\[universal-argument] (for ALL), list all installed packages. You may use ARGS to pass options to the package manger." (interactive "P") (if all (system-packages--run-command 'list-installed-packages-all nil args) (system-packages--run-command 'list-installed-packages nil args))) ;;;###autoload (defun system-packages-clean-cache (&optional args) "Clean the cache of the package manager. You may use ARGS to pass options to the package manger." (interactive) (system-packages--run-command 'clean-cache nil args)) ;;;###autoload (defun system-packages-log (&optional args) "Show a log from `system-packages-package-manager'. You may use ARGS to pass options to the package manger." (interactive) (system-packages--run-command 'log args)) ;;;###autoload (defun system-packages-verify-all-packages (&optional args) "Check that files owned by packages are present on the system. You may use ARGS to pass options to the package manger." (interactive) (system-packages--run-command 'verify-all-packages nil args)) ;;;###autoload (defun system-packages-verify-all-dependencies (&optional args) "Verify that all required dependencies are installed on the system. You may use ARGS to pass options to the package manger." (interactive) (system-packages--run-command 'verify-all-dependencies nil args)) (provide 'system-packages) ;;; system-packages.el ends here