From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp2 ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms11 with LMTPS id 4PCqAQiOC1/4YAAA0tVLHw (envelope-from ) for ; Sun, 12 Jul 2020 22:26:16 +0000 Received: from aspmx1.migadu.com ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp2 with LMTPS id yJ0kOQeOC1+uOgAAB5/wlQ (envelope-from ) for ; Sun, 12 Jul 2020 22:26:15 +0000 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 6AD789403AD for ; Sun, 12 Jul 2020 22:26:15 +0000 (UTC) Received: from localhost ([::1]:36754 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jukQI-0004ua-AG for larch@yhetil.org; Sun, 12 Jul 2020 18:26:14 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:55178) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jukQ8-0004rw-T8 for guix-patches@gnu.org; Sun, 12 Jul 2020 18:26:04 -0400 Received: from debbugs.gnu.org ([209.51.188.43]:35470) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1jukQ8-0005Wq-Kx for guix-patches@gnu.org; Sun, 12 Jul 2020 18:26:04 -0400 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1jukQ8-0003eX-Gi for guix-patches@gnu.org; Sun, 12 Jul 2020 18:26:04 -0400 X-Loop: help-debbugs@gnu.org Subject: [bug#42338] [PATCH 03/34] guix: Add composer-build-system. Resent-From: Julien Lepiller Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Sun, 12 Jul 2020 22:26:04 +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 Received: via spool by 42338-submit@debbugs.gnu.org id=B42338.159459276113962 (code B ref 42338); Sun, 12 Jul 2020 22:26:04 +0000 Received: (at 42338) by debbugs.gnu.org; 12 Jul 2020 22:26:01 +0000 Received: from localhost ([127.0.0.1]:46983 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1jukQ2-0003cM-RG for submit@debbugs.gnu.org; Sun, 12 Jul 2020 18:26:01 -0400 Received: from lepiller.eu ([89.234.186.109]:42902) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1jukPw-0003aw-HR for 42338@debbugs.gnu.org; Sun, 12 Jul 2020 18:25:53 -0400 Received: from lepiller.eu (localhost [127.0.0.1]) by lepiller.eu (OpenSMTPD) with ESMTP id 2891cdb7 for <42338@debbugs.gnu.org>; Sun, 12 Jul 2020 22:25:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed; d=lepiller.eu; h=from:to :subject:date:message-id:in-reply-to:references:mime-version :content-type:content-transfer-encoding; s=dkim; bh=l56iDKkIW/PU 57U9GbIxtL0WZwDZzlpkKmybhnKZj8Q=; b=W3bUYDZZ1+YrzkdPSts/lZ3x0fg7 sEwmK1tacEkGE44WLBdgFX95/VqE8/4V8dGEN8YJ+OympRrf1XAs10sVzcNbo6C0 MIrYDZCMtwsZ8DySSL3+21yD4as5gSjV6bbKXpe9IbBRYL30OzMlE8q+Ibg4UvEZ BiNqDUFWNHcUV3+XJyWhf1o3t9cuaaq/ScZNBl9YgWtL1eTfBdfpd3kF7nr7qJf4 SVpwiPkZQcx8aCVBDqyCX2HdDTu2YwoWuUEGRvm5rxolvilGcHER7tIrbBvXzkG9 hixYy3PBrTKcAWjZWLr9gTtn2x2SqZieeqitxnAkWmlcdJB9o51RoM+9gQ== Received: by lepiller.eu (OpenSMTPD) with ESMTPSA id 93c9228c (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256:NO) for <42338@debbugs.gnu.org>; Sun, 12 Jul 2020 22:25:46 +0000 (UTC) From: Julien Lepiller Date: Mon, 13 Jul 2020 00:25:07 +0200 Message-Id: <20200712222538.18092-3-julien@lepiller.eu> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20200712222538.18092-1-julien@lepiller.eu> References: <20200713002055.1553f136@tachikoma.lepiller.eu> <20200712222538.18092-1-julien@lepiller.eu> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Score: 0.0 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-Spam-Score: -1.0 (-) X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+larch=yhetil.org@gnu.org Sender: "Guix-patches" X-Scanner: scn0 Authentication-Results: aspmx1.migadu.com; dkim=fail (rsa verify failed) header.d=lepiller.eu header.s=dkim header.b=W3bUYDZZ; dmarc=fail reason="SPF not aligned (relaxed)" header.from=lepiller.eu (policy=none); spf=pass (aspmx1.migadu.com: domain of guix-patches-bounces@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=guix-patches-bounces@gnu.org X-Spam-Score: 1.09 X-TUID: 4ST65fOq8Znt * guix/build-system/composer.scm: New file. * guix/build/composer-build-system.scm: New file. * guix/build-system/findclass.php: New file. * Makefile.am: Add them. * doc/guix.texi (Build Systems): Document it. --- Makefile.am | 2 + doc/guix.texi | 14 ++ guix/build-system/composer.scm | 169 ++++++++++++++++++++ guix/build-system/findclass.php | 102 ++++++++++++ guix/build/composer-build-system.scm | 224 +++++++++++++++++++++++++++ 5 files changed, 511 insertions(+) create mode 100644 guix/build-system/composer.scm create mode 100644 guix/build-system/findclass.php create mode 100644 guix/build/composer-build-system.scm diff --git a/Makefile.am b/Makefile.am index 623ddf32b2..363af872fa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -117,6 +117,7 @@ MODULES = \ guix/build-system/cargo.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/emacs.scm \ @@ -164,6 +165,7 @@ MODULES = \ guix/build/cargo-build-system.scm \ guix/build/cargo-utils.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/emacs-build-system.scm \ diff --git a/doc/guix.texi b/doc/guix.texi index 17338ed764..0613f669a0 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -6687,6 +6687,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 defvr +@defvr {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 defvr + @defvr {Scheme Variable} 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 diff --git a/guix/build-system/composer.scm b/guix/build-system/composer.scm new file mode 100644 index 0000000000..19455b1920 --- /dev/null +++ b/guix/build-system/composer.scm @@ -0,0 +1,169 @@ +;;; 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 (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." + (local-file (string-append (current-source-directory) "/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 json) + (guix build union) + ,@%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 + '(#:source #: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 store name inputs + #:key (guile #f) + (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 json) + (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 + `(begin + (use-modules ,@modules) + (composer-build #:source ,(match (assoc-ref inputs "source") + (((? derivation? source)) + (derivation->output-path source)) + ((source) + source) + (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))) + + (define guile-for-build + (match guile + ((? package?) + (package-derivation store guile system #:graft? #f)) + (#f ; the default + (let* ((distro (resolve-interface '(gnu packages commencement))) + (guile (module-ref distro 'guile-final))) + (package-derivation store guile system #:graft? #f))))) + + (build-expression->derivation store name builder + #:system system + #:inputs inputs + #:modules imported-modules + #:outputs outputs + #:guile-for-build guile-for-build)) + +(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-system/findclass.php b/guix/build-system/findclass.php new file mode 100644 index 0000000000..a9704f809c --- /dev/null +++ b/guix/build-system/findclass.php @@ -0,0 +1,102 @@ +=')) { + $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/composer-build-system.scm b/guix/build/composer-build-system.scm new file mode 100644 index 0000000000..3578b91954 --- /dev/null +++ b/guix/build/composer-build-system.scm @@ -0,0 +1,224 @@ +;;; 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 composer-build-system) + #:use-module ((guix build gnu-build-system) #:prefix gnu:) + #:use-module (guix build json) + #:use-module (guix build utils) + #:use-module (ice-9 match) + #:export (%standard-phases + composer-build)) + +;; Commentary: +;; +;; Builder-side code of the standard composer build procedure. +;; +;; Code: + +(define* (read-package-data #:key (filename "composer.json")) + (call-with-input-file filename + (lambda (port) + (read-json port)))) + +(define* (check #:key composer-file inputs outputs tests? test-target #:allow-other-keys) + "Install 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 (match (assoc-ref package-data "scripts") + (('@ script ...) script) + (#f '()))) + (test-script + (assoc-ref scripts test-target)) + (dependencies (filter (lambda (dep) (string-contains dep "/")) + (map car + (match (assoc-ref package-data "require") + (('@ dependency ...) dependency) + (#f '()))))) + (dependencies-dev + (filter (lambda (dep) (string-contains dep "/")) + (map car + (match (assoc-ref package-data "require-dev") + (('@ dependency ...) dependency) + (#f '()))))) + (name (assoc-ref package-data "name"))) + (for-each + (lambda (input) + (let ((bin (find-php-bin (cdr input)))) + (when bin + (copy-recursively bin "vendor/bin")))) + inputs) + (match test-script + ((? string? command) + (unless (equal? (system command) 0) + (throw 'failed-command command))) + (('@ (? string? command) ...) + (for-each + (lambda (c) + (unless (equal? (system c) 0) + (throw 'failed-command c))) + command)) + (#f (invoke "vendor/bin/phpunit"))))) + #t) + +(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 (map cdr inputs))) + (if (null? inputs) + (throw 'unsatisfied-dependency "Unsatisfied dependency: required " dependency) + (let ((autoload (string-append (car inputs) "/share/web/" dependency "/vendor/autoload_conf.php"))) + (if (file-exists? autoload) + autoload + (loop (cdr inputs))))))) + +(define* (create-autoload vendor composer-file inputs #:key dev-dependencies?) + (with-output-to-file (string-append vendor "/autoload.php") + (lambda _ + (format #t " $path) {~%") + (format #t " $loader->set($namespace, $path);~%") + (format #t "}~%") + (format #t "foreach ($psr4map as $namespace => $path) {~%") + (format #t " $loader->setPsr4($namespace, $path);~%") + (format #t "}~%") + (format #t "$loader->addClassMap($classmap);~%") + (format #t "$loader->register();~%"))) + (let* ((package-data (read-package-data #:filename composer-file)) + (autoload + (match (assoc-ref package-data "autoload") + (('@ autoload ...) autoload) + (#f '()))) + (autoload-dev + (match (assoc-ref package-data "autoload-dev") + (('@ autoload-dev ...) autoload-dev) + (#f '()))) + (dependencies (filter (lambda (dep) (string-contains dep "/")) + (map car + (match (assoc-ref package-data "require") + (('@ dependency ...) dependency) + (#f '()))))) + (dependencies-dev + (filter (lambda (dep) (string-contains dep "/")) + (map car + (match (assoc-ref package-data "require-dev") + (('@ dependency ...) dependency) + (#f '())))))) + (with-output-to-file (string-append vendor "/autoload_conf.php") + (lambda _ + (format #t "