Proyectos de varios archivos (\input / \include / subfiles)

Un proyecto LaTeX de varios archivos puede repartirse en una docena de .tex sin que el compilador pestañee: pdflatex main.tex los lee todos y entrega un único PDF. Quien pierde el hilo es el editor. Basta pulsar la tecla de compilación con el capítulo tres delante para recibir ! LaTeX Error: Missing \begin{document}., porque el editor compuso obedientemente el archivo que estabas mirando. El arreglo cabe en una línea al principio de cada capítulo, % !TEX root = ../main.tex, y lo curioso es que LaTeX no lee esa línea jamás. Es un comentario, dirigido al editor y no al compilador. Esta página trata de esa segunda capa de un proyecto dividido: qué editores leen el comentario mágico y qué usan los demás en su lugar, cómo SyncTeX encuentra el camino de vuelta al archivo de capítulo correcto, dónde acaban realmente los archivos de compilación y qué se rompe cuando el archivo abierto no es el principal. Las órdenes que hacen la división —\input, \include, \includeonly— son otra historia, enlazada al pie de la página.

% !TEX root: compilar el archivo principal con un capítulo abierto

Basta poner % !TEX root = ../main.tex al principio de todo archivo que no sea el principal para que la tecla de compilación haga lo correcto, esté delante el que esté. Dos detalles de la propia documentación de TeXShop merecen recordarse, porque ambos pillan a mucha gente. Primero, la línea debe aparecer dentro de las primeras veinte líneas del archivo; enterrada bajo una larga cabecera de licencia, sencillamente no se ve. Segundo, la ruta se resuelve respecto al archivo que contiene la línea, no respecto a la raíz del proyecto: un capítulo alojado en chapters/ necesita ../main.tex, no main.tex. También sirve una ruta absoluta, al precio de volver el proyecto inamovible. El archivo principal no necesita línea alguna: ya es la raíz.

text
thesis/
  main.tex                 <- the root; needs no magic comment
  chapters/
    03-results.tex         <- carries the line below
latex
% chapters/03-results.tex -- the first lines of the file
% !TEX root = ../main.tex     % relative to THIS file, not to thesis/
% !TEX TS-program = xelatex   % pin the engine for the whole project
% !TEX encoding = UTF-8 Unicode

\chapter{Results}

La línea tiene un predecesor, y por qué ganó el sucesor resulta instructivo. TeXShop ofrecía antaño una orden de menú llamada «Set Project Root…», que anotaba la respuesta en un archivo satélite junto al capítulo: two.tex adquiría un two.texshop. Al tirar ese archivo invisible, TeXShop volvía de inmediato a componer el capítulo. La documentación de TeXShop describe hoy esa orden como retirada de los menús porque el método % !TEX root es más robusto, y toda la razón cabe en esa palabra. Una línea dentro del archivo viaja con el archivo. Sobrevive a una copia, al cambio de nombre de la carpeta que lo contiene, a un clon de Git y a un coautor que nunca ha abierto tu editor. La configuración aparcada junto a un archivo acaba siempre por perder el archivo.

Por qué LaTeX nunca lee % !TEX root

Porque % inicia un comentario, y los comentarios los descarta el analizador léxico de TeX antes que ninguna otra cosa. pdflatex, xelatex y lualatex no ven nada en esa línea. Es un recado de un programa (el editor) a otro programa (la orden de compilación del editor) que solo de paso atraviesa el archivo fuente. De ahí se siguen dos consecuencias prácticas. Primera, nadie te avisará nunca de que la línea está mal. Apúntala a un archivo inexistente y el editor recae calladamente en su propia conjetura, normalmente el archivo abierto, y vuelves a tener delante ! LaTeX Error: Missing \begin{document}. Segunda, una compilación lanzada desde un terminal o en CI —latexmk main.tex, un Makefile, un paso de GitHub Actions— nombra el archivo principal en la línea de órdenes e ignora por completo el comentario mágico. La línea es una comodidad para editar de forma interactiva, no parte de la definición del proyecto.

Existe exactamente un comentario que TeX sí lee, y conviene conocerlo para no confundir nunca ambas cosas. Si la primerísima línea del archivo de entrada principal empieza por %&, el motor la analiza él mismo para elegir un formato%&pdflatex, %&latex—, comportamiento que la página de manual de tex describe como gobernado por la opción -parse-first-line y la variable de configuración parse_first_line. Ese desciende de la maquinaria de carga de formatos de TeX y vive en el motor. Todo lo que se escribe % !TEX ... vive en el editor. El parecido visual es una casualidad: ambos querían esconder instrucciones donde LaTeX no tropezara con ellas.

latex
% main.tex -- first line only: the ENGINE parses this one
%&pdflatex

% chapters/03-results.tex -- the engine never sees this one
% !TEX root = ../main.tex

Qué editores leen % !TEX root y qué usan los demás

TeXShop, TeXworks, TeXstudio y VS Code con la extensión LaTeX Workshop leen todos la línea; Emacs con AUCTeX emplea una variable local de archivo propia, y Overleaf toma la respuesta de un ajuste del proyecto en lugar de la fuente. LaTeX Workshop es el que merece un vistazo detenido, porque documenta todo su procedimiento de decisión: mira primero el comentario mágico del editor activo, luego si el propio archivo activo contiene \documentclass o \begin{document}, después recorre los .tex de la raíz del espacio de trabajo en busca de uno que incorpore el archivo activo, a continuación reconoce el patrón de subfiles \documentclass[main.tex]{subfiles}, y por último recurre a la lista de archivos .fls que dejó la compilación anterior. El comentario mágico gana porque se consulta el primero; y si alguna vez no quieres que sea así, el ajuste se llama latex-workshop.latex.build.enableMagicComments.

EditorQué leeNota
TeXShop% !TEX rootorigen de la directiva; hermanas % !TEX TS-program, encoding, spellcheck
TeXworks% !TEX rootadopta el mismo esquema de comentarios mágicos
TeXstudio% !TeX rootdetecta la raíz automáticamente; la línea tiene prioridad
LaTeX Workshop% !TEX rootpara VS Code; primero de cinco pasos, desactivable con latex-workshop.latex.build.enableMagicComments
AUCTeXTeX-masterpara Emacs; variable local de archivo, por convención al final del archivo
Overleafun ajuste del proyectose elige en el menú del proyecto como documento principal; nada en la fuente

Emacs es la excepción interesante. AUCTeX plantea la misma pregunta, pero guarda la respuesta como variable local de archivo, por convención en un bloque al final del archivo. Como cada editor lee solo su propia convención, llevar ambas no cuesta nada: el bloque de AUCTeX es un comentario corriente para cualquier otro editor, y % !TEX root es un comentario corriente para Emacs. En repositorios compartidos abundan los archivos de capítulo que llevan las dos cosas, lo cual es correcto y cuesta dos líneas. Overleaf queda del todo al margen de la discusión: el documento principal es una propiedad del proyecto y se fija desde el menú, de modo que nada en la fuente puede desincronizarse; e igualmente, nada acompaña al archivo cuando descargas el proyecto y lo abres en local.

latex
% chapters/03-results.tex -- both conventions in one file
% !TEX root = ../main.tex

\chapter{Results}

%%% Local Variables:
%%% mode: latex
%%% TeX-master: "../main"
%%% End:

SyncTeX entre archivos: por qué al hacer clic en el PDF se abre el capítulo correcto

Porque SyncTeX anota, para cada caja de cada página, de qué archivo de entrada y de qué línea procede. Haz doble clic en un párrafo del capítulo tres dentro del PDF y se abre chapters/03-results.tex, no main.tex. Se activa con -synctex=1 y se obtiene un único main.synctex.gz en la raíz del proyecto, con el nombre del archivo raíz. No hay un archivo synctex por capítulo: un solo índice cubre todo el proyecto, y justamente por eso puede señalar cualquier archivo de su interior.

terminal
$ pdflatex -synctex=1 main.tex
$ synctex edit -o "2:100:200:main.pdf"
SyncTeX result begin
Output:main.pdf
Input:./chapters/ch2.tex
Line:2
SyncTeX result end

Probar una vez el cliente de línea de órdenes vuelve concreto el mecanismo. synctex edit recibe una página y un punto del PDF y devuelve un nombre de archivo y un número de línea; synctex view recorre el camino inverso, de una línea del fuente a un lugar de la página. La Synchronize TeXnology que hay detrás se debe, según su propia página de manual, esencialmente a Jérôme Laurens, y hoy se mantiene como parte de TeX Live. La documentación de TeXShop enlaza esto de forma explícita con la sección anterior: es la línea % !TEX root la que permite que un clic de búsqueda inversa abra y active la ventana del capítulo correcto en lugar de dejarte en el archivo principal. Justamente por eso ambas funciones suelen configurarse juntas.

La trampa llega al compilar un capítulo por separado. Una ejecución del motor deja sus subproductos en el directorio de trabajo desde el que se lanzó la compilación, no junto al archivo de entrada. Ejecuta pdflatex -synctex=1 chapters/03-results.tex desde la raíz del proyecto y 03-results.synctex.gz aparece en la raíz, justo al lado de main.synctex.gz. Ahora dos índices describen las mismas líneas del fuente, y uno de ellos apunta a un PDF de un solo capítulo que empieza en la página 1. Cuál lea el visor decide dónde aterriza tu clic, y la numeración deja de coincidir. Al volver a compilar el libro entero, borra el PDF y el archivo synctex que dejó la compilación del capítulo.

Dónde acaban los archivos de compilación y qué poner en .gitignore

\include escribe un .aux por capítulo, y lo escribe junto al archivo del capítulo. Compila un proyecto que contenga chapters/01-intro.tex y encontrarás chapters/01-intro.aux a su lado. Todo lo demás se queda en la raíz, junto al archivo principal: main.aux, main.log, main.toc, main.out, main.synctex.gz y, con latexmk, además main.fls y main.fdb_latexmk. Los archivos generados no están en un solo sitio: quedan esparcidos en capa fina por todo el árbol de fuentes.

text
thesis/
  main.tex  main.pdf
  main.aux  main.log  main.toc  main.out
  main.synctex.gz  main.fls  main.fdb_latexmk
  chapters/
    01-intro.tex   01-intro.aux    <- one .aux per \include, here
    02-method.tex  02-method.aux

Para Git esto da menos guerra de lo que parece, porque un patrón de .gitignore sin barra coincide a cualquier profundidad: una línea simple *.aux ya cubre chapters/01-intro.aux. Lo que no lo cubre es un /*.aux anclado a la raíz, ni tampoco la costumbre de limpiar con rm *.aux en la raíz del proyecto. Y, más sorprendente, tampoco latexmk: probado en TeX Live 2024, latexmk -c e incluso latexmk -C retiran los intermedios de la raíz y dejan chapters/*.aux donde estaban. Así que cuando sospeches de un .aux rancio —esa clase de fallo cuyo error señala un capítulo que no has tocado—, límpialos de forma explícita, con algo como find . -name "*.aux" -delete.

Hay un punto en el que la división rompe de verdad una herramienta: -output-directory. Pide una compilación fuera del árbol con pdflatex -output-directory=build main.tex en un proyecto que usa \include y la ejecución muere. TeX intenta abrir build/chapters/01-intro.aux, el subdirectorio no existe y aparece ! I can't write on file seguido de un error fatal y ningún PDF. TeX no crea directorios. Hay dos arreglos: cavar tú mismo de antemano los subdirectorios espejo, o encargar el trabajo a latexmk -outdir=build, que los crea por ti. Por eso los proyectos de varios archivos que compilan fuera del árbol casi siempre los gobierna latexmk y no el motor directamente.

terminal
# fails: TeX will not create build/chapters/ by itself
pdflatex -output-directory=build main.tex
# ! I can't write on file ... chapters/01-intro.aux

# works: mirror the subdirectories first
mkdir -p build/chapters && pdflatex -output-directory=build main.tex

# works: latexmk creates them for you
latexmk -pdf -outdir=build main.tex

Ese mismo archivo .fls es lo que hace funcionar la recompilación al guardar en un proyecto dividido. Ejecuta con -recorder —que latexmk añade por ti— y el motor anota cada archivo que abrió, de modo que main.fls lleva una línea INPUT chapters/01-intro.tex por capítulo. latexmk guarda la lista de dependencias resultante en main.fdb_latexmk y la vigila entera, y por eso guardar el capítulo tres recompila el libro sin que jamás le hayas dicho que el capítulo tres forma parte de él. La estructura de la división no hay que declararla dos veces: la sucesión de líneas \include ya es la declaración de dependencias.

Qué se rompe cuando el archivo abierto no es el principal

Tres síntomas, y no se parecen en nada entre sí. Primero, un archivo de capítulo normal compilado por separado se detiene de inmediato: ! Undefined control sequence. en el primer \chapter, luego ! LaTeX Error: Missing \begin{document}., luego ! Emergency stop. y ningún PDF, desenlace inevitable en un archivo sin \documentclass. Segundo, un capítulo con subfiles compilado por separado es peor, porque funciona: obtienes un PDF de un capítulo, verosímil, que empieza en la página 1 y cuyas referencias cruzadas a otros capítulos se imprimen como ??. Tercero, una compilación lanzada desde el directorio de trabajo equivocado falla en cambio con las imágenes, porque toda ruta relativa del proyecto se resuelve desde donde se ejecutó la compilación, no desde donde vive el archivo.

  • La tecla de compilación compone el archivo equivocado → pon % !TEX root en todo archivo que no sea el principal, dentro de las primeras veinte líneas, con la ruta relativa a ese archivo.
  • ! LaTeX Error: Missing \begin{document}. → estás compilando un capítulo directamente; ese archivo no tiene preámbulo ni debe tenerlo.
  • Las imágenes desaparecen, o la compilación se detiene en un archivo que falta → la compilación no se ejecuta en la raíz del proyecto; las rutas relativas se resuelven desde el directorio de trabajo.
  • Al hacer clic en el PDF se abre el archivo principal y no el capítulo → esa compilación no llevaba -synctex=1, o el visor lee un .synctex.gz rancio.
  • Aparecen .log y .pdf sueltos en la raíz tras un experimento fallido → una ejecución del motor escribe sus subproductos en el directorio de trabajo, no junto al archivo de entrada.

Al final, dos hábitos mantienen callada esta capa. Uno: nada de espacios en nombres de archivo ni de carpeta. Toda herramienta de esta página acaba entregando la ruta a un shell o a un comentario mágico encabezado por %, y el espacio es donde viven los fallos de comillas. Dos: lanzar siempre la compilación desde la raíz del proyecto, a mano, desde un Makefile o dejando que lo haga el editor. El directorio de trabajo es el único punto de referencia que comparten \includegraphics, \include y -output-directory; si se desplaza, los tres se desplazan a la vez. Con esas dos cosas resueltas, la capa de varios archivos se vuelve invisible, que es el único estado en el que está haciendo su trabajo.