Merge pull request #294826 from xworld21/texlive-install-tl

texlive.withPackages: replace postBuild with install-tl like script
This commit is contained in:
Dmitry Kalinkin 2024-03-31 16:43:12 -04:00 committed by GitHub
commit ce426df9bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 2250 additions and 301 deletions

View File

@ -81,6 +81,14 @@ rec {
name = "texlive-test-context";
format = "context";
texLive = texliveConTeXt;
# check that the PDF has been created: we have hit cases of context
# failing with exit status 0 due to a misconfigured texlive
postTest = ''
if [[ ! -f "$name".pdf ]] ; then
echo "ConTeXt test failed: file '$name.pdf' not found"
exit 1
fi
'';
text = ''
\starttext
\startsection[title={ConTeXt test document}]
@ -333,18 +341,6 @@ rec {
done
'';
# test that fmtutil.cnf is fully regenerated on scheme-full
fmtutilCnf = runCommand "texlive-test-fmtutil.cnf" {
kpathsea = texlive.pkgs.kpathsea.tex;
schemeFull = texliveFull;
} ''
mkdir -p "$out"
diff --ignore-matching-lines='^# Generated by ' -u \
{"$kpathsea","$schemeFull"/share/texmf-var}/web2c/fmtutil.cnf \
| tee "$out/fmtutil.cnf.patch"
'';
# verify that the restricted mode gets enabled when
# needed (detected by checking if it disallows --gscmd)
repstopdf = runCommand "texlive-test-repstopdf" {

View File

@ -38,6 +38,12 @@ lib.fix (self: {
}@args:
let
### buildEnv with custom attributes
buildEnv' = args: (buildEnv
({ inherit (args) name paths; })
// lib.optionalAttrs (args ? extraOutputsToInstall) { inherit (args) extraOutputsToInstall; })
.overrideAttrs (removeAttrs args [ "extraOutputsToInstall" "name" "paths" "pkgs" ]);
### texlive.combine backward compatibility
# if necessary, convert old style { pkgs = [ ... ]; } packages to attribute sets
isOldPkgList = p: ! p.outputSpecified or false && p ? pkgs && builtins.all (p: p ? tlType) p.pkgs;
@ -78,7 +84,7 @@ let
otherOutputs = lib.genAttrs otherOutputNames (n: builtins.catAttrs n specified.wrong);
outputsToInstall = builtins.catAttrs "key" (builtins.genericClosure {
startSet = map (key: { inherit key; })
([ "out" ] ++ lib.optional (splitOutputs ? man) "man"
([ "out" ] ++ lib.optional (otherOutputs ? man) "man"
++ lib.concatLists (builtins.catAttrs "outputsToInstall" (builtins.catAttrs "meta" specified.wrong)));
operator = _: [ ];
});
@ -103,6 +109,14 @@ let
# outputs that do not become part of the environment
nonEnvOutputs = lib.subtractLists [ "out" "tex" "texdoc" "texsource" "tlpkg" ] otherOutputNames;
# packages that contribute to config files and formats
fontMaps = lib.filter (p: p ? fontMaps && (p.tlOutputName or p.outputName == "tex")) nonbin;
sortedFontMaps = builtins.sort (a: b: a.pname < b.pname) fontMaps;
hyphenPatterns = lib.filter (p: p ? hyphenPatterns && (p.tlOutputName or p.outputName == "tex")) nonbin;
sortedHyphenPatterns = builtins.sort (a: b: a.pname < b.pname) hyphenPatterns;
formatPkgs = lib.filter (p: p ? formats && (p.outputSpecified or false -> p.tlOutputName or p.outputName == "tex") && builtins.any (f: f.enabled or true) p.formats) all;
sortedFormatPkgs = builtins.sort (a: b: a.pname < b.pname) formatPkgs;
};
# list generated by inspecting `grep -IR '\([^a-zA-Z]\|^\)gs\( \|$\|"\)' "$TEXMFDIST"/scripts`
@ -113,7 +127,7 @@ let
name = if __combine then "texlive-${__extraName}-${bin.texliveYear}${__extraVersion}" # texlive.combine: old name name
else "texlive-${bin.texliveYear}-env";
texmfdist = (buildEnv {
texmfdist = buildEnv' {
name = "${name}-texmfdist";
# remove fake derivations (without 'outPath') to avoid undesired build dependencies
@ -126,14 +140,14 @@ let
''
mktexlsr "$out"
'';
}).overrideAttrs (_: { allowSubstitutes = true; });
};
tlpkg = (buildEnv {
tlpkg = buildEnv {
name = "${name}-tlpkg";
# remove fake derivations (without 'outPath') to avoid undesired build dependencies
paths = builtins.catAttrs "outPath" pkgList.tlpkg;
}).overrideAttrs (_: { allowSubstitutes = true; });
};
# the 'non-relocated' packages must live in $TEXMFROOT/texmf-dist
# and sometimes look into $TEXMFROOT/tlpkg (notably fmtutil, updmap look for perl modules in both)
@ -170,12 +184,10 @@ let
(requiredTeXPackages tl);
};
# emulate split output derivation
splitOutputs = {
texmfdist = texmfdist // { outputSpecified = true; };
texmfroot = texmfroot // { outputSpecified = true; };
} // (lib.genAttrs pkgList.nonEnvOutputs (outName: (buildEnv {
# other outputs
nonEnvOutputs = lib.genAttrs pkgList.nonEnvOutputs (outName: buildEnv' {
inherit name;
outputs = [ outName ];
paths = builtins.catAttrs "outPath"
(pkgList.otherOutputs.${outName} or [ ] ++ pkgList.specifiedOutputs.${outName} or [ ]);
# force the output to be ${outName} or nix-env will not work
@ -183,11 +195,9 @@ let
export out="''${${outName}-}"
'') ];
inherit meta passthru;
}).overrideAttrs { outputs = [ outName ]; } // { outputSpecified = true; }));
});
passthru = {
# these are not part of pkgList.nonEnvOutputs and must be exported in passthru
inherit (splitOutputs) texmfdist texmfroot;
# This is set primarily to help find-tarballs.nix to do its job
requiredTeXPackages = builtins.filter lib.isDerivation (pkgList.bin ++ pkgList.nonbin
++ lib.optionals (! __fromCombineWrapper)
@ -201,13 +211,57 @@ let
withPackages = reqs: self (args // { requiredTeXPackages = ps: requiredTeXPackages ps ++ reqs ps; __fromCombineWrapper = false; });
};
# TeXLive::TLOBJ::fmtutil_cnf_lines
fmtutilLine = { name, engine, enabled ? true, patterns ? [ "-" ], options ? "", ... }:
lib.optionalString (! enabled) "#! " + "${name} ${engine} ${lib.concatStringsSep "," patterns} ${options}";
fmtutilLines = { pname, formats, ...}:
[ "#" "# from ${pname}:" ] ++ map fmtutilLine formats;
# TeXLive::TLOBJ::language_dat_lines
langDatLine = { name, file, synonyms ? [ ], ... }:
[ "${name} ${file}" ] ++ map (s: "=" + s) synonyms;
langDatLines = { pname, hyphenPatterns, ... }:
[ "% from ${pname}:" ] ++ builtins.concatMap langDatLine hyphenPatterns;
# TeXLive::TLOBJ::language_def_lines
# see TeXLive::TLUtils::parse_AddHyphen_line for default values
langDefLine = { name, file, lefthyphenmin ? "", righthyphenmin ? "", synonyms ? [ ], ... }:
map (n: "\\addlanguage{${n}}{${file}}{}{${if lefthyphenmin == "" then "2" else lefthyphenmin}}{${if righthyphenmin == "" then "3" else righthyphenmin}}")
([ name ] ++ synonyms);
langDefLines = { pname, hyphenPatterns, ... }:
[ "% from ${pname}:" ] ++ builtins.concatMap langDefLine hyphenPatterns;
# TeXLive::TLOBJ::language_lua_lines
# see TeXLive::TLUtils::parse_AddHyphen_line for default values
langLuaLine = { name, file, lefthyphenmin ? "", righthyphenmin ? "", synonyms ? [ ], ... }@args: ''
''\t['${name}'] = {
''\t''\tloader = '${file}',
''\t''\tlefthyphenmin = ${if lefthyphenmin == "" then "2" else lefthyphenmin},
''\t''\trighthyphenmin = ${if righthyphenmin == "" then "3" else righthyphenmin},
''\t''\tsynonyms = { ${lib.concatStringsSep ", " (map (s: "'${s}'") synonyms)} },
''
+ lib.optionalString (args ? file_patterns) "\t\tpatterns = '${args.file_patterns}',\n"
+ lib.optionalString (args ? file_exceptions) "\t\thyphenation = '${args.file_exceptions}',\n"
+ lib.optionalString (args ? luaspecial) "\t\tspecial = '${args.luaspecial}',\n"
+ "\t},";
langLuaLines = { pname, hyphenPatterns, ... }:
[ "-- from ${pname}:" ] ++ map langLuaLine hyphenPatterns;
assembleConfigLines = f: packages:
builtins.concatStringsSep "\n" (builtins.concatMap f packages);
updmapLines = { pname, fontMaps, ...}:
[ "# from ${pname}:" ] ++ fontMaps;
out =
# no indent for git diff purposes
(buildEnv {
buildEnv' {
inherit name;
ignoreCollisions = false;
# use attrNames, attrValues to ensure the two lists are sorted in the same way
outputs = [ "out" ] ++ lib.optionals (! __combine) (builtins.attrNames nonEnvOutputs);
otherOutputs = lib.optionals (! __combine) (builtins.attrValues nonEnvOutputs);
# remove fake derivations (without 'outPath') to avoid undesired build dependencies
paths = builtins.catAttrs "outPath" pkgList.bin
@ -230,219 +284,26 @@ let
perl
];
buildInputs = [ coreutils gawk gnugrep gnused ] ++ lib.optional needsGhostscript ghostscript;
inherit meta passthru;
postBuild =
# create outputs
lib.optionalString (! __combine) ''
for otherOutputName in $outputs ; do
if [[ "$otherOutputName" == 'out' ]] ; then continue ; fi
otherOutput="otherOutput_$otherOutputName"
ln -s "''${!otherOutput}" "''${!otherOutputName}"
done
'' +
# environment variables (note: only export the ones that are used in the wrappers)
''
TEXMFROOT="${texmfroot}"
TEXMFDIST="${texmfdist}"
export PATH="$out/bin:$PATH"
TEXMFSYSCONFIG="$out/share/texmf-config"
TEXMFSYSVAR="$out/share/texmf-var"
export TEXMFCNF="$TEXMFSYSVAR/web2c"
'' +
# wrap executables with required env vars as early as possible
# 1. we use the wrapped binaries in the scripts below, to catch bugs
# 2. we do not want to wrap links generated by texlinks
''
enable -f '${bash}/lib/bash/realpath' realpath
declare -i wrapCount=0
for link in "$out"/bin/*; do
target="$(realpath "$link")"
if [[ "''${target##*/}" != "''${link##*/}" ]] ; then
# detected alias with different basename, use immediate target of $link to preserve $0
# relevant for mktexfmt, repstopdf, ...
target="$(readlink "$link")"
fi
inherit texmfdist texmfroot;
# skip non-executable files (such as context.lua)
if [[ ! -x "$target" ]] ; then continue ; fi
fontconfigFile = makeFontsConf { fontDirectories = [ "${texmfroot}/texmf-dist/fonts" ]; };
rm "$link"
makeWrapper "$target" "$link" \
--inherit-argv0 \
--prefix PATH : "${
# very common dependencies that are not detected by tests.texlive.binaries
lib.makeBinPath ([ coreutils gawk gnugrep gnused ] ++ lib.optional needsGhostscript ghostscript)}:$out/bin" \
--set-default TEXMFCNF "$TEXMFCNF" \
--set-default FONTCONFIG_FILE "${
# necessary for XeTeX to find the fonts distributed with texlive
makeFontsConf { fontDirectories = [ "${texmfroot}/texmf-dist/fonts" ]; }
}"
wrapCount=$((wrapCount + 1))
done
echo "wrapped $wrapCount binaries and scripts"
'' +
# patch texmf-dist -> $TEXMFDIST
# patch texmf-local -> $out/share/texmf-local
# patch texmf.cnf -> $TEXMFSYSVAR/web2c/texmf.cnf
# TODO: perhaps do lua actions?
# tried inspiration from install-tl, sub do_texmf_cnf
''
mkdir -p "$TEXMFCNF"
if [ -e "$TEXMFDIST/web2c/texmfcnf.lua" ]; then
sed \
-e "s,\(TEXMFOS[ ]*=[ ]*\)[^\,]*,\1\"$TEXMFROOT\",g" \
-e "s,\(TEXMFDIST[ ]*=[ ]*\)[^\,]*,\1\"$TEXMFDIST\",g" \
-e "s,\(TEXMFSYSVAR[ ]*=[ ]*\)[^\,]*,\1\"$TEXMFSYSVAR\",g" \
-e "s,\(TEXMFSYSCONFIG[ ]*=[ ]*\)[^\,]*,\1\"$TEXMFSYSCONFIG\",g" \
-e "s,\(TEXMFLOCAL[ ]*=[ ]*\)[^\,]*,\1\"$out/share/texmf-local\",g" \
-e "s,\$SELFAUTOLOC,$out,g" \
-e "s,selfautodir:/,$out/share/,g" \
-e "s,selfautodir:,$out/share/,g" \
-e "s,selfautoparent:/,$out/share/,g" \
-e "s,selfautoparent:,$out/share/,g" \
"$TEXMFDIST/web2c/texmfcnf.lua" > "$TEXMFCNF/texmfcnf.lua"
fi
fmtutilCnf = assembleConfigLines fmtutilLines pkgList.sortedFormatPkgs;
updmapCfg = assembleConfigLines updmapLines pkgList.sortedFontMaps;
sed \
-e "s,\(TEXMFROOT[ ]*=[ ]*\)[^\,]*,\1$TEXMFROOT,g" \
-e "s,\(TEXMFDIST[ ]*=[ ]*\)[^\,]*,\1$TEXMFDIST,g" \
-e "s,\(TEXMFSYSVAR[ ]*=[ ]*\)[^\,]*,\1$TEXMFSYSVAR,g" \
-e "s,\(TEXMFSYSCONFIG[ ]*=[ ]*\)[^\,]*,\1$TEXMFSYSCONFIG,g" \
-e "s,\$SELFAUTOLOC,$out,g" \
-e "s,\$SELFAUTODIR,$out/share,g" \
-e "s,\$SELFAUTOPARENT,$out/share,g" \
-e "s,\$SELFAUTOGRANDPARENT,$out/share,g" \
-e "/^mpost,/d" `# CVE-2016-10243` \
"$TEXMFDIST/web2c/texmf.cnf" > "$TEXMFCNF/texmf.cnf"
'' +
# now filter hyphenation patterns and formats
(let
hyphens = lib.filter (p: p.hasHyphens or false && p.tlOutputName or p.outputName == "tex") pkgList.nonbin;
hyphenPNames = map (p: p.pname) hyphens;
formats = lib.filter (p: p ? formats && p.tlOutputName or p.outputName == "tex") pkgList.nonbin;
formatPNames = map (p: p.pname) formats;
# sed expression that prints the lines in /start/,/end/ except for /end/
section = start: end: "/${start}/,/${end}/{ /${start}/p; /${end}/!p; };\n";
script =
writeText "hyphens.sed" (
# document how the file was generated (for language.dat)
"1{ s/^(% Generated by .*)$/\\1, modified by ${if __combine then "texlive.combine" else "Nixpkgs"}/; p; }\n"
# pick up the header
+ "2,/^% from/{ /^% from/!p; };\n"
# pick up all sections matching packages that we combine
+ lib.concatMapStrings (pname: section "^% from ${pname}:$" "^% from|^%%% No changes may be made beyond this point.$") hyphenPNames
# pick up the footer (for language.def)
+ "/^%%% No changes may be made beyond this point.$/,$p;\n"
);
scriptLua =
writeText "hyphens.lua.sed" (
"1{ s/^(-- Generated by .*)$/\\1, modified by ${if __combine then "texlive.combine" else "Nixpkgs"}/; p; }\n"
+ "2,/^-- END of language.us.lua/p;\n"
+ lib.concatMapStrings (pname: section "^-- from ${pname}:$" "^}$|^-- from") hyphenPNames
+ "$p;\n"
);
# formats not being installed must be disabled by prepending #! (see man fmtutil)
# sed expression that enables the formats in /start/,/end/
enableFormats = pname: "/^# from ${pname}:$/,/^# from/{ s/^#! //; };\n";
fmtutilSed =
writeText "fmtutil.sed" (
# document how file was generated
"1{ s/^(# Generated by .*)$/\\1, modified by ${if __combine then "texlive.combine" else "Nixpkgs"}/; }\n"
# disable all formats, even those already disabled
+ "s/^([^#]|#! )/#! \\1/;\n"
# enable the formats from the packages being installed
+ lib.concatMapStrings enableFormats formatPNames
# clean up formats that have been disabled twice
+ "s/^#! #! /#! /;\n"
);
in ''
mkdir -p "$TEXMFSYSVAR/tex/generic/config"
for fname in tex/generic/config/language.{dat,def}; do
[[ -e "$TEXMFDIST/$fname" ]] && sed -E -n -f '${script}' "$TEXMFDIST/$fname" > "$TEXMFSYSVAR/$fname"
done
[[ -e "$TEXMFDIST"/tex/generic/config/language.dat.lua ]] && sed -E -n -f '${scriptLua}' \
"$TEXMFDIST"/tex/generic/config/language.dat.lua > "$TEXMFSYSVAR"/tex/generic/config/language.dat.lua
[[ -e "$TEXMFDIST"/web2c/fmtutil.cnf ]] && sed -E -f '${fmtutilSed}' "$TEXMFDIST"/web2c/fmtutil.cnf > "$TEXMFCNF"/fmtutil.cnf
languageDat = assembleConfigLines langDatLines pkgList.sortedHyphenPatterns;
languageDef = assembleConfigLines langDefLines pkgList.sortedHyphenPatterns;
languageLua = assembleConfigLines langLuaLines pkgList.sortedHyphenPatterns;
# create $TEXMFSYSCONFIG database, make new $TEXMFSYSVAR files visible to kpathsea
mktexlsr "$TEXMFSYSCONFIG" "$TEXMFSYSVAR"
'') +
# generate format links (reads fmtutil.cnf to know which ones) *after* the wrappers have been generated
''
texlinks --quiet "$out/bin"
'' +
# temporarily patch mtxrun.lua to generate uuid's deterministically from SOURCE_DATE_EPOCH
''
if [[ -e "$out/bin/mtxrun" ]]; then
mv "$out"/bin/mtxrun.lua{,.orig}
substitute "$TEXMFDIST"/scripts/context/lua/mtxrun.lua "$out"/bin/mtxrun.lua \
--replace-fail 'randomseed(math.initialseed)' "randomseed($SOURCE_DATE_EPOCH)"
fi
'' +
# texlive postactions (see TeXLive::TLUtils::_do_postaction_script)
# this step includes generating the ConTeXt file databases since TL 2023
(lib.concatMapStrings (pkg: ''
postaction='${pkg.postactionScript}'
case "$postaction" in
*.pl) postInterp=perl ;;
*.texlua) postInterp=texlua ;;
*) postInterp= ;;
esac
echo "postaction install script for ${pkg.pname}: ''${postInterp:+$postInterp }$postaction install $TEXMFROOT"
FORCE_SOURCE_DATE=1 TZ= $postInterp "$TEXMFROOT"/$postaction install "$TEXMFROOT"
'') (lib.filter (pkg: pkg ? postactionScript) pkgList.tlpkg)) +
# restore the original mtxrun.lua
''
if [[ -e "$out/bin/mtxrun" ]]; then
mv "$out"/bin/mtxrun.lua{.orig,}
fi
'' +
# generate formats
# TODO generate ConTeXt formats (based on fmtutil.cnf?)
''
# many formats still ignore SOURCE_DATE_EPOCH even when FORCE_SOURCE_DATE=1
# libfaketime fixes non-determinism related to timestamps ignoring FORCE_SOURCE_DATE
# we cannot fix further randomness caused by luatex; for further details, see
# https://salsa.debian.org/live-team/live-build/-/blob/master/examples/hooks/reproducible/2006-reproducible-texlive-binaries-fmt-files.hook.chroot#L52
# note that calling faketime and fmtutil is fragile (faketime uses LD_PRELOAD, fmtutil calls /bin/sh, causing potential glibc issues on non-NixOS)
# so we patch fmtutil to use faketime, rather than calling faketime fmtutil
substitute "$TEXMFDIST"/scripts/texlive/fmtutil.pl fmtutil \
--replace-fail 'my $cmdline = "$eng -ini ' 'my $cmdline = "faketime -f '"'"'\@1980-01-01 00:00:00 x0.001'"'"' $eng -ini '
FORCE_SOURCE_DATE=1 TZ= perl fmtutil --sys --all | grep '^fmtutil' # too verbose
postactionScripts = builtins.catAttrs "postactionScript" pkgList.tlpkg;
# Disable unavailable map files
echo y | updmap --sys --syncwithtrees --force 2>&1 | grep '^\(updmap\| /\)'
# Regenerate the map files (this is optional)
updmap --sys --force 2>&1 | grep '^\(updmap\| /\)'
# sort entries to improve reproducibility
[[ -f "$TEXMFSYSCONFIG"/web2c/updmap.cfg ]] && sort -o "$TEXMFSYSCONFIG"/web2c/updmap.cfg "$TEXMFSYSCONFIG"/web2c/updmap.cfg
mktexlsr "$TEXMFSYSCONFIG" "$TEXMFSYSVAR" # to make sure (of what?)
'' +
# remove *-sys scripts since /nix/store is readonly
''
rm "$out"/bin/*-sys
'' +
# Get rid of all log files. They are not needed, but take up space
# and render the build unreproducible by their embedded timestamps
# and other non-deterministic diagnostics.
''
find "$TEXMFSYSVAR"/web2c -name '*.log' -delete
'' +
# link TEXMFDIST in $out/share for backward compatibility
''
ln -s "$TEXMFDIST" "$out"/share/texmf
''
;
}).overrideAttrs (prev:
{ allowSubstitutes = true; }
// lib.optionalAttrs (! __combine) ({
outputs = [ "out" ] ++ pkgList.nonEnvOutputs;
meta = prev.meta // { inherit (pkgList) outputsToInstall; };
} // builtins.listToAttrs
(map (out: { name = "otherOutput_" + out; value = splitOutputs.${out}; }) pkgList.nonEnvOutputs)
)
);
in out)
postBuild = ''
. "${./build-tex-env.sh}"
'';
};
# outputsToInstall must be set *after* overrideAttrs (used in buildEnv') or it fails the checkMeta tests
in if __combine then out else lib.addMetaAttrs { inherit (pkgList) outputsToInstall; } out)

View File

@ -0,0 +1,328 @@
# shellcheck shell=bash
# Replicate the post install phase of the upstream TeX Live installer.
#
# This script is based on the install-tl script and the TeXLive::TLUtils perl
# module, down to using the same (prefixed) function names and log messages.
#
# When updating to the next TeX Live release, review install-tl for changes and
# update this script accordingly.
### install-tl
# adjust texmf.cnf and texmfcnf.lua
installtl_do_texmf_cnf () {
# unlike install-tl, we make a copy of the entire texmf.cnf
# and point the binaries at $TEXMFCNF/texmf.cnf via wrappers
mkdir -p "$TEXMFCNF"
if [[ -e $texmfdist/web2c/texmfcnf.lua ]]; then
tlutils_info "writing texmfcnf.lua to $TEXMFCNF/texmfcnf.lua"
sed -e "s,\(TEXMFOS[ ]*=[ ]*\)[^\,]*,\1\"$texmfroot\",g" \
-e "s,\(TEXMFDIST[ ]*=[ ]*\)[^\,]*,\1\"$texmfdist\",g" \
-e "s,\(TEXMFSYSVAR[ ]*=[ ]*\)[^\,]*,\1\"$TEXMFSYSVAR\",g" \
-e "s,\(TEXMFSYSCONFIG[ ]*=[ ]*\)[^\,]*,\1\"$TEXMFSYSCONFIG\",g" \
-e "s,\(TEXMFLOCAL[ ]*=[ ]*\)[^\,]*,\1\"$out/share/texmf-local\",g" \
-e "s,\$SELFAUTOLOC,$out,g" \
-e "s,selfautodir:/,$out/share/,g" \
-e "s,selfautodir:,$out/share/,g" \
-e "s,selfautoparent:/,$out/share/,g" \
-e "s,selfautoparent:,$out/share/,g" \
"$texmfdist/web2c/texmfcnf.lua" > "$TEXMFCNF/texmfcnf.lua"
fi
tlutils_info "writing texmf.cnf to $TEXMFCNF/texmf.cnf"
sed -e "s,\(TEXMFROOT[ ]*=[ ]*\)[^\,]*,\1$texmfroot,g" \
-e "s,\(TEXMFDIST[ ]*=[ ]*\)[^\,]*,\1$texmfdist,g" \
-e "s,\(TEXMFSYSVAR[ ]*=[ ]*\)[^\,]*,\1$TEXMFSYSVAR,g" \
-e "s,\(TEXMFSYSCONFIG[ ]*=[ ]*\)[^\,]*,\1$TEXMFSYSCONFIG,g" \
-e "s,\$SELFAUTOLOC,$out,g" \
-e "s,\$SELFAUTODIR,$out/share,g" \
-e "s,\$SELFAUTOPARENT,$out/share,g" \
-e "s,\$SELFAUTOGRANDPARENT,$out/share,g" \
"$texmfdist/web2c/texmf.cnf" > "$TEXMFCNF/texmf.cnf"
}
# run postaction scripts from texlive.tlpdb
# note that the other postactions (fileassoc, ...) are Windows only
installtl_do_tlpdb_postactions () {
local postaction postInterp
if [[ -n $postactionScripts ]] ; then
tlutils_info "running package-specific postactions"
for postaction in $postactionScripts ; do
# see TeXLive::TLUtils::_installtl_do_postaction_script
case "$postaction" in
*.pl)
postInterp=perl ;;
*.texlua)
postInterp=texlua ;;
*)
postInterp= ;;
esac
tlutils_info "${postInterp:+$postInterp }$postaction install $texmfroot"
FORCE_SOURCE_DATE=1 $postInterp "$texmfroot/$postaction" install "$texmfroot" >>"$TEXMFSYSVAR/postaction-${postaction##*/}.log"
done
tlutils_info "finished with package-specific postactions"
fi
}
installtl_do_path_adjustments () {
# here install-tl would add a system symlink to the man pages
# instead we run other nixpkgs related adjustments
# generate wrappers
tlutils_info "wrapping binaries"
local bash cmd extraPaths link path target wrapCount
bash="$(command -v bash)"
enable -f "${bash%/bin/bash}"/lib/bash/realpath realpath
# common runtime dependencies
for cmd in cat awk sed grep gs ; do
# do not fail if gs is absent
path="$(PATH="$HOST_PATH" command -v "$cmd" || :)"
if [[ -n $path ]] ; then
extraPaths="${extraPaths:+$extraPaths:}${path%/"$cmd"}"
fi
done
declare -i wrapCount=0
for link in "$out"/bin/* ; do
target="$(realpath "$link")"
# skip non-executable files (such as context.lua)
if [[ ! -x $target ]] ; then
continue
fi
if [[ ${target##*/} != "${link##*/}" ]] ; then
# detected alias with different basename, use immediate target of $link to preserve $0
# relevant for mktexfmt, repstopdf, ...
target="$(readlink "$link")"
fi
rm "$link"
makeWrapper "$target" "$link" \
--inherit-argv0 \
--prefix PATH : "$extraPaths:$out/bin" \
--set-default TEXMFCNF "$TEXMFCNF" \
--set-default FONTCONFIG_FILE "$fontconfigFile"
wrapCount=$((wrapCount + 1))
done
tlutils_info "wrapped $wrapCount binaries and scripts"
# generate format symlinks (using fmtutil.cnf)
tlutils_info "generating format symlinks"
texlinks --quiet "$out/bin"
# remove *-sys scripts since /nix/store is readonly
rm "$out"/bin/*-sys
# link TEXMFDIST in $out/share for backward compatibility
ln -s "$texmfdist" "$out"/share/texmf
# generate other outputs
local otherOutput otherOutputName
local otherOutputs="$otherOutputs"
for otherOutputName in $outputs ; do
if [[ $otherOutputName == out ]] ; then
continue
fi
otherOutput="${otherOutputs%% *}"
otherOutputs="${otherOutputs#* }"
ln -s "$otherOutput" "${!otherOutputName}"
done
}
# run all post install parts
installtl_do_postinst_stuff () {
installtl_do_texmf_cnf
# create various config files
# in principle, we could use writeText and share them across different
# environments, but the eval & build overhead is not worth the savings
tlutils_create_fmtutil
tlutils_create_updmap
tlutils_create_language_dat
tlutils_create_language_def
tlutils_create_language_lua
# make new files available
tlutils_info "running mktexlsr $TEXMFSYSVAR $TEXMFSYSCONFIG"
mktexlsr "$TEXMFSYSVAR" "$TEXMFSYSCONFIG"
# update font maps
tlutils_info "generating font maps"
updmap-sys --quiet --force --nohash 2>&1
# configure the paper size
# tlmgr --no-execute-actions paper letter
# install-tl: "rerun mktexlsr for updmap-sys and tlmgr paper updates"
tlutils_info "re-running mktexlsr $TEXMFSYSVAR $TEXMFSYSCONFIG"
mktexlsr "$TEXMFSYSVAR" "$TEXMFSYSCONFIG"
tlutils_update_context_cache
# generate formats
# install-tl would run fmtutil-sys $common_fmtutil_args --no-strict --all
# instead, we want fmtutil to exit with error on failure
if [[ -n $fmtutilCnf ]] ; then
tlutils_info "pre-generating all format files, be patient..."
# many formats still ignore SOURCE_DATE_EPOCH even when FORCE_SOURCE_DATE=1
# libfaketime fixes non-determinism related to timestamps ignoring FORCE_SOURCE_DATE
# we cannot fix further randomness caused by luatex; for further details, see
# https://salsa.debian.org/live-team/live-build/-/blob/master/examples/hooks/reproducible/2006-reproducible-texlive-binaries-fmt-files.hook.chroot#L52
# note that calling faketime and fmtutil is fragile (faketime uses LD_PRELOAD, fmtutil calls /bin/sh, causing potential glibc issues on non-NixOS)
# so we patch fmtutil to use faketime, rather than calling faketime fmtutil
substitute "$texmfdist"/scripts/texlive/fmtutil.pl fmtutil \
--replace-fail "my \$cmdline = \"\$eng -ini " "my \$cmdline = \"faketime -f '\@$(date +'%F %T' --date=@"$SOURCE_DATE_EPOCH") x0.001' \$eng -ini "
FORCE_SOURCE_DATE=1 perl fmtutil --quiet --strict --sys --all 2>&1 | grep '^fmtutil' # too verbose
fi
installtl_do_path_adjustments
installtl_do_tlpdb_postactions
# remove log files to improve reproducibility
find "$TEXMFSYSVAR" -name '*.log' -delete
}
### TeXLive::TLUtils
tlutils_info () {
printf '%s\n' "texlive: $*"
}
tlutils_create_fmtutil () {
# fmtutil.cnf created by install-tl already exists readonly in $texmfdist
# so here we need to *disable* the entries that are not in $fmtutilCnf
# and write the output in the writeable $TEXMFSYSVAR
local engine fmt line outFile sedExpr
outFile="$TEXMFSYSVAR"/web2c/fmtutil.cnf
tlutils_info "writing fmtutil.cnf to $outFile"
while IFS= read -r line ; do
# a line is 'fmt engine ...' or '#! fmt engine ...'
# (see fmtutil.pl::read_fmtutil_file)
line="${line#\#! }"
read -r fmt engine _ <<<"$line"
# if a line for the ($fmt,$engine) pair exists, remove it to avoid
# pointless warnings from fmtutil
sedExpr="$sedExpr /^(#! )?$fmt $engine /d;"
done <<<"$fmtutilCnf"
# disable all the remaining formats
sedExpr="$sedExpr /^[^#]/{ s/^/#! /p };"
{
echo "# Generated by nixpkgs"
sed -E -n -e "$sedExpr" "$texmfdist"/web2c/fmtutil.cnf
[[ -z $fmtutilCnf ]] || printf '%s' "$fmtutilCnf"
} >"$outFile"
}
tlutils_create_updmap () {
# updmap.cfg created by install-tl already exists readonly in $texmfdist
# so here we need to *disable* the entries that are not in $updmapCfg
# and write the output in the writeable $TEXMFSYSVAR
local line map outFile sedExpr
outFile="$TEXMFSYSVAR"/web2c/updmap.cfg
tlutils_info "writing updmap.cfg to $outFile"
while IFS= read -r line ; do
# a line is 'type map' or '#! type map'
# (see fmtutil.pl::read_updmap_file)
read -r _ map <<<"$line"
# if a line for $map exists, remove it to avoid
# pointless warnings from updmap
sedExpr="$sedExpr /^(#! )?[^ ]*Map $map$/d;"
done <<<"$updmapCfg"
# disable all the remaining font maps
sedExpr="$sedExpr /^[^ ]*Map/{ s/^/#! /p };"
{
echo "# Generated by nixpkgs"
sed -E -n -e "$sedExpr" "$texmfdist"/web2c/updmap.cfg
[[ -z $updmapCfg ]] || printf '%s' "$updmapCfg"
} >"$outFile"
}
tlutils__create_config_files () {
# simplified arguments
local header="$1"
local dest="$2"
local prefix="$3"
local lines="$4"
local suffix="$5"
if [[ -z "$header" || -e "$header" ]] ; then
tlutils_info "writing ${dest##*/} to $dest"
{
[[ -z $prefix ]] || printf '%s\n' "$prefix"
[[ ! -e $header ]] || cat "$header"
[[ -z $lines ]] || printf '%s\n' "$lines"
[[ -z $suffix ]] || printf '%s\n' "$suffix"
} >"$dest"
fi
}
tlutils_create_language_dat () {
tlutils__create_config_files \
"$texmfdist"/tex/generic/config/language.us \
"$TEXMFSYSVAR"/tex/generic/config/language.dat \
'% Generated by nixpkgs' \
"$languageDat" \
''
}
tlutils_create_language_def () {
tlutils__create_config_files \
"$texmfdist"/tex/generic/config/language.us.def \
"$TEXMFSYSVAR"/tex/generic/config/language.def \
'' \
"$languageDef" \
'%%% No changes may be made beyond this point.
\uselanguage {USenglish} %%% This MUST be the last line of the file.'
}
tlutils_create_language_lua () {
tlutils__create_config_files \
"$texmfdist"/tex/generic/config/language.us.lua \
"$TEXMFSYSVAR"/tex/generic/config/language.dat.lua \
'-- Generated by nixpkgs' \
"$languageLua" \
'}'
}
tlutils_update_context_cache () {
if [[ -x $out/bin/mtxrun ]] ; then
tlutils_info "setting up ConTeXt cache"
# temporarily patch mtxrun.lua to generate uuid's deterministically from SOURCE_DATE_EPOCH
mv "$out"/bin/mtxrun.lua{,.orig}
substitute "$out"/bin/mtxrun.lua.orig "$out"/bin/mtxrun.lua \
--replace-fail 'randomseed(math.initialseed)' "randomseed($SOURCE_DATE_EPOCH)"
# this is very verbose, save the output for debugging purposes
{
mtxrun --generate
context --luatex --generate
[[ ! -x $out/bin/luajittex ]] || context --luajittex --generate
} >"$TEXMFSYSVAR"/context-cache.log
mv "$out"/bin/mtxrun.lua{.orig,}
fi
}
# init variables (export only the ones that are used in the wrappers)
export PATH="$out/bin:$PATH"
TEXMFSYSCONFIG="$out/share/texmf-config"
TEXMFSYSVAR="$out/share/texmf-var"
export TEXMFCNF="$TEXMFSYSVAR/web2c"
installtl_do_postinst_stuff

View File

@ -82,8 +82,9 @@ let
outputSpecified = true;
inherit tex;
} // lib.optionalAttrs (args ? deps) { tlDeps = args.deps; }
// lib.optionalAttrs (args ? fontMaps) { inherit (args) fontMaps; }
// lib.optionalAttrs (args ? formats) { inherit (args) formats; }
// lib.optionalAttrs hasHyphens { inherit hasHyphens; }
// lib.optionalAttrs (args ? hyphenPatterns) { inherit (args) hyphenPatterns; }
// lib.optionalAttrs (args ? postactionScript) { inherit (args) postactionScript; }
// lib.optionalAttrs hasDocfiles { texdoc = texdoc; }
// lib.optionalAttrs hasSource { texsource = texsource; }

View File

@ -80,6 +80,22 @@ $a}
D # restart cycle from the current line
}
# extract font maps
/^execute add.*Map /{
# open a list
i\ fontMaps = [
# loop through following map lines
:next-map
s/^\n?execute add(.*Map .*)$/ "\1"/p # print map
s/^.*$// # clear pattern space
N; /^\nexecute add.*Map /b next-map
# close the string
i\ ];
D # restart cycle from the current line
}
# detect presence of notable files
/^docfiles /{
s/^.*$// # ignore the first line
@ -129,9 +145,36 @@ $a}
# extract postaction scripts (right now, at most one per package, so a string suffices)
s/^postaction script file=(.*)$/ postactionScript = "\1";/p
# extract hyphenation patterns and formats
# (this may create duplicate lines, use uniq to remove them)
/^execute\sAddHyphen/i\ hasHyphens = true;
# extract hyphenation patterns
/^execute\sAddHyphen\s/{
# open a list
i\ hyphenPatterns = [
# create one attribute set per hyphenation pattern
# plain keys: name, lefthyphenmin, righthyphenmin, file, file_patterns, file_exceptions, comment
# optionally double quoted key: luaspecial, comment
# comma-separated lists: databases, synonyms
:next-hyphen
s/(^|\n)execute\sAddHyphen/ {/
s/\s+luaspecial="([^"]+)"/\n luaspecial = "\1";/
s/\s+(name|lefthyphenmin|righthyphenmin|file|file_patterns|file_exceptions|luaspecial|comment)=([^ \t\n]*)/\n \1 = "\2";/g
s/\s+(databases|synonyms)=([^ \t\n]+)/\n \1 = [ "\2" ];/g
s/$/\n }/
:split-hyphens
s/"([^,]+),([^"]+)" ]/"\1" "\2" ]/;
t split-hyphens # repeat until there are no commas
p
s/^.*$// # clear pattern space
N
/^\nexecute\sAddHyphen\s/b next-hyphen
# close the list
i\ ];
D # restart cycle from the current line
}
# extract format details
/^execute\sAddFormat\s/{

File diff suppressed because it is too large Load Diff