php: add new builder buildComposerProject

This commit is contained in:
Pol Dellaiera 2023-04-09 11:53:42 +02:00
parent 27e3b694e7
commit b36ad2f517
No known key found for this signature in database
GPG key ID: D476DFE9C67467CA
9 changed files with 347 additions and 5 deletions

3
.github/CODEOWNERS vendored
View file

@ -257,7 +257,8 @@ pkgs/development/python-modules/buildcatrust/ @ajs124 @lukegb @mweinelt
# PHP interpreter, packages, extensions, tests and documentation
/doc/languages-frameworks/php.section.md @aanderse @drupol @etu @globin @ma27 @talyz
/nixos/tests/php @aanderse @drupol @etu @globin @ma27 @talyz
/pkgs/build-support/build-pecl.nix @aanderse @drupol @etu @globin @ma27 @talyz
/pkgs/build-support/php/build-pecl.nix @aanderse @drupol @etu @globin @ma27 @talyz
/pkgs/build-support/php @drupol @etu
/pkgs/development/interpreters/php @jtojnar @aanderse @drupol @etu @globin @ma27 @talyz
/pkgs/development/php-packages @aanderse @drupol @etu @globin @ma27 @talyz
/pkgs/top-level/php-packages.nix @jtojnar @aanderse @drupol @etu @globin @ma27 @talyz

View file

@ -0,0 +1,61 @@
{ stdenvNoCC, lib, writeTextDir, php, makeBinaryWrapper, fetchFromGitHub, fetchurl, composer-local-repo-plugin }:
let
buildComposerProjectOverride = finalAttrs: previousAttrs:
let
phpDrv = finalAttrs.php or php;
composer = finalAttrs.composer or phpDrv.packages.composer;
composerLock = finalAttrs.composerLock or null;
in
{
nativeBuildInputs = (previousAttrs.nativeBuildInputs or [ ]) ++ [
composer
composer-local-repo-plugin
phpDrv.composerHooks.composerInstallHook
];
buildInputs = (previousAttrs.buildInputs or [ ]) ++ [
phpDrv
];
patches = previousAttrs.patches or [ ];
strictDeps = previousAttrs.strictDeps or true;
# Should we keep these empty phases?
configurePhase = previousAttrs.configurePhase or ''
runHook preConfigure
runHook postConfigure
'';
buildPhase = previousAttrs.buildPhase or ''
runHook preBuild
runHook postBuild
'';
doCheck = previousAttrs.doCheck or true;
checkPhase = previousAttrs.checkPhase or ''
runHook preCheck
runHook postCheck
'';
installPhase = previousAttrs.installPhase or ''
runHook preInstall
runHook postInstall
'';
composerRepository = phpDrv.mkComposerRepository {
inherit composer composer-local-repo-plugin composerLock;
inherit (finalAttrs) patches pname src vendorHash version;
};
meta = previousAttrs.meta or { } // {
platforms = lib.platforms.all;
};
};
in
args: (stdenvNoCC.mkDerivation args).overrideAttrs buildComposerProjectOverride

View file

@ -0,0 +1,79 @@
{ stdenvNoCC, lib, writeTextDir, fetchFromGitHub, php, composer-local-repo-plugin }:
let
mkComposerRepositoryOverride =
/*
We cannot destruct finalAttrs since the attrset below is used to construct it
and Nix currently does not support lazy attribute names.
{
php ? null,
composer ? null,
composerLock ? "composer.lock",
src,
vendorHash,
...
}@finalAttrs:
*/
finalAttrs: previousAttrs:
let
phpDrv = finalAttrs.php or php;
composer = finalAttrs.composer or phpDrv.packages.composer;
in
assert (lib.assertMsg (previousAttrs ? src) "mkComposerRepository expects src argument.");
assert (lib.assertMsg (previousAttrs ? vendorHash) "mkComposerRepository expects vendorHash argument.");
assert (lib.assertMsg (previousAttrs ? version) "mkComposerRepository expects version argument.");
assert (lib.assertMsg (previousAttrs ? pname) "mkComposerRepository expects pname argument.");
{
name = "${previousAttrs.pname}-${previousAttrs.version}-composer-repository";
# See https://github.com/NixOS/nix/issues/6660
dontPatchShebangs = previousAttrs.dontPatchShebangs or true;
nativeBuildInputs = (previousAttrs.nativeBuildInputs or [ ]) ++ [
composer
composer-local-repo-plugin
phpDrv.composerHooks.composerRepositoryHook
];
buildInputs = previousAttrs.buildInputs or [ ];
strictDeps = previousAttrs.strictDeps or true;
# Should we keep these empty phases?
configurePhase = previousAttrs.configurePhase or ''
runHook preConfigure
runHook postConfigure
'';
buildPhase = previousAttrs.buildPhase or ''
runHook preBuild
runHook postBuild
'';
doCheck = previousAttrs.doCheck or true;
checkPhase = previousAttrs.checkPhase or ''
runHook preCheck
runHook postCheck
'';
installPhase = previousAttrs.installPhase or ''
runHook preInstall
runHook postInstall
'';
COMPOSER_CACHE_DIR = "/dev/null";
COMPOSER_MIRROR_PATH_REPOS = "1";
COMPOSER_HTACCESS_PROTECT = "0";
COMPOSER_DISABLE_NETWORK = "0";
outputHashMode = "recursive";
outputHashAlgo = if (finalAttrs ? vendorHash && finalAttrs.vendorHash != "") then null else "sha256";
outputHash = finalAttrs.vendorHash or "";
};
in
args: (stdenvNoCC.mkDerivation args).overrideAttrs mkComposerRepositoryOverride

View file

@ -0,0 +1,109 @@
declare composerHomeDir
declare composerRepository
declare version
preConfigureHooks+=(composerInstallConfigureHook)
preBuildHooks+=(composerInstallBuildHook)
preCheckHooks+=(composerInstallCheckHook)
preInstallHooks+=(composerInstallInstallHook)
composerInstallConfigureHook() {
echo "Executing composerInstallConfigureHook"
if [[ ! -e "${composerRepository}" ]]; then
echo "No local composer repository found."
exit 1
fi
if [[ -e "$composerLock" ]]; then
cp $composerLock composer.lock
fi
if [[ ! -f "composer.lock" ]]; then
echo "No composer.lock file found, consider adding one to your repository to ensure reproducible builds."
if [[ -f "${composerRepository}/composer.lock" ]]; then
cp ${composerRepository}/composer.lock composer.lock
fi
echo "Using an autogenerated composer.lock file."
fi
chmod +w composer.json composer.lock
echo "Finished composerInstallConfigureHook"
}
composerInstallBuildHook() {
echo "Executing composerInstallBuildHook"
# Since this file cannot be generated in the composer-repository-hook.sh
# because the file contains hardcoded nix store paths, we generate it here.
composer-local-repo-plugin --no-ansi build-local-repo -p ${composerRepository} > packages.json
# Remove all the repositories of type "composer"
# from the composer.json file.
jq -r -c 'del(try .repositories[] | select(.type == "composer"))' composer.json | sponge composer.json
# Configure composer to disable packagist and avoid using the network.
composer config repo.packagist false
# Configure composer to use the local repository.
composer config repo.composer composer file://$PWD/packages.json
# Since the composer.json file has been modified in the previous step, the
# composer.lock file needs to be updated.
COMPOSER_DISABLE_NETWORK=1 \
COMPOSER_ROOT_VERSION="${version}" \
composer \
--lock \
--no-ansi \
--no-install \
--no-interaction \
--no-plugins \
--no-scripts \
update
echo "Finished composerInstallBuildHook"
}
composerInstallCheckHook() {
echo "Executing composerInstallCheckHook"
composer validate --no-ansi --no-interaction
echo "Finished composerInstallCheckHook"
}
composerInstallInstallHook() {
echo "Executing composerInstallInstallHook"
# Finally, run `composer install` to install the dependencies and generate
# the autoloader.
# The COMPOSER_ROOT_VERSION environment variable is needed only for
# vimeo/psalm.
COMPOSER_CACHE_DIR=/dev/null \
COMPOSER_DISABLE_NETWORK=1 \
COMPOSER_ROOT_VERSION="${version}" \
COMPOSER_MIRROR_PATH_REPOS="1" \
composer \
--no-ansi \
--no-interaction \
--no-scripts \
--no-plugins \
install
# Remove packages.json, we don't need it in the store.
rm packages.json
# Copy the relevant files only in the store.
mkdir -p $out/share/php/${pname}
cp -r . $out/share/php/${pname}/
# Create symlinks for the binaries.
jq -r -c 'try .bin[]' composer.json | while read bin; do
mkdir -p $out/share/php/${pname} $out/bin
ln -s $out/share/php/${pname}/$bin $out/bin/$(basename $bin)
done
echo "Finished composerInstallInstallHook"
}

View file

@ -0,0 +1,66 @@
declare composerHomeDir
declare composerLock
declare version
preConfigureHooks+=(composerRepositoryConfigureHook)
preBuildHooks+=(composerRepositoryBuildHook)
preCheckHooks+=(composerRepositoryCheckHook)
preInstallHooks+=(composerRepositoryInstallHook)
composerRepositoryConfigureHook() {
echo "Executing composerRepositoryConfigureHook"
if [[ -e "$composerLock" ]]; then
cp $composerLock composer.lock
fi
if [[ ! -f "composer.lock" ]]; then
echo "No composer.lock file found, consider adding one to your repository to ensure reproducible builds."
composer \
--no-ansi \
--no-install \
--no-interaction \
--no-plugins \
--no-scripts \
update
echo "Using an autogenerated composer.lock file."
fi
echo "Finished composerRepositoryConfigureHook"
}
composerRepositoryBuildHook() {
echo "Executing composerRepositoryBuildHook"
mkdir -p repository
# Build the local composer repository
# The command 'build-local-repo' is provided by the Composer plugin
# nix-community/composer-local-repo-plugin.
COMPOSER_CACHE_DIR=/dev/null \
composer-local-repo-plugin --no-ansi build-local-repo -r repository
echo "Finished composerRepositoryBuildHook"
}
composerRepositoryCheckHook() {
echo "Executing composerRepositoryCheckHook"
composer validate --no-ansi --no-interaction
echo "Finished composerRepositoryCheckHook"
}
composerRepositoryInstallHook() {
echo "Executing composerRepositoryInstallHook"
mkdir -p $out
cp -ar repository/. $out/
# Copy the composer.lock files to the output directory, in case it has been
# autogenerated.
cp composer.lock $out/
echo "Finished composerRepositoryInstallHook"
}

View file

@ -0,0 +1,21 @@
{ makeSetupHook
, php
, jq
, moreutils
}:
{
composerRepositoryHook = makeSetupHook
{
name = "composer-repository-hook.sh";
propagatedBuildInputs = [ php jq moreutils ];
substitutions = { };
} ./composer-repository-hook.sh;
composerInstallHook = makeSetupHook
{
name = "composer-install-hook.sh";
propagatedBuildInputs = [ php jq moreutils ];
substitutions = { };
} ./composer-install-hook.sh;
}

View file

@ -159,7 +159,7 @@ let
nixos = lib.recurseIntoAttrs nixosTests."php${lib.strings.replaceStrings [ "." ] [ "" ] (lib.versions.majorMinor php.version)}";
package = tests.php;
};
inherit (php-packages) extensions buildPecl mkExtension;
inherit (php-packages) extensions buildPecl mkComposerRepository buildComposerProject composerHooks mkExtension;
packages = php-packages.tools;
meta = php.meta // {
outputsToInstall = [ "out" ];

View file

@ -1,4 +1,6 @@
{ stdenv
, config
, callPackages
, lib
, pkgs
, phpPackage
@ -44,12 +46,15 @@
}:
lib.makeScope pkgs.newScope (self: with self; {
buildPecl = import ../build-support/build-pecl.nix {
buildPecl = callPackage ../build-support/php/build-pecl.nix {
php = php.unwrapped;
inherit lib;
inherit (pkgs) stdenv autoreconfHook fetchurl re2c nix-update-script;
};
composerHooks = callPackages ../build-support/php/hooks { };
mkComposerRepository = callPackage ../build-support/php/build-composer-repository.nix { };
buildComposerProject = callPackage ../build-support/php/build-composer-project.nix { };
# Wrap mkDerivation to prepend pname with "php-" to make names consistent
# with how buildPecl does it and make the file easier to overview.
mkDerivation = origArgs: