about summary refs log tree commit diff
path: root/pkgs/build-support
diff options
context:
space:
mode:
authorPol Dellaiera <pol.dellaiera@protonmail.com>2023-04-09 11:53:42 +0200
committerPol Dellaiera <pol.dellaiera@protonmail.com>2023-09-13 15:00:03 +0200
commitb36ad2f51797d82ddd4479835c0edd71b3386865 (patch)
tree930c61b8b3a433d632c7e88ef2ac6bc67c8cfcf3 /pkgs/build-support
parent27e3b694e7153ba8e5780fba6cf8f7ef447c1062 (diff)
php: add new builder `buildComposerProject`
Diffstat (limited to 'pkgs/build-support')
-rw-r--r--pkgs/build-support/php/build-composer-project.nix61
-rw-r--r--pkgs/build-support/php/build-composer-repository.nix79
-rw-r--r--pkgs/build-support/php/build-pecl.nix (renamed from pkgs/build-support/build-pecl.nix)0
-rw-r--r--pkgs/build-support/php/hooks/composer-install-hook.sh109
-rw-r--r--pkgs/build-support/php/hooks/composer-repository-hook.sh66
-rw-r--r--pkgs/build-support/php/hooks/default.nix21
6 files changed, 336 insertions, 0 deletions
diff --git a/pkgs/build-support/php/build-composer-project.nix b/pkgs/build-support/php/build-composer-project.nix
new file mode 100644
index 0000000000000..f9589ace1309c
--- /dev/null
+++ b/pkgs/build-support/php/build-composer-project.nix
@@ -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
diff --git a/pkgs/build-support/php/build-composer-repository.nix b/pkgs/build-support/php/build-composer-repository.nix
new file mode 100644
index 0000000000000..7a7ba6f146c0a
--- /dev/null
+++ b/pkgs/build-support/php/build-composer-repository.nix
@@ -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
diff --git a/pkgs/build-support/build-pecl.nix b/pkgs/build-support/php/build-pecl.nix
index 389447e066fa2..389447e066fa2 100644
--- a/pkgs/build-support/build-pecl.nix
+++ b/pkgs/build-support/php/build-pecl.nix
diff --git a/pkgs/build-support/php/hooks/composer-install-hook.sh b/pkgs/build-support/php/hooks/composer-install-hook.sh
new file mode 100644
index 0000000000000..139f6357c5d79
--- /dev/null
+++ b/pkgs/build-support/php/hooks/composer-install-hook.sh
@@ -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"
+}
diff --git a/pkgs/build-support/php/hooks/composer-repository-hook.sh b/pkgs/build-support/php/hooks/composer-repository-hook.sh
new file mode 100644
index 0000000000000..707c944522563
--- /dev/null
+++ b/pkgs/build-support/php/hooks/composer-repository-hook.sh
@@ -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"
+}
diff --git a/pkgs/build-support/php/hooks/default.nix b/pkgs/build-support/php/hooks/default.nix
new file mode 100644
index 0000000000000..98198f0128795
--- /dev/null
+++ b/pkgs/build-support/php/hooks/default.nix
@@ -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;
+}