;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2019 Julien Lepiller ;;; Copyright © 2023 Nicolas Graves ;;; ;;; This file is part of GNU Guix. ;;; ;;; GNU Guix is free software; you can redistribute it and/or modify it ;;; under the terms of the GNU General Public License as published by ;;; the Free Software Foundation; either version 3 of the License, or (at ;;; your option) any later version. ;;; ;;; GNU Guix is distributed in the hope that it will be useful, but ;;; WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;;; GNU General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with GNU Guix. If not, see . (define-module (guix build composer-build-system) #:use-module ((guix build gnu-build-system) #:prefix gnu:) #:use-module (guix build utils) #:use-module (ice-9 match) #:use-module (json) #:use-module (srfi srfi-26) #:export (%standard-phases composer-build)) ;; Commentary: ;; ;; Builder-side code of the standard composer build procedure. ;; ;; Code: (define (json->require dict) (if dict (let loop ((result '()) (require dict)) (match require (() result) ((((? (cut string-contains <> "/") name) . _) require ...) (loop (cons name result) require)) ((_ require ...) (loop result require)) (_ result))) '())) (define (if-specified-to-list fn) (match-lambda ((? unspecified?) '()) (arg (fn arg)) (_ '()))) (define-json-mapping make-composer-autoload composer-autoload? json->composer-autoload (psr-4 composer-autoload-psr-4 "psr-4" (match-lambda ((? unspecified?) '()) ((? (lambda (al) (and (list? al) (pair? (car al)) (vector? (cdar al)))) al) (append-map (lambda (vect-el) (list (cons (caar al) vect-el))) (vector->list (cdar al)))) ((? list? l) l) (_ '()))) (psr-0 composer-autoload-psr-0 "psr-0" (if-specified-to-list identity)) (classmap composer-autoload-classmap "classmap" (if-specified-to-list vector->list)) (files composer-autoload-files "files" (if-specified-to-list vector->list))) (define-json-mapping make-composer-package composer-package? json->composer-package (name composer-package-name) (autoload composer-package-autoload "autoload" (if-specified-to-list json->composer-autoload)) (autoload-dev composer-package-autoload-dev "autoload-dev" (if-specified-to-list json->composer-autoload)) (require composer-package-require "require" json->require) (dev-require composer-package-dev-require "require-dev" json->require) (scripts composer-package-scripts "scripts" (if-specified-to-list identity)) (binaries composer-package-binaries "bin" (if-specified-to-list vector->list))) (define* (read-package-data #:key (filename "composer.json")) (call-with-input-file filename (lambda (port) (json->composer-package (json->scm port))))) (define* (create-test-autoload #:key composer-file inputs outputs tests? #:allow-other-keys) "Create the autoload.php file for tests. This is a standalone phase so that the autoload.php file can be edited before the check phase." (when tests? (mkdir-p "vendor") (create-autoload (string-append (getcwd) "/vendor") composer-file inputs #:dev-dependencies? #t))) (define (find-bin script inputs) (search-input-file inputs (string-append "bin/" (string-drop script (string-length "vendor/bin/"))))) (define* (check #:key composer-file inputs tests? test-target test-flags #:allow-other-keys) "Test the given package. Please note that none of the PHP packages at the time of the rewrite of the build-system did use the test-script field. This means that the @code{match test-script} part is not tested on a real example and relies on the original implementation." (if tests? (let* ((package-data (read-package-data #:filename composer-file)) (scripts (composer-package-scripts package-data)) (test-script (assoc-ref scripts test-target))) (match test-script ((? string? bin) (let ((command (find-bin bin inputs))) (unless (zero? (apply system command test-flags)) (throw 'failed-command command)))) (('@ (? string? bins) ...) (for-each (lambda (c) (let ((command (find-bin bin inputs))) (unless (zero? (apply system command test-flags)) (throw 'failed-command command)))) bins)) (_ (if (file-exists? "phpunit.xml.dist") (apply invoke (with-exception-handler (lambda (exn) (if (search-error? exn) (error "\ Missing php-phpunit-phpunit native input.~%") (raise exn))) (lambda () (search-input-file (or inputs '()) "bin/phpunit"))) test-flags)) (format #t "No test suite found.~%")))) (format #t "Test suite not run.~%"))) (define* (create-autoload vendor composer-file inputs #:key dev-dependencies?) "creates an autoload.php file that sets up the class locations for this package, so it can be autoloaded by PHP when the package classes are required." (with-output-to-file (string-append vendor "/autoload.php") (lambda _ (display (string-append " $paths) { foreach ($paths as $path) { $loader->addPsr4($namespace, $path); } } $loader->addClassMap($classmap); $loader->register(); ")))) ;; Now, create autoload_conf.php that contains the actual data, as a set ;; of arrays (let* ((package-data (read-package-data #:filename composer-file)) (autoload (composer-package-autoload package-data)) (autoload-dev (composer-package-autoload-dev package-data)) (dependencies (composer-package-require package-data)) (dependencies-dev (composer-package-dev-require package-data))) (with-output-to-file (string-append vendor "/autoload_conf.php") (lambda _ (format #t "