about summary refs log tree commit diff
path: root/pkgs/build-support
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/build-support')
-rw-r--r--pkgs/build-support/php/builders/default.nix6
-rw-r--r--pkgs/build-support/php/builders/v2/build-composer-project.nix108
-rw-r--r--pkgs/build-support/php/builders/v2/build-composer-vendor.nix103
-rw-r--r--pkgs/build-support/php/builders/v2/hooks/composer-install-hook.sh91
-rw-r--r--pkgs/build-support/php/builders/v2/hooks/composer-vendor-hook.sh91
-rw-r--r--pkgs/build-support/php/builders/v2/hooks/default.nix45
-rw-r--r--pkgs/build-support/php/builders/v2/hooks/php-script-utils.bash86
7 files changed, 530 insertions, 0 deletions
diff --git a/pkgs/build-support/php/builders/default.nix b/pkgs/build-support/php/builders/default.nix
index ea9bb33504356..d8d60ad6da1e7 100644
--- a/pkgs/build-support/php/builders/default.nix
+++ b/pkgs/build-support/php/builders/default.nix
@@ -6,4 +6,10 @@
     mkComposerRepository = callPackage ./v1/build-composer-repository.nix { };
     composerHooks = callPackages ./v1/hooks { };
   };
+
+  v2 = {
+    buildComposerProject = callPackage ./v2/build-composer-project.nix { };
+    mkComposerVendor = callPackage ./v2/build-composer-vendor.nix { };
+    composerHooks = callPackages ./v2/hooks { };
+  };
 }
diff --git a/pkgs/build-support/php/builders/v2/build-composer-project.nix b/pkgs/build-support/php/builders/v2/build-composer-project.nix
new file mode 100644
index 0000000000000..6013225e7c594
--- /dev/null
+++ b/pkgs/build-support/php/builders/v2/build-composer-project.nix
@@ -0,0 +1,108 @@
+{
+  nix-update-script,
+  stdenvNoCC,
+  lib,
+  php,
+}:
+
+let
+  buildComposerProjectOverride =
+    finalAttrs: previousAttrs:
+
+    let
+      phpDrv = finalAttrs.php or php;
+      composer = finalAttrs.composer or phpDrv.packages.composer;
+    in
+    {
+      composerLock = previousAttrs.composerLock or null;
+      composerNoDev = previousAttrs.composerNoDev or true;
+      composerNoPlugins = previousAttrs.composerNoPlugins or true;
+      composerNoScripts = previousAttrs.composerNoScripts or true;
+      composerStrictValidation = previousAttrs.composerStrictValidation or true;
+
+      nativeBuildInputs = (previousAttrs.nativeBuildInputs or [ ]) ++ [
+        composer
+        phpDrv
+        phpDrv.composerHooks2.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
+        '';
+
+      doInstallCheck = previousAttrs.doInstallCheck or false;
+      installCheckPhase =
+        previousAttrs.installCheckPhase or ''
+          runHook preInstallCheck
+
+          runHook postInstallCheck
+        '';
+
+      composerVendor =
+        previousAttrs.composerVendor or (phpDrv.mkComposerVendor {
+          inherit composer;
+          inherit (finalAttrs)
+            patches
+            pname
+            src
+            vendorHash
+            version
+            ;
+
+          composerLock = previousAttrs.composerLock or null;
+          composerNoDev = previousAttrs.composerNoDev or true;
+          composerNoPlugins = previousAttrs.composerNoPlugins or true;
+          composerNoScripts = previousAttrs.composerNoScripts or true;
+          composerStrictValidation = previousAttrs.composerStrictValidation or true;
+        });
+
+      # Projects providing a lockfile from upstream can be automatically updated.
+      passthru = previousAttrs.passthru or { } // {
+        updateScript =
+          previousAttrs.passthru.updateScript
+            or (if finalAttrs.composerVendor.composerLock == null then nix-update-script { } else null);
+      };
+
+      env = {
+        COMPOSER_CACHE_DIR = "/dev/null";
+        COMPOSER_DISABLE_NETWORK = "1";
+        COMPOSER_MIRROR_PATH_REPOS = "1";
+      };
+
+      meta = previousAttrs.meta or { } // {
+        platforms = lib.platforms.all;
+      };
+    };
+in
+args: (stdenvNoCC.mkDerivation args).overrideAttrs buildComposerProjectOverride
diff --git a/pkgs/build-support/php/builders/v2/build-composer-vendor.nix b/pkgs/build-support/php/builders/v2/build-composer-vendor.nix
new file mode 100644
index 0000000000000..b68e16e6cc1a0
--- /dev/null
+++ b/pkgs/build-support/php/builders/v2/build-composer-vendor.nix
@@ -0,0 +1,103 @@
+{
+  stdenvNoCC,
+  lib,
+  php,
+}:
+
+let
+  mkComposerVendorOverride =
+    /*
+      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) "mkComposerVendor expects src argument.");
+    assert (lib.assertMsg (previousAttrs ? vendorHash) "mkComposerVendor expects vendorHash argument.");
+    assert (lib.assertMsg (previousAttrs ? version) "mkComposerVendor expects version argument.");
+    assert (lib.assertMsg (previousAttrs ? pname) "mkComposerVendor expects pname argument.");
+    {
+      composerNoDev = previousAttrs.composerNoDev or true;
+      composerNoPlugins = previousAttrs.composerNoPlugins or true;
+      composerNoScripts = previousAttrs.composerNoScripts or true;
+      composerStrictValidation = previousAttrs.composerStrictValidation or true;
+
+      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
+        phpDrv
+        phpDrv.composerHooks2.composerVendorHook
+      ];
+
+      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
+        '';
+
+      doInstallCheck = previousAttrs.doInstallCheck or false;
+      installCheckPhase =
+        previousAttrs.installCheckPhase or ''
+          runHook preInstallCheck
+
+          runHook postInstallCheck
+        '';
+
+      env = {
+        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 mkComposerVendorOverride
diff --git a/pkgs/build-support/php/builders/v2/hooks/composer-install-hook.sh b/pkgs/build-support/php/builders/v2/hooks/composer-install-hook.sh
new file mode 100644
index 0000000000000..7cc176058b504
--- /dev/null
+++ b/pkgs/build-support/php/builders/v2/hooks/composer-install-hook.sh
@@ -0,0 +1,91 @@
+declare composerVendor
+declare version
+declare composerNoDev
+declare composerNoPlugins
+declare composerNoScripts
+
+preConfigureHooks+=(composerInstallConfigureHook)
+preBuildHooks+=(composerInstallBuildHook)
+preCheckHooks+=(composerInstallCheckHook)
+preInstallHooks+=(composerInstallInstallHook)
+
+source @phpScriptUtils@
+
+composerInstallConfigureHook() {
+    echo "Executing composerInstallConfigureHook"
+
+    setComposeRootVersion
+
+    if [[ ! -e "${composerVendor}" ]]; then
+        echo "No local composer vendor found."
+        exit 1
+    fi
+
+    install -Dm644 ${composerVendor}/composer.{json,lock} .
+
+    if [[ ! -f "composer.lock" ]]; then
+        composer \
+            --no-install \
+            --no-interaction \
+            --no-progress \
+            --optimize-autoloader \
+            ${composerNoDev:+--no-dev} \
+            ${composerNoPlugins:+--no-plugins} \
+            ${composerNoScripts:+--no-scripts} \
+            update
+
+        install -Dm644 composer.lock -t $out/
+
+        echo
+        echo -e "\e[31mERROR: No composer.lock found\e[0m"
+        echo
+        echo -e '\e[31mNo composer.lock file found, consider adding one to your repository to ensure reproducible builds.\e[0m'
+        echo -e "\e[31mIn the meantime, a composer.lock file has been generated for you in $out/composer.lock\e[0m"
+        echo
+        echo -e '\e[31mTo fix the issue:\e[0m'
+        echo -e "\e[31m1. Copy the composer.lock file from $out/composer.lock to the project's source:\e[0m"
+        echo -e "\e[31m  cp $out/composer.lock <path>\e[0m"
+        echo -e '\e[31m2. Add the composerLock attribute, pointing to the copied composer.lock file:\e[0m'
+        echo -e '\e[31m  composerLock = ./composer.lock;\e[0m'
+        echo
+
+        exit 1
+    fi
+
+    chmod +w composer.{json,lock}
+
+    echo "Finished composerInstallConfigureHook"
+}
+
+composerInstallBuildHook() {
+    echo "Executing composerInstallBuildHook"
+
+    echo "Finished composerInstallBuildHook"
+}
+
+composerInstallCheckHook() {
+    echo "Executing composerInstallCheckHook"
+
+    checkComposerValidate
+
+    echo "Finished composerInstallCheckHook"
+}
+
+composerInstallInstallHook() {
+    echo "Executing composerInstallInstallHook"
+
+    cp -ar ${composerVendor}/* .
+
+    # 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[] | select(test(".bat$")? | not) )' composer.json | while read -r bin; do
+        echo -e "\e[32mCreating symlink ${bin}...\e[0m"
+        mkdir -p "$out"/bin
+        ln -s "$out"/share/php/"${pname}"/"$bin" "$out"/bin/"$(basename "$bin")"
+    done
+
+    echo "Finished composerInstallInstallHook"
+}
diff --git a/pkgs/build-support/php/builders/v2/hooks/composer-vendor-hook.sh b/pkgs/build-support/php/builders/v2/hooks/composer-vendor-hook.sh
new file mode 100644
index 0000000000000..7a14e544ae0de
--- /dev/null
+++ b/pkgs/build-support/php/builders/v2/hooks/composer-vendor-hook.sh
@@ -0,0 +1,91 @@
+declare composerLock
+declare version
+declare composerNoDev
+declare composerNoPlugins
+declare composerNoScripts
+declare composerStrictValidation
+
+preConfigureHooks+=(composerVendorConfigureHook)
+preBuildHooks+=(composerVendorBuildHook)
+preCheckHooks+=(composerVendorCheckHook)
+preInstallHooks+=(composerVendorInstallHook)
+
+source @phpScriptUtils@
+
+composerVendorConfigureHook() {
+    echo "Executing composerVendorConfigureHook"
+
+    setComposeRootVersion
+
+    if [[ -e "$composerLock" ]]; then
+        echo -e "\e[32mUsing user provided \`composer.lock\` file from \`$composerLock\`\e[0m"
+        install -Dm644 $composerLock ./composer.lock
+    fi
+
+    if [[ ! -f "composer.lock" ]]; then
+        composer \
+            --no-install \
+            --no-interaction \
+            --no-progress \
+            --optimize-autoloader \
+            ${composerNoDev:+--no-dev} \
+            ${composerNoPlugins:+--no-plugins} \
+            ${composerNoScripts:+--no-scripts} \
+            update
+
+        install -Dm644 composer.lock -t $out/
+
+        echo
+        echo -e "\e[31mERROR: No composer.lock found\e[0m"
+        echo
+        echo -e '\e[31mNo composer.lock file found, consider adding one to your repository to ensure reproducible builds.\e[0m'
+        echo -e "\e[31mIn the meantime, a composer.lock file has been generated for you in $out/composer.lock\e[0m"
+        echo
+        echo -e '\e[31mTo fix the issue:\e[0m'
+        echo -e "\e[31m1. Copy the composer.lock file from $out/composer.lock to the project's source:\e[0m"
+        echo -e "\e[31m  cp $out/composer.lock <path>\e[0m"
+        echo -e '\e[31m2. Add the composerLock attribute, pointing to the copied composer.lock file:\e[0m'
+        echo -e '\e[31m  composerLock = ./composer.lock;\e[0m'
+        echo
+
+        exit 1
+    fi
+
+    chmod +w composer.{json,lock}
+
+    echo "Finished composerVendorConfigureHook"
+}
+
+composerVendorBuildHook() {
+    echo "Executing composerVendorBuildHook"
+
+    composer \
+        --apcu-autoloader \
+        --apcu-autoloader-prefix="$(jq -r -c 'try ."content-hash"' < composer.lock)" \
+        --no-interaction \
+        --no-progress \
+        --optimize-autoloader \
+        ${composerNoDev:+--no-dev} \
+        ${composerNoPlugins:+--no-plugins} \
+        ${composerNoScripts:+--no-scripts} \
+        install
+
+    echo "Finished composerVendorBuildHook"
+}
+
+composerVendorCheckHook() {
+    echo "Executing composerVendorCheckHook"
+
+    checkComposerValidate
+
+    echo "Finished composerVendorCheckHook"
+}
+
+composerVendorInstallHook() {
+    echo "Executing composerVendorInstallHook"
+
+    mkdir -p $out
+    cp -ar composer.{json,lock} $(composer config vendor-dir) $out/
+
+    echo "Finished composerVendorInstallHook"
+}
diff --git a/pkgs/build-support/php/builders/v2/hooks/default.nix b/pkgs/build-support/php/builders/v2/hooks/default.nix
new file mode 100644
index 0000000000000..e4d6dcd8ffc7e
--- /dev/null
+++ b/pkgs/build-support/php/builders/v2/hooks/default.nix
@@ -0,0 +1,45 @@
+{
+  lib,
+  makeSetupHook,
+  jq,
+  writeShellApplication,
+  moreutils,
+  cacert,
+  buildPackages,
+}:
+
+let
+  php-script-utils = writeShellApplication {
+    name = "php-script-utils";
+    runtimeInputs = [ jq ];
+    text = builtins.readFile ./php-script-utils.bash;
+  };
+in
+{
+  composerVendorHook = makeSetupHook {
+    name = "composer-vendor-hook.sh";
+    propagatedBuildInputs = [
+      jq
+      moreutils
+      cacert
+    ];
+    substitutions = {
+      phpScriptUtils = lib.getExe php-script-utils;
+    };
+  } ./composer-vendor-hook.sh;
+
+  composerInstallHook = makeSetupHook {
+    name = "composer-install-hook.sh";
+    propagatedBuildInputs = [
+      jq
+      moreutils
+      cacert
+    ];
+    substitutions = {
+      # Specify the stdenv's `diff` by abspath to ensure that the user's build
+      # inputs do not cause us to find the wrong `diff`.
+      cmp = "${lib.getBin buildPackages.diffutils}/bin/cmp";
+      phpScriptUtils = lib.getExe php-script-utils;
+    };
+  } ./composer-install-hook.sh;
+}
diff --git a/pkgs/build-support/php/builders/v2/hooks/php-script-utils.bash b/pkgs/build-support/php/builders/v2/hooks/php-script-utils.bash
new file mode 100644
index 0000000000000..3e8299bf15d37
--- /dev/null
+++ b/pkgs/build-support/php/builders/v2/hooks/php-script-utils.bash
@@ -0,0 +1,86 @@
+declare version
+declare composerStrictValidation
+declare composerGlobal
+
+setComposeRootVersion() {
+    set +e # Disable exit on error
+
+    if [[ -v version ]]; then
+        echo -e "\e[32mSetting COMPOSER_ROOT_VERSION to $version\e[0m"
+        export COMPOSER_ROOT_VERSION=$version
+    fi
+
+    set -e
+}
+
+checkComposerValidate() {
+    if [ "1" == "${composerGlobal-}" ]; then
+      global="global";
+    else
+      global="";
+    fi
+
+    command="composer ${global} validate --strict --quiet --no-interaction --no-check-all --no-check-lock"
+    if ! $command; then
+        if [ "1" == "${composerStrictValidation-}" ]; then
+            echo
+            echo -e "\e[31mERROR: composer files validation failed\e[0m"
+            echo
+            echo -e '\e[31mThe validation of the composer.json failed.\e[0m'
+            echo -e '\e[31mMake sure that the file composer.json is valid.\e[0m'
+            echo
+            echo -e '\e[31mTo address the issue efficiently, follow one of these steps:\e[0m'
+            echo -e '\e[31m  1. File an issue in the project'\''s issue tracker with detailed information, and apply any available remote patches as a temporary solution '\('with fetchpatch'\)'.\e[0m'
+            echo -e '\e[31m  2. If an immediate fix is needed or if reporting upstream isn'\''t suitable, develop a temporary local patch.\e[0m'
+            echo
+            exit 1
+        else
+            echo
+            echo -e "\e[33mWARNING: composer files validation failed\e[0m"
+            echo
+            echo -e '\e[33mThe validation of the composer.json failed.\e[0m'
+            echo -e '\e[33mMake sure that the file composer.json is valid.\e[0m'
+            echo
+            echo -e '\e[33mTo address the issue efficiently, follow one of these steps:\e[0m'
+            echo -e '\e[33m  1. File an issue in the project'\''s issue tracker with detailed information, and apply any available remote patches as a temporary solution with '\('with fetchpatch'\)'.\e[0m'
+            echo -e '\e[33m  2. If an immediate fix is needed or if reporting upstream isn'\''t suitable, develop a temporary local patch.\e[0m'
+            echo
+            echo -e '\e[33mThis check is not blocking, but it is recommended to fix the issue.\e[0m'
+            echo
+        fi
+    fi
+
+    command="composer ${global} validate --strict --no-ansi --no-interaction --quiet --no-check-all --check-lock"
+    if ! $command; then
+        if [ "1" == "${composerStrictValidation-}" ]; then
+            echo
+            echo -e "\e[31mERROR: composer files validation failed\e[0m"
+            echo
+            echo -e '\e[31mThe validation of the composer.json and composer.lock failed.\e[0m'
+            echo -e '\e[31mMake sure that the file composer.lock is consistent with composer.json.\e[0m'
+            echo
+            echo -e '\e[31mThis often indicates an issue with the upstream project, which can typically be resolved by reporting the issue to the relevant project maintainers.\e[0m'
+            echo
+            echo -e '\e[31mTo address the issue efficiently, follow one of these steps:\e[0m'
+            echo -e '\e[31m  1. File an issue in the project'\''s issue tracker with detailed information '\('run '\''composer update --lock --no-install'\'' to fix the issue'\)', and apply any available remote patches as a temporary solution with '\('with fetchpatch'\)'.\e[0m'
+            echo -e '\e[31m  2. If an immediate fix is needed or if reporting upstream isn'\''t suitable, develop a temporary local patch.\e[0m'
+            echo
+            exit 1
+        else
+            echo
+            echo -e "\e[33mWARNING: composer files validation failed\e[0m"
+            echo
+            echo -e '\e[33mThe validation of the composer.json and composer.lock failed.\e[0m'
+            echo -e '\e[33mMake sure that the file composer.lock is consistent with composer.json.\e[0m'
+            echo
+            echo -e '\e[33mThis often indicates an issue with the upstream project, which can typically be resolved by reporting the issue to the relevant project maintainers.\e[0m'
+            echo
+            echo -e '\e[33mTo address the issue efficiently, follow one of these steps:\e[0m'
+            echo -e '\e[33m  1. File an issue in the project'\''s issue tracker with detailed information '\('run '\''composer update --lock --no-install'\'' to fix the issue'\)', and apply any available remote patches as a temporary solution with '\('with fetchpatch'\)'.\e[0m'
+            echo -e '\e[33m  2. If an immediate fix is needed or if reporting upstream isn'\''t suitable, develop a temporary local patch.\e[0m'
+            echo
+            echo -e '\e[33mThis check is not blocking, but it is recommended to fix the issue.\e[0m'
+            echo
+        fi
+    fi
+}