guix: Add maven-build-system.

* guix/build-system/maven.scm: New file.
* guix/build/maven-build-system.scm: New file.
* Makefile.am (MODULES): Add them.
* doc/guix.texi (Build Systems): Document the maven build system.
This commit is contained in:
Julien Lepiller 2020-06-28 00:07:50 +02:00
parent 562408a8c0
commit 55b90c9053
No known key found for this signature in database
GPG Key ID: 53D457B2D636EE82
4 changed files with 421 additions and 0 deletions

View File

@ -132,6 +132,7 @@ MODULES = \
guix/build-system/haskell.scm \
guix/build-system/julia.scm \
guix/build-system/linux-module.scm \
guix/build-system/maven.scm \
guix/build-system/node.scm \
guix/build-system/perl.scm \
guix/build-system/python.scm \
@ -181,6 +182,7 @@ MODULES = \
guix/build/gnu-build-system.scm \
guix/build/gnu-dist.scm \
guix/build/guile-build-system.scm \
guix/build/maven-build-system.scm \
guix/build/node-build-system.scm \
guix/build/perl-build-system.scm \
guix/build/python-build-system.scm \

View File

@ -6811,6 +6811,48 @@ uuid, the package version, and a list of dependencies specified by their name
and their uuid.
@end defvr
@defvr {Scheme Variable} maven-build-system
This variable is exported by @code{(guix build-system maven)}. It implements
a build procedure for @uref{https://maven.apache.org, Maven} packages. Maven
is a dependency and lifecycle management tool for Java. A user of Maven
specifies dependencies and plugins in a @file{pom.xml} file that Maven reads.
When Maven does not have one of the dependencies or plugins in its repository,
it will download them and use them to build the package.
The maven build system ensures that maven will not try to download any
dependency by running in offline mode. Maven will fail if a dependency is
missing. Before running Maven, the @file{pom.xml} (and subprojects) are
modified to specify the version of dependencies and plugins that match the
versions available in the guix build environment. Dependencies and plugins
must be installed in the fake maven repository at @file{lib/m2}, and are
symlinked into a proper repository before maven is run. Maven is instructed
to use that repository for the build and installs built artifacts there.
Changed files are copied to the @file{lib/m2} directory of the package output.
You can specify a @file{pom.xml} file with the @code{#:pom-file} argument,
or let the build system use the default @file{pom.xml} file in the sources.
In case you need to specify a dependency's version manually, you can use the
@code{#:local-packages} argument. It takes an association list where the key
is the groupId of the package and its value is an association list where the
key is the artifactId of the package and its value is the version you want to
override in the @file{pom.xml}.
Some packages use dependencies or plugins that are not useful at runtime nor
at build time in Guix. You can alter the @file{pom.xml} file to remove them
using the @code{#:exclude} argument. Its value is an association list where
the key is the groupId of the plugin or dependency you want to remove, and
the value is a list of artifactId you want to remove.
You can override the default @code{jdk} and @code{maven} packages with the
corresponding argument, @code{#:jdk} and @code{#:maven}.
The @code{#:maven-plugins} argument is a list of maven plugins used during
the build, with the same format as the @code{inputs} fields of the package
declaration. Its default value is @code{%default-maven-plugins} which is
also exported.
@end defvr
@defvr {Scheme Variable} minify-build-system
This variable is exported by @code{(guix build-system minify)}. It
implements a minification procedure for simple JavaScript packages.

214
guix/build-system/maven.scm Normal file
View File

@ -0,0 +1,214 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2020 Julien Lepiller <julien@lepiller.eu>
;;;
;;; 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 <http://www.gnu.org/licenses/>.
(define-module (guix build-system maven)
#: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 packages)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:export (%maven-build-system-modules
default-maven
%default-maven-plugins
%default-exclude
lower
maven-build
maven-build-system))
;; Commentary:
;;
;; Standard build procedure for Maven packages. This is implemented as an
;; extension of `gnu-build-system'.
;;
;; Code:
(define %maven-build-system-modules
;; Build-side modules imported by default.
`((guix build maven-build-system)
(guix build maven pom)
,@%gnu-build-system-modules))
(define (default-maven)
"Return the default maven package."
;; Do not use `@' to avoid introducing circular dependencies.
(let ((module (resolve-interface '(gnu packages maven))))
(module-ref module 'maven)))
(define (default-maven-compiler-plugin)
"Return the default maven compiler plugin package."
;; Do not use `@' to avoid introducing circular dependencies.
(let ((module (resolve-interface '(gnu packages maven))))
(module-ref module 'maven-compiler-plugin)))
(define (default-maven-jar-plugin)
"Return the default maven jar plugin package."
;; Do not use `@' to avoid introducing circular dependencies.
(let ((module (resolve-interface '(gnu packages maven))))
(module-ref module 'maven-jar-plugin)))
(define (default-maven-resources-plugin)
"Return the default maven resources plugin package."
;; Do not use `@' to avoid introducing circular dependencies.
(let ((module (resolve-interface '(gnu packages maven))))
(module-ref module 'maven-resources-plugin)))
(define (default-maven-surefire-plugin)
"Return the default maven surefire plugin package."
;; Do not use `@' to avoid introducing circular dependencies.
(let ((module (resolve-interface '(gnu packages maven))))
(module-ref module 'maven-surefire-plugin)))
(define (default-java-surefire-junit4)
"Return the default surefire junit4 provider package."
;; Do not use `@' to avoid introducing circular dependencies.
(let ((module (resolve-interface '(gnu packages maven))))
(module-ref module 'java-surefire-junit4)))
(define (default-maven-install-plugin)
"Return the default maven install plugin package."
;; Do not use `@' to avoid introducing circular dependencies.
(let ((module (resolve-interface '(gnu packages maven))))
(module-ref module 'maven-install-plugin)))
(define (default-jdk)
"Return the default JDK package."
;; Lazily resolve the binding to avoid a circular dependency.
(let ((jdk-mod (resolve-interface '(gnu packages java))))
(module-ref jdk-mod 'icedtea)))
(define %default-maven-plugins
`(("maven-compiler-plugin" ,(default-maven-compiler-plugin))
("maven-jar-plugin" ,(default-maven-jar-plugin))
("maven-resources-plugin" ,(default-maven-resources-plugin))
("maven-surefire-plugin" ,(default-maven-surefire-plugin))
("java-surefire-junit4" ,(default-java-surefire-junit4))
("maven-install-plugin" ,(default-maven-install-plugin))))
(define %default-exclude
`(("org.apache.maven.plugins" .
("maven-release-plugin" "maven-site-plugin"))))
(define* (lower name
#:key source inputs native-inputs outputs system target
(maven (default-maven))
(jdk (default-jdk))
(maven-plugins %default-maven-plugins)
(local-packages '())
(exclude %default-exclude)
#:allow-other-keys
#:rest arguments)
"Return a bag for NAME."
(define private-keywords
'(#:source #:target #:jdk #:maven #:maven-plugins #: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 `(("maven" ,maven)
("jdk" ,jdk "jdk")
,@maven-plugins
,@native-inputs))
(outputs outputs)
(build maven-build)
(arguments (strip-keyword-arguments private-keywords arguments)))))
(define* (maven-build store name inputs
#:key (guile #f)
(outputs '("out"))
(search-paths '())
(out-of-source? #t)
(validate-runpath? #t)
(patch-shebangs? #t)
(strip-binaries? #t)
(exclude %default-exclude)
(local-packages '())
(tests? #t)
(strip-flags ''("--strip-debug"))
(strip-directories ''("lib" "lib64" "libexec"
"bin" "sbin"))
(phases '(@ (guix build maven-build-system)
%standard-phases))
(system (%current-system))
(imported-modules %maven-build-system-modules)
(modules '((guix build maven-build-system)
(guix build maven pom)
(guix build utils))))
"Build SOURCE using PATCHELF, and with INPUTS. This assumes that SOURCE
provides its own binaries."
(define builder
`(begin
(use-modules ,@modules)
(maven-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
#:exclude (quote ,exclude)
#:local-packages (quote ,local-packages)
#:tests? ,tests?
#:out-of-source? ,out-of-source?
#: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 maven-build-system
(build-system
(name 'maven)
(description "The standard Maven build system")
(lower lower)))
;;; maven.scm ends here

View File

@ -0,0 +1,163 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2020 Julien Lepiller <julien@lepiller.eu>
;;;
;;; 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 <http://www.gnu.org/licenses/>.
(define-module (guix build maven-build-system)
#:use-module ((guix build gnu-build-system) #:prefix gnu:)
#:use-module (guix build utils)
#:use-module (guix build maven pom)
#:use-module (ice-9 match)
#:export (%standard-phases
maven-build))
;; Commentary:
;;
;; Builder-side code of the standard maven build procedure.
;;
;; Code:
(define* (set-home #:key outputs inputs #:allow-other-keys)
(let ((home (string-append (getcwd) "/build-home")))
(setenv "HOME" home))
(setenv "JAVA_HOME" (assoc-ref inputs "jdk"))
#t)
(define* (configure #:key inputs #:allow-other-keys)
(let* ((m2-files (map
(lambda (input)
(match input
((name . dir)
(let ((m2-dir (string-append dir "/lib/m2")))
(if (file-exists? m2-dir) m2-dir #f)))))
inputs))
(m2-files (filter (lambda (a) a) m2-files)))
(for-each
(lambda (m2-dir)
(for-each
(lambda (file)
(let ((dir (string-append (getenv "HOME") "/.m2/repository/"
(dirname file))))
(mkdir-p dir)
(symlink (string-append m2-dir "/" file)
(string-append dir "/" (basename file)))))
(with-directory-excursion m2-dir
(find-files "." ".*.(jar|pom)$"))))
m2-files))
(invoke "mvn" "-v")
#t)
(define (add-local-package local-packages group artifact version)
(define (alist-set lst key val)
(match lst
('() (list (cons key val)))
(((k . v) lst ...)
(if (equal? k key)
(cons (cons key val) lst)
(cons (cons k v) (alist-set lst key val))))))
(alist-set local-packages group
(alist-set (or (assoc-ref local-packages group) '()) artifact
version)))
(define (fix-pom pom-file inputs local-packages excludes)
(chmod pom-file #o644)
(format #t "fixing ~a~%" pom-file)
(fix-pom-dependencies pom-file (map cdr inputs)
#:with-plugins? #t #:with-build-dependencies? #t
#:local-packages local-packages
#:excludes excludes)
(let* ((pom (get-pom pom-file))
(java-inputs (map cdr inputs))
(artifact (pom-artifactid pom))
(group (pom-groupid pom java-inputs local-packages))
(version (pom-version pom java-inputs local-packages)))
(let loop ((modules (pom-ref pom "modules"))
(local-packages
(add-local-package local-packages group artifact version)))
(pk 'local-packages local-packages)
(match modules
(#f local-packages)
('() local-packages)
(((? string? _) modules ...)
(loop modules local-packages))
(((_ module) modules ...)
(loop
modules
(fix-pom (string-append (dirname pom-file) "/" module "/pom.xml")
inputs local-packages excludes)))))))
(define* (fix-pom-files #:key inputs local-packages exclude #:allow-other-keys)
(fix-pom "pom.xml" inputs local-packages exclude))
(define* (build #:key outputs #:allow-other-keys)
"Build the given package."
(invoke "mvn" "package"
;; offline mode: don't download dependencies
"-o"
;, set directory where dependencies are installed
(string-append "-Duser.home=" (getenv "HOME")))
#t)
(define* (check #:key tests? #:allow-other-keys)
"Check the given package."
(when tests?
(invoke "mvn" "test"
(string-append "-Duser.home=" (getenv "HOME"))
"-e"))
#t)
(define* (install #:key outputs #:allow-other-keys)
"Install the given package."
(let* ((out (assoc-ref outputs "out"))
(java (string-append out "/lib/m2")))
(invoke "mvn" "install" "-o" "-e"
"-DskipTests"
(string-append "-Duser.home=" (getenv "HOME")))
;; Go through the repository to find files that can be installed
(with-directory-excursion (string-append (getenv "HOME") "/.m2/repository")
(let ((installable
(filter (lambda (file)
(not (eq? 'symlink (stat:type (lstat file)))))
(find-files "." "."))))
(mkdir-p java)
(for-each
(lambda (file)
(mkdir-p (string-append java "/" (dirname file)))
(copy-file file (string-append java "/" file)))
installable)))
;; Remove some files that are not required and introduce timestamps
(for-each delete-file (find-files out "maven-metadata-local.xml"))
(for-each delete-file (find-files out "_remote.repositories")))
#t)
(define %standard-phases
;; Everything is as with the GNU Build System except for the `configure'
;; , `build', `check' and `install' phases.
(modify-phases gnu:%standard-phases
(delete 'bootstrap)
(add-before 'configure 'set-home set-home)
(replace 'configure configure)
(add-after 'configure 'fix-pom-files fix-pom-files)
(replace 'build build)
(replace 'check check)
(replace 'install install)))
(define* (maven-build #:key inputs (phases %standard-phases)
#:allow-other-keys #:rest args)
"Build the given package, applying all of PHASES in order."
(apply gnu:gnu-build #:inputs inputs #:phases phases args))
;;; maven-build-system.scm ends here