Merge pull request #268619 from tweag/lib-descriptions
[NixPkgs.git] / pkgs / development / interpreters / php / generic.nix
blobba5ce5537bbbc97f2d06428845b9972036bfb80c
1 # We have tests for PCRE and PHP-FPM in nixos/tests/php/ or
2 # both in the same attribute named nixosTests.php
4 let
5   generic =
6     { callPackage
7     , lib
8     , stdenv
9     , nixosTests
10     , tests
11     , fetchurl
12     , makeBinaryWrapper
13     , symlinkJoin
14     , writeText
15     , autoconf
16     , automake
17     , bison
18     , flex
19     , libtool
20     , pkg-config
21     , re2c
22     , apacheHttpd
23     , libargon2
24     , libxml2
25     , pcre2
26     , systemd
27     , system-sendmail
28     , valgrind
29     , xcbuild
30     , writeShellScript
31     , common-updater-scripts
32     , curl
33     , jq
35     , version
36     , phpSrc ? null
37     , hash ? null
38     , extraPatches ? [ ]
39     , packageOverrides ? (final: prev: { })
40     , phpAttrsOverrides ? (attrs: { })
41     , pearInstallPhar ? (callPackage ./install-pear-nozlib-phar.nix { })
43       # Sapi flags
44     , cgiSupport ? true
45     , cliSupport ? true
46     , fpmSupport ? true
47     , pearSupport ? true
48     , pharSupport ? true
49     , phpdbgSupport ? true
51       # Misc flags
52     , apxs2Support ? false
53     , argon2Support ? true
54     , cgotoSupport ? false
55     , embedSupport ? false
56     , ipv6Support ? true
57     , systemdSupport ? lib.meta.availableOn stdenv.hostPlatform systemd
58     , valgrindSupport ? !stdenv.isDarwin && lib.meta.availableOn stdenv.hostPlatform valgrind
59     , ztsSupport ? apxs2Support
60     }@args:
62     let
63       # Compose two functions of the type expected by 'overrideAttrs'
64       # into one where changes made in the first are available to the second.
65       composeOverrides =
66         f: g: attrs:
67         let
68           fApplied = f attrs;
69           attrs' = attrs // fApplied;
70         in
71         fApplied // g attrs';
73       # buildEnv wraps php to provide additional extensions and
74       # configuration. Its usage is documented in
75       # doc/languages-frameworks/php.section.md.
76       #
77       # Create a buildEnv with earlier overridden values and
78       # extensions functions in its closure. This is necessary for
79       # consecutive calls to buildEnv and overrides to work as
80       # expected.
81       mkBuildEnv = prevArgs: prevExtensionFunctions: lib.makeOverridable (
82         { extensions ? ({ enabled, ... }: enabled), extraConfig ? "", ... }@innerArgs:
83         let
84           allArgs = args // prevArgs // innerArgs;
85           filteredArgs = builtins.removeAttrs allArgs [ "extensions" "extraConfig" ];
86           php = generic filteredArgs;
88           php-packages = (callPackage ../../../top-level/php-packages.nix {
89             phpPackage = phpWithExtensions;
90           }).overrideScope packageOverrides;
92           allExtensionFunctions = prevExtensionFunctions ++ [ extensions ];
93           enabledExtensions =
94             builtins.foldl'
95               (enabled: f:
96                 f { inherit enabled; all = php-packages.extensions; })
97               [ ]
98               allExtensionFunctions;
100           getExtName = ext: ext.extensionName;
102           # Recursively get a list of all internal dependencies
103           # for a list of extensions.
104           getDepsRecursively = extensions:
105             let
106               deps = lib.concatMap
107                 (ext: (ext.internalDeps or [ ]) ++ (ext.peclDeps or [ ]))
108                 extensions;
109             in
110             if ! (deps == [ ]) then
111               deps ++ (getDepsRecursively deps)
112             else
113               deps;
115           # Generate extension load configuration snippets from the
116           # extension parameter. This is an attrset suitable for use
117           # with textClosureList, which is used to put the strings in
118           # the right order - if a plugin which is dependent on
119           # another plugin is placed before its dependency, it will
120           # fail to load.
121           extensionTexts =
122             lib.listToAttrs
123               (map
124                 (ext:
125                   let
126                     extName = getExtName ext;
127                     phpDeps = (ext.internalDeps or [ ]) ++ (ext.peclDeps or [ ]);
128                     type = "${lib.optionalString (ext.zendExtension or false) "zend_"}extension";
129                   in
130                   lib.nameValuePair extName {
131                     text = "${type}=${ext}/lib/php/extensions/${extName}.so";
132                     deps = map getExtName phpDeps;
133                   })
134                 (enabledExtensions ++ (getDepsRecursively enabledExtensions)));
136           extNames = map getExtName enabledExtensions;
137           extraInit = writeText "php-extra-init-${version}.ini" ''
138             ${lib.concatStringsSep "\n"
139               (lib.textClosureList extensionTexts extNames)}
140             ${extraConfig}
141           '';
143           phpWithExtensions = symlinkJoin {
144             name = "php-with-extensions-${version}";
145             inherit (php) version;
146             nativeBuildInputs = [ makeBinaryWrapper ];
147             passthru = php.passthru // {
148               buildEnv = mkBuildEnv allArgs allExtensionFunctions;
149               withExtensions = mkWithExtensions allArgs allExtensionFunctions;
150               overrideAttrs =
151                 f:
152                 let
153                   newPhpAttrsOverrides = composeOverrides (filteredArgs.phpAttrsOverrides or (attrs: { })) f;
154                   php = generic (filteredArgs // { phpAttrsOverrides = newPhpAttrsOverrides; });
155                 in
156                 php.buildEnv { inherit extensions extraConfig; };
157               phpIni = "${phpWithExtensions}/lib/php.ini";
158               unwrapped = php;
159               # Select the right php tests for the php version
160               tests = {
161                 nixos = lib.recurseIntoAttrs nixosTests."php${lib.strings.replaceStrings [ "." ] [ "" ] (lib.versions.majorMinor php.version)}";
162                 package = tests.php;
163               };
164               inherit (php-packages) extensions buildPecl mkComposerRepository buildComposerProject composerHooks mkExtension;
165               packages = php-packages.tools;
166               meta = php.meta // {
167                 outputsToInstall = [ "out" ];
168               };
169             };
170             paths = [ php ];
171             postBuild = ''
172               ln -s ${extraInit} $out/lib/php.ini
174               if test -e $out/bin/php; then
175                 wrapProgram $out/bin/php --set-default PHP_INI_SCAN_DIR $out/lib
176               fi
178               if test -e $out/bin/php-fpm; then
179                 wrapProgram $out/bin/php-fpm --set-default PHP_INI_SCAN_DIR $out/lib
180               fi
182               if test -e $out/bin/phpdbg; then
183                 wrapProgram $out/bin/phpdbg --set-default PHP_INI_SCAN_DIR $out/lib
184               fi
186               if test -e $out/bin/php-cgi; then
187                 wrapProgram $out/bin/php-cgi --set-default PHP_INI_SCAN_DIR $out/lib
188               fi
189             '';
190           };
191         in
192         phpWithExtensions
193       );
195       mkWithExtensions = prevArgs: prevExtensionFunctions: extensions:
196         mkBuildEnv prevArgs prevExtensionFunctions { inherit extensions; };
198       defaultPhpSrc = fetchurl {
199         url = "https://www.php.net/distributions/php-${version}.tar.bz2";
200         inherit hash;
201       };
202     in
203     stdenv.mkDerivation (
204       let
205         attrs = {
206           pname = "php";
208           inherit version;
210           enableParallelBuilding = true;
212           nativeBuildInputs = [ autoconf automake bison flex libtool pkg-config re2c ]
213             ++ lib.optional stdenv.isDarwin xcbuild;
215           buildInputs =
216             # PCRE extension
217             [ pcre2 ]
219             # Enable sapis
220             ++ lib.optionals pearSupport [ libxml2.dev ]
222             # Misc deps
223             ++ lib.optional apxs2Support apacheHttpd
224             ++ lib.optional argon2Support libargon2
225             ++ lib.optional systemdSupport systemd
226             ++ lib.optional valgrindSupport valgrind
227           ;
229           CXXFLAGS = lib.optionalString stdenv.cc.isClang "-std=c++11";
230           SKIP_PERF_SENSITIVE = 1;
232           configureFlags =
233             # Disable all extensions
234             [ "--disable-all" ]
236             # PCRE
237             ++ [ "--with-external-pcre=${pcre2.dev}" ]
240             # Enable sapis
241             ++ lib.optional (!cgiSupport) "--disable-cgi"
242             ++ lib.optional (!cliSupport) "--disable-cli"
243             ++ lib.optional fpmSupport "--enable-fpm"
244             ++ lib.optionals pearSupport [ "--with-pear" "--enable-xml" "--with-libxml" ]
245             ++ lib.optional pharSupport "--enable-phar"
246             ++ lib.optional (!phpdbgSupport) "--disable-phpdbg"
249             # Misc flags
250             ++ lib.optional apxs2Support "--with-apxs2=${apacheHttpd.dev}/bin/apxs"
251             ++ lib.optional argon2Support "--with-password-argon2=${libargon2}"
252             ++ lib.optional cgotoSupport "--enable-re2c-cgoto"
253             ++ lib.optional embedSupport "--enable-embed"
254             ++ lib.optional (!ipv6Support) "--disable-ipv6"
255             ++ lib.optional systemdSupport "--with-fpm-systemd"
256             ++ lib.optional valgrindSupport "--with-valgrind=${valgrind.dev}"
257             ++ lib.optional ztsSupport "--enable-zts"
260             # Sendmail
261             ++ [ "PROG_SENDMAIL=${system-sendmail}/bin/sendmail" ]
262           ;
264           hardeningDisable = [ "bindnow" ];
266           preConfigure =
267             # Don't record the configure flags since this causes unnecessary
268             # runtime dependencies
269             ''
270               for i in main/build-defs.h.in scripts/php-config.in; do
271                 substituteInPlace $i \
272                   --replace '@CONFIGURE_COMMAND@' '(omitted)' \
273                   --replace '@CONFIGURE_OPTIONS@' "" \
274                   --replace '@PHP_LDFLAGS@' ""
275               done
277               export EXTENSION_DIR=$out/lib/php/extensions
279               ./buildconf --copy --force
281               if [ -f "scripts/dev/genfiles" ]; then
282                 ./scripts/dev/genfiles
283               fi
284             '' + lib.optionalString stdenv.isDarwin ''
285               substituteInPlace configure --replace "-lstdc++" "-lc++"
286             '';
288           # When compiling PHP sources from Github, this file is missing and we
289           # need to install it ourselves.
290           # On the other hand, a distribution includes this file by default.
291           preInstall = ''
292             if [[ ! -f ./pear/install-pear-nozlib.phar ]]; then
293               cp ${pearInstallPhar} ./pear/install-pear-nozlib.phar
294             fi
295           '';
297           postInstall = ''
298             test -d $out/etc || mkdir $out/etc
299             cp php.ini-production $out/etc/php.ini
300           '';
302           postFixup = ''
303             mkdir -p $dev/bin $dev/share/man/man1
304             mv $out/bin/phpize $out/bin/php-config $dev/bin/
305             mv $out/share/man/man1/phpize.1.gz \
306                $out/share/man/man1/php-config.1.gz \
307                $dev/share/man/man1/
308           '';
310           src = if phpSrc == null then defaultPhpSrc else phpSrc;
312           patches = [ ./fix-paths-php7.patch ] ++ extraPatches;
314           separateDebugInfo = true;
316           outputs = [ "out" "dev" ];
318           passthru = {
319             updateScript =
320               let
321                 script = writeShellScript "php${lib.versions.major version}${lib.versions.minor version}-update-script" ''
322                   set -o errexit
323                   PATH=${lib.makeBinPath [ common-updater-scripts curl jq ]}
324                   new_version=$(curl --silent "https://www.php.net/releases/active" | jq --raw-output '."${lib.versions.major version}"."${lib.versions.majorMinor version}".version')
325                   update-source-version "$UPDATE_NIX_ATTR_PATH.unwrapped" "$new_version" "--file=$1"
326                 '';
327               in [
328                 script
329                 # Passed as an argument so that update.nix can ensure it does not become a store path.
330                 (./. + "/${lib.versions.majorMinor version}.nix")
331               ];
332             buildEnv = mkBuildEnv { } [ ];
333             withExtensions = mkWithExtensions { } [ ];
334             overrideAttrs =
335               f:
336               let
337                 newPhpAttrsOverrides = composeOverrides phpAttrsOverrides f;
338                 php = generic (args // { phpAttrsOverrides = newPhpAttrsOverrides; });
339               in
340               php;
341             inherit ztsSupport;
342           };
344           meta = with lib; {
345             description = "An HTML-embedded scripting language";
346             homepage = "https://www.php.net/";
347             license = licenses.php301;
348             mainProgram = "php";
349             maintainers = teams.php.members;
350             platforms = platforms.all;
351             outputsToInstall = [ "out" "dev" ];
352           };
353         };
354       in
355       attrs // phpAttrsOverrides attrs
356     );
358 generic