Persistente Umgebung für die Mx-Kompilierung

8

Wenn ich M-x compilees ausführe, wird eine neue Subshell erzeugt, um meinen Kompilierungsbefehl auszuführen. Sobald der Kompilierungsbefehl zurückgegeben wird, wird der Shell-Prozess beendet. Ich kann sehen, warum dies in den meisten Fällen wünschenswert wäre, aber ich bin derzeit in einer Situation, in der es nicht hilfreich ist.

Ich arbeite gerade in einer speziellen Build-Umgebung, die einige erste Schritte zum Einrichten des Builds erfordert, bevor der Compiler ausgeführt wird. Solange die Umgebung bestehen bleibt, muss ich die Einrichtungsschritte nur einmal ausführen. Aber wenn ich es benutze M-x compile, bedeutet das, dass ich die Schritte jedes Mal ausführen muss, wenn ich kompilieren oder neu kompilieren möchte.

Gibt es eine Möglichkeit, eine Subshell zu erzeugen, die im Hintergrund verbleibt? Einer, M-x compileund M-x gdbjedes Mal , wenn sie benötigen , können einen Shell - Prozess laufen zu lassen?


Motivation:

Ich habe ein Programm (das wir aufrufen werden xcc), das C-Code für spezielle Plattformen erstellt. Um meinen Code zu erstellen, beginne ich zuerst mit xccder tcshEingabeaufforderung:

$ xcc

Das Laden des Programms dauert mehr als 10 Sekunden, und dann kann ich an der interaktiven Eingabeaufforderung Befehle eingeben

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

Die obigen Schritte können in ein benutzerdefiniertes Makro integriert werden, buildmyprog.macrosodass ich es direkt von der Shell oder von Emacs mit ausführen kannM-x compile

$ xcc buildmyprog.macro

Das Hauptproblem bei diesem Ansatz ist die Tatsache, dass das xccLaden des Programms 10 Sekunden dauert , bevor die Kompilierung überhaupt beginnt. Ich habe müde genug um die zusätzlichen 10 Sekunden jedes Mal , wenn ich zusammengestellt warten , dass ich laufen begonnen haben , xccin eine ansi-termin einem separaten Puffer. Nachdem ich den Code geändert und gespeichert habe, wechsle ich zum ansi-termPuffer und führe ihn aus

xcc>> build myprogram

Das funktioniert gut, aber jedes Mal, wenn ich zu diesem Puffer wechsle, denke ich: "Wäre es nicht großartig, wenn ich nur pushen könnte F7und mein Kompilierungsbefehl an die bereits laufende Instanz von gesendet würde xcc?"

Nispio
quelle
Welche Auswirkungen hat Ihr Setup auf die Umgebung? Umgebungsvariablen, temporäre Dateien?
T. Verron
Die Umwelt ist ein eigenes Programm. Während Umgebungsvariablen und temporäre Dateien verwendet werden, behält das Programm selbst seine eigenen intenralen Zustände bei, die beim Schließen des Programms verschwinden.
Nispio
3
Aufgrund der Bearbeitung denke ich, dass Sie einen comintabgeleiteten Modus für Ihre Umgebung benötigen . Wenn Sie sich abenteuerlustig fühlen, finden Sie hier eine Anleitung zum Schreiben einer.
Vamsi
@Vamsi Ich bin ein Abenteuer, und das sieht aus wie eine große Führung. Vielen Dank.
Nispio
1
Der einfachste und schmutzigste Weg, einen Befehl an einen Comint-Puffer zu senden, besteht darin, ihn in den Puffer einzufügen und aufzurufen comint-send-input. Das ist im Grunde das, was Sie von Hand tun. Die Konvertierung in Elisp sollte nicht sehr schwierig sein (insbesondere im Vergleich zum Einrichten des Comint).
T. Verron

Antworten:

2

Können Sie das Setup in Ihrer Shell durchführen, bevor Sie Emacs starten? Die compileSub-Shell sollte die Umgebung von ihren Großeltern über Emacs erben.

PT
quelle
1
Stellen Sie sich vor, die "Umgebung", die ich verwende, ist Python. (Es ist nicht Python.) Auch wenn ich Emacs möglicherweise über den Python-Interpreter starten kann, werden M-x compileseine Befehle nicht an den Python-Interpreter zurückgesendet.
Nispio
0

Verwenden Sie dann eine tatsächliche Shell in Emacs, wie eshelloder ansi-term. Sie sollten compilezum Kompilieren verwenden, obwohl es zum Ausführen eines beliebigen Befehls verwendet werden kann.

Ich denke nicht, dass es mit dem Kompilierungsbefehl möglich ist, aber vielleicht fehlt mir etwas. Andernfalls können Sie alle Setup-Schritte in ein Shell-Skript einfügen und dieses Skript mit ausführen M-x compile.

Tu Do.
quelle
3
Nun ja, aber die Frage ist dann, wie man es an M-x compileeinen alternativen Befehl anschließt, der die aktuelle Datei kompiliert.
Gilles 'SO - hör auf böse zu sein'
@ Gilles the OP möchte eine konsistente Shell-Umgebung zwischen den M-x compileAufrufen, und genau das tut eine tatsächliche Shell. Ich denke nicht, dass es mit dem Kompilierungsbefehl möglich ist, aber vielleicht fehlt mir etwas. Andernfalls könnte das OP alle Setup-Schritte in ein Shell-Skript einfügen und dieses Skript mitM-x compile
Tu Do
Ich bin mir nicht sicher, ob ich Ihren ersten Vorschlag verstehe. Wollen Sie damit sagen "Verwenden Sie eshellfür Shell-Dinge und compilezum Kompilieren von Dingen"? Das macht den ganzen Punkt zunichte.
Nispio
Ich habe den zweiten Vorschlag ausprobiert, hatte aber einige Probleme. (Was mich dazu veranlasste, diese Frage zu stellen.) Es ist zu langsam, weil es jeder Kompilierung viel zusätzlichen Aufwand hinzufügt. Dies ist unnötig, da ich die Umgebung nur einmal einrichten muss. Es ist auch nicht dynamisch genug, weil ich nicht nur eine Sache in der aktuellen Umgebung optimieren und erneut versuchen kann, sie zu kompilieren. Die Build-Umgebung ist ein Programm, daher kann ich kein Shell-Skript schreiben, um es zu steuern. Ich muss ein kompiliertes Makroskript für die Umgebung schreiben und den Kompilierungsbefehl selbst in das Makro aufnehmen.
Nispio
0

Eine mögliche Lösung wäre, einen permanenten Interpreter in einem dedizierten Puffer zu haben (ähnlich wie der *compilation*Puffer, aber wo der zugrunde liegende Prozess niemals zurückkehren würde). Sie könnten dann einen recompileähnlichen Befehl haben, um einen vordefinierten Kompilierungsbefehl an den Interpreter zu senden und die Ergebnisse anzuzeigen.

Nachfolgend finden Sie eine vorläufige Umsetzung einer solchen Strategie. Der persistent-compileBefehl kümmert sich sowohl um die Initialisierung des Prozesses (wenn er zum ersten Mal aufgerufen wird oder mit einem Präfixargument) als auch um die Neukompilierung (wenn der Interpreterprozess bereits ausgeführt wird). Die Kompilierungsergebnisse werden in einem *persistent-compilation*Puffer angezeigt , um compilation-shell-minor-modedie üblichen compilation-modeFunktionen zum Navigieren zwischen Fehlern zu nutzen.

Hier ist eine Beispielverwendung zusammen mit dem resultierenden Inhalt des *persistent-compilation*Puffers:

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

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

    $ echo $FOO
    bar
    $
    

Achtung, der folgende Code wurde nicht viel getestet. Ich bin mir beispielsweise nicht sicher, was passiert, wenn Sie versuchen, vor dem Beenden der vorherigen Kompilierung eine Neukompilierung durchzuführen.

(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
quelle