Archivo de Tangle Org que contiene directivas # + INCLUDE y múltiples objetivos de enredo

Oct 18 2020

Contexto

Estoy usando org-babel-tanglepara generar mi configuración desde un archivo Org config.org. Recientemente, hice una limpieza por

  1. envío config.orga múltiples archivos Org config/*.org;
  2. reemplace el contenido de config.orgcon varias #+INCLUDE: config/*.orgdirectivas.

Para ilustrar un poco, el árbol de directorios es el siguiente:

config/
|______ A.org
|______ B.org
|______ C.org
|
config.org

dónde

## config.org
#+INCLUDE: config/A.org
#+INCLUDE: config/B.org
#+INCLUDE: config/C.org
...

y

## [ABC].org
#+BEGIN_SRC emacs-lisp :tangle target1.el
;; elisp code
#+END_SRC

#+BEGIN_SRC emacs-lisp :tangle target2.el
;; elisp code
#+END_SRC
...

Problema e intento

Sin embargo, me encontré con el problema que org-babel-tangleno maneja #+INCLUDEdirectivas. Entonces, sigo la idea de esta publicación que consiste en

  1. exportar config.orga un archivo Org config-export.orgcon org-export-to-file;
  2. org-babel-tangleel archivo Org exportado config-export.orgpara enredar los objetivos.

Este enfoque reemplaza las múltiples #+INCLUDEdirectivas de config.orgpor el contenido de config/*.org, pero se borran todas las opciones de enredo . Es decir, el exportado se config-export.orgve así (se pierden todos los nombres de archivo de destino de enredo de emacs-lisp):

## config-export.org
#+BEGIN_SRC emacs-lisp
;; elisp code for target1.el
#+END_SRC

#+BEGIN_SRC emacs-lisp
;; elisp code for target2.el
#+END_SRC
...

Pregunta

¿Cómo org-babel-tanglecrear un archivo Org con #+INCLUDEdirectivas como está previsto?

( es decir, (1) incluya todos los archivos Org (preservando :tangle target.el), luego (2) org-babel-tangleel archivo Org completo).

Pregunta relacionada

Se ha planteado una pregunta similar en la lista de correo del modo Org.

Respuestas

2 NickD Oct 19 2020 at 23:26

No sé cómo hacer esto sin cambiar el código (podría ser posible con org-babel-pre-tangle-hookpero no parecía nada simple, así que no lo intenté). Pero tan pronto como acepta los cambios de código, no parece tan malo.

La forma más sencilla que se me ocurrió fue emular en org-babel-tanglelo que org-export-asestá haciendo WRT incluidos los archivos. Una prueba simple (similar a la que describió anteriormente) tuvo éxito fácilmente (pero vea las advertencias al final), por lo que si desea intentar replicar el cambio, incluyo el parche a continuación.

El parche en sí se vería grande, pero eso es solo porque estoy tomando todo el código org-babel-tangley encerrándolo en otra capa, lo que cambia la sangría y eso es la mayor parte del cambio. Ignorando los cambios de espacios en blanco, el cambio "real" es mucho menor: obtuve el parche de abajo con git diff -bpara ignorar los espacios en blanco. Luego puede ver que es muy pequeño: simplemente encerramos casi todo el cuerpo de org-babel-tanglein (org-export-with-buffer-copy ...), para que podamos hacer transformaciones al texto en un búfer temporal y no alterar el original y luego agregar una transformación más para procesar las #+INCLUDE:directivas, antes el búfer resultante se enreda:

diff --git a/lisp/ob-tangle.el b/lisp/ob-tangle.el
index b74b3fa0c..5e6eebf63 100644
--- a/lisp/ob-tangle.el
+++ b/lisp/ob-tangle.el
@@ -206,6 +206,9 @@ export file for all source blocks.  Optional argument LANG-RE can
 be used to limit the exported source code blocks by languages
 matching a regular expression."
   (interactive "P")
+  (org-export-with-buffer-copy
+   (org-export-expand-include-keyword)
+
    (run-hooks 'org-babel-pre-tangle-hook)
    ;; Possibly Restrict the buffer to the current code block
    (save-restriction
@@ -306,7 +309,7 @@ matching a regular expression."
     (mapc (lambda (pair)
         (when (cdr pair) (set-file-modes (car pair) (cdr pair))))
           path-collector)
-   (mapcar #'car path-collector)))))
+    (mapcar #'car path-collector))))))
 
 (defun org-babel-tangle-clean ()
   "Remove comments inserted by `org-babel-tangle'.

Eso produce los resultados esperados WRT los archivos enredados, creo, pero debe verificar para asegurarse de que hace lo correcto en su caso: mis pruebas son pruebas de juguete, por lo que pueden pasar por alto situaciones difíciles que puedan surgir en sus pruebas del "mundo real" .

Hay un problema que todavía no entiendo: el config.orgbúfer en sí se cambia en mis pruebas, a pesar de org-export-with-copy-buffer, así que al final del enredo tengo que deshacer el cambio C-x uy guardarlo. Probablemente se deba a que no entiendo lo org-export-with-copy-bufferque realmente está haciendo, pero aún no he verificado ese código.

OTOH, si eso se resuelve y todas las pruebas pasan, podemos proponer esto como una mejora a la lista de correo del modo Org y dejar atrás ese problema (¡desde 2010!).

EDITAR: No veo ningún problema con org-babel-tangle-file: llama org-babel-tangledebajo, por lo que en teoría debería hacer lo (casi) correcto y probarlo con un ejemplo simple funciona bien para mí. ¿Quizás puedas publicar un (pequeño) ejemplo que te dé problemas al editar tu pregunta?

También estoy probando con la siguiente solución para solucionar el problema del config.orgcambio:

#+begin_src emacs-lisp
(copy-file "./config.org.orig" "./config.org")
(org-babel-tangle-file "./config.org")
(delete-file "./config.org")
#+end_src

Feo, pero parece funcionar (excepto que tengo que responder a la pregunta "cambiado en el disco").

2 Melioratus Oct 26 2020 at 09:30

Método 1

Método manual usando combinaciones de teclas predeterminadas en modo org

Configuración

  1. Agregue Orga la Org Babel Load Languagesconfiguración y luego aplique y guarde la nueva configuración.

  2. Agregue una #+EXPORT_FILE_NAME:palabra clave cerca de la parte superior de config.org.

    p.ej

    #+EXPORT_FILE_NAME: temp.org
    
  3. Agregue una #+PROPERTY: header-args:org :results drawer replacepalabra clave cerca de la parte superior de config.org.

    p.ej

    #+PROPERTY: header-args:org :results drawer replace
    
  4. Actualice #+INCLUDE:para incluir la src orgsintaxis.

    p.ej

    #+INCLUDE: config/A.org src org
    #+INCLUDE: config/B.org src org
    #+INCLUDE: config/C.org src org
    
  5. Agregue :eval neverencabezado a cada uno de los SRCbloques que se enredarán dentro de los config/*.orgarchivos incluidos .

    p.ej

    #+BEGIN_SRC elisp :tangle tangle1.el :eval never 
      ;; Code goes here
    #+END_SRC
    

Pasos de ejecución manual

  1. Abrir config.orgen emacs.

    El config.orgarchivo debe tener un aspecto similar al siguiente ejemplo:

    #+EXPORT_FILE_NAME: temp.org
    #+PROPERTY: header-args:org :results drawer replace 
    
    #+INCLUDE: config/A.org src org
    #+INCLUDE: config/B.org src org
    #+INCLUDE: config/C.org src org
    
  2. Exportar config.orgusando C-c C-e O vun acorde clave. Esto creará un nuevo temp.orgarchivo y se abrirá en emacs.

    El temp.orgarchivo debe tener un aspecto similar al siguiente ejemplo:

    #+export_file_name: temp.org
    #+property: header-args:org :results drawer replace
    
    #+begin_src org
      ,#+BEGIN_SRC elisp :tangle tangle1.el :eval never 
        ;; Code goes here
      ,#+END_SRC
    #+end_src
    #+begin_src org
      ,#+BEGIN_SRC elisp :tangle tangle2.el :eval never 
        ;; Code goes here
      ,#+END_SRC
    #+end_src
    #+begin_src org
      ,#+BEGIN_SRC elisp :tangle tangle3.el :eval never 
        ;; Code goes here
      ,#+END_SRC
    #+end_src
    
  3. Ejecute todos los bloques en el acorde de tecla temp.orgusing C-c C-v C-b.

    El temp.orgarchivo debe tener un aspecto similar al siguiente ejemplo:

    #+export_file_name: temp.org
    #+property: header-args:org :results drawer replace
    
    #+begin_src org
      ,#+BEGIN_SRC elisp :tangle tangle1.el :eval never 
        ;; Code goes here
      ,#+END_SRC
    #+end_src
    
    #+RESULTS:
    :results:
    #+BEGIN_SRC elisp :tangle tangle1.el :eval never 
      ;; Code goes here
    #+END_SRC
    :end:
    
    #+begin_src org
      ,#+BEGIN_SRC elisp :tangle tangle2.el :eval never 
        ;; Code goes here
      ,#+END_SRC
    #+end_src
    
    #+RESULTS:
    :results:
    #+BEGIN_SRC elisp :tangle tangle2.el :eval never 
      ;; Code goes here
    #+END_SRC
    :end:
    
    #+begin_src org
      ,#+BEGIN_SRC elisp :tangle tangle3.el :eval never 
        ;; Code goes here
      ,#+END_SRC
    #+end_src
    
    #+RESULTS:
    :results:
    #+BEGIN_SRC elisp :tangle tangle3.el :eval never 
      ;; Code goes here
    #+END_SRC
    :end:
    
  4. Ejecute el enredo en el acorde de tecla de temp.orguso C-c C-v C-t. Esto enredará los SRCbloques importados de los config/*.orgarchivos.

    Esto creará archivos enredados similares a los que se enumeran a continuación:

    config.org
    tangle1.el
    tangle2.el
    tangle3.el
    temp.org
    
    ./config:
    A.org
    B.org
    C.org
    

Esta respuesta fue probada usando:
emacs versión: GNU Emacs 27.1
org-mode versión: 9.3.7