diff options
author | Karl Berry <karl@freefriends.org> | 2023-04-04 20:14:52 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2023-04-04 20:14:52 +0000 |
commit | 0bdc1ad6005c59285dca135dbdafd52991dc8973 (patch) | |
tree | 13f3ab9745e1383b9eedb8c5959b3987b4f1d1d3 /Master/texmf-dist/scripts | |
parent | dbdd291ab3ef56fc289c1b11c6eab568af814002 (diff) |
latexmk (4apr23)
git-svn-id: svn://tug.org/texlive/trunk@66766 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts')
-rwxr-xr-x | Master/texmf-dist/scripts/latexmk/latexmk.pl | 635 |
1 files changed, 458 insertions, 177 deletions
diff --git a/Master/texmf-dist/scripts/latexmk/latexmk.pl b/Master/texmf-dist/scripts/latexmk/latexmk.pl index 3067c358bc3..434da3529c6 100755 --- a/Master/texmf-dist/scripts/latexmk/latexmk.pl +++ b/Master/texmf-dist/scripts/latexmk/latexmk.pl @@ -1,43 +1,6 @@ #!/usr/bin/env perl use warnings; -# ???!!! TO DO 9 Aug 2022: -# 0. RETHINK warning listing handling etc, including warnings as -# errors, bad warnings as errors. Must systematize. Code is too split -# up. -# 1b. ===> In any case, check treatment of runs that give an error. -# 2. Think generally through logic of error handling. E.g., error in -# xelatex: should dvipdfmx be run? (Or with config. variable for the -# post-primaries.) Currently it's run. Probably correct: viewed file, -# if it can be made, can be useful for error diagnosis. Also: -# consistency between direct making of pdf file and indirect. -# But abort after error can also be useful. -# DONE 3. Are post-primaries guaranteed to be run in correct order? -# YES: See definition of @post_primary. -# 4. Clean up rdb_diagnose_changes2. -# COMMENTED OUT FOR NOW 5. Perhaps, remove use of kpsewhich in case of disappeared files? -# 6. Review rdb_make and rdb_make1 again. -# 7. Start in -pvc when prior run gave error: There's an error report after -# the first make. Same outside -pvc if have error run, then run -# latexmk; it reports error from previous run. -# 9, Re %pass and rdb_rerun_needed: Perhaps remove outside_make_loop, and -# have separate subroutine for use by make_preview_continuous, and -# standard rdb_rerun_needed calls it and then does its source_rule -# stuff. -# 10. Do I need to make rdb_user_changes better? -# -# OK 12. Is classification of post_primary watertight? They are intended to -# be outside any loop. Start with target files, and call encountered -# rules post_primary until a primary is encountered. That assumes that -# on the pathway to target from primary, source files are not generated -# by non-primary rules. -# There's no requirement for post_primaries to be outside dependency -# loops. The make algorithm handles that. All that matters is that this -# commonly so, and therefore we are doing an appropriate ordering of -# examination of rules, by pre-primary, primary, post-primary. -# Post-primaries always have primary as a prerequisite for the source -# files. - ## Copyright John Collins 1998-2023 ## (username jcc8 at node psu.edu) ## (and thanks to David Coppit (username david at node coppit.org) @@ -79,8 +42,8 @@ use warnings; $my_name = 'latexmk'; $My_name = 'Latexmk'; -$version_num = '4.79'; -$version_details = "$My_name, John Collins, 7 Jan. 2023. Version $version_num"; +$version_num = '4.80'; +$version_details = "$My_name, John Collins, 4 Apr. 2023. Version $version_num"; use Config; use File::Basename; @@ -410,6 +373,7 @@ $log_wrap = 79; ## will give match to line starting "No data file found `filename'" @file_not_found = ( '^No file\\s*(.*)\\.$', + '^No file\\s*(.+)\s*$', '^\\! LaTeX Error: File `([^\\\']*)\\\' not found\\.', '^\\! I can\\\'t find file `([^\\\']*)\\\'\\.', '.*?:\\d*: LaTeX Error: File `([^\\\']*)\\\' not found\\.', @@ -946,6 +910,63 @@ $search_path_separator = ':'; $tmpdir = "."; +# Latexmk does tests on whether a particular generated file, e.g., log or +# fls, has been generated on a current run of a rule, especially *latex, or +# is leftover from previous runs. This is done by finding whether or not +# the modification time of the file is at least as recent as the system +# time at the start of the run. A file with a modification time +# significantly less than the time at the start of the run is presumably +# left over from a previous run and not generated in the currrent run. (An +# allowance is made in this comparison for the effects of granularity of +# file-system times. Such granularity can make a file time a second or two +# earlier than the system time at which the file was last modified.) +# +# But generated files may be on a file system hosted by a server computer +# that is different than the computer running latexmk. There may there may +# be an offset between the time on the two computers; this can make it +# appear that the generated files were made before the run. Most of the +# time, this problem does not arise, since (a) typical usage of latexmk is +# with a local file system, and (b) current-day computers and operating +# systems have their time synchronized accurately with a time server. +# +# But when latexmk sees symptoms of an excessive offset, it measures the +# offset between the filesystem time and the system time. This involves +# writing a temporary file, getting its modification time, and deleting +# it. The following variables are used for this purpose. + +# +our $filetime_offset_measured = 0; # Measurement not yet done. +our $filetime_offset = 0; # Filetime relative to system time. +our $filetime_offset_report_threshold = 10; # Threshold beyond which filetime offsets + # are reported; large offsets indicate + # incorrect system time on at least one system. +# The following variable gives the threshold for detection of left-over +# file. It allows for (a) different granularity between system time and +# filesystem time, (b) for some mismatch between file and system time. +# Note that the making or not making of a file is controlled by the +# state of the document being compiled and by latexmk's configuration. +# So a file that is left over from a previous run and not overwritten +# on the current run will have a file time at least many seconds less +# than the current time, corresponding to the time scale for a human +# run-edit-run cycle. +# +# Note that the making or not making of a file is controlled by the +# state of the document being compiled and by latexmk's configuration. +# So a file that is left over from a previous run and not overwritten +# on the current run will have a file time at least many seconds less +# than the current time, corresponding to the time scale for a human +# run-edit-run cycle. So one does NOT have to tune this variable +# precisely. +# +# Concerning granularity of file system +# FAT file system: 2 sec granularity. Others 1 sec or less. +# Perl CORE's mtime in stat: 1 sec. +# Perl CORE's time(): 1 sec. Time::HiRes::time(): Much less than 1 sec. + +our $filetime_causality_threshold = 5; + + +################################################################ ################################################################ @@ -1387,6 +1408,11 @@ $dependents_phony = 0; # Whether list(s) of dependencies includes phony targets # (as with 'gcc -MP'). $deps_file = '-'; # File for dependency list output. Default stdout. $rules_list = 0; # Whether to display list(s) of dependencies +# Kind of escaping in names of files written to deps file. +$deps_escape = 'none'; +# Allowed kinds of escape: +%deps_escape_kinds = ( 'none' => ' ', 'unix' => '\ ', 'nmake' => '^ '); + @dir_stack = (); # Stack of pushed directories, each of form of # pointer to array [ cwd, good_cwd ], where # good_cwd differs from cwd by being converted @@ -1431,13 +1457,6 @@ our $show_time = 0; # appears not to included subprocess times, so we use clock time instead. our $times_are_clock = ($^O eq "MSWin32" ); -# Allowance for different granularity in time since Epoch and file mtime. -# Needed in testing whether a file was generated during a run of a program. -# FAT file system: 2 sec granularity. Others 1 sec or less. -# Perl CORE's mtime in stat: 1 sec. -# Perl CORE's time(): 1 sec. Time::HiRes::time(): Much less than 1 sec. -our $filetime_slop = 2; - # Data for 1 run and global (ending in '0'): our ( $processing_time1, $processing_time0, @timings1, @timings0); @@ -1650,7 +1669,7 @@ $allow_switch = 1; # Allow switch of rule structure to accommodate # out_of_date, out_of_date_user, # time_of_last_run, time_of_last_file_check, # changed - # last_result, last_message, + # last_result, last_result_info, last_message, # default_extra_generated, # ] # where @@ -1727,6 +1746,19 @@ $allow_switch = 1; # Allow switch of rule structure to accommodate # important enough to be reported with # the error summary. The warning # message is stored in last_message. + # last_result_info is info about run that gave + # code in last_result. Currently used values: + # '' No record of this rule being run + # 'CURR' Run of rule was in current + # round of compilation. + # 'PREV' Run of rule was in a previous + # round of compilation (as with + # -pvc), but in current invocation + # of latexmk. + # 'CACHE' Run of rule was in a previous + # invocation of latexmk, with + # last_result having been read + # from fdb_latexmk file. # last_message is error message for last run # default_extra_generated is a reference to an array # of specifications of extra generated files (beyond @@ -1950,8 +1982,7 @@ if ( $auto_rc_use ) { @command_line_file_list = (); $bad_options = 0; -while ($_ = $ARGV[0]) -{ +while (defined($_ = $ARGV[0])) { # Make -- and - equivalent at beginning of option, # but save original for possible use in *latex command line $original = $_; @@ -1976,6 +2007,12 @@ while ($_ = $ARGV[0]) elsif (/^-d$/) { $banner = 1; } elsif (/^-dependents$/ || /^-deps$/ || /^-M$/ ) { $dependents_list = 1; } elsif (/^-nodependents$/ || /^-dependents-$/ || /^-deps-$/) { $dependents_list = 0; } + elsif (/^-deps-escape=(.*)$/) { + if ( $deps_escape_kinds{$1} ) { $deps_escape = $1; } + else { warn "$My_name: In '$_', kind of escape is not one of those I know, which are\n", + " ", join( ' ', sort( keys %deps_escape_kinds )), "\n"; + } + } elsif (/^-deps-out=(.*)$/) { $deps_file = $1; $dependents_list = 1; @@ -2758,8 +2795,7 @@ foreach $filename ( @file_list ) # assisted by dependency information from log files about previous # run, if the log file exists. - if ( $cleanup_mode ) { &do_small_cleanup; } - if ( $cleanup_mode == 1 ) { &do_extra_cleanup; } + if ( $cleanup_mode ) { do_cleanup( $cleanup_mode ); } if ($cleanup_only) { next FILE; } if ( ! -e $aux_main ) { @@ -2877,6 +2913,27 @@ if ( $do_cd && ($#dir_stack > -1) ) { &finish_dir_stack; } +if ($filetime_offset_measured) { + if ( (abs($filetime_offset) >= $filetime_offset_report_threshold) + && ($diagnostics || ! $silent) ) + { + warn "$My_name: I am working around an offset relative to my system time by\n", + " $filetime_offset secs for file times in directory '$aux_dir1'.\n", + " This **probably** indicates that \n", + " (a) I ($my_name) am running on one computer, while the filesystem is\n", + " hosted on a different computer/\n", + " (b) There is a substantial time offset between system times on the two\n", + " computers.\n", + " (c) Therefore at least one of the computers has a misconfigured operating\n", + " system such that its system time is not correctly synchronized with a\n", + " time server.\n", + " These issues are likely to cause problems with other software, and any\n", + " such operating-system misconfigurations should be corrected. By default\n", + " current operating systems are configured to correctly synchronize system\n", + " time when they are connected to the Internet\n"; + } +} + if ($failure_count > 0) { if ( $#file_list > 0 ) { # Error occured, but multiple files were processed, so @@ -2961,9 +3018,13 @@ sub show_timing_grand { sub set_tex_cmds { # Usage, e.g., set_tex_cmds( '%O %S' ) + # or set_tex_cmds( '%C-dev %O %S' ) my $args = $_[0]; foreach my $cmd ( keys %possible_primaries ) { - ${$cmd} = "$cmd $args"; + my $spec = $args; + if ( $spec =~ /%C/ ) { $spec =~ s/%C/$cmd/g; } + else { $spec = "$cmd $args"; } + ${$cmd} = $spec; } # N.B. See setting of $latex_default_switches, ..., # $xelatex_default_switches, etc, for any special options needed. @@ -3646,17 +3707,65 @@ sub set_trivial_aux_fdb { #************************************************************ #************************************************************ -sub do_extra_cleanup { - print "$My_name: Doing extra clean up (of final output files) for '$texfile_name'\n" - if ! $silent; - cleanup1( $aux_dir1, @final_output_exts ); - if ( $out_dir1 ne $aux_dir1 ) { cleanup1( $out_dir1, @final_output_exts ); } -} # END do_extra_cleanup +sub do_cleanup { + my $kind = $_[0]; + if (! $kind ) { return; } + my @files_to_delete = (); + @dirs = ($aux_dir1); + if ($out_dir1 ne $aux_dir1) { push @dirs, $out_dir1; } + + push @files_to_delete, &get_small_cleanup; + if ($kind == 1) { + foreach my $dir1 (@dirs) { + push @files_to_delete, cleanup_get1( $dir1, @final_output_exts ); + } + } + # show_array( "Files to delete", sort @files_to_delete ); -#************************************************************ + # Names of contents of directory are longer than the name of the directory, + # but contain the directory name as an initial segment. + # Therefore deleting files and directories in the order given by reverse + # sort deletes contents of directory before attempting to delete the + # directory: + unlink_or_move( reverse sort @files_to_delete ); + + # If the fdb file (or log, fls and/or aux files) exist, it/they will have + # been used to make a changed rule database. But a cleanup implies + # that we need a virgin rule database, corresponding to current state + # of files (after cleanup) so we reset the rule database and rule net: + &rdb_initialize_rules; +} -sub do_small_cleanup { - # Assume dependency information from previous run has been obtained. +#---------------------------------------- + +sub cleanup_get1 { + # Usage: cleanup_get1( directory, patterns_or_exts_without_period, ... ) + # Return array of files obeying the specification in the given directory. + # Specifications are either extensions to be appended to root_filename + # or are patterns containing %R for root_filename of job, with possible + # wildcards. + # Directory name must include directory separator, e.g., './' or 'output/', + # or be blank, i.e., suitable for prepending to file name. + + # The directory and the root file name are fixed names, so I must escape + # any glob metacharacters in them: + my $dir = fix_pattern( shift ); + my $root_fixed = fix_pattern( $root_filename ); + my @files = (); + foreach (@_) { + my $name = ( /%R/ ? $_ : "%R.$_" ); + $name =~ s/%R/${root_fixed}/; + $name = $dir.$name; + push @files, my_glob( "$name" ); + } + return @files; +} #END cleanup_get1 + +#---------------------------------------- + +sub get_small_cleanup { + # Get list of files to be deleted in a small cleanup. + # Assume dependency information from previous run has been obtained. my %other_generated = (); my %cusdep_generated = (); my @index_bibtex_generated = (); @@ -3759,32 +3868,22 @@ sub do_small_cleanup { } my @clean_args = keys %generated_exts; - cleanup1( $aux_dir1, @clean_args ); - if ( $out_dir1 ne $aux_dir1 ) { cleanup1( $out_dir1, @clean_args ); } + push @files, cleanup_get1( $aux_dir1, @clean_args ); + if ( $out_dir1 ne $aux_dir1 ) { push @files, cleanup_get1( $out_dir1, @clean_args ); } - my @specific_deletions = @std_small_cleanup_files; + push @files, @std_small_cleanup_files; foreach my $file (@std_small_cleanup_files) { foreach my $dir ($aux_dir1, $out_dir1 ) { - if ($dir) { push @specific_deletions, "$dir$file"; } + if ($dir) { push @files, "$dir$file"; } } } - unlink_or_move( @specific_deletions, - keys %index_bibtex_generated, - keys %aux_files ); + push @files, keys %index_bibtex_generated, keys %aux_files; - if ($cleanup_includes_generated) { - unlink_or_move( keys %other_generated ); - } - if ( $cleanup_includes_cusdep_generated) { - unlink_or_move( keys %cusdep_generated ); - } - unlink_or_move( $fdb_name ); - # If the fdb file (or log, fls and/or aux files), it/they will have been - # used to make a changed rule database. But a cleanup implies that we - # need a virgin rule database, corresponding to current state of files, - # so we reset the rule database and net: - &rdb_initialize_rules; -} # END do_small_cleanup + if ($cleanup_includes_generated) { push @files, keys %other_generated; } + if ( $cleanup_includes_cusdep_generated) { push @files, keys %cusdep_generated; } + push @files, $fdb_name; + return @files; +} # END get_small_cleanup #************************************************************ @@ -4211,6 +4310,14 @@ CHANGE: $have_break = 0; $last_action_time = time(); $waiting = 1; + rdb_for_some( + [keys %rule_db], + sub{ + if ($$Plast_result_info eq 'CURR') { + $$Plast_result_info = 'PREV'; + } + } + ); print "\n=== Watching for updated files. Use ctrl/C to stop ...\n"; WAIT: while (1) { sleep( $sleep_time ); @@ -4369,23 +4476,6 @@ sub execute_code_string { #************************************************************ -sub cleanup1 { - # Usage: cleanup1( directory, exts_without_period, ... ) - # - # The directory and the root file name are fixed names, so I must escape - # any glob metacharacters in them: - my $dir = fix_pattern( shift ); - my $root_fixed = fix_pattern( $root_filename ); - foreach (@_) { - my $name = /%R/ ? $_ : "%R.$_"; - $name =~ s/%R/${root_fixed}/; - $name = $dir.$name; - unlink_or_move( my_glob( "$name" ) ); - } -} #END cleanup1 - -#************************************************************ - sub cleanup_cusdep_generated { # Remove files generated by custom dependencies rdb_for_actives( \&cleanup_one_cusdep_generated ); @@ -4523,6 +4613,8 @@ sub print_help " -cd- - Do NOT change to directory of source file when processing it\n", " -dependents or -deps - Show list of dependent files after processing\n", " -dependents- or -deps- - Do not show list of dependent files\n", + " -deps-escape=<kind> - Set kind of escaping of spaces in names in deps file\n", + " (Possible values: ", join( ' ', sort keys %deps_escape_kinds ), ")\n", " -deps-out=file - Set name of output file for dependency list,\n", " and turn on showing of dependency list\n", " -dF <filter> - Filter to apply to dvi file\n", @@ -5100,15 +5192,24 @@ sub correct_aux_out_files { # Deal with situations after a *latex run where files are in different # directories than expected (specifically aux v. output directory). # Do minimal fix ups to allow latexmk to analyze dependencies with log - # and fls files in expected places + # and fls files in expected places. + + + # Deal with log file in unexpected place (e.g., lack of support by *latex + # of -aux-directory option. This is to be done first, since a run of + # *latex always produces a log file unless there is a bad error, so + # this gives the best chance of diagnosing errors. + my $where_log = &find_set_log; + if ( $emulate_aux && ($aux_dir ne $out_dir) ) { - # Move output and fls files to out_dir + # Move output files from aux_dir to out_dir + # Move fls file also, if the configuration is for fls in out_dir. # Omit 'xdv', that goes to aux_dir (as with MiKTeX). It's not final output. foreach my $ext ( 'fls', 'dvi', 'pdf', 'ps', 'synctex', 'synctex.gz' ) { if ( ($ext eq 'fls') && ! $fls_uses_out_dir ) {next;} my $from = "$aux_dir1$root_filename.$ext"; my $to = "$out_dir1$root_filename.$ext" ; - if ( test_gen_file( $from ) ) { + if ( test_gen_file_time( $from ) ) { if (! $silent) { print "$My_name: Moving '$from' to '$to'\n"; } my $ret = move( $from, $to ); if ( ! $ret ) { die " That failed, with message '$!'\n";} @@ -5116,10 +5217,6 @@ sub correct_aux_out_files { } } - # Deal with log file in unexpected place (e.g., lack of support by *latex - # of -aux-directory option. - &find_set_log; - # Fix ups on fls file: if ($recorder) { # Deal with following special cases: @@ -5167,18 +5264,18 @@ sub correct_aux_out_files { # place. But only do this if the file time is compatible with being # generated in the current run, and if the standard fls file hasn't # been made in the current run, as tested by the use of - # test_gen_file; that avoids problems with fls files left over from + # test_gen_file_time; that avoids problems with fls files left over from # earlier runs with other versions of latex. - if ( ! test_gen_file( $std_fls_file ) ) { + if ( ! test_gen_file_time ( $std_fls_file ) ) { foreach my $cand (@other_fls_names) { - if ( test_gen_file( $cand ) ) { + if ( test_gen_file_time( $cand ) ) { print "$My_name: Copying '$cand' to '$std_fls_file'.\n"; copy $cand, $std_fls_file; last; } } } - if ( ! test_gen_file( $std_fls_file ) ) { + if ( ! test_gen_file_time( $std_fls_file ) ) { warn "$My_name: fls file doesn't appear to have been made.\n"; } } @@ -5186,37 +5283,120 @@ sub correct_aux_out_files { #----------------- +sub find_log { + # Locate log file generated on this run. + # Side effect: measure filetime offset if necessary. + # Don't take other actions. + # Returns + # 0 log file not found; + # 1 log file in aux_dir i.e., correct place; + # 2 log file **not** in aux_dir but in out_dir + # (only applies if $emulate_aux off) + # 3 log file is in ., not aux_dir or out_dir. + # 4 log file in aux_dir, but out-of-date + # 5 log file in out_dir, but out-of-date, + # (only applies if $emulate_aux off) + # 6 log file is in ., but out-of-date + + + my $where_log = -1; # Nothing analyzed yet + my $log_aux = "$aux_dir1$root_filename.log"; + my $log_out = "$out_dir1$root_filename.log"; + my $log_cwd = "./$root_filename.log"; + + # Basic tests first that assume accuracy of time of file system: + if ( test_gen_file_time( $log_aux ) ) { + # Expected case + return 1; + } + elsif ( (! $emulate_aux) && test_gen_file_time( $log_out ) ) { + # *latex was called with -aux-directory option, but does not + # implement it (e.g., TeXLive's version) + return 2; + } + elsif ( test_gen_file_time( $log_cwd ) ) { + # Arrive here typically with configuration error so that aux_dir + # and/or out_dir aren't supplied to *latex. + return 3; + } + + # Arrive here only if a log file with a time stamp not too much earlier + # than the run time has not found in a relevant place. + # If relevant files exist, then we must test for a serious offset + # between system time and filesystem time (i.e., filesystem server + # time). + if ( ! $filetime_offset_measured ) { + $filetime_offset = get_filetime_offset( $aux_dir1."tmp" ); + $filetime_offset_measured = 1; + } + + my @candidates = ( ); + my $latest_mtime = undef; + my $latest_log = undef; + + if ( -e $log_aux ) { + if ( test_gen_file_time( $log_aux ) ) { return 1; } + return 4; + } + # Get here if log file in aux doesn't exist or is apparently too old. + if ( (! $emulate_aux) && ( -e $log_out ) ) { + if (test_gen_file_time( $log_out ) ) { return 2; } + return 5; + } + if ( -e $log_cwd ) { + if (test_gen_file_time( $log_cwd ) ) { return 3; } + return 6; + } + return 0; +} + sub find_set_log { # Locate the log file, generated on this run. # It should be in aux_dir. But: - # 1. With a sufficiently severe error in *latex, no log file was generated. - # 2. With aux_dir ne out_dir and emulate_aux off and a (TeXLive) *latex + # a. With aux_dir ne out_dir and emulate_aux off and a (TeXLive) *latex # that doesn't support aux_dir, the log file is in out_dir. - # 3. If the specified command has no %O or if *latex doesn't support + # b. If the specified command has no %O or if *latex doesn't support # out_dir (hence not TeXLive and not MiKTeX), the log file would # be in cwd. + # c. With a sufficiently severe error in *latex, no log file was generated. + # Any log file that exists will be a left over from a previous run, + # and hence have a filetime less than the system time at the start of + # the current run. (The strict filetime criterion is modified in the + # implementation to allow for issues from file system's time + # granularity, and mismatch of time on server hosting file system.) # - # 3 is fatal error. - # 2 is handled by turning emulate_aux on, after which next run of *latex - # handles problem. - # 1 is like any other error. - # 4 is treated like 1 (log file not found). - # - # Returns + # Possible return values, and side effects. # 0 log file not found; # 1 log file in aux_dir i.e., correct place; - # 2 log file **not** in aux_dir but in out_dir, - # emulate_aux turned on, commands fixed, - # and log file copied to aux_dir - # If the log file is found in cwd, report it and give fatal error. - - my $where_log = -1; # Nothing analyzed yet + # 2 log file **not** in aux_dir but in out_dir + # (only applies if $emulate_aux off) + # $emulate_aux turned on, commands fixed, log file copied to + # aux_dir, and flags set to cause rerun + # 3 log file is in ., not aux_dir or out_dir. + # Fatal error raised here, since cause is normally a configuration error + # not an error caused by contents of user file. + # 4 log file in aux_dir, but out-of-date + # 5 log file in out_dir, but out-of-date, + # (only applies if $emulate_aux off) + # 6 log file is in ., but out-of-date + # + # Cases: 0, 4, 5, 6 are error conditions to be handled by caller + # 2 is to be handled by caller by a rerun + # 1 is success. + + + my $log_aux = "$aux_dir1$root_filename.log"; + my $log_out = "$out_dir1$root_filename.log"; + my $log_cwd = "./$root_filename.log"; - if ( test_gen_file( "$aux_dir1$root_filename.log" ) ) { - # .log file is in expected place. - $where_log = 1; + my $where_log = &find_log; + my $good_log_found = 0; + + if ($where_log == 1 ) { + # As expected + $good_log_found = 1; } - elsif ( (! $emulate_aux) && test_gen_file( "$out_dir1$root_filename.log" ) ) { + elsif ($where_log == 2 ) { warn "$My_name: .log file in '$out_dir' instead of expected '$aux_dir'\n", " But emulate_aux is off. So I'll turn it on.\n", " I'll copy the log file to the correct place.\n", @@ -5231,8 +5411,9 @@ sub find_set_log { s/ -output-directory=[^ ]*(?= )//g; s/ -aux(-directory=[^ ]*)(?= )/ -output$1/g; } + $good_log_found = 1; } - elsif ( test_gen_file( "$root_filename.log" ) ) { + if ($where_log == 3 ) { # .log file is not in out_dir nor in aux_dir, but is in cwd. # Presumably there is a configuration error # that prevents the directories from being used by latex. @@ -5242,14 +5423,35 @@ sub find_set_log { " options with the *latex command.\n", " I'll stop.\n"; } - else { - # No .log file found + elsif ($where_log == 4 ) { + warn "$My_name: The expected log file, '$log_aux', does exist, but it appears\n", + " to be left over from a previous run: The time at the start of the\n", + " current run was $$Prun_time, but the log file appears to have been\n", + " created significantly earlier, at ", get_mtime($log_aux), ".\n"; + } + elsif ($where_log == 5 ) { + warn "$My_name: The expected log file, '$log_aux', does not exist, but one is found\n", + " in '$out_dir', but it apears to be left over from a previous run. The time\n", + " at the start of the current run was $$Prun_time, but the log file appears to\n", + " have been created significantly earlier, at ", get_mtime($log_out), ".\n"; + } + elsif ($where_log == 6 ) { + warn "$My_name: The expected log file, '$log_aux', does not exist, but one is found\n", + " in '.', but it apears to be left over from a previous run. The time\n", + " at the start of the current run was $$Prun_time, but the log file appears to\n", + " have been created significantly earlier, at ", get_mtime($log_cwd), ".\n"; + } + elsif ($where_log == 0) { + warn "$My_name: No log file was found, neither the expected one, '$log_aux', nor one in '.'.\n"; + if (! $emulate_aux) { warn " I also looked in '$out_dir'\n"; } + } + if ( ! $good_log_found ) { $failure = 1; $$Plast_result = 2; - $where_log = 0; $failure_msg = "*LaTeX didn't generate the expected log file '$log_name'\n"; } + return $where_log; } #END find_set_log @@ -6960,6 +7162,7 @@ sub rdb_read_set_rule { $$Prun_time = $run_time; $$Pcheck_time = $check_time; $$Plast_result = $last_result; + $$Plast_result_info = 'CACHE'; # Deal with possibility that destination file in fdb_latexmk from # run differs from what is currently set. Often that just reflects a @@ -7249,7 +7452,7 @@ sub rdb_set_latex_deps { # But we'll use the results later, so that they take priority over the findings # from the log file. local $fls_file_analyzed = 0; - if ($recorder && test_gen_file($fls_name) ) { + if ($recorder && test_gen_file_time($fls_name) ) { $fls_file_analyzed = (0== parse_fls( $fls_name, \%source_fls, \%generated_fls, \%first_read_after_write, \$pwd_latex )); if (! $fls_file_analyzed ) { @@ -7590,7 +7793,7 @@ NEW_SOURCE: } if ($diagnostics) { if ($num_new > 0 ) { - show_array( "$num_new new source files for rule '$rule':", @more_sources );k { print " '$_'\n"; } + show_array( "$num_new new source files for rule '$rule':", @more_sources ); } else { print "No new source files for rule '$rule':\n"; @@ -7684,20 +7887,25 @@ sub switch_output { sub test_gen_file { # Usage: test_gen_file( filename ) - # Tests whether the file was generated during a run of *latex. + # Tests whether a file of given name was generated during current run + # of *latex, with override of comparison of file and run time by + # file being listed in %generated_log or %generated_fls # Assumes context for primary rule. - # Two kinds of test are used: - # a. From %generated_log and %generated_fls, which works after the fls and - # log files hav been parsed, but in the log file only for certain files - # and for those TeX engines (not MiKTeX) that put \openout lines in log - # file. - # b. By the file existing and being at least as new as the system - # time at the start of the run. But make an allowance ($filetime_slop) for - # differences in granularity between reported system time and reported - # file mtimes. my $file = shift; return exists $generated_log{$file} || $generated_fls{$file} - || ( -e $file && ( get_mtime( $file ) >= $$Prun_time - $filetime_slop )); + || test_gen_file_time($file); +} + +#************************************************************ + +sub test_gen_file_time { + # Usage: test_gen_file_time( filename ) + # Tests whether a file of given name exists and was generated during + # current run of *latex. Comparison of file and run time used for + # testing whether file was generated or is left over from a previous run. + # + my $file = shift; + return (-e $file) && ( get_mtime( $file ) >= $$Prun_time + $filetime_offset - $filetime_causality_threshold ); } #************************************************************ @@ -7855,6 +8063,11 @@ sub rdb_one_dep { return; } local $new_dest = $file; + if ($$PHdest{$new_dest} ) { + # We already have a way of making the file. + # No need to find another one. + return; + } my ($base_name, $path, $toext) = fileparseA( $new_dest ); $base_name = $path.$base_name; $toext =~ s/^\.//; @@ -7863,6 +8076,7 @@ DEP: foreach my $dep ( @cus_dep_list ) { my ($fromext,$proptoext,$must,$func_name) = split('\s+',$dep); if ( $toext eq $proptoext ) { + # Look in search path for file of correct name: $base_name = rdb_find_source_file($base_name, $fromext); my $source = "$base_name.$fromext"; # Found match of rule @@ -7872,6 +8086,11 @@ DEP: if ( -e $source ) { my $from_rule = "cusdep $fromext $toext $base_name"; my $new_new_dest = "$base_name.$toext"; + if ($$PHdest{$new_new_dest} ) { + # We already have a way of making the file. + # No need to find another one. + return; + } if ($new_new_dest ne $new_dest) { rdb_ensure_file( $rule, $new_new_dest ); $new_dest = $new_new_dest; @@ -7919,6 +8138,11 @@ DEP: if ( -e $source ) { $new_dest = "$base_name.$proptoext"; my $from_rule = "cusdep $fromext $proptoext $base_name"; + if ( $$PHdest{$new_dest} ) { + # We already have a way of making the file. + # No need to find another one. + return; + } push @new_sources, $new_dest; print "$My_name: Ensuring rule for '$from_rule', to make '$new_dest'\n" if $diagnostics > -1; @@ -8009,6 +8233,15 @@ sub deps_list { if ($dvi_mode) {push @dest_exts, '.dvi';} if ($postscript_mode) {push @dest_exts, '.ps';} + my $deps_space = ' '; + if ($deps_escape eq 'unix' ) { $deps_space = '\ '; } + elsif ($deps_escape eq 'nmake' ) { $deps_space = '^ '; } + $Pescape = sub { + my $name = shift; + $name =~ s/ /$deps_space/g; + return $name; + }; + my %source = ( $texfile_name => 1 ); my @accessible_all = &rdb_accessible; rdb_for_some( @@ -8036,9 +8269,9 @@ sub deps_list { # deps file out-of-date are the same as those that make the real # target file (e.g., .pdf) out-of-date. So the GNU method seems # completely unnecessary. - fprint8 $fh, "${out_dir1}${root_filename}${ext} :"; + fprint8 $fh, &$Pescape(${out_dir1}.${root_filename}.${ext}), " :"; foreach (sort keys %source) { - fprint8 $fh, "\\\n $_"; + fprint8 $fh, "\\\n ", &$Pescape($_); } fprint8 $fh, "\n"; } @@ -8067,7 +8300,8 @@ sub rdb_show { if ( !defined($_) ) { $_='undef';} } print " [$rule]: '$$Pcmd_type' '$$Pext_cmd' '@int_cmd' $$Pno_history ", - "'$$Psource' '$$Pdest' '$$Pbase' $$Pout_of_date $$Pout_of_date_user\n"; }, + "'$$Psource' '$$Pdest' '$$Pbase' $$Pout_of_date $$Pout_of_date_user\n"; + }, sub{ print " '$file': $$Ptime $$Psize $$Pmd5 '", ($from_rules{$file} || ''), "'\n"; } ); if ($count_rules <= 0) { @@ -8554,26 +8788,33 @@ sub rdb_show_rule_errors { local @errors = (); local @warnings = (); rdb_for_actives( - sub{ - if ($$Plast_message ne '') { - if ($$Plast_result == 200) { - push @warnings, "$rule: $$Plast_message"; - } - else { - push @errors, "$rule: $$Plast_message"; - } - } - elsif ($$Plast_result == 1) { - push @errors, "$rule: failed to create output file"; - } - elsif ($$Plast_result == 2) { - push @errors, "$rule: gave an error"; - } - elsif ($$Prun_time == 0) { - # This can have innocuous causes. So don't report - } - } - ); + sub { + my $message_tail = ""; + if ( $$Plast_result_info eq 'PREV' ) { + $message_tail = " in previous round of document compilation."; + } + elsif ( $$Plast_result_info eq 'CACHE' ) { + $message_tail = " in previous invocation of $my_name."; + } + if ($$Plast_message ne '') { + if ($$Plast_result == 200) { + push @warnings, "$rule: $$Plast_message"; + } + else { + push @errors, "$rule: $$Plast_message"; + } + } + elsif ($$Plast_result == 1) { + push @errors, "$rule: failed to create output file$message_tail"; + } + elsif ($$Plast_result == 2) { + push @errors, "$rule: gave an error$message_tail"; + } + elsif ($$Prun_time == 0) { + # This can have innocuous causes. So don't report + } + } + ); if ($#warnings > -1) { show_array( "Collected warning summary (may duplicate other messages):". @warnings ); } @@ -8761,6 +9002,7 @@ sub rdb_run1 { $$Pchanged = 0; # No special changes in files $$Plast_result = 0; + $$Plast_result_info = 'CURR'; $$Plast_message = ''; my $latex_like = ($$Pcmd_type eq 'primary'); @@ -8983,6 +9225,7 @@ sub rdb_dummy_run0 { $$Prun_time = time(); $$Pchanged = 0; # No special changes in files $$Plast_result = 0; + $$Plast_result_info = 'CURR'; $$Plast_message = ''; $$Pout_of_date = $$Pout_of_date_user = 0; @@ -9343,7 +9586,7 @@ sub rdb_rerun_needed { } # Non-source-file-change reasons for rerun: - if ( ( ($$Prun_time == 0) || ( $$Plast_result == -1 ) ) + if ( ( ($$Prun_time == 0) || ( $$Plast_result =~ /^-1/ ) ) && ( $$Pcmd_type eq 'primary' ) ) { # Never run. Only use this test with primary, so we can get @@ -9736,7 +9979,7 @@ sub rdb_one_rule { $Psource, $Pdest, $Pbase, $Pout_of_date, $Pout_of_date_user, $Prun_time, $Pcheck_time, $Pchanged, - $Plast_result, $Plast_message, $PA_extra_gen ) + $Plast_result, $Plast_result_info, $Plast_message, $PA_extra_gen ) = Parray( $PArule_data ); &$rule_act1 if $rule_act1; @@ -9830,7 +10073,6 @@ sub rdb_remove_rule { #************************************************************ sub rdb_create_rule { - # ???!!! OUT OF DATE. PLACE HOLDER for what was test_kind # rdb_create_rule( rule, command_type, ext_cmd, int_cmd, DUMMY, # source, dest, base, # needs_making, run_time, check_time, set_file_not_exists, @@ -9842,7 +10084,12 @@ sub rdb_create_rule { # Makes rule. Update rule if it already exists. # Omitted arguments: replaced by 0, '', or [] as needed. # Rule is made active -# ==== Sets rule data ==== + # 5th argument DUMMY is argument that used to be used (test_kind), but + # is not used any more. But I keep it there to avoid having to change + # calls, which are not only in the latexmk code itself, but may be in + # latexmkrc files created by others. + +# ==== Set rule data from arguments ==== my ( $rule, $cmd_type, $ext_cmd, $PAint_cmd, $DUMMY, $source, $dest, $base, $needs_making, $run_time, $check_time, $set_file_not_exists, @@ -9859,6 +10106,7 @@ sub rdb_create_rule { if (! defined $_) { $_ = []; } } my $last_result = -1; + my $last_result_info = ''; my $no_history = ($run_time <= 0); my $active = 1; my $changed = 0; @@ -9884,7 +10132,7 @@ sub rdb_create_rule { [ [$cmd_type, $ext_cmd, $PAint_cmd, $no_history, $source, $dest, $base, $needs_making, 0, $run_time, $check_time, $changed, - $last_result, '', $PAextra_gen ], + $last_result, $last_result_info, '', $PAextra_gen ], {}, {}, {}, @@ -10540,12 +10788,14 @@ sub unlink_or_move { if (!-e) {next;} if (-d) { if (!rmdir) { - warn "$My_name: Cannot remove directory '$_'\n"; + warn "$My_name: Cannot remove directory '$_'\n", + " Error message = '$!'\n"; } } else { if (!unlink) { - warn "$My_name: Cannot remove file '$_'\n"; + warn "$My_name: Cannot remove file '$_'\n", + " Error message = '$!'\n"; } } } @@ -10553,7 +10803,8 @@ sub unlink_or_move { else { foreach (@_) { if (-e $_ && ! move $_, "$del_dir/$_" ) { - warn "$My_name: Cannot move '$_' to '$del_dir/$_'\n"; + warn "$My_name: Cannot move '$_' to '$del_dir/$_'\n", + " Error message = '$!'\n"; } } } @@ -10981,6 +11232,36 @@ sub split_search_path ################################# +sub get_filetime_offset { + # Usage: get_filetime_offset( prefix, [suffix] ) + # Measures offset between filetime in a directory and system time + # Makes a temporary file of a unique name, and deletes in. + # Filename is of form concatenation of prefix, an integer, suffix. + # Prefix is normally of form dir/ or dir/tmp. + # Default default suffix ".tmp". + my $prefix = $_[0]; + my $suffix = $_[1] || '.tmp'; + my $tmp_file_count = 0; + while (1==1) { + # Find a new temporary file, and make it. + $tmp_file_count++; + my $tmp_file = "${prefix}${tmp_file_count}${suffix}"; + if ( ! -e $tmp_file ) { + open( TMP, ">$tmp_file" ) + or die "$My_name.get_filetime_offset: In measuring filetime offset, couldn't write to\n", + " temporary file '$tmp_file'\n"; + my $time = time(); + close(TMP); + my $offset = get_mtime($tmp_file) - $time; + unlink $tmp_file; + return $offset; + } + } + die "$My_name.get_filetime_offset: BUG TO ARRIVE HERE\n"; +} + +################################# + sub tempfile1 { # Makes a temporary file of a unique name. I could use file::temp, # but it is not present in all versions of perl. |