Come posso patchare un pacchetto Emacs?


16

Vorrei modificare un pacchetto, testarlo e spero di inviare successivamente una richiesta pull. Come lo faccio in modo sicuro ed efficiente? La domanda potrebbe sembrare troppo ampia, accetterò la risposta che copre i seguenti problemi:

  1. Mi aspetterei di installare un ramo separato di un pacchetto e di essere in grado di passare da un ramo all'altro per un capriccio, con la ricompilazione eseguita automaticamente quando è necessario, ma package.elnon sembra offrire un modo semplice per farlo. Questa risposta su emacs-SE ci informa che "Se sono installate più copie di un pacchetto, allora il primo verrà caricato", quindi immagino che si possa fare un casino manualmente load-pathma questo non sembra affidabile. Qual è il modo standard di selezionare una versione specifica del pacchetto tra quelli installati?

  2. Anche se riesco a esporre diversi rami a Emacs, per modifiche significative devo assicurarmi che il ramo senza patch sia "scaricato" e che i suoi effetti collaterali siano isolati. Gestisce unload-featurequesto correttamente o forse ha delle idiosincrasie che ogni tester di pacchetti multi-versione dovrebbe conoscere?

  3. Come installo e collaudo la versione locale? La risposta sembra dipendere dal fatto che il pacchetto sia semplice (= un file) o multiplo. EmacsWiki dice dei pacchetti multi-file : " MELPA crea pacchetti per te ". Dubito di dover (o dovrei) parlare con MELPA ogni volta che cambio un defunmodulo in un pacchetto multiplo, ma la domanda rimane. Almeno devo dire al gestore pacchetti della versione locale e, in tal caso, come posso farlo?

  4. Quali nomi devo assegnare alle versioni locali dei pacchetti? Supponiamo che io voglia lavorare su più funzionalità o bug contemporaneamente, il che significa avere diversi rami. Emacs non consentirà di nominare le versioni in modo descrittivo (lungo le linee di 20170117.666-somebugorfeature). Immagino che potrei rinominare il pacchetto stesso, un suffisso per ramo, ma ancora una volta, come fare casini load-pathnel Q1, questo è un brutto hack, quindi non lo proverò con qualcosa che intendo inviare a monte a meno che non sia una pratica ampiamente accettata .

Le domande probabilmente sono ingenue, dal momento che non ho mai scritto una patch né applicato uno con git o un VCC simile. Tuttavia, per molti utenti di Emacs, l'applicazione di patch a un pacchetto Emacs potrebbe essere il loro primo (o forse l'unico) sforzo di programmazione sociale, motivo per cui, credo, le risposte a questa domanda sarebbero comunque utili.

Risposte:


7

Per entrare in contatto con un flusso di lavoro leggermente diverso per il caricamento di diverse versioni di pacchetti, ecco un paio di varianti di quello che faccio, entrambi i quali usano il load-pathper controllare quale versione sto usando (cambiare il nome del pacchetto è una cattiva idea se ci sono dipendenze). Ho installato la versione corrente di "nice-package" in ~/.emacs.d/elpauso M-x package-installe il repository di pacchetti clona in ~/src/nice-packagecon git clone ....

Con pacchetto d'uso

In init.el, ho

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

Con la :load-pathriga non commentata, verrà utilizzata la versione git del pacchetto. Commentando questa riga e ricaricando emacs si utilizza la versione elpa.

Simile senza pacchetto d'uso

In init.el,

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

Ora fai lo stesso trucco con i commenti con la prima riga.

utilizzando emacs -L

Questa è la stessa idea, ma manipolando load-pathdalla riga di comando. Puoi caricare un'istanza di emacs con la versione git del pacchetto con

emacs -L ~/src/nice-package

che antepone questo percorso all'inizio del percorso di caricamento. In questo modo, è possibile avviare emacsda un terminale diverso e far funzionare le versioni precedenti e nuove del pacchetto.

Commenti vari

  1. Utilizzare M-x eval-bufferdopo aver modificato un file del pacchetto per caricare le nuove definizioni create.
  2. Controllare anche ciò che dice il compilatore M-x emacs-lisp-byte-compileè utile

Sto usando l' emacs -Lapproccio per caricare una versione locale di un pacchetto che ho anche installato a livello globale usando Cask. Una cosa che mi ha sconcertato è che l'esecuzione <package>-versionrestituisce sempre la versione installata a livello globale, anche quando stavo effettivamente eseguendo la versione modificata locale. Si scopre che ciò è dovuto al fatto che <package>-versionper questo pacchetto ottiene la versione da packages.el.
ntc2,

3

Buona domanda! La risposta è che fino ad ora non c'era una buona risposta, poiché nessuno dei gestori di pacchetti esistenti era progettato per questo caso d'uso (tranne Borg , ma Borg non tenta di gestire altre operazioni comuni di gestione dei pacchetti come la gestione delle dipendenze) .

Ma ora esiste straight.elun gestore di pacchetti di prossima generazione per Emacs che affronta questo problema nel modo più completo possibile. Disclaimer: ho scritto straight.el!

Dopo aver inserito lo snippet di bootstrap , installare un pacchetto è semplice come

(straight-use-package 'magit)

Questo clonerà il repository Git per Magit, costruirà il pacchetto collegando i suoi file in una directory separata, compilando byte, genererà e valuterà i caricamenti automatici e configurerà load-pathcorrettamente. Naturalmente, se il pacchetto è già stato clonato e compilato, non accade nulla e il tuo tempo di avvio non ne risente.

Come si apportano modifiche a Magit? È banale! Basta usare M-x find-functiono M-x find-libraryper saltare al codice sorgente e hackerare! Puoi valutare le tue modifiche per testarle dal vivo, come è prassi generale per lo sviluppo di Emacs Lisp, e quando riavvierai Emacs, il pacchetto verrà automaticamente ricostruito, ricompilato e così via. È completamente automatico e infallibile.

Quando sei soddisfatto delle modifiche, esegui semplicemente il commit, il push e invia una richiesta pull. Hai il controllo totale sui tuoi pacchetti locali. Ma la tua configurazione può essere ancora riproducibile al 100% perché puoi chiedere straight.eldi creare un file di blocco che salvi le revisioni Git di tutti i tuoi pacchetti, incluso straight.else stesso, MELPA e così via.

straight.elpuò installare qualsiasi pacchetto da MELPA, GNU ELPA o EmacsMirror. Ma ha anche una ricetta DSL altamente flessibile che ti consente di installare da qualsiasi luogo, nonché di personalizzare la modalità di creazione del pacchetto. Ecco un esempio che mostra alcune delle opzioni:

(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.elha una documentazione ridicolmente completa. Leggi tutto su GitHub .


2

Queste sono tutte buone domande!

Emacs funziona su un modello di immagine di memoria, in cui il caricamento di un nuovo codice altera l'immagine di memoria dell'istanza in esecuzione. La definizione di nuove funzioni e variabili è facilmente annullata, se si mantiene un elenco di esse, ma ci sono molti effetti collaterali che un modulo potrebbe avere che si vorrebbe annullare. Sembra che unload-featureci riesca piuttosto bene.

Penso che ciò che vorrai fare sia una combinazione di codifica live e di tanto in tanto il riavvio di Emacs, caricando il modulo su cui stai lavorando dal tuo ramo anziché da dove è installato. Se finisci con molti di questi rami, potresti volere uno script di shell che avvii emacs con quello corretto load-pathper quello su cui stai lavorando al momento. In ogni caso non rinominerei il pacchetto; Penso che sarebbe ancora più confuso poiché emacs potrebbe caricarli entrambi.

Mentre sviluppi le tue patch, puoi iniziare semplicemente ridefinendo le funzioni che stai cambiando proprio nella tua sessione Emacs live. Ciò consente di testare immediatamente le nuove definizioni, senza uscire da Emacs. In particolare, mentre modifichi un file elisp puoi usare C-M-x( eval-defun) per valutare la funzione corrente nella tua sessione Emacs corrente. È quindi possibile chiamarlo per assicurarsi che funzioni. Se stai cambiando qualcosa che accade all'avvio di Emacs, probabilmente dovrai avviare e interrompere Emacs per testarlo; potresti farlo avviando e arrestando un processo Emacs separato in modo che la sessione di modifica non venga interrotta.


2

Non credo che ci sia ancora una buona risposta (mi aspetto che tu possa ottenere una soluzione parziale con Cask, anche se non ho abbastanza familiarità con esso per darti una buona risposta utilizzandola; spero che qualcun altro lo farà), ma ecco cosa faccio (uso raramente un pacchetto Elisp senza apportare modifiche locali ad esso, quindi è davvero il mio modo "normale"):

  • cd ~/src; git clone ..../elpa.git
  • per ogni pacchetto cd ~/src/elisp; git clone ....thepackage.git
  • cd ~/src/elpa/packages; ln -s ~/src/elisp/* .
  • cd ~/src/elpa; make
  • nella tua ~/.emacsaggiunta

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

In questo modo, tutti i pacchetti vengono installati "direttamente da Git", un semplice cd ~/src/elpa; makericompilerà quelli che ne hanno bisogno e C-h o thepackage-functionpasserà a un file sorgente sotto Git.

Per "alternare tra esso e il ramo stabile per un capriccio", è necessario git checkout <branch>; cd ~/src/elpa; make; e se vuoi che influenzi l'esecuzione delle sessioni di Emacs ci vorrà più lavoro. In genere raccomando di non usare unload-featurese non in situazioni eccezionali (è una buona funzionalità, ma al momento non è abbastanza affidabile).

Inoltre non soddisfa molte delle tue esigenze. E ha alcuni aspetti negativi, soprattutto il fatto che il clone Git di molti pacchetti non corrisponde esattamente al layout e ai contenuti previsti dal makefile di elpa.git, quindi dovrai iniziare modificando quei pacchetti (in genere cose che hanno a che fare con <pkg>-pkg.el, poiché il makefile di elpa.git prevede di compilare questo file <pkg>.elpiuttosto che fornirlo , ma più problematicamente, la compilazione viene eseguita in modo diverso, quindi a volte è necessario giocare con i file requires).

Oh, e ovviamente, questo in pratica significa che stai installando questi pacchetti a mano, quindi devi prestare attenzione alle dipendenze. Questa configurazione interagisce correttamente con altri pacchetti installati da package-installtho, quindi non è poi così terribile.


2

Le altre risposte a questa domanda, compresa la mia altra risposta , parlano della correzione di un pacchetto Emacs modificando il suo codice. Ma le persone che trovano questa domanda tramite Google potrebbero pensare a qualcos'altro quando dicono "patch un pacchetto Emacs" - vale a dire, sovrascrivendo il suo comportamento senza dover modificare il suo codice sorgente.

I meccanismi per farlo includono, in ordine crescente di aggressività:

  • aggiunta di funzioni agli hook o associazione di variabili dinamiche mediantelet
  • il potente sistema di consulenza
  • semplicemente ignorando la funzione che si desidera modificare

Nonostante la potenza delle prime due opzioni, mi sono ritrovato a prendere la terza strada abbastanza spesso, poiché a volte non c'è altro modo. Ma poi la domanda è: cosa succede se la definizione della funzione originale cambia? Non avresti modo di sapere che dovevi aggiornare la versione di quella definizione che avevi copiato e incollato nel tuo file init!

Dato che sono ossessionato dalle patch, ho scritto il pacchetto el-patch, che risolve questo problema nel modo più completo possibile. L'idea è di definire differenze basate sull'espressione s nel file init, che descrivono sia la definizione della funzione originale sia le modifiche ad essa. Ciò rende le patch molto più leggibili e consente anche di verificare el-patchin seguito se la definizione della funzione originale è stata aggiornata da quando è stata creata la patch. (In tal caso, ti mostrerà le modifiche tramite Ediff!) Citando dalla documentazione:

Considera la seguente funzione definita nel company-statisticspacchetto:

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

Supponiamo di voler cambiare il terzo argomento da nila 'nomessage, per sopprimere il messaggio che viene registrato quando company-statisticscarica il suo file delle statistiche. Possiamo farlo inserendo il seguente codice nel nostro init.el:

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

Chiamare semplicemente el-patch-defuninvece di defundefinire una patch no-op: cioè, non ha alcun effetto (beh, non del tutto — vedi più avanti ). Tuttavia, includendo le direttive patch , è possibile rendere la versione modificata della funzione diversa dall'originale.

In questo caso, utilizziamo la el-patch-swapdirettiva. Il el-patch-swapmodulo viene sostituito con nilnella definizione originale (ovvero, la versione che viene confrontata con la definizione "ufficiale" in company-statistics.el) e con 'nomessagela definizione modificata (ovvero, la versione effettivamente valutata nel file init).


0

Quando apporti molte modifiche, penso che dovresti usare straight.el, vedi la risposta di Radon Rosborough .

Se si desidera solo apportare una modifica una tantum, supponiamo che si tratti di un progetto chiamato fork-mode, procedere come segue:

  • Crea una directory per memorizzare git mkdir ~/.emacs.d/lisp-gits
  • Fai un fork del progetto che desideri modificare, ad esempio https://github.com/user/fork-mode
  • Clona la tua forchetta cd ~/.emacs.d/lisp-gits && git clone git@github.com:user/fork-mode.git

Scrivi il seguente codice nel tuo .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"))))

Ora puoi usare la modalità emacs, usando C-h fper trovare le funzioni che vuoi cambiare. Noterai che quando il pacchetto verrà installato in lisp-gits, passerai a lì. Usa magit o altri comandi git per eseguire il commit / push delle modifiche, quindi usa github per inviare le tue richieste pull.

Una volta accettate le richieste pull, puoi semplicemente rimuovere il progetto ~/.emacs.d/lisp-gitse lasciare che il gestore pacchetti faccia il suo lavoro.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.