at master 15 kB view raw
1# We have tests for PCRE and PHP-FPM in nixos/tests/php/ or 2# both in the same attribute named nixosTests.php 3 4let 5 generic = 6 { 7 callPackage, 8 lib, 9 stdenv, 10 nixosTests, 11 tests, 12 fetchurl, 13 makeBinaryWrapper, 14 symlinkJoin, 15 writeText, 16 autoconf, 17 automake, 18 bison, 19 flex, 20 libtool, 21 pkg-config, 22 re2c, 23 apacheHttpd, 24 libargon2, 25 libxml2, 26 pcre2, 27 systemdLibs, 28 system-sendmail, 29 valgrind, 30 xcbuild, 31 writeShellScript, 32 common-updater-scripts, 33 curl, 34 jq, 35 coreutils, 36 formats, 37 38 version, 39 phpSrc ? null, 40 hash ? null, 41 extraPatches ? [ ], 42 packageOverrides ? (final: prev: { }), 43 phpAttrsOverrides ? (final: prev: { }), 44 pearInstallPhar ? (callPackage ./install-pear-nozlib-phar.nix { }), 45 46 # Sapi flags 47 cgiSupport ? true, 48 cliSupport ? true, 49 fpmSupport ? true, 50 pearSupport ? true, 51 pharSupport ? true, 52 phpdbgSupport ? true, 53 54 # Misc flags 55 apxs2Support ? false, 56 argon2Support ? true, 57 cgotoSupport ? false, 58 embedSupport ? false, 59 staticSupport ? false, 60 ipv6Support ? true, 61 zendSignalsSupport ? true, 62 zendMaxExecutionTimersSupport ? false, 63 systemdSupport ? lib.meta.availableOn stdenv.hostPlatform systemdLibs, 64 valgrindSupport ? 65 !stdenv.hostPlatform.isDarwin && lib.meta.availableOn stdenv.hostPlatform valgrind, 66 ztsSupport ? apxs2Support, 67 }@args: 68 69 let 70 # buildEnv wraps php to provide additional extensions and 71 # configuration. Its usage is documented in 72 # doc/languages-frameworks/php.section.md. 73 # 74 # Create a buildEnv with earlier overridden values and 75 # extensions functions in its closure. This is necessary for 76 # consecutive calls to buildEnv and overrides to work as 77 # expected. 78 mkBuildEnv = 79 prevArgs: prevExtensionFunctions: 80 lib.makeOverridable ( 81 { 82 extensions ? ({ enabled, ... }: enabled), 83 extraConfig ? "", 84 ... 85 }@innerArgs: 86 let 87 allArgs = args // prevArgs // innerArgs; 88 filteredArgs = builtins.removeAttrs allArgs [ 89 "extensions" 90 "extraConfig" 91 ]; 92 php = generic filteredArgs; 93 94 php-packages = 95 (callPackage ../../../top-level/php-packages.nix { 96 phpPackage = phpWithExtensions; 97 }).overrideScope 98 packageOverrides; 99 100 allExtensionFunctions = prevExtensionFunctions ++ [ extensions ]; 101 enabledExtensions = builtins.foldl' ( 102 enabled: f: 103 f { 104 inherit enabled; 105 all = php-packages.extensions; 106 } 107 ) [ ] allExtensionFunctions; 108 109 getExtName = ext: ext.extensionName; 110 111 # Recursively get a list of all internal dependencies 112 # for a list of extensions. 113 getDepsRecursively = 114 extensions: 115 let 116 deps = lib.concatMap (ext: (ext.internalDeps or [ ]) ++ (ext.peclDeps or [ ])) extensions; 117 in 118 if !(deps == [ ]) then deps ++ (getDepsRecursively deps) else deps; 119 120 # Generate extension load configuration snippets from the 121 # extension parameter. This is an attrset suitable for use 122 # with textClosureList, which is used to put the strings in 123 # the right order - if a plugin which is dependent on 124 # another plugin is placed before its dependency, it will 125 # fail to load. 126 extensionTexts = lib.listToAttrs ( 127 map ( 128 ext: 129 let 130 extName = getExtName ext; 131 phpDeps = (ext.internalDeps or [ ]) ++ (ext.peclDeps or [ ]); 132 type = "${lib.optionalString (ext.zendExtension or false) "zend_"}extension"; 133 in 134 lib.nameValuePair extName { 135 text = "${type}=${ext}/lib/php/extensions/${extName}.so"; 136 deps = map getExtName phpDeps; 137 } 138 ) (enabledExtensions ++ (getDepsRecursively enabledExtensions)) 139 ); 140 141 extNames = map getExtName enabledExtensions; 142 extraInit = writeText "php-extra-init-${version}.ini" '' 143 ${lib.concatStringsSep "\n" (lib.textClosureList extensionTexts extNames)} 144 ${extraConfig} 145 ''; 146 147 phpWithExtensions = symlinkJoin { 148 name = "php-with-extensions-${version}"; 149 inherit (php) version; 150 nativeBuildInputs = [ makeBinaryWrapper ]; 151 passthru = php.passthru // { 152 buildEnv = mkBuildEnv allArgs allExtensionFunctions; 153 withExtensions = mkWithExtensions allArgs allExtensionFunctions; 154 overrideAttrs = 155 f: 156 let 157 phpAttrsOverrides = filteredArgs.phpAttrsOverrides or (final: prev: { }); 158 newPhpAttrsOverrides = lib.composeExtensions (lib.toExtension phpAttrsOverrides) ( 159 lib.toExtension f 160 ); 161 php = generic (filteredArgs // { phpAttrsOverrides = newPhpAttrsOverrides; }); 162 in 163 php.buildEnv { inherit extensions extraConfig; }; 164 phpIni = "${phpWithExtensions}/lib/php.ini"; 165 unwrapped = php; 166 # Select the right php tests for the php version 167 tests = { 168 nixos = 169 lib.recurseIntoAttrs 170 nixosTests."php${lib.strings.replaceStrings [ "." ] [ "" ] (lib.versions.majorMinor php.version)}"; 171 package = tests.php; 172 }; 173 inherit (php-packages) 174 extensions 175 buildPecl 176 mkComposerRepository 177 mkComposerVendor 178 buildComposerProject 179 buildComposerProject2 180 buildComposerWithPlugin 181 composerHooks 182 composerHooks2 183 mkExtension 184 ; 185 packages = php-packages.tools; 186 meta = php.meta // { 187 outputsToInstall = [ "out" ]; 188 }; 189 }; 190 paths = [ php ]; 191 postBuild = '' 192 ln -s ${extraInit} $out/lib/php.ini 193 194 if test -e $out/bin/php; then 195 wrapProgram $out/bin/php --set-default PHP_INI_SCAN_DIR $out/lib 196 fi 197 198 if test -e $out/bin/php-fpm; then 199 wrapProgram $out/bin/php-fpm --set-default PHP_INI_SCAN_DIR $out/lib 200 fi 201 202 if test -e $out/bin/phpdbg; then 203 wrapProgram $out/bin/phpdbg --set-default PHP_INI_SCAN_DIR $out/lib 204 fi 205 206 if test -e $out/bin/php-cgi; then 207 wrapProgram $out/bin/php-cgi --set-default PHP_INI_SCAN_DIR $out/lib 208 fi 209 ''; 210 }; 211 in 212 phpWithExtensions 213 ); 214 215 mkWithExtensions = 216 prevArgs: prevExtensionFunctions: extensions: 217 mkBuildEnv prevArgs prevExtensionFunctions { inherit extensions; }; 218 219 defaultPhpSrc = fetchurl { 220 url = "https://www.php.net/distributions/php-${version}.tar.bz2"; 221 inherit hash; 222 }; 223 in 224 stdenv.mkDerivation ( 225 finalAttrs: 226 let 227 attrs = { 228 pname = "php"; 229 230 inherit version; 231 232 enableParallelBuilding = true; 233 234 nativeBuildInputs = [ 235 autoconf 236 automake 237 bison 238 flex 239 libtool 240 pkg-config 241 re2c 242 ] 243 ++ lib.optional stdenv.hostPlatform.isDarwin xcbuild; 244 245 buildInputs = 246 # PCRE extension 247 [ pcre2 ] 248 249 # Enable sapis 250 ++ lib.optionals pearSupport [ libxml2.dev ] 251 252 # Misc deps 253 ++ lib.optional apxs2Support apacheHttpd 254 ++ lib.optional argon2Support libargon2 255 ++ lib.optional systemdSupport systemdLibs 256 ++ lib.optional valgrindSupport valgrind; 257 258 CXXFLAGS = lib.optionalString stdenv.cc.isClang "-std=c++11"; 259 SKIP_PERF_SENSITIVE = 1; 260 261 configureFlags = 262 # Disable all extensions 263 [ "--disable-all" ] 264 265 # PCRE 266 ++ [ "--with-external-pcre=${pcre2.dev}" ] 267 268 # Enable sapis 269 ++ lib.optional (!cgiSupport) "--disable-cgi" 270 ++ lib.optional (!cliSupport) "--disable-cli" 271 ++ lib.optional fpmSupport "--enable-fpm" 272 ++ lib.optionals pearSupport [ 273 "--with-pear" 274 "--enable-xml" 275 "--with-libxml" 276 ] 277 ++ lib.optional pharSupport "--enable-phar" 278 ++ lib.optional (!phpdbgSupport) "--disable-phpdbg" 279 280 # Misc flags 281 ++ lib.optional apxs2Support "--with-apxs2=${apacheHttpd.dev}/bin/apxs" 282 ++ lib.optional argon2Support "--with-password-argon2=${libargon2}" 283 ++ lib.optional cgotoSupport "--enable-re2c-cgoto" 284 ++ lib.optional embedSupport "--enable-embed${lib.optionalString staticSupport "=static"}" 285 ++ lib.optional (!ipv6Support) "--disable-ipv6" 286 ++ lib.optional systemdSupport "--with-fpm-systemd" 287 ++ lib.optional valgrindSupport "--with-valgrind=${valgrind.dev}" 288 ++ lib.optional ztsSupport "--enable-zts" 289 ++ lib.optional staticSupport "--enable-static" 290 ++ lib.optional (!zendSignalsSupport) [ "--disable-zend-signals" ] 291 ++ lib.optional zendMaxExecutionTimersSupport "--enable-zend-max-execution-timers" 292 293 # Sendmail 294 ++ [ "PROG_SENDMAIL=${system-sendmail}/bin/sendmail" ]; 295 296 hardeningDisable = [ "bindnow" ]; 297 298 preConfigure = 299 # Don't record the configure flags since this causes unnecessary 300 # runtime dependencies 301 '' 302 substituteInPlace main/build-defs.h.in \ 303 --replace-fail '@CONFIGURE_COMMAND@' '(omitted)' 304 substituteInPlace scripts/php-config.in \ 305 --replace-fail '@CONFIGURE_OPTIONS@' "" \ 306 --replace-fail '@PHP_LDFLAGS@' "" 307 308 export EXTENSION_DIR=$out/lib/php/extensions 309 310 ./buildconf --copy --force 311 312 if [ -f "scripts/dev/genfiles" ]; then 313 ./scripts/dev/genfiles 314 fi 315 '' 316 + lib.optionalString stdenv.hostPlatform.isDarwin '' 317 substituteInPlace configure --replace-fail "-lstdc++" "-lc++" 318 ''; 319 320 # When compiling PHP sources from Github, this file is missing and we 321 # need to install it ourselves. 322 # On the other hand, a distribution includes this file by default. 323 preInstall = '' 324 if [[ ! -f ./pear/install-pear-nozlib.phar ]]; then 325 cp ${pearInstallPhar} ./pear/install-pear-nozlib.phar 326 fi 327 ''; 328 329 postInstall = '' 330 test -d $out/etc || mkdir $out/etc 331 cp php.ini-production $out/etc/php.ini 332 ''; 333 334 postFixup = '' 335 mkdir -p $dev/bin $dev/lib $dev/share/man/man1 336 mv $out/bin/phpize $out/bin/php-config $dev/bin/ 337 mv $out/lib/build $dev/lib/ 338 mv $out/share/man/man1/phpize.1.gz \ 339 $out/share/man/man1/php-config.1.gz \ 340 $dev/share/man/man1/ 341 342 substituteInPlace $dev/bin/phpize \ 343 --replace-fail "$out/lib" "$dev/lib" 344 ''; 345 346 src = if phpSrc == null then defaultPhpSrc else phpSrc; 347 348 patches = 349 lib.optionals (lib.versionOlder version "8.4") [ 350 ./fix-paths-php7.patch 351 ] 352 ++ lib.optionals (lib.versionAtLeast version "8.4") [ 353 ./fix-paths-php84.patch 354 ] 355 ++ extraPatches; 356 357 separateDebugInfo = true; 358 359 outputs = [ 360 "out" 361 "dev" 362 ]; 363 364 passthru = { 365 updateScript = 366 let 367 script = writeShellScript "php${lib.versions.major version}${lib.versions.minor version}-update-script" '' 368 set -o errexit 369 PATH=${ 370 lib.makeBinPath [ 371 common-updater-scripts 372 curl 373 jq 374 ] 375 } 376 new_version=$(curl --silent "https://www.php.net/releases/active" | jq --raw-output '."${lib.versions.major version}"."${lib.versions.majorMinor version}".version') 377 update-source-version "$UPDATE_NIX_ATTR_PATH.unwrapped" "$new_version" "--file=$1" 378 ''; 379 in 380 [ 381 script 382 # Passed as an argument so that update.nix can ensure it does not become a store path. 383 (./. + "/${lib.versions.majorMinor version}.nix") 384 ]; 385 buildEnv = mkBuildEnv { } [ ]; 386 withExtensions = mkWithExtensions { } [ ]; 387 overrideAttrs = 388 f: 389 let 390 newPhpAttrsOverrides = lib.composeExtensions (lib.toExtension phpAttrsOverrides) ( 391 lib.toExtension f 392 ); 393 php = generic (args // { phpAttrsOverrides = newPhpAttrsOverrides; }); 394 in 395 php; 396 inherit ztsSupport; 397 398 services.default = { 399 imports = [ 400 (lib.modules.importApply ./service.nix { 401 inherit formats coreutils; 402 }) 403 ]; 404 php-fpm.package = lib.mkDefault finalAttrs.finalPackage; 405 }; 406 }; 407 408 meta = with lib; { 409 description = "HTML-embedded scripting language"; 410 homepage = "https://www.php.net/"; 411 license = licenses.php301; 412 mainProgram = "php"; 413 teams = [ teams.php ]; 414 platforms = platforms.all; 415 outputsToInstall = [ 416 "out" 417 "dev" 418 ]; 419 }; 420 }; 421 final = attrs // (lib.toExtension phpAttrsOverrides) final attrs; 422 in 423 final 424 ); 425in 426generic