print "Latexmk rc file for using precompiled headers.\n", "John Collins. V. 1.3. 3 Jan 2024.\n"; # John Collins jcc8 at psu dot edu # To implement precompilation of preamble by the package mylatexformat. # See the documentation for the package mylatexformat for more information. # Overall strategy: # 1. At start of each run of *latex rule, copy (relevant part of) preamble # from the primary .tex file to a file with extension .hdr. # 2. The base name of the header file has -latex, or -pdflatex, etc # appended to it. That provides a flag as to which program is to be # used to make the .fmt file, and what program can use the .fmt file. # 2. A cus-dep is defined to make a .fmt file from the corresponding .hdr # file. It uses mylatexformat, and whichever of the -latex, -pdflatex, # etc strings appears at the end of its base name indicates which # program to use to create it. (The -ini option is given to the # program to indicate that a .fmt file is to be made.) # 4. Compilation of document: use .fmt file if it exists. # If not, inject a message into the log file, to indicate that the .fmt # is missing. After the analysis of the results of the compilationi, # latexmk takes this as an indication that it should try to make the # .fmt file. # # Note that on an initial round of compilation, before the format file has # been created, latexmk will do one round of compilation without a # precompiled preamble. After that latexmk creates the format file for the # precompiled header. Some trickery could be used to get the format file # to be made first, but since this is a one-time-only issue, it's simpler # not to do the trickery. # # Restriction: Doesn't work with spaces and/or non-ASCII characters in # name of .tex file. # STILL TO DO: # # 1. Get full dependency information for the step for making the format. # Ideally that needs support from latexmk, so as to use its # already existing tools. (Complication: the usual way in which latexmk # determines dependencies from a run of a *latex type program assumes # that a correct run always generates an .aux file. That's not the case # for "pdflatex -ini" etc. # I've done a sufficient job, I think, by using the -recorder option, # analyzing the resulting .fls file and using latexmk's documented # rdb_set_source subroutine. # 2. On make of .fmt file, need ideally to use jobname = $root, to avoid # problems listed in # https://tex.stackexchange.com/questions/19403/tikzs-externalization-and-mylatex # Then I would need to do some renaming; perhaps save fls and log files from # main run, compile the header, rename the fls, log and fmt files from this # run, and finally, put the original fls and log files in place. # Better, perhaps: Use subdirectories of auxdir, e.g., named fmt-latex, # fmt-pdflatex, etc, to contain .fmt file (and .hdr file), along with # suitable setting of search path (in variable TEXFORMATS). # General settings $out_dir = 'output'; $aux_dir = 'auxdir'; $emulate_aux = 1; # Avoid any problems with TeXLive, which doesn't # natively support different aux and out directories. $show_time = 1; # Show diagnostics on timings, so as to make visible # time savings due to use of preamble. # Sub-second sleep time will work in latexmk v. 4.82 upwards. # That gives excellent responsiveness when preview-continuous mode is used, # and helps to enhance any significant gain in compilation time from the # use of a precompiled preamble. $sleep_time = 0.05; $pdf_mode = 1; # Use pdflatex. Note that xelatex and lualatex don't work # with precompiled preambles. # Configuration for precompiled preamble: $make_fmt = '%C %O -ini -output-directory=%V -jobname=%B "&%C" mylatexformat.ltx %S'; # I only arrange to use latex and pdflatex with precompiled preambles. # None of lualatex, xelatex, and hilatex can use precompiled headers (from # tests on 22 Dec 2023). Each had different trouble, and this is a known # problem. # But things did work for latex and pdflatex. # Therefore only set things up for latex and pdflatex. foreach my $engine ( 'latex', 'pdflatex' ) { say "Setting rules for $engine"; ${$engine} = "internal mylatex %R $engine %O %S"; push @generated_exts, "%R-$engine.*"; } add_cus_dep( 'hdr', 'fmt', 0, make_fmt_cus_dep ); push @generated_exts, '%R-*.hdr', '%R-*.fmt'; #================================================= sub make_fmt_cus_dep { my $path_base = $_[0]; my ($base, $path) = fileparse($path_base); my $source = "$path_base.hdr"; my $dest = "$path_base.fmt"; my $fls = "$path_base.fls"; my $engine = 'pdflatex'; if ( $base =~ /-([^-]+)$/ ) { $engine = $1; } my $cmd = $make_fmt; $cmd =~ s/%C/$engine/g; my $options = '-recorder'; if ($silent) { $options .= ' -interaction=batchmode'; } my $ret = Run_subst( $cmd, 2, $options, $source, $dest, $base ); my @source_files = get_fls_inputs( $fls ); if (@source_files) { rdb_set_source( $rule, @source_files ); } else { warn "Could not determine source files for '$rule'\n"; } return $ret; } #================================================= sub make_hdrA { use strict; my ($source, $dest) = @_; my $h_in = undef; my $h_out = undef; if ( ! open( $h_in, "<", $source ) ) { print "make_header could not read source file '$source'\n"; return 1; } if (! open( $h_out, ">", $dest ) ) { print "make_header could not write header file '$dest'\n"; return 2; } while (<$h_in>) { if ( /^\s*\\begin\s*{document}/ || /^\s*\\endofdump/ || /^\s*\\csname\s+endofdump\\endcsname/ ) { last; } print $h_out $_; } print $h_out "\\endofdump\n"; close $h_out; close $h_in; return 0; } #================================================= sub mylatex { # Arguments are: # 1. base name of the fmt file # 2. the compilation program # then the arguments to be use strict; our ($aux_dir1, $out_dir1, $emulate_aux, $Pbase, $Psource ); my ($fmt_base, $engine, @args) = @_; $fmt_base .= "-$engine"; my $source = $$Psource; my $ret = 0; my $fmt_file = "$aux_dir1$fmt_base.fmt"; my $hdr_file = "$aux_dir1$fmt_base.hdr"; if ( make_hdrA( $source, $hdr_file ) > 0 ) { die "I'm stopping, since I could not make '$hdr_file' from '$source'.\n"; } # Location of .fls file after running engine, before any fudge to # emulate aux dir: my $fls_file = ($emulate_aux ? $aux_dir1 : $out_dir1 ) . "$$Pbase.fls"; my $log_file = "$aux_dir1$$Pbase.log"; if ( -e $fmt_file ) { @args = ("-fmt=$fmt_base", @args); } else { print "Format file '$fmt_file' does not exist. I'll run without it.\n"; } my @cmd = ($engine, @args); print "Running '@cmd'\n"; $ret = system @cmd; # Add lines to fls file to add dependency information: # (a) that a hdr file is generated. # (b) dependency on fls file. # Earlier I thought I needed to force a dependence on fls file. # (22 Dec 2023) I don't remember why, so I'll leave that in. append( $fls_file, "INPUT $fls_file\n". "OUTPUT $hdr_file\n" ); # Ensure latexmk knows that the .fmt file needs to be made if it # doesn't yet exist: if ( !-e $fmt_file ) { append( $log_file, "No file $fmt_file.\n" ); } return $ret; } #================================================= sub append { use strict; my ($file, $string) = @_; if ( !-e $file ) { return 1; } open( my $fh, ">>", $file ) or die "Cannot append to '$file'\n $_"; print $fh $string; close $fh; return 0; } #================================================= sub get_fls_inputs { my $fls_file = shift; my %inputs = (); my $fh = undef; if ( ! open( $fh, '<', $fls_file ) ) { warn "Cannot read '$fls_file':\n$!"; return (); } while (my $line = <$fh>) { if ( $line =~ /^INPUT\s+(.+)\s*$/ ) { my $file = $1; $inputs{$file} = 1; } } close $fh; return keys %inputs; } #=================================================