User:Schievel/emacs-as-an-ebuild-IDE

From Gentoo Wiki
Jump to:navigation Jump to:search

app-misc/emacs is a very powerful and flexible editor. It is ideal for customizing it into an 'IDE' for Ebuilds. I use Doom Emacs, the Doom in Doom Emacs is a configuration framework for Emacs. It sets certain variables to sane defaults, provides coherent keybindings across packages and has it's own sets of packages that belong together bundled and installable with one command. Read more about Doom Emacs here.

Tip
Doom Emacs is run best with emacs in GUI mode, so install emacs with gui and one of gtk, athena or motif. Additionally Doom needs xft to render fonts correctly.

If you have any other cool things, helper functions etc. regarding ebuild writing in Emacs, let me know on the discussion page.. I will add that to this page then.

ebuild-mode

Ebuild mode is the center package of ebuild development on Gentoo. It sits on top of sh mode.

Tip
Read the documentation of ebuild-mode outside emacs with
user $bzcat /usr/share/info/ebuild-mode.info.bz2
. (bzcat comes from app-arch/bzip2) Or follow this link.

Installation

Ebuild-mode is available on Gentoos main repository as app-misc/ebuild-mode, so install it like any other package with

root #emerge -a app-emacs/ebuild-mode

.

The ebuild mode package brings in several other modes apart from the ebuild-mode core, which are devbook-mode, gentoo-newsitem-mode and glep-mode. I don't use them, and therefore this article will focus on ebuild-mode itself. Ebuild mode is then installed into /usr/share/emacs/site-lisp/ebuild-mode. Usually this path is added to emacs' load-path automatically, using Gentoo's site-lisp mechanism for emacs. For me in one emacs installation Doom altered this load process. Putting

CODE
(add-to-list 'load-path "/usr/share/emacs/site-lisp")
(require 'site-gentoo)

at the top of ~/.doom.d/init.el made it load again.

Tip
If you opted out of gentoos site-lisp mechanism, but still want to load some system installed emacs packages, you can put (add-to-list 'load-path "/usr/share/emacs/site-lisp/site-gentoo.d") into the config and then load the indiviual packages. E.g. with (load "/usr/share/emacs/site-lisp/site-gentoo.d/50ebuild-mode-gentoo.el"). The filenames are always 50$PACKAGENAME$-gentoo.el.

For the pkgdev and pkgcheck commands you will additionally need dev-util/pkgdev and dev-util/pkgcheck.

Features

Ebuild mode has several different features to make writing ebuild easier and more convenient:

  • code highlighting of ebuild keywords like emake, insinto, eapply etc.
  • Automatic updating of the ebuild header (changes # Copyright 1999-2021 Gentoo Authors automatically to # Copyright 1999-2023 Gentoo Authors)

This will only happen once, if you edit it manually, ebuild mode will notice and don't overwrite what you wrote again. (To reset change ebuild-mode-update-copyright to true again.

  • Removal of useless whitespace like spaces at end of lines, double empty lines...
  • Replacement of spaces with tabs for indentation
  • special commands to work with ebuilds (see Keybindings below)
Ebuild mode in action

Configuration

Keybindings

By default ebuild mode uses conventional emacs keybindings, you can look them up by pressing SPC h m while being in ebuild-mode.

CODE
key             binding
---             -------
C-c C-b         ebuild-mode-all-keywords-unstable
C-c C-e         ebuild-run-command
C-c C-k         ebuild-mode-keyword
C-c C-n         ebuild-mode-insert-skeleton
C-c C-y         ebuild-mode-ekeyword
C-c -           ebuild-mode-insert-tag-line
C-c C-p         ebuild-mode-run-pkgdev
C-c C-q         ebuild-mode-run-pkgcheck
C-c C-d         ebuild-mode-find-workdir

I remapped them so that they are more coherent with Doom Emacs:

CODE
(map! :localleader
      :map ebuild-mode-map
      "r" #'ebuild-run-command                ;; run a provided phase of the currently open ebuild
      "k" #'ebuild-mode-keyword               ;; change status of a single keyword e.g. from unstable to stable
      "s" #'ebuild-mode-insert-skeleton       ;; insert a skeleton of an ebuild to work from
      "u" #'ebuild-mode-all-keywords-unstable ;; mark all keywords unstable (~)
      "e" #'ebuild-mode-ekeyword              ;; run ekeyword on the current ebuild.
      "p" #'ebuild-mode-run-pkgdev            ;; run pkgdev command
      "c" #'ebuild-mode-run-pkgcheck          ;; run pkgcheck command
      "t" #'ebuild-mode-insert-tag-line      ;; Insert a tag with name and email
      "w" #'ebuild-mode-find-workdir          ;; Goto work dir of the ebuild
)
Tip
When running commands that take one or more arguments, like ebuild-run-command or ebuild-mode-run-pkgdev, you can easily jump to through the history of arguments with M-p. This is very useful when trying things out and you need to run the same ebuild commands again and again. When using helm C-o works for that as well.

For the tag line ebuild mode uses the variables user-full-name and user-email-address. They are present in the default config of Doom Emacs. If you want to use a different name or address for ebuilds than for the rest of your Emacs life (e.g. you have a dedicated mail address for Gentoo related work) set the variables ebuild-mode-full-name and ebuild-mode-mail-address.

ebuild-run-mode

Ebuild run mode provides the functionality to jump directly from an error in the output of ebuild-run-command to the location of the code snippet that produced the error.

Installation

Install ebuild-run-mode with

root #emerge -a app-emacs/ebuild-run-mode

.

Ebuild-run-mode sits on top of ebuild-mode, so only run that after ebuild-mode is loaded:

CODE
(eval-after-load 'ebuild-mode `(setq ebuild-log-buffer-mode 'ebuild-run-mode))

Usage

When an ebuild was run using ebuild-run-command a buffer with the output will pop up. When there is an error, place the point (the cursor) on it and press return (bound to compile-goto-error) to jump to the line that produces the error in the packages code.

emacs-ebuild-snippets

This package provides yasnippets. Those are skeletons of typical functions used in ebuilds.

Installation

Install yasnippets in Doom emacs by uncommenting the line ;;snippets in your ~/.doom.d/init.el.

Install emacs-ebuild-snippets with

root #emerge -a app-emacs/emacs-ebuild-snippets

.

Emacs-ebuild-snippets is installed into /usr/share/emacs/site-lisp/emacs-ebuild-snippets.

Usage

Insert snippets into the an open ebuild with M-x yas-insert-snippet. For Doom users that is also bound to SPC i s.

company-ebuild

Company is a package in emacs that provides code completion. When you start typing something company tries to guess what you want and a little pop up will show up with suggestions. The package company-ebuild provides suggestions for ebuilds.

Company showing suggestions for licenses

Installation

Install company in Doom emacs by uncommenting the line ;;company in your ~/.doom.d/init.el. Install company-ebuild with

root #emerge -a app-emacs/company-ebuild

.

company-ebuild is installed into /usr/share/emacs/site-lisp/company-ebuild.

To tell Doom emacs to use company-ebuild as a backend for ebuild mode completions, put

CODE
(after! ebuild-mode
  (set-company-backend! 'ebuild-mode 'company-ebuild 'company-shell 'company-yasnippet))

into ~/.doom.d/config.el

Usage

To use company just start typing a command and wait. Company will show a pop up with suggestions.

nxml-gentoo-schemas

Nxml-gentoo-schemas will check the metadata.xml and such and can also provide suggestions on how to fill them and syntax highlighting. For example it will highlight a typo in the upstreams remote-ids:

nxml highligthing a typo

Installation

Install nxml-gentoo-schemas with

root #emerge -a app-emacs/nxml-gentoo-schemas

.

nxml-gentoo-schemas is installed into /usr/share/emacs/site-lisp/nxml-gentoo-schemas.

Usage

The highlighting of mistakes is activated automatically. If a xml file is invalid nxml-gentoo-schemas will show that in the modeline. Press C-M-i for completion at point. Company will show a list of suggestions.

Little helpers

Put that in config.el/ init.el (for Doom that is ~/.doom.d/config.el).

Tag line everywere

Tip
This is fixed in ebuild-mode 1.67 and you can use the function ebuild mode provides in .patch files etc.

Because this is really useful even outside of ebuilds (in patches for example) I changed the ebuild-mode-insert-tag-line a little and bound it to a key that is always available instead of binding it to a key in ebuild-mode-map.

CODE
(defun ebuilds/insert-tag-line ()
  "Insert a tag line with the user's name, e-mail address and date.
Format is \"# Larry The Cow <larry@gentoo.org> (2019-07-01)\"."
  (interactive)
  (beginning-of-line)
  (insert (format "%s%s <%s> (%s)\n"
		  (if comment-start (concat comment-start " ") "")
		  ebuild-mode-full-name ebuild-mode-mail-address
		  (format-time-string "%F" nil t))))

This makes sure that a comment sign is only put into that tagline when a comment sign is known. Before when the line was inserted into a .patch-file, it would put nil at the start, because .patch-files do not have comment signs. (instead only lines with a space in front are not comments.) You could also use user-full-name and user-mail-address here instead of their ebuild-mode counterparts. For me those are the same so it does not really matter.

I bound that to SPC T.

CODE
(map! :leader
        :desc "Insert my tagline"           "T" #'ebuilds/insert-tag-line)

Environment variables

To test ebuilds we often need to set environment variables like USE and CC for example. To set them in Emacs we use M-x setenv. To make things a little easier we can define little functions to set sets of environment variables and bind them to keys in ebuild-mode-map or call them with M-x. E.g. for Clang16 bugs I made this:

CODE
(defun ebuild-mode-set-CC-clang ()
    (interactive)
    (setenv "CC" "clang")
    (setenv "CXX" "clang++"))

(defun ebuild-mode-set-CC-gcc ()
    (interactive)
    (setenv "CC" "gcc")
    (setenv "CXX" "g++"))

Call scrub-patch from emacs

According to the Gentoo Dev Manual patches should meet certain QA conditions as well. Therefore we have a neat little program called scrub-patch which is part of app-portage/iwdevtools, so we should install that using

root #emerge -a app-portage/iwdevtools

This thing scrubs the patch thoroughly and all we have to do is insert a short description what this patch is for, references to bugs and our tag. I wrote a function for Emacs to call scrub-patch on the current buffer or on the marked file in dired, so we don't have to go to the shell that often. In dired we mark files with m then call M-x ebuilds/scrub-patch. Or, if we have a patch-file open in the currently open buffer, just M-x ebuilds/scrub-patch.

CODE
(defun ebuilds/scrub-patch (&optional @fname)
"Call scrub-patch on marked file in dired or on current file.
 Needs app-portage/iwdevtools.
 Got this from xah lee, modified a bit
 URL `http://xahlee.info/emacs/emacs/emacs_dired_open_file_in_ext_apps.html'"
  (interactive)
  (let* (
         ($file-list
          (if @fname
              (progn (list @fname))
            (if (string-equal major-mode "dired-mode")
                (dired-get-marked-files)
              (list (buffer-file-name)))))
         ($do-it-p (if (<= (length $file-list) 5)
                       t
                     (y-or-n-p "Scrub more than 5 files? "))))
    (when $do-it-p
        (mapc
         (lambda ($fpath)
           (shell-command
            (concat "scrub-patch -c -i " (shell-quote-argument $fpath))))  $file-list)
        (when (not (string-equal major-mode "dired-mode"))
            (revert-buffer)))))

repo-cd integration

repo-cd is a useful tool from app-portage/iwdevtools. Given a package name it will change directory of the shell into the packages directory in a Gentoo repository. It also displays some information of the package, like the upstream URI, but we don't need that. I need this in emacs to conveniently go to a packages ebuild.

CODE
(defun ebuilds/repo-cd (package)
  "Change emacs' curret directory to a package directory in a gentoo repository.
   Needs app-portage/iwdevtools"
  (interactive "sEnter package name: ")
  (find-file
   (car
    (split-string
     (shell-command-to-string
      (concat "repo-cd -q " package " 2>/dev/null"))
     nil t))))

Or if you need the additional information repo-cd prints out when changing directory:

CODE
(defun ebuilds/repo-cd (package)
  "Change emacs' curret directory to a package directory in a gentoo repository.
   Needs app-portage/iwdevtools"
  (interactive "sEnter package name: ")
  (message "%s"
     (shell-command-to-string
      (concat "repo-cd --no-color " package " 1>/dev/null")))
  (find-file
   (car
    (split-string
     (shell-command-to-string
      (concat "repo-cd -q " package " 2>/dev/null"))
     nil t))))

Right now this always chooses the default when severeal search results come up. But I found that this is sufficient enough for me.

Use M-x ebulids/repo-cd or bind to a key:

CODE
(map! :localleader
      :map ebuild-mode-map
      "c" #'ebuilds/repo-cd)      ;; go to dir of ebuild

Call qa-vdb from emacs

I also need to call qa-vdb from app-portage/iwdevtools quite often, so I made a simple emacs integration for it.

CODE
(defun ebuilds/qa-vdb (package)
  "Call qa-vdb to check if the binary of a package against the ebuilds
dependencies. Needs app-portage/iwdevtools"
  (interactive "sEnter package name: ")
  (message "%s"
     (shell-command-to-string
      (concat "qa-vdb --no-color " package))))

Link to bugs from ebuild files

Usually what Gentoo devs do when they want to link to a bug from an ebuild file is they either link with the whole URL (e.g. https://bugs.gentoo.org/12345) or they write #12345. For the latter there is Emacs' bug-reference-mode. In the config we can set up bug-reference mode to interpret five digit long numbers with a preceding # as link to bugs.gentoo.org when ebuild-mode is started.

CODE
(add-hook 'ebuild-mode-hook #'(lambda ()
                (setq bug-reference-bug-regexp "\\(#\\([0-9]\\{5\\}\\)\\)")
                (setq bug-reference-url-format "https://bugs.gentoo.org/%s")
                (bug-reference-prog-mode)))

See also