2024-05-02 00:46:19 +00:00
|
|
|
{
|
|
|
|
# texlive package set
|
2024-06-30 08:16:52 +00:00
|
|
|
tl,
|
|
|
|
bin,
|
|
|
|
|
|
|
|
lib,
|
|
|
|
buildEnv,
|
|
|
|
libfaketime,
|
|
|
|
makeFontsConf,
|
|
|
|
makeWrapper,
|
|
|
|
runCommand,
|
|
|
|
writeShellScript,
|
|
|
|
writeText,
|
|
|
|
toTLPkgSets,
|
|
|
|
bash,
|
|
|
|
perl,
|
2024-05-02 00:46:19 +00:00
|
|
|
|
|
|
|
# common runtime dependencies
|
2024-06-30 08:16:52 +00:00
|
|
|
coreutils,
|
|
|
|
gawk,
|
|
|
|
gnugrep,
|
|
|
|
gnused,
|
|
|
|
ghostscript,
|
2024-05-02 00:46:19 +00:00
|
|
|
}:
|
|
|
|
|
2024-06-30 08:16:52 +00:00
|
|
|
lib.fix (
|
|
|
|
self:
|
|
|
|
{
|
|
|
|
withDocs ? false,
|
|
|
|
withSources ? false,
|
|
|
|
requiredTeXPackages ? ps: [ ps.scheme-infraonly ],
|
|
|
|
|
|
|
|
### texlive.combine backward compatibility
|
|
|
|
__extraName ? "combined",
|
|
|
|
__extraVersion ? "",
|
|
|
|
# emulate the old texlive.combine (e.g. add man pages to main output)
|
|
|
|
__combine ? false,
|
|
|
|
# adjust behavior further if called from the texlive.combine wrapper
|
|
|
|
__fromCombineWrapper ? false,
|
|
|
|
}@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;
|
|
|
|
ensurePkgSets =
|
|
|
|
ps:
|
|
|
|
if !__fromCombineWrapper && builtins.any isOldPkgList ps then
|
|
|
|
let
|
|
|
|
oldPkgLists = builtins.partition isOldPkgList ps;
|
|
|
|
in
|
|
|
|
oldPkgLists.wrong ++ lib.concatMap toTLPkgSets oldPkgLists.right
|
|
|
|
else
|
|
|
|
ps;
|
|
|
|
|
|
|
|
pkgList = rec {
|
|
|
|
# resolve dependencies of the packages that affect the runtime
|
|
|
|
all =
|
|
|
|
let
|
|
|
|
# order of packages is irrelevant
|
|
|
|
packages = builtins.sort (a: b: a.pname < b.pname) (ensurePkgSets (requiredTeXPackages tl));
|
|
|
|
runtime = builtins.partition (
|
|
|
|
p:
|
|
|
|
p.outputSpecified or false
|
|
|
|
-> builtins.elem (p.tlOutputName or p.outputName) [
|
|
|
|
"out"
|
|
|
|
"tex"
|
|
|
|
"tlpkg"
|
|
|
|
]
|
|
|
|
) packages;
|
|
|
|
keySet = p: {
|
|
|
|
key = ((p.name or "${p.pname}-${p.version}") + "-" + p.tlOutputName or p.outputName or "");
|
|
|
|
inherit p;
|
|
|
|
tlDeps = if p ? tlDeps then ensurePkgSets p.tlDeps else (p.requiredTeXPackages or (_: [ ]) tl);
|
|
|
|
};
|
|
|
|
in
|
|
|
|
# texlive.combine: the wrapper already resolves all dependencies
|
|
|
|
if __fromCombineWrapper then
|
|
|
|
requiredTeXPackages null
|
|
|
|
else
|
|
|
|
builtins.catAttrs "p" (
|
|
|
|
builtins.genericClosure {
|
|
|
|
startSet = map keySet runtime.right;
|
|
|
|
operator = p: map keySet p.tlDeps;
|
|
|
|
}
|
|
|
|
)
|
|
|
|
++ runtime.wrong;
|
|
|
|
|
|
|
|
# group the specified outputs
|
|
|
|
specified = builtins.partition (p: p.outputSpecified or false) all;
|
|
|
|
specifiedOutputs = lib.groupBy (p: p.tlOutputName or p.outputName) specified.right;
|
|
|
|
otherOutputNames = builtins.catAttrs "key" (
|
|
|
|
builtins.genericClosure {
|
|
|
|
startSet = map (key: { inherit key; }) (
|
|
|
|
lib.concatLists (builtins.catAttrs "outputs" specified.wrong)
|
|
|
|
);
|
|
|
|
operator = _: [ ];
|
|
|
|
}
|
|
|
|
);
|
|
|
|
otherOutputs = lib.genAttrs otherOutputNames (n: builtins.catAttrs n specified.wrong);
|
|
|
|
outputsToInstall = builtins.catAttrs "key" (
|
|
|
|
builtins.genericClosure {
|
|
|
|
startSet = map (key: { inherit key; }) (
|
|
|
|
[ "out" ]
|
|
|
|
++ lib.optional (otherOutputs ? man) "man"
|
|
|
|
++ lib.concatLists (builtins.catAttrs "outputsToInstall" (builtins.catAttrs "meta" specified.wrong))
|
|
|
|
);
|
|
|
|
operator = _: [ ];
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
# split binary and tlpkg from tex, texdoc, texsource
|
|
|
|
bin =
|
|
|
|
if __fromCombineWrapper then
|
|
|
|
builtins.filter (p: p.tlType == "bin") all # texlive.combine: legacy filter
|
|
|
|
else
|
|
|
|
otherOutputs.out or [ ] ++ specifiedOutputs.out or [ ];
|
|
|
|
tlpkg =
|
|
|
|
if __fromCombineWrapper then
|
|
|
|
builtins.filter (p: p.tlType == "tlpkg") all # texlive.combine: legacy filter
|
|
|
|
else
|
|
|
|
otherOutputs.tlpkg or [ ] ++ specifiedOutputs.tlpkg or [ ];
|
|
|
|
|
|
|
|
nonbin =
|
|
|
|
if __fromCombineWrapper then
|
|
|
|
builtins.filter (p: p.tlType != "bin" && p.tlType != "tlpkg") all # texlive.combine: legacy filter
|
|
|
|
else
|
|
|
|
(
|
|
|
|
if __combine then # texlive.combine: emulate old input ordering to avoid rebuilds
|
|
|
|
lib.concatMap (
|
|
|
|
p:
|
|
|
|
lib.optional (p ? tex) p.tex
|
|
|
|
++ lib.optional ((withDocs || p ? man) && p ? texdoc) p.texdoc
|
|
|
|
++ lib.optional (withSources && p ? texsource) p.texsource
|
|
|
|
) specified.wrong
|
|
|
|
else
|
|
|
|
otherOutputs.tex or [ ]
|
|
|
|
++ lib.optionals withDocs (otherOutputs.texdoc or [ ])
|
|
|
|
++ lib.optionals withSources (otherOutputs.texsource or [ ])
|
|
|
|
)
|
|
|
|
++ specifiedOutputs.tex or [ ]
|
|
|
|
++ specifiedOutputs.texdoc or [ ]
|
|
|
|
++ specifiedOutputs.texsource or [ ];
|
|
|
|
|
|
|
|
# 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`
|
|
|
|
# and `grep -IR rungs "$TEXMFDIST"`
|
|
|
|
# and ignoring luatex, perl, and shell scripts (those must be patched using postFixup)
|
|
|
|
needsGhostscript = lib.any (
|
|
|
|
p:
|
|
|
|
lib.elem p.pname [
|
|
|
|
"context"
|
|
|
|
"dvipdfmx"
|
|
|
|
"latex-papersize"
|
|
|
|
"lyluatex"
|
|
|
|
]
|
|
|
|
) pkgList.bin;
|
|
|
|
|
|
|
|
name =
|
|
|
|
if __combine then
|
|
|
|
"texlive-${__extraName}-${bin.texliveYear}${__extraVersion}" # texlive.combine: old name name
|
|
|
|
else
|
|
|
|
"texlive-${bin.texliveYear}-env";
|
|
|
|
|
|
|
|
texmfdist = buildEnv' {
|
|
|
|
name = "${name}-texmfdist";
|
|
|
|
|
|
|
|
# remove fake derivations (without 'outPath') to avoid undesired build dependencies
|
|
|
|
paths = builtins.catAttrs "outPath" pkgList.nonbin;
|
|
|
|
|
|
|
|
# mktexlsr
|
|
|
|
nativeBuildInputs = [ tl."texlive.infra" ];
|
|
|
|
|
|
|
|
postBuild = # generate ls-R database
|
|
|
|
''
|
|
|
|
mktexlsr "$out"
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
tlpkg = buildEnv {
|
|
|
|
name = "${name}-tlpkg";
|
|
|
|
|
|
|
|
# remove fake derivations (without 'outPath') to avoid undesired build dependencies
|
|
|
|
paths = builtins.catAttrs "outPath" pkgList.tlpkg;
|
|
|
|
};
|
|
|
|
|
|
|
|
# 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)
|
|
|
|
texmfroot = runCommand "${name}-texmfroot" { inherit texmfdist tlpkg; } ''
|
|
|
|
mkdir -p "$out"
|
|
|
|
ln -s "$texmfdist" "$out"/texmf-dist
|
|
|
|
ln -s "$tlpkg" "$out"/tlpkg
|
2024-05-02 00:46:19 +00:00
|
|
|
'';
|
2024-06-30 08:16:52 +00:00
|
|
|
|
|
|
|
# texlive.combine: expose info and man pages in usual /share/{info,man} location
|
|
|
|
doc = buildEnv {
|
|
|
|
name = "${name}-doc";
|
|
|
|
|
|
|
|
paths = [ (texmfdist.outPath + "/doc") ];
|
|
|
|
extraPrefix = "/share";
|
|
|
|
|
|
|
|
pathsToLink = [
|
|
|
|
"/info"
|
|
|
|
"/man"
|
|
|
|
];
|
|
|
|
};
|
|
|
|
|
|
|
|
meta = {
|
|
|
|
description =
|
|
|
|
"TeX Live environment"
|
|
|
|
+ lib.optionalString withDocs " with documentation"
|
|
|
|
+ lib.optionalString (withDocs && withSources) " and"
|
|
|
|
+ lib.optionalString withSources " with sources";
|
|
|
|
platforms = lib.platforms.all;
|
|
|
|
longDescription =
|
|
|
|
"Contains the following packages and their transitive dependencies:\n - "
|
|
|
|
+ lib.concatMapStringsSep "\n - " (
|
|
|
|
p:
|
|
|
|
p.pname + (lib.optionalString (p.outputSpecified or false) " (${p.tlOutputName or p.outputName})")
|
|
|
|
) (requiredTeXPackages tl);
|
|
|
|
};
|
|
|
|
|
|
|
|
# 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
|
|
|
|
nativeBuildInputs = [
|
|
|
|
(writeShellScript "force-output.sh" ''
|
|
|
|
export out="''${${outName}-}"
|
|
|
|
'')
|
|
|
|
];
|
|
|
|
inherit meta passthru;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
passthru = {
|
|
|
|
# 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) (lib.concatMap (
|
|
|
|
n: (pkgList.otherOutputs.${n} or [ ] ++ pkgList.specifiedOutputs.${n} or [ ])
|
|
|
|
)) pkgList.nonEnvOutputs
|
|
|
|
);
|
|
|
|
# useful for inclusion in the `fonts.packages` nixos option or for use in devshells
|
|
|
|
fonts = "${texmfroot}/texmf-dist/fonts";
|
|
|
|
# support variants attrs, (prev: attrs)
|
|
|
|
__overrideTeXConfig =
|
|
|
|
newArgs:
|
|
|
|
let
|
|
|
|
appliedArgs = if builtins.isFunction newArgs then newArgs args else newArgs;
|
|
|
|
in
|
2024-05-02 00:46:19 +00:00
|
|
|
self (args // { __fromCombineWrapper = false; } // appliedArgs);
|
2024-06-30 08:16:52 +00:00
|
|
|
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' {
|
|
|
|
|
|
|
|
inherit name;
|
|
|
|
|
|
|
|
# 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 ++ lib.optional __combine doc;
|
|
|
|
pathsToLink = [
|
|
|
|
"/"
|
|
|
|
"/share/texmf-var/scripts"
|
|
|
|
"/share/texmf-var/tex/generic/config"
|
|
|
|
"/share/texmf-var/web2c"
|
|
|
|
"/share/texmf-config"
|
|
|
|
"/bin" # ensure these are writeable directories
|
|
|
|
];
|
|
|
|
|
|
|
|
nativeBuildInputs = [
|
|
|
|
makeWrapper
|
|
|
|
libfaketime
|
|
|
|
tl."texlive.infra" # mktexlsr
|
|
|
|
tl.texlive-scripts # fmtutil, updmap
|
|
|
|
tl.texlive-scripts-extra # texlinks
|
|
|
|
perl
|
|
|
|
];
|
|
|
|
|
|
|
|
buildInputs = [
|
|
|
|
coreutils
|
|
|
|
gawk
|
|
|
|
gnugrep
|
|
|
|
gnused
|
|
|
|
] ++ lib.optional needsGhostscript ghostscript;
|
|
|
|
|
|
|
|
inherit meta passthru;
|
|
|
|
|
|
|
|
inherit texmfdist texmfroot;
|
|
|
|
|
|
|
|
fontconfigFile = makeFontsConf { fontDirectories = [ "${texmfroot}/texmf-dist/fonts" ]; };
|
|
|
|
|
|
|
|
fmtutilCnf = assembleConfigLines fmtutilLines pkgList.sortedFormatPkgs;
|
|
|
|
updmapCfg = assembleConfigLines updmapLines pkgList.sortedFontMaps;
|
|
|
|
|
|
|
|
languageDat = assembleConfigLines langDatLines pkgList.sortedHyphenPatterns;
|
|
|
|
languageDef = assembleConfigLines langDefLines pkgList.sortedHyphenPatterns;
|
|
|
|
languageLua = assembleConfigLines langLuaLines pkgList.sortedHyphenPatterns;
|
|
|
|
|
|
|
|
postactionScripts = builtins.catAttrs "postactionScript" pkgList.tlpkg;
|
|
|
|
|
|
|
|
postBuild = ''
|
|
|
|
. "${./build-tex-env.sh}"
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
in
|
2024-05-02 00:46:19 +00:00
|
|
|
# outputsToInstall must be set *after* overrideAttrs (used in buildEnv') or it fails the checkMeta tests
|
2024-06-30 08:16:52 +00:00
|
|
|
if __combine then out else lib.addMetaAttrs { inherit (pkgList) outputsToInstall; } out
|
|
|
|
)
|