diff mbox series

[bug#54796,v3,22/22] build-system: Add 'rebar3-build-system'.

Message ID 4227ca2ef20c4ea7f6bb452dba271baad877a93f.1649436567.git.h.goebel@crazy-compilers.com
State Accepted
Headers show
Series Add importer for hex.pm and rebar3 build-system for Erlang | expand

Commit Message

Hartmut Goebel April 8, 2022, 5:03 p.m. UTC
* guix/build-system/rebar3.scm, guix/build/rebar3-build-system.scm: New files.
* Makefile.am (MODULES): Register them.
* doc/guix.texi (Build Systems): Document it.
---
 Makefile.am                        |   2 +
 doc/guix.texi                      |  41 ++++++++
 guix/build-system/rebar3.scm       | 143 ++++++++++++++++++++++++++++
 guix/build/rebar3-build-system.scm | 147 +++++++++++++++++++++++++++++
 4 files changed, 333 insertions(+)
 create mode 100644 guix/build-system/rebar3.scm
 create mode 100644 guix/build/rebar3-build-system.scm

Comments

M April 9, 2022, 1:16 p.m. UTC | #1
Hartmut Goebel schreef op vr 08-04-2022 om 19:03 [+0200]:

> * guix/build-system/rebar3.scm, guix/build/rebar3-build-system.scm: New files.

This patch seems out-of-order.  Wouldn't rebar3-build-system need to be
added before packages using rebar3-build-system?

Greetings,
Maxime.
M April 9, 2022, 1:19 p.m. UTC | #2
Hartmut Goebel schreef op vr 08-04-2022 om 19:03 [+0200]:
> +         (system system)
> +         (host-inputs `(,@(if source
> +                              `(("source" ,source))
> +                              '())
> +                        ,@inputs
> +                         ;; Keep the standard inputs of 'gnu-build-system'.
> +                         ,@(standard-packages)))

(standard-packages) needs to be in 'build-inputs', at least according
to 'lower' in (guix build-system gnu).

> +         (build-inputs `(("rebar" ,rebar)
> +                         ("erlang" ,erlang) ;; for escriptize
> +                         ,@native-inputs))

Greetings,
Maxime.
Hartmut Goebel April 9, 2022, 4:04 p.m. UTC | #3
Am 09.04.22 um 15:16 schrieb Maxime Devos:
> This patch seems out-of-order.  Wouldn't rebar3-build-system need to be
> added before packages using rebar3-build-system?

However you like. Just tell me.

Since the rebar3 build-system requires rebar3, any way round results in 
a hen-and-egg-problem.
Hartmut Goebel April 9, 2022, 4:28 p.m. UTC | #4
Am 09.04.22 um 15:19 schrieb Maxime Devos:
> (standard-packages) needs to be in 'build-inputs', at least according
> to 'lower' in (guix build-system gnu).

Build-systems are inconsistent here: texlive, ruby, python have them in 
host-inputs. gnu, cmake, qt have them in build-inputs. Seems like thus 
generating platform independent code tend to have it in host, the others 
in build.

Can you elaborate the criteria which to choose? (I also just created 
https://issues.guix.gnu.org/54820 for this)
M April 9, 2022, 5:57 p.m. UTC | #5
Hartmut Goebel schreef op za 09-04-2022 om 18:28 [+0200]:
> Am 09.04.22 um 15:19 schrieb Maxime Devos:
> > (standard-packages) needs to be in 'build-inputs', at least according
> > to 'lower' in (guix build-system gnu).
> 
> Build-systems are inconsistent here: texlive, ruby, python have them in 
> host-inputs. gnu, cmake, qt have them in build-inputs. Seems like thus 
> generating platform independent code tend to have it in host,

ruby and python are, from Guix POV, not platform-independent code, as
they may have baked-in references to platform-specific binaries.
Their build systems don't support cross-compilation yet though ...

>  the others 
> in build.
> 
> Can you elaborate the criteria which to choose? (I also just created 
> https://issues.guix.gnu.org/54820 for this)

Always build-inputs -- some of the not-yet-cross-compiling build
systems do it different, but maybe they will gain cross-compilation
someday, and to do that, (standard-packages) needs to be in the right
place!  I've added some info to <https://issues.guix.gnu.org/54820>.

Greetings,
Maxime.
M April 9, 2022, 8:56 p.m. UTC | #6
Hartmut Goebel schreef op za 09-04-2022 om 18:04 [+0200]:
> Am 09.04.22 um 15:16 schrieb Maxime Devos:
> > This patch seems out-of-order.  Wouldn't rebar3-build-system need to be
> > added before packages using rebar3-build-system?
> 
> However you like. Just tell me.
> 
> Since the rebar3 build-system requires rebar3, any way round results in 
> a hen-and-egg-problem.
> 

Oh right.  I would do the rebar3 build-system before rebar3.  Then the
packages stil cannot be for quite a few commit, but at there won't be
'Unbound variable: rebar3-build-system’ errors when doing a bisection
with git.

The guix-data-service might still not like it though, so CC'ing cbaines
for input.

Greetings,
Maxime.
Hartmut Goebel April 10, 2022, 6:47 p.m. UTC | #7
Am 09.04.22 um 19:57 schrieb Maxime Devos:
> Always build-inputs

Done.
Hartmut Goebel April 10, 2022, 6:49 p.m. UTC | #8
Am 09.04.22 um 22:56 schrieb Maxime Devos:
> Oh right.  I would do the rebar3 build-system before rebar3.  Then the
> packages stil cannot be for quite a few commit, but at there won't be
> 'Unbound variable: rebar3-build-system’ errors when doing a bisection
> with git.

Good point. Changed.
diff mbox series

Patch

diff --git a/Makefile.am b/Makefile.am
index be2c183206..bb538222b3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -164,6 +164,7 @@  MODULES =					\
   guix/build-system/waf.scm			\
   guix/build-system/r.scm			\
   guix/build-system/rakudo.scm			\
+  guix/build-system/rebar3.scm			\
   guix/build-system/ruby.scm			\
   guix/build-system/scons.scm			\
   guix/build-system/texlive.scm			\
@@ -217,6 +218,7 @@  MODULES =					\
   guix/build/r-build-system.scm			\
   guix/build/renpy-build-system.scm			\
   guix/build/rakudo-build-system.scm		\
+  guix/build/rebar3-build-system.scm		\
   guix/build/ruby-build-system.scm		\
   guix/build/scons-build-system.scm		\
   guix/build/texlive-build-system.scm		\
diff --git a/doc/guix.texi b/doc/guix.texi
index 6c17b26d70..518fabf48d 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -8717,6 +8717,47 @@  with @code{#:zef} or removed by passing @code{#f} to the
 @code{with-zef?} parameter.
 @end defvr
 
+@defvr {Scheme Variable} rebar3-build-system
+This variable is exported by @code{(guix build-system rebar3)}.  It
+implements a build procedure around @code{rebar3}.
+
+It adds both @code{rebar3} and the @code{erlang} to the set of inputs.
+Different packages can be specified with the @code{#:rebar} and
+@code{#:erlang} parameters, respectively.
+
+This build system is based on @code{gnu-build-system}, but with the
+following phases changed:
+
+@table @code
+
+@item unpack
+This phase, after unpacking the source like the @code{gnu-build-system}
+does, checks for a file @code{contents.tar.gz} at the top-level of the
+source.  If this file exists, it will be unpacked, too.  This eases
+handling of package hosted by @i{hex.pm}.
+
+@item bootstrap
+@item configure
+There are no @code{bootstrap} and @code{configure} phase because erlang
+packages typically don’t need to be configured.
+
+@item build
+This phase runs @code{rebar3 compile}
+with the flags listed in @code{#:rebar-flags}.
+
+@item check
+Unless @code{#:tests? #f} is passed,
+this phase runs @code{rebar3 eunit},
+or some other target specified with @code{#:test-target},
+with the flags listed in @code{#:rebar-flags},
+
+@item install
+This installs the files created in the @i{default} profile, or some
+other profile specified with @code{#:install-profile}.
+
+@end table
+@end defvr
+
 @defvr {Scheme Variable} texlive-build-system
 This variable is exported by @code{(guix build-system texlive)}.  It is
 used to build TeX packages in batch mode with a specified engine.  The
diff --git a/guix/build-system/rebar3.scm b/guix/build-system/rebar3.scm
new file mode 100644
index 0000000000..b96c806258
--- /dev/null
+++ b/guix/build-system/rebar3.scm
@@ -0,0 +1,143 @@ 
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2016 Ricardo Wurmus <rekado@elephly.net>
+;;; Copyright © 2020 Hartmut Goebel <h.goebel@crazy-compilers.com>
+;;;
+;;; 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 rebar3)
+  #:use-module (guix store)
+  #:use-module (guix utils)
+  #:use-module (guix gexp)
+  #:use-module (guix packages)
+  #:use-module (guix monads)
+  #:use-module (guix search-paths)
+  #:use-module (guix build-system)
+  #:use-module (guix build-system gnu)
+  #:use-module ((guix hexpm-download) #:select (hexpm-uri) #:prefix dl:)
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-26)
+  #:export (hexpm-uri
+            %rebar3-build-system-modules
+            rebar3-build
+            rebar3-build-system))
+
+;;
+;; Standard build procedure for Erlang packages using Rebar3.
+;;
+
+(define hexpm-uri dl:hexpm-uri)
+
+(define %rebar3-build-system-modules
+  ;; Build-side modules imported by default.
+  `((guix build rebar3-build-system)
+    ,@%gnu-build-system-modules))
+
+(define (default-rebar3)
+  "Return the default Rebar3 package."
+  ;; Lazily resolve the binding to avoid a circular dependency.
+  (let ((erlang-mod (resolve-interface '(gnu packages erlang))))
+    (module-ref erlang-mod 'rebar3)))
+
+(define (default-erlang)
+  "Return the default Erlang package."
+  ;; Lazily resolve the binding to avoid a circular dependency.
+  (let ((erlang-mod (resolve-interface '(gnu packages erlang))))
+    (module-ref erlang-mod 'erlang)))
+
+(define* (lower name
+                #:key source inputs native-inputs outputs system target
+                (rebar (default-rebar3))
+                (erlang (default-erlang))
+                #:allow-other-keys
+                #:rest arguments)
+  "Return a bag for NAME from the given arguments."
+  (define private-keywords
+    '(#:target #:rebar #:erlang #: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 `(("rebar" ,rebar)
+                         ("erlang" ,erlang) ;; for escriptize
+                         ,@native-inputs))
+         (outputs outputs)
+         (build rebar3-build)
+         (arguments (strip-keyword-arguments private-keywords arguments)))))
+
+(define* (rebar3-build name inputs
+                       #:key
+                       guile source
+                       (rebar-flags ''("skip_deps=true" "-vv"))
+                       (tests? #t)
+                       (test-target "eunit")
+                       ;; TODO: install-name  ; default: based on guix package name
+                       (install-profile "default")
+                       (phases '(@ (guix build rebar3-build-system)
+                                   %standard-phases))
+                       (outputs '("out"))
+                       (search-paths '())
+                       (native-search-paths '())
+                       (system (%current-system))
+                       (imported-modules %rebar3-build-system-modules)
+                       (modules '((guix build rebar3-build-system)
+                                  (guix build utils))))
+  "Build SOURCE with INPUTS."
+
+  (define builder
+    (with-imported-modules imported-modules
+      #~(begin
+          (use-modules #$@(sexp->gexp modules))
+
+          #$(with-build-variables inputs outputs
+              #~(rebar3-build #:source #+source
+                      #:system #$system
+                      #:name #$name
+                      #:rebar-flags #$rebar-flags
+                      #:tests? #$tests?
+                      #:test-target #$test-target
+                      ;; TODO: #:install-name #$install-name
+                      #:install-profile #$install-profile
+                      #:phases #$(if (pair? phases)
+                                     (sexp->gexp phases)
+                                     phases)
+                      #:outputs %outputs
+                      #:search-paths '#$(sexp->gexp
+                                         (map search-path-specification->sexp
+                                              search-paths))
+                      #:inputs %build-inputs)))))
+
+  (mlet %store-monad ((guile (package->derivation (or guile (default-guile))
+                                                  system #:graft? #f)))
+    ;; Note: Always pass #:graft? #f.  Without it, ALLOWED-REFERENCES &
+    ;; co. would be interpreted as referring to grafted packages.
+    (gexp->derivation name builder
+                      #:system system
+                      #:target #f
+                      #:graft? #f
+                      #:guile-for-build guile)))
+
+(define rebar3-build-system
+  (build-system
+    (name 'rebar3)
+    (description "The standard Rebar3 build system")
+    (lower lower)))
diff --git a/guix/build/rebar3-build-system.scm b/guix/build/rebar3-build-system.scm
new file mode 100644
index 0000000000..ae18538ed3
--- /dev/null
+++ b/guix/build/rebar3-build-system.scm
@@ -0,0 +1,147 @@ 
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2016, 2018 Ricardo Wurmus <rekado@elephly.net>
+;;; Copyright © 2019 Björn Höfling <bjoern.hoefling@bjoernhoefling.de>
+;;; Copyright © 2020, 2022 Hartmut Goebel <h.goebel@crazy-compilers.com>
+;;;
+;;; 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 rebar3-build-system)
+  #:use-module ((guix build gnu-build-system) #:prefix gnu:)
+  #:use-module ((guix build utils) #:hide (delete))
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 ftw)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-26)
+  #:export (rebar3-build
+            %standard-phases))
+
+;;
+;; Builder-side code of the standard build procedure for Erlang packages using
+;; rebar3.
+;;
+;; TODO: Think about whether bindir ("ebin"), libdir ("priv") and includedir
+;; "(include") need to be configurable
+
+(define %erlang-libdir "/lib/erlang/lib")
+
+(define* (erlang-depends #:key inputs #:allow-other-keys)
+  (define input-directories
+    (match inputs
+      (((_ . dir) ...)
+       dir)))
+  (mkdir-p "_checkouts")
+
+  (for-each
+   (lambda (input-dir)
+     (let ((elibdir (string-append input-dir %erlang-libdir)))
+       (when (directory-exists? elibdir)
+         (for-each
+          (lambda (dirname)
+            (let ((dest (string-append elibdir "/" dirname))
+                  (link (string-append "_checkouts/" dirname)))
+              (when (not (file-exists? link))
+                ;; RETHINK: Maybe better copy and make writable to avoid some
+                ;; error messages e.g. when using with rebar3-git-vsn.
+                (symlink dest link))))
+          (list-directories elibdir)))))
+   input-directories))
+
+(define* (unpack #:key source #:allow-other-keys)
+  "Unpack SOURCE in the working directory, and change directory within the
+source.  When SOURCE is a directory, copy it in a sub-directory of the current
+working directory."
+  (let ((gnu-unpack (assoc-ref gnu:%standard-phases 'unpack)))
+    (gnu-unpack #:source source)
+    ;; Packages from hex.pm typically have a contents.tar.gz containing the
+    ;; actual source. If this tar file exists, extract it.
+    (when (file-exists? "contents.tar.gz")
+      (invoke "tar" "xvf" "contents.tar.gz"))))
+
+(define* (build #:key (rebar-flags '()) #:allow-other-keys)
+  (apply invoke `("rebar3" "compile" ,@rebar-flags)))
+
+(define* (check #:key target (rebar-flags '()) (tests? (not target))
+                (test-target "eunit")
+                #:allow-other-keys)
+  (if tests?
+      (apply invoke `("rebar3" ,test-target ,@rebar-flags))
+      (format #t "test suite not run~%")))
+
+(define (erlang-package? name)
+  "Check if NAME correspond to the name of an Erlang package."
+  (string-prefix? "erlang-" name))
+
+(define (package-name-version->erlang-name name+ver)
+  "Convert the Guix package NAME-VER to the corresponding Erlang name-version
+format.  Essentially drop the prefix used in Guix and replace dashes by
+underscores."
+  (let* ((name- (package-name->name+version name+ver)))
+    (string-join
+     (string-split
+      (if (erlang-package? name-)  ; checks for "erlang-" prefix
+          (string-drop name- (string-length "erlang-"))
+          name-)
+      #\-)
+     "_")))
+
+(define (list-directories directory)
+  "Return file names of the sub-directory of DIRECTORY."
+  (scandir directory
+           (lambda (file)
+             (and (not (member file '("." "..")))
+                  (file-is-directory? (string-append directory "/" file))))))
+
+(define* (install #:key name outputs
+                  (install-name (package-name-version->erlang-name name))
+                  (install-profile "default") ; build profile outputs to install
+                  #:allow-other-keys)
+  (let* ((out (assoc-ref outputs "out"))
+         (pkg-dir (string-append out %erlang-libdir "/" install-name)))
+    (let ((bin-dir (string-append "_build/" install-profile "/bin"))
+          (lib-dir (string-append "_build/" install-profile "/lib")))
+      ;; install _build/PROFILE/bin
+      (when (file-exists? bin-dir)
+        (copy-recursively bin-dir out #:follow-symlinks? #t))
+      ;; install _build/PROFILE/lib/*/{ebin,include,priv}
+      (for-each
+       (lambda (*)
+         (for-each
+          (lambda (dirname)
+            (let ((src-dir (string-append lib-dir "/" * "/" dirname))
+                  (dst-dir (string-append pkg-dir "/" dirname)))
+              (when (file-exists? src-dir)
+                (copy-recursively src-dir dst-dir #:follow-symlinks? #t))
+              (false-if-exception
+               (delete-file (string-append dst-dir "/.gitignore")))))
+          '("ebin" "include" "priv")))
+       (list-directories lib-dir))
+      (false-if-exception
+       (delete-file (string-append pkg-dir "/priv/Run-eunit-loop.expect"))))))
+
+(define %standard-phases
+  (modify-phases gnu:%standard-phases
+    (replace 'unpack unpack)
+    (delete 'bootstrap)
+    (delete 'configure)
+    (add-before 'build 'erlang-depends erlang-depends)
+    (replace 'build build)
+    (replace 'check check)
+    (replace 'install install)))
+
+(define* (rebar3-build #:key inputs (phases %standard-phases)
+                       #:allow-other-keys #:rest args)
+  "Build the given Erlang package, applying all of PHASES in order."
+  (apply gnu:gnu-build #:inputs inputs #:phases phases args))