[Tex/LaTex] Latexmk can’t see a dependency on a .fmt format file

automationcompilingformat-fileslatexmk

I'm using latex on a small netbook, and with average-sized files (~150 pages at the moment) compilation is already pretty slow. So I am looking for every way to speed up the compilation.

In this search, I found this page explaining how to "precompile a preamble", or, latexically-speaking, how to make a custom format file.
That works fine enough.

But my preamble isn't as static as it should be, I keep adding or modifying definitions in there.
To mix the two solutions, at the moment, I use a Makefile together with latexmk, the makefile handling the regeneration of the .fmt file if needed, and latexmk taking good care of the rest.

But I have two problems with this :

  • integration with emacs to compile the whole document when working on a single file (make needs to be called in the proper directory)
  • latexmk doesn't recompile the file every time the preamble is modified, because it doesn't count as a dependency.

So I'd like to find a way to get rid of this Makefile, and get all the stuff done by latexmk.

I edited my .latexmkrc to include this :

pdflatex = 'pdflatex -fmt main %O %S';

add_input_ext('pdflatex','fmt');

add_cus_dep('tex', 'fmt', 1, 'compilepreamble');
sub compilepreamble {
    print "Preamble compiling...\n";
    $command = '&.pdflatex ./fmt/preamble.tex\dump';
    system("pdflatex -ini -jobname='$_[0]' '$command'");
};   

But with this, if the file main.fmt is not present or if fmt/preamble.tex is updated, the main.fmt file won't be regenerated.
Actually, in the first case, I get an error from pdflatex saying the it can't find main.fmt format file. So I understand latexmk doesn't even try to build main.fmt, it's probably not a problem in my custom dependency. However, I think I've added everything necessary for this to work.

So… Did I forget something? Is there a way round? Or is it a hopeless quest?

Best Answer

What you forgot is that a custom dependency is intended to have input and output files that differ only in their extensions, e.g., to convert foo.ltx to foo.fmt. But they don't work to convert, for example, fmt/preamble.tex to main.fmt. A second, but less severe, problem is that when pdfLaTeX doesn't find the .fmt file, it dies with an error message to the screen, but it doesn't put the error message in the log file (so that latexmk doesn't detect that there is a missing file).

If you rename your preamble file to main.ltx (not in a subdirectory), then the following initialization code should work:

$recorder = 1;
$pdflatex = 'pdflatex -fmt main %O %S';
add_input_ext('pdflatex','fmt');
add_cus_dep('ltx', 'fmt', 1, 'compilepreamble');
sub compilepreamble {
    print "Preamble compiling for '$_[0]'...\n";
    my $fls_file = "$_[0].fls";
    my $source = "$_[0].ltx";
    my $fmt_file = "$_[0].fmt";
    my $return = system( "pdflatex", "-interaction=batchmode",
                         "-ini", "-recorder", "-jobname=$_[0]",
                         "&pdflatex $source \\dump" );
    if ($return) { 
        warn "Error in making format file '$fmt_file'\n";
       return $return;
    }
    my %input_files = ();
    my %output_files = ();
    $return = parse_fls( $fls_file, \%input_files, \%output_files );
    if ($return) {
        warn "No fls file '$fls_file' made; I cannot get dependency data\n";
        return 0; 
    }
    # Use latexmk's internal variables and subroutines for setting the
    #   dependency information.
    # Note that when this subroutine is called to implement a custom
    #   dependency, the following variables are set:
    #       $rule  contains the name of the current rule (as in the
    #              fdb_latexmk file)
    #       $PHsource is a pointer to a hash of source file
    #              information for the rule.  The keys of the hash are
    #              the names of the source files of the rule.
    foreach my $file (keys %input_files) {
        rdb_ensure_file( $rule, $file );
    }
    foreach my $file (keys %$PHsource) {
        if ( ! exists $input_files{$file} ) {
            print "   Source file of previous run '$file' ",
                  "IS NO LONGER IN USE\n";
            rdb_remove_files( $rule, $file );
        }
    }
    return 0;
};

I've used an extension .ltx rather than .tex so that you can have a preamble file with the same basename as the main file.

You'll need to run the pdflatex -ini ... manually the first time, to create the .fmt file. After that latexmk should handle things automatically.

Edit (2 April 2012): I improved the code so that it detects dependencies on files used by pdflatex -ini. This uses some of the internal variables and subroutines of latexmk.

Related Question