Entorno persistente para compilación Mx

8

Cuando lo ejecuto M-x compilegenera una nueva subshell para ejecutar mi comando de compilación. Tan pronto como el comando de compilación regresa, el proceso de shell se cierra. Puedo ver por qué esto sería deseable en la mayoría de los casos, pero actualmente estoy en una situación en la que no es útil.

Estoy trabajando en un entorno de compilación especializado en este momento que requiere que tome algunos pasos iniciales para configurar la compilación antes de ejecutar el compilador. Mientras el entorno persista, solo necesito hacer los pasos de configuración una vez. Pero cuando lo uso M-x compilesignifica que tengo que hacer los pasos cada vez que quiero compilar o recompilar.

¿Hay alguna forma de generar una subshell que persista en segundo plano? Uno que M-x compiley M-x gdbpuede utilizar cada vez que necesitan para ejecutar un proceso de shell?


Motivación:

Tengo un programa (que llamaremos xcc) que construye el código C para plataformas especiales. Para construir mi código, primero comienzo xccdesde el tcshindicador:

$ xcc

El programa tarda más de 10 segundos en cargar, y luego puedo ingresar comandos en su solicitud interactiva

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

Los pasos anteriores pueden integrarse en una macro personalizada buildmyprog.macropara que pueda ejecutarla directamente desde el shell o desde emacs conM-x compile

$ xcc buildmyprog.macro

El principal problema con este enfoque es el hecho de que el xccprograma tarda 10 segundos en cargarse, incluso antes de que comience la compilación. Me cansé lo suficiente de esperar los 10 segundos adicionales cada vez que compilé que comencé a correr xccen un ansi-termbúfer separado. Ahora, después de modificar y guardar el código, cambio al ansi-termbúfer y ejecuto

xcc>> build myprogram

Esto funciona bien, pero cada vez que cambio a ese búfer pienso: "¿No sería genial si pudiera empujar F7y enviar mi comando de compilación a la instancia ya en ejecución xcc?"

nispio
fuente
¿Qué efecto tiene su configuración en el medio ambiente? Variables de entorno, archivos temporales?
T. Verron
El medio ambiente es su propio programa. Si bien utiliza algunas variables de entorno y archivos temporales, el programa en sí mantiene sus propios estados internos que desaparecen cuando se cierra el programa.
nispio
3
Según la edición, creo que necesita un comintmodo derivado para su entorno. Si te sientes aventurero, aquí hay una guía para escribir uno.
Vamsi
@Vamsi Me siento aventurero, y eso parece una gran ventaja. Gracias.
nispio
1
La forma más fácil y sucia de enviar un comando a un búfer de comint es insertarlo en el búfer y llamar comint-send-input. Eso es básicamente lo que está haciendo a mano, convertirlo a elisp no debería ser muy difícil (especialmente en comparación con la configuración de la comint).
T. Verron

Respuestas:

2

¿Puedes hacer la configuración en tu shell antes de iniciar emacs? El compilesub-shell debe heredar el entorno de sus abuelos a través de emacs.

PT
fuente
1
Imagine que el "entorno" que estoy ejecutando es Python. (No es Python). Aunque pueda iniciar Emacs desde el intérprete de Python, M-x compileno envía sus comandos al intérprete de Python.
nispio
0

Use un shell real dentro de Emacs, como eshello ansi-term. Debe usarlo compilepara compilar, aunque puede usarse para ejecutar cualquier comando.

No creo que sea posible con el comando de compilación, pero tal vez me falta algo. De lo contrario, podría poner todos los pasos de configuración en un script de shell y ejecutar ese script con M-x compile.

Tu Do
fuente
3
Bueno, sí, pero la pregunta es cómo conectarlo M-x compileo algún comando alternativo que compile el archivo actual.
Gilles 'SO- deja de ser malvado'
@Gilles, el OP quiere un entorno de shell consistente entre M-x compilellamadas, y eso es lo que hace un shell real. No creo que sea posible con el comando de compilación, pero tal vez me falta algo. De lo contrario, el OP podría poner todos los pasos de configuración en un script de shell y ejecutar ese script conM-x compile
Tu Do
No estoy seguro de entender tu primera sugerencia. ¿Estás diciendo "Usar eshellpara cosas de shell y compilepara compilar cosas"? Eso derrota todo el punto.
nispio
Intenté la segunda sugerencia, pero tuve algunos problemas. (Lo que me llevó a publicar esta pregunta). Es demasiado lento porque agrega mucha sobrecarga adicional a cada compilación. Esto es innecesario, porque solo debería configurar el entorno una vez. También termina no siendo lo suficientemente dinámico, porque no puedo simplemente modificar una cosa en el entorno actual e intentar compilar nuevamente. El entorno de compilación es un programa, por lo que no puedo escribir un script de shell para controlarlo. Tengo que escribir un script de macro compilado para el entorno e incluir el comando de compilación en la macro.
nispio
0

Una posible solución sería tener un intérprete permanente en vivo en un búfer dedicado (algo así como el *compilation*búfer, pero donde el proceso subyacente nunca volvería). Luego, podría tener un recompilecomando similar para enviar un comando de compilación predefinido al intérprete y mostrar los resultados.

A continuación se muestra una implementación tentativa de dicha estrategia. El persistent-compilecomando se ocupa tanto de la inicialización del proceso (cuando se llama por primera vez o con un argumento de prefijo) como de la recompilación (si el proceso del intérprete ya está en funcionamiento). Los resultados de la compilación se muestran en un *persistent-compilation*búfer compilation-shell-minor-modey se aprovechan de las compilation-modecaracterísticas habituales para navegar entre errores.

Aquí hay un ejemplo de uso, junto con el contenido resultante del *persistent-compilation*búfer:

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

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

    $ echo $FOO
    bar
    $
    

Cuidado, el siguiente código no fue probado mucho. Por ejemplo, no estoy seguro de lo que sucede cuando intenta volver a compilar antes de la finalización de la compilación anterior.

(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))
François Févotte
fuente