diff options
Diffstat (limited to 'nixpkgs/pkgs/tools/typesetting/tex/nix')
9 files changed, 595 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/tools/typesetting/tex/nix/animatedot.sh b/nixpkgs/pkgs/tools/typesetting/tex/nix/animatedot.sh new file mode 100644 index 000000000000..f038b83ff7ad --- /dev/null +++ b/nixpkgs/pkgs/tools/typesetting/tex/nix/animatedot.sh @@ -0,0 +1,9 @@ +source $stdenv/setup + +mkdir -p $out + +for ((i = 1; i <= $nrFrames; i++)); do + echo "producing frame $i..."; + targetName=$out/$(basename $(stripHash $dotGraph) .dot)-f-$i.dot + cpp -DFRAME=$i < $dotGraph > $targetName +done diff --git a/nixpkgs/pkgs/tools/typesetting/tex/nix/copy-includes.pl b/nixpkgs/pkgs/tools/typesetting/tex/nix/copy-includes.pl new file mode 100644 index 000000000000..2cec62fc7cb6 --- /dev/null +++ b/nixpkgs/pkgs/tools/typesetting/tex/nix/copy-includes.pl @@ -0,0 +1,43 @@ +use strict; +use File::Basename; + +sub createDirs; +sub createDirs { + my $path = shift; + return unless $path =~ /^(.*)\/([^\/]*)$/; + my $dir = $1; + return if -d $dir; + return if -e $dir; + createDirs $dir; + mkdir $dir or die "cannot create directory `$dir'"; +} + +my $maxParents = 0; +for (my $n = 0; $n < @ARGV; $n += 2) { + my $fullPath = $ARGV[$n]; + my $relPath = $ARGV[$n + 1]; + my $parents = 0; + foreach my $comp (split /\//, $relPath) { + $parents++ if ($comp eq "..") + } + $maxParents = $parents if $parents > $maxParents; +} + +my $startDir = "./"; +for (my $n = 0; $n < $maxParents; $n++) { + $startDir .= "dotdot/"; + mkdir "$startDir" or die "cannot create directory `$startDir': $!"; +} + +chdir $startDir or die; + +for (my $n = 0; $n < @ARGV; $n += 2) { + my $fullPath = $ARGV[$n]; + my $relPath = $ARGV[$n + 1]; + + createDirs $relPath; + + symlink $fullPath, $relPath or die "cannot create symlink `$relPath'"; +} + +print "$startDir\n"; diff --git a/nixpkgs/pkgs/tools/typesetting/tex/nix/default.nix b/nixpkgs/pkgs/tools/typesetting/tex/nix/default.nix new file mode 100644 index 000000000000..4ee45bf4bc8f --- /dev/null +++ b/nixpkgs/pkgs/tools/typesetting/tex/nix/default.nix @@ -0,0 +1,249 @@ +pkgs: + +rec { + + + runLaTeX = + { rootFile + , generatePDF ? true # generate PDF, not DVI + , generatePS ? false # generate PS in addition to DVI + , extraFiles ? [] + , compressBlanksInIndex ? true + , packages ? [] + , texPackages ? {} + , copySources ? false + }: + + assert generatePDF -> !generatePS; + + let + tex = pkgs.texlive.combine + # always include basic stuff you need for LaTeX + ({inherit (pkgs.texlive) scheme-basic;} // texPackages); + in + + pkgs.stdenv.mkDerivation { + name = "doc"; + + builder = ./run-latex.sh; + copyIncludes = ./copy-includes.pl; + + inherit rootFile generatePDF generatePS extraFiles + compressBlanksInIndex copySources; + + includes = map (x: [x.key (baseNameOf (toString x.key))]) + (findLaTeXIncludes {inherit rootFile;}); + + buildInputs = [ tex pkgs.perl ] ++ packages; + }; + + + # Returns the closure of the "dependencies" of a LaTeX source file. + # Dependencies are other LaTeX source files (e.g. included using + # \input{}), images (e.g. \includegraphics{}), bibliographies, and + # so on. + findLaTeXIncludes = + { rootFile + }: + + builtins.genericClosure { + startSet = [{key = rootFile;}]; + + operator = + {key, ...}: + + let + + # `find-includes.pl' returns the dependencies of the current + # source file (`key') as a list, e.g. [{type = "tex"; name = + # "introduction.tex";} {type = "img"; name = "example"}]. + # The type denotes the kind of dependency, which determines + # what extensions we use to look for it. + deps = import (pkgs.runCommand "latex-includes" + { rootFile = baseNameOf (toString rootFile); src = key; } + "${pkgs.perl}/bin/perl ${./find-includes.pl}"); + + # Look for the dependencies of `key', trying various + # extensions determined by the type of each dependency. + # TODO: support a search path. + foundDeps = dep: xs: + let + exts = + if dep.type == "img" then [".pdf" ".png" ".ps" ".jpg"] + else if dep.type == "tex" then [".tex" ""] + else [""]; + fn = pkgs.lib.findFirst (fn: builtins.pathExists fn) null + (map (ext: dirOf key + ("/" + dep.name + ext)) exts); + in if fn != null then [{key = fn;}] ++ xs + else xs; + + in pkgs.lib.fold foundDeps [] deps; + }; + + + findLhs2TeXIncludes = + { rootFile + }: + + builtins.genericClosure { + startSet = [{key = rootFile;}]; + + operator = + {key, ...}: + + let + + deps = import (pkgs.runCommand "lhs2tex-includes" + { src = key; } + "${pkgs.stdenv.bash}/bin/bash ${./find-lhs2tex-includes.sh}"); + + in pkgs.lib.concatMap (x: if builtins.pathExists x then [{key = x;}] else []) + (map (x: dirOf key + ("/" + x)) deps); + }; + + dot2pdf = + { dotGraph + }: + + pkgs.stdenv.mkDerivation { + name = "pdf"; + builder = ./dot2pdf.sh; + inherit dotGraph fontsConf; + buildInputs = [ + pkgs.perl pkgs.graphviz + ]; + }; + + + dot2ps = + { dotGraph + }: + + pkgs.stdenv.mkDerivation { + name = "ps"; + builder = ./dot2ps.sh; + inherit dotGraph; + buildInputs = [ + pkgs.perl pkgs.graphviz pkgs.ghostscript + ]; + }; + + lhs2tex = + { source, flags ? null } : + pkgs.stdenv.mkDerivation { + name = "tex"; + builder = ./lhs2tex.sh; + inherit source flags; + buildInputs = [ pkgs.lhs2tex pkgs.perl ]; + copyIncludes = ./copy-includes.pl; + includes = map (x: [x.key (baseNameOf (toString x.key))]) + (findLhs2TeXIncludes {rootFile = source;}); + }; + + animateDot = dotGraph: nrFrames: pkgs.stdenv.mkDerivation { + name = "dot-frames"; + builder = ./animatedot.sh; + inherit dotGraph nrFrames; + }; + + + # Wrap a piece of TeX code in a document. Useful when generating + # inline images from TeX code. + wrapSimpleTeX = + { preamble ? null + , body + , name ? baseNameOf (toString body) + }: + + pkgs.stdenv.mkDerivation { + inherit name preamble body; + buildCommand = '' + touch $out + echo '\documentclass{article}' >> $out + echo '\pagestyle{empty}' >> $out + if test -n "$preamble"; then cat $preamble >> $out; fi + echo '\begin{document}' >> $out + cat $body >> $out + echo '\end{document}' >> $out + ''; + }; + + + # Convert a Postscript file to a PNG image, trimming it so that + # there is no unnecessary surrounding whitespace. + postscriptToPNG = + { postscript + }: + + pkgs.stdenv.mkDerivation { + name = "png"; + inherit postscript; + + buildInputs = [pkgs.imagemagick pkgs.ghostscript]; + + buildCommand = '' + if test -d $postscript; then + input=$(ls $postscript/*.ps) + else + input=$(stripHash $postscript) + ln -s $postscript $input + fi + + mkdir -p $out + convert -units PixelsPerInch \ + -density 600 \ + -trim \ + -matte \ + -transparent '#ffffff' \ + -type PaletteMatte \ + +repage \ + $input \ + "$out/$(basename $input .ps).png" + ''; # */ + }; + + + # Convert a piece of TeX code to a PNG image. + simpleTeXToPNG = + { preamble ? null + , body + , packages ? [] + }: + + postscriptToPNG { + postscript = runLaTeX { + rootFile = wrapSimpleTeX { + inherit body preamble; + }; + inherit packages; + generatePDF = false; + generatePS = true; + }; + }; + + + # Convert a piece of TeX code to a PDF. + simpleTeXToPDF = + { preamble ? null + , body + , packages ? [] + }: + + runLaTeX { + rootFile = wrapSimpleTeX { + inherit body preamble; + }; + inherit packages; + }; + + + # Some tools (like dot) need a fontconfig configuration file. + # This should be extended to allow the called to add additional + # fonts. + fontsConf = pkgs.makeFontsConf { + fontDirectories = [ + "${pkgs.ghostscript}/share/ghostscript/fonts" + ]; + }; + +} diff --git a/nixpkgs/pkgs/tools/typesetting/tex/nix/dot2pdf.sh b/nixpkgs/pkgs/tools/typesetting/tex/nix/dot2pdf.sh new file mode 100644 index 000000000000..71cf601dfac0 --- /dev/null +++ b/nixpkgs/pkgs/tools/typesetting/tex/nix/dot2pdf.sh @@ -0,0 +1,20 @@ +source $stdenv/setup + +mkdir -p $out + +dot2pdf() { + sourceFile=$1 + targetName=$out/$(basename $(stripHash $sourceFile) .dot).pdf + echo "converting $sourceFile to $targetName..." + export FONTCONFIG_FILE=$fontsConf + dot -Tpdf $sourceFile > $targetName +} + +for i in $dotGraph; do + if test -d $i; then + for j in $i/*; do dot2pdf $j; done + else + dot2pdf $i + fi +done + diff --git a/nixpkgs/pkgs/tools/typesetting/tex/nix/dot2ps.sh b/nixpkgs/pkgs/tools/typesetting/tex/nix/dot2ps.sh new file mode 100644 index 000000000000..dd8de4a23dbc --- /dev/null +++ b/nixpkgs/pkgs/tools/typesetting/tex/nix/dot2ps.sh @@ -0,0 +1,19 @@ +source $stdenv/setup + +mkdir -p $out + +dot2ps() { + sourceFile=$1 + targetName=$out/$(basename $(stripHash $sourceFile) .dot).ps + echo "converting $sourceFile to $targetName..." + dot -Tps $sourceFile > $targetName +} + +for i in $dotGraph; do + if test -d $i; then + for j in $i/*; do dot2ps $j; done + else + dot2ps $i + fi +done + diff --git a/nixpkgs/pkgs/tools/typesetting/tex/nix/find-includes.pl b/nixpkgs/pkgs/tools/typesetting/tex/nix/find-includes.pl new file mode 100644 index 000000000000..41675e939f60 --- /dev/null +++ b/nixpkgs/pkgs/tools/typesetting/tex/nix/find-includes.pl @@ -0,0 +1,69 @@ +use strict; +use File::Basename; + +my $src = $ENV{"src"}; +my $out = $ENV{"out"}; +my $path = $ENV{"searchRelativeTo"}; + +open OUT, ">$out" or die; +print OUT "[\n"; + +open FILE, "< $src" or die; + +sub addName { + my ($type, $name) = @_; + print OUT "{ type = \"$type\"; name = \"$name\"; }\n"; +} + +while (<FILE>) { + if (/\\input\{(.*)\}/) { + my $fn2 = $1; + die "absolute path! $fn2" if substr($fn2, 0, 1) eq "/"; + addName "tex", "$fn2"; + } elsif (/\\input (.*)$/) { + my $fn2 = $1; + die "absolute path! $fn2" if substr($fn2, 0, 1) eq "/"; + addName "tex", "$fn2"; + } elsif (/\\RequirePackage(\[.*\])?\{(.*)\}/) { + my $fn2 = $2; + die "absolute path! $fn2" if substr($fn2, 0, 1) eq "/"; + addName "misc", "$fn2.sty"; + } elsif (/\\usepackage(\[.*\])?\{(.*)\}/) { + my $fn2 = $2; + die "absolute path! $fn2" if substr($fn2, 0, 1) eq "/"; + addName "misc", "$fn2.sty"; + } elsif (/\\documentclass(\[.*\])?\{(.*)\}/) { + my $fn2 = $2; + die "absolute path! $fn2" if substr($fn2, 0, 1) eq "/"; + addName "misc", "$fn2.cls"; + } elsif (/\\bibliographystyle\{(.*)\}/) { + my $fn2 = $1; + die "absolute path! $fn2" if substr($fn2, 0, 1) eq "/"; + addName "misc", "$fn2.bst"; + } elsif (/\\bibliography\{(.*)\}/) { + foreach my $bib (split /,/, $1) { + $bib =~ s/^\s+//; # remove leading / trailing whitespace + $bib =~ s/\s+$//; + addName "misc", "$bib.bib"; + addName "misc", (basename($ENV{"rootFile"}, ".tex", ".ltx") . ".bbl"); + } + } elsif (/\\includegraphics(\[.*\])?\{(.*)\}/) { + my $fn2 = $2; + die "absolute path! $fn2" if substr($fn2, 0, 1) eq "/"; + addName "img", "$fn2"; + } elsif (/\\pgfdeclareimage(\[.*\])?\{.*\}\{(.*)\}/) { + my $fn2 = $2; + die "absolute path! $fn2" if substr($fn2, 0, 1) eq "/"; + addName "img", "$fn2"; + } elsif (/\\pgfimage(\[.*\])?\{(.*)\}/) { + my $fn2 = $2; + die "absolute path! $fn2" if substr($fn2, 0, 1) eq "/"; + addName "img", "$fn2"; + } + # !!! also support \usepackage +} + +close FILE; + +print OUT "]\n"; +close OUT; diff --git a/nixpkgs/pkgs/tools/typesetting/tex/nix/find-lhs2tex-includes.sh b/nixpkgs/pkgs/tools/typesetting/tex/nix/find-lhs2tex-includes.sh new file mode 100644 index 000000000000..2dc12aa517a7 --- /dev/null +++ b/nixpkgs/pkgs/tools/typesetting/tex/nix/find-lhs2tex-includes.sh @@ -0,0 +1,3 @@ +echo '[' > $out +grep '^%include ' $src | cut -d ' ' -f 2 | sed 's/^\(.*\)$/"\1"/' >> $out +echo ']' >> $out diff --git a/nixpkgs/pkgs/tools/typesetting/tex/nix/lhs2tex.sh b/nixpkgs/pkgs/tools/typesetting/tex/nix/lhs2tex.sh new file mode 100644 index 000000000000..bfef3df6b0e1 --- /dev/null +++ b/nixpkgs/pkgs/tools/typesetting/tex/nix/lhs2tex.sh @@ -0,0 +1,19 @@ +source $stdenv/setup + +mkdir -p $out + +mkdir root +cd root + +startDir=$(perl $copyIncludes $includes) +cd $startDir + +lhstex() { + sourceFile=$1 + targetName=$out/$(basename $(stripHash $sourceFile) .lhs).tex + echo "converting $sourceFile to $targetName..." + lhs2TeX -o "$targetName" $flags "$sourceFile" +} + +lhstex $source + diff --git a/nixpkgs/pkgs/tools/typesetting/tex/nix/run-latex.sh b/nixpkgs/pkgs/tools/typesetting/tex/nix/run-latex.sh new file mode 100644 index 000000000000..3f8a16580ea5 --- /dev/null +++ b/nixpkgs/pkgs/tools/typesetting/tex/nix/run-latex.sh @@ -0,0 +1,164 @@ +source $stdenv/setup + +mkdir -p $out + +export VARTEXFONTS=$TMPDIR/texfonts +export TEXMFCNF=$TMPDIR: +echo 'max_print_line = 8192' >> $TMPDIR/texmf.cnf + +mkdir root +cd root + +startDir=$(perl $copyIncludes $includes) +cd $startDir + +for i in $extraFiles; do + if test -d $i; then + ln -s $i/* . + else + ln -s $i $(stripHash $i) + fi +done + +rootName=$(basename $(stripHash "$rootFile")) + +rootNameBase=$(echo "$rootName" | sed 's/\..*//') + +if test -n "$generatePDF"; then + latex=pdflatex +else + latex=latex +fi + +latexFlags="-file-line-error" +tmpFile=$out/log + +showError() { + echo + echo "LATEX ERROR (LAST LOG LINES SHOWN):" + tail -n 20 $tmpFile + bzip2 $tmpFile + exit 1 +} + +pass=0 + +runLaTeX() { + ((pass=pass+1)) + echo "PASS $pass..." + if ! $latex $latexFlags $rootName >$tmpFile 2>&1; then showError; fi + runNeeded= + if fgrep -q \ + -e "LaTeX Warning: Label(s) may have changed." \ + -e "Rerun to get citations correct." \ + -e "Please rerun LaTeX." \ + "$tmpFile"; then + runNeeded=1 + fi + echo +} + +echo + + +if test -n "$copySources"; then + cp -prd $TMPDIR/root $out/tex-srcs +fi + + +runLaTeX + +for auxFile in $(find . -name "*.aux"); do + # Run bibtex to process all bibliographies. There may be several + # when we're using the multibib package. + if grep -q '\\citation' $auxFile; then + auxBase=$(basename $auxFile .aux) + if [ -e $auxBase.bbl ]; then + echo "SKIPPING BIBTEX ON $auxFile!" + else + echo "RUNNING BIBTEX ON $auxFile..." + bibtex --terse $auxBase + cp $auxBase.bbl $out + runNeeded=1 + fi + echo + fi + + # "\pgfsyspdfmark" in the aux file seems to indicate that PGF/TikZ + # requires a second run (e.g. to resolve arrows between pictures). + if grep -q pgfsyspdfmark $auxFile; then + runNeeded=1 + fi +done + +if test "$runNeeded"; then + runLaTeX +fi + + +if test -f $rootNameBase.idx; then + echo "MAKING INDEX..." + if test -n "$compressBlanksInIndex"; then + makeindexFlags="$makeindexFlags -c" + fi + makeindex $makeindexFlags $rootNameBase.idx + runNeeded=1 + echo +fi + +# We check that pass is less than 2 to catch situations where the document is +# simple enough (no bibtex, etc.) so that it would otherwise require only one +# pass but also contains a ToC. +# In essence this check ensures that we do at least two passes on all documents. +if test "$runNeeded" = 1 -o "$pass" -lt 2 ; then + runLaTeX +fi + +if test "$runNeeded"; then + runLaTeX +fi + + +if test "$runNeeded"; then + echo "Hm, still not done :-(" + echo +fi + + +if test -n "$generatePDF"; then + cp $rootNameBase.pdf $out +else + cp $rootNameBase.dvi $out + if test -n "$generatePS"; then + echo "CONVERTING TO POSTSCRIPT..." + dvips $rootNameBase.dvi -o $out/$rootNameBase.ps + echo + fi +fi + + +echo "WARNINGS:" +cat $tmpFile | grep "Warning:" | grep -v "Citation.*undefined" || true + +echo +echo "OVERFULL/UNDERFULL:" +cat $tmpFile | egrep "Overfull|Underfull" || true + +echo +echo "UNDEFINED REFERENCES:" +cat $tmpFile | grep "Reference.*undefined" || true + +echo +echo "UNDEFINED CITATIONS:" +cat $tmpFile | grep "Citation.*undefined" || true + +echo +echo "STATS:" +printf "%5d overfull/underfull h/vboxes\n" $(cat $tmpFile | egrep -c "Overfull|Underfull" || true) +printf "%5d undefined references\n" $(cat $tmpFile | grep -c "Reference.*undefined" || true) +printf "%5d undefined citations\n" $(cat $tmpFile | grep -c "Citation.*undefined" || true) +printf "%5d pages\n" \ + $(cat $tmpFile | grep "Output written.*(.*pages" | sed "s/.*(\([0-9]*\) pages.*/\1/" || true) +echo + +bzip2 $tmpFile |