summaryrefslogtreecommitdiff
path: root/support/latexmk/example_rcfiles/precompile-preamble_latexmkrc
blob: 843334b05f7a1b18e0a0c8d57b5647ca4344c4f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
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;
}

#=================================================