¿Cómo parcho un paquete de Emacs?

16

Deseo modificar un paquete, probarlo y, con suerte, enviar una solicitud de extracción después. ¿Cómo lo hago de manera segura y eficiente? La pregunta puede parecer demasiado amplia, aceptaré la respuesta que cubre los siguientes problemas:

  1. Esperaría instalar una rama separada de un paquete y poder cambiar entre ella y una rama estable por capricho, con la recompilación realizada automáticamente cuando sea necesario, pero package.elno parece ofrecer una forma sencilla de hacerlo. Esta respuesta en emacs-SE nos informa que "si se instalan varias copias de un paquete, entonces se cargará la primera", por lo que supongo que uno podría meterse manualmente, load-pathpero esto no parece robusto. ¿Cuál es la forma estándar de seleccionar una versión específica del paquete entre los instalados?

  2. Incluso si logro exponer varias ramas a Emacs, para ajustes importantes necesito asegurarme de que la rama sin parche esté "descargada" y sus efectos secundarios aislados. ¿ unload-featureManeja esto correctamente o tal vez tiene idiosincrasias que todo probador de paquetes de versiones múltiples debería conocer?

  3. ¿Cómo instalo y pruebo la versión local? La respuesta parece depender de si el paquete es simple (= un archivo) o de múltiples archivos. EmacsWiki dice acerca de los paquetes de múltiples archivos : " MELPA crea paquetes para usted ". Dudo que deba (o deba) hablar con MELPA cada vez que cambie un defunformulario en un paquete de múltiples archivos, pero la pregunta continúa. Al menos necesito decirle al administrador de paquetes sobre la versión local, y si es así, ¿cómo lo hago?

  4. ¿Qué nombres debo asignar a las versiones locales de paquetes? Supongamos que quiero trabajar en múltiples funciones o errores simultáneamente, lo que significa tener varias ramas. Emacs no permitirá nombrar versiones de manera descriptiva (en la línea de 20170117.666-somebugorfeature). Supongo que podría cambiar el nombre del paquete en sí, un sufijo por rama, pero nuevamente, como meterse manualmente load-pathen Q1, este es un truco feo, por lo que no lo intentaré con algo que pretendo enviar en sentido ascendente a menos que sea una práctica ampliamente aceptada .

Las preguntas probablemente son ingenuas, ya que nunca escribí un parche ni apliqué uno con git o un vcs similar. Sin embargo, para muchos usuarios de Emacs, aplicar un parche a un paquete de Emacs podría ser su primer (o quizás el único) esfuerzo de programación social, por lo que creo que las respuestas a esta pregunta seguirían siendo valiosas.

akater
fuente

Respuestas:

7

Para intervenir con un flujo de trabajo ligeramente diferente para cargar diferentes versiones de paquetes, aquí hay un par de variaciones de lo que hago, que usan load-pathpara controlar qué versión estoy usando (cambiar el nombre del paquete es una mala idea si hay dependencias) Tengo la versión actual de "buen paquete" instalada en ~/.emacs.d/elpael uso M-x package-install, y el clon paquete de recompra en ~/src/nice-packagecon git clone ....

Con paquete de uso

En init.el, tengo

(use-package nice-package
  :load-path "~/src/nice-package"
  ...)

Con la :load-pathlínea sin comentarios, esto usará la versión git del paquete. Al comentar esta línea, y volver a cargar emacs usa la versión elpa.

Similar sin paquete de uso

En init.el,

(add-to-list 'load-path "~/src/nice-package")
(require 'nice-package)
...

Ahora haz el mismo truco para comentar con la primera línea.

Utilizando emacs -L

Esta es la misma idea, pero manipulando load-pathdesde la línea de comandos. Puede cargar una instancia de emacs con la versión git del paquete con

emacs -L ~/src/nice-package

que solo antepone esta ruta al frente de la ruta de carga. De esa manera, puede iniciar emacsdesde una terminal diferente y hacer que las versiones antiguas y nuevas del paquete se ejecuten una al lado de la otra.

Comentarios misceláneos

  1. Úselo M-x eval-bufferdespués de editar un archivo de paquete para cargar las nuevas definiciones que ha creado.
  2. Comprobando lo que dice el compilador con M-x emacs-lisp-byte-compile también es útil
justbur
fuente
Estoy usando el emacs -Lenfoque para cargar una versión local de un paquete que también he instalado globalmente usando Cask. Una cosa que me desconcertó fue que la ejecución <package>-versionsiempre devuelve la versión instalada globalmente, incluso cuando en realidad estaba ejecutando la versión local modificada. Resulta que esto se debió a que <package>-versionpara este paquete obtiene la versión packages.el.
ntc2
3

¡Buena pregunta! La respuesta es que hasta ahora, no había una buena respuesta, ya que ninguno de los administradores de paquetes existentes fueron diseñados para este caso de uso (excepto Borg , pero Borg no intenta manejar otras operaciones comunes de administración de paquetes como el manejo de dependencias) .

Pero ahora, hay straight.elun administrador de paquetes de próxima generación para Emacs que aborda este problema de la manera más completa posible. Descargo de responsabilidad: escribí straight.el!

Después de insertar el fragmento de arranque , instalar un paquete es tan simple como

(straight-use-package 'magit)

Esto clonará el repositorio de Git para Magit, compilará el paquete mediante la unión simbólica de sus archivos en un directorio separado, compilará byte, generará y evaluará cargas automáticas y configurará load-pathcorrectamente. Por supuesto, si el paquete ya está clonado y construido, no pasa nada y su tiempo de inicio no se ve afectado.

¿Cómo se realizan cambios en Magit? Es trivial! ¡Solo use M-x find-functiono M-x find-librarypara saltar al código fuente y piratear! Puede evaluar sus cambios para probarlos en vivo, como es una práctica general para el desarrollo de Emacs Lisp, y cuando reinicie Emacs, el paquete se reconstruirá automáticamente, se volverá a compilar, etc. Es completamente automático e infalible.

Cuando esté satisfecho con sus cambios, simplemente confirme, presione y haga una solicitud de extracción. Tienes control total sobre tus paquetes locales. Pero su configuración aún puede ser 100% reproducible porque puede solicitar straight.elhacer un archivo de bloqueo que guarde las revisiones de Git de todos sus paquetes, incluidosstraight.el mismo, MELPA, etc.

straight.elpuede instalar cualquier paquete de MELPA, GNU ELPA o EmacsMirror. Pero también tiene una receta DSL altamente flexible que le permite instalar desde cualquier lugar, así como personalizar cómo se construye el paquete. Aquí hay un ejemplo que muestra algunas de las opciones:

(straight-use-package
 '(magit :type git 
         :files ("lisp/magit*.el" "lisp/git-rebase.el"
                 "Documentation/magit.texi" "Documentation/AUTHORS.md"
                 "COPYING" (:exclude "lisp/magit-popup.el"))
         :host github :repo "raxod502/magit"
         :upstream (:host github :repo "magit/magit")))

straight.eltiene documentación ridículamente completa. Lea todo sobre esto en GitHub .

Radon Rosborough
fuente
2

¡Todas estas son buenas preguntas!

Emacs funciona en un modelo de imagen de memoria, donde cargar nuevo código altera la imagen de memoria de la instancia en ejecución. La definición de nuevas funciones y variables se deshace fácilmente, si mantiene una lista de ellas, pero hay muchos efectos secundarios que un módulo podría tener que desearía deshacer. Sin unload-featureembargo, parece que funciona bastante bien.

Creo que lo que querrá hacer es una combinación de codificación en vivo y ocasionalmente relanzar Emacs, cargando el módulo en el que está trabajando desde su rama en lugar de desde donde está instalado. Si termina con muchas de estas ramas, es posible que desee un script de shell que inicie emacs con el correcto load-pathpara el que está trabajando en este momento. En cualquier caso, no cambiaría el nombre del paquete; Creo que sería aún más confuso ya que emacs podría cargarlos a ambos.

A medida que desarrolle sus parches, puede comenzar simplemente redefiniendo las funciones que está cambiando en su sesión de Emacs en vivo. Esto le permite probar las nuevas definiciones inmediatamente, sin salir de Emacs. Específicamente, a medida que edita un archivo elisp puede usar C-M-x( eval-defun) para evaluar la función actual en su sesión actual de Emacs. Luego puede llamarlo para asegurarse de que funciona. Si está cambiando algo que sucede al inicio de Emacs, es probable que tenga que iniciar y detener Emacs para probarlo; puede hacerlo iniciando y deteniendo un proceso Emacs separado para que su sesión de edición no se interrumpa.

db48x
fuente
2

No creo que haya una buena respuesta para eso todavía (espero que pueda obtener una solución parcial con Cask, aunque no estoy lo suficientemente familiarizado para darle una buena respuesta al usarla; espero que alguien más lo haga), pero aquí está lo que hago (rara vez uso un paquete de Elisp sin hacer cambios locales, por lo que es realmente mi forma "normal"):

  • cd ~/src; git clone ..../elpa.git
  • para cada paquete cd ~/src/elisp; git clone ....thepackage.git
  • cd ~/src/elpa/packages; ln -s ~/src/elisp/* .
  • cd ~/src/elpa; make
  • en su ~/.emacscomplemento

    (eval-after-load 'package
     '(add-to-list 'package-directory-list
                   "~/src/elpa/packages"))
    

De esta forma, todos los paquetes se instalan "directamente desde el Git", un simple cd ~/src/elpa; makerecompilará aquellos que lo necesiten y C-h o thepackage-functionsaltará a un archivo fuente que está bajo Git.

Para "cambiar entre él y una rama estable por capricho", necesitará git checkout <branch>; cd ~/src/elpa; make; y si desea que afecte la ejecución de sesiones de Emacs, tomará más trabajo. En general, recomiendo no usar, unload-featureexcepto en situaciones excepcionales (es una buena característica, pero actualmente no es lo suficientemente confiable).

Tampoco satisface muchos de sus requisitos. Y tiene algunas desventajas adicionales, principalmente el hecho de que el clon Git de muchos paquetes no coincide con el diseño y los contenidos esperados por el makefile de elpa.git, por lo que deberá comenzar ajustando esos paquetes (generalmente cosas que tienen que ver con <pkg>-pkg.el, dado que el archivo make de elpa.git espera construir este archivo en <pkg>.ellugar de proporcionarlo, pero lo más problemático es que la compilación se realiza de manera diferente, por lo que a veces es necesario jugar con el requires).

Ah, y por supuesto, esto básicamente significa que está instalando esos paquetes a mano, por lo que debe prestar atención a las dependencias. Esta configuración interactúa correctamente con otros paquetes instalados por package-install, aunque no es tan terrible.

Stefan
fuente
2

Las otras respuestas a esta pregunta, incluida mi otra respuesta , hablan sobre parchear un paquete de Emacs haciendo cambios en su código. Pero las personas que encuentran esta pregunta a través de Google podrían estar pensando en otra cosa cuando dicen "parchear un paquete de Emacs", es decir, anular su comportamiento sin tener que modificar su código fuente.

Los mecanismos para hacer esto incluyen, en orden creciente de agresividad:

A pesar del poder de las dos primeras opciones, me encontré tomando la tercera ruta con bastante frecuencia, ya que a veces no hay otra manera. Pero entonces la pregunta es, ¿qué pasa si cambia la definición de la función original? ¡No tendría forma de saber que necesita actualizar la versión de esa definición que copió y pegó en su archivo init!

Debido a que estoy obsesionado con parchear cosas, escribí el paquete el-patch, que resuelve este problema de la manera más completa posible. La idea es que defina diferencias basadas en la expresión s en su archivo init, que describen tanto la definición de la función original como sus cambios. Esto hace que sus parches sean mucho más legibles, y también permite el-patchvalidar posteriormente si la definición de función original se ha actualizado desde que realizó su parche. (Si es así, ¡le mostrará los cambios a través de Ediff!) Citando de la documentación:

Considere la siguiente función definida en el company-statisticspaquete:

(defun company-statistics--load ()
  "Restore statistics."
  (load company-statistics-file 'noerror nil 'nosuffix))

Supongamos que queremos cambiar el tercer argumento de nila 'nomessage, para suprimir el mensaje que se registra cuando company-statisticscarga su archivo de estadísticas. Podemos hacerlo colocando el siguiente código en nuestro init.el:

(el-patch-defun company-statistics--load ()
  "Restore statistics."
  (load company-statistics-file 'noerror
        (el-patch-swap nil 'nomessage)
        'nosuffix))

Simplemente llamar en el-patch-defunlugar de defundefinir un parche no operativo: es decir, no tiene ningún efecto (bueno, no del todo, ver más adelante ). Sin embargo, al incluir directivas de parches , puede hacer que la versión modificada de la función sea diferente de la original.

En este caso, usamos la el-patch-swapdirectiva. El el-patch-swapformulario se reemplaza con nilla definición original (es decir, la versión que se compara con la definición "oficial" company-statistics.el) y con 'nomessagela definición modificada (es decir, la versión que realmente se evalúa en el archivo init).

Radon Rosborough
fuente
0

Cuando haga muchos cambios, creo que debería usarlos straight.el, vea la respuesta de Radon Rosborough .

Si solo desea realizar un cambio único, supongamos que se trata de un proyecto llamado fork-mode, realice los siguientes pasos:

  • Haz un directorio para almacenar el git mkdir ~/.emacs.d/lisp-gits
  • Haga una bifurcación del proyecto que desea cambiar, diga en https://github.com/user/fork-mode
  • Clona tu tenedor cd ~/.emacs.d/lisp-gits && git clone [email protected]:user/fork-mode.git

Escriba el siguiente código en su .emacs

(if (file-exists-p "~/.emacs.d/lisp-gits/fork-mode")
    (use-package fork-mode :load-path "~/.emacs.d/lisp-gits/fork-mode")
  (use-package fork-mode :ensure t))

(use-package fork-mode
  :config
  (setq fork-mode-setting t)
  :hook
  ((fork-mode . (lambda () (message "Inside hook"))))

Ahora puede usar el modo emacs, C-h fpara encontrar las funciones que desea cambiar. Notarás que cuando el paquete se instala en lisp-gits, saltarás allí. Use magit u otros comandos git para confirmar / enviar cambios y luego use github para enviar sus solicitudes de extracción.

Una vez que se aceptan sus solicitudes de extracción, puede eliminar el proyecto ~/.emacs.d/lisp-gitsy dejar que el administrador de paquetes haga su trabajo.

ppareit
fuente