# mill-scale -- Another rust module for flakelight # Copyright (C) 2024 Robin Appelman # SPDX-License-Identifier: MIT { lib, src, config, flakelight, ... }: let inherit (builtins) elem readFile pathExists match fromJSON attrNames head replaceStrings; inherit (lib) getExe mkDefault mkIf mkMerge mkOption warnIf optionalAttrs types listToAttrs; inherit (lib.fileset) fileFilter toSource unions; inherit (flakelight.types) fileset function; genAttrs' = xs: f: listToAttrs (map f xs); # TODO remove after we're at newer nixpkgs phpVersions = { "5.6" = pkgs: pkgs.php56; "7.0" = pkgs: pkgs.php70; "7.1" = pkgs: pkgs.php71; "7.2" = pkgs: pkgs.php72; "7.3" = pkgs: pkgs.php73; "7.4" = pkgs: pkgs.php74; "8.0" = pkgs: pkgs.php80; "8.1" = pkgs: pkgs.php81; "8.2" = pkgs: pkgs.php82; "8.3" = pkgs: pkgs.php83; "8.4" = pkgs: pkgs.php84; }; filteredSrc = toSource { root = src; fileset = unions (config.extraPaths ++ [config.fileset]); }; composerJson = fromJSON (readFile config.composerJson); composerMeta = (import ./composer-meta.nix { inherit lib; allVersions = attrNames phpVersions; }) composerJson; hasPsalm = pathExists config.psalmConfig; hasPhpStan = pathExists config.phpStanConfig; hasPhpCsFixer = pathExists config.phpCsFixerConfig; buildPhpVersion = pkgs: version: (phpVersions.${version} pkgs).buildEnv { extensions = { enabled, all, }: let extra = { krb5 = pkgs.callPackage ./pkgs/php-krb5.nix {php = phpVersions.${version} pkgs;}; }; in enabled ++ (config.phpExtensions (all // extra)); }; buildPhp = pkgs: buildPhpVersion pkgs (head composerMeta.phpVersions); removeDots = replaceStrings ["."] [""]; in warnIf (! builtins ? readFileType) "Unsupported Nix version in use." { options = { composerJson = mkOption { type = with types; path; default = src + /composer.json; }; composerLock = mkOption { type = with types; path; default = src + /composer.lock; }; psalmConfig = mkOption { type = with types; path; default = src + /psalm.xml; }; phpStanConfig = mkOption { type = with types; path; default = src + /phpstan.neon; }; phpCsFixerConfig = mkOption { type = with types; path; default = src + /.php-cs-fixer.dist.php; }; extraPaths = mkOption { type = with types; listOf path; default = []; }; fileset = mkOption { type = fileset; default = fileFilter (file: file.hasExt "php" || file.hasExt "stub" || file.hasExt "phpstub" || match "snapshot__.*\.snap" file.name != null || elem file.name [".php-cs-fixer.dist.php" "psalm.xml" "composer.json" "composer.lock" "phpstan.neon"]) src; }; phpExtensions = mkOption { type = function; default = all: []; description = "extra php extensions to enable"; }; tools = mkOption { type = function; default = pkgs: []; description = "extra packages to make available in the dev shells"; }; vendorHash = mkOption { type = with types; str; }; testDependencies = mkOption { type = function; default = pkgs: []; description = "extra dependencies needed during testing"; }; }; config = mkMerge [ (mkIf (pathExists config.composerJson) { withOverlays = [ (final: prev: { minPhp = buildPhp final; vendor = final.callPackage ./pkgs/vendor.nix { php = final.minPhp; inherit (config) composerJson composerLock vendorHash; }; phpunit = final.callPackage ./apps/phpunit.nix { php = final.minPhp; majorVersion = composerMeta.phpUnitVersion; testDependencies = config.testDependencies final; }; inherit composerMeta filteredSrc; inherit (final.minPhp.packages) php-cs-fixer composer psalm phpstan; }) ]; description = mkIf (composerMeta ? description) composerMeta.description; license = mkIf (composerMeta ? license) (mkDefault composerMeta.license); pname = composerMeta.name; outputs = { lib = { phpVersions = composerMeta.phpVersions; inherit hasPsalm hasPhpCsFixer; }; }; packages = { vendor = pkgs: pkgs.vendor; }; checks = {pkgs, ...}: let forPhpVersions = name: checkFile: genAttrs' composerMeta.phpVersions (version: { name = "${name}${removeDots version}"; value = pkgs.callPackage ./checks/psalm.nix { src = filteredSrc; php = buildPhpVersion pkgs version; }; }); in { vendor-hash = pkgs.callPackage ./checks/vendor-hash.nix {}; } // (optionalAttrs hasPsalm (forPhpVersions "psalm" ./checks/psalm.nix)) // (optionalAttrs hasPhpStan (forPhpVersions "phpstan" ./checks/phpstan.nix)); apps = {pkgs, ...}: let forPhpVersions = name: app: genAttrs' composerMeta.phpVersions (version: { name = "${name}${removeDots version}"; value = ''${getExe (app.override { php = buildPhpVersion pkgs version; })} "$@"''; }); in { phpunit = ''${getExe pkgs.phpunit} "$@"''; } // (forPhpVersions "phpunit" pkgs.phpunit); }) { devShells = { default = { packages = pkgs: (config.tools pkgs) ++ (with pkgs; [minPhp php-cs-fixer composer psalm phpstan phpunit]); }; }; formatters = pkgs: with pkgs; ({ "*.nix" = getExe alejandra; } // optionalAttrs hasPhpCsFixer { "*.php" = "${getExe php-cs-fixer} fix"; "*.stub" = "${getExe php-cs-fixer} fix"; "*.phpstub" = "${getExe php-cs-fixer} fix"; }); } ]; }