Environnement persistant pour la compilation Mx


8

Lorsque je l'exécute, M-x compileil génère un nouveau sous-shell pour exécuter ma commande de compilation. Dès que la commande de compilation revient, le processus shell est tué. Je peux voir pourquoi cela serait souhaitable dans la plupart des cas, mais je suis actuellement dans une situation où cela n'est pas utile.

Je travaille actuellement dans un environnement de build spécialisé qui nécessite que je prenne quelques mesures initiales pour configurer la build avant d'exécuter le compilateur. Tant que l'environnement persiste, je n'ai besoin d'effectuer les étapes de configuration qu'une seule fois. Mais lorsque j'utilise M-x compilecela signifie que je dois faire les étapes chaque fois que je veux compiler ou recompiler.

Existe-t-il un moyen de générer un sous-shell qui persistera en arrière-plan? Un que M-x compileet M-x gdbpeut utiliser chaque fois qu'ils ont besoin d'exécuter un processus shell?


Motivation:

J'ai un programme (que nous appellerons xcc) qui construit du code C pour des plateformes spéciales. Afin de construire mon code, je commence d'abord xccpar l' tcshinvite:

$ xcc

Le programme prend plus de 10 secondes à charger, puis je peux entrer des commandes à l'invite interactive

xcc>> add target myprogram
xcc>> set source myprogram $PROJDIR/src/
xcc>> set includes myprogram $PROJDIR/include/
xcc>> set type myprogram primitive
xcc>> set inputs myprogram int8,int8
xcc>> set outputs myprogram fix16,fix16
xcc>> build myprogram

Les étapes ci-dessus peuvent être intégrées dans une macro personnalisée buildmyprog.macroafin que je puisse l'exécuter directement à partir du shell ou d'Emacs avecM-x compile

$ xcc buildmyprog.macro

Le principal problème de cette approche est le fait qu'il faut au xccprogramme 10 secondes pour se charger, avant même que la compilation ne commence. Je me suis assez fatigué d'attendre les 10 secondes supplémentaires chaque fois que j'ai compilé que j'ai commencé à courir xccdans un ansi-termdans un tampon séparé. Maintenant, après avoir modifié et enregistré le code, je passe au ansi-termtampon et je lance

xcc>> build myprogram

Cela fonctionne très bien, mais chaque fois que je passe à ce tampon, je pense: "Ne serait-ce pas génial si je pouvais simplement pousser F7et que ma commande de compilation serait envoyée à l'instance déjà en cours d'exécution de xcc?"


Quel effet votre configuration a-t-elle sur l'environnement? Variables d'environnement, fichiers temporaires?
T. Verron

L'environnement est son propre programme. Bien qu'il utilise des variables d'environnement et des fichiers temporaires, le programme lui-même conserve ses propres états intérieurs qui disparaissent à la fermeture du programme.
nispio

3
Sur la base de l'édition, je pense que vous avez besoin d'un comintmode dérivé pour votre environnement. Si vous vous sentez aventureux, voici un guide pour en écrire un.
Vamsi du

@Vamsi Je me sens aventureux, et cela ressemble à une excellente piste. Merci.
nispio

1
Le moyen le plus simple et le plus sale d'envoyer une commande à un tampon comint est de l'insérer dans le tampon et d'appeler comint-send-input. C'est essentiellement ce que vous faites à la main, le convertir en elisp ne devrait pas être très difficile (surtout par rapport à la configuration de comint).
T. Verron

Réponses:


2

Pouvez-vous faire la configuration dans votre shell avant de démarrer emacs? Le compilesous-shell doit hériter de l'environnement de ses grands-parents via emacs.


1
Imaginez que «l'environnement» que j'exécute est Python. (Ce n'est pas Python.) Même si je peux démarrer Emacs depuis l'interpréteur Python, M-x compileil n'envoie pas ses commandes à l'interpréteur Python.
nispio

0

Utilisez ensuite un véritable shell dans Emacs, comme eshellou ansi-term. Vous devez utiliser compilepour la compilation, bien qu'il puisse être utilisé pour exécuter n'importe quelle commande.

Je ne pense pas que ce soit possible avec la commande de compilation, mais il me manque peut-être quelque chose. Sinon, vous pouvez mettre toutes les étapes de configuration dans un script shell et exécuter ce script avec M-x compile.


3
Eh bien, oui, mais la question est alors de savoir comment le connecter M-x compileou une autre commande qui compilera le fichier actuel.
Gilles 'SO- arrête d'être méchant'

@Gilles l'OP veut un environnement shell cohérent entre les M-x compileappels, et c'est ce que fait un shell réel. Je ne pense pas que ce soit possible avec la commande de compilation, mais il me manque peut-être quelque chose. Sinon, l'OP pourrait mettre toutes les étapes de configuration dans un script shell et exécuter ce script avecM-x compile
Tu Do

Je ne suis pas sûr de comprendre votre première suggestion. Êtes-vous en train de dire "Utiliser eshellpour les choses shell et compilepour compiler les choses"? Cela bat tout le point.
nispio

J'ai essayé la deuxième suggestion, mais j'ai eu quelques problèmes. (Ce qui m'a amené à poster cette question.) C'est trop lent car cela ajoute beaucoup de surcharge supplémentaire à chaque compilation. Cela est inutile, car je ne devrais avoir à configurer l'environnement qu'une seule fois. Cela finit également par ne pas être suffisamment dynamique, car je ne peux pas simplement modifier une chose dans l'environnement actuel et essayer de compiler à nouveau. L'environnement de construction est un programme, donc je ne peux pas écrire un script shell pour le contrôler. Je dois écrire un script de macro compilé pour l'environnement et inclure la commande de compilation elle-même dans la macro.
nispio

0

Une solution possible serait d'avoir un interprète permanent en direct dans un tampon dédié (un peu comme le *compilation*tampon, mais où le processus sous-jacent ne reviendrait jamais). Vous pourriez alors avoir une recompilecommande -like pour envoyer une commande de compilation prédéfinie à l'interpréteur et afficher les résultats.

Voici une mise en œuvre provisoire d'une telle stratégie. La persistent-compilecommande s'occupe à la fois de l'initialisation du processus (lorsqu'il est appelé pour la première fois ou avec un argument préfixe) et de la recompilation (si le processus interpréteur est déjà en cours d'exécution). Les résultats de la compilation sont affichés dans un *persistent-compilation*tampon, mis compilation-shell-minor-modeà profit pour bénéficier des compilation-modefonctionnalités habituelles pour naviguer entre les erreurs.

Voici un exemple d'utilisation, avec le contenu résultant du *persistent-compilation*tampon:

  1. M-xpersistent-compileRET /bin/bashRET FOO=barRET echo $FOORET

    $ FOO=bar
    $ echo $FOO
    bar
    $
    
  2. M-xpersistent-compileRET

    $ echo $FOO
    bar
    $
    

Attention, le code suivant n'a pas été beaucoup testé. Par exemple, je ne sais pas ce qui se passe lorsque vous essayez de recompiler avant la fin de la compilation précédente.

(defvar persistent-compile-interpreter "/bin/bash"
  "Interpreter to be used for persistent compilations.")

(defvar persistent-compile-init ""
  "Initialization command for persistent compilations.")

(defvar persistent-compile-command "make -k"
  "Compilation command for persistent compilations.")

;; Local variable in the persistent compilation buffer
(defvar persistent-compile--next-action)

(defun persistent-compile (&optional edit-command)
  "(Re-)run a persistent compilation.

The first time a persistent compilation is run, the user is asked
for an interpreter, an initialization command and a compilation
command.
The interpreter is started and optionally set up with the
initialization command.  The compilation command is then sent to
the interpreter.

All subsequent recompilations are sent to the same,
already-initialized interpreter, so as to keep the customized
environment.

If EDIT-COMMAND is non-nil, the user can edit the
parameters (interpreter, initialization command and compilation
command) and the interpreter is restarted."
  (interactive "P")
  (when (or edit-command
            (null (get-buffer "*persistent-compilation*"))
            (not persistent-compile-interpreter)
            (not persistent-compile-init)
            (not persistent-compile-command))
    (setq persistent-compile-interpreter (read-from-minibuffer "Interpreter: "
                                                               persistent-compile-interpreter)
          persistent-compile-init        (read-from-minibuffer "Initialization command: "
                                                               persistent-compile-init)
          persistent-compile-command     (read-from-minibuffer "Command: "
                                                               persistent-compile-command)))
  (with-current-buffer (get-buffer-create "*persistent-compilation*")
    (if (and edit-command
             (get-buffer-process (current-buffer)))
        ;; Kill an existing process and schedule for a recompilation with
        ;; the new parameters
        (progn
          (set-process-sentinel
           (get-buffer-process (current-buffer))
           (lambda (process event)
             (persistent-recompile nil)))
          (kill-process (get-buffer-process (current-buffer))))

      (if (not (get-buffer-process (current-buffer)))
          ;; Start and initialize a new process
          (progn
            (erase-buffer)
            (make-comint-in-buffer "persistent-compile" (current-buffer) persistent-compile-interpreter)
            (compilation-shell-minor-mode 1)
            (make-local-variable 'persistent-compile--next-action)
            (setq persistent-compile--next-action 'persistent-compile--initialize)
            (add-hook 'comint-output-filter-functions 'persistent-compile--at-prompt))

        ;; Run command
        (erase-buffer)
        (persistent-compile--send-command)))))

(defun persistent-compile--at-prompt (&optional output)
  (when persistent-compile--next-action
    ;; There is probably a better way of checking whether we are
    ;; just after a prompt, but I didn't find it...
    (let ((p1 (point))
          (p2 (save-excursion (comint-bol))))
      (unless (= p1 p2)
        (let ((action persistent-compile--next-action))
          (setq persistent-compile--next-action nil)
          (funcall action))))))

(defun persistent-compile--initialize ()
  (setq persistent-compile--next-action 'persistent-compile--send-command)
  (display-buffer (current-buffer))
  (insert persistent-compile-init)
  (comint-send-input nil t))

(defun persistent-compile--send-command ()
  (display-buffer (current-buffer))
  (insert persistent-compile-command)
  (comint-send-input nil t))
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.