guix_mirror_bot pushed a commit to branch beam-team
in repository guix.
commit 875223f9d4dc589d504d9bb8d73c3b2a5c452e97
Author: Igorj Gorjaĉev <[email protected]>
AuthorDate: Fri Jan 9 14:16:50 2026 +0200
build-system: mix: Add support for importing deps from 'mix.lock'.
* guix/import/mix.scm: New file.
* guix/import/mix/mix-lock.scm: New file.
* guix/scripts/import/mix.scm: New file.
* tests/import/mix.scm: New file.
* Makefile.am (MODULES, SCM_TESTS): Register them.
* etc/teams.scm (beam): Likewise.
* gnu/packages/beam-apps.scm: New file.
* gnu/packages/beam-packages.scm: New file.
* gnu/packages/beam-sources.scm: New file.
* gnu/local.mk (GNU_SYSTEM_MODULES): Register them.
* etc/teams.scm (beam): Likewise.
* guix/build-system/mix.scm (define-mix-inputs): New macro.
(beam-name->package-name, hexpm-source, mix-inputs): New procedures.
(mix-build): Add new variables.
* guix/build/mix-build-system.scm (unpack-vendorize,
symlink-vendorize, beam-package?): New procedures.
(%standard-phases): Add new phases.
(build, check, install): Add vendoring support.
(%elixir-prefix, %beam-prefix, %vendorize-env-var): Add new symbols.
(strip-prefix, package-name->elixir-name): Add support for beam-prefixed
packages.
* guix/scripts/import.scm (process-args): Add 'mix' importer.
* etc/teams.scm (beam)<#:description>: Add Mix importer mention.
* etc/teams.scm (beam)<#:scope>: Add "tests/import/hexpm.scm".
* CODEOWNERS: Regenerate file.
* guix/import/hexpm.scm (dependencies->package-names,
hexpm->guix-package): Add optional mix-inputs support.
* guix/scripts/import/hexpm.scm (show-help, %options,
parse-options): Likewise.
* tests/import/hexpm.scm: Likewise.
* guix/import/hexpm.scm (hexpm->guix-package): Fix typo.
* doc/guix.texi: Add initial documentation for mix importer and
new option for hexpm importer.
* doc/guix-cookbook.texi: Add example of packaging Elixir app.
Change-Id: I5d11f8be111963fbac2fd6ab5fd0bd644f9488b0
Signed-off-by: Giacomo Leidi <[email protected]>
---
CODEOWNERS | 6 ++
Makefile.am | 5 +
doc/guix-cookbook.texi | 105 +++++++++++++++++++++
doc/guix.texi | 26 ++++++
etc/teams.scm | 12 ++-
gnu/local.mk | 4 +
gnu/packages/beam-apps.scm | 25 +++++
gnu/packages/beam-packages.scm | 43 +++++++++
gnu/packages/beam-sources.scm | 29 ++++++
guix/build-system/mix.scm | 59 +++++++++++-
guix/build/mix-build-system.scm | 137 ++++++++++++++++++++++-----
guix/import/hexpm.scm | 59 ++++++------
guix/import/mix.scm | 143 ++++++++++++++++++++++++++++
guix/import/mix/mix-lock.scm | 201 ++++++++++++++++++++++++++++++++++++++++
guix/scripts/import.scm | 20 ++--
guix/scripts/import/hexpm.scm | 9 +-
guix/scripts/import/mix.scm | 122 ++++++++++++++++++++++++
tests/import/hexpm.scm | 44 +++++++++
tests/import/mix.scm | 97 +++++++++++++++++++
19 files changed, 1084 insertions(+), 62 deletions(-)
diff --git a/CODEOWNERS b/CODEOWNERS
index 8f0acc8933..fa2c50dbd8 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -12,6 +12,7 @@ gnu/packages/audio\.scm @guix/audio
gnu/packages/fluidplug\.scm @guix/audio
gnu/packages/xiph\.scm @guix/audio
+gnu/packages/beam-.+\.scm$ @guix/beam
gnu/packages/elixir(-.+|)\.scm$ @guix/beam
guix/build/mix-build-system\.scm @guix/beam
guix/build-system/mix\.scm @guix/beam
@@ -19,7 +20,12 @@ gnu/packages/erlang(-.+|)\.scm$ @guix/beam
guix/build/rebar-build-system\.scm @guix/beam
guix/build-system/rebar\.scm @guix/beam
guix/import/hexpm\.scm @guix/beam
+guix/import/mix\.scm @guix/beam
+guix/import/mix/mix-lock\.scm @guix/beam
guix/scripts/import/hexpm\.scm @guix/beam
+guix/scripts/import/mix\.scm @guix/beam
+tests/import/hexpm\.scm @guix/beam
+tests/import/mix\.scm @guix/beam
gnu/packages/bioinformatics\.scm @guix/bioinformatics
diff --git a/Makefile.am b/Makefile.am
index 96b2acbcea..476e1f9864 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -22,6 +22,7 @@
# Copyright © 2024 gemmaro <[email protected]>
# Copyright © 2025 Brice Waegeneire <[email protected]>
# Copyright © 2025 Florian Pelz <[email protected]>
+# Copyright © 2025 Igorj Gorjaĉev <[email protected]>
#
# This file is part of GNU Guix.
#
@@ -319,6 +320,8 @@ MODULES = \
guix/import/launchpad.scm \
guix/import/luanti.scm \
guix/import/minetest.scm \
+ guix/import/mix.scm \
+ guix/import/mix/mix-lock.scm \
guix/import/nuget.scm \
guix/import/npm-binary.scm \
guix/import/opam.scm \
@@ -376,6 +379,7 @@ MODULES = \
guix/scripts/import/json.scm \
guix/scripts/import/luanti.scm \
guix/scripts/import/minetest.scm \
+ guix/scripts/import/mix.scm \
guix/scripts/import/npm-binary.scm \
guix/scripts/import/nuget.scm \
guix/scripts/import/opam.scm \
@@ -580,6 +584,7 @@ SCM_TESTS = \
tests/import/hackage.scm \
tests/import/hexpm.scm \
tests/import/luanti.scm \
+ tests/import/mix.scm \
tests/import/npm-binary.scm \
tests/import/nuget.scm \
tests/import/opam.scm \
diff --git a/doc/guix-cookbook.texi b/doc/guix-cookbook.texi
index b04ca538ab..d8fa65bd64 100644
--- a/doc/guix-cookbook.texi
+++ b/doc/guix-cookbook.texi
@@ -29,6 +29,7 @@ Copyright @copyright{} 2025 45mg@*
Copyright @copyright{} 2023 Marek Felšöci@*
Copyright @copyright{} 2023 Konrad Hinsen@*
Copyright @copyright{} 2023 Philippe Swartvagher@*
+Copyright @copyright{} 2026 Igorj Gorjaĉev@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -140,6 +141,7 @@ Programmable and automated package definition
Packaging Workflows
* Packaging Rust Crates::
+* Packaging Elixir Applications::
Packaging Rust Crates
@@ -1629,6 +1631,7 @@ systems, serving as extensions to the concise packaging
guidelines
@menu
* Packaging Rust Crates::
+* Packaging Elixir Applications::
@end menu
@node Packaging Rust Crates
@@ -1984,6 +1987,108 @@ method, one of the most popular choices for Traditional
Chinese users.")
(license license:lgpl2.1+)))
@end lisp
+@node Packaging Elixir Applications
+@subsection Packaging Elixir Applications
+
+In preparation, add the following packages to your environment:
+
+@example
+$ guix shell elixir elixir-depscheck elixir-mix-audit
+@end example
+
+In this example, we package @uref{https://livebook.dev, livebook},
+which is an interactive, web-based notebook environment for writing,
+running, and sharing Elixir code with rich documentation and
+visualizations.
+
+@enumerate
+@item
+We retrieve the @code{livebook} source code with the latest version
+from the corresponding @code{git} repository:
+
+@example
+$ git clone --branch v0.18.2 --depth 1 \
+ https://github.com/livebook-dev/livebook
+@end example
+
+We should prepare the following definiton for this package (the suggested
+location for this definition should be @code{gnu/packages/beam-packages.scm}
+or up to your mind):
+
+@lisp
+(define-public livebook
+ (package
+ (name "livebook")
+ (version "0.18.2")
+ (source
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/livebook-dev/livebook")
+ (commit (string-append "v" version))))
+ (file-name (git-file-name name version))
+ (sha256
+ (base32
+ "1pq227nabslr7gh4qvg24wj4320djc0vrh6x6l23g3468x6znxjm"))))
+ (build-system mix-build-system)
+ (arguments
+ (list
+ #:tests? #f ; TODO: make them work a little bit later
+ #:vendorize? #t
+ #:phases
+ #~(modify-phases %standard-phases
+ (replace 'install
+ (lambda* (#:key outputs #:allow-other-keys)
+ (let ((out (assoc-ref outputs "out")))
+ (copy-recursively "_build/prod/rel/livebook/"
+ out)))))))
+ (inputs (mix-inputs 'livebook))
+ (synopsis "Web application for writing code notebooks")
+ (description "This package provides @code{Livebook}, a web application
+for writing interactive and collaborative code notebooks.")
+ (home-page "https://livebook.dev/")
+ (license license:asl2.0)))
+@end lisp
+
+The identifier used to invoke @code{mix-inputs}, in this case the scheme
+symbol @code{'livebook}, must be unique, usually matching the variable name
+of the package.
+
+@item
+Navigate to the unpacked directory, then run the following commands:
+
+@example
+$ mix deps.get
+$ mix deps.audit
+$ mix depscheck --verbose
+@end example
+
+@command{mix deps.audit} checks known vulnerabilities and @command{mix
+depscheck --verbose} checks licenses, for all the dependencies. We must
+have an acceptable output (i.e. message ``No vulnerabilities found.'') of
+@command{mix deps.audit} and ensure all dependencies are licensed with our
+supported licenses (@pxref{Defining Packages,,, guix, GNU Guix Reference
Manual}).
+
+@item
+Import dependencies from the lockfile:
+
+@example
+$ guix import --insert=gnu/packages/beam-packages.scm \
+ mix --lockfile=/path/to/mix.lock livebook
+
+# or:
+$ guix import -i gnu/packages/beam-packages.scm \
+ mix -f /path/to/mix.lock livebook
+@end example
+
+After that the package could be build by using the following command:
+
+@example
+$ guix build livebook
+@end example
+
+@end enumerate
+
@c *********************************************************************
@node System Configuration
diff --git a/doc/guix.texi b/doc/guix.texi
index 4c113518fa..03e3280629 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -149,6 +149,7 @@ Copyright @copyright{} 2025 Rodion Goritskov@*
Copyright @copyright{} 2025 dan@*
Copyright @copyright{} 2025 Noé Lopez@*
Copyright @copyright{} 2026 David Elsing@*
+Copyright @copyright{} 2026 Igorj Gorjaĉev@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -15349,6 +15350,31 @@ Additional options include:
Traverse the dependency graph of the given upstream package recursively
and generate package expressions for all those packages that are not yet
in Guix.
+@item --mix-inputs
+@itemx -m
+Use vendored @code{mix-inputs} for @code{input} field instead of common
+dependencies.
+@end table
+
+@item mix
+@cindex mix
+Import metadata from the @file{mix.lock}, Elixir's
+@uref{https://hexdocs.pm/elixir/introduction-to-mix.html, mix} lock file,
+as in this example:
+
+@example
+guix import mix -f mix.lock yourapp
+@end example
+
+Mandatory options are:
+
+@table @code
+@item --lockfile=@var{file}
+@itemx -f @var{file}
+@option{--lockfile} must be a @file{mix.lock} file.
+
+@xref{Packaging Elixir Applications,,, guix-cookbook, GNU Guix Cookbook}, for
+packaging workflow utilizing it.
@end table
@end table
diff --git a/etc/teams.scm b/etc/teams.scm
index 6730a91008..ab21e6dfee 100755
--- a/etc/teams.scm
+++ b/etc/teams.scm
@@ -481,15 +481,21 @@ already exists. Lookup team IDs among CURRENT-TEAMS."
#:name "Erlang/Elixir/BEAM team"
#:description "The virtual machine at the core of the Erlang Open
Telecom Platform (OTP), Erlang and Elxir languages and packages, development
-of Rebar and Mix build systems and Hex.pm importer."
- #:scope (list (make-regexp* "^gnu/packages/elixir(-.+|)\\.scm$")
+of Rebar and Mix build systems, Hex.pm and Mix importers."
+ #:scope (list (make-regexp* "^gnu/packages/beam-.+\\.scm$")
+ (make-regexp* "^gnu/packages/elixir(-.+|)\\.scm$")
"guix/build/mix-build-system.scm"
"guix/build-system/mix.scm"
(make-regexp* "^gnu/packages/erlang(-.+|)\\.scm$")
"guix/build/rebar-build-system.scm"
"guix/build-system/rebar.scm"
"guix/import/hexpm.scm"
- "guix/scripts/import/hexpm.scm")))
+ "guix/import/mix.scm"
+ "guix/import/mix/mix-lock.scm"
+ "guix/scripts/import/hexpm.scm"
+ "guix/scripts/import/mix.scm"
+ "tests/import/hexpm.scm"
+ "tests/import/mix.scm")))
(define-team bioinformatics
(team 'bioinformatics
diff --git a/gnu/local.mk b/gnu/local.mk
index 60b1020b91..acbac89204 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -75,6 +75,7 @@
# Copyright © 2025 Nigko Yerden <[email protected]>
# Copyright © 2025 Cayetano Santos <[email protected]>
# Copyright © 2025 bdunahu <[email protected]>
+# Copyright © 2025 Igorj Gorjaĉev <[email protected]>
#
# This file is part of GNU Guix.
#
@@ -174,6 +175,9 @@ GNU_SYSTEM_MODULES = \
%D%/packages/bash.scm \
%D%/packages/batik.scm \
%D%/packages/bdw-gc.scm \
+ %D%/packages/beam-apps.scm \
+ %D%/packages/beam-packages.scm \
+ %D%/packages/beam-sources.scm \
%D%/packages/benchmark.scm \
%D%/packages/bioconductor.scm \
%D%/packages/bioinformatics.scm \
diff --git a/gnu/packages/beam-apps.scm b/gnu/packages/beam-apps.scm
new file mode 100644
index 0000000000..0fa7b96307
--- /dev/null
+++ b/gnu/packages/beam-apps.scm
@@ -0,0 +1,25 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Igorj Gorjaĉev <[email protected]>
+;;;
+;;; 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 (gnu packages beam-apps)
+ #:use-module ((guix licenses) #:prefix license:)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (guix download)
+ #:use-module (guix git-download)
+ #:use-module (guix build-system mix))
diff --git a/gnu/packages/beam-packages.scm b/gnu/packages/beam-packages.scm
new file mode 100644
index 0000000000..3144afb148
--- /dev/null
+++ b/gnu/packages/beam-packages.scm
@@ -0,0 +1,43 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Igorj Gorjaĉev <[email protected]>
+;;;
+;;; 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 (gnu packages beam-packages)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (guix download)
+ #:use-module (guix git-download)
+ #:use-module (guix build-system mix)
+ #:use-module (gnu packages beam-sources)
+ #:export (lookup-mix-inputs))
+
+;;;
+;;; This file is managed by ‘guix import’. Do NOT add definitions manually.
+;;;
+;;; BEAM libraries fetched from hex.pm.
+;;;
+
+(define aaaa-separator 'begin-of-beam-packages)
+
+(define cccc-separator 'end-of-beam-packages)
+
+
+;;;
+;;; Mix inputs.
+;;;
+
+(define-mix-inputs lookup-mix-inputs)
diff --git a/gnu/packages/beam-sources.scm b/gnu/packages/beam-sources.scm
new file mode 100644
index 0000000000..efc2db9606
--- /dev/null
+++ b/gnu/packages/beam-sources.scm
@@ -0,0 +1,29 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Igorj Gorjaĉev <[email protected]>
+;;;
+;;; 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 (gnu packages beam-sources)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (guix download)
+ #:use-module (guix git-download)
+ #:use-module (guix build-system mix))
+
+;;;
+;;; BEAM libraries requiring modification.
+;;; These packages are hidden, as they are not interesting to users.
+;;;
diff --git a/guix/build-system/mix.scm b/guix/build-system/mix.scm
index 224f4ff94c..709fed017f 100644
--- a/guix/build-system/mix.scm
+++ b/guix/build-system/mix.scm
@@ -1,6 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Pierre-Henry Fröhring <[email protected]>
;;; Copyright © 2025 Giacomo Leidi <[email protected]>
+;;; Copyright © 2025 Igorj Gorjaĉev <[email protected]>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -28,7 +29,10 @@
#:use-module (guix build mix-build-system)
#:use-module (guix build-system gnu)
#:use-module (guix build-system)
+ #:use-module (guix diagnostics)
+ #:use-module (guix download)
#:use-module (guix gexp)
+ #:use-module (guix i18n)
#:use-module (guix monads)
#:use-module (guix packages)
#:use-module (guix search-paths)
@@ -37,7 +41,12 @@
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-26)
- #:export (mix-build-system hexpm-uri))
+ #:export (mix-build-system
+ hexpm-uri
+ beam-name->package-name
+ hexpm-source
+ define-mix-inputs
+ mix-inputs))
(define (default-elixir-hex)
(@* (gnu packages elixir) elixir-hex))
@@ -64,6 +73,48 @@ See:
https://github.com/hexpm/specifications/blob/main/endpoints.md"
strip-prefix)
name))
+(define (beam-name->package-name name)
+ (downstream-package-name "beam-" name))
+
+;; NOTE: Only use this procedure in (gnu packages beam-packages).
+(define* (hexpm-source package-name hexpm-name hexpm-version hexpm-hash
+ #:key (patches '()) (snippet #f))
+ (origin
+ (method url-fetch)
+ (uri (hexpm-uri hexpm-name hexpm-version))
+ (file-name
+ (string-append "beam-" package-name "-" hexpm-version ".tar"))
+ (sha256 (base32 hexpm-hash))
+ (modules '((guix build utils)))
+ (patches patches)
+ (snippet snippet)))
+
+(define-syntax define-mix-inputs
+ (syntax-rules (=>)
+ ((_ lookup inputs ...)
+ (define lookup
+ (let ((table (make-hash-table)))
+ (letrec-syntax ((record
+ (syntax-rules (=>)
+ ((_) #t)
+ ((_ (name => lst) rest (... ...))
+ (begin
+ (hashq-set! table 'name (filter identity lst))
+ (record rest (... ...)))))))
+ (record inputs ...)
+ (lambda (name)
+ "Return the inputs for NAME."
+ (hashq-ref table name))))))))
+
+(define* (mix-inputs name #:key (module '(gnu packages beam-packages)))
+ "Lookup Mix inputs for NAME defined in MODULE, return an empty list if
+unavailable."
+ (let ((lookup (module-ref (resolve-interface module) 'lookup-mix-inputs)))
+ (or (lookup name)
+ (begin
+ (warning (G_ "no Mix inputs available for '~a'~%") name)
+ '()))))
+
;; A number of environment variables specific to the Mix build system are
;; reflected here. They are documented at
;; https://hexdocs.pm/mix/Mix.html#module-environment-variables. Other
@@ -75,6 +126,9 @@ See:
https://github.com/hexpm/specifications/blob/main/endpoints.md"
source
(tests? #t)
(test-flags ''())
+ (vendorize? #f)
+ (vendor-symlinks? #t)
+ (vendor-dir "guix-vendor")
(mix-path #f) ;See MIX_PATH.
(mix-exs "mix.exs") ;See MIX_EXS.
(build-per-environment #t) ;See :build_per_environment.
@@ -110,6 +164,9 @@ See:
https://github.com/hexpm/specifications/blob/main/endpoints.md"
#:system #$system
#:tests? #$tests?
#:test-flags #$test-flags
+ #:vendorize? #$vendorize?
+ #:vendor-symlinks? #$vendor-symlinks?
+ #:vendor-dir #$vendor-dir
#:mix-path #$mix-path
#:mix-exs #$mix-exs
#:mix-environments '#$mix-environments
diff --git a/guix/build/mix-build-system.scm b/guix/build/mix-build-system.scm
index f16fb1eaae..7a68b28559 100644
--- a/guix/build/mix-build-system.scm
+++ b/guix/build/mix-build-system.scm
@@ -1,6 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Pierre-Henry Fröhring <[email protected]>
-;;; Copyright © 2024, 2026 Igorj Gorjaĉev <[email protected]>
+;;; Copyright © 2024-2026 Igorj Gorjaĉev <[email protected]>
;;; Copyright © 2024, 2025 Giacomo Leidi <[email protected]>
;;;
;;; This file is part of GNU Guix.
@@ -48,7 +48,13 @@
VERSION are installed."
(string-append path "/lib/elixir/" version))
-(define* (strip-prefix name #:optional (prefix "elixir-"))
+(define %elixir-prefix "elixir-")
+
+(define %beam-prefix "beam-")
+
+(define %vendorize-env-var "GUIX_MIX_VENDOR_DIR")
+
+(define* (strip-prefix name #:optional (prefix %elixir-prefix))
"Return NAME without the prefix PREFIX."
(if (string-prefix? prefix name)
(string-drop name (string-length prefix))
@@ -83,9 +89,73 @@ working directory."
(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)))))))
+ (scandir
+ dir (lambda (filename)
+ (and (not (member filename '("." "..")))
+ (directory-exists? (string-append dir "/" filename)))))))
+
+(define* (unpack-vendorize #:key vendorize? vendor-dir inputs
+ #:allow-other-keys)
+ "Unpack vendored packages."
+ (define (inputs->beam-inputs inputs)
+ "Filter using the label part from INPUTS."
+ (filter-map (lambda (input)
+ (match input
+ ((name . file)
+ (and (beam-package? name) file))))
+ inputs))
+ (define (beam-input->upstream-name beam-input)
+ "Convert BEAM-INPUT into upstream package name."
+ ((compose
+ (cute package-name->elixir-name <> %beam-prefix)
+ (cute substring <> 33)
+ basename)
+ beam-input))
+ (define (copy-or-unpack-beam-package beam-input dep-dir)
+ "Copy contents, if BEAM-INPUT is a checkout package, otherwise unpack it."
+ (or (directory-exists? dep-dir)
+ (if (file-is-directory? beam-input)
+ (copy-recursively beam-input dep-dir)
+ (begin
+ (mkdir-p dep-dir)
+ (invoke "sh" "-c"
+ (string-append "tar -xOf " beam-input
+ " contents.tar.gz"
+ " | tar -xz -C " dep-dir))))))
+ (and
+ vendorize?
+ (begin
+ (mkdir-p vendor-dir)
+ (setenv %vendorize-env-var (canonicalize-path vendor-dir))
+ (let ((beam-inputs (delete-duplicates (inputs->beam-inputs inputs))))
+ (unless (null? beam-inputs)
+ (for-each
+ (lambda (beam-input)
+ (let* ((upstream-name (beam-input->upstream-name beam-input))
+ (dep-dir (string-append vendor-dir "/" upstream-name)))
+ (copy-or-unpack-beam-package beam-input dep-dir)))
+ beam-inputs))))))
+
+(define* (symlink-vendorize #:key vendorize? vendor-symlinks?
+ #:allow-other-keys)
+ (and
+ vendorize?
+ vendor-symlinks?
+ (let* ((vendor-dir (getenv %vendorize-env-var))
+ (deps-dir (list-directories vendor-dir)))
+ (for-each
+ (lambda (dep-dir)
+ (let ((snapshot (string-contains dep-dir "_snapshot")))
+ (and
+ snapshot
+ (let ((canonical
+ (string-drop-right
+ dep-dir (- (string-length dep-dir) snapshot))))
+ (symlink dep-dir canonical)))))
+ deps-dir))))
+
+(define (beam-package? name)
+ (string-prefix? %beam-prefix name))
(define* (set-mix-env #:key inputs mix-path mix-exs #:allow-other-keys)
"Set environment variables.
@@ -113,22 +183,32 @@ See:
https://hexdocs.pm/mix/1.15.7/Mix.html#module-environment-variables"
(%elixir-version (elixir-version inputs))
(format #t "Elixir version: ~a~%" (%elixir-version)))
-(define* (build #:key mix-environments #:allow-other-keys)
+
+(define* (build #:key mix-environments vendorize?
+ #:allow-other-keys)
"Builds the Mix project."
(for-each (lambda (mix-env)
(setenv "MIX_ENV" mix-env)
- (invoke "mix" "compile" "--no-deps-check"
- "--no-prune-code-paths"))
+ (if vendorize?
+ (invoke "mix" "release")
+ (invoke "mix" "compile" "--no-prune-code-paths"
+ "--no-deps-check")))
mix-environments))
-(define* (check #:key (tests? #t) (test-flags '()) #:allow-other-keys)
+(define* (check #:key (tests? #t) (test-flags '())
+ vendorize?
+ #:allow-other-keys)
"Test the Mix project."
(if tests?
- (begin
+ (let ((maybe-no-deps-check-flag
+ (if vendorize? '() '("--no-deps-check"))))
(setenv "MIX_ENV" "test")
- (apply invoke "mix" "do" "compile" "--no-deps-check"
- "--no-prune-code-paths" "+" "test"
- "--no-deps-check" test-flags))
+ (apply invoke
+ (append '("mix" "do" "compile")
+ maybe-no-deps-check-flag
+ '("--no-prune-code-paths" "+" "test")
+ maybe-no-deps-check-flag
+ test-flags)))
(format #t "tests? = ~a~%" tests?)))
(define* (remove-mix-dirs . _)
@@ -137,7 +217,7 @@ 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)
+(define* (package-name->elixir-name name+ver #:optional (prefix
%elixir-prefix))
"Convert the Guix package NAME-VER to the corresponding Elixir name-version
format. Example: elixir-a-pkg-1.2.3 -> a_pkg or elixir-a-pkg-0.0.0-0.e51e36e
-> a_pkg"
@@ -146,7 +226,7 @@ format. Example: elixir-a-pkg-1.2.3 -> a_pkg or
elixir-a-pkg-0.0.0-0.e51e36e
(cute string-join <> "_")
(cute drop-right <> (if git-version? 2 1))
(cute string-split <> #\-))
- (strip-prefix name+ver)))
+ (strip-prefix name+ver prefix)))
(define* (install #:key
inputs
@@ -155,13 +235,24 @@ format. Example: elixir-a-pkg-1.2.3 -> a_pkg or
elixir-a-pkg-0.0.0-0.e51e36e
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)))
+ (if (beam-package? name)
+ (let* ((out (assoc-ref outputs "out"))
+ (excluded '("CHECKSUM" "contents.tar.gz" "environment-variables"
+ "metadata.config" "VERSION"))
+ (files
+ (filter
+ (lambda (f)
+ (not (member f (cons* "." ".." excluded))))
+ (scandir "."))))
+ (mkdir-p out)
+ (apply invoke "cp" "-r" (append files (list out))))
+ (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
@@ -170,6 +261,8 @@ format. Example: elixir-a-pkg-1.2.3 -> a_pkg or
elixir-a-pkg-0.0.0-0.e51e36e
(add-after 'install-locale 'set-mix-env set-mix-env)
(add-after 'set-mix-env 'set-elixir-version set-elixir-version)
(replace 'unpack unpack)
+ (add-after 'unpack 'unpack-vendorize unpack-vendorize)
+ (add-after 'unpack-vendorize 'symlink-vendorize symlink-vendorize)
(replace 'build build)
(replace 'check check)
(add-before 'install 'remove-mix-dirs remove-mix-dirs)
diff --git a/guix/import/hexpm.scm b/guix/import/hexpm.scm
index 885e416fea..e0b2ac8de1 100644
--- a/guix/import/hexpm.scm
+++ b/guix/import/hexpm.scm
@@ -177,33 +177,37 @@ as symbols."
(define* (make-hexpm-sexp #:key name version tarball-url
home-page synopsis description license
language build-system dependencies
+ mix-inputs?
#:allow-other-keys)
"Return the `package' s-expression for a hexpm package with the given NAME,
VERSION, TARBALL-URL, HOME-PAGE, SYNOPSIS, DESCRIPTION, and LICENSE. The
created package's name will stem from LANGUAGE. BUILD-SYSTEM defined the
-build-system, and DEPENDENCIES the inputs for the package."
- (call-with-temporary-output-file
- (lambda (temp port)
- (and (url-fetch tarball-url temp)
- (values
- `(package
- (name ,(hexpm-name->package-name name language))
- (version ,version)
- (source (origin
- (method url-fetch)
- (uri (hexpm-uri ,name version))
- (sha256 (base32 ,(guix-hash-url temp)))))
- (build-system ,build-system)
- ,@(maybe-inputs (dependencies->package-names dependencies) 'inputs)
- (synopsis ,synopsis)
- (description ,(beautify-description description))
- (home-page ,(match home-page
- (() "")
- (_ home-page)))
- (license ,(match license
- (() #f)
- ((license) license)
- (_ `(list ,@license))))))))))
+build-system, and DEPENDENCIES or MIX-INPUTS? the inputs for the package."
+ (let ((package-name (hexpm-name->package-name name language)))
+ (call-with-temporary-output-file
+ (lambda (temp port)
+ (and (url-fetch tarball-url temp)
+ (values
+ `(package
+ (name ,package-name)
+ (version ,version)
+ (source (origin
+ (method url-fetch)
+ (uri (hexpm-uri ,name version))
+ (sha256 (base32 ,(guix-hash-url temp)))))
+ (build-system ,build-system)
+ ,@(if mix-inputs?
+ `((inputs (mix-inputs ',(string->symbol package-name))))
+ (maybe-inputs (dependencies->package-names dependencies)
'inputs))
+ (synopsis ,synopsis)
+ (description ,(beautify-description description))
+ (home-page ,(match home-page
+ (() "")
+ (_ home-page)))
+ (license ,(match license
+ (() #f)
+ ((license) license)
+ (_ `(list ,@license)))))))))))
(define (strings->licenses strings)
"Convert the list of STRINGS into a list of license objects."
@@ -221,11 +225,13 @@ object, ordered from newest to oldest."
(sort (map hexpm-version-number (hexpm-versions package))
version>?))
-(define* (hexpm->guix-package package-name #:key version #:allow-other-keys)
- "Fetch the metadata for PACKAGE-NAME from hexpms.io, and return the
+(define* (hexpm->guix-package package-name #:key version
+ mix-inputs? #:allow-other-keys)
+ "Fetch the metadata for PACKAGE-NAME from hex.pm, and return the
`package' s-expression corresponding to that package, or #f on failure.
When VERSION is specified, attempt to fetch that version; otherwise fetch the
-latest version of PACKAGE-NAME."
+latest version of PACKAGE-NAME. It can use `inputs` with `mix-inputs` when
+MIX-INPUTS? is specified."
(define package
(lookup-hexpm package-name))
@@ -277,6 +283,7 @@ latest version of PACKAGE-NAME."
#:name package-name
#:version version-number
#:dependencies dependencies
+ #:mix-inputs? mix-inputs?
#:home-page (or (and (not (eq? docs-html-url 'null))
docs-html-url)
;; TODO: Homepage?
diff --git a/guix/import/mix.scm b/guix/import/mix.scm
new file mode 100644
index 0000000000..89a6e6fa4e
--- /dev/null
+++ b/guix/import/mix.scm
@@ -0,0 +1,143 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Igorj Gorjaĉev <[email protected]>
+;;;
+;;; 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 import mix)
+ #:use-module (guix import mix mix-lock)
+ #:use-module (guix base16)
+ #:use-module (guix base32)
+ #:use-module (guix read-print)
+ #:use-module (guix scripts download)
+ #:autoload (guix utils) (find-definition-location)
+ #:use-module (ice-9 match)
+ #:use-module (ice-9 textual-ports)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-26)
+ #:use-module (srfi srfi-71)
+ #:use-module (guix build-system mix)
+ #:export (mix-lock->expressions
+ mix-inputs-from-lockfile
+ find-mix-inputs-location
+ extract-mix-inputs))
+
+
+;;;
+;;; Convert ‘mix.lock’ to Guix sources.
+;;;
+
+(define (mix-lock->expressions lockfile package-name)
+ "Given LOCKFILE, a 'mix.lock' file, import its content as source
+expressions. Return a source list and a mix inputs entry for PACKAGE-NAME
+referencing all imported sources."
+ (define (mix-lock-entry->guix-source mix-lock-entry)
+ (match mix-lock-entry
+ (('mix-lock-entry
+ ('entry-name entry-name)
+ ('entry-hexpm
+ ('hexpm-name hexpm-name)
+ ('hexpm-version hexpm-version)
+ _
+ ('hexpm-checksum hexpm-checksum)))
+ `(define
+ ,(string->symbol
+ (string-append
+ (beam-name->package-name entry-name) "-" hexpm-version))
+ (hexpm-source ,entry-name ,hexpm-name ,hexpm-version
+ ,(bytevector->nix-base32-string
+ (base16-string->bytevector hexpm-checksum)))))
+ ;; Git snapshot.
+ (('mix-lock-entry
+ ('entry-name entry-name)
+ ('entry-git
+ ('git-url git-url)
+ ('git-commit git-commit)))
+ (begin
+ (let* ((version (string-append "snapshot." (string-take git-commit
7)))
+ (checksum
+ (second
+ (string-split
+ (with-output-to-string
+ (lambda _
+ (guix-download "-g" git-url
+ (string-append "--commit=" git-commit))))
+ #\newline))))
+ `(define
+ ,(string->symbol
+ (string-append
+ (beam-name->package-name entry-name) "-" version))
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url ,git-url)
+ (commit ,git-commit)))
+ (file-name
+ (git-file-name
+ ,(beam-name->package-name entry-name) ,version))
+ (sha256 (base32 ,checksum)))))))
+ (else #f)))
+
+ (let* ((source-expressions
+ (filter-map mix-lock-entry->guix-source
+ (mix-lock-string->scm
+ (call-with-input-file lockfile get-string-all))))
+ (beam-inputs-entry
+ `(,(string->symbol package-name) =>
+ (list ,@(map second source-expressions)))))
+ (values source-expressions beam-inputs-entry)))
+
+(define* (mix-inputs-from-lockfile #:optional (lockfile "mix.lock"))
+ "Given LOCKFILE (default to \"mix.lock\" in current directory), return a
+source list imported from it, to be used as package inputs. This procedure
+can be used for adding a manifest file within the source tree of a BEAM
+application."
+ (let ((source-expressions
+ mix-inputs-entry
+ (mix-lock->expressions lockfile "mix-inputs-temporary")))
+ (eval-string
+ (call-with-output-string
+ (lambda (port)
+ (for-each
+ (cut pretty-print-with-comments port <>)
+ `((use-modules (guix build-system mix))
+ ,@source-expressions
+ (define-mix-inputs lookup-mix-inputs ,mix-inputs-entry)
+ (lookup-mix-inputs 'mix-inputs-temporary))))))))
+
+(define (find-mix-inputs-location file)
+ "Search in FILE for a top-level definition of mix inputs. Return the
+location if found, or #f otherwise."
+ (find-definition-location file 'lookup-mix-inputs
+ #:define-prefix 'define-mix-inputs))
+
+(define* (extract-mix-inputs file #:key exclude)
+ "Search in FILE for a top-level definition of mix inputs. If found,
+return its entries excluding EXCLUDE, or an empty list otherwise."
+ (call-with-input-file file
+ (lambda (port)
+ (do ((syntax (read-syntax port)
+ (read-syntax port)))
+ ((match (syntax->datum syntax)
+ (('define-mix-inputs 'lookup-mix-inputs _ ...) #t)
+ ((? eof-object?) #t)
+ (_ #f))
+ (or (and (not (eof-object? syntax))
+ (match (syntax->datum syntax)
+ (('define-mix-inputs 'lookup-mix-inputs inputs ...)
+ (remove (lambda (mix-input-entry)
+ (eq? exclude (first mix-input-entry)))
+ inputs))))
+ '()))))))
diff --git a/guix/import/mix/mix-lock.scm b/guix/import/mix/mix-lock.scm
new file mode 100644
index 0000000000..4d2c9ecec5
--- /dev/null
+++ b/guix/import/mix/mix-lock.scm
@@ -0,0 +1,201 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Igorj Gorjaĉev <[email protected]>
+;;;
+;;; 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 import mix mix-lock)
+ #:use-module (ice-9 peg)
+ #:export (mix-lock-string->scm
+
+ entry-git
+ entry-hexpm
+ entry-name
+ git-commit
+ git-url
+ hexpm-build-system
+ hexpm-build-systems
+ hexpm-name
+ hexpm-version
+ mix-lock
+ mix-lock-entry))
+
+;;;
+;;; PEG parser for ‘mix.lock’.
+;;;
+
+(define (mix-lock-string->scm str)
+ (peg:tree (search-for-pattern mix-lock str)))
+
+;; Auxiliar peg patterns
+(define-peg-pattern numeric-char body
+ (range #\0 #\9))
+
+(define-peg-pattern lowercase-char body
+ (range #\a #\z))
+
+(define-peg-pattern uppercase-char body
+ (range #\A #\Z))
+
+(define-peg-pattern alphabetic-char body
+ (or lowercase-char uppercase-char))
+
+(define-peg-pattern alphanumeric-char body
+ (or alphabetic-char numeric-char))
+
+(define-peg-pattern space-char body
+ (+ (or " " "\t" "\n" "\r")))
+
+(define-peg-pattern opt-space-char body
+ (* space-char))
+
+;; string: "string"
+(define-peg-pattern string-char body
+ (or alphanumeric-char
+ space-char
+ "_" "." "~" "=" "<" ">" "/" ":" "-"))
+
+(define-peg-pattern string body
+ (and (ignore "\"")
+ (* string-char)
+ (ignore "\"")))
+
+;; atom: :hex or true | false | nil
+(define-peg-pattern atom-char body
+ (or alphanumeric-char "_" "@" "?" "!"))
+
+(define-peg-pattern atom body
+ (or
+ (and (ignore ":")
+ (+ atom-char))
+ "true" "false" "nil"))
+
+;; list: [ ... ]
+(define-peg-pattern list body
+ (and (ignore "[")
+ (* (and
+ (ignore (? ", "))
+ (or value key-value)))
+ (ignore "]")))
+
+;; tuple: { ... }
+(define-peg-pattern tuple body
+ (and (ignore "{")
+ (* (and
+ (ignore (? ", "))
+ value))
+ (ignore "}")
+ (ignore (? "\n"))))
+
+;; syntactic sugar for [{key, value} ...]
+(define-peg-pattern key-value body
+ (ignore
+ (and (+ atom-char) ": " value)))
+
+;; value may be string, atom, tuple, list
+(define-peg-pattern value body
+ (and (ignore opt-space-char)
+ (or string atom tuple list)
+ (ignore opt-space-char)))
+
+;; ignore several constructions
+(define-peg-pattern ignore-string body
+ (ignore
+ (and "\""
+ (* (or string-char
+ space-char))
+ "\"")))
+
+(define-peg-pattern ignore-comma-space body
+ (ignore ", "))
+
+(define-peg-pattern ignore-list body
+ (ignore (and "["
+ (* (and
+ (? ", ")
+ (or value key-value)))
+ "]")))
+
+;; exported symbols
+(define-peg-pattern entry-name all
+ string)
+
+(define-peg-pattern hexpm-name all
+ atom)
+
+(define-peg-pattern hexpm-version all
+ string)
+
+(define-peg-pattern hexpm-checksum all
+ string)
+
+(define-peg-pattern hexpm-build-system all
+ (capture (+ (or alphanumeric-char "_" "-"))))
+
+(define-peg-pattern hexpm-build-systems all
+ (and (ignore "[")
+ (capture
+ (* (and (ignore ":")
+ (capture hexpm-build-system)
+ (ignore (? ", ")))))
+ (ignore "]")))
+
+(define-peg-pattern git-url all
+ string)
+
+(define-peg-pattern git-commit all
+ string)
+
+(define-peg-pattern entry-hexpm all
+ (and
+ (ignore ": {:hex, ")
+ hexpm-name
+ ignore-comma-space
+ (capture hexpm-version)
+ ignore-comma-space
+ ignore-string
+ ignore-comma-space
+ (capture hexpm-build-systems)
+ ignore-comma-space
+ ignore-list
+ ignore-comma-space
+ ignore-string
+ ignore-comma-space
+ (capture hexpm-checksum)
+ (ignore "}")))
+
+(define-peg-pattern entry-git all
+ (and
+ (ignore ": {:git, ")
+ git-url
+ ignore-comma-space
+ git-commit
+ ignore-comma-space
+ ignore-list
+ (ignore "}")))
+
+(define-peg-pattern mix-lock-entry all
+ (and (ignore (? " "))
+ (capture entry-name)
+ (or
+ entry-hexpm
+ entry-git)
+ (ignore (? ",\n"))))
+
+;; mix.lock
+(define-peg-pattern mix-lock all
+ (and (ignore "%{\n")
+ (* mix-lock-entry)
+ (ignore "}")))
diff --git a/guix/scripts/import.scm b/guix/scripts/import.scm
index 464fa372cd..ef33ef5f40 100644
--- a/guix/scripts/import.scm
+++ b/guix/scripts/import.scm
@@ -7,6 +7,7 @@
;;; Copyright © 2021 Xinglu Chen <[email protected]>
;;; Copyright © 2022 Philip McGrath <[email protected]>
;;; Copyright © 2024 Herman Rimm <[email protected]>
+;;; Copyright © 2025 Igorj Gorjaĉev <[email protected]>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -63,6 +64,7 @@
(define importers '("composer" "cpan" "cran" "crate" "egg" "elm" "elpa"
"gem" "gnu" "go" "hackage" "hexpm" "json" "luanti"
"minetest" ; deprecated
+ "mix"
"npm-binary" "nuget" "opam" "pypi" "stackage" "texlive"))
(define (resolve-importer name)
@@ -143,10 +145,10 @@ PROC callback."
((or ("-i" file importer args ...)
("--insert" file importer args ...))
(let* ((define-prefixes
- `(,@(if (member importer '("crate"))
- '(define)
- '())
- define-public))
+ `(,@(if (member importer '("crate" "mix"))
+ '(define)
+ '())
+ define-public))
(define-prefix? (cut member <> define-prefixes))
(find-and-insert
(lambda (expr)
@@ -172,9 +174,9 @@ PROC callback."
((importer args ...)
(let ((print (lambda (expr)
(leave-on-EPIPE
- (pretty-print-with-comments
- (current-output-port) expr)
- ;; Two newlines: one after the closing paren, and
- ;; one to leave a blank line.
- (newline) (newline)))))
+ (pretty-print-with-comments
+ (current-output-port) expr)
+ ;; Two newlines: one after the closing paren, and
+ ;; one to leave a blank line.
+ (newline) (newline)))))
(import-as-definitions importer args print)))))
diff --git a/guix/scripts/import/hexpm.scm b/guix/scripts/import/hexpm.scm
index eb9a1b0af5..c645273129 100644
--- a/guix/scripts/import/hexpm.scm
+++ b/guix/scripts/import/hexpm.scm
@@ -47,6 +47,8 @@ Import and convert the hex.pm package for PACKAGE-NAME.\n"))
(newline)
(display (G_ "
-r, --recursive import packages recursively"))
+ (display (G_ "
+ -m, --mix-inputs use mix-inputs for input field"))
(newline)
(show-bug-report-information))
@@ -62,6 +64,9 @@ Import and convert the hex.pm package for PACKAGE-NAME.\n"))
(option '(#\r "recursive") #f #f
(lambda (opt name arg result)
(alist-cons 'recursive #t result)))
+ (option '("mix-inputs") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'mix-inputs #t result)))
%standard-import-options))
@@ -94,7 +99,9 @@ Import and convert the hex.pm package for PACKAGE-NAME.\n"))
(_ #f))
(hexpm-recursive-import name version))
;; Single import
- (let ((sexp (hexpm->guix-package name #:version version)))
+ (let* ((mix-inputs (assoc-ref opts 'mix-inputs))
+ (sexp (hexpm->guix-package name #:version version
+ #:mix-inputs? mix-inputs)))
(unless sexp
(leave (G_ "failed to download meta-data for package
'~a'~%")
spec))
diff --git a/guix/scripts/import/mix.scm b/guix/scripts/import/mix.scm
new file mode 100644
index 0000000000..476ff9de48
--- /dev/null
+++ b/guix/scripts/import/mix.scm
@@ -0,0 +1,122 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Igorj Gorjaĉev <[email protected]>
+;;;
+;;; 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 scripts import mix)
+ #:use-module (guix ui)
+ #:use-module (guix utils)
+ #:use-module (guix read-print)
+ #:use-module (guix scripts)
+ #:use-module (guix import mix)
+ #:use-module (guix scripts import)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-37)
+ #:use-module (srfi srfi-71)
+ #:use-module (ice-9 match)
+ #:export (guix-import-mix))
+
+
+;;;
+;;; Command-line options.
+;;;
+
+(define %default-options
+ '())
+
+(define (show-help)
+ (display (G_ "Usage: guix import mix PACKAGE-NAME
+Import package dependencies from its 'mix.lock'.\n"))
+ (display (G_ "
+ -h, --help display this help and exit"))
+ (display (G_ "
+ -V, --version display version information and exit"))
+ (newline)
+ (display (G_ "
+ -f, --lockfile=FILE import dependencies from FILE, a 'mix.lock' file"))
+ (newline)
+ (show-bug-report-information))
+
+(define %options
+ ;; Specification of the command-line options.
+ (cons* (option '(#\h "help") #f #f
+ (lambda args
+ (show-help)
+ (exit 0)))
+ (option '(#\V "version") #f #f
+ (lambda args
+ (show-version-and-exit "guix import mix")))
+ (option '(#\f "lockfile") #f #t
+ (lambda (opt name arg result)
+ (if (file-exists? arg)
+ (alist-cons 'lockfile arg result)
+ (leave (G_ "file '~a' does not exist~%") arg))))
+ %standard-import-options))
+
+
+;;;
+;;; Entry point.
+;;;
+
+(define (guix-import-mix . args)
+ (define (parse-options)
+ ;; Return the alist of option values.
+ (parse-command-line args %options (list %default-options)
+ #:build-options? #f))
+
+ (let* ((opts (parse-options))
+ (lockfile (assoc-ref opts 'lockfile))
+ (file-to-insert (assoc-ref opts 'file-to-insert))
+ (args (filter-map (match-lambda
+ (('argument . value)
+ value)
+ (_ #f))
+ (reverse opts))))
+ (match args
+ ((spec)
+ (define-values (name version)
+ (package-name->name+version spec))
+
+ (if lockfile
+ (let ((source-expressions
+ _
+ (mix-lock->expressions lockfile name)))
+ (when file-to-insert
+ (let* ((source-expressions
+ mix-inputs-entry
+ (mix-lock->expressions lockfile name))
+ (term (first mix-inputs-entry))
+ (mix-inputs
+ `(define-mix-inputs lookup-mix-inputs
+ ,@(sort
+ (cons mix-inputs-entry
+ (extract-mix-inputs
+ file-to-insert #:exclude term))
+ (lambda (a b)
+ (string< (symbol->string (first a))
+ (symbol->string (first b)))))))
+ (_
+ (and=> (find-mix-inputs-location file-to-insert)
+ delete-expression))
+ (port (open-file file-to-insert "a")))
+ (pretty-print-with-comments port mix-inputs)
+ (newline port)
+ (close-port port)))
+ source-expressions)))
+ (()
+ (leave (G_ "too few arguments~%")))
+ ((many ...)
+ (leave (G_ "too many arguments~%"))))))
diff --git a/tests/import/hexpm.scm b/tests/import/hexpm.scm
index 1e746f9b34..e99eb0e394 100644
--- a/tests/import/hexpm.scm
+++ b/tests/import/hexpm.scm
@@ -250,4 +250,48 @@
(x
(pk 'fail x #f))))))
+(test-assert "hexpm-with-mix-inputs"
+ ;; Replace network resources with sample data.
+ (mock ((guix http-client) http-fetch
+ (lambda (url . rest)
+ (match url
+ ("https://hex.pm/api/packages/bla"
+ (values (open-input-string test-bla-package)
+ (string-length test-bla-package)))
+ ("https://hex.pm/api/packages/bla/releases/1.5.0"
+ (values (open-input-string test-bla-release)
+ (string-length test-bla-release)))
+ (_ (error "http-fetch got unexpected URL: " url)))))
+ (mock ((guix build download) url-fetch
+ (lambda* (url file-name
+ #:key
+ (mirrors '()) verify-certificate?)
+ (with-output-to-file file-name
+ (lambda ()
+ (display
+ (match url
+ ("https://repo.hex.pm/tarballs/bla-1.5.0.tar"
+ "source")
+ (_ (error "url-fetch got unexpected URL: " url))))))))
+ (match (hexpm->guix-package "bla" #:mix-inputs? #t)
+ (`(package
+ (name "erlang-bla")
+ (version "1.5.0")
+ (source
+ (origin
+ (method url-fetch)
+ (uri (hexpm-uri "bla" version))
+ (sha256
+ (base32
+ "0zcl4dgcmqwl1g5xb901pd6dz61r1xgmac9mqlwvh022paa6gks1"))))
+ (build-system rebar-build-system)
+ (inputs (mix-inputs 'erlang-bla))
+ (synopsis "A cool package")
+ (description "This package provides a cool package.")
+ (home-page "https://hex.pm/packages/bla")
+ (license (list license:expat license:asl2.0)))
+ #t)
+ (x
+ (pk 'fail x #f))))))
+
(test-end "hexpm")
diff --git a/tests/import/mix.scm b/tests/import/mix.scm
new file mode 100644
index 0000000000..46d33165a3
--- /dev/null
+++ b/tests/import/mix.scm
@@ -0,0 +1,97 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Igorj Gorjaĉev <[email protected]>
+;;;
+;;; 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 (test-mix)
+ #:use-module (guix import mix)
+ #:use-module (guix base32)
+ #:use-module (guix build-system mix)
+ #:use-module (gcrypt hash)
+ #:use-module (guix tests)
+ #:use-module (srfi srfi-11)
+ #:use-module (srfi srfi-64)
+ #:use-module (ice-9 binary-ports)
+ #:use-module (ice-9 match))
+
+(define test-mix-lock-file
+ "\
+%{
+ \"gen_stage\": {:hex, :gen_stage, \"1.3.2\", \
+\"7c77e5d1e97de2c6c2f78f306f463bca64bf2f4c3cdd606affc0100b89743b7b\", \
+[:mix], [], \"hexpm\", \
+\"0ffae547fa777b3ed889a6b9e1e64566217413d018cabd825f786e843ffe63e7\"},
+ \"eini\": {:hex, :eini_beam, \"2.2.4\", \
+\"02143b1dce4dda4243248e7d9b3d8274b8d9f5a666445e3d868e2cce79e4ff22\", \
+[:rebar3], [], \"hexpm\", \
+\"12de479d144b19e09bb92ba202a7ea716739929afdf9dff01ad802e2b1508471\"},
+ \"erlydtl\": {:git, \"https://github.com/manuel-rubio/erlydtl.git\", \
+\"dffa1a73ee2bfba14195b8b3964c39f007ff1284\", []},
+}")
+
+(define temp-file
+ (string-append "t-mix-" (number->string (getpid))))
+
+(test-begin "mix")
+
+(test-assert "mix-lock-file-import"
+ (begin
+ (call-with-output-file temp-file
+ (lambda (port)
+ (display test-mix-lock-file port)))
+ (mock
+ ((guix scripts download) guix-download
+ (lambda _
+ (format #t "~a~%~a~%"
+ "/gnu/store/m43vixiijc26ni5p9zvbvjrs311h4fsm-erlydtl-dffa1a7"
+ "1jhcfh0idadlh9999kjzx1riqjw0k05wm6ii08xkjvirhjg0vawh")))
+ (let-values
+ (((source-expressions beam-inputs-entry)
+ (mix-lock->expressions temp-file "test")))
+ (and
+ (match source-expressions
+ ('((define beam-gen-stage-1.3.2
+ (hexpm-source
+ "gen_stage" "gen_stage" "1.3.2"
+ "1rv3zqzq8vkqby1bvjhqs09p88b68pkf3fd6i7c3wyvpz93ybyhg"))
+ (define beam-eini-2.2.4
+ (hexpm-source
+ "eini" "eini_beam" "2.2.4"
+ "0wc4a2qy40nq3bqdzygxka93jrvixakh58ibp6dy06ab2jflgphj"))
+ (define beam-erlydtl-snapshot.dffa1a7
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/manuel-rubio/erlydtl.git")
+ (commit "dffa1a73ee2bfba14195b8b3964c39f007ff1284")))
+ (file-name (git-file-name "beam-erlydtl" "snapshot.dffa1a7"))
+ (sha256
+ (base32
+ "1jhcfh0idadlh9999kjzx1riqjw0k05wm6ii08xkjvirhjg0vawh")))))
+ #t)
+ (x
+ (pk 'fail (pretty-print-with-comments (current-output-port) x) #f)))
+ (match beam-inputs-entry
+ (`(test => (list beam-gen-stage-1.3.2
+ beam-eini-2.2.4
+ beam-erlydtl-snapshot.dffa1a7))
+ #t)
+ (x
+ (pk 'fail x #f))))))))
+
+(test-end "mix")
+
+(false-if-exception (delete-file temp-file))