summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/mkjobtexmf
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2008-03-31 18:51:12 +0000
committerKarl Berry <karl@freefriends.org>2008-03-31 18:51:12 +0000
commitd78671b834cc3819f5822260d1faa415ecec0867 (patch)
tree8466a115e442b88b22aff0863543c7fa3944faea /Master/texmf-dist/scripts/mkjobtexmf
parente91427cf3060fb9e6249be35ad7eb417d8d17901 (diff)
handle mkjobtexmf through our script mechanism instead of building
git-svn-id: svn://tug.org/texlive/trunk@7262 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts/mkjobtexmf')
-rw-r--r--Master/texmf-dist/scripts/mkjobtexmf/mkjobtexmf.pl799
1 files changed, 799 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/mkjobtexmf/mkjobtexmf.pl b/Master/texmf-dist/scripts/mkjobtexmf/mkjobtexmf.pl
new file mode 100644
index 00000000000..27f94a5a375
--- /dev/null
+++ b/Master/texmf-dist/scripts/mkjobtexmf/mkjobtexmf.pl
@@ -0,0 +1,799 @@
+#!/usr/bin/env perl
+#
+# ToDos/unsolved problems:
+# * collision of symbol links
+# * texmf.cnf (path settings, variables)
+# * aliases
+# * binaries, libraries
+#
+use strict;
+$^W=1;
+
+my $prj = 'mkjobtexmf';
+my $version = '0.5';
+my $date = '2007/09/06';
+my $author = 'Heiko Oberdiek';
+my $copyright = "Copyright 2007 $author";
+
+my $cmd_tex = 'pdflatex';
+my $cmd_kpsewhich = 'kpsewhich';
+my $cmd_texhash = 'texhash';
+my $cmd_strace = 'strace';
+my $ext_tex = '.tex';
+my $ext_recorder = '.fls';
+my $ext_strace = '.strace';
+my $ext_mkjobtexmf = '.mjt';
+my $jobname = '';
+my $texname = '';
+my $destdir = '';
+my @args = ();
+my @texopt = ();
+my $verbose = 0;
+my $output = 0;
+my $strace = 0;
+my $copy = 0;
+my $flat = 0;
+my $needs_texhash = 0;
+my @texmf;
+my %files;
+my %links;
+my %flat_ignore = (
+ 'ls-R' => '',
+ 'aliases' => '',
+);
+
+my $title = "\U$prj\E $date v$version, $copyright\n";
+
+print $title;
+
+sub die_error ($) {
+ my $msg = shift;
+ die "!!! Error: $msg!\n";
+}
+
+sub warning ($) {
+ my $msg = shift;
+ print "!!! Warning: $msg!\n";
+}
+
+sub verbose (@) {
+ my @msg = @_;
+ print "* @msg\n" if $verbose;
+}
+
+sub value ($) {
+ my $value = $_[0];
+ "[$value]";
+}
+
+sub die_usage {
+ my $msg = $_[0];
+ pod2usage(
+ -exitstatus => 2,
+ -msg => "\n==> $msg!\n");
+}
+
+use Getopt::Long;
+use Pod::Usage;
+
+my $man = 0;
+my $help = 0;
+
+GetOptions(
+ 'jobname=s' => \$jobname,
+ 'texname=s' => \$texname,
+ 'texopt=s' => \@texopt,
+ 'destdir=s' => \$destdir,
+ 'cmd-tex=s' => \$cmd_tex,
+ 'cmd-kpsewhich=s' => \$cmd_kpsewhich,
+ 'cmd-texhash=s' => \$cmd_texhash,
+ 'strace' => \$strace,
+ 'copy' => \$copy,
+ 'flat' => \$flat,
+ 'verbose' => \$verbose,
+ 'output' => \$output,
+ 'help|?' => \$help,
+ 'man' => \$man,
+) or die_usage('Unknown option');
+pod2usage(1) if $help;
+pod2usage(-exitstatus => 0, -verbose => 2) if $man;
+
+if (@ARGV > 0) {
+ $strace = 1;
+ $texname = '';
+ my @args = @ARGV;
+}
+$jobname or die_usage('Missing jobname');
+$texname = "$jobname$ext_tex" unless $texname;
+$destdir = "$jobname$ext_mkjobtexmf" unless $destdir;
+
+verbose "jobname: " . value $jobname;
+verbose "texname: " . value $texname if $texname;
+verbose "command: " . value "@args" if @args;
+verbose "destdir: " . value $destdir;
+
+if (!$copy) {
+ my $symlink_exists = eval { symlink('', ''); 1 };
+ if ($symlink_exists) {
+ verbose "symbolic linking: supported";
+ }
+ else {
+ $copy = 1;
+ verbose "symbolic linking: unsupported";
+ }
+}
+my $umask = umask;
+if (defined($umask)) {
+ verbose "umask: " . sprintf("%04o", $umask);
+}
+else {
+ $umask = 0;
+ verbose "umask: unsupported";
+}
+
+if ($copy) {
+ use File::Copy;
+}
+if ($flat) {
+ use File::Basename;
+}
+
+sub check_child_error () {
+ if ($? != 0) {
+ if ($? == -1) {
+ die_error "Failed to execute: $!";
+ }
+ elsif ($? & 127) {
+ die_error sprintf "Child died with signal %d, %s coredump",
+ ($? & 127), ($? & 128) ? 'with' : 'without';
+ }
+ else {
+ die_error sprintf "Child exited with value %d", $? >> 8;
+ }
+ }
+ verbose "child exit: ok";
+}
+
+sub run_generic (@) {
+ my @args = @_;
+ my $cmd = $_[0];
+ verbose "exec: " . value "@args";
+ print '>' x 79, "\n";
+ system $cmd @args;
+ print '<' x 79, "\n";
+ check_child_error;
+}
+
+sub run_tex {
+ if ($strace) {
+ my @run_args;
+ if (@args) {
+ @run_args = @args;
+ }
+ else {
+ @run_args = (
+ $cmd_tex,
+ '-interaction=nonstopmode',
+ @texopt,
+ $texname
+ );
+ }
+ run_generic(
+ $cmd_strace,
+ '-f',
+ '-e',
+ 'trace=open,access', # trace=file
+ '-o',
+ "$jobname$ext_strace",
+ @run_args
+ )
+ }
+ else {
+ run_generic(
+ $cmd_tex,
+ '-recorder',
+ "-jobname=$jobname",
+ '-interaction=nonstopmode',
+ @texopt,
+ $texname
+ );
+ }
+}
+
+sub run_texhash {
+ return if $flat;
+ if ($needs_texhash) {
+ run_generic(
+ $cmd_texhash,
+ "$destdir/texmf"
+ );
+ }
+ else {
+ verbose("texhash run skipped, no files added");
+ }
+}
+
+use Cwd 'abs_path', 'getcwd';
+
+sub get_texmf_trees () {
+ return if $flat;
+ my $cmdline = "$cmd_kpsewhich -expand-path='\$TEXMF'";
+ verbose "exec: " . value($cmdline);
+ my $str = `$cmdline`;
+ check_child_error;
+ chomp $str;
+ @texmf = split ':', $str;
+ my %texmf;
+ foreach my $texmf (@texmf) {
+ $texmf{$texmf} = '';
+ $texmf{abs_path($texmf)} = '';
+ }
+ @texmf = sort keys %texmf;
+ if ($verbose) {
+ if (@texmf) {
+ map { verbose 'texmf: ' . value($_) } @texmf;
+ }
+ else {
+ verbose 'texmf: none';
+ }
+ }
+}
+
+sub analyze_recorder {
+ my $pwd = getcwd;
+ verbose "pwd: " . value($pwd);
+
+ my $file_rec = $jobname . ($strace ? $ext_strace : $ext_recorder);
+ verbose 'File with recorded file names: ' . value($file_rec);
+ open(IN, '<', $file_rec)
+ or die_error "Cannot open `$file_rec'";
+ if ($strace) {
+ while (<IN>) {
+ chomp;
+ next if /\)\s+= -\d/; # -1 ENOENT, ...
+ next if /\WO_DIRECTORY\W/; # skip directories
+ my $type = 'INPUT';
+ if ($output) {
+ $type = 'OUTPUT' if /\WO_WRONLY\W/;
+ }
+ else {
+ next if /\WO_WRONLY\W/;
+ }
+ /^\d+\s+\w+\(\"([^"]+)\",/ or warning "Unknown entry `$_'";
+ my $file = $1;
+ $files{$file} = '';
+ }
+ }
+ else {
+ while (<IN>) {
+ chomp;
+ next if /^PWD /;
+ next if not $output and /^OUTPUT /;
+ /^(INPUT|OUTPUT) (.*)$/ or warning "Unknown entry `$_'";
+ my $type = $1;
+ my $file = $2;
+ $files{$file} = '';
+ }
+ }
+ close(IN);
+}
+
+sub map_files {
+ if ($flat) {
+ map_files_flat();
+ }
+ else {
+ map_files_texmf();
+ }
+}
+
+sub map_files_flat {
+ my %abs_files;
+ my %names;
+ my %clashes;
+
+ foreach my $file (keys %files) {
+ $abs_files{abs_path($file)} = '';
+ }
+
+ foreach my $file (keys %abs_files) {
+ my $name = basename($file);
+ next if exists $flat_ignore{$name};
+ if (defined($names{$name})) {
+ push @{$names{$name}}, $file;
+ $clashes{$name} = '';
+ }
+ else {
+ my @a = ($file);
+ $names{$name} = \@a;
+ }
+ }
+
+ foreach my $name (sort keys %clashes) {
+ print "* file name clash for " . value($name) . "\n";
+ my @a = @{$names{$name}};
+ foreach my $file (@a) {
+ print " " . value($file) . "\n";
+ }
+ }
+
+ foreach my $name (sort keys %names) {
+ my $file = @{$names{$name}}[0];
+ my $clash = $clashes{$name} ? ' (clash)' : '';
+ verbose value($name) . ' => ' . value($file) . $clash;
+ $links{$name} = $file;
+ }
+}
+
+sub map_files_texmf {
+ my @failed;
+
+ foreach my $file (sort keys %files) {
+ verbose "file: " . value($file);
+
+ my $abs_file = abs_path($file);
+
+ my $found = '';
+ foreach (@texmf) {
+ my $texmf = "$_/";
+ my $len = length($texmf);
+ my $str = substr $file, 0, $len;
+ if ($texmf eq $str) {
+ $found = 'texmf/' . substr $file, $len;
+ if ($found =~ /(^|\/)\.\.\//) {
+ $found = '';
+ }
+ }
+ last if $found;
+ my $str = substr $abs_file, 0, $len;
+ if ($texmf eq $str) {
+ $found = 'texmf/' . substr $abs_file, $len;
+ last;
+ }
+ }
+ if (not($found)) {
+ if ($file =~ /(^|\/)\.\.\// or $file =~ /^\//) {
+ push @failed, $file;
+ }
+ else {
+ $found = $file;
+ }
+ }
+ if ($found) {
+ $links{$found} = abs_path($file);
+ }
+ }
+
+ if ($verbose) {
+ foreach (sort keys %links) {
+ verbose value($_) . ' => ' . value($links{$_});
+ }
+ }
+
+ foreach (@failed) {
+ print "!!! Failed: " . value($_) . "\n";
+ }
+}
+
+sub make_dirs ($) {
+ my $path = shift;
+ my @elems = split /\/+/, $path;
+ if (@elems <= 1) {
+ return;
+ }
+ pop @elems;
+ my $dir = '';
+ foreach my $elem (@elems) {
+ $dir .= '/' if $dir;
+ $dir .= $elem;
+ next if -d $dir;
+ verbose 'mkdir: ' . value($dir);
+ mkdir $dir or die_error "Cannot make directory `$dir'";
+ }
+}
+
+sub make_links {
+
+ foreach my $key (sort keys %links) {
+ my $source = $links{$key};
+ my $dest = "$destdir/$key";
+ make_dirs $dest;
+ if (-e $dest) {
+ my $type = '';
+ if (-l $dest) {
+ $type .= 'link';
+ }
+ elsif (-f $dest) {
+ $type = 'file';
+ }
+ elsif (-d $dest) {
+ $type = 'directory';
+ }
+ elsif (-b $dest) {
+ $type = 'block device';
+ }
+ elsif (-c $dest) {
+ $type = 'character device';
+ }
+ elsif (-p $dest) {
+ $type = 'pipe';
+ }
+ elsif (-S $dest) {
+ $type = 'socket';
+ }
+ elsif (-t $dest) {
+ $type = 'tty';
+ }
+ $type = " ($type)" if $type;
+ verbose "destination$type exists: " . value($dest);
+ next;
+ }
+ $needs_texhash = 1;
+ do_link_copy($source, $dest);
+ }
+}
+
+sub do_link_copy {
+ my $source = shift;
+ my $dest = shift;
+ my $success = 0;
+
+ if ($copy) {
+ if (copy($source, $dest) == 1) {
+ $success = 1;
+ my ($source_mode, $source_atime, $source_mtime)
+ = (stat($source))[2, 8, 9];
+ my ($dest_mode, $dest_atime, $dest_mtime)
+ = (stat($dest))[2, 8, 9];
+ # preserve executable permissions if necessary
+ my $new_dest_mode = $dest_mode
+ | (($source_mode & 0111) & ~$umask);
+ if ($new_dest_mode != $dest_mode) {
+ if (chmod($new_dest_mode, $dest) < 1) {
+ print "!!! Setting executive mode failed: "
+ . value($dest) . "\n";
+ }
+ }
+ # preserve file times
+ if ($source_atime != $dest_atime
+ || $source_mtime != $dest_mtime) {
+ if (utime($source_atime, $source_mtime, $dest) < 1) {
+ print "!!! Setting file times failed: "
+ . value($dest) . "\n";
+ }
+ }
+ }
+ }
+ else {
+ if (symlink($source, $dest) == 1) {
+ $success = 1;
+ }
+ }
+ if ($success == 0) {
+ my $method = $copy ? 'Copying' : 'Symbolic linking';
+ print "!!! $method failed:\n "
+ . value($dest) . ' => ' . value($source) . "\n";
+ }
+}
+
+run_tex;
+get_texmf_trees;
+analyze_recorder;
+map_files;
+make_links;
+run_texhash;
+
+1;
+
+__DATA__
+
+=head1 NAME
+
+mkjobtexmf -- Generate a texmf tree for a particular job
+
+=head1 SYNOPSIS
+
+The progam B<mkjobtexmf> runs a program and tries to
+find the used file names. Two methods are available,
+option C<-recorder> of TeX (Web2C) or the program B<strace>.
+
+Then it generates a directory with a texmf tree. It checks
+the found files and tries sort them in this texmf tree.
+
+It can be used for archiving purposes or to speed up
+following TeX runs.
+
+ mkjobtexmf [options]
+
+This runs TeX that can be configured by options.
+Both methods for getting the used file names are available.
+
+ mkjobtexmf [options] -- <cmd> [args]
+
+The latter form runs program I<cmd> with arguments I<args>
+instead of TeX. As method only program B<strace> is available.
+
+Options:
+
+ --jobname <name> Name of the job (mandatory).
+ Usually this is the TeX file
+ without extension
+ --texname <file> Input file for TeX. Default is the
+ job name with extension '.tex'
+ --texopt <option> Option for TeX run
+ --destdir <directory> Destination directory,
+ default is `<jobname>.mjt'
+ --output Add also output files
+ --strace Use strace instead of TeX's
+ option -recorder
+ --copy Copy files instead of creating
+ symbol links
+ --flat Junk paths, do not make directories
+ inside the destination directory
+ --cmd-tex <cmd> Command for the TeX compiler
+ --cmd-kpsewhich <cmd> Command for kpsewhich
+ --cmd-texhash <cmd> Command for texhash
+ --cmd-strace <cmd> Command for strace
+ --verbose Verbose output
+ --help Brief help message
+ --man Full documentation
+
+=head1 DESCRIPTION
+
+=head2 Running the program
+
+First B<mkjobtexmf> runs a program, usually TeX. The TeX compiler
+is configured by option B<--cmd-tex>. Option B<--texname> can
+be used, if the file name extension differs from F<.tex>:
+
+ mkjobtexmf --jobname foo --texname foo.ltx
+
+Even more complicate cases are possible:
+
+ mkjobtexmf --jobname foo --texname '\def\abc{...}\input{foo}'
+
+If another program than TeX should be used (dvips, ...),
+then this program can be given after C<-->:
+
+ mkjobtexmf --jobname foo -- dvips foo
+
+=head2 File recording
+
+Two methods are available to get the used file names:
+
+=over
+
+=item Recorder of TeX
+
+Some TeX distributions (e.g. Web2C) support the option B<-recorder>
+for its TeX compilers. Then the TeX compiler generates a file with
+extension F<.fls> that records the used input and output files.
+
+=item Program strace
+
+This program traces system calls and signals. It is used here
+to log the used files.
+
+=back
+
+=head2 Analyze and link/copy found files
+
+The result directory F<I<jobname>.mjt> is generated. Inside the
+result TEXMF tree is created. Each found file is compared against
+a list of paths of TEXMF trees. If a match is found, the file is
+linked/copied into the TEXMF tree. The list of paths is generated by
+program B<kpsewhich>.
+
+If the file cannot be mapped to a TEXMF tree and the file is
+a relative file name, then it is directly linked/copied into the
+result directory F<I<jobname>.mjt>. Absolute file names
+are not supported and neither paths with links to parent directories.
+
+Symbolic links are created by default. The files are copied
+if option --copy is given or symbolic linking is not available.
+
+=head1 OPTIONS
+
+=over
+
+=item B<--jobname>=I<jobname>
+
+It is the name of the job. `<jobname>.tex' serves as default for
+the TeX file and <jobname> is used for naming various directories
+and files. See section L</FILES>.
+
+=item B<--texname>=I<name>
+
+The name of the TeX input file, if it differs from <jobname>.tex.
+
+=item B<--texopt>=I<opt>
+
+Additional option for the TeX compiler, examples are --ini or
+--shell-escape. This option can be given more than once.
+
+=item B<--destdir>=I<directory>
+
+Specifies the name of the destination directory where the result
+is collected. As default a directory is generated in the current
+directory with the job name and extension `.mjt'.
+
+=item B<--output>
+
+Also add output files.
+
+=item B<--strace>
+
+Use method with program B<strace>, see L<"DESCRIPTION">.
+
+=item B<--copy>
+
+Files are copied instead of creating symbolic links.
+
+=item B<--flat>
+
+Files are linked or copied without path elements.
+The destination directory will contain a flat list of
+files or links without directory.
+
+The files `ls-R' and `aliases' are ignored.
+
+=item B<--cmd-tex>=I<cmd>
+
+Command for the TeX compiler. Default is pdflatex.
+
+=item B<--cmd-kpsewhich>=I<cmd>
+
+Command for kpsewhich.
+
+=item B<--cmd-texhash>=I<cmd>
+
+Command for updating the file name database of the generated
+texmf tree. Default is texmf.
+
+=item B<--cmd-strace>=I<cmd>
+
+Command for strace.
+
+=item B<--verbose>
+
+Verbose messages.
+
+=item B<--help>
+
+Display help screen.
+
+=back
+
+=head1 EXAMPLES
+
+TeX file F<test.tex> using TeX's recorder method:
+
+ mkjobtexmf --jobname test
+
+TeX file F<test.tex> using LaTeX:
+
+ mkjobtexmf --jobname test --cmd-tex latex
+
+Format generation:
+
+ mkjobtexmf --jobname test --texopt -ini --texname pdflatex.ini
+
+Example, how the new texmf tree (Linux/bash) can be used:
+
+ TEXMF=!!test.mjt/texmf pdflatex test
+
+Example for generating a zip archive (Linux/bash):
+
+ (cd test.mjt && zip -9r ../test .)
+
+Example for generating a tar archive:
+
+ tar cjhvf test.tar.bz2 -C test.mjt .
+
+=head1 UNSOLVED ISSUES, CAVEATS, TODOS
+
+=over
+
+=item Experimental software
+
+Options, defaults, how the program works might change in
+future versions.
+
+=item F<texmf.cnf>
+
+Currently the method with B<strace> records this files.
+TeX's recorder does not. Useful are F<texmf.cnf> files for
+variable settings. Because we have just one TEXMF tree,
+the path sections should probably rewritten.
+
+=item Settings in environment variables
+
+They are not stored at all.
+
+=item Collisions
+
+The program uses one destination directory and at most
+one TEXMF tree for the result. However, the source files
+can come from different directories and TEXMF trees.
+Therefore name collisions are possible.
+
+The program follows the strategy not to delete files
+in the destination directory. That allows to collect files
+from differnt runs. Thus collisions are resolved
+in the manner that the first entry that is made in
+the destination directory wins.
+
+=item Configuration file
+
+It would save the user from retyping the same options again and again.
+
+=item Uncomplete recording
+
+Bugs in TeX's file recording might result in incomplete
+file recording (e.g. pdfTeX 1.40.3 does not record .pfb and
+.pk files).
+
+=item ...
+
+=back
+
+=head1 FILES
+
+=over
+
+=item F<E<lt>jobnameE<gt>.mjt/>
+
+Directory where the resulting texmf tree and symbol links
+are stored. It can be changed by option --destdir.
+
+=item F<E<lt>jobnameE<gt>.fls>
+
+Name of TeX's recorder file.
+
+=item F<E<lt>jobnameE<gt>.strace>
+
+Log file where the result of B<strace> is stored.
+
+=back
+
+=head1 AUTHOR
+
+Heiko Oberdiek, email: oberdiek at uni-freiburg.de
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 by Heiko Oberdiek.
+
+This library is free software; you may redistribute it and/or
+modify it under the same terms as Perl itself
+(Perl Artistic License/GNU General Public License, version 2).
+
+=head1 HISTORY
+
+=over
+
+=item 2007/04/16 v0.1
+
+* First experimental version.
+
+=item 2007/05/09 v0.2
+
+* Typo in option name fixed.
+
+=item 2007/09/03 v0.3
+
+* New options: --copy, --flat, --destdir
+
+=item 2007/09/04 v0.4
+
+* Bug fix in map_files_texmf.
+
+=item 2007/09/06 v0.5
+
+* Support for `configure' added.
+ (Thanks to Norbert Preining for writing a first version of
+ the configure stuff.)
+
+=back
+
+=cut