Compare commits

...

5 Commits

Author SHA1 Message Date
Igorj Gorjaĉev
7b5f2d456b gnu: Add elixir-mix-audit.
* gnu/packages/elixir-xyz.scm (elixir-mix-audit): New variable.

Closes: https://codeberg.org/guix/guix/pulls/3524
Change-Id: I1d4565135a6d4b67046c17660573849493809e1c
Signed-off-by: Giacomo Leidi <therewasa@fishinthecalculator.me>
2026-04-11 17:47:14 +02:00
Igorj Gorjaĉev
e1e28adb79 gnu: Add elixir-yaml-elixir.
* gnu/packages/elixir-xyz.scm (elixir-yaml-elixir): New variable.

Change-Id: I47e3bd31eac50699a5bccd1d515a4117ffb37563
Signed-off-by: Giacomo Leidi <therewasa@fishinthecalculator.me>
2026-04-11 17:47:14 +02:00
Igorj Gorjaĉev
ea66f97ff3 gnu: Add elixir-depscheck.
* gnu/packages/elixir-xyz.scm (elixir-depscheck): New variable.

Change-Id: I7bb81fc7ed8fc068eedb1052b1d7891abfa9bbea
Signed-off-by: Giacomo Leidi <therewasa@fishinthecalculator.me>
2026-04-11 17:47:14 +02:00
Igorj Gorjaĉev
76ac03cb0b 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 <therewasa@fishinthecalculator.me>
2026-04-11 17:47:09 +02:00
Igorj Gorjaĉev
f88a63b636 gnu: elixir: Add vendoring support.
* gnu/packages/elixir.scm (elixir): Add vendoring support.
* gnu/packages/patches/elixir-vendoring-support.patch: New file.
* gnu/local.mk (dist_patch_DATA): Register it.

Change-Id: Id295ac66684c724ac799878ec58d84617bf9a5be
Signed-off-by: Giacomo Leidi <therewasa@fishinthecalculator.me>
2026-04-11 17:46:40 +02:00
22 changed files with 1214 additions and 64 deletions

View File

@@ -13,6 +13,7 @@ gnu/packages/fluidplug\.scm @guix/audio
gnu/packages/music\.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
@@ -20,7 +21,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

View File

@@ -22,6 +22,7 @@
# Copyright © 2024 gemmaro <gemmaro.dev@gmail.com>
# Copyright © 2025 Brice Waegeneire <brice@waegenei.re>
# Copyright © 2025 Florian Pelz <pelzflorian@pelzflorian.de>
# Copyright © 2025 Igorj Gorjaĉev <igor@goryachev.org>
#
# This file is part of GNU Guix.
#
@@ -320,6 +321,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 \
@@ -377,6 +380,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 \
@@ -577,6 +581,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 \

View File

@@ -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
@@ -1632,6 +1634,7 @@ systems, serving as extensions to the concise packaging guidelines
@menu
* Packaging Rust Crates::
* Packaging Elixir Applications::
@end menu
@node Packaging Rust Crates
@@ -1987,6 +1990,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

View File

@@ -151,6 +151,7 @@ Copyright @copyright{} 2025 Noé Lopez@*
Copyright @copyright{} 2026 David Elsing@*
Copyright @copyright{} 2026 Nguyễn Gia Phong@*
Copyright @copyright{} 2026 Yarl Baudig@*
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
@@ -15388,6 +15389,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

View File

@@ -482,15 +482,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

View File

@@ -75,6 +75,7 @@
# Copyright © 2025 Nigko Yerden <nigko.yerden@gmail.com>
# Copyright © 2025 Cayetano Santos <csantosb@inventati.org>
# Copyright © 2025 bdunahu <bdunahu@operationnull.com>
# Copyright © 2025 Igorj Gorjaĉev <igor@goryachev.org>
#
# This file is part of GNU Guix.
#
@@ -175,6 +176,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 \
@@ -1228,6 +1232,7 @@ dist_patch_DATA = \
%D%/packages/patches/elfutils-tests-ptrace.patch \
%D%/packages/patches/elixir-httpoison-tag-network-dependent-test-cases.patch \
%D%/packages/patches/elixir-path-length.patch \
%D%/packages/patches/elixir-vendoring-support.patch \
%D%/packages/patches/elm-ghc9.2.patch \
%D%/packages/patches/exaile-gstreamer-1.28.patch \
%D%/packages/patches/python-treelib-remove-python2-compat.patch \

View File

@@ -0,0 +1,25 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2025 Igorj Gorjaĉev <igor@goryachev.org>
;;;
;;; 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))

View File

@@ -0,0 +1,43 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2025 Igorj Gorjaĉev <igor@goryachev.org>
;;;
;;; 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)

View File

@@ -0,0 +1,29 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2025 Igorj Gorjaĉev <igor@goryachev.org>
;;;
;;; 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.
;;;

View File

@@ -1,6 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Pierre-Henry Fröhring <phfrohring@deeplinks.com>
;;; Copyright © 2024 Igor Goryachev <igor@goryachev.org>
;;; Copyright © 2024, 2026 Igorj Gorjaĉev <igor@goryachev.org>
;;; Copyright © 2025 Giacomo Leidi <therewasa@fishinthecalculator.me>
;;;
;;; This file is part of GNU Guix.
@@ -24,6 +24,7 @@
#:use-module (gnu packages linux)
#:use-module (gnu packages compression)
#:use-module (gnu packages base)
#:use-module (gnu packages erlang)
#:use-module (gnu packages erlang-xyz)
#:use-module (gnu packages gettext)
#:use-module (gnu packages)
@@ -423,6 +424,25 @@ implementing function decorators for Elixir.")
(home-page "https://hexdocs.pm/decorator/")
(license license:expat)))
(define-public elixir-depscheck
(package
(name "elixir-depscheck")
(version "1.0.11")
(source
(origin
(method url-fetch)
(uri (hexpm-uri "depscheck" version))
(sha256
(base32 "1v33ml2i19s59518j9031bsvipbf4xxr4xk49qx6xs3qby0018aw"))))
(build-system mix-build-system)
(synopsis "Checking dependency license compatibility in Elixir projects")
(description "This package provides @code{elixir-depscheck}, a @code{CI/CD}
tool for checking dependency license compatibility in Elixir projects. Reads
license information from local hex metadata files and validates compatibility
with your project's license using industry-standard rules.")
(home-page "https://hexdocs.pm/depscheck/")
(license license:expat)))
(define-public elixir-erlex
(package
(name "elixir-erlex")
@@ -731,6 +751,28 @@ Elixir.")
(home-page "https://hexdocs.pm/mimic/")
(license license:asl2.0)))
(define-public elixir-mix-audit
(package
(name "elixir-mix-audit")
(version "2.1.5")
(source
(origin
(method url-fetch)
(uri (hexpm-uri "mix_audit" version))
(sha256
(base32 "0s9dnk42n5665s6l22qf05x63ly11n37am2kmybzccns4672kyc7"))))
(build-system mix-build-system)
(propagated-inputs
(list elixir-jason
elixir-yaml-elixir))
(synopsis "Scanning Mix dependencies for security vulnerabilities")
(description "This package provides @code{elixir-mix-audit}, a
@code{mix deps.audit} task to scan @code{Mix} dependencies for security
vulnerabilities. It draw its inspiration from tools like npm audit and
bundler-audit.")
(home-page "https://hexdocs.pm/mix_audit/")
(license license:bsd-3)))
(define-public elixir-mox
(package
(name "elixir-mox")
@@ -1227,6 +1269,34 @@ provides a polyfill for @code{dbg} which was introduced in Elixir 1.14.")
(home-page "https://hexdocs.pm/verbs/")
(license license:expat)))
(define-public elixir-yaml-elixir
(package
(name "elixir-yaml-elixir")
(version "2.12.0")
(source
(origin
(method url-fetch)
(uri (hexpm-uri "yaml_elixir" version))
(sha256
(base32 "1ihknd9qy8y533zd150ch2yag2h895hsp86wamqpm4dcgfpaqsya"))))
(build-system mix-build-system)
(arguments
(list
#:phases
#~(modify-phases %standard-phases
(add-after 'unpack 'fix-yamerl-hrl-path
(lambda _
(substitute* "lib/yaml_elixir/records.ex"
(("internal\\/yamerl_constr\\.hrl")
"yamerl_constr.hrl")))))))
(propagated-inputs
(list erlang-yamerl))
(synopsis "YAML parser for Elixir based on native Erlang implementation")
(description "This package provides @code{elixir-yaml-elixir}, a @code{YAML}
parser for @code{Elixir} based on native @code{Erlang} implementation.")
(home-page "https://hexdocs.pm/yaml_elixir/")
(license license:expat)))
(define-public elixir-zest
(package
(name "elixir-zest")

View File

@@ -51,7 +51,8 @@
(file-name (git-file-name name version))
(sha256
(base32 "1i10a5d7mlcrav47k7qirqvrqn2kbl5265fbcp8fzavr86xz67m6"))
(patches (search-patches "elixir-path-length.patch"))))
(patches (search-patches "elixir-path-length.patch"
"elixir-vendoring-support.patch"))))
(build-system gnu-build-system)
(arguments
(list

View File

@@ -0,0 +1,56 @@
Author: Igorj Gorjaĉev <igor@goryachev.org>
Date: Wed Oct 22 10:10:13 2025 +0300
vendoring support for guix
diff --git a/lib/mix/lib/mix/dep/loader.ex b/lib/mix/lib/mix/dep/loader.ex
index e47bfc556..0ddd49fe5 100644
--- a/lib/mix/lib/mix/dep/loader.ex
+++ b/lib/mix/lib/mix/dep/loader.ex
@@ -198,6 +198,10 @@ defp with_scm_and_app(app, req, opts, original, locked?) do
)
end
+ scm = Mix.Guix.scm(scm)
+ req = Mix.Guix.req(req)
+ opts = Mix.Guix.opts(opts, app)
+
%Mix.Dep{
scm: scm,
app: app,
diff --git a/lib/mix/lib/mix/guix.ex b/lib/mix/lib/mix/guix.ex
new file mode 100644
index 000000000..9c821704c
--- /dev/null
+++ b/lib/mix/lib/mix/guix.ex
@@ -0,0 +1,30 @@
+defmodule Mix.Guix do
+ @moduledoc false
+
+ @guix_vendorize "GUIX_MIX_VENDOR_DIR"
+
+ def scm(scm), do: (vendorize?() && Mix.SCM.Path) || scm
+
+ def req(req), do: (vendorize?() && nil) || req
+
+ def opts(opts, app) do
+ if vendorize?() do
+ if Keyword.has_key?(opts, :path) do
+ opts
+ else
+ dep_dir = vendor_dep_dir(app)
+
+ [path: dep_dir, dest: dep_dir] ++
+ Keyword.drop(opts, [:hex, :override, :repo, :lock, :dest])
+ end
+ else
+ opts
+ end
+ end
+
+ defp vendorize?, do: not is_nil(vendor_dir())
+
+ defp vendor_dir, do: System.get_env(@guix_vendorize)
+
+ defp vendor_dep_dir(name), do: "#{vendor_dir()}/#{name}"
+end

View File

@@ -1,6 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Pierre-Henry Fröhring <contact@phfrohring.com>
;;; Copyright © 2025 Giacomo Leidi <therewasa@fishinthecalculator.me>
;;; Copyright © 2025 Igorj Gorjaĉev <igor@goryachev.org>
;;;
;;; 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

View File

@@ -1,6 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Pierre-Henry Fröhring <contact@phfrohring.com>
;;; Copyright © 2024, 2026 Igorj Gorjaĉev <igor@goryachev.org>
;;; Copyright © 2024-2026 Igorj Gorjaĉev <igor@goryachev.org>
;;; Copyright © 2024, 2025 Giacomo Leidi <therewasa@fishinthecalculator.me>
;;;
;;; 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)

View File

@@ -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?

143
guix/import/mix.scm Normal file
View File

@@ -0,0 +1,143 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2025 Igorj Gorjaĉev <igor@goryachev.org>
;;;
;;; 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))))
'()))))))

View File

@@ -0,0 +1,201 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2025 Igorj Gorjaĉev <igor@goryachev.org>
;;;
;;; 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 "}")))

View File

@@ -7,6 +7,7 @@
;;; Copyright © 2021 Xinglu Chen <public@yoctocell.xyz>
;;; Copyright © 2022 Philip McGrath <philip@philipmcgrath.com>
;;; Copyright © 2024 Herman Rimm <herman@rimm.ee>
;;; Copyright © 2025 Igorj Gorjaĉev <igor@goryachev.org>
;;;
;;; 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)))))

View File

@@ -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))

122
guix/scripts/import/mix.scm Normal file
View File

@@ -0,0 +1,122 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2025 Igorj Gorjaĉev <igor@goryachev.org>
;;;
;;; 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~%"))))))

View File

@@ -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")

97
tests/import/mix.scm Normal file
View File

@@ -0,0 +1,97 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2025 Igorj Gorjaĉev <igor@goryachev.org>
;;;
;;; 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))