From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp12.migadu.com ([2001:41d0:303:e224::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms8.migadu.com with LMTPS id wHSTFA2+Q2WdPwEAauVa8A:P1 (envelope-from ) for ; Thu, 02 Nov 2023 16:19:41 +0100 Received: from aspmx1.migadu.com ([2001:41d0:303:e224::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp12.migadu.com with LMTPS id wHSTFA2+Q2WdPwEAauVa8A (envelope-from ) for ; Thu, 02 Nov 2023 16:19:41 +0100 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id CA6BB65D9F for ; Thu, 2 Nov 2023 16:19:40 +0100 (CET) Authentication-Results: aspmx1.migadu.com; dkim=none; dmarc=pass (policy=none) header.from=gnu.org; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org" ARC-Seal: i=1; s=key1; d=yhetil.org; t=1698938381; a=rsa-sha256; cv=none; b=UloY3AWfkTJmFTw7aQFYXEeu++FcxBaFvNRcoHypnvS0aGgnFzyzKWg/zZYBEvcF3xOuKh 4papuynAG5Njd/kUicmqahrSiPm/n86gikiqCsYMjCAp1ITV09WjVlQ7K8kB33upcRPL71 sfDqiDTuj+2+8FoXX6ZadBrviJfNXZQY3+snuibsZeUyuj8fYyHr8pkldyF+3xVWIuLXr0 iSGjBJdtCPVVkvHg5jJQs+Mkf3ssscyzla2iowKGm5R4rJyY72idUMxJuZx6VcF5U6xBSa /xpzHZD8jbu6U/31cqnNz6SVho2EKsZy9DrIGWg05TKkGtumps6aBqZVMWuDBA== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=none; dmarc=pass (policy=none) header.from=gnu.org; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org" ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1698938381; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding:resent-cc: resent-from:resent-sender:resent-message-id:in-reply-to:in-reply-to: references:references:list-id:list-help:list-unsubscribe: list-subscribe:list-post; bh=kqLUcjt6OOPCq4DWL9tpP2kQAuOk2ffgapc3AWMNgHQ=; b=apaF+/oIC6fQyuZ6GxDQJgYk8Ai2jgHgQFBg1ecf1INM1K88fIbQorwOIeiT4wiFxxpQjJ srupsaVaN6+u+OjnM0PZZNCAcUMhUW2yiJyo0QGnBRd27EzrEMM3o12rbvXcsNSiicbgqs 7wXfxaEioTH+PFdmzU8YOWXpvw6KEcThFVypj9fkcRAX06NJ3a4/SkpyhzhDFjkpumSX/i zKtV310FkTiynLCkYfDHFT4DnfZzFbUg6Xugbs7lxxQlbjtZ+6I+IRofgohu1m0ghroPbv ZOb05segXRxLmvsADxmxqhnoPB1yvmMZDvtcjcTfI+OqoOBNq9zwegq1KxUnDQ== Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1qyZSq-00006W-Dd; Thu, 02 Nov 2023 11:18:32 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qyZSm-0008Vz-9J for guix-patches@gnu.org; Thu, 02 Nov 2023 11:18:28 -0400 Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1qyZSm-0003pg-1x for guix-patches@gnu.org; Thu, 02 Nov 2023 11:18:28 -0400 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1qyZTL-0006bk-1k for guix-patches@gnu.org; Thu, 02 Nov 2023 11:19:03 -0400 X-Loop: help-debbugs@gnu.org Subject: [bug#42338] [PATCH v5 3/9] guix: Add composer-build-system. Resent-From: Nicolas Graves Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Thu, 02 Nov 2023 15:19:03 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 42338 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 42338@debbugs.gnu.org Cc: ngraves@ngraves.fr Received: via spool by 42338-submit@debbugs.gnu.org id=B42338.169893830025262 (code B ref 42338); Thu, 02 Nov 2023 15:19:03 +0000 Received: (at 42338) by debbugs.gnu.org; 2 Nov 2023 15:18:20 +0000 Received: from localhost ([127.0.0.1]:55776 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qyZSc-0006ZE-K8 for submit@debbugs.gnu.org; Thu, 02 Nov 2023 11:18:20 -0400 Received: from 20.mo584.mail-out.ovh.net ([46.105.33.73]:57807) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qyZSY-0006Yc-Mo for 42338@debbugs.gnu.org; Thu, 02 Nov 2023 11:18:17 -0400 Received: from director4.ghost.mail-out.ovh.net (unknown [10.108.20.237]) by mo584.mail-out.ovh.net (Postfix) with ESMTP id 690F6285F5 for <42338@debbugs.gnu.org>; Thu, 2 Nov 2023 15:17:38 +0000 (UTC) Received: from ghost-submission-6684bf9d7b-w9xmf (unknown [10.110.208.100]) by director4.ghost.mail-out.ovh.net (Postfix) with ESMTPS id E6BAC1FE62; Thu, 2 Nov 2023 15:17:37 +0000 (UTC) Received: from ngraves.fr ([37.59.142.110]) by ghost-submission-6684bf9d7b-w9xmf with ESMTPSA id dCRiM5G9Q2VUGQEAkDpEYg (envelope-from ); Thu, 02 Nov 2023 15:17:37 +0000 X-OVh-ClientIp: 87.88.157.103 Date: Thu, 2 Nov 2023 16:16:50 +0100 Message-ID: <20231102151725.31362-4-ngraves@ngraves.fr> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20231102151725.31362-1-ngraves@ngraves.fr> References: <20231102151725.31362-1-ngraves@ngraves.fr> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Ovh-Tracer-Id: 13659980621468132066 X-VR-SPAMSTATE: OK X-VR-SPAMSCORE: 0 X-VR-SPAMCAUSE: gggruggvucftvghtrhhoucdtuddrgedvkedruddtiedgjeehucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuqfggjfdpvefjgfevmfevgfenuceurghilhhouhhtmecuhedttdenucenucfjughrpefhvfevufffkffojghfgggtgfesthekredtredtjeenucfhrhhomheppfhitgholhgrshcuifhrrghvvghsuceonhhgrhgrvhgvshesnhhgrhgrvhgvshdrfhhrqeenucggtffrrghtthgvrhhnpefhgfehieffffdvjedujeelkeefuefhueehvdetieehiefhteffueejjeeuteffjeenucffohhmrghinhepghgvthgtohhmphhoshgvrhdrohhrghdpughunhgvrdgsuhhilhgupdhgihhthhhusgdrtghomhdpghhnuhdrohhrghdpvgigrghmphhlvgdrtghomhenucfkphepuddvjedrtddrtddruddpkeejrdekkedrudehjedruddtfedpfeejrdehledrudegvddruddutdenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepihhnvghtpeduvdejrddtrddtrddupdhmrghilhhfrhhomhepoehnghhrrghvvghssehnghhrrghvvghsrdhfrheqpdhnsggprhgtphhtthhopedupdhrtghpthhtohepgedvfeefkeesuggvsggsuhhgshdrghhnuhdrohhrghdpoffvtefjohhsthepmhhoheekgedpmhhouggvpehsmhhtphhouhht X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-to: Nicolas Graves X-ACL-Warn: , Nicolas Graves via Guix-patches From: Nicolas Graves via Guix-patches via Errors-To: guix-patches-bounces+larch=yhetil.org@gnu.org Sender: guix-patches-bounces+larch=yhetil.org@gnu.org X-Migadu-Flow: FLOW_IN X-Migadu-Country: US X-Migadu-Queue-Id: CA6BB65D9F X-Migadu-Scanner: mx12.migadu.com X-Migadu-Spam-Score: -6.61 X-Spam-Score: -6.61 X-TUID: Y/5Bf4LxQQTE * guix/build-system/composer.scm: New file. * guix/build/composer-build-system.scm: New file. * gnu/packages/aux-files/findclass.php: New file. * Makefile.am: Add them. * doc/guix.texi (Build Systems): Document it. --- Makefile.am | 3 + doc/guix.texi | 16 +- gnu/packages/aux-files/findclass.php | 125 ++++++++++++++ guix/build-system/composer.scm | 162 ++++++++++++++++++ guix/build/composer-build-system.scm | 247 +++++++++++++++++++++++++++ tests/composer.scm | 36 ++-- 6 files changed, 568 insertions(+), 21 deletions(-) create mode 100644 gnu/packages/aux-files/findclass.php create mode 100644 guix/build-system/composer.scm create mode 100644 guix/build/composer-build-system.scm diff --git a/Makefile.am b/Makefile.am index 3fec98f064..47d1cb19ed 100644 --- a/Makefile.am +++ b/Makefile.am @@ -149,6 +149,7 @@ MODULES = \ guix/build-system/chicken.scm \ guix/build-system/clojure.scm \ guix/build-system/cmake.scm \ + guix/build-system/composer.scm \ guix/build-system/dub.scm \ guix/build-system/dune.scm \ guix/build-system/elm.scm \ @@ -206,6 +207,7 @@ MODULES = \ guix/build/cargo-utils.scm \ guix/build/chicken-build-system.scm \ guix/build/cmake-build-system.scm \ + guix/build/composer-build-system.scm \ guix/build/dub-build-system.scm \ guix/build/dune-build-system.scm \ guix/build/elm-build-system.scm \ @@ -411,6 +413,7 @@ dist_noinst_DATA = \ AUX_FILES = \ gnu/packages/aux-files/chromium/master-preferences.json \ gnu/packages/aux-files/emacs/guix-emacs.el \ + gnu/packages/aux-files/findclass.php \ gnu/packages/aux-files/guix.vim \ gnu/packages/aux-files/linux-libre/6.5-arm.conf \ gnu/packages/aux-files/linux-libre/6.5-arm64.conf \ diff --git a/doc/guix.texi b/doc/guix.texi index 0e64654715..6a1c0aeaa5 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -9554,6 +9554,20 @@ debugging information''), which roughly means that code is compiled with @code{-O2 -g}, as is the case for Autoconf-based packages by default. @end defvar +@defvar {Scheme Variable} composer-build-system +This variable is exported by @code{(guix build-system composer)}. It +implements the build procedure for packages using +@url{https://getcomposer.org/, Composer}, the PHP package manager. + +It automatically adds the @code{php} package to the set of inputs. Which +package is used can be specified with the @code{#:php} parameter. + +The @code{#:test-target} parameter is used to control which script is run +for the tests. By default, the @code{test} script is run if it exists. If +the script does not exist, the build system will run @code{phpunit} from the +source directory, assuming there is a @file{phpunit.xml} file. +@end defvar + @defvar dune-build-system This variable is exported by @code{(guix build-system dune)}. It supports builds of packages using @uref{https://dune.build/, Dune}, a build @@ -14550,7 +14564,7 @@ in Guix. @item composer @cindex Composer @cindex PHP -Import metadat from the @uref{https://getcomposer.org/, Composer} package +Import metadata from the @uref{https://getcomposer.org/, Composer} package archive used by the PHP community, as in this example: @example diff --git a/gnu/packages/aux-files/findclass.php b/gnu/packages/aux-files/findclass.php new file mode 100644 index 0000000000..d0b250c8e1 --- /dev/null +++ b/gnu/packages/aux-files/findclass.php @@ -0,0 +1,125 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * This file is copied from the Symfony package. + * + * (c) Fabien Potencier + * + * To the extent to wich it makes sense, as the author of the extract: + * Copyright © 2020 Julien Lepiller + */ + +/** + * Extract the classes in the given file + * + * @param string $path The file to check + * @throws \RuntimeException + * @return array The found classes + */ +function findClasses($path) +{ + $extraTypes = PHP_VERSION_ID < 50400 ? '' : '|trait'; + if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.3', '>=')) { + $extraTypes .= '|enum'; + } + // Use @ here instead of Silencer to actively suppress 'unhelpful' output + // @link https://github.com/composer/composer/pull/4886 + $contents = @php_strip_whitespace($path); + if (!$contents) { + if (!file_exists($path)) { + $message = 'File at "%s" does not exist, check your classmap definitions'; + } elseif (!is_readable($path)) { + $message = 'File at "%s" is not readable, check its permissions'; + } elseif ('' === trim(file_get_contents($path))) { + // The input file was really empty and thus contains no classes + return array(); + } else { + $message = 'File at "%s" could not be parsed as PHP, it may be binary or corrupted'; + } + $error = error_get_last(); + if (isset($error['message'])) { + $message .= PHP_EOL . 'The following message may be helpful:' . PHP_EOL . $error['message']; + } + throw new \RuntimeException(sprintf($message, $path)); + } + // return early if there is no chance of matching anything in this file + if (!preg_match('{\b(?:class|interface'.$extraTypes.')\s}i', $contents)) { + return array(); + } + // strip heredocs/nowdocs + $contents = preg_replace('{<<<[ \t]*([\'"]?)(\w+)\\1(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)(?:\s*)\\2(?=\s+|[;,.)])}s', 'null', $contents); + // strip strings + $contents = preg_replace('{"[^"\\\\]*+(\\\\.[^"\\\\]*+)*+"|\'[^\'\\\\]*+(\\\\.[^\'\\\\]*+)*+\'}s', 'null', $contents); + // strip leading non-php code if needed + if (substr($contents, 0, 2) !== '(?:[^<]++|<(?!\?))*+<\?}s', '?>'); + if (false !== $pos && false === strpos(substr($contents, $pos), '])(?Pclass|interface'.$extraTypes.') \s++ (?P[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:\-]*+) + | \b(?])(?Pnamespace) (?P\s++[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\s*+\\\\\s*+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+)? \s*+ [\{;] + ) + }ix', $contents, $matches); + $classes = array(); + $namespace = ''; + for ($i = 0, $len = count($matches['type']); $i < $len; $i++) { + if (!empty($matches['ns'][$i])) { + $namespace = str_replace(array(' ', "\t", "\r", "\n"), '', $matches['nsname'][$i]) . '\\'; + } else { + $name = $matches['name'][$i]; + // skip anon classes extending/implementing + if ($name === 'extends' || $name === 'implements') { + continue; + } + if ($name[0] === ':') { + // This is an XHP class, https://github.com/facebook/xhp + $name = 'xhp'.substr(str_replace(array('-', ':'), array('_', '__'), $name), 1); + } elseif ($matches['type'][$i] === 'enum') { + // In Hack, something like: + // enum Foo: int { HERP = '123'; } + // The regex above captures the colon, which isn't part of + // the class name. + $name = rtrim($name, ':'); + } + $classes[] = ltrim($namespace . $name, '\\'); + } + } + return $classes; +} + +$options = getopt('i:f:', []); +$file = $options["f"]; +$input = $options["i"]; + +$classes = findClasses($file); +foreach($classes as $class) { + echo '$classmap[\''.$class.'\'] = \''.$input.'/'.$file.'\';'; + echo "\n"; +} diff --git a/guix/build-system/composer.scm b/guix/build-system/composer.scm new file mode 100644 index 0000000000..8bf99ff9c5 --- /dev/null +++ b/guix/build-system/composer.scm @@ -0,0 +1,162 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2019 Julien Lepiller +;;; +;;; 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-system composer) + #:use-module (guix store) + #:use-module (guix utils) + #:use-module (guix derivations) + #:use-module (guix search-paths) + #:use-module (guix build-system) + #:use-module (guix build-system gnu) + #:use-module (guix gexp) + #:use-module (guix packages) + #:use-module (gnu packages) + #:use-module (ice-9 match) + #:use-module (srfi srfi-1) + #:export (%composer-build-system-modules + lower + composer-build + composer-build-system)) + +;; Commentary: +;; +;; Standard build procedure for PHP packages using Composer. This is implemented +;; as an extension of `gnu-build-system'. +;; +;; Code: + +(define (default-php) + "Return the default PHP package." + + ;; Do not use `@' to avoid introducing circular dependencies. + (let ((module (resolve-interface '(gnu packages php)))) + (module-ref module 'php))) + +(define (default-findclass) + "Return the default findclass script." + (search-auxiliary-file "findclass.php")) + +(define (default-composer-classloader) + "Return the default composer-classloader package." + + ;; Do not use `@' to avoid introducing circular dependencies. + (let ((module (resolve-interface '(gnu packages php-xyz)))) + (module-ref module 'composer-classloader))) + +(define %composer-build-system-modules + ;; Build-side modules imported by default. + `((guix build composer-build-system) + (guix build union) + (json) + (json builder) + (json parser) + (json record) + ,@%gnu-build-system-modules)) + +(define* (lower name + #:key source inputs native-inputs outputs system target + (php (default-php)) + (composer-classloader (default-composer-classloader)) + (findclass (default-findclass)) + #:allow-other-keys + #:rest arguments) + "Return a bag for NAME." + (define private-keywords + '(#:target #:php #:composer-classloader #:findclass #:inputs #:native-inputs)) + + (and (not target) ;XXX: no cross-compilation + (bag + (name name) + (system system) + (host-inputs `(,@(if source + `(("source" ,source)) + '()) + ,@inputs + + ;; Keep the standard inputs of 'gnu-build-system'. + ,@(standard-packages))) + (build-inputs `(("php" ,php) + ("findclass.php" ,findclass) + ("composer-classloader" ,composer-classloader) + ,@native-inputs)) + (outputs outputs) + (build composer-build) + (arguments (strip-keyword-arguments private-keywords arguments))))) + +(define* (composer-build name inputs + #:key + guile source + (outputs '("out")) + (configure-flags ''()) + (search-paths '()) + (out-of-source? #t) + (composer-file "composer.json") + (tests? #t) + (test-target "test") + (install-target "install") + (validate-runpath? #t) + (patch-shebangs? #t) + (strip-binaries? #t) + (strip-flags #~'("--strip-debug")) + (strip-directories #~'("lib" "lib64" "libexec" + "bin" "sbin")) + (phases '(@ (guix build composer-build-system) + %standard-phases)) + (system (%current-system)) + (imported-modules %composer-build-system-modules) + (modules '((guix build composer-build-system) + (guix build utils)))) + "Build SOURCE using PHP, and with INPUTS. This assumes that SOURCE provides +a 'composer.json' file as its build system." + (define builder + (with-imported-modules imported-modules + #~(begin + (use-modules #$@(sexp->gexp modules)) + + #$(with-build-variables inputs outputs + #~(composer-build + #:source #$source + #:system #$system + #:outputs %outputs + #:inputs %build-inputs + #:search-paths '#$(map search-path-specification->sexp + search-paths) + #:phases #$phases + #:out-of-source? #$out-of-source? + #:composer-file #$composer-file + #:tests? #$tests? + #:test-target #$test-target + #:install-target #$install-target + #:validate-runpath? #$validate-runpath? + #:patch-shebangs? #$patch-shebangs? + #:strip-binaries? #$strip-binaries? + #:strip-flags #$strip-flags + #:strip-directories #$strip-directories))))) + + (gexp->derivation name builder + #:system system + #:target #f + #:graft? #f + #:guile-for-build guile)) + +(define composer-build-system + (build-system + (name 'composer) + (description "The standard Composer build system") + (lower lower))) + +;;; composer.scm ends here diff --git a/guix/build/composer-build-system.scm b/guix/build/composer-build-system.scm new file mode 100644 index 0000000000..bcbae27021 --- /dev/null +++ b/guix/build/composer-build-system.scm @@ -0,0 +1,247 @@ +;;; 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" (if-specified-to-list identity)) + (classmap composer-autoload-classmap "classmap" + (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* (check #:key composer-file inputs outputs tests? test-target #:allow-other-keys) + "Test the given package." + (when tests? + (mkdir-p "vendor") + (create-autoload (string-append (getcwd) "/vendor") composer-file + (append inputs outputs) #:dev-dependencies? #t) + (let* ((package-data (read-package-data #:filename composer-file)) + (scripts (composer-package-scripts package-data)) + (test-script (assoc-ref scripts test-target)) + (dependencies (composer-package-require package-data)) + (dependencies-dev (composer-package-dev-require package-data)) + (name (composer-package-name package-data))) + (for-each + (match-lambda + ((_ . input) + (let ((bin (find-php-bin input))) + (when bin + (copy-recursively bin "vendor/bin"))))) + inputs) + (match test-script + ((? string? command) + (unless (zero? (system command)) + (throw 'failed-command command))) + (('@ (? string? command) ...) + (for-each + (lambda (c) + (unless (zero? (system c)) + (throw 'failed-command c))) + command)) + (#f (invoke "vendor/bin/phpunit")))))) + +(define (find-php-bin input) + (let* ((web-dir (string-append input "/share/web")) + (vendors (if (file-exists? web-dir) + (find-files web-dir "^vendor$" #:directories? #t) + #f))) + (match vendors + ((vendor) + (let ((bin (string-append vendor "/bin"))) + (and (file-exists? bin) bin))) + (_ #f)))) + +(define (find-php-dep inputs dependency) + (let loop ((inputs inputs)) + (match inputs + (() (throw 'unsatisfied-dependency "Unsatisfied dependency: required " + dependency)) + (((_ . input) inputs ...) + (let ((autoload (string-append input "/share/web/" dependency + "/vendor/autoload_conf.php"))) + (if (file-exists? autoload) + autoload + (loop inputs)))) + ((input inputs ...) + (let ((autoload (string-append input "/share/web/" dependency + "/vendor/autoload_conf.php"))) + (if (file-exists? autoload) + autoload + (loop inputs))))))) + +(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 + " $path) { + $loader->set($namespace, $path); +} +foreach ($psr4map as $namespace => $path) { + $loader->setPsr4($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 " +;;; Copyright © 2023 Nicolas Graves ;;; ;;; This file is part of GNU Guix. ;;; @@ -54,9 +55,6 @@ (define test-json (define test-source "foobar") -;; Avoid collisions with other tests. -(%http-server-port 10450) - (test-begin "composer") (test-assert "composer->guix-package" @@ -66,23 +64,21 @@ (define test-source (parameterize ((%composer-base-url (%local-url)) (current-http-proxy (%local-url))) (match (composer->guix-package "foo/bar") - (('package - ('name "php-foo-bar") - ('version "0.1") - ('source ('origin - ('method 'url-fetch) - ('uri "http://example.com/Bar-0.1.tar.gz") - ('sha256 - ('base32 - (? string? hash))))) - ('build-system 'composer-build-system) - ('native-inputs - ('quasiquote - (("php-phpunit-phpunit" ('unquote 'php-phpunit-phpunit))))) - ('synopsis "") - ('description "description") - ('home-page "http://example.com") - ('license 'license:bsd-3)) + (`(package + (name "php-foo-bar") + (version "0.1") + (source (origin + (method url-fetch) + (uri "http://example.com/Bar-0.1.tar.gz") + (sha256 + (base32 + ,(? string? hash))))) + (build-system composer-build-system) + (native-inputs (list php-phpunit-phpunit)) + (synopsis "") + (description "description") + (home-page "http://example.com") + (license license:bsd-3)) (string=? (bytevector->nix-base32-string (call-with-input-string test-source port-sha256)) hash)) -- 2.41.0