[PATCH 00/30] gnu: elm: Update to 0.19.1. Add build system & importer.

DoneSubmitted by Philip McGrath.
Details
2 participants
  • Ludovic Courtès
  • Philip McGrath
Owner
unassigned
Severity
normal
P
P
Philip McGrath wrote on 20 Apr 01:27 +0200
(address . guix-patches@gnu.org)
20220419232736.272970-1-philip@philipmcgrath.com
Hi,

This patch series updates Elm to version 0.9.1, then adds an
'elm-build-system' and a 'guix import elm' command.

To exercise the new features, this patch series then:

* Build the front-end for the `elm reactor` command (which is written in Elm)
and adds a variant of Elm to Guix with the command enabled;

* Builds 'elm-todomvc', an official example of a basic Elm application; and

* Builds a feature-rich third-party package, "terezka/elm-charts":

-Philip

Philip McGrath (30):
gnu: elm-compiler: Update to 0.19.1.
gnu: elm: Rename package to match the command.
guix: Add elm-build-system and 'guix import elm'.
gnu: Add elm-core and elm-json.
build-system/elm: Add implicit Elm inputs.
gnu: Add elm-virtual-dom.
gnu: Add elm-html.
gnu: Add elm-svg.
gnu: Add elm-time.
gnu: Add elm-url.
gnu: Add elm-browser.
gnu: Add elm-bytes.
gnu: Add elm-file.
gnu: Add elm-http.
gnu: Add elm-parser.
gnu: Add elm-project-metadata-utils.
gnu: Add elm-explorations-markdown.
gnu: elm: Support 'elm reactor'.
gnu: Add elm-todomvc.
gnu: Add elm-debois-elm-dom.
gnu: Add elm-random.
gnu: Add elm-explorations-test.
gnu: Add elm-danhandrea-elm-date-format.
gnu: Add elm-danhandrea-elm-time-extra.
gnu: Add elm-justinmimbs-date.
gnu: Add elm-justinmimbs-time-extra.
gnu: Add elm-myrho-elm-round.
gnu: Add elm-ryannhg-date-format.
gnu: Add elm-terezka-intervals.
gnu: Add elm-terezka-elm-charts.

gnu/local.mk | 4 +-
gnu/packages/elm.scm | 767 +++++++++++++++++-
.../elm-compiler-disable-reactor.patch | 71 --
.../patches/elm-compiler-fix-map-key.patch | 38 -
.../elm-offline-package-registry.patch | 71 ++
.../patches/elm-reactor-static-files.patch | 251 ++++++
guix/build-system/elm.scm | 176 ++++
guix/build/elm-build-system.scm | 380 +++++++++
guix/import/elm.scm | 148 ++++
guix/scripts/import.scm | 3 +-
guix/scripts/import/elm.scm | 107 +++
11 files changed, 1883 insertions(+), 133 deletions(-)
delete mode 100644 gnu/packages/patches/elm-compiler-disable-reactor.patch
delete mode 100644 gnu/packages/patches/elm-compiler-fix-map-key.patch
create mode 100644 gnu/packages/patches/elm-offline-package-registry.patch
create mode 100644 gnu/packages/patches/elm-reactor-static-files.patch
create mode 100644 guix/build-system/elm.scm
create mode 100644 guix/build/elm-build-system.scm
create mode 100644 guix/import/elm.scm
create mode 100644 guix/scripts/import/elm.scm

--
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:31 +0200
[PATCH 01/30] gnu: elm-compiler: Update to 0.19.1.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-1-philip@philipmcgrath.com
* gnu/packages/patches/elm-compiler-disable-reactor.patch,
gnu/packages/patches/elm-compiler-fix-map-key.patch: Delete files.
* gnu/packages/patches/elm-reactor-static-files.patch: New file.
* gnu/local.mk (dist_patch_DATA): Update accordingly.
* gnu/packages/elm.scm (elm-compiler): Update to 0.19.1.
[origin]<patches>: Remove stale patches. Add new patch.
[arguments]: Use G-expressions. Add #:configure-flags for new patch.
[inputs]: Remove ghc-file-embed. Add ghc-filelock.
---
gnu/local.mk | 3 +-
gnu/packages/elm.scm | 42 +--
.../elm-compiler-disable-reactor.patch | 71 -----
.../patches/elm-compiler-fix-map-key.patch | 38 ---
.../patches/elm-reactor-static-files.patch | 251 ++++++++++++++++++
5 files changed, 278 insertions(+), 127 deletions(-)
delete mode 100644 gnu/packages/patches/elm-compiler-disable-reactor.patch
delete mode 100644 gnu/packages/patches/elm-compiler-fix-map-key.patch
create mode 100644 gnu/packages/patches/elm-reactor-static-files.patch

Toggle diff (116 lines)
diff --git a/gnu/local.mk b/gnu/local.mk
index 0e721236d9..2af4d018ba 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1024,8 +1024,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/einstein-build.patch			\
   %D%/packages/patches/elfutils-tests-ptrace.patch		\
   %D%/packages/patches/elixir-path-length.patch			\
-  %D%/packages/patches/elm-compiler-disable-reactor.patch	\
-  %D%/packages/patches/elm-compiler-fix-map-key.patch		\
+  %D%/packages/patches/elm-reactor-static-files.patch		\
   %D%/packages/patches/elogind-revert-polkit-detection.patch	\
   %D%/packages/patches/emacs-exec-path.patch			\
   %D%/packages/patches/emacs-fix-scheme-indent-function.patch	\
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index ca7c61041b..708c1cf0d2 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -1,5 +1,6 @@
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2019 Robert Vollmert <rob@vllmrt.net>
+;;; Copyright © 2022 Philip McGrath <philip@philipmcgrath.com>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -24,18 +25,22 @@ (define-module (gnu packages elm)
   #:use-module (gnu packages haskell-xyz)
   #:use-module (gnu packages haskell-web)
   #:use-module (guix build-system haskell)
+  #:use-module (guix gexp)
   #:use-module (guix git-download)
   #:use-module ((guix licenses) #:prefix license:)
   #:use-module (guix packages))
 
-;; The full elm build calls out to itself via Template Haskell to
-;; compile the elm reactor web app. elm reactor isn't required to
-;; compile elm applications, so we take this part out of this
-;; bootstrap package.
+;; The `elm` build usually calls out to itself via Template Haskell to compile
+;; the `elm reactor` web app (which depends on additional Elm packages) and
+;; embeds the static files into itself.  The reactor isn't required to compile
+;; elm applications, so we want to skip it from the bootstrap package, but we
+;; also want to be able to enable it once we can build it.  We patch Elm to
+;; instead look for the files on disk relative to the executable and exit with
+;; a useful error message if they aren't there.
 (define-public elm-compiler
   (package
     (name "elm-compiler")
-    (version "0.19.0")
+    (version "0.19.1")
     (source
      (origin
        (method git-fetch)
@@ -44,24 +49,29 @@ (define-public elm-compiler
              (url "https://github.com/elm/compiler/")
              (commit version)))
        (sha256
-        (base32 "0s93z9vr0vp5w894ghc5s34nsq09sg1msf59zfiba87sid5vgjqy"))
+        (base32 "1rdg3xp3js9xadclk3cdypkscm5wahgsfmm4ldcw3xswzhw6ri8w"))
        (patches
-        (search-patches "elm-compiler-disable-reactor.patch"
-                        "elm-compiler-fix-map-key.patch"))))
+        (search-patches "elm-reactor-static-files.patch"))))
     (build-system haskell-build-system)
     (arguments
-     `(#:phases
-       (modify-phases %standard-phases
-         (add-before 'configure 'update-constraints
-           (lambda _
-             (substitute* "elm.cabal"
-               (("(ansi-terminal|containers|network|http-client|language-glsl)\\s+[^,]+" all dep)
-                dep)))))))
+     (list
+      #:configure-flags
+      #~(list (string-append "--ghc-option=-DGUIX_REACTOR_STATIC_REL_ROOT="
+                             "\"../share/elm/reactor-"
+                             #$(package-version this-package)
+                             "\""))
+      #:phases
+      #~(modify-phases %standard-phases
+          (add-before 'configure 'update-constraints
+            (lambda _
+              (substitute* "elm.cabal"
+                (("(ansi-terminal|containers|network|http-client|language-glsl)\\s+[^,]+" all dep)
+                 dep)))))))
     (inputs
      (list ghc-ansi-terminal
            ghc-ansi-wl-pprint
            ghc-edit-distance
-           ghc-file-embed
+           ghc-filelock
            ghc-http
            ghc-http-client
            ghc-http-client-tls
diff --git a/gnu/packages/patches/elm-compiler-disable-reactor.patch b/gnu/packages/patches/elm-compiler-disable-reactor.patch
deleted file mode 100644
index 9871b55e8d..0000000000
--- a/gnu/packages/patches/elm-compiler-disable-reactor.patch
+++ /dev/null
@@ -1,71 +0,0 @@
-commit 20d80e2323b565a36751c9455e535d8f73fa32f7
-Author: Robert Vollmert <rob@vllmrt.net>
-Date:   Fri Jun 14 16:05:47 2019 +0200
-
-    disable reactor
-
-diff --git a/elm.cabal b/elm.cabal
-index c75f9689..ece63c46 100644
---- a/elm.cabal
-+++ b/elm.cabal
-@@ -45,9 +45,6 @@ Executable elm
-         builder/src
-         ui/terminal/src
- 
--    other-extensions:
--        TemplateHaskell
--
- Main-Is:
- Main.hs
-
-@@ -56,8 +53,6 @@ Executable elm
- Develop
- Develop.Generate.Help
- Develop.Generate.Index
-- Develop.StaticFiles
-- Develop.StaticFiles.Build
- Diff
- Init
- Install
-diff --git a/ui/terminal/src/Develop.hs b/ui/terminal/src/Develop.hs
-index 4b2252e1..7ed7716e 100644
---- a/ui/terminal/src/Develop.hs
-+++ b/ui/terminal/src/Develop.hs
-@@ -23,7 +23,6 @@ import Snap.Util.FileServe
- import qualified Elm.Project as Project
- import qualified Develop.Generate.Help as Generate
- import qualified Develop.Generate.Index as Index
--import qualified Develop.StaticFiles as StaticFiles
- import qualified Generate.Output as Output
- import qualified Json.Encode as Encode
- import qualified Reporting.Exit as Exit
-@@ -219,16 +218,7 @@ compileToHtmlBuilder mode file =
-
-
- serveAssets :: Snap ()
--serveAssets =
-- do file <- getSafePath
-- case StaticFiles.lookup file of
-- Nothing ->
-- pass
--
-- Just (content, mimeType) ->
-- do modifyResponse (setContentType (mimeType <> ";charset=utf-8"))
-- writeBS content
--
-+serveAssets = pass
-
-
- -- MIME TYPES
-diff --git a/ui/terminal/src/Main.hs b/terminal/src/Main.hs
-index 7000f3ca..2c76965a 100644
---- a/ui/terminal/src/Main.hs
-+++ b/ui/terminal/src/Main.hs
-@@ -39,7 +39,6 @@ main =
- complex intro outro
- [ repl
- , init
-- , reactor
- , make
- , install
- , bump
Toggle diff (303 lines)
diff --git a/gnu/packages/patches/elm-compiler-fix-map-key.patch b/gnu/packages/patches/elm-compiler-fix-map-key.patch
deleted file mode 100644
index 4f05ded530..0000000000
--- a/gnu/packages/patches/elm-compiler-fix-map-key.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-commit e3512d887df41a8162c3e361171c04beca08415b
-Author: Tom Stejskal <tom.stejskal@gmail.com>
-Date:   Mon Nov 19 20:09:43 2018 +0100
-
-    Fix Map.!: given key is not an element in the map
-
-diff --git a/compiler/src/Elm/Compiler/Type/Extract.hs b/compiler/src/Elm/Compiler/Type/Extract.hs
-index 1aafe1d4..99763392 100644
---- a/compiler/src/Elm/Compiler/Type/Extract.hs
-+++ b/compiler/src/Elm/Compiler/Type/Extract.hs
-@@ -10,6 +10,7 @@ module Elm.Compiler.Type.Extract
- 
- 
- import Data.Map ((!))
-+import qualified Data.Map as Map
- import qualified Data.Maybe as Maybe
- import qualified Data.Set as Set
- 
-@@ -134,11 +135,15 @@ extractUnion interfaces (Opt.Global home name) =
-     else
-       let
-         pname = toPublicName home name
--        unions = I._unions (interfaces ! home)
-+        maybeUnions = I._unions <$> Map.lookup home interfaces
-       in
--      case I.toUnionInternals (unions ! name) of
--        Can.Union vars ctors _ _ ->
--          T.Union pname vars <$> traverse extractCtor ctors
-+      case Map.lookup name =<< maybeUnions of
-+        Just union ->
-+          case I.toUnionInternals union of
-+            Can.Union vars ctors _ _ ->
-+              T.Union pname vars <$> traverse extractCtor ctors
-+        Nothing ->
-+          return $ T.Union pname [] []
- 
- 
- extractCtor :: Can.Ctor -> Extractor (N.Name, [T.Type])
diff --git a/gnu/packages/patches/elm-reactor-static-files.patch b/gnu/packages/patches/elm-reactor-static-files.patch
new file mode 100644
index 0000000000..94c4aa0cd1
--- /dev/null
+++ b/gnu/packages/patches/elm-reactor-static-files.patch
@@ -0,0 +1,251 @@
+From 41d219a29b03f3114af7a0521c8b2dbbb487c3e1 Mon Sep 17 00:00:00 2001
+From: Philip McGrath <philip@philipmcgrath.com>
+Date: Wed, 13 Apr 2022 18:45:58 -0400
+Subject: [PATCH] reactor: look for static files relative to executable
+
+Must built with `-DGUIX_REACTOR_STATIC_REL_ROOT="../path/to/reactor"`.
+
+This lets us build a version of Elm without the `elm reactor` for
+bootstrapping, then simply put the files in place in the final package.
+---
+ elm.cabal                                 |  2 +-
+ terminal/src/Develop.hs                   | 32 +++++++++++----
+ terminal/src/Develop/StaticFiles.hs       | 37 ++++++++++-------
+ terminal/src/Develop/StaticFiles/Build.hs | 50 ++++++++++++++---------
+ 4 files changed, 79 insertions(+), 42 deletions(-)
+
+diff --git a/elm.cabal b/elm.cabal
+index bf1cfcf0..93161072 100644
+--- a/elm.cabal
++++ b/elm.cabal
+@@ -50,6 +50,7 @@ Executable elm
+ 
+     other-extensions:
+         TemplateHaskell
++        CPP
+ 
+     Main-Is:
+         Main.hs
+@@ -211,7 +212,6 @@ Executable elm
+         containers >= 0.5.8.2 && < 0.6,
+         directory >= 1.2.3.0 && < 2.0,
+         edit-distance >= 0.2 && < 0.3,
+-        file-embed,
+         filelock,
+         filepath >= 1 && < 2.0,
+         ghc-prim >= 0.5.2,
+diff --git a/terminal/src/Develop.hs b/terminal/src/Develop.hs
+index 00339364..6855b03e 100644
+--- a/terminal/src/Develop.hs
++++ b/terminal/src/Develop.hs
+@@ -33,6 +33,7 @@ import qualified Reporting.Exit as Exit
+ import qualified Reporting.Task as Task
+ import qualified Stuff
+ 
++import System.Exit as SysExit
+ 
+ 
+ -- RUN THE DEV SERVER
+@@ -45,13 +46,29 @@ data Flags =
+ 
+ 
+ run :: () -> Flags -> IO ()
+-run () (Flags maybePort) =
++run () flags = do
++  frontEnd <- StaticFiles.prepare
++  case frontEnd of
++    Right lookup ->
++      reallyRun lookup flags
++    Left missing ->
++      SysExit.die $ unlines
++      [ "The `reactor` command is not available."
++      , ""
++      , "On Guix, these files are needed for `elm reactor` to work,"
++      , "but they are missing:"
++      , ""
++      , unlines (map (\pth -> "    " ++ (show pth)) missing)
++      ]
++
++reallyRun :: StaticFiles.Lookup -> Flags -> IO ()
++reallyRun lookup (Flags maybePort) =
+   do  let port = maybe 8000 id maybePort
+       putStrLn $ "Go to http://localhost:" ++ show port ++ " to see your project dashboard."
+       httpServe (config port) $
+         serveFiles
+         <|> serveDirectoryWith directoryConfig "."
+-        <|> serveAssets
++        <|> serveAssets lookup
+         <|> error404
+ 
+ 
+@@ -169,16 +186,15 @@ compile path =
+ -- SERVE STATIC ASSETS
+ 
+ 
+-serveAssets :: Snap ()
+-serveAssets =
++serveAssets :: StaticFiles.Lookup -> Snap ()
++serveAssets lookup =
+   do  path <- getSafePath
+-      case StaticFiles.lookup path of
++      case lookup path of
+         Nothing ->
+           pass
+ 
+-        Just (content, mimeType) ->
+-          do  modifyResponse (setContentType (mimeType <> ";charset=utf-8"))
+-              writeBS content
++        Just (fsPath, mimeType) ->
++          serveFileAs (mimeType <> ";charset=utf-8") fsPath
+ 
+ 
+ 
+diff --git a/terminal/src/Develop/StaticFiles.hs b/terminal/src/Develop/StaticFiles.hs
+index 94ee72dc..3227d617 100644
+--- a/terminal/src/Develop/StaticFiles.hs
++++ b/terminal/src/Develop/StaticFiles.hs
+@@ -2,7 +2,8 @@
+ {-# LANGUAGE OverloadedStrings #-}
+ {-# LANGUAGE TemplateHaskell #-}
+ module Develop.StaticFiles
+-  ( lookup
++  ( prepare
++  , Lookup
+   , cssPath
+   , elmPath
+   , waitingPath
+@@ -11,9 +12,7 @@ module Develop.StaticFiles
+ 
+ import Prelude hiding (lookup)
+ import qualified Data.ByteString as BS
+-import Data.FileEmbed (bsToExp)
+ import qualified Data.HashMap.Strict as HM
+-import Language.Haskell.TH (runIO)
+ import System.FilePath ((</>))
+ 
+ import qualified Develop.StaticFiles.Build as Build
+@@ -26,20 +25,29 @@ import qualified Develop.StaticFiles.Build as Build
+ type MimeType =
+   BS.ByteString
+ 
++type Lookup = FilePath -> Maybe (FilePath, MimeType)
+ 
+-lookup :: FilePath -> Maybe (BS.ByteString, MimeType)
+-lookup path =
++prepare :: IO (Either [FilePath] Lookup)
++prepare = do
++  found <- Build.findReactorFrontEnd expectedFiles
++  return $ case found of
++    Left missing ->
++      Left missing
++    Right resolved ->
++      Right (mkLookup (HM.fromList resolved))
++
++mkLookup :: HM.HashMap FilePath (FilePath, MimeType) -> Lookup
++mkLookup dict path =
+   HM.lookup path dict
+ 
+ 
+-dict :: HM.HashMap FilePath (BS.ByteString, MimeType)
+-dict =
+-  HM.fromList
+-    [ faviconPath  ==> (favicon , "image/x-icon")
+-    , elmPath      ==> (elm     , "application/javascript")
+-    , cssPath      ==> (css     , "text/css")
+-    , codeFontPath ==> (codeFont, "font/ttf")
+-    , sansFontPath ==> (sansFont, "font/ttf")
++expectedFiles :: [(FilePath, MimeType)]
++expectedFiles =
++    [ faviconPath  ==> "image/x-icon"
++    , elmPath      ==> "application/javascript"
++    , cssPath      ==> "text/css"
++    , codeFontPath ==> "font/ttf"
++    , sansFontPath ==> "font/ttf"
+     ]
+ 
+ 
+@@ -82,7 +90,7 @@ sansFontPath =
+   "_elm" </> "source-sans-pro.ttf"
+ 
+ 
+-
++{-
+ -- ELM
+ 
+ 
+@@ -121,3 +129,4 @@ sansFont =
+ favicon :: BS.ByteString
+ favicon =
+   $(bsToExp =<< runIO (Build.readAsset "favicon.ico"))
++-}
+diff --git a/terminal/src/Develop/StaticFiles/Build.hs b/terminal/src/Develop/StaticFiles/Build.hs
+index c61fae57..c39b08b0 100644
+--- a/terminal/src/Develop/StaticFiles/Build.hs
++++ b/terminal/src/Develop/StaticFiles/Build.hs
+@@ -1,28 +1,39 @@
+ {-# LANGUAGE OverloadedStrings #-}
++{-# LANGUAGE CPP #-}
+ module Develop.StaticFiles.Build
+-  ( readAsset
+-  , buildReactorFrontEnd
++  ( findReactorFrontEnd
+   )
+   where
+ 
+-
+-import qualified Data.ByteString as BS
+-import qualified Data.ByteString.Builder as B
+-import qualified Data.ByteString.Lazy as LBS
+-import qualified Data.NonEmptyList as NE
+ import qualified System.Directory as Dir
+-import System.FilePath ((</>))
+-
+-import qualified BackgroundWriter as BW
+-import qualified Build
+-import qualified Elm.Details as Details
+-import qualified Generate
+-import qualified Reporting
+-import qualified Reporting.Exit as Exit
+-import qualified Reporting.Task as Task
+-
+-
+-
++import System.FilePath ((</>), takeDirectory)
++import System.Environment (getExecutablePath)
++import Data.Either as Either
++
++reactorStaticRelRoot :: FilePath
++reactorStaticRelRoot = GUIX_REACTOR_STATIC_REL_ROOT
++
++type Resolved a = (FilePath, (FilePath, a))
++
++findReactorFrontEnd :: [(FilePath, a)] -> IO (Either [FilePath] [Resolved a])
++findReactorFrontEnd specs = do
++  exe <- getExecutablePath
++  let dir = takeDirectory exe </> reactorStaticRelRoot
++  dirExists <- Dir.doesDirectoryExist dir
++  files <- sequence (map (findFile dir) specs)
++  return $ case Either.lefts files of
++           [] ->
++             Right (Either.rights files)
++           missing ->
++             Left $ if dirExists then missing else [dir]
++
++findFile :: FilePath -> (FilePath, a) -> IO (Either FilePath (Resolved a))
++findFile dir (rel, rhs) = do
++  let abs = dir </> rel
++  exists <- Dir.doesFileExist abs
++  return $ if not exists then Left abs else Right (rel, (abs, rhs))
++
++{-
+ -- ASSETS
+ 
+ 
+@@ -71,3 +82,4 @@ runTaskUnsafe task =
+                 \\nCompile with `elm make` directly to figure it out faster\
+                 \\n--------------------------------------------------------\
+                 \\n"
++-}
+-- 
+2.32.0
+
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:31 +0200
[PATCH 02/30] gnu: elm: Rename package to match the command.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-2-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-compiler): Rename to ...
(elm): ... this variable.
[description]: Tweak.
---
gnu/packages/elm.scm | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)

Toggle diff (36 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 708c1cf0d2..be2e4ebcbd 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -33,13 +33,13 @@ (define-module (gnu packages elm)
 ;; The `elm` build usually calls out to itself via Template Haskell to compile
 ;; the `elm reactor` web app (which depends on additional Elm packages) and
 ;; embeds the static files into itself.  The reactor isn't required to compile
-;; elm applications, so we want to skip it from the bootstrap package, but we
+;; Elm applications, so we want to skip it from the bootstrap package, but we
 ;; also want to be able to enable it once we can build it.  We patch Elm to
-;; instead look for the files on disk relative to the executable and exit with
-;; a useful error message if they aren't there.
-(define-public elm-compiler
+;; instead look for the files on disk relative to the executable and to have
+;; `elm reactor` exit with a useful error message if they aren't there.
+(define-public elm
   (package
-    (name "elm-compiler")
+    (name "elm")
     (version "0.19.1")
     (source
      (origin
@@ -91,7 +91,7 @@ (define-public elm-compiler
     (home-page "https://elm-lang.org")
     (synopsis "Programming language for Web applications")
     (description
-     "This package provides Elm, a statically-typed functional programming
-language for the browser.  It includes commands for developers such as
-@command{elm make} and @command{elm repl}.")
+     "Elm is a statically-typed, purely-functional programming language for
+the browser.  The @command{elm} exectable includes commands for developers
+such as @command{elm make} and @command{elm repl}.")
     (license license:bsd-3)))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:31 +0200
[PATCH 03/30] guix: Add elm-build-system and 'guix import elm'.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-3-philip@philipmcgrath.com
* gnu/packages/patches/elm-offline-package-registry.scm: New file.
* gnu/local.mk (dist_patch_DATA): Add it.
* gnu/packages/elm.scm (elm): Use it.
* guix/build-system/elm.scm, guix/build/elm-build-system.scm,
guix/import/elm.scm, guix/scripts/import/elm.scm: New files.
* guix/scripts/import.scm (importers): Add "elm".
---
gnu/local.mk | 1 +
gnu/packages/elm.scm | 8 +-
.../elm-offline-package-registry.patch | 71 ++++
guix/build-system/elm.scm | 144 +++++++
guix/build/elm-build-system.scm | 380 ++++++++++++++++++
guix/import/elm.scm | 148 +++++++
guix/scripts/import.scm | 3 +-
guix/scripts/import/elm.scm | 107 +++++
8 files changed, 859 insertions(+), 3 deletions(-)
create mode 100644 gnu/packages/patches/elm-offline-package-registry.patch
create mode 100644 guix/build-system/elm.scm
create mode 100644 guix/build/elm-build-system.scm
create mode 100644 guix/import/elm.scm
create mode 100644 guix/scripts/import/elm.scm

Toggle diff (953 lines)
diff --git a/gnu/local.mk b/gnu/local.mk
index 2af4d018ba..6f02f0a6fd 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1024,6 +1024,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/einstein-build.patch			\
   %D%/packages/patches/elfutils-tests-ptrace.patch		\
   %D%/packages/patches/elixir-path-length.patch			\
+  %D%/packages/patches/elm-offline-package-registry.patch	\
   %D%/packages/patches/elm-reactor-static-files.patch		\
   %D%/packages/patches/elogind-revert-polkit-detection.patch	\
   %D%/packages/patches/emacs-exec-path.patch			\
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index be2e4ebcbd..22c1db5942 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -25,6 +25,7 @@ (define-module (gnu packages elm)
   #:use-module (gnu packages haskell-xyz)
   #:use-module (gnu packages haskell-web)
   #:use-module (guix build-system haskell)
+  #:use-module (guix build-system elm)
   #:use-module (guix gexp)
   #:use-module (guix git-download)
   #:use-module ((guix licenses) #:prefix license:)
@@ -37,6 +38,8 @@ (define-module (gnu packages elm)
 ;; also want to be able to enable it once we can build it.  We patch Elm to
 ;; instead look for the files on disk relative to the executable and to have
 ;; `elm reactor` exit with a useful error message if they aren't there.
+(define %reactor-root-base
+  "share/elm/reactor-")
 (define-public elm
   (package
     (name "elm")
@@ -51,13 +54,14 @@ (define-public elm
        (sha256
         (base32 "1rdg3xp3js9xadclk3cdypkscm5wahgsfmm4ldcw3xswzhw6ri8w"))
        (patches
-        (search-patches "elm-reactor-static-files.patch"))))
+        (search-patches "elm-reactor-static-files.patch"
+                        "elm-offline-package-registry.patch"))))
     (build-system haskell-build-system)
     (arguments
      (list
       #:configure-flags
       #~(list (string-append "--ghc-option=-DGUIX_REACTOR_STATIC_REL_ROOT="
-                             "\"../share/elm/reactor-"
+                             "\"../" #$%reactor-root-base
                              #$(package-version this-package)
                              "\""))
       #:phases
diff --git a/gnu/packages/patches/elm-offline-package-registry.patch b/gnu/packages/patches/elm-offline-package-registry.patch
new file mode 100644
index 0000000000..761ec69878
--- /dev/null
+++ b/gnu/packages/patches/elm-offline-package-registry.patch
@@ -0,0 +1,71 @@
+From 06563409e6f2b1cca7bc1b27e31efd07a7569da8 Mon Sep 17 00:00:00 2001
+From: Philip McGrath <philip@philipmcgrath.com>
+Date: Thu, 14 Apr 2022 22:41:04 -0400
+Subject: [PATCH] minimal support for offline builds
+
+Normally, Elm performs HTTP requests before building to obtain or
+update its list of all registed packages and their versions.
+This is problematic in the Guix build environment.
+
+This patch causes Elm to check if the `GUIX_ELM_OFFLINE_REGISTRY_FILE`
+is set and, if so, to use the contents of the file it specifies as
+though it were the response from
+https://package.elm-lang.org/all-packages.
+
+This patch does not attempt to add more general support for offline
+builds. In particular, it does not attempt to support incremental
+updates to the package registry cache file. See also discussion at
+https://discourse.elm-lang.org/t/private-package-tool-spec/6779/25.
+---
+ builder/src/Deps/Registry.hs | 25 +++++++++++++++++++++----
+ 1 file changed, 21 insertions(+), 4 deletions(-)
+
+diff --git a/builder/src/Deps/Registry.hs b/builder/src/Deps/Registry.hs
+index 8d7def98..70cf3622 100644
+--- a/builder/src/Deps/Registry.hs
++++ b/builder/src/Deps/Registry.hs
+@@ -18,6 +18,8 @@ import Control.Monad (liftM2)
+ import Data.Binary (Binary, get, put)
+ import qualified Data.List as List
+ import qualified Data.Map.Strict as Map
++import System.Environment as Env
++import qualified Data.ByteString as BS
+ 
+ import qualified Deps.Website as Website
+ import qualified Elm.Package as Pkg
+@@ -190,13 +192,28 @@ getVersions' name (Registry _ versions) =
+ post :: Http.Manager -> String -> D.Decoder x a -> (a -> IO b) -> IO (Either Exit.RegistryProblem b)
+ post manager path decoder callback =
+   let
+-    url = Website.route path []
+-  in
+-  Http.post manager url [] Exit.RP_Http $
+-    \body ->
++    mkBodyCallback url body =
+       case D.fromByteString decoder body of
+         Right a -> Right <$> callback a
+         Left _ -> return $ Left $ Exit.RP_Data url body
++    postOnline url cb =
++      Http.post manager url [] Exit.RP_Http cb
++    performPost f url =
++      f url (mkBodyCallback url)
++  in
++    do
++      maybeFile <- Env.lookupEnv "GUIX_ELM_OFFLINE_REGISTRY_FILE"
++      case (path, maybeFile) of
++        ( "/all-packages", Just file ) ->
++          performPost postOffline file
++        ( _, _ ) ->
++          -- don't know how to handle other endpoints yet
++          performPost postOnline (Website.route path [])
++
++postOffline :: String -> (BS.ByteString -> IO a) -> IO a
++postOffline file callback = do
++  body <- BS.readFile file
++  callback body
+ 
+ 
+ 
+-- 
+2.32.0
+
diff --git a/guix/build-system/elm.scm b/guix/build-system/elm.scm
new file mode 100644
index 0000000000..bf77df6519
--- /dev/null
+++ b/guix/build-system/elm.scm
@@ -0,0 +1,144 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 Philip McGrath <philip@philipmcgrath.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix build-system elm)
+  #:use-module (guix store)
+  #:use-module (guix utils)
+  #:use-module (guix packages)
+  #:use-module (guix gexp)
+  #:use-module (guix monads)
+  #:use-module (guix search-paths)
+  #:use-module (guix git-download)
+  #:use-module (guix build-system)
+  #:use-module (guix build-system gnu)
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-1)
+  #:export (elm->package-name
+            elm-package-origin
+            %elm-build-system-modules
+            %elm-default-modules
+            elm-build
+            elm-build-system))
+
+(define (elm->package-name name)
+  "Given the NAME of an Elm package, return a Guix-style package name."
+  (let ((converted
+         (string-join (string-split (string-downcase name) #\/) "-")))
+    (if (string-prefix? "elm-" converted)
+        converted
+        (string-append "elm-" converted))))
+
+(define (elm-package-origin name version hash)
+  "Return an origin for the Elm package NAME at the given VERSION with sha256
+checksum HASH."
+  ;; elm requires this very specific repository structure and tagging regime
+  (origin
+    (method git-fetch)
+    (uri (git-reference
+          (url (string-append "https://github.com/" name))
+          (commit version)))
+    (file-name (git-file-name (elm->package-name name) version))
+    (sha256 hash)))
+
+(define %elm-build-system-modules
+  ;; Build-side modules imported by default.
+  `((guix build elm-build-system)
+    (guix build json)
+    (guix build union)
+    ,@%gnu-build-system-modules))
+
+(define %elm-default-modules
+  ;; Modules in scope in the build-side environment.
+  '((guix build elm-build-system)
+    (guix build utils)
+    (guix build json)
+    (guix build union)))
+
+(define (default-elm)
+  "Return the default Elm package for builds."
+  ;; Lazily resolve the binding to avoid a circular dependency.
+  (let ((elm (resolve-interface '(gnu packages elm))))
+    (module-ref elm 'elm)))
+
+(define* (lower name
+                #:key source inputs native-inputs outputs system target
+                (implicit-elm-package-inputs? #t)
+                (elm (default-elm))
+                #:allow-other-keys
+                #:rest arguments)
+  "Return a bag for NAME."
+  (define private-keywords
+    '(#:target #:implicit-elm-package-inputs? #:elm #:inputs #:native-inputs))
+  (cond
+   (target
+    ;; Cross-compilation is not yet supported.  It should be easy, though,
+    ;; since the build products are all platform-independent.
+    #f)
+   (else
+    (bag
+      (name name)
+      (system system)
+      (host-inputs
+       `(,@(if source
+               `(("source" ,source))
+               '())
+         ,@inputs
+         ("elm" ,elm)
+         ;; TODO: probably don't need most of (standard-packages)
+         ,@(standard-packages)))
+      (outputs outputs)
+      (build elm-build)
+      (arguments (strip-keyword-arguments private-keywords arguments))))))
+
+(define* (elm-build name inputs
+                    #:key
+                    source
+                    (tests? #t)
+                    (phases '%standard-phases)
+                    (outputs '("out"))
+                    (search-paths '())
+                    (system (%current-system))
+                    (guile #f)
+                    (imported-modules %elm-build-system-modules)
+                    (modules %elm-default-modules))
+  "Build SOURCE using ELM."
+  (define builder
+    (with-imported-modules imported-modules
+      #~(begin
+          (use-modules #$@(sexp->gexp modules))
+          (elm-build #:name #$name
+                     #:source #+source
+                     #:system #$system
+                     #:tests? #$tests?
+                     #:phases #$phases
+                     #:outputs #$(outputs->gexp outputs)
+                     #:search-paths '#$(sexp->gexp
+                                        (map search-path-specification->sexp
+                                             search-paths))
+                     #:inputs #$(input-tuples->gexp inputs)))))
+  (mlet %store-monad ((guile (package->derivation (or guile (default-guile))
+                                                  system #:graft? #f)))
+    (gexp->derivation name builder
+                      #:system system
+                      #:guile-for-build guile)))
+
+(define elm-build-system
+  (build-system
+    (name 'elm)
+    (description "The Elm build system")
+    (lower lower)))
diff --git a/guix/build/elm-build-system.scm b/guix/build/elm-build-system.scm
new file mode 100644
index 0000000000..b47b16973d
--- /dev/null
+++ b/guix/build/elm-build-system.scm
@@ -0,0 +1,380 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 Philip McGrath <philip@philipmcgrath.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix build elm-build-system)
+  #:use-module ((guix build gnu-build-system) #:prefix gnu:)
+  #:use-module (guix build utils)
+  #:use-module (guix build json)
+  #:use-module (guix build union)
+  #:use-module (ice-9 ftw)
+  #:use-module (ice-9 rdelim)
+  #:use-module (ice-9 regex)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 popen)
+  #:use-module (ice-9 vlist)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-26)
+  #:use-module (srfi srfi-71)
+  #:export (%standard-phases
+            patch-application-dependencies
+            patch-json-string-escapes
+            read-offline-registry->vhash
+            elm-build))
+
+;; COMMENTARY:
+;;
+;; Elm draws a sharp distinction between "projects" with `{"type":"package"}`
+;; vs. `{"type":"application"}` in the "elm.json" file: see
+;; <https://github.com/elm/compiler/blob/master/docs/elm.json/package.md> and
+;; <https://github.com/elm/compiler/blob/master/docs/elm.json/application.md>.
+;; For now, `elm-build-system` is designed for "package"s: packaging
+;; "application"s requires ad-hoc replacements for some phases---but see
+;; `patch-application-dependencies`, which helps to work around a known issue
+;; discussed below.  It would be nice to add more streamlined support for
+;; "application"s one we have more experience building them in Guix.  For
+;; example, we could incorporate the `uglifyjs` advice from
+;; <https://github.com/elm/compiler/blob/master/hints/optimize.md>.
+;;
+;; We want building an Elm "package" to produce:
+;;
+;;   - a "docs.json" file with extracted documentation; and
+;;
+;;   - an "artifacts.dat" file with compilation results for use in building
+;;     "package"s and "application"s.
+;;
+;; Unfortunately, there isn't an entry point to the Elm compiler that builds
+;; those files directly.  Building with `elm make` does something different,
+;; more oriented toward development, testing, and building "application"s.  We
+;; work around this limitation by staging the "package" we're building as
+;; though it were already installed in ELM_HOME, generating a trivial Elm
+;; "application" that depends on the "package", and building the
+;; "application", which causes the files for the "package" to be built.
+;;
+;; Much of the ceremony involved is to avoid using `elm` in ways that would
+;; make it try to do network IO beyond the bare minimum functionality for
+;; which we've patched a replacement into our `elm`.  On the other hand, we
+;; get to take advantage of the very regular structure required of Elm
+;; packages.
+;;
+;; *Known issue:* Elm itself supports multiple versions of "package"s
+;; coexisting simultaneously under ELM_HOME, but we do not support this yet.
+;; Sometimes, parallel versions coexisting causes `elm` to try to write to
+;; built "artifacts.dat" files.  For now, two workarounds are possible:
+;;
+;;  - Use `patch-application-dependencies` to rewrite an "application"'s
+;;    "elm.json" file to refer to the versions of its inputs actually
+;;    packaged in Guix.
+;;
+;;  - Use a Guix package transformation to rewrite your "application"'s
+;;    dependencies recursively, so that only one version of each Elm
+;;    "package" is included in your "application"'s build environment.
+;;
+;; Patching `elm` more extensively---perhaps adding an `elm guix`
+;; subcommand`---might let us address these issues more directly.
+;;
+;; CODE:
+;;
+
+(define %essential-elm-packages
+  ;; elm/json isn't essential in a fundamental sense,
+  ;; but it's required for a {"type":"application"},
+  ;; which we are generating to trigger the build
+  '("elm/core" "elm/json"))
+
+(define* (target-elm-version #:optional elm)
+  "Return the version of ELM or whichever 'elm' is in $PATH.
+Return #false if it cannot be determined."
+  (let* ((pipe (open-pipe* OPEN_READ
+                           (or elm "elm")
+                           "--version"))
+         (line (read-line pipe)))
+    (and (zero? (close-pipe pipe))
+         (string? line)
+         line)))
+
+(define* (prepare-elm-home #:key native-inputs inputs #:allow-other-keys)
+  "Set the ELM_HOME environment variable and populate the indicated directory
+with the union of the Elm \"package\" inputs.  Also, set GUIX_ELM_VERSION to
+the version of the Elm compiler in use."
+  (let* ((elm (search-input-file (or native-inputs inputs) "/bin/elm"))
+         (elm-version (target-elm-version elm)))
+    (setenv "GUIX_ELM_VERSION" elm-version)
+    (mkdir "../elm-home")
+    (with-directory-excursion "../elm-home"
+      (union-build elm-version
+                   (search-path-as-list
+                    (list (string-append "share/elm/" elm-version))
+                    (map cdr inputs))
+                   #:create-all-directories? #t)
+      (setenv "ELM_HOME" (getcwd)))))
+
+(define* (stage #:key native-inputs inputs  #:allow-other-keys)
+  "Extract the installable files from the Elm \"package\" into a staging
+directory and link it into the ELM_HOME tree.  Also, set GUIX_ELM_PKG_NAME and
+GUIX_ELM_PKG_VERSION to the name and version, respectively, of the Elm package
+being built, as defined in its \"elm.json\" file."
+  (let* ((elm-version (getenv "GUIX_ELM_VERSION"))
+         (elm-home (getenv "ELM_HOME"))
+         (info (match (call-with-input-file "elm.json" read-json)
+                 (('@ . alist) alist)))
+         (name (assoc-ref info "name"))
+         (version (assoc-ref info "version"))
+         (rel-dir (string-append elm-version "/packages/" name "/" version))
+         (staged-dir (string-append elm-home "/../staged/" rel-dir)))
+    (setenv "GUIX_ELM_PKG_NAME" name)
+    (setenv "GUIX_ELM_PKG_VERSION" version)
+    (mkdir-p staged-dir)
+    (mkdir-p (string-append elm-home "/" (dirname rel-dir)))
+    (symlink staged-dir
+             (string-append elm-home "/" rel-dir))
+    (copy-recursively "src" (string-append staged-dir "/src"))
+    (install-file "elm.json" staged-dir)
+    (install-file "README.md" staged-dir)
+    (when (file-exists? "LICENSE")
+      (install-file "LICENSE" staged-dir))))
+
+(define (patch-json-string-escapes file)
+  "Work around a bug in the Elm compiler's JSON parser by attempting to
+replace REVERSE-SOLIDUS--SOLIDUS escape sequences in FILE with unescaped
+SOLIDUS characters."
+  ;; https://github.com/elm/compiler/issues/2255
+  (substitute* file
+    (("\\\\/")
+     "/")))
+
+(define (directory-list dir)
+  "Like DIRECTORY-LIST from 'racket/base': lists the contents of DIR, not
+including the special \".\" and \"..\" entries."
+  (scandir dir (lambda (f)
+                 (not (member f '("." ".."))))))
+
+(define* (make-offline-registry-file #:key inputs #:allow-other-keys)
+  "Generate an \"offline-package-registry.json\" file and sets
+GUIX_ELM_OFFLINE_REGISTRY_FILE to its path, cooperating with a patch to `elm`
+to avoid attempting to download a list of all published Elm package names and
+versions from the internet."
+  (let* ((elm-home (getenv "ELM_HOME"))
+         (elm-version (getenv "GUIX_ELM_VERSION"))
+         (registry-file
+          (string-append elm-home "/../offline-package-registry.json"))
+         (registry-alist
+          ;; here, we don't need to look up entries, so we build the
+          ;; alist directly, rather than using a vhash
+          (with-directory-excursion
+              (string-append elm-home "/" elm-version "/packages")
+            (append-map (lambda (org)
+                          (with-directory-excursion org
+                            (map (lambda (repo)
+                                   (cons (string-append org "/" repo)
+                                         (directory-list repo)))
+                                 (directory-list "."))))
+                        (directory-list ".")))))
+    (call-with-output-file registry-file
+      (lambda (out)
+        (write-json `(@ ,@registry-alist) out)))
+    (patch-json-string-escapes registry-file)
+    (setenv "GUIX_ELM_OFFLINE_REGISTRY_FILE" registry-file)))
+
+(define (read-offline-registry->vhash)
+  "Return a vhash mapping Elm \"package\" names to lists of available version
+strings."
+  (alist->vhash
+   (match (call-with-input-file (getenv "GUIX_ELM_OFFLINE_REGISTRY_FILE")
+            read-json)
+     (('@ . alist) alist))))
+
+(define (find-indirect-dependencies registry-vhash root-pkg root-version)
+  "Return the recursive dependencies of ROOT-PKG, an Elm \"package\" name, at
+version ROOT-VERSION as an alist mapping Elm \"package\" names to (single)
+versions.  The resulting alist will not include entries for
+%ESSENTIAL-ELM-PACKAGES or for ROOT-PKG itself.  The REGISTRY-VHASH is used in
+conjunction with the ELM_HOME environment variable to find dependencies."
+  (with-directory-excursion
+      (string-append (getenv "ELM_HOME")
+                     "/" (getenv "GUIX_ELM_VERSION")
+                     "/packages")
+    (define (get-dependencies pkg version acc)
+      (let* ((elm-json-alist
+              (match (call-with-input-file
+                         (string-append pkg "/" version "/elm.json")
+                       read-json)
+                (('@ . alist) alist)))
+             (deps-alist
+              (match (assoc-ref elm-json-alist "dependencies")
+                (('@ . alist) alist)))
+             (deps-names
+              (filter-map (match-lambda
+                            ((name . range)
+                             (and (not (member name %essential-elm-packages))
+                                  name)))
+                          deps-alist)))
+        (fold register-dependency acc deps-names)))
+    (define (register-dependency pkg acc)
+      ;; Using vhash-cons unconditionally would add duplicate entries,
+      ;; which would then cause problems when we must emit JSON.
+      ;; Plus, we can avoid needlessly duplicating work.
+      (if (vhash-assoc pkg acc)
+          acc
+          (match (vhash-assoc pkg registry-vhash)
+            ((_ version . _)
+             ;; in the rare case that multiple versions are present,
+             ;; just picking an arbitrary one seems to work well enough for now
+             (get-dependencies pkg version (vhash-cons pkg version acc))))))
+    (vlist->list
+     (get-dependencies root-pkg root-version vlist-null))))
+
+(define* (patch-application-dependencies #:key inputs #:allow-other-keys)
+  "Rewrites the \"elm.json\" file in the working directory---which must be of
+`\"type\":\"application\"`, not `\"type\":\"package\"`---to refer to the
+dependency versions actually provided via Guix.  The
+GUIX_ELM_OFFLINE_REGISTRY_FILE environment variable is used to find available
+versions."
+  (let* ((registry-vhash (read-offline-registry->vhash))
+         (rewrite-dep-version
+          (match-lambda
+            ((name . _)
+             (cons name (match (vhash-assoc name registry-vhash)
+                          ((_ version) ;; no dot
+                           version))))))
+         (rewrite-direct/indirect
+          (match-lambda
+            ;; a little checking to avoid confusing misuse with "package"
+            ;; project dependencies, which have a different shape
+            (((and key (or "direct" "indirect"))
+              '@ . alist)
+             `(,key @ ,@(map rewrite-dep-version alist)))))
+         (rewrite-json-section
+          (match-lambda
+            (((and key (or "dependencies" "test-dependencies"))
+              '@ . alist)
+             `(,key @ ,@(map rewrite-direct/indirect alist)))
+            ((k . v)
+             (cons k v))))
+         (rewrite-elm-json
+          (match-lambda
+            (('@ . alist)
+             `(@ ,@(map rewrite-json-section alist))))))
+    (with-atomic-file-replacement "elm.json"
+      (lambda (in out)
+        (write-json (rewrite-elm-json (read-json in))
+                    out)))
+    (patch-json-string-escapes "elm.json")))
+
+(define* (configure #:key native-inputs inputs #:allow-other-keys)
+  "Generate a trivial Elm \"application\" with a direct dependency on the Elm
+\"package\" currently being built."
+  (let* ((info (match (call-with-input-file "elm.json" read-json)
+                 (('@ . alist) alist)))
+         (name (getenv "GUIX_ELM_PKG_NAME"))
+         (version (getenv "GUIX_ELM_PKG_VERSION"))
+         (elm-home (getenv "ELM_HOME"))
+         (registry-vhash (read-offline-registry->vhash))
+         (app-dir (string-append elm-home "/../fake-app")))
+    (mkdir-p (string-append app-dir "/src"))
+    (with-directory-excursion app-dir
+      (call-with-output-file "elm.json"
+        (lambda (out)
+          (write-json
+           `(@ ("type" . "application")
+               ("source-directories" "src") ;; intentionally no dot
+               ("elm-version" . ,(getenv "GUIX_ELM_VERSION"))
+               ("dependencies"
+                @ ("direct"
+                   @ ,@(map (lambda (pkg)
+                              (match (vhash-assoc pkg registry-vhash)
+                                ((_ pkg-version . _)
+                                 (cons pkg
+                                       (if (equal? pkg name)
+                                           version
+                                           pkg-version)))))
+                            (if (member name %essential-elm-packages)
+                                %essential-elm-packages
+                                (cons name %essential-elm-packages))))
+                  ("indirect"
+                   @ ,@(if (member name %essential-elm-packages)
+                           '()
+                           (find-indirect-dependencies registry-vhash
+                                                       name
+                                                       version))))
+               ("test-dependencies"
+                @ ("direct" @)
+                  ("indirect" @)))
+           out)))
+      (patch-json-string-escapes  "elm.json")
+      (with-output-to-file "src/Main.elm"
+        ;; the most trivial possible elm program
+        (lambda ()
+          (display "module Main exposing (..)
+main : Program () () ()
+main = Platform.worker
+ { init = \\_ -> ( (), Cmd.none )
+ , update = \\_ -> \\_ -> ( (), Cmd.none )
+ , subscriptions = \\_ -> Sub.none }"))))))
+
+(define* (build #:key native-inputs inputs #:allow-other-keys)
+  "Run `elm make` to build the Elm \"application\" generated by CONFIGURE."
+  (with-directory-excursion (string-append (getenv "ELM_HOME") "/../fake-app")
+    (invoke (search-input-file (or native-inputs inputs) "/bin/elm")
+            "make"
+            "src/Main.elm")))
+
+(define* (check #:key tests? #:allow-other-keys)
+  "Does nothing, because the `elm-test` executable has not yet been packaged
+for Guix."
+  (when tests?
+    (display "elm-test has not yet been packaged for Guix\n")))
+
+(define* (install #:key outputs #:allow-other-keys)
+  "Installs the contents of the directory generated by STAGE, including any
+files added by BUILD, to the Guix package output."
+  (copy-recursively
+   (string-append (getenv "ELM_HOME") "/../staged")
+   (string-append (assoc-ref outputs "out") "/share/elm")))
+
+(define* (validate-compiled #:key outputs #:allow-other-keys)
+  "Checks that the files \"artifacts.dat\" and \"docs.json\" have been
+installed."
+  (let ((base (string-append "/share/elm/"
+                             (getenv "GUIX_ELM_VERSION")
+                             "/packages/"
+                             (getenv "GUIX_ELM_PKG_NAME")
+                             "/"
+                             (getenv "GUIX_ELM_PKG_VERSION")))
+        (expected '("artifacts.dat" "docs.json")))
+    (for-each (lambda (name)
+                (search-input-file outputs (string-append base "/" name)))
+              expected)))
+
+(define %standard-phases
+  (modify-phases gnu:%standard-phases
+    (add-after 'unpack 'prepare-elm-home prepare-elm-home)
+    (delete 'bootstrap)
+    (add-after 'patch-source-shebangs 'stage stage)
+    (add-after 'stage 'make-offline-registry-file make-offline-registry-file)
+    (replace 'configure configure)
+    (delete 'patch-generated-file-shebangs)
+    (replace 'build build)
+    (replace 'check check)
+    (replace 'install install)
+    (add-before 'validate-documentation-location 'validate-compiled
+      validate-compiled)))
+
+(define* (elm-build #:key inputs (phases %standard-phases)
+                    #:allow-other-keys #:rest args)
+  "Builds the given Elm project, applying all of the PHASES in order."
+  (apply gnu:gnu-build #:inputs inputs #:phases phases args))
diff --git a/guix/import/elm.scm b/guix/import/elm.scm
new file mode 100644
index 0000000000..ef0a31207c
--- /dev/null
+++ b/guix/import/elm.scm
@@ -0,0 +1,148 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 Philip McGrath <philip@philipmcgrath.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix import elm)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 regex)
+  #:use-module (ice-9 vlist)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-11)
+  #:use-module (srfi srfi-26)
+  #:use-module (srfi srfi-34)
+  #:use-module (srfi srfi-35)
+  #:use-module (guix utils)
+  #:use-module (guix base32)
+  #:use-module (guix hash)
+  #:use-module (guix memoization)
+  #:use-module (guix diagnostics)
+  #:use-module (guix i18n)
+  #:use-module ((guix ui) #:select (display-hint))
+  #:use-module ((guix build utils)
+                #:select ((package-name->name+version
+                           . hyphen-package-name->name+version)
+                          find-files
+                          invoke))
+  #:use-module (guix import utils)
+  #:use-module (guix git)
+  #:use-module (guix import json)
+  #:autoload   (gcrypt hash) (hash-algorithm sha256)
+  #:use-module (json)
+  #:use-module (guix packages)
+  #:use-module (guix upstream)
+  #:use-module ((guix licenses) #:prefix license:)
+  #:use-module (guix build-system elm)
+  #:export (elm-recursive-import
+            elm->guix-package))
+
+(define elm-package-registry
+  ;; It is much nicer to fetch this small (< 40 KB gzipped)
+  ;; file once than to do many HTTP requests.
+  (mlambda ()
+    "Fetch the Elm package registry, represented as a vhash mapping package
+names to lists of available versions, sorted from latest to oldest."
+    (let ((url "https://package.elm-lang.org/all-packages"))
+      (cond
+       ((json-fetch url)
+        => (lambda (alist)
+             (fold (lambda (entry vh)
+                     (match entry
+                       ((name . vec)
+                        (vhash-cons name
+                                    (sort (vector->list vec) version>?)
+                                    vh))))
+                   vlist-null
+                   alist)))
+       (else
+        (raise (formatted-message
+                (G_ "error downloading Elm package registry from ~a")
+                url)))))))
+
+(define (make-elm-package-sexp name version)
+  "Return two values: the `package' s-expression for the Elm package with the
+given NAME and VERSION, and a list of Elm packages it depends on."
+  (define-values (checkout _commit _relation)
+    ;; Elm requires that packages use this very specific format
+    (update-cached-checkout (string-append "https://github.com/" name)
+                            #:ref `(tag . ,version)))
+  (define info
+    (call-with-input-file (string-append checkout "/elm.json")
+      json->scm))
+  (define (get-deps key)
+    (cond
+     ((assoc-ref info key)
+      => (cut map car <>))
+     (else
+      '())))
+  (define dependencies
+    (get-deps "dependencies"))
+  (define test-dependencies
+    (get-deps "test-dependencies"))
+  (values
+   `(package
+      (name ,(elm->package-name name))
+      (version ,version)
+      (source (elm-package-origin
+               ,name
+               version ;; no ,
+               (base32
+                ,(bytevector->nix-base32-string
+                  (file-hash* checkout
+                              #:algorithm (hash-algorithm sha256)
+                              #:recursive? #t)))))
+      (build-system elm-build-system)
+      ,@(maybe-propagated-inputs (map elm->package-name dependencies))
+      ,@(maybe-inputs (map elm->package-name test-dependencies))
+      (home-page ,(string-append "https://package.elm-lang.org/packages/"
+                                 name "/" version))
+      (synopsis ,(assoc-ref info "summary"))
+      (description
+       ;; Try to use the first paragraph of README.md (which Elm requires),
+       ;; or fall back to summary otherwise.
+       ,(beautify-description
+         (match (chunk-lines (call-with-input-file
+                                 (string-append checkout "/README.md")
+                               read-lines))
+           ((_ par . _)
+            (string-join par " "))
+           (_
+            (assoc-ref info "summary")))))
+      (license ,(spdx-string->license (assoc-ref info "license")))
+      ;; so we know where the "/" goes
+      (properties '((upstream-name . ,name))))
+   (append dependencies test-dependencies)))
+
+(define elm->guix-package
+  (memoize
+   (lambda* (package-name #:key repo version)
+     "Fetch the metadata for PACKAGE-NAME, an Elm package registered at
+package.elm.org, and return two values: the `package' s-expression
+corresponding to that package (or #f on failure) and a list of Elm
+dependencies.."
+     (cond
+      ((vhash-assoc package-name (elm-package-registry))
+       => (match-lambda
+            ((_found latest . _versions)
+             (make-elm-package-sexp package-name (or version latest)))))
+      (else
+       (values #f '()))))))
+
+(define* (elm-recursive-import package-name #:optional version)
+  (recursive-import package-name
+                    #:version version
+                    #:repo->guix-package elm->guix-package
+                    #:guix-name elm->package-name))
diff --git a/guix/scripts/import.scm b/guix/scripts/import.scm
index 40fa6759ae..fa79f3211e 100644
--- a/guix/scripts/import.scm
+++ b/guix/scripts/import.scm
@@ -5,6 +5,7 @@
 ;;; Copyright © 2019 Ricardo Wurmus <rekado@elephly.net>
 ;;; Copyright © 2021 Simon Tournier <zimon.toutoune@gmail.com>
 ;;; Copyright © 2021 Xinglu Chen <public@yoctocell.xyz>
+;;; Copyright © 2022 Philip McGrath <philip@philipmcgrath.com>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -80,7 +81,7 @@ (define %standard-import-options '())
 
 (define importers '("gnu" "pypi" "cpan" "hackage" "stackage" "egg" "elpa"
                     "gem" "go" "cran" "crate" "texlive" "json" "opam"
-                    "minetest"))
+                    "minetest" "elm"))
 
 (define (resolve-importer name)
   (let ((module (resolve-interface
diff --git a/guix/scripts/import/elm.scm b/guix/scripts/import/elm.scm
new file mode 100644
index 0000000000..68dcbf1070
--- /dev/null
+++ b/guix/scripts/import/elm.scm
@@ -0,0 +1,107 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 Philip McGrath <philip@philipmcgrath.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix scripts import elm)
+  #:use-module (guix ui)
+  #:use-module (guix utils)
+  #:use-module (guix scripts)
+  #:use-module (guix import elm)
+  #:use-module (guix scripts import)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-11)
+  #:use-module (srfi srfi-37)
+  #:use-module (srfi srfi-71)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 format)
+  #:export (guix-import-elm))
+
+
+;;;
+;;; Command-line options.
+;;;
+
+(define %default-options
+  '())
+
+(define (show-help)
+  (display (G_ "Usage: guix import elm PACKAGE-NAME
+
+Import and convert the Elm package PACKAGE-NAME.  Optionally, a version
+can be specified after the arobas (@) character.\n"))
+  (display (G_ "
+  -h, --help             display this help and exit"))
+  (display (G_ "
+  -r, --recursive        import packages recursively"))
+  (display (G_ "
+  -V, --version          display version information and exit"))
+  (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 elm")))
+         (option '(#\r "recursive") #f #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'recursive #t result)))
+         %standard-import-options))
+
+
+;;;
+;;; Entry point.
+;;;
+
+(define (guix-import-elm . 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))
+         (args (filter-map (match-lambda
+                             (('argument . value)
+                              value)
+                             (_ #f))
+                           (reverse opts))))
+    (match args
+      ((spec)
+       (with-error-handling
+         (let ((name version (package-name->name+version spec)))
+           (if (assoc-ref opts 'recursive)
+               ;; Recursive import
+               (map (match-lambda
+                      ((and ('package ('name name) . rest) pkg)
+                       `(define-public ,(string->symbol name)
+                          ,pkg))
+                      (_ #f))
+                    (elm-recursive-import name version))
+               ;; Single import
+               (let ((sexp (elm->guix-package name #:version version)))
+                 (unless sexp
+                   (leave (G_ "failed to download meta-data for package '~a'~%")
+                          name))
+                 sexp)))))
+      (()
+       (leave (G_ "too few arguments~%")))
+      ((many ...)
+       (leave (G_ "too many arguments~%"))))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:31 +0200
[PATCH 04/30] gnu: Add elm-core and elm-json.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-4-philip@philipmcgrath.com
Both of these packages are needed for 'elm-build-system' to work.

* gnu/packages/elm.scm (elm-core, elm-json, elm-json-bootstrap): New
variables.
---
gnu/packages/elm.scm | 56 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)

Toggle diff (66 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 22c1db5942..b61d238cff 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -99,3 +99,59 @@ (define-public elm
 the browser.  The @command{elm} exectable includes commands for developers
 such as @command{elm make} and @command{elm repl}.")
     (license license:bsd-3)))
+
+(define-public elm-core
+  (package
+    (name "elm-core")
+    (version "1.0.5")
+    (source
+     (elm-package-origin
+      "elm/core"
+      version
+      (base32 "0g3xbi8f9k5q45s95nx3jfvzwdf4b2n63a52wr4027d2xjx0pmvl")))
+    (build-system elm-build-system)
+    (inputs (list elm-json-bootstrap))
+    (arguments
+     (list #:implicit-elm-package-inputs? #f))
+    (home-page "https://package.elm-lang.org/packages/elm/core/1.0.5")
+    (synopsis "Elm's standard libraries")
+    (description "Every Elm project needs this package!")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm/core")))))
+
+(define-public elm-json
+  (package
+    (name "elm-json")
+    (version "1.1.3")
+    (source
+     (elm-package-origin
+      "elm/json"
+      version
+      (base32 "1hx986yqw1v2bpkrh6brszl8n8awwg1s8zi7v5qg0p1rqwvjlicz")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-core))
+    (arguments
+     (list #:implicit-elm-package-inputs? #f))
+    (home-page "https://package.elm-lang.org/packages/elm/json/1.1.3")
+    (synopsis "Encode and decode JSON values in Elm")
+    (description
+     "This package helps you convert between Elm values and JSON values.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm/json")))))
+
+(define-public elm-json-bootstrap
+  ;; elm/core doesn't depend on elm/json,
+  ;; but elm-build-system's strategy for building it
+  ;; (and everything else) does
+  (hidden-package
+   (package
+     (inherit elm-json)
+     (name "elm-json-bootstrap")
+     (propagated-inputs '())
+     (arguments
+      (list #:phases
+            #~(modify-phases %standard-phases
+                (delete 'configure)
+                (delete 'build)
+                (delete 'validate-compiled))
+            #:implicit-elm-package-inputs? #f)))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:31 +0200
[PATCH 05/30] build-system/elm: Add implicit Elm inputs.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-5-philip@philipmcgrath.com
* guix/build-system/elm.scm (default-elm-core): New variable.
(default-elm-json): Likewise
(lower): Use them as implicit inputs when applicable.
---
guix/build-system/elm.scm | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)

Toggle diff (52 lines)
diff --git a/guix/build-system/elm.scm b/guix/build-system/elm.scm
index bf77df6519..046e6e0a1f 100644
--- a/guix/build-system/elm.scm
+++ b/guix/build-system/elm.scm
@@ -75,6 +75,18 @@ (define (default-elm)
   (let ((elm (resolve-interface '(gnu packages elm))))
     (module-ref elm 'elm)))
 
+(define (default-elm-core)
+  "Return the default elm-core package."
+  ;; Lazily resolve the binding to avoid a circular dependency.
+  (let ((elm (resolve-interface '(gnu packages elm))))
+    (module-ref elm 'elm-core)))
+
+(define (default-elm-json)
+  "Return the default elm-json package."
+  ;; Lazily resolve the binding to avoid a circular dependency.
+  (let ((elm (resolve-interface '(gnu packages elm))))
+    (module-ref elm 'elm-json)))
+
 (define* (lower name
                 #:key source inputs native-inputs outputs system target
                 (implicit-elm-package-inputs? #t)
@@ -99,6 +111,26 @@ (define private-keywords
                '())
          ,@inputs
          ("elm" ,elm)
+         ,@(cond
+            (implicit-elm-package-inputs?
+             ;; These are needed for elm-build-system even if not actually
+             ;; needed by the package being built.  But "elm/json" is often
+             ;; present in practice, and "elm/core" always is: only add the
+             ;; default packages if no suitable inputs have been given explicitly.
+             (filter-map (match-lambda
+                           ((name get-default)
+                            (cond
+                             ((find (match-lambda
+                                      ((_ pkg . _)
+                                       (equal? name (package-upstream-name pkg))))
+                                    inputs)
+                              #f)
+                             (else
+                              `(,name ,(get-default))))))
+                         `(("elm/core" ,default-elm-core)
+                           ("elm/json" ,default-elm-json))))
+            (else
+             '()))
          ;; TODO: probably don't need most of (standard-packages)
          ,@(standard-packages)))
       (outputs outputs)
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:31 +0200
[PATCH 06/30] gnu: Add elm-virtual-dom.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-6-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-virtual-dom): New variable.
---
gnu/packages/elm.scm | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

Toggle diff (31 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index b61d238cff..af12804301 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -155,3 +155,24 @@ (define-public elm-json-bootstrap
                 (delete 'build)
                 (delete 'validate-compiled))
             #:implicit-elm-package-inputs? #f)))))
+
+(define-public elm-virtual-dom
+  (package
+    (name "elm-virtual-dom")
+    (version "1.0.2")
+    (source
+     (elm-package-origin
+      "elm/virtual-dom"
+      version
+      (base32 "1d6pqv6cvrsk2cp3dxxrq82vwrij4hrgjazg3lh93z75jkpvywhl")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-json elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/virtual-dom/1.0.2")
+    (synopsis
+     "Elm's low-level virtual DOM implementation")
+    (description
+     "This package provides a virtual DOM implementation that backs Elm's
+core libraries for HTML and SVG.  You should almost certainly use those
+higher-level libraries directly.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm/virtual-dom")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:31 +0200
[PATCH 07/30] gnu: Add elm-html.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-7-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-html): New variable.
---
gnu/packages/elm.scm | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

Toggle diff (30 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index af12804301..4ab7063adc 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -176,3 +176,23 @@ (define-public elm-virtual-dom
 higher-level libraries directly.")
     (license license:bsd-3)
     (properties '((upstream-name . "elm/virtual-dom")))))
+
+(define-public elm-html
+  (package
+    (name "elm-html")
+    (version "1.0.0")
+    (source
+     (elm-package-origin
+      "elm/html"
+      version
+      (base32 "15k1679ja57vvlpinpv06znmrxy09lbhzfkzdc89i01qa8c4gb4a")))
+    (build-system elm-build-system)
+    (propagated-inputs
+     (list elm-virtual-dom
+           elm-json
+           elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/html/1.0.0")
+    (synopsis "Fast HTML, rendered with virtual DOM diffing")
+    (description "This package provides Elm's HTML rendering library.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm/html")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:31 +0200
[PATCH 08/30] gnu: Add elm-svg.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-8-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-svg): New variable.
---
gnu/packages/elm.scm | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)

Toggle diff (33 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 4ab7063adc..053a2e62f7 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -196,3 +196,26 @@ (define-public elm-html
     (description "This package provides Elm's HTML rendering library.")
     (license license:bsd-3)
     (properties '((upstream-name . "elm/html")))))
+
+(define-public elm-svg
+  (package
+    (name "elm-svg")
+    (version "1.0.1")
+    (source
+     (elm-package-origin
+      "elm/svg"
+      version
+      (base32 "1iqsc3p129j56lp1y3z3mfc6x1shvrmx3pkhri2777ylhyw90qvl")))
+    (build-system elm-build-system)
+    (propagated-inputs
+     (list elm-html
+           elm-virtual-dom
+           elm-json
+           elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/svg/1.0.1")
+    (synopsis "Fast SVG, rendered with virtual DOM diffing")
+    (description
+     "This package provides Elm's @acronym{SVG, Scalable Vector Graphics}
+library.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm/svg")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:31 +0200
[PATCH 09/30] gnu: Add elm-time.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-9-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-time): New variable.
---
gnu/packages/elm.scm | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

Toggle diff (30 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 053a2e62f7..0a296506ba 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -219,3 +219,23 @@ (define-public elm-svg
 library.")
     (license license:bsd-3)
     (properties '((upstream-name . "elm/svg")))))
+
+(define-public elm-time
+  (package
+    (name "elm-time")
+    (version "1.0.0")
+    (source
+     (elm-package-origin
+      "elm/time"
+      version
+      (base32 "0wqa2vhl1zf8z0j2yd3yjwfhr0dydfns43bbzll3k4rhnjadxr1l")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/time/1.0.0")
+    (synopsis
+     "POSIX time and time zones in Elm")
+    (description
+     "This package provides an Elm library for working with POSIX times, time
+zones, formatting, and the clock.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm/time")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:31 +0200
[PATCH 10/30] gnu: Add elm-url.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-10-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-url): New variable.
---
gnu/packages/elm.scm | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)

Toggle diff (39 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 0a296506ba..0116ffc1e9 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -239,3 +239,32 @@ (define-public elm-time
 zones, formatting, and the clock.")
     (license license:bsd-3)
     (properties '((upstream-name . "elm/time")))))
+
+(define-public elm-url
+  (package
+    (name "elm-url")
+    (version "1.0.0")
+    (source
+     (elm-package-origin
+      "elm/url"
+      version
+      (base32 "1f2ij4i7zmijnj2i50qf19lpkr14bhms8dkq029inb5mydi9f8gs")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/url/1.0.0")
+    (synopsis
+     "Create and parse URLs in Elm")
+    (description
+     "This package helps you:
+
+@enumerate
+@item
+build new URLs; and
+
+@item
+parse existing URLs into nice Elm data structures.
+@end enumerate
+
+Use it for HTTP and for @dfn{routing} in @acronym{SPAs, single-page apps}.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm/url")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:31 +0200
[PATCH 11/30] gnu: Add elm-browser.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-11-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-browser): New variable.
---
gnu/packages/elm.scm | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)

Toggle diff (36 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 0116ffc1e9..114cb231a2 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -268,3 +268,29 @@ (define-public elm-url
 Use it for HTTP and for @dfn{routing} in @acronym{SPAs, single-page apps}.")
     (license license:bsd-3)
     (properties '((upstream-name . "elm/url")))))
+
+(define-public elm-browser
+  (package
+    (name "elm-browser")
+    (version "1.0.2")
+    (source
+     (elm-package-origin
+      "elm/browser"
+      version
+      (base32 "0863nw2hhbpm3s03lm1imi5x28wwknzrwg2p79s5mydgvdvgwjf0")))
+    (build-system elm-build-system)
+    (propagated-inputs
+     (list elm-virtual-dom
+           elm-url
+           elm-time
+           elm-json
+           elm-html
+           elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/browser/1.0.2")
+    (synopsis
+     "Run Elm in browsers")
+    (description
+     "This package allows you to create Elm programs that run in browsers,
+with access to browser history for @acronym{SPAs, single-page apps}.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm/browser")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:31 +0200
[PATCH 12/30] gnu: Add elm-bytes.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-12-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-bytes): New variable.
---
gnu/packages/elm.scm | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

Toggle diff (29 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 114cb231a2..8d6a98962e 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -294,3 +294,22 @@ (define-public elm-browser
 with access to browser history for @acronym{SPAs, single-page apps}.")
     (license license:bsd-3)
     (properties '((upstream-name . "elm/browser")))))
+
+(define-public elm-bytes
+  (package
+    (name "elm-bytes")
+    (version "1.0.8")
+    (source
+     (elm-package-origin
+      "elm/bytes"
+      version
+      (base32 "0n411j2cyz9m241q6vszfzpq3fraradwal5m0gigp2505mdfpz3x")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/bytes/1.0.8")
+    (synopsis "Work with sequences of bytes in Elm")
+    (description "This package provides an Elm library for working with
+densely packed sequences of bytes, such as @code{ArrayBuffer}, typed arrays,
+and @code{DataView}.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm/bytes")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:31 +0200
[PATCH 13/30] gnu: Add elm-file.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-13-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-file): New variable.
---
gnu/packages/elm.scm | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

Toggle diff (32 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 8d6a98962e..0099adca90 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -313,3 +313,25 @@ (define-public elm-bytes
 and @code{DataView}.")
     (license license:bsd-3)
     (properties '((upstream-name . "elm/bytes")))))
+
+(define-public elm-file
+  (package
+    (name "elm-file")
+    (version "1.0.5")
+    (source
+     (elm-package-origin
+      "elm/file"
+      version
+      (base32 "0aimgicrdpys0v89m2wjx413561zil14cczjh6mkn9jcgckx6yng")))
+    (build-system elm-build-system)
+    (propagated-inputs
+     (list elm-time
+           elm-json
+           elm-core
+           elm-bytes))
+    (home-page "https://package.elm-lang.org/packages/elm/file/1.0.5")
+    (synopsis "Work with files in Elm")
+    (description "This package enables Elm programs to select files, download
+files, and work with file content.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm/file")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:31 +0200
[PATCH 14/30] gnu: Add elm-http.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-14-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-http): New variable.
---
gnu/packages/elm.scm | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)

Toggle diff (33 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 0099adca90..6a9cac8553 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -335,3 +335,26 @@ (define-public elm-file
 files, and work with file content.")
     (license license:bsd-3)
     (properties '((upstream-name . "elm/file")))))
+
+(define-public elm-http
+  (package
+    (name "elm-http")
+    (version "2.0.0")
+    (source
+     (elm-package-origin
+      "elm/http"
+      version
+      (base32 "0mfbz0lkfidmq5xpv5csw8943q0yrpvj0rwd2vb0gc8rbsfc9dg8")))
+    (build-system elm-build-system)
+    (propagated-inputs
+     (list elm-json
+           elm-file
+           elm-core
+           elm-bytes))
+    (home-page "https://package.elm-lang.org/packages/elm/http/2.0.0")
+    (synopsis "Make HTTP requests in Elm")
+    (description "This package enables Elm programs to make HTTP requests and
+talk to servers.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm/http")))))
+
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:31 +0200
[PATCH 15/30] gnu: Add elm-parser.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-15-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-parser): New variable.
---
gnu/packages/elm.scm | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

Toggle diff (31 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 6a9cac8553..512b4fc01c 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -358,3 +358,24 @@ (define-public elm-http
     (license license:bsd-3)
     (properties '((upstream-name . "elm/http")))))
 
+(define-public elm-parser
+  (package
+    (name "elm-parser")
+    (version "1.1.0")
+    (source
+     (elm-package-origin
+      "elm/parser"
+      version
+      (base32 "06xx29rmagc5r45qfpvrd393lz83ylngidfp08432f1qc8y6r3lh")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/parser/1.1.0")
+    (synopsis
+     "Parsing library for Elm")
+    (description
+     "Regular expressions are quite confusing and difficult to use.  This
+library provides a coherent alternative that handles more cases and produces
+clearer code.  It is focused on simplicity and great error messages.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm/parser")))))
+
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:32 +0200
[PATCH 16/30] gnu: Add elm-project-metadata-utils.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-16-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-project-metadata-utils): New variable.
---
gnu/packages/elm.scm | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)

Toggle diff (34 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 512b4fc01c..58bcf322d9 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -379,3 +379,27 @@ (define-public elm-parser
     (license license:bsd-3)
     (properties '((upstream-name . "elm/parser")))))
 
+(define-public elm-project-metadata-utils
+  (package
+    (name "elm-project-metadata-utils")
+    (version "1.0.2")
+    (source
+     (elm-package-origin
+      "elm/project-metadata-utils"
+      version
+      (base32 "1wj7chfy4knwwyc3k0hy431c80hs7hc686qsr34ayn8gip73x2jj")))
+    (build-system elm-build-system)
+    (propagated-inputs
+     (list elm-parser
+           elm-json
+           elm-core))
+    (home-page
+     "https://package.elm-lang.org/packages/elm/project-metadata-utils/1.0.2")
+    (synopsis "Work with @file{elm.json} and @file{docs.json} files in Elm")
+    (description
+     "This package is meant for people creating Elm tooling, like editor
+plugins.  If you just want to make stuff in Elm, there is nothing here for
+you.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm/project-metadata-utils")))))
+
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:32 +0200
[PATCH 17/30] gnu: Add elm-explorations-markdown.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-17-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-explorations-markdown): New variable.
---
gnu/packages/elm.scm | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

Toggle diff (30 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 58bcf322d9..53e250ee22 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -403,3 +403,23 @@ (define-public elm-project-metadata-utils
     (license license:bsd-3)
     (properties '((upstream-name . "elm/project-metadata-utils")))))
 
+(define-public elm-explorations-markdown
+  (package
+    (name "elm-explorations-markdown")
+    (version "1.0.0")
+    (source
+     (elm-package-origin
+      "elm-explorations/markdown"
+      version
+      (base32 "1f57ikdpbbhchcpwj32216gxjxijrc3sdpg27s1cgzia9pnkqk6p")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-html elm-core))
+    (home-page
+     "https://package.elm-lang.org/packages/elm-explorations/markdown/1.0.0")
+    (synopsis "Fast markdown parsing and rendering in Elm")
+    (description
+     "This package is for markdown parsing and rendering in Elm.  It is based
+on the @code{marked} project, which focuses on speed.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm-explorations/markdown")))))
+
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:32 +0200
[PATCH 18/30] gnu: elm: Support 'elm reactor'.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-18-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm): Rename to ...
(elm-sans-reactor): ... this new variable.
[synopsis, description]: Tweak.
(elm): New variable.
* guix/build-system/elm.scm (default-elm): Use elm-sans-reactor.
---
gnu/packages/elm.scm | 75 +++++++++++++++++++++++++++++++++++++--
guix/build-system/elm.scm | 2 +-
2 files changed, 74 insertions(+), 3 deletions(-)

Toggle diff (109 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 53e250ee22..4181322393 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -40,9 +40,9 @@ (define-module (gnu packages elm)
 ;; `elm reactor` exit with a useful error message if they aren't there.
 (define %reactor-root-base
   "share/elm/reactor-")
-(define-public elm
+(define-public elm-sans-reactor
   (package
-    (name "elm")
+    (name "elm-sans-reactor")
     (version "0.19.1")
     (source
      (origin
@@ -93,6 +93,77 @@ (define-public elm
            ghc-vector
            ghc-zip-archive))
     (home-page "https://elm-lang.org")
+    (synopsis "Minimal variant of @command{elm}")
+    (description
+     "This package provides a version of the Elm compiler without support for
+the @command{elm reactor} development command.")
+    (license license:bsd-3)))
+
+(define-public elm
+  (package
+    (name "elm")
+    (version (package-version elm-sans-reactor))
+    (source (package-source elm-sans-reactor))
+    (native-inputs (list elm-sans-reactor))
+    (inputs (list elm-sans-reactor
+                  elm-browser
+                  elm-core
+                  elm-html
+                  elm-http
+                  elm-json
+                  elm-project-metadata-utils
+                  elm-svg
+                  elm-explorations-markdown))
+    (build-system elm-build-system)
+    (arguments
+     (list
+      #:modules
+      `((srfi srfi-26)
+        ,@%elm-default-modules)
+      #:phases
+      #~(modify-phases %standard-phases
+          (delete 'stage)
+          (replace 'configure
+            (lambda* (#:key native-inputs inputs #:allow-other-keys)
+              (with-directory-excursion "reactor"
+                (patch-application-dependencies))))
+          (replace 'build
+            (lambda* (#:key native-inputs inputs #:allow-other-keys)
+              (with-directory-excursion "reactor"
+                (invoke (search-input-file (or native-inputs inputs)
+                                           "/bin/elm")
+                        "make"
+                        "--optimize"
+                        "src/NotFound.elm"
+                        "src/Errors.elm"
+                        "src/Index.elm"))))
+          (replace 'install
+            (lambda* (#:key inputs #:allow-other-keys)
+              (let* ((out-dir #$output)
+                     (bin-dir (string-append out-dir "/bin"))
+                     (reactor-dir (string-append out-dir
+                                                 "/"
+                                                 #$%reactor-root-base
+                                                 (getenv "GUIX_ELM_VERSION")))
+                     (reactor-subdir (string-append reactor-dir "/_elm")))
+                ;; We can't use a symlink here because Haskell's
+                ;; `getExecutablePath` follows all symlinks.
+                ;; Guix can make it a hard link later.
+                (install-file (search-input-file inputs ;; NOT native-inputs
+                                                 "/bin/elm")
+                              bin-dir)
+                (install-file "reactor/assets/favicon.ico" reactor-dir)
+                (for-each (cut install-file <> reactor-subdir)
+                          '("reactor/elm.js"
+                            "reactor/assets/styles.css"
+                            ;; TODO: these are source-code-pro v1.017 and
+                            ;; source-sans-pro v1.050: there may be breaking
+                            ;; changes in Guix's existing
+                            ;; font-adobe-source-{code,sans}-pro packages
+                            "reactor/assets/source-code-pro.ttf"
+                            "reactor/assets/source-sans-pro.ttf")))))
+          (delete 'validate-compiled))))
+    (home-page "https://elm-lang.org")
     (synopsis "Programming language for Web applications")
     (description
      "Elm is a statically-typed, purely-functional programming language for
diff --git a/guix/build-system/elm.scm b/guix/build-system/elm.scm
index 046e6e0a1f..5e2db3b052 100644
--- a/guix/build-system/elm.scm
+++ b/guix/build-system/elm.scm
@@ -73,7 +73,7 @@ (define (default-elm)
   "Return the default Elm package for builds."
   ;; Lazily resolve the binding to avoid a circular dependency.
   (let ((elm (resolve-interface '(gnu packages elm))))
-    (module-ref elm 'elm)))
+    (module-ref elm 'elm-sans-reactor)))
 
 (define (default-elm-core)
   "Return the default elm-core package."
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:32 +0200
[PATCH 19/30] gnu: Add elm-todomvc.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-19-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-todomvc): New variable.
---
gnu/packages/elm.scm | 53 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)

Toggle diff (63 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 4181322393..e4cae693c4 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -494,3 +494,56 @@ (define-public elm-explorations-markdown
     (license license:bsd-3)
     (properties '((upstream-name . "elm-explorations/markdown")))))
 
+(define-public elm-todomvc
+  (let ((commit "f236e7e56941c7705aba6e42cb020ff515fe3290")
+        (revision "1"))
+    (package
+      (name "elm-todomvc")
+      (version (git-version "1" revision commit))
+      (source
+       (origin
+         (method git-fetch)
+         (uri (git-reference
+               (url "https://github.com/evancz/elm-todomvc")
+               (commit commit)))
+         (sha256
+          (base32 "0g37bglzshkf79s4n7aq9ib44h5qn8ng7n72sh2xslgd20h05nfw"))
+         (file-name (git-file-name name version))))
+      (inputs (list elm-browser elm-core elm-html elm-json))
+      (build-system elm-build-system)
+      (arguments
+       (list
+        #:modules
+        `((srfi srfi-26)
+          ,@%elm-default-modules)
+        #:phases
+        #~(modify-phases %standard-phases
+            (delete 'stage)
+            (replace 'configure
+              patch-application-dependencies)
+            (replace 'build
+              (lambda* (#:key native-inputs inputs #:allow-other-keys)
+                (invoke (search-input-file (or native-inputs inputs)
+                                           "/bin/elm")
+                        "make"
+                        "src/Main.elm"
+                        "--output=elm.js")))
+            (replace 'install
+              (lambda args
+                (let* ((out-dir #$output)
+                       (dest-dir
+                        (string-append out-dir
+                                       "/share/"
+                                       (strip-store-file-name out-dir))))
+                  (for-each (cut install-file <> dest-dir)
+                            `("elm.js"
+                              "index.html"
+                              "style.css"
+                              "README.md")))))
+            (delete 'validate-compiled))))
+      (home-page "https://github.com/evancz/elm-todomvc")
+      (synopsis "TodoMVC in Elm")
+      (description "This is the official Elm implementation of
+@url{https://todomvc.com,TodoMVC}, a simple to-do--list application used to
+compare front-end web frameworks.")
+      (license license:bsd-3))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:32 +0200
[PATCH 20/30] gnu: Add elm-debois-elm-dom.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-20-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-debois-elm-dom): New variable.
---
gnu/packages/elm.scm | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

Toggle diff (31 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index e4cae693c4..81fe54bf39 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -547,3 +547,24 @@ (define-public elm-todomvc
 @url{https://todomvc.com,TodoMVC}, a simple to-do--list application used to
 compare front-end web frameworks.")
       (license license:bsd-3))))
+
+(define-public elm-debois-elm-dom
+  (package
+    (name "elm-debois-elm-dom")
+    (version "1.3.0")
+    (source
+     (elm-package-origin
+      "debois/elm-dom"
+      version
+      (base32 "0w4z4w6ip118lk5g80w6mbbfxhfmivbmdkdm6wsmk63x85gscmqx")))
+    (build-system elm-build-system)
+    (propagated-inputs
+     (list elm-json elm-html elm-core))
+    (home-page "https://package.elm-lang.org/packages/debois/elm-dom/1.3.0")
+    (synopsis "DOM traversal for Elm event-handlers")
+    (description
+     "This package provides a library for reading information off the DOM.
+Use this if you need to discover geometry information (width, position, etc.)
+of rendered elements.")
+    (license license:asl2.0)
+    (properties '((upstream-name . "debois/elm-dom")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:32 +0200
[PATCH 21/30] gnu: Add elm-random.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-21-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-random): New variable.
---
gnu/packages/elm.scm | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

Toggle diff (29 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 81fe54bf39..940c456e14 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -568,3 +568,22 @@ (define-public elm-debois-elm-dom
 of rendered elements.")
     (license license:asl2.0)
     (properties '((upstream-name . "debois/elm-dom")))))
+
+(define-public elm-random
+  (package
+    (name "elm-random")
+    (version "1.0.0")
+    (source
+     (elm-package-origin
+      "elm/random"
+      version
+      (base32 "0z0znkwfs35xiabk6pa9yqwsq03jssgd7jmsj1kk000mikfm7f39")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-time elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/random/1.0.0")
+    (synopsis "Generate random numbers and values in Elm")
+    (description
+     "Need to generate random numbers?  How about random game boards?  Or
+random positions in 3D space?  This is the package for you!")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm/random")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:32 +0200
[PATCH 22/30] gnu: Add elm-explorations-test.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-22-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-explorations-test): New variable.
---
gnu/packages/elm.scm | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)

Toggle diff (36 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 940c456e14..3498516b82 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -587,3 +587,29 @@ (define-public elm-random
 random positions in 3D space?  This is the package for you!")
     (license license:bsd-3)
     (properties '((upstream-name . "elm/random")))))
+
+(define-public elm-explorations-test
+  (package
+    (name "elm-explorations-test")
+    (version "1.2.2")
+    (source
+     (elm-package-origin
+      "elm-explorations/test"
+      version
+      (base32 "0kw32x0lr6nh5j9xk56vgg7x7c705g38grghh7cdp49frwdd6w3l")))
+    (build-system elm-build-system)
+    (propagated-inputs
+     (list elm-virtual-dom
+           elm-random
+           elm-json
+           elm-html
+           elm-core))
+    (home-page
+     "https://package.elm-lang.org/packages/elm-explorations/test/1.2.2")
+    (synopsis "Testing framework for Elm")
+    (description "This package enables writing unit and fuzz tests for Elm
+code.  To actually run the tests, you need the command-line tool from
+@url{https://github.com/rtfeldman/node-test-runner}, which has not yet been
+packaged for Guix.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "elm-explorations/test")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:32 +0200
[PATCH 23/30] gnu: Add elm-danhandrea-elm-date-format.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-23-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-danhandrea-elm-date-format): New variable.
---
gnu/packages/elm.scm | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

Toggle diff (30 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 3498516b82..d21feef89e 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -613,3 +613,23 @@ (define-public elm-explorations-test
 packaged for Guix.")
     (license license:bsd-3)
     (properties '((upstream-name . "elm-explorations/test")))))
+
+(define-public elm-danhandrea-elm-date-format
+  (package
+    (name "elm-danhandrea-elm-date-format")
+    (version "2.0.1")
+    (source
+     (elm-package-origin
+      "danhandrea/elm-date-format"
+      version
+      (base32 "03mglzyywij5sm56m7q2jhhbsv7f9rdirgwmq0q41ghir81bzpv6")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-time elm-core))
+    (inputs (list elm-explorations-test))
+    (home-page
+     "https://package.elm-lang.org/packages/danhandrea/elm-date-format/2.0.1")
+    (synopsis "Date formatting for Elm")
+    (description "This package enhances @code{elm/time} with support for
+format strings and internationalization of dates.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "danhandrea/elm-date-format")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:32 +0200
[PATCH 24/30] gnu: Add elm-danhandrea-elm-time-extra.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-24-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-danhandrea-elm-time-extra): New variable.
---
gnu/packages/elm.scm | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

Toggle diff (32 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index d21feef89e..9e398403e4 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -633,3 +633,25 @@ (define-public elm-danhandrea-elm-date-format
 format strings and internationalization of dates.")
     (license license:bsd-3)
     (properties '((upstream-name . "danhandrea/elm-date-format")))))
+
+(define-public elm-danhandrea-elm-time-extra
+  (package
+    (name "elm-danhandrea-elm-time-extra")
+    (version "1.1.0")
+    (source
+     (elm-package-origin
+      "danhandrea/elm-time-extra"
+      version
+      (base32 "0z13q8x148d5amcc89f846yzql89n9gyan8fr48y91dhhn7vxibf")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-time elm-core))
+    (inputs
+     (list elm-explorations-test
+           elm-danhandrea-elm-date-format))
+    (home-page
+     "https://package.elm-lang.org/packages/danhandrea/elm-time-extra/1.1.0")
+    (synopsis "Extra utilities for POSIX time in Elm")
+    (description "This package enhances @code{elm/time} with extra utilities
+for working with POSIX times.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "danhandrea/elm-time-extra")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:32 +0200
[PATCH 25/30] gnu: Add elm-justinmimbs-date.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-25-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-justinmimbs-date): New variable.
---
gnu/packages/elm.scm | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

Toggle diff (32 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 9e398403e4..9f8d74f695 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -655,3 +655,25 @@ (define-public elm-danhandrea-elm-time-extra
 for working with POSIX times.")
     (license license:bsd-3)
     (properties '((upstream-name . "danhandrea/elm-time-extra")))))
+
+(define-public elm-justinmimbs-date
+  (package
+    (name "elm-justinmimbs-date")
+    (version "4.0.1")
+    (source
+     (elm-package-origin
+      "justinmimbs/date"
+      version
+      (base32 "13mf97137f0yb3gx1mxbya2y70qciah4hp5bcnpj8166vgzb7l3l")))
+    (build-system elm-build-system)
+    (propagated-inputs
+     (list elm-time
+           elm-parser
+           elm-core))
+    (home-page "https://package.elm-lang.org/packages/justinmimbs/date/4.0.1")
+    (synopsis "Work with dates without times or zones in Elm")
+    (description
+     "This Elm package provides a simple @code{Date} type for working with
+dates without times or zones.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "justinmimbs/date")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:32 +0200
[PATCH 26/30] gnu: Add elm-justinmimbs-time-extra.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-26-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-justinmimbs-time-extra): New variable.
---
gnu/packages/elm.scm | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)

Toggle diff (33 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 9f8d74f695..8992112e13 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -677,3 +677,26 @@ (define-public elm-justinmimbs-date
 dates without times or zones.")
     (license license:bsd-3)
     (properties '((upstream-name . "justinmimbs/date")))))
+
+(define-public elm-justinmimbs-time-extra
+  (package
+    (name "elm-justinmimbs-time-extra")
+    (version "1.1.1")
+    (source
+     (elm-package-origin
+      "justinmimbs/time-extra"
+      version
+      (base32 "1gmgvzlpzkysvm86d0md75ply10pz28bg699m4763frss0jvrngh")))
+    (build-system elm-build-system)
+    (propagated-inputs
+     (list elm-justinmimbs-date
+           elm-time
+           elm-core))
+    (home-page
+     "https://package.elm-lang.org/packages/justinmimbs/time-extra/1.1.1")
+    (synopsis "Extra functions for POSIX times in Elm")
+    (description
+     "This package provides extra functions for working with @code{Posix}
+times from @code{elm/time}.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "justinmimbs/time-extra")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:32 +0200
[PATCH 27/30] gnu: Add elm-myrho-elm-round.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-27-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-myrho-elm-round): New variable.
---
gnu/packages/elm.scm | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

Toggle diff (32 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 8992112e13..cdbc7e14a5 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -700,3 +700,25 @@ (define-public elm-justinmimbs-time-extra
 times from @code{elm/time}.")
     (license license:bsd-3)
     (properties '((upstream-name . "justinmimbs/time-extra")))))
+
+(define-public elm-myrho-elm-round
+  (package
+    (name "elm-myrho-elm-round")
+    (version "1.0.4")
+    (source
+     (elm-package-origin
+      "myrho/elm-round"
+      version
+      (base32 "0y3j43wr815cvwz5310zalnqzpg3hw8h127zjjlf6x8ynapc2mdb")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-core))
+    (inputs (list elm-explorations-test))
+    (home-page "https://package.elm-lang.org/packages/myrho/elm-round/1.0.4")
+    (synopsis "Flexible rounding of Elm @code{Float}s")
+    (description
+     "This library converts a @code{Float} to a @code{String} with ultimate
+control how many digits after the decimal point are shown and how the
+remaining digits are rounded.  It rounds, floors and ceils the @dfn{common}
+way (i.e. half up) or the @dfn{commerical} way (ie. half away from zero).")
+    (license license:bsd-3)
+    (properties '((upstream-name . "myrho/elm-round")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:32 +0200
[PATCH 28/30] gnu: Add elm-ryannhg-date-format.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-28-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-ryannhg-date-format): New variable.
---
gnu/packages/elm.scm | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)

Toggle diff (33 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index cdbc7e14a5..4f1b093165 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -722,3 +722,26 @@ (define-public elm-myrho-elm-round
 way (i.e. half up) or the @dfn{commerical} way (ie. half away from zero).")
     (license license:bsd-3)
     (properties '((upstream-name . "myrho/elm-round")))))
+
+(define-public elm-ryannhg-date-format
+  (package
+    (name "elm-ryannhg-date-format")
+    (version "2.3.0")
+    (source
+     (elm-package-origin
+      "ryannhg/date-format"
+      version
+      (base32 "0razh6im5qwmla10knk67j8w11mqlqlyfnclykbfl06kaksfm3sp")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-time elm-core))
+    (inputs (list elm-explorations-test))
+    (home-page
+     "https://package.elm-lang.org/packages/ryannhg/date-format/2.3.0")
+    (synopsis "Reliable advanced dates formatting for Elm")
+    (description
+     "This package provides Elm programs with reliable, powerful tools for
+formatting dates and times.  It uses Elm's type system instead of format
+strings, which makes formatting code more readable and can catch some errors
+at compile time.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "ryannhg/date-format")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:32 +0200
[PATCH 29/30] gnu: Add elm-terezka-intervals.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-29-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-terezka-intervals): New variable.
---
gnu/packages/elm.scm | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)

Toggle diff (42 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 4f1b093165..186af9d768 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -745,3 +745,35 @@ (define-public elm-ryannhg-date-format
 at compile time.")
     (license license:bsd-3)
     (properties '((upstream-name . "ryannhg/date-format")))))
+
+(define-public elm-terezka-intervals
+  (package
+    (name "elm-terezka-intervals")
+    (version "2.0.1")
+    (source
+     (elm-package-origin
+      "terezka/intervals"
+      version
+      (base32 "0h3im58sa6awyppch1v8ppcrzwc5h48yn45crx98m8zs4isx91lj")))
+    (build-system elm-build-system)
+    (propagated-inputs
+     (list elm-ryannhg-date-format
+           elm-myrho-elm-round
+           elm-justinmimbs-time-extra
+           elm-time
+           elm-svg
+           elm-json
+           elm-html
+           elm-core
+           elm-danhandrea-elm-time-extra))
+    (inputs (list elm-explorations-test))
+    (home-page "https://package.elm-lang.org/packages/terezka/intervals/2.0.1")
+    (synopsis "Produce nice intervals for Elm charts")
+    (description
+     "This package provides an Elm library for producing nice intervals for
+charts.  It's useful in combination with @code{terezka/elm-charts}.  When I
+say ``nice'', I just mean that I try to calculate intervals which begin with
+10, 5, 3, 2, or 1 (adjusted to magnitude, of course!).  For dates, I try to
+hit whole days, weeks, and months or hours, minutes, and seconds.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "terezka/intervals")))))
-- 
2.32.0
P
P
Philip McGrath wrote on 20 Apr 01:32 +0200
[PATCH 30/30] gnu: Add elm-terezka-elm-charts.
(address . 55030@debbugs.gnu.org)(name . Philip McGrath)(address . philip@philipmcgrath.com)
20220419233214.275789-30-philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-terezka-elm-charts): New variable.
---
gnu/packages/elm.scm | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)

Toggle diff (43 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 186af9d768..1e9cbd4a30 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -777,3 +777,36 @@ (define-public elm-terezka-intervals
 hit whole days, weeks, and months or hours, minutes, and seconds.")
     (license license:bsd-3)
     (properties '((upstream-name . "terezka/intervals")))))
+
+(define-public elm-terezka-elm-charts
+  (package
+    (name "elm-terezka-elm-charts")
+    (version "3.0.0") ;; NOTE! Tags like 5.1.0 are from an OLDER version
+    (source
+     (elm-package-origin
+      "terezka/elm-charts"
+      version
+      (base32 "17syq73jwldc7fk7snm5k8s85nsvxyprb34rs1rwjsapc1vii7hc")))
+    (build-system elm-build-system)
+    (propagated-inputs
+     (list elm-terezka-intervals
+           elm-ryannhg-date-format
+           elm-time
+           elm-svg
+           elm-json
+           elm-html
+           elm-core
+           elm-debois-elm-dom))
+    (home-page
+     "https://elm-charts.org")
+    (synopsis "SVG chart components in Elm")
+    (description
+     "Make SVG charts in all Elm.  The package can draw charts at a variety of
+different levels of customization, from basic charts with standard features to
+very custom styles.  The library also allows including your very own SVG
+elements while still easily utilizing the coordinate system calculated from
+your data, as well as editing the SVGs made by the package.  It has great
+support for interactivity, layering different charts, and adding irregular
+details.")
+    (license license:bsd-3)
+    (properties '((upstream-name . "terezka/elm-charts")))))
-- 
2.32.0
L
L
Ludovic Courtès wrote on 1 May 22:22 +0200
Re: bug#55030: [PATCH 00/30] gnu: elm: Update to 0.19.1. Add build system & importer.
(name . Philip McGrath)(address . philip@philipmcgrath.com)(address . 55030@debbugs.gnu.org)
87k0b52ec3.fsf@gnu.org
Hi Philip,

Philip McGrath <philip@philipmcgrath.com> skribis:

Toggle quote (3 lines)
> This patch series updates Elm to version 0.9.1, then adds an
> 'elm-build-system' and a 'guix import elm' command.

Impressive!

Toggle quote (10 lines)
> To exercise the new features, this patch series then:
>
> * Build the front-end for the `elm reactor` command (which is written in Elm)
> and adds a variant of Elm to Guix with the command enabled;
>
> * Builds 'elm-todomvc', an official example of a basic Elm application; and
>
> * Builds a feature-rich third-party package, "terezka/elm-charts":
> <https://elm-charts.org>.

Woow, neat.

Annoying question that I have to ask: do these packages bundle
JavaScript libraries? If yes, is it source or is it “minified”?

(My take is that we could tolerate some level of bundling if “doing the
right thing” is impractical, but it’d rather be source.)

Thanks,
Ludo’.
L
L
Ludovic Courtès wrote on 1 May 22:22 +0200
(name . Philip McGrath)(address . philip@philipmcgrath.com)(address . 55030@debbugs.gnu.org)
87fslt2eb0.fsf_-_@gnu.org
Philip McGrath <philip@philipmcgrath.com> skribis:

Toggle quote (4 lines)
> * gnu/packages/elm.scm (elm-compiler): Rename to ...
> (elm): ... this variable.
> [description]: Tweak.

Could you add a deprecated alias, with ‘deprecated-package’?
L
L
Ludovic Courtès wrote on 1 May 22:35 +0200
(name . Philip McGrath)(address . philip@philipmcgrath.com)(address . 55030@debbugs.gnu.org)
874k292dpq.fsf_-_@gnu.org
Philip McGrath <philip@philipmcgrath.com> skribis:

Toggle quote (7 lines)
> * gnu/packages/patches/elm-offline-package-registry.scm: New file.
> * gnu/local.mk (dist_patch_DATA): Add it.
> * gnu/packages/elm.scm (elm): Use it.
> * guix/build-system/elm.scm, guix/build/elm-build-system.scm,
> guix/import/elm.scm, guix/scripts/import/elm.scm: New files.
> * guix/scripts/import.scm (importers): Add "elm".

I think the custom would be to add the importer in a separate commit; if
you can do that, that’s great.

Could you add an entry for the importer under “Invoking guix import”,
and one for the build system under “Build Systems” in guix.texi? You
can follow existing entries as a template.

It would be nice to have tests for the importer. One way to do that is
like ‘tests/cpan.scm’, which spawns an HTTP server that mimics the real
registry.

Toggle quote (2 lines)
> +;; COMMENTARY:

Nitpick: You can make that literally “;;; Commentary:”. That’s what
(ice-9 documentation) expects.

Toggle quote (2 lines)
> +;; CODE:

Likewise: “;;; Code:”.

Toggle quote (40 lines)
> +(define elm-package-registry
> + ;; It is much nicer to fetch this small (< 40 KB gzipped)
> + ;; file once than to do many HTTP requests.
> + (mlambda ()
> + "Fetch the Elm package registry, represented as a vhash mapping package
> +names to lists of available versions, sorted from latest to oldest."
> + (let ((url "https://package.elm-lang.org/all-packages"))
> + (cond
> + ((json-fetch url)
> + => (lambda (alist)
> + (fold (lambda (entry vh)
> + (match entry
> + ((name . vec)
> + (vhash-cons name
> + (sort (vector->list vec) version>?)
> + vh))))
> + vlist-null
> + alist)))
> + (else
> + (raise (formatted-message
> + (G_ "error downloading Elm package registry from ~a")
> + url)))))))
> +
> +(define (make-elm-package-sexp name version)
> + "Return two values: the `package' s-expression for the Elm package with the
> +given NAME and VERSION, and a list of Elm packages it depends on."
> + (define-values (checkout _commit _relation)
> + ;; Elm requires that packages use this very specific format
> + (update-cached-checkout (string-append "https://github.com/" name)
> + #:ref `(tag . ,version)))
> + (define info
> + (call-with-input-file (string-append checkout "/elm.json")
> + json->scm))
> + (define (get-deps key)
> + (cond
> + ((assoc-ref info key)
> + => (cut map car <>))
> + (else
> + '())))

The way the importer fiddles with alists isn’t pretty IMO. :-)

How about using ‘define-json-mapping’ (also from Guile-JSON) to “map”
JSON data structures to records? See how pypi.scm and others do it.
The resulting code should be clearer.

Also, instead of or in addition to memoizing ‘elm-package-registry’,
would it make sense to use ‘http-fetch/cached’ to fetch that file?

Nitpick: Guile has multiple-value truncation, so you can write:

(define checkout
(update-cached-checkout …))

I haven’t looked into much detail at the build system, but I’m sure you
know what you’re doing. :-)

Ludo’.
L
L
Ludovic Courtès wrote on 1 May 22:37 +0200
(name . Philip McGrath)(address . philip@philipmcgrath.com)(address . 55030@debbugs.gnu.org)
87zgk10z2o.fsf_-_@gnu.org
Philip McGrath <philip@philipmcgrath.com> skribis:

Toggle quote (3 lines)
> + (package
> + (name "elm-virtual-dom")

[...]

Toggle quote (2 lines)
> + (properties '((upstream-name . "elm/virtual-dom")))))

Could/should the importer infer the upstream name from the Guix name by
default?

That way, we’d only need to specify that property where the automatic
Guix->upstream name mapping wouldn’t work.
L
L
Ludovic Courtès wrote on 1 May 22:41 +0200
(name . Philip McGrath)(address . philip@philipmcgrath.com)(address . 55030@debbugs.gnu.org)
87v8up0yv4.fsf_-_@gnu.org
I sent a few comments to individual patches that probably warrant a v2,
but overall I think the patch series is almost ready to go.

Thanks!

Ludo’.
P
P
Philip McGrath wrote on 1 May 23:26 +0200
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 55030@debbugs.gnu.org)
8bb2cf89-ae0e-cdcd-5149-a2d62aa3bbcc@philipmcgrath.com
Hi,

Hi,

On 5/1/22 16:22, Ludovic Courtès wrote:
Toggle quote (10 lines)
> Hi Philip,
>
> Philip McGrath <philip@philipmcgrath.com> skribis:
>
>> This patch series updates Elm to version 0.9.1, then adds an
>> 'elm-build-system' and a 'guix import elm' command.
>
> Impressive!
>

Thanks!

Toggle quote (19 lines)
>> To exercise the new features, this patch series then:
>>
>> * Build the front-end for the `elm reactor` command (which is written in Elm)
>> and adds a variant of Elm to Guix with the command enabled;
>>
>> * Builds 'elm-todomvc', an official example of a basic Elm application; and
>>
>> * Builds a feature-rich third-party package, "terezka/elm-charts":
>> <https://elm-charts.org>.
>
> Woow, neat.
>
> Annoying question that I have to ask: do these packages bundle
> JavaScript libraries? If yes, is it source or is it “minified”?
>
> (My take is that we could tolerate some level of bundling if “doing the
> right thing” is impractical, but it’d rather be source.)
>

Short answer: no, they don't!

Longer answer:

Elm basically takes the view that the existing JavaScript/NPM thicket
should be considered harmful. It imposes a lot of very strict
requirements on Elm "packages" (vs. "applications") to avoid whole
classes of problems. Not all of them are precisely the requirements I
would have chosen, but I like them better than the alternative chaos.

(One reason I gave this a try was to get some hands-on experience
writing a build system and importer in a simplified context before
trying to write `racket-build-system`.)

In particular, allowing arbitrary JavaScript would defeat the strong
guarantees Elm wants to offer as a statically-typed, purely-functional
language with compiler-enforced semantic versioning (well, for a
decidable subset of "semantics") that can make runtime errors
vanishingly rare in practice. To that end, Elm requires that
"packages"---the things `elm-build-system` knows how to build---be
written in pure Elm, with no JavaScript at all. For "applications",
interop is limited to asynchronous message passing.[1] The only two
"applications" in this series, the `elm reactor` frontend and
`elm-todomvc`, don't use any message passing.

Of course, Elm needs some way to implement primitives. These are
provided by modules in the `Elm.Kernel.*` namespace, which are written
in JavaScript with the undocumented, unsafe conventions expected by the
Elm compiler. The Elm compiler only allows kernel modules in packages in
the `elm/*` and `elm-explorations/*` namespaces, so users can know that
third-party packages won't break Elm's guaranteed, and the compiler is
free to change its internal APIs without breaking anything. (This is
free software, so you could patch the compiler to do whatever you want:
it's just a community norm so strong it's expressed in code.) Even this
is all source code with whitespace and comments: it's written in a very
stylized way, as an ASM file in another compiler implementation might
be, but it isn't generated code.

For people who want to "minify", Elm suggests flags for UglifyJS that
can also do otherwise-unsafe whole-program optimization of the compiled
"application".[2] (Compiling "packages" emits only an internal
representation, not JavaScript.) In my own projects, I've also done
other post-processing, like adding LibreJS comments and converting the
output to an ES6 module. I haven't tried to make `elm-build-system` do
any of those kinds of things for "applications": I think we should find
more examples of Elm applications people would want to package for Guix
first, rather than trying to guess what they might need.


-Philip
P
P
Philip McGrath wrote on 1 May 23:27 +0200
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 55030@debbugs.gnu.org)
d9b42ca6-22c1-0bc2-3249-316c451c8405@philipmcgrath.com
On 5/1/22 16:22, Ludovic Courtès wrote:
Toggle quote (8 lines)
> Philip McGrath <philip@philipmcgrath.com> skribis:
>
>> * gnu/packages/elm.scm (elm-compiler): Rename to ...
>> (elm): ... this variable.
>> [description]: Tweak.
>
> Could you add a deprecated alias, with ‘deprecated-package’?

Yes, will do. (Though it will be at least a few days before I circle
back to this.)
P
P
Philip McGrath wrote on 2 May 00:03 +0200
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 55030@debbugs.gnu.org)
1cf62d38-77f7-b50c-6137-a345a255826c@philipmcgrath.com
Hi,

On 5/1/22 16:35, Ludovic Courtès wrote:
Toggle quote (13 lines)
> Philip McGrath <philip@philipmcgrath.com> skribis:
>
>> * gnu/packages/patches/elm-offline-package-registry.scm: New file.
>> * gnu/local.mk (dist_patch_DATA): Add it.
>> * gnu/packages/elm.scm (elm): Use it.
>> * guix/build-system/elm.scm, guix/build/elm-build-system.scm,
>> guix/import/elm.scm, guix/scripts/import/elm.scm: New files.
>> * guix/scripts/import.scm (importers): Add "elm".
>
> I think the custom would be to add the importer in a separate commit; if
> you can do that, that’s great.
>

I certainly can split it that way. I did it like this because I actually
wrote the importer before the build system.

Toggle quote (5 lines)
> Could you add an entry for the importer under “Invoking guix import”,
> and one for the build system under “Build Systems” in guix.texi? You
> can follow existing entries as a template.
>

I will give it a try! I haven't written any Texinfo before.

Toggle quote (5 lines)
> It would be nice to have tests for the importer. One way to do that is
> like ‘tests/cpan.scm’, which spawns an HTTP server that mimics the real
> registry.
>

I'll take a look at that.

Toggle quote (10 lines)
>> +;; COMMENTARY:
>
> Nitpick: You can make that literally “;;; Commentary:”. That’s what
> (ice-9 documentation) expects.
>
>> +;; CODE:
>
> Likewise: “;;; Code:”.
>

Will do.

Toggle quote (47 lines)
>> +(define elm-package-registry
>> + ;; It is much nicer to fetch this small (< 40 KB gzipped)
>> + ;; file once than to do many HTTP requests.
>> + (mlambda ()
>> + "Fetch the Elm package registry, represented as a vhash mapping package
>> +names to lists of available versions, sorted from latest to oldest."
>> + (let ((url "https://package.elm-lang.org/all-packages"))
>> + (cond
>> + ((json-fetch url)
>> + => (lambda (alist)
>> + (fold (lambda (entry vh)
>> + (match entry
>> + ((name . vec)
>> + (vhash-cons name
>> + (sort (vector->list vec) version>?)
>> + vh))))
>> + vlist-null
>> + alist)))
>> + (else
>> + (raise (formatted-message
>> + (G_ "error downloading Elm package registry from ~a")
>> + url)))))))
>> +
>> +(define (make-elm-package-sexp name version)
>> + "Return two values: the `package' s-expression for the Elm package with the
>> +given NAME and VERSION, and a list of Elm packages it depends on."
>> + (define-values (checkout _commit _relation)
>> + ;; Elm requires that packages use this very specific format
>> + (update-cached-checkout (string-append "https://github.com/" name)
>> + #:ref `(tag . ,version)))
>> + (define info
>> + (call-with-input-file (string-append checkout "/elm.json")
>> + json->scm))
>> + (define (get-deps key)
>> + (cond
>> + ((assoc-ref info key)
>> + => (cut map car <>))
>> + (else
>> + '())))
>
> The way the importer fiddles with alists isn’t pretty IMO. :-)
>
> How about using ‘define-json-mapping’ (also from Guile-JSON) to “map”
> JSON data structures to records? See how pypi.scm and others do it.
> The resulting code should be clearer.
>

I had tried that first, but there were some problems: IIRC, there might
have been an issue with potentially-absent fields defaulting to
*unspecified*, some alist manipulation was needed anyway for fields that
use JSON objects as key--value maps, and, with a view toward being able
to process `{"type":"application"}` files some day, there didn't seem to
be enough ability to adapt parsing based on the value for the key. I
found this code less confusing. But I can try again if it seems important!

Toggle quote (4 lines)
> Also, instead of or in addition to memoizing ‘elm-package-registry’,
> would it make sense to use ‘http-fetch/cached’ to fetch that file?
>

I'll take a look!

Toggle quote (6 lines)
> Nitpick: Guile has multiple-value truncation, so you can write:
>
> (define checkout
> (update-cached-checkout …))
>

I saw that some places in Guix relied on that already, but I also saw
that `info guile values` says that:

> The effect of passing no
> value or more than one value to continuations that were not created
> by ‘call-with-values’ is unspecified.

... so I wasn't sure what to do.

-Philip
P
P
Philip McGrath wrote on 2 May 00:17 +0200
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 55030@debbugs.gnu.org)
1752ba0e-1808-a587-049b-cb46dba6f65c@philipmcgrath.com
Hi,

On 5/1/22 16:37, Ludovic Courtès wrote:
Toggle quote (15 lines)
> Philip McGrath <philip@philipmcgrath.com> skribis:
>
>> + (package
>> + (name "elm-virtual-dom")
>
> [...]
>
>> + (properties '((upstream-name . "elm/virtual-dom")))))
>
> Could/should the importer infer the upstream name from the Guix name by
> default?
>
> That way, we’d only need to specify that property where the automatic
> Guix->upstream name mapping wouldn’t work.

It could, but the heuristics seemed a bit brittle. To pick a few examples:

1. elm-virtual-dom -> "elm/virtual-dom"
2. elm-explorations-markdown -> "elm-explorations/markdown"
2. elm-terezka-intervals -> "terezka/intervals"

We could add a special case for the "elm-explorations/*" namespace, but
at least one of the others would need an explicit property. I *think*
most of the packages in the "elm/*" namespace are single-element (e.g.
"elm/html"), so maybe we could require the property for e.g.
"elm/virtual-dom" and "elm/project-metadata-utils" ...

-Philip
P
P
Philip McGrath wrote on 2 May 00:22 +0200
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 55030@debbugs.gnu.org)
c9fcf5ff-59e3-dfac-1b5e-18b95f467780@philipmcgrath.com
Hi,

On 5/1/22 16:41, Ludovic Courtès wrote:
Toggle quote (7 lines)
> I sent a few comments to individual patches that probably warrant a v2,
> but overall I think the patch series is almost ready to go.
>
> Thanks!
>
> Ludo’.

Thanks for taking a look! I have a few other things to wrap up before I
return to this, but I'll put together a v2 relatively soon.

Tangentially, I learned after sending this series about a Rust-based
runner [1] for "elm-explorations/test" tests, which may be easier for us
to package than the Node.js-based runner.[2] Still, I'd probably save
that for some future patch series.

-Philip

L
L
Ludovic Courtès wrote on 8 May 23:34 +0200
(name . Philip McGrath)(address . philip@philipmcgrath.com)(address . 55030@debbugs.gnu.org)
87ee13wvwf.fsf_-_@gnu.org
Hi Philip,

Philip McGrath <philip@philipmcgrath.com> skribis:

Toggle quote (45 lines)
> Elm basically takes the view that the existing JavaScript/NPM thicket
> should be considered harmful. It imposes a lot of very strict
> requirements on Elm "packages" (vs. "applications") to avoid whole
> classes of problems. Not all of them are precisely the requirements I
> would have chosen, but I like them better than the alternative chaos.
>
> (One reason I gave this a try was to get some hands-on experience
> writing a build system and importer in a simplified context before
> trying to write `racket-build-system`.)
>
> In particular, allowing arbitrary JavaScript would defeat the strong
> guarantees Elm wants to offer as a statically-typed, purely-functional
> language with compiler-enforced semantic versioning (well, for a
> decidable subset of "semantics") that can make runtime errors
> vanishingly rare in practice. To that end, Elm requires that
> "packages"---the things `elm-build-system` knows how to build---be
> written in pure Elm, with no JavaScript at all. For "applications",
> interop is limited to asynchronous message passing.[1] The only two
> "applications" in this series, the `elm reactor` frontend and
> `elm-todomvc`, don't use any message passing.
>
> Of course, Elm needs some way to implement primitives. These are
> provided by modules in the `Elm.Kernel.*` namespace, which are written
> in JavaScript with the undocumented, unsafe conventions expected by
> the Elm compiler. The Elm compiler only allows kernel modules in
> packages in the `elm/*` and `elm-explorations/*` namespaces, so users
> can know that third-party packages won't break Elm's guaranteed, and
> the compiler is free to change its internal APIs without breaking
> anything. (This is free software, so you could patch the compiler to
> do whatever you want: it's just a community norm so strong it's
> expressed in code.) Even this is all source code with whitespace and
> comments: it's written in a very stylized way, as an ASM file in
> another compiler implementation might be, but it isn't generated code.
>
> For people who want to "minify", Elm suggests flags for UglifyJS that
> can also do otherwise-unsafe whole-program optimization of the
> compiled "application".[2] (Compiling "packages" emits only an
> internal representation, not JavaScript.) In my own projects, I've
> also done other post-processing, like adding LibreJS comments and
> converting the output to an ES6 module. I haven't tried to make
> `elm-build-system` do any of those kinds of things for "applications":
> I think we should find more examples of Elm applications people would
> want to package for Guix first, rather than trying to guess what they
> might need.

Thanks for explaining! Elm is fascinating in many ways.

Ludo’.
P
P
Philip McGrath wrote on 18 May 20:10 +0200
[PATCH v2 00/34] gnu: elm: Update to 0.19.1. Add build system & importer.
(address . 55030@debbugs.gnu.org)
cover.1652890702.git.philip@philipmcgrath.com
Hi,

Here is v2!

A few notes before the patches:

On 5/1/22 18:17, Philip McGrath wrote:
Toggle quote (31 lines)
> Hi,
>
> On 5/1/22 16:37, Ludovic Courtès wrote:
>> Philip McGrath <philip@philipmcgrath.com> skribis:
>>
>>> + (package
>>> + (name "elm-virtual-dom")
>>
>> [...]
>>
>>> + (properties '((upstream-name . "elm/virtual-dom")))))
>>
>> Could/should the importer infer the upstream name from the Guix name by
>> default?
>>
>> That way, we’d only need to specify that property where the automatic
>> Guix->upstream name mapping wouldn’t work.
>
> It could, but the heuristics seemed a bit brittle. To pick a few examples:
>
> 1. elm-virtual-dom -> "elm/virtual-dom"
> 2. elm-explorations-markdown -> "elm-explorations/markdown"
> 2. elm-terezka-intervals -> "terezka/intervals"
>
> We could add a special case for the "elm-explorations/*" namespace, but
> at least one of the others would need an explicit property. I *think*
> most of the packages in the "elm/*" namespace are single-element (e.g.
> "elm/html"), so maybe we could require the property for e.g.
> "elm/virtual-dom" and "elm/project-metadata-utils" ...
>

I think I've come up with an approach to inferring upstream names that handles
most cases (everything in this series but "elm/virtual-dom" and
"elm/project-metadata-utils") while also not adding so much magic that the
behavior would be unpredictable.

I've put the functions going in both directions in `(guix build-system elm)`
rather than `(guix import elm)` (which seemed somewhat more common) because
`elm-package-origin` needs `elm->package-name`, and it seemed better overall
to define the Elm-to-Guix and Guix-to-Elm conversions in the same place.

On 5/1/22 18:03, Philip McGrath wrote:
Toggle quote (11 lines)
> Hi,
>
> On 5/1/22 16:35, Ludovic Courtès wrote:
>> Could you add an entry for the importer under “Invoking guix import”,
>> and one for the build system under “Build Systems” in guix.texi? You
>> can follow existing entries as a template.
>>
>
> I will give it a try! I haven't written any Texinfo before.
>

I've added documentation there, and also a section on naming conventions in
contributing.texi. I'm not sure all of my Texinfo is idiomatic (well, I
didn't find equivalents for all of the Scribble cross-reference functions I'm
used to), but it does build and work ok.

Toggle quote (8 lines)
>> It would be nice to have tests for the importer. One way to do that is
>> like ‘tests/cpan.scm’, which spawns an HTTP server that mimics the real
>> registry.
>>
>
> I'll take a look at that.
>

I've added a number of tests. They seem to be producing more "PASS" lines than
other tests, though. It seems like nested `test-group`s may be handled
differently than I'm used to from RackUnit and (less so) Racket's `srfi/64`
implementation, but I'm not sure what the idiomatic way to structure them
would be, if this isn't it.

Toggle quote (13 lines)
>>> +;; COMMENTARY:
>>
>> Nitpick: You can make that literally “;;; Commentary:”. That’s what
>> (ice-9 documentation) expects.
>>
>>> +;; CODE:
>>
>> Likewise: “;;; Code:”.
>>
>
> Will do.
>

Ironically, I couldn't find any documentation for `(ice-9 documentation)`
other than the comments in the source file, but I hope I've done this
properly now.

Toggle quote (17 lines)
>>
>> The way the importer fiddles with alists isn’t pretty IMO. :-)
>>
>> How about using ‘define-json-mapping’ (also from Guile-JSON) to “map”
>> JSON data structures to records? See how pypi.scm and others do it.
>> The resulting code should be clearer.
>>
>
> I had tried that first, but there were some problems: IIRC, there might
> have been an issue with potentially-absent fields defaulting to
> *unspecified*, some alist manipulation was needed anyway for fields that
> use JSON objects as key--value maps, and, with a view toward being able
> to process `{"type":"application"}` files some day, there didn't seem to
> be enough ability to adapt parsing based on the value for the key. I
> found this code less confusing. But I can try again if it seems important!
>

Most of the problems I'd run into were because I'd gotten the wrong idea from
the grammar of 'define-json-mapping`, and `define-json-mapping` didn't check
its implicit requirements: I've reported the details and potential

The remaining, unescapable alist manipulation is for cases when JSON objects
are used as key--value maps, rather than records with some finite set of
potential keys.

Toggle quote (7 lines)
>> Also, instead of or in addition to memoizing ‘elm-package-registry’,
>> would it make sense to use ‘http-fetch/cached’ to fetch that file?
>>
>
> I'll take a look!
>

Using `http-fetch/cached` without duplicating `json-fetch` required a few
additional patches:

- [PATCH v2 06/34] http-client: Accept '#:headers' in 'http-fetched/cached'.
- [PATCH v2 07/34] http-client: 'http-fetch/cached' converts strings to URIs.
- [PATCH v2 08/34] import: json: Accept '#:http-fetch' in 'json-fetch'.

They seemed small enough, and IIUC they don't affect any build-side code or
trigger rebuilds, but OTOH the actuall JSON being fetched is quite small, so
it wouldn't be a problem to drop them if there are any problems.

One other difference is that `elm-virtual-dom` had an upstream release since
the first version of this patch series, so that package is now at 1.0.3.

-Philip

Philip McGrath (34):
gnu: elm-compiler: Update to 0.19.1.
gnu: elm: Rename package to match the command.
guix: Add elm-build-system.
gnu: Add elm-core and elm-json.
build-system/elm: Add implicit Elm inputs.
http-client: Accept '#:headers' in 'http-fetched/cached'.
http-client: 'http-fetch/cached' converts strings to URIs.
import: json: Accept '#:http-fetch' in 'json-fetch'.
import: Add Elm importer.
gnu: Add elm-virtual-dom.
gnu: Add elm-html.
gnu: Add elm-svg.
gnu: Add elm-time.
gnu: Add elm-url.
gnu: Add elm-browser.
gnu: Add elm-bytes.
gnu: Add elm-file.
gnu: Add elm-http.
gnu: Add elm-parser.
gnu: Add elm-project-metadata-utils.
gnu: Add elm-explorations-markdown.
gnu: elm: Support 'elm reactor'.
gnu: Add elm-todomvc.
gnu: Add elm-debois-elm-dom.
gnu: Add elm-random.
gnu: Add elm-explorations-test.
gnu: Add elm-danhandrea-elm-date-format.
gnu: Add elm-danhandrea-elm-time-extra.
gnu: Add elm-justinmimbs-date.
gnu: Add elm-justinmimbs-time-extra.
gnu: Add elm-myrho-elm-round.
gnu: Add elm-ryannhg-date-format.
gnu: Add elm-terezka-intervals.
gnu: Add elm-terezka-elm-charts.

Makefile.am | 5 +
doc/contributing.texi | 82 ++
doc/guix.texi | 80 ++
gnu/local.mk | 4 +-
gnu/packages/elm.scm | 749 +++++++++++++++++-
.../elm-compiler-disable-reactor.patch | 71 --
.../patches/elm-compiler-fix-map-key.patch | 38 -
.../elm-offline-package-registry.patch | 71 ++
.../patches/elm-reactor-static-files.patch | 251 ++++++
guix/build-system/elm.scm | 206 +++++
guix/build/elm-build-system.scm | 380 +++++++++
guix/http-client.scm | 24 +-
guix/import/elm.scm | 210 +++++
guix/import/json.scm | 9 +-
guix/scripts/import.scm | 3 +-
guix/scripts/import/elm.scm | 107 +++
tests/elm.scm | 268 +++++++
17 files changed, 2414 insertions(+), 144 deletions(-)
delete mode 100644 gnu/packages/patches/elm-compiler-disable-reactor.patch
delete mode 100644 gnu/packages/patches/elm-compiler-fix-map-key.patch
create mode 100644 gnu/packages/patches/elm-offline-package-registry.patch
create mode 100644 gnu/packages/patches/elm-reactor-static-files.patch
create mode 100644 guix/build-system/elm.scm
create mode 100644 guix/build/elm-build-system.scm
create mode 100644 guix/import/elm.scm
create mode 100644 guix/scripts/import/elm.scm
create mode 100644 tests/elm.scm


base-commit: 665dd8211cb5c7556f0fb83944d33215accf957a
--
2.32.0
P
P
Philip McGrath wrote on 18 May 20:10 +0200
[PATCH v2 01/34] gnu: elm-compiler: Update to 0.19.1.
(address . 55030@debbugs.gnu.org)
b876244079de101bb3a781e0b0f48fe5f6f7f57c.1652890702.git.philip@philipmcgrath.com
* gnu/packages/patches/elm-compiler-disable-reactor.patch,
gnu/packages/patches/elm-compiler-fix-map-key.patch: Delete files.
* gnu/packages/patches/elm-reactor-static-files.patch: New file.
* gnu/local.mk (dist_patch_DATA): Update accordingly.
* gnu/packages/elm.scm (elm-compiler): Update to 0.19.1.
[origin]<patches>: Remove stale patches. Add new patch.
[arguments]: Use G-expressions. Add #:configure-flags for new patch.
[inputs]: Remove ghc-file-embed. Add ghc-filelock.
---
gnu/local.mk | 3 +-
gnu/packages/elm.scm | 44 +--
.../elm-compiler-disable-reactor.patch | 71 -----
.../patches/elm-compiler-fix-map-key.patch | 38 ---
.../patches/elm-reactor-static-files.patch | 251 ++++++++++++++++++
5 files changed, 280 insertions(+), 127 deletions(-)
delete mode 100644 gnu/packages/patches/elm-compiler-disable-reactor.patch
delete mode 100644 gnu/packages/patches/elm-compiler-fix-map-key.patch
create mode 100644 gnu/packages/patches/elm-reactor-static-files.patch

Toggle diff (118 lines)
diff --git a/gnu/local.mk b/gnu/local.mk
index 79019e8564..de044bdbff 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1024,8 +1024,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/einstein-build.patch			\
   %D%/packages/patches/elfutils-tests-ptrace.patch		\
   %D%/packages/patches/elixir-path-length.patch			\
-  %D%/packages/patches/elm-compiler-disable-reactor.patch	\
-  %D%/packages/patches/elm-compiler-fix-map-key.patch		\
+  %D%/packages/patches/elm-reactor-static-files.patch		\
   %D%/packages/patches/elogind-revert-polkit-detection.patch	\
   %D%/packages/patches/emacs-exec-path.patch			\
   %D%/packages/patches/emacs-git-email-missing-parens.patch	\
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index ca7c61041b..988cc02de1 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -1,5 +1,6 @@
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2019 Robert Vollmert <rob@vllmrt.net>
+;;; Copyright © 2022 Philip McGrath <philip@philipmcgrath.com>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -24,18 +25,24 @@ (define-module (gnu packages elm)
   #:use-module (gnu packages haskell-xyz)
   #:use-module (gnu packages haskell-web)
   #:use-module (guix build-system haskell)
+  #:use-module (guix gexp)
   #:use-module (guix git-download)
   #:use-module ((guix licenses) #:prefix license:)
   #:use-module (guix packages))
 
-;; The full elm build calls out to itself via Template Haskell to
-;; compile the elm reactor web app. elm reactor isn't required to
-;; compile elm applications, so we take this part out of this
-;; bootstrap package.
+;; The `elm` build usually calls out to itself via Template Haskell to compile
+;; the `elm reactor` web app (which depends on additional Elm packages) and
+;; embeds the static files into itself.  The reactor isn't required to compile
+;; Elm applications, so we want to skip it for the bootstrap package, but we
+;; also want to be able to enable it once we can build it.  We patch Elm to
+;; instead look for the files on disk relative to the executable and to have
+;; `elm reactor` exit with a useful error message if they aren't there.
+(define %reactor-root-base
+  "share/elm/reactor-")
 (define-public elm-compiler
   (package
     (name "elm-compiler")
-    (version "0.19.0")
+    (version "0.19.1")
     (source
      (origin
        (method git-fetch)
@@ -44,24 +51,29 @@ (define-public elm-compiler
              (url "https://github.com/elm/compiler/")
              (commit version)))
        (sha256
-        (base32 "0s93z9vr0vp5w894ghc5s34nsq09sg1msf59zfiba87sid5vgjqy"))
+        (base32 "1rdg3xp3js9xadclk3cdypkscm5wahgsfmm4ldcw3xswzhw6ri8w"))
        (patches
-        (search-patches "elm-compiler-disable-reactor.patch"
-                        "elm-compiler-fix-map-key.patch"))))
+        (search-patches "elm-reactor-static-files.patch"))))
     (build-system haskell-build-system)
     (arguments
-     `(#:phases
-       (modify-phases %standard-phases
-         (add-before 'configure 'update-constraints
-           (lambda _
-             (substitute* "elm.cabal"
-               (("(ansi-terminal|containers|network|http-client|language-glsl)\\s+[^,]+" all dep)
-                dep)))))))
+     (list
+      #:configure-flags
+      #~(list (string-append "--ghc-option=-DGUIX_REACTOR_STATIC_REL_ROOT="
+                             "\"../" #$%reactor-root-base
+                             #$(package-version this-package)
+                             "\""))
+      #:phases
+      #~(modify-phases %standard-phases
+          (add-before 'configure 'update-constraints
+            (lambda _
+              (substitute* "elm.cabal"
+                (("(ansi-terminal|containers|network|http-client|language-glsl)\\s+[^,]+" all dep)
+                 dep)))))))
     (inputs
      (list ghc-ansi-terminal
            ghc-ansi-wl-pprint
            ghc-edit-distance
-           ghc-file-embed
+           ghc-filelock
            ghc-http
            ghc-http-client
            ghc-http-client-tls
diff --git a/gnu/packages/patches/elm-compiler-disable-reactor.patch b/gnu/packages/patches/elm-compiler-disable-reactor.patch
deleted file mode 100644
index 9871b55e8d..0000000000
--- a/gnu/packages/patches/elm-compiler-disable-reactor.patch
+++ /dev/null
@@ -1,71 +0,0 @@
-commit 20d80e2323b565a36751c9455e535d8f73fa32f7
-Author: Robert Vollmert <rob@vllmrt.net>
-Date:   Fri Jun 14 16:05:47 2019 +0200
-
-    disable reactor
-
-diff --git a/elm.cabal b/elm.cabal
-index c75f9689..ece63c46 100644
---- a/elm.cabal
-+++ b/elm.cabal
-@@ -45,9 +45,6 @@ Executable elm
-         builder/src
-         ui/terminal/src
- 
--    other-extensions:
--        TemplateHaskell
--
- Main-Is:
- Main.hs
-
-@@ -56,8 +53,6 @@ Executable elm
- Develop
- Develop.Generate.Help
- Develop.Generate.Index
-- Develop.StaticFiles
-- Develop.StaticFiles.Build
- Diff
- Init
- Install
-diff --git a/ui/terminal/src/Develop.hs b/ui/terminal/src/Develop.hs
-index 4b2252e1..7ed7716e 100644
---- a/ui/terminal/src/Develop.hs
-+++ b/ui/terminal/src/Develop.hs
-@@ -23,7 +23,6 @@ import Snap.Util.FileServe
- import qualified Elm.Project as Project
- import qualified Develop.Generate.Help as Generate
- import qualified Develop.Generate.Index as Index
--import qualified Develop.StaticFiles as StaticFiles
- import qualified Generate.Output as Output
- import qualified Json.Encode as Encode
- import qualified Reporting.Exit as Exit
-@@ -219,16 +218,7 @@ compileToHtmlBuilder mode file =
-
-
- serveAssets :: Snap ()
--serveAssets =
-- do file <- getSafePath
-- case StaticFiles.lookup file of
-- Nothing ->
-- pass
--
-- Just (content, mimeType) ->
-- do modifyResponse (setContentType (mimeType <> ";charset=utf-8"))
-- writeBS content
--
-+serveAssets = pass
-
-
- -- MIME TYPES
-diff --git a/ui/terminal/src/Main.hs b/terminal/src/Main.hs
-index 7000f3ca..2c76965a 100644
---- a/ui/terminal/src/Main.hs
-+++ b/ui/terminal/src/Main.hs
-@@ -39,7 +39,6 @@ main =
- complex intro outro
- [ repl
- , init
-- , reactor
- , make
- , install
- , bump
Toggle diff (303 lines)
diff --git a/gnu/packages/patches/elm-compiler-fix-map-key.patch b/gnu/packages/patches/elm-compiler-fix-map-key.patch
deleted file mode 100644
index 4f05ded530..0000000000
--- a/gnu/packages/patches/elm-compiler-fix-map-key.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-commit e3512d887df41a8162c3e361171c04beca08415b
-Author: Tom Stejskal <tom.stejskal@gmail.com>
-Date:   Mon Nov 19 20:09:43 2018 +0100
-
-    Fix Map.!: given key is not an element in the map
-
-diff --git a/compiler/src/Elm/Compiler/Type/Extract.hs b/compiler/src/Elm/Compiler/Type/Extract.hs
-index 1aafe1d4..99763392 100644
---- a/compiler/src/Elm/Compiler/Type/Extract.hs
-+++ b/compiler/src/Elm/Compiler/Type/Extract.hs
-@@ -10,6 +10,7 @@ module Elm.Compiler.Type.Extract
- 
- 
- import Data.Map ((!))
-+import qualified Data.Map as Map
- import qualified Data.Maybe as Maybe
- import qualified Data.Set as Set
- 
-@@ -134,11 +135,15 @@ extractUnion interfaces (Opt.Global home name) =
-     else
-       let
-         pname = toPublicName home name
--        unions = I._unions (interfaces ! home)
-+        maybeUnions = I._unions <$> Map.lookup home interfaces
-       in
--      case I.toUnionInternals (unions ! name) of
--        Can.Union vars ctors _ _ ->
--          T.Union pname vars <$> traverse extractCtor ctors
-+      case Map.lookup name =<< maybeUnions of
-+        Just union ->
-+          case I.toUnionInternals union of
-+            Can.Union vars ctors _ _ ->
-+              T.Union pname vars <$> traverse extractCtor ctors
-+        Nothing ->
-+          return $ T.Union pname [] []
- 
- 
- extractCtor :: Can.Ctor -> Extractor (N.Name, [T.Type])
diff --git a/gnu/packages/patches/elm-reactor-static-files.patch b/gnu/packages/patches/elm-reactor-static-files.patch
new file mode 100644
index 0000000000..94c4aa0cd1
--- /dev/null
+++ b/gnu/packages/patches/elm-reactor-static-files.patch
@@ -0,0 +1,251 @@
+From 41d219a29b03f3114af7a0521c8b2dbbb487c3e1 Mon Sep 17 00:00:00 2001
+From: Philip McGrath <philip@philipmcgrath.com>
+Date: Wed, 13 Apr 2022 18:45:58 -0400
+Subject: [PATCH] reactor: look for static files relative to executable
+
+Must built with `-DGUIX_REACTOR_STATIC_REL_ROOT="../path/to/reactor"`.
+
+This lets us build a version of Elm without the `elm reactor` for
+bootstrapping, then simply put the files in place in the final package.
+---
+ elm.cabal                                 |  2 +-
+ terminal/src/Develop.hs                   | 32 +++++++++++----
+ terminal/src/Develop/StaticFiles.hs       | 37 ++++++++++-------
+ terminal/src/Develop/StaticFiles/Build.hs | 50 ++++++++++++++---------
+ 4 files changed, 79 insertions(+), 42 deletions(-)
+
+diff --git a/elm.cabal b/elm.cabal
+index bf1cfcf0..93161072 100644
+--- a/elm.cabal
++++ b/elm.cabal
+@@ -50,6 +50,7 @@ Executable elm
+ 
+     other-extensions:
+         TemplateHaskell
++        CPP
+ 
+     Main-Is:
+         Main.hs
+@@ -211,7 +212,6 @@ Executable elm
+         containers >= 0.5.8.2 && < 0.6,
+         directory >= 1.2.3.0 && < 2.0,
+         edit-distance >= 0.2 && < 0.3,
+-        file-embed,
+         filelock,
+         filepath >= 1 && < 2.0,
+         ghc-prim >= 0.5.2,
+diff --git a/terminal/src/Develop.hs b/terminal/src/Develop.hs
+index 00339364..6855b03e 100644
+--- a/terminal/src/Develop.hs
++++ b/terminal/src/Develop.hs
+@@ -33,6 +33,7 @@ import qualified Reporting.Exit as Exit
+ import qualified Reporting.Task as Task
+ import qualified Stuff
+ 
++import System.Exit as SysExit
+ 
+ 
+ -- RUN THE DEV SERVER
+@@ -45,13 +46,29 @@ data Flags =
+ 
+ 
+ run :: () -> Flags -> IO ()
+-run () (Flags maybePort) =
++run () flags = do
++  frontEnd <- StaticFiles.prepare
++  case frontEnd of
++    Right lookup ->
++      reallyRun lookup flags
++    Left missing ->
++      SysExit.die $ unlines
++      [ "The `reactor` command is not available."
++      , ""
++      , "On Guix, these files are needed for `elm reactor` to work,"
++      , "but they are missing:"
++      , ""
++      , unlines (map (\pth -> "    " ++ (show pth)) missing)
++      ]
++
++reallyRun :: StaticFiles.Lookup -> Flags -> IO ()
++reallyRun lookup (Flags maybePort) =
+   do  let port = maybe 8000 id maybePort
+       putStrLn $ "Go to http://localhost:" ++ show port ++ " to see your project dashboard."
+       httpServe (config port) $
+         serveFiles
+         <|> serveDirectoryWith directoryConfig "."
+-        <|> serveAssets
++        <|> serveAssets lookup
+         <|> error404
+ 
+ 
+@@ -169,16 +186,15 @@ compile path =
+ -- SERVE STATIC ASSETS
+ 
+ 
+-serveAssets :: Snap ()
+-serveAssets =
++serveAssets :: StaticFiles.Lookup -> Snap ()
++serveAssets lookup =
+   do  path <- getSafePath
+-      case StaticFiles.lookup path of
++      case lookup path of
+         Nothing ->
+           pass
+ 
+-        Just (content, mimeType) ->
+-          do  modifyResponse (setContentType (mimeType <> ";charset=utf-8"))
+-              writeBS content
++        Just (fsPath, mimeType) ->
++          serveFileAs (mimeType <> ";charset=utf-8") fsPath
+ 
+ 
+ 
+diff --git a/terminal/src/Develop/StaticFiles.hs b/terminal/src/Develop/StaticFiles.hs
+index 94ee72dc..3227d617 100644
+--- a/terminal/src/Develop/StaticFiles.hs
++++ b/terminal/src/Develop/StaticFiles.hs
+@@ -2,7 +2,8 @@
+ {-# LANGUAGE OverloadedStrings #-}
+ {-# LANGUAGE TemplateHaskell #-}
+ module Develop.StaticFiles
+-  ( lookup
++  ( prepare
++  , Lookup
+   , cssPath
+   , elmPath
+   , waitingPath
+@@ -11,9 +12,7 @@ module Develop.StaticFiles
+ 
+ import Prelude hiding (lookup)
+ import qualified Data.ByteString as BS
+-import Data.FileEmbed (bsToExp)
+ import qualified Data.HashMap.Strict as HM
+-import Language.Haskell.TH (runIO)
+ import System.FilePath ((</>))
+ 
+ import qualified Develop.StaticFiles.Build as Build
+@@ -26,20 +25,29 @@ import qualified Develop.StaticFiles.Build as Build
+ type MimeType =
+   BS.ByteString
+ 
++type Lookup = FilePath -> Maybe (FilePath, MimeType)
+ 
+-lookup :: FilePath -> Maybe (BS.ByteString, MimeType)
+-lookup path =
++prepare :: IO (Either [FilePath] Lookup)
++prepare = do
++  found <- Build.findReactorFrontEnd expectedFiles
++  return $ case found of
++    Left missing ->
++      Left missing
++    Right resolved ->
++      Right (mkLookup (HM.fromList resolved))
++
++mkLookup :: HM.HashMap FilePath (FilePath, MimeType) -> Lookup
++mkLookup dict path =
+   HM.lookup path dict
+ 
+ 
+-dict :: HM.HashMap FilePath (BS.ByteString, MimeType)
+-dict =
+-  HM.fromList
+-    [ faviconPath  ==> (favicon , "image/x-icon")
+-    , elmPath      ==> (elm     , "application/javascript")
+-    , cssPath      ==> (css     , "text/css")
+-    , codeFontPath ==> (codeFont, "font/ttf")
+-    , sansFontPath ==> (sansFont, "font/ttf")
++expectedFiles :: [(FilePath, MimeType)]
++expectedFiles =
++    [ faviconPath  ==> "image/x-icon"
++    , elmPath      ==> "application/javascript"
++    , cssPath      ==> "text/css"
++    , codeFontPath ==> "font/ttf"
++    , sansFontPath ==> "font/ttf"
+     ]
+ 
+ 
+@@ -82,7 +90,7 @@ sansFontPath =
+   "_elm" </> "source-sans-pro.ttf"
+ 
+ 
+-
++{-
+ -- ELM
+ 
+ 
+@@ -121,3 +129,4 @@ sansFont =
+ favicon :: BS.ByteString
+ favicon =
+   $(bsToExp =<< runIO (Build.readAsset "favicon.ico"))
++-}
+diff --git a/terminal/src/Develop/StaticFiles/Build.hs b/terminal/src/Develop/StaticFiles/Build.hs
+index c61fae57..c39b08b0 100644
+--- a/terminal/src/Develop/StaticFiles/Build.hs
++++ b/terminal/src/Develop/StaticFiles/Build.hs
+@@ -1,28 +1,39 @@
+ {-# LANGUAGE OverloadedStrings #-}
++{-# LANGUAGE CPP #-}
+ module Develop.StaticFiles.Build
+-  ( readAsset
+-  , buildReactorFrontEnd
++  ( findReactorFrontEnd
+   )
+   where
+ 
+-
+-import qualified Data.ByteString as BS
+-import qualified Data.ByteString.Builder as B
+-import qualified Data.ByteString.Lazy as LBS
+-import qualified Data.NonEmptyList as NE
+ import qualified System.Directory as Dir
+-import System.FilePath ((</>))
+-
+-import qualified BackgroundWriter as BW
+-import qualified Build
+-import qualified Elm.Details as Details
+-import qualified Generate
+-import qualified Reporting
+-import qualified Reporting.Exit as Exit
+-import qualified Reporting.Task as Task
+-
+-
+-
++import System.FilePath ((</>), takeDirectory)
++import System.Environment (getExecutablePath)
++import Data.Either as Either
++
++reactorStaticRelRoot :: FilePath
++reactorStaticRelRoot = GUIX_REACTOR_STATIC_REL_ROOT
++
++type Resolved a = (FilePath, (FilePath, a))
++
++findReactorFrontEnd :: [(FilePath, a)] -> IO (Either [FilePath] [Resolved a])
++findReactorFrontEnd specs = do
++  exe <- getExecutablePath
++  let dir = takeDirectory exe </> reactorStaticRelRoot
++  dirExists <- Dir.doesDirectoryExist dir
++  files <- sequence (map (findFile dir) specs)
++  return $ case Either.lefts files of
++           [] ->
++             Right (Either.rights files)
++           missing ->
++             Left $ if dirExists then missing else [dir]
++
++findFile :: FilePath -> (FilePath, a) -> IO (Either FilePath (Resolved a))
++findFile dir (rel, rhs) = do
++  let abs = dir </> rel
++  exists <- Dir.doesFileExist abs
++  return $ if not exists then Left abs else Right (rel, (abs, rhs))
++
++{-
+ -- ASSETS
+ 
+ 
+@@ -71,3 +82,4 @@ runTaskUnsafe task =
+                 \\nCompile with `elm make` directly to figure it out faster\
+                 \\n--------------------------------------------------------\
+                 \\n"
++-}
+-- 
+2.32.0
+
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:10 +0200
[PATCH v2 02/34] gnu: elm: Rename package to match the command.
(address . 55030@debbugs.gnu.org)
923649034e32b375843f3772a4d469062cac21f8.1652890702.git.philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-compiler): Rename to ...
(elm): ... this variable, leaving 'elm-compiler' as a deprecated alias.
[description]: Tweak.
---
gnu/packages/elm.scm | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)

Toggle diff (33 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 988cc02de1..a3863e6e6f 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -39,9 +39,9 @@ (define-module (gnu packages elm)
 ;; `elm reactor` exit with a useful error message if they aren't there.
 (define %reactor-root-base
   "share/elm/reactor-")
-(define-public elm-compiler
+(define-public elm
   (package
-    (name "elm-compiler")
+    (name "elm")
     (version "0.19.1")
     (source
      (origin
@@ -93,7 +93,11 @@ (define-public elm-compiler
     (home-page "https://elm-lang.org")
     (synopsis "Programming language for Web applications")
     (description
-     "This package provides Elm, a statically-typed functional programming
-language for the browser.  It includes commands for developers such as
-@command{elm make} and @command{elm repl}.")
+     "Elm is a statically-typed, purely-functional programming language for
+the browser.  The @command{elm} exectable includes commands for developers
+such as @command{elm make} and @command{elm repl}.")
     (license license:bsd-3)))
+
+;; The 'elm' package used to be called 'elm-compiler'.
+(define-public elm-compiler
+  (deprecated-package "elm-compiler" elm))
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:10 +0200
[PATCH v2 03/34] guix: Add elm-build-system.
(address . 55030@debbugs.gnu.org)
b3ea36abed9770dbfdfbde3618c59f174667df95.1652890702.git.philip@philipmcgrath.com
* gnu/packages/patches/elm-offline-package-registry.patch: New file.
* gnu/local.mk (dist_patch_DATA): Add it.
* gnu/packages/elm.scm (elm): Use it.
* guix/build-system/elm.scm, guix/build/elm-build-system.scm,
tests/elm.scm: New files.
* Makefile.scm (MODULES, SCM_TESTS): Add them.
* doc/guix.texi (Build Systems): Document 'elm-build-system'.
* doc/contributing.texi (Elm Packages): New section. Document naming
conventions and utilities.
---
Makefile.am | 3 +
doc/contributing.texi | 82 ++++
doc/guix.texi | 52 +++
gnu/local.mk | 1 +
gnu/packages/elm.scm | 4 +-
.../elm-offline-package-registry.patch | 71 ++++
guix/build-system/elm.scm | 172 ++++++++
guix/build/elm-build-system.scm | 380 ++++++++++++++++++
tests/elm.scm | 97 +++++
9 files changed, 861 insertions(+), 1 deletion(-)
create mode 100644 gnu/packages/patches/elm-offline-package-registry.patch
create mode 100644 guix/build-system/elm.scm
create mode 100644 guix/build/elm-build-system.scm
create mode 100644 tests/elm.scm

Toggle diff (978 lines)
diff --git a/Makefile.am b/Makefile.am
index 85a22be99c..9ca92c407c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -141,6 +141,7 @@ MODULES =					\
   guix/build-system/cmake.scm			\
   guix/build-system/dub.scm			\
   guix/build-system/dune.scm			\
+  guix/build-system/elm.scm			\
   guix/build-system/emacs.scm			\
   guix/build-system/font.scm			\
   guix/build-system/go.scm			\
@@ -192,6 +193,7 @@ MODULES =					\
   guix/build/cmake-build-system.scm		\
   guix/build/dub-build-system.scm		\
   guix/build/dune-build-system.scm		\
+  guix/build/elm-build-system.scm		\
   guix/build/emacs-build-system.scm		\
   guix/build/meson-build-system.scm		\
   guix/build/minify-build-system.scm		\
@@ -472,6 +474,7 @@ SCM_TESTS =					\
   tests/derivations.scm			\
   tests/discovery.scm				\
   tests/egg.scm				\
+  tests/elm.scm				\
   tests/elpa.scm				\
   tests/file-systems.scm			\
   tests/gem.scm				\
diff --git a/doc/contributing.texi b/doc/contributing.texi
index 862dcbf12a..555b9bb961 100644
--- a/doc/contributing.texi
+++ b/doc/contributing.texi
@@ -447,6 +447,7 @@ Packaging Guidelines
 * Perl Modules::                Little pearls.
 * Java Packages::               Coffee break.
 * Rust Crates::                 Beware of oxidation.
+* Elm Packages::                Trees of browser code
 * Fonts::                       Fond of fonts.
 @end menu
 
@@ -898,6 +899,87 @@ Rust Crates
 Rust compiler, or the test suite may have atrophied since it was released.
 
 
+@node Elm Packages
+@subsection Elm Packages
+
+@cindex Elm
+Elm applications can be named like other software: their names need not
+mention Elm.
+
+Packages in the Elm sense (see @code{elm-build-system} under @ref{Build
+Systems}) are required use names of the format
+@var{author}@code{/}@var{project}, where both the @var{author} and the
+@var{project} may contain hyphens internally, and the @var{author} sometimes
+contains uppercase letters.
+
+To form the Guix package name from the upstream name, we follow a convention
+similar to Python packages (@pxref{Python Modules}), adding an @code{elm-}
+prefix unless the name would already begin with @code{elm-}.
+
+In many cases we can reconstruct an Elm package's upstream name heuristically,
+but, since conversion to a Guix-style name involves a loss of information,
+this is not always possible.  Care should be taken to add the
+@code{'upstream-name} property when necessary so that tools
+will work correctly. The most notable scenarios
+when explicitly specifying the upstream name is necessary are:
+
+@enumerate
+@item
+When the @var{author} is @code{elm} and the @var{project} contains one or more
+hyphens, as with @code{elm/virtual-dom}; and
+
+@item
+When the @var{author} contains hyphens or uppercase letters, as with
+@code{Elm-Canvas/raster-shapes}---unless the @var{author} is
+@code{elm-explorations}, which is handled as a special case, so packages like
+@code{elm-explorations/markdown} do @emph{not} need to use the
+@code{'upstream-name} property.
+@end enumerate
+
+The module @code{(guix build-system elm)} provides the following utilities for
+working with names and related conventions:
+
+@deffn {Scheme procedure} elm-package-origin @var{elm-name} @var{version} @
+  @var{hash}
+Returns a Git origin using the repository naming and tagging regime required
+for a published Elm package with the upstream name @var{elm-name} at version
+@var{version} with sha256 checksum @var{hash}.
+
+For example:
+@lisp
+(package
+  (name "elm-html")
+  (version "1.0.0")
+  (source
+   (elm-package-origin
+    "elm/html"
+    version
+    (base32 "15k1679ja57vvlpinpv06znmrxy09lbhzfkzdc89i01qa8c4gb4a")))
+  ...)
+@end lisp
+@end deffn
+
+@deffn {Scheme procedure} elm->package-name @var{elm-name}
+Returns the Guix-style package name for an Elm package with upstream name
+@var{elm-name}.
+
+Note that there is more than one possible @var{elm-name} for which
+@code{elm->package-name} will produce a given result.
+@end deffn
+
+@deffn {Scheme procedure} guix-package->elm-name @var{package}
+Given an Elm @var{package}, returns the possibly-inferred upstream name, or
+@code{#f} the upstream name is not specified via the @code{'upstream-name}
+property and can not be inferred by @code{infer-elm-package-name}.
+@end deffn
+
+@deffn {Scheme procedure} infer-elm-package-name @var{guix-name}
+Given the @var{guix-name} of an Elm package, returns the inferred upstream
+name, or @code{#f} if the upstream name can't be inferred.  If the result is
+not @code{#f}, supplying it to @code{elm->package-name} would produce
+@var{guix-name}.
+@end deffn
+
 @node Fonts
 @subsection Fonts
 
diff --git a/doc/guix.texi b/doc/guix.texi
index c007c93dd3..63fb647045 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -102,6 +102,7 @@
 Copyright @copyright{} 2021 Josselin Poiret@*
 Copyright @copyright{} 2022 Remco van 't Veer@*
 Copyright @copyright{} 2022 Aleksandr Vityazev@*
+Copyright @copyright{} 2022 Philip M@sup{c}Grath@*
 
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -8717,6 +8718,57 @@ Build Systems
 
 @end defvr
 
+@defvr {Scheme variable} elm-build-system
+This variable is exported by @code{(guix build-system elm)}.  It implements a
+build procedure for @url{https://elm-lang.org, Elm} packages similar to
+@samp{elm install}.
+
+The build system adds an Elm compiler package to the set of inputs.  The
+default compiler package (currently @code{elm}) can be overridden
+using the @code{#:elm} argument.  Additionally, Elm packages needed by the
+build system itself are added as implicit inputs if they are not already
+present: to suppress this behavior, use the
+@code{#:implicit-elm-package-inputs?} argument, which is primarily useful for
+bootstrapping.
+
+The @code{"dependencies"} and @code{"test-dependencies"} in an Elm package's
+@file{elm.json} file correspond to @code{propagated-inputs} and @code{inputs},
+respectively.
+
+Elm requires a particular structure for package names: @pxref{Elm Packages}
+for more details, including utilities provided by @code{(guix build-system
+elm)}.
+
+There are currently a few noteworthy limitations to @code{elm-build-system}:
+
+@itemize
+@item
+The build system is focused on @dfn{packages} in the Elm sense of the word:
+Elm @dfn{projects} which declare @code{@{ "type": "package" @}} in their
+@file{elm.json} files.  Using @code{elm-build-system} to build Elm
+@dfn{applications} (which declare @code{@{ "type": "application" @}}) is
+possible, but requires ad-hoc modifications to the build phases.
+
+@item
+Elm supports multiple versions of a package coexisting simultaneously under
+@env{ELM_HOME}, but this does not yet work well with @code{elm-build-system}.
+This limitation primarily affects Elm applications, because they specify
+exact versions for their dependencies, whereas Elm packages specify supported
+version ranges.  As a workaround, you can use
+the @code{patch-application-dependencies} procedure provided by
+@code{(guix build elm-build-system)} to rewrite their @file{elm.json} files to
+refer to the package versions actually present in the build environment.
+Alternatively, Guix package transformations (@pxref{Defining Package
+Variants}) could be used to rewrite an application's entire dependency graph.
+
+@item
+We are not yet able to run tests for Elm projects because neither
+@url{https://github.com/mpizenberg/elm-test-rs, @command{elm-test-rs}} nor the
+Node.js-based @url{https://github.com/rtfeldman/node-test-runner,
+@command{elm-test}} runner has been packaged for Guix yet.
+@end itemize
+@end defvr
+
 @defvr {Scheme Variable} go-build-system
 This variable is exported by @code{(guix build-system go)}.  It
 implements a build procedure for Go packages using the standard
diff --git a/gnu/local.mk b/gnu/local.mk
index de044bdbff..94590ab5b5 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1024,6 +1024,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/einstein-build.patch			\
   %D%/packages/patches/elfutils-tests-ptrace.patch		\
   %D%/packages/patches/elixir-path-length.patch			\
+  %D%/packages/patches/elm-offline-package-registry.patch	\
   %D%/packages/patches/elm-reactor-static-files.patch		\
   %D%/packages/patches/elogind-revert-polkit-detection.patch	\
   %D%/packages/patches/emacs-exec-path.patch			\
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index a3863e6e6f..35bdcc65f5 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -25,6 +25,7 @@ (define-module (gnu packages elm)
   #:use-module (gnu packages haskell-xyz)
   #:use-module (gnu packages haskell-web)
   #:use-module (guix build-system haskell)
+  #:use-module (guix build-system elm)
   #:use-module (guix gexp)
   #:use-module (guix git-download)
   #:use-module ((guix licenses) #:prefix license:)
@@ -53,7 +54,8 @@ (define-public elm
        (sha256
         (base32 "1rdg3xp3js9xadclk3cdypkscm5wahgsfmm4ldcw3xswzhw6ri8w"))
        (patches
-        (search-patches "elm-reactor-static-files.patch"))))
+        (search-patches "elm-reactor-static-files.patch"
+                        "elm-offline-package-registry.patch"))))
     (build-system haskell-build-system)
     (arguments
      (list
diff --git a/gnu/packages/patches/elm-offline-package-registry.patch b/gnu/packages/patches/elm-offline-package-registry.patch
new file mode 100644
index 0000000000..761ec69878
--- /dev/null
+++ b/gnu/packages/patches/elm-offline-package-registry.patch
@@ -0,0 +1,71 @@
+From 06563409e6f2b1cca7bc1b27e31efd07a7569da8 Mon Sep 17 00:00:00 2001
+From: Philip McGrath <philip@philipmcgrath.com>
+Date: Thu, 14 Apr 2022 22:41:04 -0400
+Subject: [PATCH] minimal support for offline builds
+
+Normally, Elm performs HTTP requests before building to obtain or
+update its list of all registed packages and their versions.
+This is problematic in the Guix build environment.
+
+This patch causes Elm to check if the `GUIX_ELM_OFFLINE_REGISTRY_FILE`
+is set and, if so, to use the contents of the file it specifies as
+though it were the response from
+https://package.elm-lang.org/all-packages.
+
+This patch does not attempt to add more general support for offline
+builds. In particular, it does not attempt to support incremental
+updates to the package registry cache file. See also discussion at
+https://discourse.elm-lang.org/t/private-package-tool-spec/6779/25.
+---
+ builder/src/Deps/Registry.hs | 25 +++++++++++++++++++++----
+ 1 file changed, 21 insertions(+), 4 deletions(-)
+
+diff --git a/builder/src/Deps/Registry.hs b/builder/src/Deps/Registry.hs
+index 8d7def98..70cf3622 100644
+--- a/builder/src/Deps/Registry.hs
++++ b/builder/src/Deps/Registry.hs
+@@ -18,6 +18,8 @@ import Control.Monad (liftM2)
+ import Data.Binary (Binary, get, put)
+ import qualified Data.List as List
+ import qualified Data.Map.Strict as Map
++import System.Environment as Env
++import qualified Data.ByteString as BS
+ 
+ import qualified Deps.Website as Website
+ import qualified Elm.Package as Pkg
+@@ -190,13 +192,28 @@ getVersions' name (Registry _ versions) =
+ post :: Http.Manager -> String -> D.Decoder x a -> (a -> IO b) -> IO (Either Exit.RegistryProblem b)
+ post manager path decoder callback =
+   let
+-    url = Website.route path []
+-  in
+-  Http.post manager url [] Exit.RP_Http $
+-    \body ->
++    mkBodyCallback url body =
+       case D.fromByteString decoder body of
+         Right a -> Right <$> callback a
+         Left _ -> return $ Left $ Exit.RP_Data url body
++    postOnline url cb =
++      Http.post manager url [] Exit.RP_Http cb
++    performPost f url =
++      f url (mkBodyCallback url)
++  in
++    do
++      maybeFile <- Env.lookupEnv "GUIX_ELM_OFFLINE_REGISTRY_FILE"
++      case (path, maybeFile) of
++        ( "/all-packages", Just file ) ->
++          performPost postOffline file
++        ( _, _ ) ->
++          -- don't know how to handle other endpoints yet
++          performPost postOnline (Website.route path [])
++
++postOffline :: String -> (BS.ByteString -> IO a) -> IO a
++postOffline file callback = do
++  body <- BS.readFile file
++  callback body
+ 
+ 
+ 
+-- 
+2.32.0
+
diff --git a/guix/build-system/elm.scm b/guix/build-system/elm.scm
new file mode 100644
index 0000000000..b54954bf4e
--- /dev/null
+++ b/guix/build-system/elm.scm
@@ -0,0 +1,172 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 Philip McGrath <philip@philipmcgrath.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix build-system elm)
+  #:use-module (guix store)
+  #:use-module (guix utils)
+  #:use-module (guix packages)
+  #:use-module (guix gexp)
+  #:use-module (guix monads)
+  #:use-module (guix search-paths)
+  #:use-module (guix git-download)
+  #:use-module (guix build-system)
+  #:use-module (guix build-system gnu)
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-1)
+  #:export (elm->package-name
+            guix-package->elm-name
+            infer-elm-package-name
+            elm-package-origin
+            %elm-build-system-modules
+            %elm-default-modules
+            elm-build
+            elm-build-system))
+
+(define (elm->package-name name)
+  "Given the NAME of an Elm package, return a Guix-style package name."
+  (let ((converted
+         (string-join (string-split (string-downcase name) #\/) "-")))
+    (if (string-prefix? "elm-" converted)
+        converted
+        (string-append "elm-" converted))))
+
+(define (guix-package->elm-name package)
+  "Given an Elm PACKAGE, return the possibly-inferred upstream name, or #f the
+upstream name is not specified and can't be inferred."
+  (or (assoc-ref (package-properties package) 'upstream-name)
+      (infer-elm-package-name (package-name package))))
+
+(define (infer-elm-package-name guix-name)
+  "Given the GUIX-NAME of an Elm package, return the inferred upstream name,
+or #f if it can't be inferred.  If the result is not #f, supplying it to
+'elm->package-name' would produce GUIX-NAME.
+
+See also 'guix-package->elm-name', which respects the 'upstream-name'
+property."
+  (define (parts-join part0 parts)
+    (string-join (cons part0 parts) "-"))
+  (match (string-split guix-name #\-)
+    (("elm" "explorations" part0 parts ...)
+     (string-append "elm-explorations/"
+                    (parts-join part0 parts)))
+    (("elm" owner part0 parts ...)
+     (string-append owner "/" (parts-join part0 parts)))
+    (("elm" repo)
+     (string-append "elm/" repo))
+    (_
+     #f)))
+
+(define (elm-package-origin elm-name version hash)
+  "Return an origin for the Elm package with upstream name ELM-NAME at the
+given VERSION with sha256 checksum HASH."
+  ;; elm requires this very specific repository structure and tagging regime
+  (origin
+    (method git-fetch)
+    (uri (git-reference
+          (url (string-append "https://github.com/" elm-name))
+          (commit version)))
+    (file-name (git-file-name (elm->package-name elm-name) version))
+    (sha256 hash)))
+
+(define %elm-build-system-modules
+  ;; Build-side modules imported by default.
+  `((guix build elm-build-system)
+    (guix build json)
+    (guix build union)
+    ,@%gnu-build-system-modules))
+
+(define %elm-default-modules
+  ;; Modules in scope in the build-side environment.
+  '((guix build elm-build-system)
+    (guix build utils)
+    (guix build json)
+    (guix build union)))
+
+(define (default-elm)
+  "Return the default Elm package for builds."
+  ;; Lazily resolve the binding to avoid a circular dependency.
+  (let ((elm (resolve-interface '(gnu packages elm))))
+    (module-ref elm 'elm)))
+
+(define* (lower name
+                #:key source inputs native-inputs outputs system target
+                (implicit-elm-package-inputs? #t)
+                (elm (default-elm))
+                #:allow-other-keys
+                #:rest arguments)
+  "Return a bag for NAME."
+  (define private-keywords
+    '(#:target #:implicit-elm-package-inputs? #:elm #:inputs #:native-inputs))
+  (cond
+   (target
+    ;; Cross-compilation is not yet supported.  It should be easy, though,
+    ;; since the build products are all platform-independent.
+    #f)
+   (else
+    (bag
+      (name name)
+      (system system)
+      (host-inputs
+       `(,@(if source
+               `(("source" ,source))
+               '())
+         ,@inputs
+         ("elm" ,elm)
+         ;; TODO: probably don't need most of (standard-packages)
+         ,@(standard-packages)))
+      (outputs outputs)
+      (build elm-build)
+      (arguments (strip-keyword-arguments private-keywords arguments))))))
+
+(define* (elm-build name inputs
+                    #:key
+                    source
+                    (tests? #t)
+                    (phases '%standard-phases)
+                    (outputs '("out"))
+                    (search-paths '())
+                    (system (%current-system))
+                    (guile #f)
+                    (imported-modules %elm-build-system-modules)
+                    (modules %elm-default-modules))
+  "Build SOURCE using ELM."
+  (define builder
+    (with-imported-modules imported-modules
+      #~(begin
+          (use-modules #$@(sexp->gexp modules))
+          (elm-build #:name #$name
+                     #:source #+source
+                     #:system #$system
+                     #:tests? #$tests?
+                     #:phases #$phases
+                     #:outputs #$(outputs->gexp outputs)
+                     #:search-paths '#$(sexp->gexp
+                                        (map search-path-specification->sexp
+                                             search-paths))
+                     #:inputs #$(input-tuples->gexp inputs)))))
+  (mlet %store-monad ((guile (package->derivation (or guile (default-guile))
+                                                  system #:graft? #f)))
+    (gexp->derivation name builder
+                      #:system system
+                      #:guile-for-build guile)))
+
+(define elm-build-system
+  (build-system
+    (name 'elm)
+    (description "The Elm build system")
+    (lower lower)))
diff --git a/guix/build/elm-build-system.scm b/guix/build/elm-build-system.scm
new file mode 100644
index 0000000000..02d7c029dd
--- /dev/null
+++ b/guix/build/elm-build-system.scm
@@ -0,0 +1,380 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 Philip McGrath <philip@philipmcgrath.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix build elm-build-system)
+  #:use-module ((guix build gnu-build-system) #:prefix gnu:)
+  #:use-module (guix build utils)
+  #:use-module (guix build json)
+  #:use-module (guix build union)
+  #:use-module (ice-9 ftw)
+  #:use-module (ice-9 rdelim)
+  #:use-module (ice-9 regex)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 popen)
+  #:use-module (ice-9 vlist)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-26)
+  #:use-module (srfi srfi-71)
+  #:export (%standard-phases
+            patch-application-dependencies
+            patch-json-string-escapes
+            read-offline-registry->vhash
+            elm-build))
+
+;;; Commentary:
+;;;
+;;; Elm draws a sharp distinction between "projects" with `{"type":"package"}`
+;;; vs. `{"type":"application"}` in the "elm.json" file: see
+;;; <https://github.com/elm/compiler/blob/master/docs/elm.json/package.md> and
+;;; <https://github.com/elm/compiler/blob/master/docs/elm.json/application.md>.
+;;; For now, `elm-build-system` is designed for "package"s: packaging
+;;; "application"s requires ad-hoc replacements for some phases---but see
+;;; `patch-application-dependencies`, which helps to work around a known issue
+;;; discussed below.  It would be nice to add more streamlined support for
+;;; "application"s one we have more experience building them in Guix.  For
+;;; example, we could incorporate the `uglifyjs` advice from
+;;; <https://github.com/elm/compiler/blob/master/hints/optimize.md>.
+;;;
+;;; We want building an Elm "package" to produce:
+;;;
+;;;   - a "docs.json" file with extracted documentation; and
+;;;
+;;;   - an "artifacts.dat" file with compilation results for use in building
+;;;     "package"s and "application"s.
+;;;
+;;; Unfortunately, there isn't an entry point to the Elm compiler that builds
+;;; those files directly.  Building with `elm make` does something different,
+;;; more oriented toward development, testing, and building "application"s.
+;;; We work around this limitation by staging the "package" we're building as
+;;; though it were already installed in ELM_HOME, generating a trivial Elm
+;;; "application" that depends on the "package", and building the
+;;; "application", which causes the files for the "package" to be built.
+;;;
+;;; Much of the ceremony involved is to avoid using `elm` in ways that would
+;;; make it try to do network IO beyond the bare minimum functionality for
+;;; which we've patched a replacement into our `elm`.  On the other hand, we
+;;; get to take advantage of the very regular structure required of Elm
+;;; packages.
+;;;
+;;; *Known issue:* Elm itself supports multiple versions of "package"s
+;;; coexisting simultaneously under ELM_HOME, but we do not support this yet.
+;;; Sometimes, parallel versions coexisting causes `elm` to try to write to
+;;; built "artifacts.dat" files.  For now, two workarounds are possible:
+;;;
+;;;  - Use `patch-application-dependencies` to rewrite an "application"'s
+;;;    "elm.json" file to refer to the versions of its inputs actually
+;;;    packaged in Guix.
+;;;
+;;;  - Use a Guix package transformation to rewrite your "application"'s
+;;;    dependencies recursively, so that only one version of each Elm
+;;;    "package" is included in your "application"'s build environment.
+;;;
+;;; Patching `elm` more extensively---perhaps adding an `elm guix`
+;;; subcommand`---might let us address these issues more directly.
+;;;
+;;; Code:
+;;;
+
+(define %essential-elm-packages
+  ;; elm/json isn't essential in a fundamental sense,
+  ;; but it's required for a {"type":"application"},
+  ;; which we are generating to trigger the build
+  '("elm/core" "elm/json"))
+
+(define* (target-elm-version #:optional elm)
+  "Return the version of ELM or whichever 'elm' is in $PATH.
+Return #false if it cannot be determined."
+  (let* ((pipe (open-pipe* OPEN_READ
+                           (or elm "elm")
+                           "--version"))
+         (line (read-line pipe)))
+    (and (zero? (close-pipe pipe))
+         (string? line)
+         line)))
+
+(define* (prepare-elm-home #:key native-inputs inputs #:allow-other-keys)
+  "Set the ELM_HOME environment variable and populate the indicated directory
+with the union of the Elm \"package\" inputs.  Also, set GUIX_ELM_VERSION to
+the version of the Elm compiler in use."
+  (let* ((elm (search-input-file (or native-inputs inputs) "/bin/elm"))
+         (elm-version (target-elm-version elm)))
+    (setenv "GUIX_ELM_VERSION" elm-version)
+    (mkdir "../elm-home")
+    (with-directory-excursion "../elm-home"
+      (union-build elm-version
+                   (search-path-as-list
+                    (list (string-append "share/elm/" elm-version))
+                    (map cdr inputs))
+                   #:create-all-directories? #t)
+      (setenv "ELM_HOME" (getcwd)))))
+
+(define* (stage #:key native-inputs inputs  #:allow-other-keys)
+  "Extract the installable files from the Elm \"package\" into a staging
+directory and link it into the ELM_HOME tree.  Also, set GUIX_ELM_PKG_NAME and
+GUIX_ELM_PKG_VERSION to the name and version, respectively, of the Elm package
+being built, as defined in its \"elm.json\" file."
+  (let* ((elm-version (getenv "GUIX_ELM_VERSION"))
+         (elm-home (getenv "ELM_HOME"))
+         (info (match (call-with-input-file "elm.json" read-json)
+                 (('@ . alist) alist)))
+         (name (assoc-ref info "name"))
+         (version (assoc-ref info "version"))
+         (rel-dir (string-append elm-version "/packages/" name "/" version))
+         (staged-dir (string-append elm-home "/../staged/" rel-dir)))
+    (setenv "GUIX_ELM_PKG_NAME" name)
+    (setenv "GUIX_ELM_PKG_VERSION" version)
+    (mkdir-p staged-dir)
+    (mkdir-p (string-append elm-home "/" (dirname rel-dir)))
+    (symlink staged-dir
+             (string-append elm-home "/" rel-dir))
+    (copy-recursively "src" (string-append staged-dir "/src"))
+    (install-file "elm.json" staged-dir)
+    (install-file "README.md" staged-dir)
+    (when (file-exists? "LICENSE")
+      (install-file "LICENSE" staged-dir))))
+
+(define (patch-json-string-escapes file)
+  "Work around a bug in the Elm compiler's JSON parser by attempting to
+replace REVERSE-SOLIDUS--SOLIDUS escape sequences in FILE with unescaped
+SOLIDUS characters."
+  ;; https://github.com/elm/compiler/issues/2255
+  (substitute* file
+    (("\\\\/")
+     "/")))
+
+(define (directory-list dir)
+  "Like DIRECTORY-LIST from 'racket/base': lists the contents of DIR, not
+including the special \".\" and \"..\" entries."
+  (scandir dir (lambda (f)
+                 (not (member f '("." ".."))))))
+
+(define* (make-offline-registry-file #:key inputs #:allow-other-keys)
+  "Generate an \"offline-package-registry.json\" file and set
+GUIX_ELM_OFFLINE_REGISTRY_FILE to its path, cooperating with a patch to `elm`
+to avoid attempting to download a list of all published Elm package names and
+versions from the internet."
+  (let* ((elm-home (getenv "ELM_HOME"))
+         (elm-version (getenv "GUIX_ELM_VERSION"))
+         (registry-file
+          (string-append elm-home "/../offline-package-registry.json"))
+         (registry-alist
+          ;; here, we don't need to look up entries, so we build the
+          ;; alist directly, rather than using a vhash
+          (with-directory-excursion
+              (string-append elm-home "/" elm-version "/packages")
+            (append-map (lambda (org)
+                          (with-directory-excursion org
+                            (map (lambda (repo)
+                                   (cons (string-append org "/" repo)
+                                         (directory-list repo)))
+                                 (directory-list "."))))
+                        (directory-list ".")))))
+    (call-with-output-file registry-file
+      (lambda (out)
+        (write-json `(@ ,@registry-alist) out)))
+    (patch-json-string-escapes registry-file)
+    (setenv "GUIX_ELM_OFFLINE_REGISTRY_FILE" registry-file)))
+
+(define (read-offline-registry->vhash)
+  "Return a vhash mapping Elm \"package\" names to lists of available version
+strings."
+  (alist->vhash
+   (match (call-with-input-file (getenv "GUIX_ELM_OFFLINE_REGISTRY_FILE")
+            read-json)
+     (('@ . alist) alist))))
+
+(define (find-indirect-dependencies registry-vhash root-pkg root-version)
+  "Return the recursive dependencies of ROOT-PKG, an Elm \"package\" name, at
+version ROOT-VERSION as an alist mapping Elm \"package\" names to (single)
+versions.  The resulting alist will not include entries for
+%ESSENTIAL-ELM-PACKAGES or for ROOT-PKG itself.  The REGISTRY-VHASH is used in
+conjunction with the ELM_HOME environment variable to find dependencies."
+  (with-directory-excursion
+      (string-append (getenv "ELM_HOME")
+                     "/" (getenv "GUIX_ELM_VERSION")
+                     "/packages")
+    (define (get-dependencies pkg version acc)
+      (let* ((elm-json-alist
+              (match (call-with-input-file
+                         (string-append pkg "/" version "/elm.json")
+                       read-json)
+                (('@ . alist) alist)))
+             (deps-alist
+              (match (assoc-ref elm-json-alist "dependencies")
+                (('@ . alist) alist)))
+             (deps-names
+              (filter-map (match-lambda
+                            ((name . range)
+                             (and (not (member name %essential-elm-packages))
+                                  name)))
+                          deps-alist)))
+        (fold register-dependency acc deps-names)))
+    (define (register-dependency pkg acc)
+      ;; Using vhash-cons unconditionally would add duplicate entries,
+      ;; which would then cause problems when we must emit JSON.
+      ;; Plus, we can avoid needlessly duplicating work.
+      (if (vhash-assoc pkg acc)
+          acc
+          (match (vhash-assoc pkg registry-vhash)
+            ((_ version . _)
+             ;; in the rare case that multiple versions are present,
+             ;; just picking an arbitrary one seems to work well enough for now
+             (get-dependencies pkg version (vhash-cons pkg version acc))))))
+    (vlist->list
+     (get-dependencies root-pkg root-version vlist-null))))
+
+(define* (patch-application-dependencies #:key inputs #:allow-other-keys)
+  "Rewrites the \"elm.json\" file in the working directory---which must be of
+`\"type\":\"application\"`, not `\"type\":\"package\"`---to refer to the
+dependency versions actually provided via Guix.  The
+GUIX_ELM_OFFLINE_REGISTRY_FILE environment variable is used to find available
+versions."
+  (let* ((registry-vhash (read-offline-registry->vhash))
+         (rewrite-dep-version
+          (match-lambda
+            ((name . _)
+             (cons name (match (vhash-assoc name registry-vhash)
+                          ((_ version) ;; no dot
+                           version))))))
+         (rewrite-direct/indirect
+          (match-lambda
+            ;; a little checking to avoid confusing misuse with "package"
+            ;; project dependencies, which have a different shape
+            (((and key (or "direct" "indirect"))
+              '@ . alist)
+             `(,key @ ,@(map rewrite-dep-version alist)))))
+         (rewrite-json-section
+          (match-lambda
+            (((and key (or "dependencies" "test-dependencies"))
+              '@ . alist)
+             `(,key @ ,@(map rewrite-direct/indirect alist)))
+            ((k . v)
+             (cons k v))))
+         (rewrite-elm-json
+          (match-lambda
+            (('@ . alist)
+             `(@ ,@(map rewrite-json-section alist))))))
+    (with-atomic-file-replacement "elm.json"
+      (lambda (in out)
+        (write-json (rewrite-elm-json (read-json in))
+                    out)))
+    (patch-json-string-escapes "elm.json")))
+
+(define* (configure #:key native-inputs inputs #:allow-other-keys)
+  "Generate a trivial Elm \"application\" with a direct dependency on the Elm
+\"package\" currently being built."
+  (let* ((info (match (call-with-input-file "elm.json" read-json)
+                 (('@ . alist) alist)))
+         (name (getenv "GUIX_ELM_PKG_NAME"))
+         (version (getenv "GUIX_ELM_PKG_VERSION"))
+         (elm-home (getenv "ELM_HOME"))
+         (registry-vhash (read-offline-registry->vhash))
+         (app-dir (string-append elm-home "/../fake-app")))
+    (mkdir-p (string-append app-dir "/src"))
+    (with-directory-excursion app-dir
+      (call-with-output-file "elm.json"
+        (lambda (out)
+          (write-json
+           `(@ ("type" . "application")
+               ("source-directories" "src") ;; intentionally no dot
+               ("elm-version" . ,(getenv "GUIX_ELM_VERSION"))
+               ("dependencies"
+                @ ("direct"
+                   @ ,@(map (lambda (pkg)
+                              (match (vhash-assoc pkg registry-vhash)
+                                ((_ pkg-version . _)
+                                 (cons pkg
+                                       (if (equal? pkg name)
+                                           version
+                                           pkg-version)))))
+                            (if (member name %essential-elm-packages)
+                                %essential-elm-packages
+                                (cons name %essential-elm-packages))))
+                  ("indirect"
+                   @ ,@(if (member name %essential-elm-packages)
+                           '()
+                           (find-indirect-dependencies registry-vhash
+                                                       name
+                                                       version))))
+               ("test-dependencies"
+                @ ("direct" @)
+                  ("indirect" @)))
+           out)))
+      (patch-json-string-escapes  "elm.json")
+      (with-output-to-file "src/Main.elm"
+        ;; the most trivial possible elm program
+        (lambda ()
+          (display "module Main exposing (..)
+main : Program () () ()
+main = Platform.worker
+ { init = \\_ -> ( (), Cmd.none )
+ , update = \\_ -> \\_ -> ( (), Cmd.none )
+ , subscriptions = \\_ -> Sub.none }"))))))
+
+(define* (build #:key native-inputs inputs #:allow-other-keys)
+  "Run `elm make` to build the Elm \"application\" generated by CONFIGURE."
+  (with-directory-excursion (string-append (getenv "ELM_HOME") "/../fake-app")
+    (invoke (search-input-file (or native-inputs inputs) "/bin/elm")
+            "make"
+            "src/Main.elm")))
+
+(define* (check #:key tests? #:allow-other-keys)
+  "Does nothing, because the `elm-test` executable has not yet been packaged
+for Guix."
+  (when tests?
+    (display "elm-test has not yet been packaged for Guix\n")))
+
+(define* (install #:key outputs #:allow-other-keys)
+  "Installs the contents of the directory generated by STAGE, including any
+files added by BUILD, to the Guix package output."
+  (copy-recursively
+   (string-append (getenv "ELM_HOME") "/../staged")
+   (string-append (assoc-ref outputs "out") "/share/elm")))
+
+(define* (validate-compiled #:key outputs #:allow-other-keys)
+  "Checks that the files \"artifacts.dat\" and \"docs.json\" have been
+installed."
+  (let ((base (string-append "/share/elm/"
+                             (getenv "GUIX_ELM_VERSION")
+                             "/packages/"
+                             (getenv "GUIX_ELM_PKG_NAME")
+                             "/"
+                             (getenv "GUIX_ELM_PKG_VERSION")))
+        (expected '("artifacts.dat" "docs.json")))
+    (for-each (lambda (name)
+                (search-input-file outputs (string-append base "/" name)))
+              expected)))
+
+(define %standard-phases
+  (modify-phases gnu:%standard-phases
+    (add-after 'unpack 'prepare-elm-home prepare-elm-home)
+    (delete 'bootstrap)
+    (add-after 'patch-source-shebangs 'stage stage)
+    (add-after 'stage 'make-offline-registry-file make-offline-registry-file)
+    (replace 'configure configure)
+    (delete 'patch-generated-file-shebangs)
+    (replace 'build build)
+    (replace 'check check)
+    (replace 'install install)
+    (add-before 'validate-documentation-location 'validate-compiled
+      validate-compiled)))
+
+(define* (elm-build #:key inputs (phases %standard-phases)
+                    #:allow-other-keys #:rest args)
+  "Builds the given Elm project, applying all of the PHASES in order."
+  (apply gnu:gnu-build #:inputs inputs #:phases phases args))
diff --git a/tests/elm.scm b/tests/elm.scm
new file mode 100644
index 0000000000..96f958f060
--- /dev/null
+++ b/tests/elm.scm
@@ -0,0 +1,97 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 Philip McGrath <philip@philipmcgrath.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (test-elm)
+  #:use-module (guix build-system elm)
+  #:use-module (srfi srfi-64))
+
+(test-begin "elm")
+
+(test-group "elm->package-name and infer-elm-package-name"
+  (test-group "round trip"
+    ;; Cases when our heuristics can find the upstream name.
+    (define-syntax-rule (test-round-trip elm guix)
+      (test-group elm
+        (test-equal "elm->package-name" guix
+                    (elm->package-name elm))
+        (test-equal "infer-elm-package-name" elm
+                    (infer-elm-package-name guix))))
+    (test-round-trip "elm/core" "elm-core")
+    (test-round-trip "elm/html" "elm-html")
+    (test-round-trip "elm-explorations/markdown" "elm-explorations-markdown")
+    (test-round-trip "elm-explorations/test" "elm-explorations-test")
+    (test-round-trip "elm-explorations/foo-bar" "elm-explorations-foo-bar")
+    (test-round-trip "elm/explorations" "elm-explorations")
+    (test-round-trip "terezka/intervals" "elm-terezka-intervals")
+    (test-round-trip "justinmimbs/time-extra" "elm-justinmimbs-time-extra")
+    (test-round-trip "danhandrea/elm-date-format"
+                     "elm-danhandrea-elm-date-format"))
+  (test-group "upstream-name needed"
+    ;; Upstream names that our heuristic can't infer.  We still check that the
+    ;; round-trip behavior of 'infer-elm-package-name' works as promised for
+    ;; the hypothetical Elm name it doesn't infer.
+    (define-syntax-rule (test-upstream-needed elm guix inferred)
+      (test-group elm
+        (test-equal "elm->package-name" guix
+                    (elm->package-name elm))
+        (test-group "infer-elm-package-name"
+          (test-equal "infers other name" inferred
+                      (infer-elm-package-name guix))
+          (test-equal "infered name round-trips" guix
+                      (elm->package-name inferred)))))
+    (test-upstream-needed "elm/virtual-dom"
+                          "elm-virtual-dom"
+                          "virtual/dom")
+    (test-upstream-needed "elm/project-metadata-utils"
+                          "elm-project-metadata-utils"
+                          "project/metadata-utils")
+    (test-upstream-needed "explorations/foo"
+                          "elm-explorations-foo"
+                          "elm-explorations/foo")
+    (test-upstream-needed "explorations/foo-bar"
+                          "elm-explorations-foo-bar"
+                          "elm-explorations/foo-bar")
+    (test-upstream-needed "explorations-central/foo"
+                          "elm-explorations-central-foo"
+                          "elm-explorations/central-foo")
+    (test-upstream-needed "explorations-central/foo-bar"
+                          "elm-explorations-central-foo-bar"
+                          "elm-explorations/central-foo-bar")
+    (test-upstream-needed "elm-xyz/foo"
+                          "elm-xyz-foo"
+                          "xyz/foo")
+    (test-upstream-needed "elm-xyz/foo-bar"
+                          "elm-xyz-foo-bar"
+                          "xyz/foo-bar")
+    (test-upstream-needed "elm-explorations-xyz/foo"
+                          "elm-explorations-xyz-foo"
+                          "elm-explorations/xyz-foo")
+    (test-upstream-needed "elm-explorations-xyz/foo-bar"
+                          "elm-explorations-xyz-foo-bar"
+                          "elm-explorations/xyz-foo-bar"))
+  (test-group "no inferred Elm name"
+    ;; Cases that 'infer-elm-package-name' should not attempt to handle,
+    ;; because 'elm->package-name' would never produce such names.
+    (define-syntax-rule (test-not-inferred guix)
+      (test-assert guix (not (infer-elm-package-name guix))))
+    (test-not-inferred "elm")
+    (test-not-inferred "guile")
+    (test-not-inferred "gcc-toolchain")
+    (test-not-inferred "font-adobe-source-sans-pro")))
+
+(test-end "elm")
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:10 +0200
[PATCH v2 04/34] gnu: Add elm-core and elm-json.
(address . 55030@debbugs.gnu.org)
44d6846eec4e21cb7016e64b6d9cc52ae101e511.1652890702.git.philip@philipmcgrath.com
Both of these packages are needed for 'elm-build-system' to work.

* gnu/packages/elm.scm (elm-core, elm-json, elm-json-bootstrap): New
variables.
---
gnu/packages/elm.scm | 55 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)

Toggle diff (65 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 35bdcc65f5..4ad3a000cb 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -103,3 +103,58 @@ (define-public elm
 ;; The 'elm' package used to be called 'elm-compiler'.
 (define-public elm-compiler
   (deprecated-package "elm-compiler" elm))
+
+(define-public elm-core
+  (package
+    (name "elm-core")
+    (version "1.0.5")
+    (source
+     (elm-package-origin
+      "elm/core"
+      version
+      (base32 "0g3xbi8f9k5q45s95nx3jfvzwdf4b2n63a52wr4027d2xjx0pmvl")))
+    (build-system elm-build-system)
+    (inputs (list elm-json-bootstrap))
+    (arguments
+     (list #:implicit-elm-package-inputs? #f))
+    (home-page "https://package.elm-lang.org/packages/elm/core/1.0.5")
+    (synopsis "Elm's standard libraries")
+    (description "Every Elm project needs this package!")
+    (license license:bsd-3)))
+
+(define-public elm-json
+  (package
+    (name "elm-json")
+    (version "1.1.3")
+    (source
+     (elm-package-origin
+      "elm/json"
+      version
+      (base32 "1hx986yqw1v2bpkrh6brszl8n8awwg1s8zi7v5qg0p1rqwvjlicz")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-core))
+    (arguments
+     (list #:implicit-elm-package-inputs? #f))
+    (home-page "https://package.elm-lang.org/packages/elm/json/1.1.3")
+    (synopsis "Encode and decode JSON values in Elm")
+    (description
+     "This package helps you convert between Elm values and JSON values.")
+    (license license:bsd-3)))
+
+(define-public elm-json-bootstrap
+  ;; elm/core doesn't depend on elm/json,
+  ;; but elm-build-system's strategy for building it
+  ;; (and everything else) does
+  (hidden-package
+   (package
+     (inherit elm-json)
+     (name "elm-json-bootstrap")
+     (properties '((upstream-name . "elm/json")))
+     (propagated-inputs '())
+     (arguments
+      (list #:phases
+            #~(modify-phases %standard-phases
+                (delete 'configure)
+                (delete 'build)
+                (delete 'validate-compiled))
+            #:implicit-elm-package-inputs? #f)))))
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:10 +0200
[PATCH v2 05/34] build-system/elm: Add implicit Elm inputs.
(address . 55030@debbugs.gnu.org)
a0eea4de284cf7a2b36a556f545624c90543583f.1652890702.git.philip@philipmcgrath.com
* guix/build-system/elm.scm (default-elm-core): New variable.
(default-elm-json): Likewise
(lower): Use them as implicit inputs when applicable.
---
guix/build-system/elm.scm | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)

Toggle diff (54 lines)
diff --git a/guix/build-system/elm.scm b/guix/build-system/elm.scm
index b54954bf4e..293bcbfb64 100644
--- a/guix/build-system/elm.scm
+++ b/guix/build-system/elm.scm
@@ -103,6 +103,18 @@ (define (default-elm)
   (let ((elm (resolve-interface '(gnu packages elm))))
     (module-ref elm 'elm)))
 
+(define (default-elm-core)
+  "Return the default elm-core package."
+  ;; Lazily resolve the binding to avoid a circular dependency.
+  (let ((elm (resolve-interface '(gnu packages elm))))
+    (module-ref elm 'elm-core)))
+
+(define (default-elm-json)
+  "Return the default elm-json package."
+  ;; Lazily resolve the binding to avoid a circular dependency.
+  (let ((elm (resolve-interface '(gnu packages elm))))
+    (module-ref elm 'elm-json)))
+
 (define* (lower name
                 #:key source inputs native-inputs outputs system target
                 (implicit-elm-package-inputs? #t)
@@ -127,6 +139,28 @@ (define* (lower name
                '())
          ,@inputs
          ("elm" ,elm)
+         ,@(cond
+            (implicit-elm-package-inputs?
+             ;; These are needed for elm-build-system even if not actually
+             ;; needed by the package being built.  But "elm/json" is often
+             ;; present in practice, and "elm/core" always is: only add the
+             ;; default packages if no suitable inputs have been given
+             ;; explicitly.
+             (filter-map
+              (match-lambda
+                ((name get-default)
+                 (cond
+                  ((find (match-lambda
+                           ((_ pkg . _)
+                            (equal? name (guix-package->elm-name pkg))))
+                         inputs)
+                   #f)
+                  (else
+                   `(,name ,(get-default))))))
+              `(("elm/core" ,default-elm-core)
+                ("elm/json" ,default-elm-json))))
+            (else
+             '()))
          ;; TODO: probably don't need most of (standard-packages)
          ,@(standard-packages)))
       (outputs outputs)
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:10 +0200
[PATCH v2 06/34] http-client: Accept '#:headers' in 'http-fetched/cached'.
(address . 55030@debbugs.gnu.org)
c748a6704454efd70211544bd7b87df7a13c6332.1652890702.git.philip@philipmcgrath.com
Callers can supply alternative headers as with 'http-fetch'.

* guix/http-client.scm (http-fetch/cached): Add '#:headers' argument.
---
guix/http-client.scm | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)

Toggle diff (53 lines)
diff --git a/guix/http-client.scm b/guix/http-client.scm
index a367c41afa..699f5dfd57 100644
--- a/guix/http-client.scm
+++ b/guix/http-client.scm
@@ -296,6 +296,7 @@ (define (cache-file-for-uri uri)
                                   #f #f base64url-alphabet))))
 
 (define* (http-fetch/cached uri #:key (ttl (%http-cache-ttl)) text?
+                            (headers '((user-agent . "GNU Guile")))
                             (write-cache dump-port)
                             (cache-miss (const #t))
                             (log-port (current-error-port))
@@ -307,6 +308,9 @@ (define* (http-fetch/cached uri #:key (ttl (%http-cache-ttl)) text?
 the data to cache.  Call CACHE-MISS with URI just before fetching data from
 URI.
 
+HEADERS is an alist of extra HTTP headers, to which cache-related headers are
+added automatically as appropriate.
+
 TIMEOUT specifies the timeout in seconds for connection establishment.
 
 Write information about redirects to LOG-PORT."
@@ -316,12 +320,12 @@ (define* (http-fetch/cached uri #:key (ttl (%http-cache-ttl)) text?
         (and cache-port
              (stat:mtime (stat cache-port))))
 
-      (define headers
-        `((user-agent . "GNU Guile")
-          ,@(if cache-time
-                `((if-modified-since
-                   . ,(time-utc->date (make-time time-utc 0 cache-time))))
-                '())))
+      (define extended-headers
+        (if cache-time
+            `((if-modified-since
+               . ,(time-utc->date (make-time time-utc 0 cache-time)))
+              ,@headers)
+            headers))
 
       ;; Update the cache and return an input port.
       (guard (c ((http-get-error? c)
@@ -332,7 +336,8 @@ (define* (http-fetch/cached uri #:key (ttl (%http-cache-ttl)) text?
                      (raise c))))
         (let ((port (http-fetch uri #:text? text?
                                 #:log-port log-port
-                                #:headers headers #:timeout timeout)))
+                                #:headers extended-headers
+                                #:timeout timeout)))
           (cache-miss uri)
           (mkdir-p (dirname file))
           (when cache-port
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:10 +0200
[PATCH v2 07/34] http-client: 'http-fetch/cached' converts strings to URIs.
(address . 55030@debbugs.gnu.org)
ee055b3aedca9be03bce0e0e6723ea43ef249fc4.1652890702.git.philip@philipmcgrath.com
* guix/http-client.scm (http-fetch/cached): Use 'string->uri' if URI is
a string, as with 'http-fetch'.
---
guix/http-client.scm | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

Toggle diff (18 lines)
diff --git a/guix/http-client.scm b/guix/http-client.scm
index 699f5dfd57..9138a627ac 100644
--- a/guix/http-client.scm
+++ b/guix/http-client.scm
@@ -314,7 +314,10 @@ (define* (http-fetch/cached uri #:key (ttl (%http-cache-ttl)) text?
 TIMEOUT specifies the timeout in seconds for connection establishment.
 
 Write information about redirects to LOG-PORT."
-  (let ((file (cache-file-for-uri uri)))
+  (let* ((uri (if (string? uri)
+                  (string->uri uri)
+                  uri))
+         (file (cache-file-for-uri uri)))
     (define (update-cache cache-port)
       (define cache-time
         (and cache-port
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:10 +0200
[PATCH v2 08/34] import: json: Accept '#:http-fetch' in 'json-fetch'.
(address . 55030@debbugs.gnu.org)
a7003bc883f6a95d53b24c175c8dccbfbf5a5e0e.1652890702.git.philip@philipmcgrath.com
For example, supplying 'http-fetch/cached' would enable caching.

* guix/import/json.scm (json-fetch): Add '#:http-fetch' argument.
---
guix/import/json.scm | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)

Toggle diff (26 lines)
diff --git a/guix/import/json.scm b/guix/import/json.scm
index 0c98bb25b8..ae00ee929e 100644
--- a/guix/import/json.scm
+++ b/guix/import/json.scm
@@ -35,13 +35,16 @@ (define-module (guix import json)
             json->scheme-file))
 
 (define* (json-fetch url
+                     #:key
+                     (http-fetch http-fetch)
                      ;; Note: many websites returns 403 if we omit a
                      ;; 'User-Agent' header.
-                     #:key (headers `((user-agent . "GNU Guile")
-                                      (Accept . "application/json"))))
+                     (headers `((user-agent . "GNU Guile")
+                                (Accept . "application/json"))))
   "Return a representation of the JSON resource URL (a list or hash table), or
 #f if URL returns 403 or 404.  HEADERS is a list of HTTP headers to pass in
-the query."
+the query.  HTTP-FETCH is called to perform the request: for example, to
+enable caching, supply 'http-fetch/cached'."
   (guard (c ((and (http-get-error? c)
                   (let ((error (http-get-error-code c)))
                     (or (= 403 error)
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:10 +0200
[PATCH v2 09/34] import: Add Elm importer.
(address . 55030@debbugs.gnu.org)
04fbe538a0ce1566381ed270987127c461c28b73.1652890702.git.philip@philipmcgrath.com
* guix/import/elm.scm, guix/scripts/import/elm.scm: New files.
* Makefile.am (MODULES): Add them.
* guix/scripts/import.scm (importers): Add "elm".
* doc/guix.texi (Invoking guix import): Document Elm importer.
* doc/contributing.texi (Elm Packages): Mention it.
* tests/elm.scm ("(guix import elm)"): New test group.
---
Makefile.am | 2 +
doc/contributing.texi | 4 +-
doc/guix.texi | 25 +++++
guix/import/elm.scm | 210 ++++++++++++++++++++++++++++++++++++
guix/scripts/import.scm | 3 +-
guix/scripts/import/elm.scm | 107 ++++++++++++++++++
tests/elm.scm | 171 +++++++++++++++++++++++++++++
7 files changed, 519 insertions(+), 3 deletions(-)
create mode 100644 guix/import/elm.scm
create mode 100644 guix/scripts/import/elm.scm

Toggle diff (610 lines)
diff --git a/Makefile.am b/Makefile.am
index 9ca92c407c..5a42bb90b2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -259,6 +259,7 @@ MODULES =					\
   guix/import/cran.scm				\
   guix/import/crate.scm				\
   guix/import/egg.scm   			\
+  guix/import/elm.scm				\
   guix/import/elpa.scm   			\
   guix/import/gem.scm				\
   guix/import/git.scm                           \
@@ -310,6 +311,7 @@ MODULES =					\
   guix/scripts/import/crate.scm			\
   guix/scripts/import/cran.scm			\
   guix/scripts/import/egg.scm   		\
+  guix/scripts/import/elm.scm			\
   guix/scripts/import/elpa.scm  		\
   guix/scripts/import/gem.scm			\
   guix/scripts/import/gnu.scm			\
diff --git a/doc/contributing.texi b/doc/contributing.texi
index 555b9bb961..2354874cb0 100644
--- a/doc/contributing.texi
+++ b/doc/contributing.texi
@@ -919,8 +919,8 @@ Elm Packages
 In many cases we can reconstruct an Elm package's upstream name heuristically,
 but, since conversion to a Guix-style name involves a loss of information,
 this is not always possible.  Care should be taken to add the
-@code{'upstream-name} property when necessary so that tools
-will work correctly. The most notable scenarios
+@code{'upstream-name} property when necessary so that @samp{guix import elm}
+will work correctly (@pxref{Invoking guix import}). The most notable scenarios
 when explicitly specifying the upstream name is necessary are:
 
 @enumerate
diff --git a/doc/guix.texi b/doc/guix.texi
index 63fb647045..d7bc7523cd 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -13157,6 +13157,31 @@ Invoking guix import
 in Guix.
 @end table
 
+@item elm
+@cindex elm
+Import metadata from the Elm package repository
+@uref{https://package.elm-lang.org, package.elm-lang.org}, as in this example:
+
+@example
+guix import elm elm-explorations/webgl
+@end example
+
+The Elm importer also allows you to specify a version string:
+
+@example
+guix import elm elm-explorations/webgl@@1.1.3
+@end example
+
+Additional options include:
+
+@table @code
+@item --recursive
+@itemx -r
+Traverse the dependency graph of the given upstream package recursively
+and generate package expressions for all those packages that are not yet
+in Guix.
+@end table
+
 @item opam
 @cindex OPAM
 @cindex OCaml
diff --git a/guix/import/elm.scm b/guix/import/elm.scm
new file mode 100644
index 0000000000..74902b8617
--- /dev/null
+++ b/guix/import/elm.scm
@@ -0,0 +1,210 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 Philip McGrath <philip@philipmcgrath.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix import elm)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 regex)
+  #:use-module (ice-9 vlist)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-11)
+  #:use-module (srfi srfi-26)
+  #:use-module (srfi srfi-34)
+  #:use-module (srfi srfi-35)
+  #:use-module (guix utils)
+  #:use-module (guix base32)
+  #:use-module (guix hash)
+  #:use-module (guix http-client)
+  #:use-module (guix memoization)
+  #:use-module (guix diagnostics)
+  #:use-module (guix i18n)
+  #:use-module ((guix ui) #:select (display-hint))
+  #:use-module ((guix build utils)
+                #:select ((package-name->name+version
+                           . hyphen-package-name->name+version)
+                          find-files
+                          invoke))
+  #:use-module (guix import utils)
+  #:use-module (guix git)
+  #:use-module (guix import json)
+  #:autoload   (gcrypt hash) (hash-algorithm sha256)
+  #:use-module (json)
+  #:use-module (guix packages)
+  #:use-module (guix upstream)
+  #:use-module ((guix licenses) #:prefix license:)
+  #:use-module (guix build-system elm)
+  #:export (elm-recursive-import
+            %elm-package-registry
+            %current-elm-checkout
+            elm->guix-package))
+
+(define %registry-url
+  ;; It is much nicer to fetch this small (< 40 KB gzipped)
+  ;; file once than to do many HTTP requests.
+  "https://package.elm-lang.org/all-packages")
+
+(define %elm-package-registry
+  ;; This is a parameter to support both testing and memoization.
+  ;; In pseudo-code, it has the contract:
+  ;;     (parameter/c (-> json/c)
+  ;;                  (promise/c (vhash/c string? (listof string?))))
+  ;; To set the parameter, provide a thunk that returns a value suitable
+  ;; as an argument to 'json->registry-vhash'.  Accessing the parameter
+  ;; returns a promise wrapping the resulting vhash.
+  (make-parameter
+   (lambda ()
+     (cond
+      ((json-fetch %registry-url #:http-fetch http-fetch/cached))
+      (else
+       (raise (formatted-message
+               (G_ "error downloading Elm package registry from ~a")
+               %registry-url)))))
+   (lambda (thunk)
+     (delay (json->registry-vhash (thunk))))))
+
+(define (json->registry-vhash jsobject)
+  "Parse the '(json)' module's representation of the Elm package registry to a
+vhash mapping package names to lists of available versions, sorted from latest
+to oldest."
+  (fold (lambda (entry vh)
+          (match entry
+            ((name . vec)
+             (vhash-cons name
+                         (sort (vector->list vec) version>?)
+                         vh))))
+        vlist-null
+        jsobject))
+
+(define (json->direct-dependencies jsobject)
+  "Parse the '(json)' module's representation of an 'elm.json' file's
+'dependencies' or 'test-dependencies' field to a list of strings naming direct
+dependencies, handling both the 'package' and 'application' grammars."
+  (cond
+   ;; *unspecified*
+   ((not (pair? jsobject))
+    '())
+   ;; {"type":"application"}
+   ((every (match-lambda
+             (((or "direct" "indirect") (_ . _) ...)
+              #t)
+             (_
+              #f))
+           jsobject)
+    (map car (or (assoc-ref jsobject "direct") '())))
+   ;; {"type":"package"}
+   (else
+    (map car jsobject))))
+
+;; <project-info> handles both {"type":"package"} and {"type":"application"}
+(define-json-mapping <project-info> make-project-info project-info?
+  json->project-info
+  (dependencies project-info-dependencies
+                "dependencies" json->direct-dependencies)
+  (test-dependencies project-info-test-dependencies
+                     "test-dependencies" json->direct-dependencies)
+  ;; "synopsis" and "license" may be missing for {"type":"application"}
+  (synopsis project-info-synopsis
+            "summary" (lambda (x)
+                        (if (string? x)
+                            x
+                            "")))
+  (license project-info-license
+           "license" (lambda (x)
+                       (if (string? x)
+                           (spdx-string->license x)
+                           #f))))
+
+(define %current-elm-checkout
+  ;; This is a parameter for testing purposes.
+  (make-parameter
+   (lambda (name version)
+     (define-values (checkout _commit _relation)
+       ;; Elm requires that packages use this very specific format
+       (update-cached-checkout (string-append "https://github.com/" name)
+                               #:ref `(tag . ,version)))
+     checkout)))
+
+(define (make-elm-package-sexp name version)
+  "Return two values: the `package' s-expression for the Elm package with the
+given NAME and VERSION, and a list of Elm packages it depends on."
+  (define checkout
+    ((%current-elm-checkout) name version))
+  (define info
+    (call-with-input-file (string-append checkout "/elm.json")
+      json->project-info))
+  (define dependencies
+    (project-info-dependencies info))
+  (define test-dependencies
+    (project-info-test-dependencies info))
+  (define guix-name
+    (elm->package-name name))
+  (values
+   `(package
+      (name ,guix-name)
+      (version ,version)
+      (source (elm-package-origin
+               ,name
+               version ;; no ,
+               (base32
+                ,(bytevector->nix-base32-string
+                  (file-hash* checkout
+                              #:algorithm (hash-algorithm sha256)
+                              #:recursive? #t)))))
+      (build-system elm-build-system)
+      ,@(maybe-propagated-inputs (map elm->package-name dependencies))
+      ,@(maybe-inputs (map elm->package-name test-dependencies))
+      (home-page ,(string-append "https://package.elm-lang.org/packages/"
+                                 name "/" version))
+      (synopsis ,(project-info-synopsis info))
+      (description
+       ;; Try to use the first paragraph of README.md (which Elm requires),
+       ;; or fall back to synopsis otherwise.
+       ,(beautify-description
+         (match (chunk-lines (call-with-input-file
+                                 (string-append checkout "/README.md")
+                               read-lines))
+           ((_ par . _)
+            (string-join par " "))
+           (_
+            (project-info-synopsis info)))))
+      ,@(let ((inferred-name (infer-elm-package-name guix-name)))
+          (if (equal? inferred-name name)
+              '()
+              `((properties '((upstream-name . ,name))))))
+      (license ,(project-info-license info)))
+   (append dependencies test-dependencies)))
+
+(define elm->guix-package
+  (memoize
+   (lambda* (package-name #:key repo version)
+     "Fetch the metadata for PACKAGE-NAME, an Elm package registered at
+package.elm.org, and return two values: the `package' s-expression
+corresponding to that package (or #f on failure) and a list of Elm
+dependencies."
+     (cond
+      ((vhash-assoc package-name (force (%elm-package-registry)))
+       => (match-lambda
+            ((_found latest . _versions)
+             (make-elm-package-sexp package-name (or version latest)))))
+      (else
+       (values #f '()))))))
+
+(define* (elm-recursive-import package-name #:optional version)
+  (recursive-import package-name
+                    #:version version
+                    #:repo->guix-package elm->guix-package
+                    #:guix-name elm->package-name))
diff --git a/guix/scripts/import.scm b/guix/scripts/import.scm
index 40fa6759ae..fa79f3211e 100644
--- a/guix/scripts/import.scm
+++ b/guix/scripts/import.scm
@@ -5,6 +5,7 @@
 ;;; Copyright © 2019 Ricardo Wurmus <rekado@elephly.net>
 ;;; Copyright © 2021 Simon Tournier <zimon.toutoune@gmail.com>
 ;;; Copyright © 2021 Xinglu Chen <public@yoctocell.xyz>
+;;; Copyright © 2022 Philip McGrath <philip@philipmcgrath.com>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -80,7 +81,7 @@ (define %standard-import-options '())
 
 (define importers '("gnu" "pypi" "cpan" "hackage" "stackage" "egg" "elpa"
                     "gem" "go" "cran" "crate" "texlive" "json" "opam"
-                    "minetest"))
+                    "minetest" "elm"))
 
 (define (resolve-importer name)
   (let ((module (resolve-interface
diff --git a/guix/scripts/import/elm.scm b/guix/scripts/import/elm.scm
new file mode 100644
index 0000000000..68dcbf1070
--- /dev/null
+++ b/guix/scripts/import/elm.scm
@@ -0,0 +1,107 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 Philip McGrath <philip@philipmcgrath.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix scripts import elm)
+  #:use-module (guix ui)
+  #:use-module (guix utils)
+  #:use-module (guix scripts)
+  #:use-module (guix import elm)
+  #:use-module (guix scripts import)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-11)
+  #:use-module (srfi srfi-37)
+  #:use-module (srfi srfi-71)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 format)
+  #:export (guix-import-elm))
+
+
+;;;
+;;; Command-line options.
+;;;
+
+(define %default-options
+  '())
+
+(define (show-help)
+  (display (G_ "Usage: guix import elm PACKAGE-NAME
+
+Import and convert the Elm package PACKAGE-NAME.  Optionally, a version
+can be specified after the arobas (@) character.\n"))
+  (display (G_ "
+  -h, --help             display this help and exit"))
+  (display (G_ "
+  -r, --recursive        import packages recursively"))
+  (display (G_ "
+  -V, --version          display version information and exit"))
+  (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 elm")))
+         (option '(#\r "recursive") #f #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'recursive #t result)))
+         %standard-import-options))
+
+
+;;;
+;;; Entry point.
+;;;
+
+(define (guix-import-elm . 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))
+         (args (filter-map (match-lambda
+                             (('argument . value)
+                              value)
+                             (_ #f))
+                           (reverse opts))))
+    (match args
+      ((spec)
+       (with-error-handling
+         (let ((name version (package-name->name+version spec)))
+           (if (assoc-ref opts 'recursive)
+               ;; Recursive import
+               (map (match-lambda
+                      ((and ('package ('name name) . rest) pkg)
+                       `(define-public ,(string->symbol name)
+                          ,pkg))
+                      (_ #f))
+                    (elm-recursive-import name version))
+               ;; Single import
+               (let ((sexp (elm->guix-package name #:version version)))
+                 (unless sexp
+                   (leave (G_ "failed to download meta-data for package '~a'~%")
+                          name))
+                 sexp)))))
+      (()
+       (leave (G_ "too few arguments~%")))
+      ((many ...)
+       (leave (G_ "too many arguments~%"))))))
diff --git a/tests/elm.scm b/tests/elm.scm
index 96f958f060..c30623da03 100644
--- a/tests/elm.scm
+++ b/tests/elm.scm
@@ -18,6 +18,13 @@
 
 (define-module (test-elm)
   #:use-module (guix build-system elm)
+  #:use-module (guix import elm)
+  #:use-module (guix base32)
+  #:use-module (guix hash)
+  #:use-module (guix utils)
+  #:autoload   (gcrypt hash) (hash-algorithm sha256)
+  #:use-module (json)
+  #:use-module (ice-9 match)
   #:use-module (srfi srfi-64))
 
 (test-begin "elm")
@@ -94,4 +101,168 @@ (define-module (test-elm)
     (test-not-inferred "gcc-toolchain")
     (test-not-inferred "font-adobe-source-sans-pro")))
 
+(define test-package-registry-json
+  ;; we intentionally list versions in different orders here
+  "{
+    \"elm/core\": [\"1.0.0\", \"1.0.1\", \"1.0.2\", \"1.0.3\", \"1.0.4\"],
+    \"elm-guix/demo\": [\"2.0.0\", \"3.0.0\", \"1.0.0\"]
+}")
+
+(define test-elm-core-json
+  "{
+    \"type\": \"package\",
+    \"name\": \"elm/core\",
+    \"summary\": \"Elm's standard libraries\",
+    \"license\": \"BSD-3-Clause\",
+    \"version\": \"1.0.4\",
+    \"exposed-modules\": {
+        \"Primitives\": [
+            \"Basics\",
+            \"String\",
+            \"Char\",
+            \"Bitwise\",
+            \"Tuple\"
+        ],
+        \"Collections\": [
+            \"List\",
+            \"Dict\",
+            \"Set\",
+            \"Array\"
+        ],
+        \"Error Handling\": [
+            \"Maybe\",
+            \"Result\"
+        ],
+        \"Debug\": [
+            \"Debug\"
+        ],
+        \"Effects\": [
+            \"Platform.Cmd\",
+            \"Platform.Sub\",
+            \"Platform\",
+            \"Process\",
+            \"Task\"
+        ]
+    },
+    \"elm-version\": \"0.19.0 <= v < 0.20.0\",
+    \"dependencies\": {},
+    \"test-dependencies\": {}
+}")
+
+(define test-elm-core-readme
+  "# Core Libraries
+
+Every Elm project needs this package!
+
+It provides **basic functionality** like addition and subtraction as well as
+**data structures** like lists, dictionaries, and sets.")
+
+(define test-elm-guix-demo-json
+  "{
+    \"type\": \"package\",
+    \"name\": \"elm-guix/demo\",
+    \"summary\": \"A test for `(guix import elm)`\",
+    \"license\": \"GPL-3.0-or-later\",
+    \"version\": \"3.0.0\",
+    \"exposed-modules\": [
+        \"Guix.Demo\"
+    ],
+    \"elm-version\": \"0.19.0 <= v < 0.20.0\",
+    \"dependencies\": {
+        \"elm/core\": \"1.0.0 <= v < 2.0.0\"
+    },
+    \"test-dependencies\": {
+        \"elm/json\": \"1.0.0 <= v < 2.0.0\"
+    }
+}")
+
+(define test-elm-guix-demo-readme
+  ;; intentionally left blank
+  "")
+
+(define (directory-sha256 directory)
+  "Returns the string representing the hash of DIRECTORY as would be used in a
+package definition."
+  (bytevector->nix-base32-string
+   (file-hash* directory
+               #:algorithm (hash-algorithm sha256)
+               #:recursive? #t)))
+
+(test-group "(guix import elm)"
+  (call-with-temporary-directory
+   (lambda (dir)
+     ;; Initialize our fake git checkouts.
+     (define elm-core-dir
+       (string-append dir "/test-elm-core-1.0.4"))
+     (define elm-guix-demo-dir
+       (string-append dir "/test-elm-guix-demo-3.0.0"))
+     (for-each (match-lambda
+                 ((dir json readme)
+                  (mkdir dir)
+                  (with-output-to-file (string-append dir "/elm.json")
+                    (lambda ()
+                      (display json)))
+                  (with-output-to-file (string-append dir "/README.md")
+                    (lambda ()
+                      (display readme)))))
+               `((,elm-core-dir ,test-elm-core-json ,test-elm-core-readme)
+                 (,elm-guix-demo-dir
+                  ,test-elm-guix-demo-json
+                  ,test-elm-guix-demo-readme)))
+     ;; Replace network resources with sample data.
+     (parameterize ((%elm-package-registry
+                     (lambda ()
+                       (json-string->scm test-package-registry-json)))
+                    (%current-elm-checkout
+                     (lambda (name version)
+                       (match (list name version)
+                         (("elm/core" "1.0.4")
+                          elm-core-dir)
+                         (("elm-guix/demo" "3.0.0")
+                          elm-guix-demo-dir)))))
+       (test-assert "(elm->guix-package \"elm/core\")"
+         (match (elm->guix-package "elm/core")
+           (`(package
+               (name "elm-core")
+               (version "1.0.4")
+               (source (elm-package-origin
+                        "elm/core"
+                        version
+                        (base32 ,(? string? hash))))
+               (build-system elm-build-system)
+               (home-page
+                "https://package.elm-lang.org/packages/elm/core/1.0.4")
+               (synopsis "Elm's standard libraries")
+               (description "Every Elm project needs this package!")
+               (license license:bsd-3))
+            (equal? (directory-sha256 elm-core-dir)
+                    hash))
+           (x
+            (raise-exception x))))
+       (test-assert "(elm-recursive-import \"elm-guix/demo\")"
+         (match (elm-recursive-import "elm-guix/demo")
+           (`((package
+                (name "elm-guix-demo")
+                (version "3.0.0")
+                (source (elm-package-origin
+                         "elm-guix/demo"
+                         version
+                         (base32 ,(? string? hash))))
+                (build-system elm-build-system)
+                (propagated-inputs
+                 ,'`(("elm-core" ,elm-core)))
+                (inputs
+                 ,'`(("elm-json" ,elm-json)))
+                (home-page
+                 "https://package.elm-lang.org/packages/elm-guix/demo/3.0.0")
+                (synopsis "A test for `(guix import elm)`")
+                (description
+                 "This package provides a test for `(guix import elm)`")
+                (properties '((upstream-name . "elm-guix/demo")))
+                (license license:gpl3+)))
+            (equal? (directory-sha256 elm-guix-demo-dir)
+                    hash))
+           (x
+            (raise-exception x))))))))
+
 (test-end "elm")
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:10 +0200
[PATCH v2 10/34] gnu: Add elm-virtual-dom.
(address . 55030@debbugs.gnu.org)
a44980eae55a83b02bb92c42e1b8eeb1ded37e29.1652890702.git.philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-virtual-dom): New variable.
---
gnu/packages/elm.scm | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

Toggle diff (31 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 4ad3a000cb..c2ccd6c557 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -158,3 +158,24 @@ (define-public elm-json-bootstrap
                 (delete 'build)
                 (delete 'validate-compiled))
             #:implicit-elm-package-inputs? #f)))))
+
+(define-public elm-virtual-dom
+  (package
+    (name "elm-virtual-dom")
+    (version "1.0.3")
+    (source
+     (elm-package-origin
+      "elm/virtual-dom"
+      version
+      (base32 "1bjyyws7l0qvgp4ixzaimwriq86ncx5bvrzaksvjx3pv7bmkbx69")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-json elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/virtual-dom/1.0.2")
+    (synopsis
+     "Elm's low-level virtual DOM implementation")
+    (description
+     "This package provides a virtual DOM implementation that backs Elm's
+core libraries for HTML and SVG.  You should almost certainly use those
+higher-level libraries directly.")
+    (properties '((upstream-name . "elm/virtual-dom")))
+    (license license:bsd-3)))
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:10 +0200
[PATCH v2 11/34] gnu: Add elm-html.
(address . 55030@debbugs.gnu.org)
df37f0dcd030c5e062e989cd67448b168129cc6f.1652890702.git.philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-html): New variable.
---
gnu/packages/elm.scm | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

Toggle diff (29 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index c2ccd6c557..98b60c48f7 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -179,3 +179,22 @@ (define-public elm-virtual-dom
 higher-level libraries directly.")
     (properties '((upstream-name . "elm/virtual-dom")))
     (license license:bsd-3)))
+
+(define-public elm-html
+  (package
+    (name "elm-html")
+    (version "1.0.0")
+    (source
+     (elm-package-origin
+      "elm/html"
+      version
+      (base32 "15k1679ja57vvlpinpv06znmrxy09lbhzfkzdc89i01qa8c4gb4a")))
+    (build-system elm-build-system)
+    (propagated-inputs
+     (list elm-virtual-dom
+           elm-json
+           elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/html/1.0.0")
+    (synopsis "Fast HTML, rendered with virtual DOM diffing")
+    (description "This package provides Elm's HTML rendering library.")
+    (license license:bsd-3)))
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:11 +0200
[PATCH v2 13/34] gnu: Add elm-time.
(address . 55030@debbugs.gnu.org)
550d12a75571e6f770d8dac42f4a22e2ef5a7aee.1652890702.git.philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-time): New variable.
---
gnu/packages/elm.scm | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

Toggle diff (29 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 5ba6743399..2b9ee3594a 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -220,3 +220,22 @@ (define-public elm-svg
      "This package provides Elm's @acronym{SVG, Scalable Vector Graphics}
 library.")
     (license license:bsd-3)))
+
+(define-public elm-time
+  (package
+    (name "elm-time")
+    (version "1.0.0")
+    (source
+     (elm-package-origin
+      "elm/time"
+      version
+      (base32 "0wqa2vhl1zf8z0j2yd3yjwfhr0dydfns43bbzll3k4rhnjadxr1l")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/time/1.0.0")
+    (synopsis
+     "POSIX time and time zones in Elm")
+    (description
+     "This package provides an Elm library for working with POSIX times, time
+zones, formatting, and the clock.")
+    (license license:bsd-3)))
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:10 +0200
[PATCH v2 12/34] gnu: Add elm-svg.
(address . 55030@debbugs.gnu.org)
2b42b23b8cd67014be1ffa3c9d67e44fa4a4c7d6.1652890702.git.philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-svg): New variable.
---
gnu/packages/elm.scm | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

Toggle diff (32 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 98b60c48f7..5ba6743399 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -198,3 +198,25 @@ (define-public elm-html
     (synopsis "Fast HTML, rendered with virtual DOM diffing")
     (description "This package provides Elm's HTML rendering library.")
     (license license:bsd-3)))
+
+(define-public elm-svg
+  (package
+    (name "elm-svg")
+    (version "1.0.1")
+    (source
+     (elm-package-origin
+      "elm/svg"
+      version
+      (base32 "1iqsc3p129j56lp1y3z3mfc6x1shvrmx3pkhri2777ylhyw90qvl")))
+    (build-system elm-build-system)
+    (propagated-inputs
+     (list elm-html
+           elm-virtual-dom
+           elm-json
+           elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/svg/1.0.1")
+    (synopsis "Fast SVG, rendered with virtual DOM diffing")
+    (description
+     "This package provides Elm's @acronym{SVG, Scalable Vector Graphics}
+library.")
+    (license license:bsd-3)))
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:11 +0200
[PATCH v2 14/34] gnu: Add elm-url.
(address . 55030@debbugs.gnu.org)
623de08aed8de9add9bb109c8fa6acbae33e2899.1652890702.git.philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-url): New variable.
---
gnu/packages/elm.scm | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)

Toggle diff (38 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 2b9ee3594a..d7d11e0d91 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -239,3 +239,31 @@ (define-public elm-time
      "This package provides an Elm library for working with POSIX times, time
 zones, formatting, and the clock.")
     (license license:bsd-3)))
+
+(define-public elm-url
+  (package
+    (name "elm-url")
+    (version "1.0.0")
+    (source
+     (elm-package-origin
+      "elm/url"
+      version
+      (base32 "1f2ij4i7zmijnj2i50qf19lpkr14bhms8dkq029inb5mydi9f8gs")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/url/1.0.0")
+    (synopsis
+     "Create and parse URLs in Elm")
+    (description
+     "This package helps you:
+
+@enumerate
+@item
+build new URLs; and
+
+@item
+parse existing URLs into nice Elm data structures.
+@end enumerate
+
+Use it for HTTP and for @dfn{routing} in @acronym{SPAs, single-page apps}.")
+    (license license:bsd-3)))
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:11 +0200
[PATCH v2 15/34] gnu: Add elm-browser.
(address . 55030@debbugs.gnu.org)
312119bcf422bb911d212d3d2427120a05950f7e.1652890702.git.philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-browser): New variable.
---
gnu/packages/elm.scm | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)

Toggle diff (35 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index d7d11e0d91..714a49a3dd 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -267,3 +267,28 @@ (define-public elm-url
 
 Use it for HTTP and for @dfn{routing} in @acronym{SPAs, single-page apps}.")
     (license license:bsd-3)))
+
+(define-public elm-browser
+  (package
+    (name "elm-browser")
+    (version "1.0.2")
+    (source
+     (elm-package-origin
+      "elm/browser"
+      version
+      (base32 "0863nw2hhbpm3s03lm1imi5x28wwknzrwg2p79s5mydgvdvgwjf0")))
+    (build-system elm-build-system)
+    (propagated-inputs
+     (list elm-virtual-dom
+           elm-url
+           elm-time
+           elm-json
+           elm-html
+           elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/browser/1.0.2")
+    (synopsis
+     "Run Elm in browsers")
+    (description
+     "This package allows you to create Elm programs that run in browsers,
+with access to browser history for @acronym{SPAs, single-page apps}.")
+    (license license:bsd-3)))
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:11 +0200
[PATCH v2 16/34] gnu: Add elm-bytes.
(address . 55030@debbugs.gnu.org)
30ffc23c80a26d183a4dec31da1e90a012b4a5fb.1652890702.git.philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-bytes): New variable.
---
gnu/packages/elm.scm | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)

Toggle diff (28 lines)
diff --git a/gnu/packages/elm.scm b/gnu/packages/elm.scm
index 714a49a3dd..ac10bd8672 100644
--- a/gnu/packages/elm.scm
+++ b/gnu/packages/elm.scm
@@ -292,3 +292,21 @@ (define-public elm-browser
      "This package allows you to create Elm programs that run in browsers,
 with access to browser history for @acronym{SPAs, single-page apps}.")
     (license license:bsd-3)))
+
+(define-public elm-bytes
+  (package
+    (name "elm-bytes")
+    (version "1.0.8")
+    (source
+     (elm-package-origin
+      "elm/bytes"
+      version
+      (base32 "0n411j2cyz9m241q6vszfzpq3fraradwal5m0gigp2505mdfpz3x")))
+    (build-system elm-build-system)
+    (propagated-inputs (list elm-core))
+    (home-page "https://package.elm-lang.org/packages/elm/bytes/1.0.8")
+    (synopsis "Work with sequences of bytes in Elm")
+    (description "This package provides an Elm library for working with
+densely packed sequences of bytes, such as @code{ArrayBuffer}, typed arrays,
+and @code{DataView}.")
+    (license license:bsd-3)))
-- 
2.32.0
P
P
Philip McGrath wrote on 18 May 20:11 +0200
[PATCH v2 18/34] gnu: Add elm-http.
(address . 55030@debbugs.gnu.org)
4359f6622eb2a21c9c4cdf1fd9a92de28bae97a9.1652890702.git.philip@philipmcgrath.com
* gnu/packages/elm.scm (elm-http): New variable.
---