about summary refs log tree commit diff
path: root/pkgs/tools/typesetting/tex/texlive/build-texlive-package.nix
blob: c5a55cf0f9c96c5934c42d6ec10461ee8b24be6b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
{ lib
, fetchurl
, runCommand
, writeShellScript

  # script interpreters
, bash
, jdk
, perl
, python3
, ruby
, snobol4
, tk

  # TeX Live prerequisites
, texliveBinaries
}:

/* Convert an attribute set extracted from tlpdb.nix (with the deps attribute
  already processed) to a fake multi-output derivation with possible outputs
  [ "tex" "texdoc" "texsource" "tlpkg" "out" "man" "info" ]

  The multi-output is emulated as follows:
  - the main derivation is a multi-output derivation that builds links to the
    containers (tex, texdoc, ...)
  - the output attributes are replaced with the actual containers with the
    outputSpecified attribute set to true

  In this way, when texlive.withPackages picks an output such as drv.tex, it
  receives the actual container, avoiding superfluous dependencies on the other
  containers (for instance doc containers).
*/

# TODO stabilise a generic interface decoupled from the finer details of the
# translation from texlive.tlpdb to tlpdb.nix
{ pname
, revision
, version ? toString revision
, extraRevision ? ""
, extraVersion ? ""
, sha512
, mirrors
, fixedHashes ? { }
, postUnpack ? ""
, postFixup ? ""
, stripPrefix ? 1
, license ? [ ]
, hasHyphens ? false
, hasInfo ? false
, hasManpages ? false
, hasRunfiles ? false
, hasTlpkg ? false
, extraNativeBuildInputs ? [ ]
, ...
}@args:

let
  # common metadata
  name = "${pname}-${version}${extraVersion}";
  meta = {
    license = map (x: lib.licenses.${x}) license;
    # TeX Live packages should not be installed directly into the user profile
    outputsToInstall = [ ];
    longDescription = ''
      This package cannot be installed directly. Please use `texlive.withPackages`.
    '';
    # discourage nix-env from matching this package
    priority = 10;
  } // lib.optionalAttrs (args ? shortdesc) {
    description = args.shortdesc;
  };

  hasBinfiles = args ? binfiles && args.binfiles != [ ];
  hasDocfiles = sha512 ? doc;
  hasSource = sha512 ? source;

  # containers that will be built by Hydra
  outputs = lib.optional hasBinfiles "out" ++
    lib.optional hasRunfiles "tex" ++
    lib.optional hasDocfiles "texdoc" ++
    # omit building sources, since as far as we know, installing them is not common
    # the sources will still be available under drv.texsource
    # lib.optional hasSource "texsource" ++
    lib.optional hasTlpkg "tlpkg" ++
    lib.optional hasManpages "man" ++
    lib.optional hasInfo "info";
  outputDrvs = lib.getAttrs outputs containers;

  passthru = {
    # metadata
    inherit pname;
    revision = toString revision + extraRevision;
    version = version + extraVersion;
    # containers behave like specified outputs
    outputSpecified = true;
  } // lib.optionalAttrs (args ? deps) { tlDeps = args.deps; }
  // lib.optionalAttrs (args ? fontMaps) { inherit (args) fontMaps; }
  // lib.optionalAttrs (args ? formats) { inherit (args) formats; }
  // lib.optionalAttrs (args ? hyphenPatterns) { inherit (args) hyphenPatterns; }
  // lib.optionalAttrs (args ? postactionScript) { inherit (args) postactionScript; }
  // lib.optionalAttrs hasSource { inherit (containers) texsource; }
  // lib.optionalAttrs (! hasRunfiles) { tex = fakeTeX; };

  # build run, doc, source, tlpkg containers
  mkContainer = tlType: tlOutputName: sha512:
    let
      fixedHash = fixedHashes.${tlType} or null; # be graceful about missing hashes
      # the basename used by upstream (without ".tar.xz" suffix)
      # tlpkg is not a true container but a subfolder of the run container
      urlName = pname + (lib.optionalString (tlType != "run" && tlType != "tlpkg") ".${tlType}");
      urls = map (up: "${up}/archive/${urlName}.r${toString revision}.tar.xz") mirrors;
      container = runCommand "${name}-${tlOutputName}"
        ({
          src = fetchurl { inherit urls sha512; };
          inherit passthru;
          # save outputName, since fixed output derivations cannot change nor override outputName
          inherit meta stripPrefix tlOutputName;
        } // lib.optionalAttrs (fixedHash != null) {
          outputHash = fixedHash;
          outputHashAlgo = "sha256";
          outputHashMode = "recursive";
        })
        (''
          mkdir "$out"
          if [[ "$tlOutputName"  == "tlpkg" ]]; then
            tar -xf "$src" \
              --strip-components=1 \
              -C "$out" --anchored --exclude=tlpkg/tlpobj --keep-old-files \
              tlpkg
          else
            tar -xf "$src" \
              --strip-components="$stripPrefix" \
              -C "$out" --anchored --exclude=tlpkg --keep-old-files
          fi
        '' + postUnpack);
    in
    # remove drv.out to avoid confusing texlive.withPackages
    removeAttrs container [ "out" ]
    // outputDrvs;

  # find interpreters for the script extensions found in tlpdb
  extToInput = {
    jar = jdk;
    lua = texliveBinaries.luatex;
    py = python3;
    rb = ruby;
    sno = snobol4;
    tcl = tk;
    texlua = texliveBinaries.luatex;
    tlu = texliveBinaries.luatex;
  };

  # fake derivation for resolving dependencies in the absence of a "tex" containers
  fakeTeX = passthru
    // { inherit meta; tlOutputName = "tex"; }
    // outputDrvs;

  containers = rec {
    tex = mkContainer "run" "tex" sha512.run;
    texdoc = mkContainer "doc" "texdoc" sha512.doc;
    texsource = mkContainer "source" "texsource" sha512.source;
    tlpkg = mkContainer "tlpkg" "tlpkg" sha512.run;

    # bin container
    out = runCommand "${name}"
      {
        inherit meta;
        passthru = passthru // { tlOutputName = "out"; };
        # shebang interpreters
        buildInputs = let outName = builtins.replaceStrings [ "-" ] [ "_" ] pname; in
          [
            texliveBinaries.core.${outName} or null
            texliveBinaries.${pname} or null
            texliveBinaries.core-big.${outName} or null
          ]
          ++ (args.extraBuildInputs or [ ]) ++ [ bash perl ]
          ++ (lib.attrVals (args.scriptExts or [ ]) extToInput);
        nativeBuildInputs = extraNativeBuildInputs;
        # absolute scripts folder
        scriptsFolder = lib.optionals (hasRunfiles && tex ? outPath) (map (f: tex.outPath + "/scripts/" + f) (lib.toList args.scriptsFolder or pname));
        # binaries info
        inherit (args) binfiles;
        binlinks = builtins.attrNames (args.binlinks or { });
        bintargets = builtins.attrValues (args.binlinks or { });
        # build scripts
        patchScripts = ./patch-scripts.sed;
        makeBinContainers = ./make-bin-containers.sh;
      }
      ''
        . "$makeBinContainers"
        ${args.postFixup or ""}
      '' // outputDrvs;

    # build man, info containers
    man = removeAttrs
      (runCommand "${name}-man"
        {
          inherit meta texdoc;
          passthru = passthru // { tlOutputName = "man"; };
        }
        ''
          mkdir -p "$out"/share
          ln -s {"$texdoc"/doc,"$out"/share}/man
        '') [ "out" ] // outputDrvs;

    info = removeAttrs
      (runCommand "${name}-info"
        {
          inherit meta texdoc;
          passthru = passthru // { tlOutputName = "info"; };
        }
        ''
          mkdir -p "$out"/share
          ln -s {"$texdoc"/doc,"$out"/share}/info
        '') [ "out" ] // outputDrvs;
  };
in
if outputs == [ ] then removeAttrs fakeTeX [ "outputSpecified" ] else
runCommand name
  {
    __structuredAttrs = true;
    inherit meta outputDrvs outputs;
    passthru = removeAttrs passthru [ "outputSpecified" ];

    # force output name in case "out" is missing
    nativeBuildInputs = lib.optional (! hasBinfiles)
      (writeShellScript "force-output.sh" ''
        export out="''${${builtins.head outputs}-}"
      '');
  }
  ''
    for outputName in ''${!outputs[@]} ; do
      ln -s "''${outputDrvs[$outputName]}" "''${outputs[$outputName]}"
    done
  '' // outputDrvs