Search code examples
cmakelatex

CMake custom target for latex document only runs when ALL is specified


I am trying to add documentation to the build process of a CMake project, a lot like in this chapter from CMake's own "Mastering CMake." Here is the excerpt:

# Add the rule to build the .pdf file from the .tex
# file. This relies on LATEX and DVIPDF being set correctly
#
add_custom_command(
  OUTPUT  ${PROJECT_BINARY_DIR}/doc1.pdf
  DEPENDS ${PROJECT_SOURCE_DIR}/doc1.tex
  COMMAND ${LATEX}  ${PROJECT_SOURCE_DIR}/doc1.tex
  COMMAND ${DVIPDF} ${PROJECT_BINARY_DIR}/doc1.dvi
  )

# finally add the custom target that when invoked
# will cause the generation of the pdf file

add_custom_target(TDocument ALL
  DEPENDS ${PROJECT_BINARY_DIR}/doc1.pdf
  )

The behavior of this bit of CMake, in alignment with the documentation for add_custom_target, compiles my latex document every time I build my project. I want the latex document to be built only when it's dependencies are altered. So, I modified it to be

# Add the rule to build the .pdf file from the .tex
# file. This relies on LATEX and DVIPDF being set correctly
#
add_custom_command(
  OUTPUT  ${PROJECT_BINARY_DIR}/doc1.pdf
  DEPENDS ${PROJECT_SOURCE_DIR}/doc1.tex
  COMMAND ${LATEX}  ${PROJECT_SOURCE_DIR}/doc1.tex
  COMMAND ${DVIPDF} ${PROJECT_BINARY_DIR}/doc1.dvi
  )

# finally add the custom target that when invoked
# will cause the generation of the pdf file

add_custom_target(TDocument # no longer specifying ALL
  DEPENDS ${PROJECT_BINARY_DIR}/doc1.pdf
  )

I thought this would be as simple as removing ALL from the custom target, but instead of building the PDF whenever its dependencies are altered, it is not built at all, whether doc1.tex is edited or even if doc1.pdf is deleted.

I have used this pattern successfully to manage, for example, CSV files that are created by C++ executables before so it's not at all clear to me why it wouldn't work with latex.


Solution

  • From the CMake documentation for [`add_custom_target()`][1] (emphasis theirs)

    Adds a target with the given name that executes the given commands. The target has no output file and is always considered out of date even if the commands try to create a file with the name of the target. Use the add_custom_command() command to generate a file with dependencies.

    Since the target is always out-of-date it will always build, regardless of its status. If you want a target with dependencies on source files and/or output use add_custom_command().

    After re-reading your question and code more carefully, your CMakeLists.txt is correct.

    $ ls
    CMakeLists.txt  doc1.tex
    
    $ cat CMakeLists.txt
    cmake_minimum_required(VERSION 3.22)
    project(q)
    
    set(LATEX "latex")
    set(DVIPDF "dvipdf")
    
    add_custom_command(
      OUTPUT  ${PROJECT_BINARY_DIR}/doc1.pdf
      DEPENDS ${PROJECT_SOURCE_DIR}/doc1.tex
      COMMAND ${LATEX}  ${PROJECT_SOURCE_DIR}/doc1.tex
      COMMAND ${DVIPDF} ${PROJECT_BINARY_DIR}/doc1.dvi
      )
    
    add_custom_target(TDocument ALL
      DEPENDS ${PROJECT_BINARY_DIR}/doc1.pdf
      )
    
    $ cmake .
    -- The C compiler identification is GNU 11.4.0
    -- The CXX compiler identification is GNU 11.4.0
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Check for working C compiler: /usr/bin/cc - skipped
    -- Detecting C compile features
    -- Detecting C compile features - done
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Check for working CXX compiler: /usr/bin/c++ - skipped
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /home/nega/q
    
    $ make VERBOSE=1
    /usr/bin/cmake -S/home/nega/q -B/home/nega/q --check-build-system CMakeFiles/Makefile.cmake 0
    /usr/bin/cmake -E cmake_progress_start /home/nega/q/CMakeFiles /home/nega/q//CMakeFiles/progress.marks
    make  -f CMakeFiles/Makefile2 all
    make[1]: Entering directory '/home/nega/q'
    make  -f CMakeFiles/TDocument.dir/build.make CMakeFiles/TDocument.dir/depend
    make[2]: Entering directory '/home/nega/q'
    cd /home/nega/q && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/nega/q /home/nega/q /home/nega/q /home/nega/q /home/nega/q/CMakeFiles/TDocument.dir/DependInfo.cmake --color=
    make[2]: Leaving directory '/home/nega/q'
    make  -f CMakeFiles/TDocument.dir/build.make CMakeFiles/TDocument.dir/build
    make[2]: Entering directory '/home/nega/q'
    [100%] Generating doc1.pdf
    latex /home/nega/q/doc1.tex
    This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2022/dev/Debian) (preloaded format=latex)
     restricted \write18 enabled.
    entering extended mode
    (/home/nega/q/doc1.tex
    LaTeX2e <2021-11-15> patch level 1
    L3 programming layer <2022-01-21>
    (/usr/share/texlive/texmf-dist/tex/latex/base/article.cls
    Document Class: article 2021/10/04 v1.4n Standard LaTeX document class
    (/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo))
    (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty
    For additional information on amsmath, use the `?' option.
    (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty
    (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty))
    (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty)
    (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty))
    (/usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-dvips.def)
    No file doc1.aux.
    [1] (./doc1.aux) )
    Output written on doc1.dvi (1 page, 1760 bytes).
    Transcript written on doc1.log.
    dvipdf /home/nega/q/doc1.dvi
    make[2]: Leaving directory '/home/nega/q'
    [100%] Built target TDocument
    make[1]: Leaving directory '/home/nega/q'
    /usr/bin/cmake -E cmake_progress_start /home/nega/q/CMakeFiles 0
    
    $ make VERBOSE=1
    /usr/bin/cmake -S/home/nega/q -B/home/nega/q --check-build-system CMakeFiles/Makefile.cmake 0
    /usr/bin/cmake -E cmake_progress_start /home/nega/q/CMakeFiles /home/nega/q//CMakeFiles/progress.marks
    make  -f CMakeFiles/Makefile2 all
    make[1]: Entering directory '/home/nega/q'
    make  -f CMakeFiles/TDocument.dir/build.make CMakeFiles/TDocument.dir/depend
    make[2]: Entering directory '/home/nega/q'
    cd /home/nega/q && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/nega/q /home/nega/q /home/nega/q /home/nega/q /home/nega/q/CMakeFiles/TDocument.dir/DependInfo.cmake --color=
    make[2]: Leaving directory '/home/nega/q'
    make  -f CMakeFiles/TDocument.dir/build.make CMakeFiles/TDocument.dir/build
    make[2]: Entering directory '/home/nega/q'
    make[2]: Nothing to be done for 'CMakeFiles/TDocument.dir/build'.
    make[2]: Leaving directory '/home/nega/q'
    [100%] Built target TDocument
    make[1]: Leaving directory '/home/nega/q'
    /usr/bin/cmake -E cmake_progress_start /home/nega/q/CMakeFiles 0
    
    $ touch doc1.tex
    
    $ make
    [100%] Generating doc1.pdf
    This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2022/dev/Debian) (preloaded format=latex)
     restricted \write18 enabled.
    entering extended mode
    (/home/nega/q/doc1.tex
    LaTeX2e <2021-11-15> patch level 1
    L3 programming layer <2022-01-21>
    (/usr/share/texlive/texmf-dist/tex/latex/base/article.cls
    Document Class: article 2021/10/04 v1.4n Standard LaTeX document class
    (/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo))
    (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty
    For additional information on amsmath, use the `?' option.
    (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty
    (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty))
    (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty)
    (/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty))
    (/usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-dvips.def)
    (./doc1.aux) [1] (./doc1.aux) )
    Output written on doc1.dvi (1 page, 1760 bytes).
    Transcript written on doc1.log.
    [100%] Built target TDocument
    
    $
    

    You'll notice that with the second invocation of make VERBOSE=1 that latex and dvipdf are not run, since doc1.tex hasn't changed. After touching doc1.tex changing its timestamp, and running make for the third time, latex and dvipdf are run again.

    If your doc1.tex is being processed every time you build, I suspect either something in your greater build process is touching your source file (doc1.tex), changing its timestamp, or your source file has a "future date". A future date for a timestamp will cause the source file to be out-of-date until that time has passed.