btrbk: add mainProgram (#356350)
[NixPkgs.git] / pkgs / development / interpreters / php / generic.nix
blob13e10103f54f921c004cde257c637b7a28abddc3
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     , staticSupport ? false
57     , ipv6Support ? true
58     , zendSignalsSupport ? true
59     , zendMaxExecutionTimersSupport ? false
60     , systemdSupport ? lib.meta.availableOn stdenv.hostPlatform systemd
61     , valgrindSupport ? !stdenv.hostPlatform.isDarwin && lib.meta.availableOn stdenv.hostPlatform valgrind
62     , ztsSupport ? apxs2Support
63     }@args:
65     let
66       # Compose two functions of the type expected by 'overrideAttrs'
67       # into one where changes made in the first are available to the second.
68       composeOverrides =
69         f: g: attrs:
70         let
71           fApplied = f attrs;
72           attrs' = attrs // fApplied;
73         in
74         fApplied // g attrs';
76       # buildEnv wraps php to provide additional extensions and
77       # configuration. Its usage is documented in
78       # doc/languages-frameworks/php.section.md.
79       #
80       # Create a buildEnv with earlier overridden values and
81       # extensions functions in its closure. This is necessary for
82       # consecutive calls to buildEnv and overrides to work as
83       # expected.
84       mkBuildEnv = prevArgs: prevExtensionFunctions: lib.makeOverridable (
85         { extensions ? ({ enabled, ... }: enabled), extraConfig ? "", ... }@innerArgs:
86         let
87           allArgs = args // prevArgs // innerArgs;
88           filteredArgs = builtins.removeAttrs allArgs [ "extensions" "extraConfig" ];
89           php = generic filteredArgs;
91           php-packages = (callPackage ../../../top-level/php-packages.nix {
92             phpPackage = phpWithExtensions;
93           }).overrideScope packageOverrides;
95           allExtensionFunctions = prevExtensionFunctions ++ [ extensions ];
96           enabledExtensions =
97             builtins.foldl'
98               (enabled: f:
99                 f { inherit enabled; all = php-packages.extensions; })
100               [ ]
101               allExtensionFunctions;
103           getExtName = ext: ext.extensionName;
105           # Recursively get a list of all internal dependencies
106           # for a list of extensions.
107           getDepsRecursively = extensions:
108             let
109               deps = lib.concatMap
110                 (ext: (ext.internalDeps or [ ]) ++ (ext.peclDeps or [ ]))
111                 extensions;
112             in
113             if ! (deps == [ ]) then
114               deps ++ (getDepsRecursively deps)
115             else
116               deps;
118           # Generate extension load configuration snippets from the
119           # extension parameter. This is an attrset suitable for use
120           # with textClosureList, which is used to put the strings in
121           # the right order - if a plugin which is dependent on
122           # another plugin is placed before its dependency, it will
123           # fail to load.
124           extensionTexts =
125             lib.listToAttrs
126               (map
127                 (ext:
128                   let
129                     extName = getExtName ext;
130                     phpDeps = (ext.internalDeps or [ ]) ++ (ext.peclDeps or [ ]);
131                     type = "${lib.optionalString (ext.zendExtension or false) "zend_"}extension";
132                   in
133                   lib.nameValuePair extName {
134                     text = "${type}=${ext}/lib/php/extensions/${extName}.so";
135                     deps = map getExtName phpDeps;
136                   })
137                 (enabledExtensions ++ (getDepsRecursively enabledExtensions)));
139           extNames = map getExtName enabledExtensions;
140           extraInit = writeText "php-extra-init-${version}.ini" ''
141             ${lib.concatStringsSep "\n"
142               (lib.textClosureList extensionTexts extNames)}
143             ${extraConfig}
144           '';
146           phpWithExtensions = symlinkJoin {
147             name = "php-with-extensions-${version}";
148             inherit (php) version;
149             nativeBuildInputs = [ makeBinaryWrapper ];
150             passthru = php.passthru // {
151               buildEnv = mkBuildEnv allArgs allExtensionFunctions;
152               withExtensions = mkWithExtensions allArgs allExtensionFunctions;
153               overrideAttrs =
154                 f:
155                 let
156                   newPhpAttrsOverrides = composeOverrides (filteredArgs.phpAttrsOverrides or (attrs: { })) f;
157                   php = generic (filteredArgs // { phpAttrsOverrides = newPhpAttrsOverrides; });
158                 in
159                 php.buildEnv { inherit extensions extraConfig; };
160               phpIni = "${phpWithExtensions}/lib/php.ini";
161               unwrapped = php;
162               # Select the right php tests for the php version
163               tests = {
164                 nixos = lib.recurseIntoAttrs nixosTests."php${lib.strings.replaceStrings [ "." ] [ "" ] (lib.versions.majorMinor php.version)}";
165                 package = tests.php;
166               };
167               inherit (php-packages) extensions buildPecl mkComposerRepository mkComposerVendor buildComposerProject buildComposerProject2 buildComposerWithPlugin composerHooks composerHooks2 mkExtension;
168               packages = php-packages.tools;
169               meta = php.meta // {
170                 outputsToInstall = [ "out" ];
171               };
172             };
173             paths = [ php ];
174             postBuild = ''
175               ln -s ${extraInit} $out/lib/php.ini
177               if test -e $out/bin/php; then
178                 wrapProgram $out/bin/php --set-default PHP_INI_SCAN_DIR $out/lib
179               fi
181               if test -e $out/bin/php-fpm; then
182                 wrapProgram $out/bin/php-fpm --set-default PHP_INI_SCAN_DIR $out/lib
183               fi
185               if test -e $out/bin/phpdbg; then
186                 wrapProgram $out/bin/phpdbg --set-default PHP_INI_SCAN_DIR $out/lib
187               fi
189               if test -e $out/bin/php-cgi; then
190                 wrapProgram $out/bin/php-cgi --set-default PHP_INI_SCAN_DIR $out/lib
191               fi
192             '';
193           };
194         in
195         phpWithExtensions
196       );
198       mkWithExtensions = prevArgs: prevExtensionFunctions: extensions:
199         mkBuildEnv prevArgs prevExtensionFunctions { inherit extensions; };
201       defaultPhpSrc = fetchurl {
202         url = "https://www.php.net/distributions/php-${version}.tar.bz2";
203         inherit hash;
204       };
205     in
206     stdenv.mkDerivation (
207       let
208         attrs = {
209           pname = "php";
211           inherit version;
213           enableParallelBuilding = true;
215           nativeBuildInputs = [ autoconf automake bison flex libtool pkg-config re2c ]
216             ++ lib.optional stdenv.hostPlatform.isDarwin xcbuild;
218           buildInputs =
219             # PCRE extension
220             [ pcre2 ]
222             # Enable sapis
223             ++ lib.optionals pearSupport [ libxml2.dev ]
225             # Misc deps
226             ++ lib.optional apxs2Support apacheHttpd
227             ++ lib.optional argon2Support libargon2
228             ++ lib.optional systemdSupport systemd
229             ++ lib.optional valgrindSupport valgrind
230           ;
232           CXXFLAGS = lib.optionalString stdenv.cc.isClang "-std=c++11";
233           SKIP_PERF_SENSITIVE = 1;
235           configureFlags =
236             # Disable all extensions
237             [ "--disable-all" ]
239             # PCRE
240             ++ [ "--with-external-pcre=${pcre2.dev}" ]
242             # Enable sapis
243             ++ lib.optional (!cgiSupport) "--disable-cgi"
244             ++ lib.optional (!cliSupport) "--disable-cli"
245             ++ lib.optional fpmSupport "--enable-fpm"
246             ++ lib.optionals pearSupport [ "--with-pear" "--enable-xml" "--with-libxml" ]
247             ++ lib.optional pharSupport "--enable-phar"
248             ++ lib.optional (!phpdbgSupport) "--disable-phpdbg"
251             # Misc flags
252             ++ lib.optional apxs2Support "--with-apxs2=${apacheHttpd.dev}/bin/apxs"
253             ++ lib.optional argon2Support "--with-password-argon2=${libargon2}"
254             ++ lib.optional cgotoSupport "--enable-re2c-cgoto"
255             ++ lib.optional embedSupport "--enable-embed${lib.optionalString staticSupport "=static"}"
256             ++ lib.optional (!ipv6Support) "--disable-ipv6"
257             ++ lib.optional systemdSupport "--with-fpm-systemd"
258             ++ lib.optional valgrindSupport "--with-valgrind=${valgrind.dev}"
259             ++ lib.optional ztsSupport "--enable-zts"
260             ++ lib.optional staticSupport "--enable-static"
261             ++ lib.optional (!zendSignalsSupport) ["--disable-zend-signals"]
262             ++ lib.optional zendMaxExecutionTimersSupport "--enable-zend-max-execution-timers"
265             # Sendmail
266             ++ [ "PROG_SENDMAIL=${system-sendmail}/bin/sendmail" ]
267           ;
269           hardeningDisable = [ "bindnow" ];
271           preConfigure =
272             # Don't record the configure flags since this causes unnecessary
273             # runtime dependencies
274             ''
275               substituteInPlace main/build-defs.h.in \
276                 --replace-fail '@CONFIGURE_COMMAND@' '(omitted)'
277               substituteInPlace scripts/php-config.in \
278                 --replace-fail '@CONFIGURE_OPTIONS@' "" \
279                 --replace-fail '@PHP_LDFLAGS@' ""
281               export EXTENSION_DIR=$out/lib/php/extensions
283               ./buildconf --copy --force
285               if [ -f "scripts/dev/genfiles" ]; then
286                 ./scripts/dev/genfiles
287               fi
288             '' + lib.optionalString stdenv.hostPlatform.isDarwin ''
289               substituteInPlace configure --replace-fail "-lstdc++" "-lc++"
290             '';
292           # When compiling PHP sources from Github, this file is missing and we
293           # need to install it ourselves.
294           # On the other hand, a distribution includes this file by default.
295           preInstall = ''
296             if [[ ! -f ./pear/install-pear-nozlib.phar ]]; then
297               cp ${pearInstallPhar} ./pear/install-pear-nozlib.phar
298             fi
299           '';
301           postInstall = ''
302             test -d $out/etc || mkdir $out/etc
303             cp php.ini-production $out/etc/php.ini
304           '';
306           postFixup = ''
307             mkdir -p $dev/bin $dev/share/man/man1
308             mv $out/bin/phpize $out/bin/php-config $dev/bin/
309             mv $out/share/man/man1/phpize.1.gz \
310                $out/share/man/man1/php-config.1.gz \
311                $dev/share/man/man1/
312           '';
314           src = if phpSrc == null then defaultPhpSrc else phpSrc;
316           patches = lib.optionals (lib.versionOlder version "8.4")  [
317             ./fix-paths-php7.patch
318           ] ++ lib.optionals (lib.versionAtLeast version "8.4") [
319             ./fix-paths-php84.patch
320           ] ++ extraPatches;
322           separateDebugInfo = true;
324           outputs = [ "out" "dev" ];
326           passthru = {
327             updateScript =
328               let
329                 script = writeShellScript "php${lib.versions.major version}${lib.versions.minor version}-update-script" ''
330                   set -o errexit
331                   PATH=${lib.makeBinPath [ common-updater-scripts curl jq ]}
332                   new_version=$(curl --silent "https://www.php.net/releases/active" | jq --raw-output '."${lib.versions.major version}"."${lib.versions.majorMinor version}".version')
333                   update-source-version "$UPDATE_NIX_ATTR_PATH.unwrapped" "$new_version" "--file=$1"
334                 '';
335               in [
336                 script
337                 # Passed as an argument so that update.nix can ensure it does not become a store path.
338                 (./. + "/${lib.versions.majorMinor version}.nix")
339               ];
340             buildEnv = mkBuildEnv { } [ ];
341             withExtensions = mkWithExtensions { } [ ];
342             overrideAttrs =
343               f:
344               let
345                 newPhpAttrsOverrides = composeOverrides phpAttrsOverrides f;
346                 php = generic (args // { phpAttrsOverrides = newPhpAttrsOverrides; });
347               in
348               php;
349             inherit ztsSupport;
350           };
352           meta = with lib; {
353             description = "HTML-embedded scripting language";
354             homepage = "https://www.php.net/";
355             license = licenses.php301;
356             mainProgram = "php";
357             maintainers = teams.php.members;
358             platforms = platforms.all;
359             outputsToInstall = [ "out" "dev" ];
360           };
361         };
362       in
363       attrs // phpAttrsOverrides attrs
364     );
366 generic