diff mbox series

[bug#66801] build-system: Add mix-build-system.

Message ID 1b3bc0974f5c26e61c95e10a376c7b28f43d2e06.1701988060.git.contact@phfrohring.com
State New
Headers show
Series [bug#66801] build-system: Add mix-build-system. | expand

Commit Message

Pierre-Henry Fröhring Dec. 7, 2023, 10:34 p.m. UTC
* guix/build-system/mix.scm: New file.
* guix/build/mix-build-system.scm: New file.

Change-Id: I8066d00f7ada4a384621bf541e679bc512e93435
---
 guix/build-system/mix.scm       | 186 ++++++++++++++++++++++++++++++++
 guix/build/mix-build-system.scm | 161 +++++++++++++++++++++++++++
 2 files changed, 347 insertions(+)
 create mode 100644 guix/build-system/mix.scm
 create mode 100644 guix/build/mix-build-system.scm


base-commit: 06f25a9a85be1bbe7a709e58ce41c1a834e5f1ae

Comments

Liliana Marie Prikler Dec. 8, 2023, 7:25 a.m. UTC | #1
Am Donnerstag, dem 07.12.2023 um 23:34 +0100 schrieb Pierre-Henry
Fröhring:
> * guix/build-system/mix.scm: New file.
> * guix/build/mix-build-system.scm: New file.
> 
> Change-Id: I8066d00f7ada4a384621bf541e679bc512e93435
> ---
Pretty sure you forgot the reroll-count in the header.  Please don't :)

>  guix/build-system/mix.scm       | 186
> ++++++++++++++++++++++++++++++++
>  guix/build/mix-build-system.scm | 161 +++++++++++++++++++++++++++
>  2 files changed, 347 insertions(+)
>  create mode 100644 guix/build-system/mix.scm
>  create mode 100644 guix/build/mix-build-system.scm
> 
> diff --git a/guix/build-system/mix.scm b/guix/build-system/mix.scm
> new file mode 100644
> index 000000000..1b04053d7
> --- /dev/null
> +++ b/guix/build-system/mix.scm
> @@ -0,0 +1,186 @@
> +;;; GNU Guix --- Functional package management for GNU
> +;;; Copyright © 2023 Pierre-Henry Fröhring <contact@phfrohring.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/>.
> +
> +;; Commentary:
> +;;
> +;; Standard build procedure for Elixir packages using 'mix'.  This
> is
> +;; implemented as an extension of 'gnu-build-system'.
> +;;
> +;; Code:
> +
> +(define-module (guix build-system mix)
> +  #:use-module (guix build mix-build-system)
> +  #:use-module (guix build-system gnu)
> +  #:use-module (guix build-system)
> +  #:use-module (guix gexp)
> +  #:use-module (guix monads)
> +  #:use-module (guix packages)
> +  #:use-module (guix search-paths)
> +  #:use-module (guix store)
> +  #:use-module (guix utils)
> +  #:use-module (ice-9 match)
> +  #:use-module (srfi srfi-1)
> +  #:use-module (srfi srfi-26)
> +  #:export (mix-build-system hexpm-uri))
> +
> +;; Lazily resolve bindings to avoid circular dependencies.
> +(define (default-glibc-utf8-locales)
> +  (let* ((base (resolve-interface '(gnu packages base))))
> +    (module-ref base 'glibc-utf8-locales)))
> +
> +(define (default-elixir-hex)
> +  (let ((elixir (resolve-interface '(gnu packages elixir))))
> +    (module-ref elixir 'elixir-hex)))
> +
> +(define (default-rebar3)
> +  (let ((erlang (resolve-interface '(gnu packages erlang))))
> +    (module-ref erlang 'rebar3)))
> +
> +(define (default-elixir)
> +  (let ((elixir (resolve-interface '(gnu packages elixir))))
> +    (module-ref elixir 'elixir)))
> +
> +(define* (strip-prefix name #:optional (prefix "elixir-"))
> +  "Return NAME without the prefix PREFIX."
> +  (if (string-prefix? prefix name)
> +      (string-drop name (string-length prefix))
> +      name))
> +
> +(define (hexpm-uri name version)
> +  "Return the URI where to fetch the sources of a Hex package NAME
> at VERSION.
> +NAME is the name of the package which should look like: elixir-pkg-
> name-X.Y.Z
> +See: https://github.com/hexpm/specifications/blob/main/endpoints.md"
> +  ((compose
> +    (cute string-append "https://repo.hex.pm/tarballs/" <> "-"
> version ".tar")
> +    (cute string-replace-substring <> "-" "_")
> +    strip-prefix)
> +   name))
> +
> +;; A number of environment variables specific to the Mix build
> system are
> +;; reflected here.  They are documented at
> +;;
> https://hexdocs.pm/mix/1.15.7/Mix.html#module-environment-variables. 
> Other
> +;; parameters located in mix.exs are defined at
> +;;
> https://hexdocs.pm/mix/1.15.7/Mix.Project.html#module-configuration
> +(define* (mix-build name
> +                    inputs
> +                    #:key
> +                    source
> +                    (tests? #t)
> +                    (mix-path #f) ;See MIX_PATH.
> +                    (mix-exs "mix.exs") ;See MIX_EXS.
> +                    (build-per-environment #t) ;See
> :build_per_environment.
> +                    (phases '%standard-phases)
> +                    (outputs '("out"))
> +                    (search-paths '())
> +                    (system (%current-system))
> +                    (guile #f)
> +                    (imported-modules `((guix build mix-build-
> system)
> +                                        ,@%gnu-build-system-
> modules))
> +                    (modules '((guix build mix-build-system)
> +                               (guix build utils))))
> +  "Build SOURCE using Elixir, and with INPUTS."
> +
> +  ;; Check the documentation of :build_per_environment here:
> +  ;;
> https://hexdocs.pm/mix/1.15.7/Mix.Project.html#module-configuration A
> nd
> +  ;; "Environments" here:
> +  ;; https://hexdocs.pm/mix/1.15.7/Mix.html#module-environments
> +  (define mix-environments
> +    (if build-per-environment
> +        `("prod" ,@(if tests? '("test") '()))
> +        '("shared")))
> +
> +  (define builder
> +    (with-imported-modules imported-modules
> +      #~(begin
> +
> +          (use-modules #$@(sexp->gexp modules))
> +
> +          #$(with-build-variables inputs outputs
> +              #~(mix-build #:name #$name
> +                           #:source #+source
> +                           #:system #$system
> +                           #:tests? #$tests?
> +                           #:mix-path #$mix-path
> +                           #:mix-exs #$mix-exs
> +                           #:mix-environments '#$mix-environments
> +                           #:build-per-environment #$build-per-
> environment
> +                           #: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)))
> +    (gexp->derivation name
> +                      builder
> +                      #:system system
> +                      #:graft? #f       ;consistent with 'gnu-build'
> +                      #:target #f
> +                      #:guile-for-build guile)))
> +
> +(define* (lower name
> +                #:key
> +                (elixir (default-elixir))
> +                (elixir-hex (default-elixir-hex))
> +                (glibc-utf8-locales (default-glibc-utf8-locales))
> +                (inputs '())
> +                (native-inputs '())
> +                (propagated-inputs '())
> +                (rebar3 (default-rebar3))
> +                (tests? #t)
> +                outputs
> +                source
> +                system
> +                target
> +                #:allow-other-keys #:rest arguments)
> +  "Return a bag for NAME."
> +  (let ((private-keywords
> +         '(#:inputs #:native-inputs
> +           #:outputs #:system #:target
> +           #:elixir #:elixir-hex #:glibc-utf8-locales
> +           #:rebar3 #:erlang))
> +        (build-inputs
> +         `(,@(standard-packages)
> +           ("glibc-utf8-locales" ,glibc-utf8-locales)
> +           ("erlang" ,(lookup-package-input elixir "erlang"))
> +           ("rebar3" ,rebar3)
> +           ("elixir" ,elixir)
> +           ("elixir-hex" ,elixir-hex)
> +           ,@inputs
> +           ,@native-inputs)))
> +  (bag (name name)
> +       (system system)
> +       (build-inputs build-inputs)
> +       (host-inputs (if target inputs '()))
> +       (outputs outputs)
> +       (build mix-build)
> +       (arguments (strip-keyword-arguments private-keywords
> arguments)))))
> +
> +(define mix-build-system
> +  (build-system (name 'mix)
> +                (description "The standard Mix build system")
> +                (lower lower)))
> +
> +;;; mix.scm ends here
> diff --git a/guix/build/mix-build-system.scm b/guix/build/mix-build-
> system.scm
> new file mode 100644
> index 000000000..fe2e36d18
> --- /dev/null
> +++ b/guix/build/mix-build-system.scm
> @@ -0,0 +1,161 @@
> +;;; GNU Guix --- Functional package management for GNU
> +;;; Copyright © 2023 Pierre-Henry Fröhring <contact@phfrohring.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/>.
> +
> +;; Commentary:
> +;;
> +;; Code:
> +
> +(define-module (guix build mix-build-system)
> +  #:use-module ((guix build gnu-build-system) #:prefix gnu:)
> +  #:use-module (guix build utils)
> +  #:use-module (ice-9 ftw)
> +  #:use-module (ice-9 match)
> +  #:use-module (ice-9 regex)
> +  #:use-module (ice-9 string-fun)
> +  #:use-module (srfi srfi-1)
> +  #:use-module (srfi srfi-26)
> +  #:use-module (srfi srfi-71)
> +  #:export (mix-build
> +            %standard-phases))
> +
> +;; The Elixir version is constant as soon as it is computable from
> the current
> +;; execution.  It is a X.Y string where X and Y are respectively the
> major and
> +;; minor version number of the Elixir used in the build.
> +(define %elixir-version (make-parameter "X.Y"))
> +
> +(define* (elixir-libdir path #:optional (version (%elixir-version)))
> +  "Return the path where all libraries under PATH for a specified
> Elixir
> +VERSION are installed."
> +  (string-append path "/lib/elixir/" version))
> +
> +(define* (strip-prefix name #:optional (prefix "elixir-"))
> +  "Return NAME without the prefix PREFIX."
> +  (if (string-prefix? prefix name)
> +      (string-drop name (string-length prefix))
> +      name))
> +
> +(define (mix-build-dir mix-build-root mix-env)
> +  "Return the directory where build artifacts are to be installed
> according to
> +en environment MIX-ENV in the current directory.  MIX-BUILD-ROOT
> depends on the
> +package arguments.  See:
> https://hexdocs.pm/mix/1.15/Mix.html#module-environment-variables"
> +  (string-append mix-build-root "/" mix-env "/lib"))
> +
> +(define (elixir-version inputs)
> +  "Return an X.Y string where X and Y are respectively the major and
> minor version number of PACKAGE.
> +Example: /gnu/store/…-elixir-1.14.0 → 1.14"
> +  ((compose
> +    (cute string-join <> ".")
> +    (cute take <> 2)
> +    (cute string-split <> #\.)
> +    strip-prefix
> +    strip-store-file-name)
> +   (assoc-ref inputs "elixir")))
Note that when cross-compiling, elixir would likely be a native-input
rather than an input.  Neither this procedure nor its callers appear to
be aware of that.

You can keep the compose intact if you do
  (and=> (assoc-ref inputs "elixir")
         (compose ...))
Then you can call it as 
  (or (elixir-version native-inputs) (elixir-version inputs))


> +(define* (unpack #:key source mix-path #: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)
> +    (when (file-exists? "contents.tar.gz")
> +      (invoke "tar" "xvf" "contents.tar.gz"))))
> +
> +(define (list-directories dir)
> +  "List absolute paths of directories directly under the directory
> DIR."
> +  (map (cute string-append dir "/" <>)
> +       (scandir dir (lambda (filename)
> +                      (and (not (member filename '("." "..")))
> +                           (directory-exists? (string-append dir "/"
> filename)))))))
> +
> +(define* (set-mix-env #:key inputs mix-path mix-exs #:allow-other-
> keys)
> +  "Set environment variables.
> +See:
> https://hexdocs.pm/mix/1.15.7/Mix.html#module-environment-variables"
> +  (setenv "MIX_ARCHIVES" "archives")
> +  (setenv "MIX_BUILD_ROOT" "_build")
> +  (setenv "MIX_DEPS_PATH" "deps")
> +  (setenv "MIX_EXS" mix-exs)
> +  (setenv "MIX_HOME" (getcwd))
> +  (setenv "MIX_PATH" (or mix-path ""))
> +  (setenv "MIX_REBAR3" (string-append (assoc-ref inputs "rebar3")
> "/bin/rebar3")))
> +
> +(define* (set-elixir-version #:key inputs #:allow-other-keys)
> +  "Store the version number of the Elixir input in a parameter."
> +  (%elixir-version (elixir-version inputs))
> +  (format #t "Elixir version: ~a~%" (%elixir-version)))
> +
> +(define* (build #:key mix-environments #:allow-other-keys)
> +  "Builds the Mix project."
> +  (for-each (lambda (mix-env)
> +              (setenv "MIX_ENV" mix-env)
> +              (invoke "mix" "compile" "--no-deps-check"))
> +            mix-environments))
> +
> +(define* (check #:key (tests? #t) #:allow-other-keys)
> +  "Test the Mix project."
> +  (if tests?
> +      (invoke "mix" "test" "--no-deps-check")
> +      (format #t "tests? = ~a~%" tests?)))
> +
> +(define* (remove-mix-dirs . _)
> +  "Remove all .mix/ directories.
> +We do not want to copy them to the installation directory."
> +  (for-each delete-file-recursively
> +            (find-files "." (file-name-predicate "\\.mix$")
> #:directories? #t)))
> +
> +(define (package-name->elixir-name name+ver)
> +  "Convert the Guix package NAME-VER to the corresponding Elixir
> name-version
> +format.  Example: elixir-a-pkg-1.2.3 -> a_pkg"
> +  ((compose
> +    (cute string-join <> "_")
> +    (cute drop-right <> 1)
> +    (cute string-split <> #\-))
> +   (strip-prefix name+ver)))
> +
> +(define* (install #:key
> +                  inputs
> +                  outputs
> +                  name
> +                  build-per-environment
> +                  #:allow-other-keys)
> +  "Install build artifacts in the store."
> +  (let* ((lib-name (package-name->elixir-name name))
> +         (lib-dir (string-append (elixir-libdir (assoc-ref outputs
> "out")) "/" lib-name))
> +         (root (getenv "MIX_BUILD_ROOT"))
> +         (env (if build-per-environment "prod" "shared")))
> +    (mkdir-p lib-dir)
> +    (copy-recursively (string-append (mix-build-dir root env) "/"
> lib-name) lib-dir
> +                      #:follow-symlinks? #t)))
> +
> +(define %standard-phases
> +  (modify-phases gnu:%standard-phases
> +    (delete 'bootstrap)
> +    (delete 'configure)
> +    (add-after 'install-locale 'set-mix-env set-mix-env)
> +    (add-after 'set-mix-env 'set-elixir-version set-elixir-version)
> +    (replace 'unpack unpack)
> +    (replace 'build build)
> +    (replace 'check check)
> +    (add-before 'install 'remove-mix-dirs remove-mix-dirs)
> +    (replace 'install install)))
> +
> +(define* (mix-build #:key inputs (phases %standard-phases)
> +                    #:allow-other-keys #:rest args)
> +  "Build the given Mix package, applying all of PHASES in order."
> +  (apply gnu:gnu-build #:inputs inputs #:phases phases args))
> +
> +;;; mix-build-system.scm ends here
> 
> base-commit: 06f25a9a85be1bbe7a709e58ce41c1a834e5f1ae
Would you be so nice as to send a complete series as a revision?  We
have focused quite a lot on the intricacies of rebar-build-system and
mix-build-system in the past review, but for testing purposes, it would
be nice to have some (minimal set of) packages that we can build with
those improvements.

Cheers
Pierre-Henry Fröhring Dec. 8, 2023, 8:01 a.m. UTC | #2
Hello lilyp,

      Would you be so nice as to send a complete series as a revision?  We
      have focused quite a lot on the intricacies of rebar-build-system and
      mix-build-system in the past review, but for testing purposes, it would
      be nice to have some (minimal set of) packages that we can build with
      those improvements.

All of them will be sent.  I cannot send you all the patches at once — /i.e./ those
linked to Erlang and Elixir — as it has proved to be too time-consuming given my
inexperience in the Guix code base among other things.

Thanks to your patient feedback, I hope to be able to send all the patches — more
that 70 of them — but let us agree on the sequence before end as I do not want to
bother you with contingent errors.

Please, tell me if these sequences of patches work with you:
1. Send patches linked to Elixir, independently of Erlang.
2. Send patches linked to Erlang, independently of Elixir.
3. Send remaining patches where Elixir packages rely on Erlang packages.

For completeness, the case where Erlang packages rely on Elixir packages should be
addressed too, but I do not have code testing that case so far.  We will probably
take care of this case afterward.

The first sequence of commits allows to build the elixir-machete package which is one
of the few packages that have tests.  This sequence of commits does not need any
modification to the rebar build system or the Erlang package.  These commits are on a
local branch at the moment:

┌────
│ f50ed258f * gnu: Add elixir-machete.
│ 8b1cd4584 * gnu: Add elixir-dialyxir.
│ 04f1a0488 * gnu: Add elixir-erlex.
│ 51f3ea964 * gnu: Add elixir-credo.
│ c635210a2 * gnu: Add elixir-excoveralls.
│ 2ed400127 * gnu: Add elixir-castore.
│ fa45db5f6 * gnu: Add elixir-inch-ex.
│ 780e57c56 * gnu: Add elixir-bunt.
│ 52c57d6a7 * gnu: Add elixir-file-system.
│ 6b1f61851 * gnu: Add elixir-jason.
│ 283d647cf * gnu: Add elixir-makeup.
│ a2dd3114c * gnu: Add elixir-nimble-parsec.
│ 0e6d80e9d * gnu: Add elixir-hex.
│ 9e842f9c9 * gnu: elixir: Wrap binaries.
│ 5b40ab6fd * build-system: Add mix-build-system.
└────

So, is it OK if we manage this sequence of patches above before proceeding further?
If it is the case, then I will send them one by one so that your suggestions on one patch will
help me fix the next before sending it.

Cheers.

P.S. My last message that you received yesterday does not appear in this
thread <https://issues.guix.gnu.org/66801>.
Liliana Marie Prikler Dec. 8, 2023, 9:52 a.m. UTC | #3
Am Freitag, dem 08.12.2023 um 09:01 +0100 schrieb Pierre-Henry
Fröhring:
> Please, tell me if these sequences of patches work with you:
> 1. Send patches linked to Elixir, independently of Erlang.
> 2. Send patches linked to Erlang, independently of Elixir.
> 3. Send remaining patches where Elixir packages rely on Erlang
> packages.
If this series works for you, it ought to work for us as well.  The
important thing is that immediate results can be verified to build.

> For completeness, the case where Erlang packages rely on Elixir
> packages should be addressed too, but I do not have code testing that
> case so far.  We will probably take care of this case afterward.
Yeah, that sounds like a premature optimization otherwise.

> The first sequence of commits allows to build the elixir-machete
> package which is one of the few packages that have tests.  This
> sequence of commits does not need any modification to the rebar build
> system or the Erlang package.  These commits are on a local branch at
> the moment:
> 
> ┌────
> │ f50ed258f * gnu: Add elixir-machete.
> │ 8b1cd4584 * gnu: Add elixir-dialyxir.
> │ 04f1a0488 * gnu: Add elixir-erlex.
> │ 51f3ea964 * gnu: Add elixir-credo.
> │ c635210a2 * gnu: Add elixir-excoveralls.
> │ 2ed400127 * gnu: Add elixir-castore.
> │ fa45db5f6 * gnu: Add elixir-inch-ex.
> │ 780e57c56 * gnu: Add elixir-bunt.
> │ 52c57d6a7 * gnu: Add elixir-file-system.
> │ 6b1f61851 * gnu: Add elixir-jason.
> │ 283d647cf * gnu: Add elixir-makeup.
> │ a2dd3114c * gnu: Add elixir-nimble-parsec.
> │ 0e6d80e9d * gnu: Add elixir-hex.
> │ 9e842f9c9 * gnu: elixir: Wrap binaries.
> │ 5b40ab6fd * build-system: Add mix-build-system.
> └────
LGTM, but I prefer the order that's given in the cover-letter produced
by format-patch, with the first patch applied being first :)

> So, is it OK if we manage this sequence of patches above before
> proceeding further?
> If it is the case, then I will send them one by one so that your
> suggestions on one patch will help me fix the next before sending it.
SGTM.  If you're unfamiliar with git send-email, at least use git
format-patch and an appropriate reroll-count (IIRC, we're at 3, but I
could be misremembering), then send each of the resulting mboxes
individually or bundle them up as attachments.

> P.S. My last message that you received yesterday does not appear in
> this thread <https://issues.guix.gnu.org/66801>.
Yeah, mumi is currently slow on the uptake – dunno what's wrong there.

Cheers
Pierre-Henry Fröhring Dec. 8, 2023, 10:17 a.m. UTC | #4
If this series works for you, it ought to work for us as well.  The important thing
      is that immediate results can be verified to build.

Great. So here comes the series of patches. The list of all patches is:
┌────
│ 0001-build-system-Add-mix-build-system.patch
│ 0002-gnu-elixir-Wrap-binaries.patch
│ 0003-gnu-Add-elixir-hex.patch
│ 0004-gnu-Add-elixir-nimble-parsec.patch
│ 0005-gnu-Add-elixir-makeup.patch
│ 0006-gnu-Add-elixir-jason.patch
│ 0007-gnu-Add-elixir-file-system.patch
│ 0008-gnu-Add-elixir-bunt.patch
│ 0009-gnu-Add-elixir-inch-ex.patch
│ 0010-gnu-Add-elixir-castore.patch
│ 0011-gnu-Add-elixir-excoveralls.patch
│ 0012-gnu-Add-elixir-credo.patch
│ 0013-gnu-Add-elixir-erlex.patch
│ 0014-gnu-Add-elixir-dialyxir.patch
│ 0015-gnu-Add-elixir-machete.patch
└────

I will start with the first 3. To test them, I have issued the following command:
┌────
│ ./pre-inst-env guix build elixir-hex
└────

which result was:
┌────
│ …
│ successfully built /gnu/store/1732zx2s5afv1da5vhik0gr923frvg35-elixir-hex-2.0.5.drv
│ /gnu/store/hak603f9adwad1vrlqkqn6ywgissfb13-elixir-hex-2.0.5
└────

If everything is correct, I will send the other ones.

Cheers.
Liliana Marie Prikler Dec. 8, 2023, 11:50 a.m. UTC | #5
Am Freitag, dem 08.12.2023 um 11:17 +0100 schrieb Pierre-Henry
Fröhring:
> I will start with the first 3. To test them, I have issued the
> following command:
> ┌────
> │ ./pre-inst-env guix build elixir-hex
> └────
> 
> which result was:
> ┌────
> │ …
> │ successfully built /gnu/store/1732zx2s5afv1da5vhik0gr923frvg35-
> elixir-hex-2.0.5.drv
> │ /gnu/store/hak603f9adwad1vrlqkqn6ywgissfb13-elixir-hex-2.0.5
> └────
> 
> If everything is correct, I will send the other ones.
For the record, I get these hashes on current master:

grafting '/gnu/store/dapdrihxaxfxfcky51sr4607a79msgp6-elixir-hex-2.0.5'
      -> '/gnu/store/blg85m7a4jd9cdyc6q9m96lnxf9d97h0-elixir-hex-2.0.5'

And you still forgot the reroll-count.  Don't forget the reroll-count.
Pierre-Henry Fröhring Dec. 8, 2023, 2:20 p.m. UTC | #6
For the record, I get these hashes on current master:

      grafting '/gnu/store/dapdrihxaxfxfcky51sr4607a79msgp6-elixir-hex-2.0.5'
            -> '/gnu/store/blg85m7a4jd9cdyc6q9m96lnxf9d97h0-elixir-hex-2.0.5'

I do not know exactly how to hash is computed.  The previous build of elixir-hex has
been done after rebasing the commits on top of the commit `06f25a9a8' of master.  For
the record:
┌────
│ git show --oneline -s
└────

┌────
│ 0fcd231da (HEAD -> master) gnu: Add elixir-machete.
└────

┌────
│ ./pre-inst-env guix build --check elixir-hex
└────

┌────
│ The following graft will be made:
│    /gnu/store/1732zx2s5afv1da5vhik0gr923frvg35-elixir-hex-2.0.5.drv
│ applying 1 graft for elixir-hex-2.0.5 ...
│ grafting '/gnu/store/dapdrihxaxfxfcky51sr4607a79msgp6-elixir-hex-2.0.5' -> '/gnu/store/hak603f9adwad1vrlqkqn6ywgissfb13-elixir-hex-2.0.5'...
│ successfully built /gnu/store/1732zx2s5afv1da5vhik0gr923frvg35-elixir-hex-2.0.5.drv
│ successfully built /gnu/store/1732zx2s5afv1da5vhik0gr923frvg35-elixir-hex-2.0.5.drv
│ /gnu/store/hak603f9adwad1vrlqkqn6ywgissfb13-elixir-hex-2.0.5
└────

      And you still forgot the reroll-count.  Don't forget the reroll-count.
Well, what I assumed the "reroll-count" was was wrong.  You mean the `v<n>' in `[PATCH
v<n>]' in the subject of the patches? In my next email, I will send the same patches
except that I will use the `--reroll-count=3' option to build them.
Liliana Marie Prikler Dec. 8, 2023, 2:55 p.m. UTC | #7
Am Freitag, dem 08.12.2023 um 15:20 +0100 schrieb Pierre-Henry
Fröhring:
>       For the record, I get these hashes on current master:
> 
>       grafting '/gnu/store/dapdrihxaxfxfcky51sr4607a79msgp6-elixir-
> hex-2.0.5'
>             -> '/gnu/store/blg85m7a4jd9cdyc6q9m96lnxf9d97h0-elixir-
> hex-2.0.5'
> 
> I do not know exactly how to hash is computed.  The previous build of
> elixir-hex has been done after rebasing the commits on top of the
> commit `06f25a9a8' of master.  For  the record:
> ┌────
> │ git show --oneline -s
> └────
> 
> ┌────
> │ 0fcd231da (HEAD -> master) gnu: Add elixir-machete.
> └────
> 
> ┌────
> │ ./pre-inst-env guix build --check elixir-hex
> └────
> 
> ┌────
> │ The following graft will be made:
> │    /gnu/store/1732zx2s5afv1da5vhik0gr923frvg35-elixir-hex-2.0.5.drv
> │ applying 1 graft for elixir-hex-2.0.5 ...
> │ grafting '/gnu/store/dapdrihxaxfxfcky51sr4607a79msgp6-elixir-hex-
> 2.0.5' -> '/gnu/store/hak603f9adwad1vrlqkqn6ywgissfb13-elixir-hex-
> 2.0.5'...
> │ successfully built /gnu/store/1732zx2s5afv1da5vhik0gr923frvg35-
> elixir-hex-2.0.5.drv
> │ successfully built /gnu/store/1732zx2s5afv1da5vhik0gr923frvg35-
> elixir-hex-2.0.5.drv
> │ /gnu/store/hak603f9adwad1vrlqkqn6ywgissfb13-elixir-hex-2.0.5
> └────
I don't get the same hashes, but at least locally, --check succeeds.

> Well, what I assumed the "reroll-count" was was wrong.  You mean the
> `v<n>' in `[PATCH v<n>]' in the subject of the patches? In my next
> email, I will send the same patches except that I will use the `--
> reroll-count=3' option to build them.
Yep, that's what I meant.  Feel free to also send 04..15.

Cheers
diff mbox series

Patch

diff --git a/guix/build-system/mix.scm b/guix/build-system/mix.scm
new file mode 100644
index 000000000..1b04053d7
--- /dev/null
+++ b/guix/build-system/mix.scm
@@ -0,0 +1,186 @@ 
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2023 Pierre-Henry Fröhring <contact@phfrohring.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/>.
+
+;; Commentary:
+;;
+;; Standard build procedure for Elixir packages using 'mix'.  This is
+;; implemented as an extension of 'gnu-build-system'.
+;;
+;; Code:
+
+(define-module (guix build-system mix)
+  #:use-module (guix build mix-build-system)
+  #:use-module (guix build-system gnu)
+  #:use-module (guix build-system)
+  #:use-module (guix gexp)
+  #:use-module (guix monads)
+  #:use-module (guix packages)
+  #:use-module (guix search-paths)
+  #:use-module (guix store)
+  #:use-module (guix utils)
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-26)
+  #:export (mix-build-system hexpm-uri))
+
+;; Lazily resolve bindings to avoid circular dependencies.
+(define (default-glibc-utf8-locales)
+  (let* ((base (resolve-interface '(gnu packages base))))
+    (module-ref base 'glibc-utf8-locales)))
+
+(define (default-elixir-hex)
+  (let ((elixir (resolve-interface '(gnu packages elixir))))
+    (module-ref elixir 'elixir-hex)))
+
+(define (default-rebar3)
+  (let ((erlang (resolve-interface '(gnu packages erlang))))
+    (module-ref erlang 'rebar3)))
+
+(define (default-elixir)
+  (let ((elixir (resolve-interface '(gnu packages elixir))))
+    (module-ref elixir 'elixir)))
+
+(define* (strip-prefix name #:optional (prefix "elixir-"))
+  "Return NAME without the prefix PREFIX."
+  (if (string-prefix? prefix name)
+      (string-drop name (string-length prefix))
+      name))
+
+(define (hexpm-uri name version)
+  "Return the URI where to fetch the sources of a Hex package NAME at VERSION.
+NAME is the name of the package which should look like: elixir-pkg-name-X.Y.Z
+See: https://github.com/hexpm/specifications/blob/main/endpoints.md"
+  ((compose
+    (cute string-append "https://repo.hex.pm/tarballs/" <> "-" version ".tar")
+    (cute string-replace-substring <> "-" "_")
+    strip-prefix)
+   name))
+
+;; A number of environment variables specific to the Mix build system are
+;; reflected here.  They are documented at
+;; https://hexdocs.pm/mix/1.15.7/Mix.html#module-environment-variables.  Other
+;; parameters located in mix.exs are defined at
+;; https://hexdocs.pm/mix/1.15.7/Mix.Project.html#module-configuration
+(define* (mix-build name
+                    inputs
+                    #:key
+                    source
+                    (tests? #t)
+                    (mix-path #f) ;See MIX_PATH.
+                    (mix-exs "mix.exs") ;See MIX_EXS.
+                    (build-per-environment #t) ;See :build_per_environment.
+                    (phases '%standard-phases)
+                    (outputs '("out"))
+                    (search-paths '())
+                    (system (%current-system))
+                    (guile #f)
+                    (imported-modules `((guix build mix-build-system)
+                                        ,@%gnu-build-system-modules))
+                    (modules '((guix build mix-build-system)
+                               (guix build utils))))
+  "Build SOURCE using Elixir, and with INPUTS."
+
+  ;; Check the documentation of :build_per_environment here:
+  ;; https://hexdocs.pm/mix/1.15.7/Mix.Project.html#module-configuration And
+  ;; "Environments" here:
+  ;; https://hexdocs.pm/mix/1.15.7/Mix.html#module-environments
+  (define mix-environments
+    (if build-per-environment
+        `("prod" ,@(if tests? '("test") '()))
+        '("shared")))
+
+  (define builder
+    (with-imported-modules imported-modules
+      #~(begin
+
+          (use-modules #$@(sexp->gexp modules))
+
+          #$(with-build-variables inputs outputs
+              #~(mix-build #:name #$name
+                           #:source #+source
+                           #:system #$system
+                           #:tests? #$tests?
+                           #:mix-path #$mix-path
+                           #:mix-exs #$mix-exs
+                           #:mix-environments '#$mix-environments
+                           #:build-per-environment #$build-per-environment
+                           #: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)))
+    (gexp->derivation name
+                      builder
+                      #:system system
+                      #:graft? #f       ;consistent with 'gnu-build'
+                      #:target #f
+                      #:guile-for-build guile)))
+
+(define* (lower name
+                #:key
+                (elixir (default-elixir))
+                (elixir-hex (default-elixir-hex))
+                (glibc-utf8-locales (default-glibc-utf8-locales))
+                (inputs '())
+                (native-inputs '())
+                (propagated-inputs '())
+                (rebar3 (default-rebar3))
+                (tests? #t)
+                outputs
+                source
+                system
+                target
+                #:allow-other-keys #:rest arguments)
+  "Return a bag for NAME."
+  (let ((private-keywords
+         '(#:inputs #:native-inputs
+           #:outputs #:system #:target
+           #:elixir #:elixir-hex #:glibc-utf8-locales
+           #:rebar3 #:erlang))
+        (build-inputs
+         `(,@(standard-packages)
+           ("glibc-utf8-locales" ,glibc-utf8-locales)
+           ("erlang" ,(lookup-package-input elixir "erlang"))
+           ("rebar3" ,rebar3)
+           ("elixir" ,elixir)
+           ("elixir-hex" ,elixir-hex)
+           ,@inputs
+           ,@native-inputs)))
+  (bag (name name)
+       (system system)
+       (build-inputs build-inputs)
+       (host-inputs (if target inputs '()))
+       (outputs outputs)
+       (build mix-build)
+       (arguments (strip-keyword-arguments private-keywords arguments)))))
+
+(define mix-build-system
+  (build-system (name 'mix)
+                (description "The standard Mix build system")
+                (lower lower)))
+
+;;; mix.scm ends here
diff --git a/guix/build/mix-build-system.scm b/guix/build/mix-build-system.scm
new file mode 100644
index 000000000..fe2e36d18
--- /dev/null
+++ b/guix/build/mix-build-system.scm
@@ -0,0 +1,161 @@ 
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2023 Pierre-Henry Fröhring <contact@phfrohring.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/>.
+
+;; Commentary:
+;;
+;; Code:
+
+(define-module (guix build mix-build-system)
+  #:use-module ((guix build gnu-build-system) #:prefix gnu:)
+  #:use-module (guix build utils)
+  #:use-module (ice-9 ftw)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 regex)
+  #:use-module (ice-9 string-fun)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-26)
+  #:use-module (srfi srfi-71)
+  #:export (mix-build
+            %standard-phases))
+
+;; The Elixir version is constant as soon as it is computable from the current
+;; execution.  It is a X.Y string where X and Y are respectively the major and
+;; minor version number of the Elixir used in the build.
+(define %elixir-version (make-parameter "X.Y"))
+
+(define* (elixir-libdir path #:optional (version (%elixir-version)))
+  "Return the path where all libraries under PATH for a specified Elixir
+VERSION are installed."
+  (string-append path "/lib/elixir/" version))
+
+(define* (strip-prefix name #:optional (prefix "elixir-"))
+  "Return NAME without the prefix PREFIX."
+  (if (string-prefix? prefix name)
+      (string-drop name (string-length prefix))
+      name))
+
+(define (mix-build-dir mix-build-root mix-env)
+  "Return the directory where build artifacts are to be installed according to
+en environment MIX-ENV in the current directory.  MIX-BUILD-ROOT depends on the
+package arguments.  See: https://hexdocs.pm/mix/1.15/Mix.html#module-environment-variables"
+  (string-append mix-build-root "/" mix-env "/lib"))
+
+(define (elixir-version inputs)
+  "Return an X.Y string where X and Y are respectively the major and minor version number of PACKAGE.
+Example: /gnu/store/…-elixir-1.14.0 → 1.14"
+  ((compose
+    (cute string-join <> ".")
+    (cute take <> 2)
+    (cute string-split <> #\.)
+    strip-prefix
+    strip-store-file-name)
+   (assoc-ref inputs "elixir")))
+
+(define* (unpack #:key source mix-path #: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)
+    (when (file-exists? "contents.tar.gz")
+      (invoke "tar" "xvf" "contents.tar.gz"))))
+
+(define (list-directories dir)
+  "List absolute paths of directories directly under the directory DIR."
+  (map (cute string-append dir "/" <>)
+       (scandir dir (lambda (filename)
+                      (and (not (member filename '("." "..")))
+                           (directory-exists? (string-append dir "/" filename)))))))
+
+(define* (set-mix-env #:key inputs mix-path mix-exs #:allow-other-keys)
+  "Set environment variables.
+See: https://hexdocs.pm/mix/1.15.7/Mix.html#module-environment-variables"
+  (setenv "MIX_ARCHIVES" "archives")
+  (setenv "MIX_BUILD_ROOT" "_build")
+  (setenv "MIX_DEPS_PATH" "deps")
+  (setenv "MIX_EXS" mix-exs)
+  (setenv "MIX_HOME" (getcwd))
+  (setenv "MIX_PATH" (or mix-path ""))
+  (setenv "MIX_REBAR3" (string-append (assoc-ref inputs "rebar3") "/bin/rebar3")))
+
+(define* (set-elixir-version #:key inputs #:allow-other-keys)
+  "Store the version number of the Elixir input in a parameter."
+  (%elixir-version (elixir-version inputs))
+  (format #t "Elixir version: ~a~%" (%elixir-version)))
+
+(define* (build #:key mix-environments #:allow-other-keys)
+  "Builds the Mix project."
+  (for-each (lambda (mix-env)
+              (setenv "MIX_ENV" mix-env)
+              (invoke "mix" "compile" "--no-deps-check"))
+            mix-environments))
+
+(define* (check #:key (tests? #t) #:allow-other-keys)
+  "Test the Mix project."
+  (if tests?
+      (invoke "mix" "test" "--no-deps-check")
+      (format #t "tests? = ~a~%" tests?)))
+
+(define* (remove-mix-dirs . _)
+  "Remove all .mix/ directories.
+We do not want to copy them to the installation directory."
+  (for-each delete-file-recursively
+            (find-files "." (file-name-predicate "\\.mix$") #:directories? #t)))
+
+(define (package-name->elixir-name name+ver)
+  "Convert the Guix package NAME-VER to the corresponding Elixir name-version
+format.  Example: elixir-a-pkg-1.2.3 -> a_pkg"
+  ((compose
+    (cute string-join <> "_")
+    (cute drop-right <> 1)
+    (cute string-split <> #\-))
+   (strip-prefix name+ver)))
+
+(define* (install #:key
+                  inputs
+                  outputs
+                  name
+                  build-per-environment
+                  #:allow-other-keys)
+  "Install build artifacts in the store."
+  (let* ((lib-name (package-name->elixir-name name))
+         (lib-dir (string-append (elixir-libdir (assoc-ref outputs "out")) "/" lib-name))
+         (root (getenv "MIX_BUILD_ROOT"))
+         (env (if build-per-environment "prod" "shared")))
+    (mkdir-p lib-dir)
+    (copy-recursively (string-append (mix-build-dir root env) "/" lib-name) lib-dir
+                      #:follow-symlinks? #t)))
+
+(define %standard-phases
+  (modify-phases gnu:%standard-phases
+    (delete 'bootstrap)
+    (delete 'configure)
+    (add-after 'install-locale 'set-mix-env set-mix-env)
+    (add-after 'set-mix-env 'set-elixir-version set-elixir-version)
+    (replace 'unpack unpack)
+    (replace 'build build)
+    (replace 'check check)
+    (add-before 'install 'remove-mix-dirs remove-mix-dirs)
+    (replace 'install install)))
+
+(define* (mix-build #:key inputs (phases %standard-phases)
+                    #:allow-other-keys #:rest args)
+  "Build the given Mix package, applying all of PHASES in order."
+  (apply gnu:gnu-build #:inputs inputs #:phases phases args))
+
+;;; mix-build-system.scm ends here