Ambiente persistente para compilação Mx

8

Quando executo M-x compile, gera um novo subshell para executar meu comando de compilação. Assim que o comando de compilação retornar, o processo do shell será interrompido. Eu posso ver por que isso seria desejável na maioria dos casos, mas atualmente estou em uma situação em que não é útil.

Atualmente, estou trabalhando em um ambiente de compilação especializado, que exige algumas etapas iniciais para configurar a compilação antes de executar o compilador. Enquanto o ambiente persistir, só preciso executar as etapas de configuração uma vez. Mas quando eu uso, M-x compileisso significa que tenho que executar as etapas sempre que desejar compilar ou recompilar.

Existe uma maneira de gerar um subshell que persistirá em segundo plano? Um que M-x compilee M-x gdbpode usar cada vez que eles precisam para executar um processo shell?


Motivação:

Eu tenho um programa (que chamaremos xcc) que cria código C para plataformas especiais. Para criar meu código, começo primeiro xccpelo tcshprompt:

$ xcc

O programa leva mais de 10 segundos para carregar e, em seguida, posso inserir comandos no prompt interativo

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

As etapas acima podem ser construídas em uma macro personalizada, buildmyprog.macropara que eu possa executá-la diretamente do shell ou do emacs comM-x compile

$ xcc buildmyprog.macro

O principal problema dessa abordagem é o fato de que o xccprograma leva 10 segundos para carregar, antes mesmo da compilação começar. Eu me cansei o suficiente de esperar os 10 segundos extras toda vez que compilei que comecei a correr xccem um ansi-termbuffer separado. Agora, depois de modificar e salvar o código, mudo para o ansi-termbuffer e corro

xcc>> build myprogram

Isso funciona bem, mas toda vez que eu mudo para esse buffer, penso: "Não seria ótimo se eu simplesmente pressionasse F7e meu comando de compilação fosse enviado para a instância já em execução do xcc?"

nispio
fonte
Que efeito sua instalação tem no ambiente? Variáveis ​​de ambiente, arquivos temporários?
T. Verron
O ambiente é seu próprio programa. Embora faça uso de variáveis ​​de ambiente e arquivos temporários, o próprio programa mantém seus próprios estados intenais que desaparecem quando o programa é fechado.
Nispio
3
Com base na edição, acho que você precisa de um comintmodo derivado para o seu ambiente. Se você estiver se sentindo aventureiro, aqui está um guia para escrever um.
Vamsi
@Vamsi Estou me sentindo aventureiro, e isso parece uma grande vantagem. Obrigado.
Nispio 13/10/2014
1
A maneira mais fácil e suja de enviar um comando para um buffer de comint é inseri-lo no buffer e chamar comint-send-input. Isso é basicamente o que você está fazendo manualmente, convertê-lo em elisp não deve ser muito difícil (especialmente se comparado à configuração do comint).
23714 Tetron Verron

Respostas:

2

Você pode fazer a configuração no seu shell antes de iniciar o emacs? O compilesub-shell deve herdar o ambiente de seus avós via emacs.

PT
fonte
1
Imagine que o "ambiente" que estou executando é o Python. (Não é Python.) Embora eu possa iniciar o Emacs do interpretador Python, M-x compileele não enviou seus comandos de volta ao interpretador python.
Nispio 13/10
0

Use um shell real dentro do Emacs, como eshellou ansi-term. Você deve usar compilepara compilar, embora possa ser usado para executar qualquer comando.

Não acho que seja possível com o comando compile, mas talvez esteja faltando alguma coisa. Caso contrário, você poderá colocar todas as etapas de configuração em um script de shell e executá-lo com M-x compile.

Tu Do
fonte
3
Bem, sim, mas a questão é como conectá-lo a M-x compilealgum comando alternativo que compilará o arquivo atual.
Gilles 'SO- stop be evil'
@Gilles, o OP quer um ambiente de shell consistente entre as M-x compilechamadas, e é isso que um shell real faz. Não acho que seja possível com o comando compile, mas talvez esteja faltando alguma coisa. Caso contrário, o OP poderia colocar todos os passos de configuração em um script shell e executar esse script comM-x compile
Tu Do
Não sei se entendi sua primeira sugestão. Você está dizendo "Use eshellpara itens de casca e compilepara compilar"? Isso derrota o ponto inteiro.
Nispio 12/10
Eu tentei a segunda sugestão, mas tive alguns problemas. (O que me levou a postar esta pergunta.) É muito lento porque acrescenta muita sobrecarga extra a cada compilação. Isso é desnecessário, porque eu deveria ter que configurar o ambiente apenas uma vez. Também acaba não sendo dinâmico o suficiente, porque não posso apenas ajustar uma coisa no ambiente atual e tentar compilar novamente. O ambiente de compilação é um programa, então não posso escrever um script de shell para controlá-lo. Eu tenho que escrever um script de macro compilado para o ambiente e incluir o próprio comando de compilação na macro.
Nispio
0

Uma solução possível seria ter um intérprete permanente vivendo em um buffer dedicado (mais ou menos como o *compilation*buffer, mas onde o processo subjacente nunca retornaria). Você pode ter um recompilecomando -like para enviar um comando de compilação predefinido ao intérprete e exibir os resultados.

Abaixo está uma implementação provisória de tal estratégia. O persistent-compilecomando cuida da inicialização do processo (quando é chamado pela primeira vez ou com um argumento de prefixo) e da recompilação (se o processo do intérprete já estiver em execução). Os resultados da compilação são exibidos em um *persistent-compilation*buffer, compilation-shell-minor-modeaproveitando os compilation-moderecursos usuais para navegar entre os erros.

Aqui está um exemplo de uso, junto com o conteúdo resultante do *persistent-compilation*buffer:

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

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

    $ echo $FOO
    bar
    $
    

Cuidado, o código a seguir não foi muito testado. Por exemplo, não tenho certeza do que acontece quando você tenta recompilar antes do término da compilação 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
fonte