all messages for Guix-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Stefan <stefan-guix@vodafonemail.de>
To: guix-devel@gnu.org
Subject: A different way to build GCC to overcome issues, especially with C++ for embedded systems
Date: Mon, 2 Oct 2023 20:55:33 +0200	[thread overview]
Message-ID: <0db7f8a7-906f-6552-26e8-93162f6d266e@vodafonemail.de> (raw)

[-- Attachment #1: Type: text/plain, Size: 4365 bytes --]

Hi!

As some of you probably know, there are issues using GCC to compile C++ 
code, especially for embedded systems.¹²³⁴⁵

Another problem I encountered is that there is only one libstdc++ 
version, which may not match the GCC version one might wish to use.

I stumbled into these problems while trying to compile some embedded C++ 
stuff for an Arm mirco-controller. So I thought to build GCC from 
scratch including libstdc++ and getting the include paths right, and 
then use that to build the needed cross GCC as well.

Finally I got c-toolchain lists for use with package-with-c-toolchain 
and complete GCC toolchains for installation. Each basically consists 
just of (the union of) GCC and Binutils.

I figured out that for GCC 12 only a single patch seems to be necessary: 
gcc-12-strmov-store-file-names.patch.

In the end it worked nicely for my embedded C++ stuff and I also managed 
to compile a custom keyboard firmware based on ZMK using Zephyr, 
although that is just C code.

The newly build GCC contains these default include paths:

$ /gnu/store/…-gcc12-12.2.0/bin/g++ -v -x c++ -E - < /dev/null
…
#include <...> search starts here:
  /gnu/store/…-gcc12-12.2.0/include-c++
  /gnu/store/…-gcc12-12.2.0/include-c++/x86_64-unknown-linux-gnu
  /gnu/store/…-gcc12-12.2.0/include-c++/backward
 
/gnu/store/…-gcc12-12.2.0-lib/lib/gcc/x86_64-unknown-linux-gnu/12.2.0/include
  /gnu/store/…-gcc12-12.2.0-lib/include
 
/gnu/store/…-gcc12-12.2.0-lib/lib/gcc/x86_64-unknown-linux-gnu/12.2.0/include-fixed
  /gnu/store/…-glibc-2.33/include
  /gnu/store/…-linux-libre-headers-5.10.35/include
End of search list.
…

This is very similar to the paths found in FHS systems. The path 
/gnu/store/…-gcc12-12.2.0-lib/include is empty and replaces 
/usr/local/include. It is impossible to omit a path at this place.

Similarly the newly build cross GCC with newlib contains these default 
include paths:

$ 
/gnu/store/…-gcc12-cross-newlib-arm-none-eabi-toolchain-12.2.0/bin/arm-none-eabi-g++ 
-v -x c++ -E - < /dev/null
…
#include <...> search starts here:
  /gnu/store/…-gcc12-cross-newlib-arm-none-eabi-12.2.0/include-c++
 
/gnu/store/…-gcc12-cross-newlib-arm-none-eabi-12.2.0/include-c++/arm-none-eabi
  /gnu/store/…-gcc12-cross-newlib-arm-none-eabi-12.2.0/include-c++/backward
 
/gnu/store/…-gcc12-cross-newlib-arm-none-eabi-12.2.0-lib/lib/gcc/arm-none-eabi/12.2.0/include
  /gnu/store/…-gcc12-cross-newlib-arm-none-eabi-12.2.0-lib/include
 
/gnu/store/…-gcc12-cross-newlib-arm-none-eabi-12.2.0-lib/lib/gcc/arm-none-eabi/12.2.0/include-fixed
  /gnu/store/…-newlib-arm-none-eabi-4.3.0/arm-none-eabi/include
End of search list.
…

You may note the missing include path to kernel headers, which do not 
exist for bare-metal builds using newlib. So this is by intention.

None of these paths will show up in C_INCLUDE_PATH or CPLUS_INCLUDE_PATH 
when installing a toolchain, which I think was basically the root of all 
the pain.

Of course this makes use of the already existing gnu-build-system and 
the gcc-toolchain. To get this going gcc-phase-fix-environment is used 
to overcome some troubles with the existing gcc-toolchain.

In the end I think it could make sense to change the way how the tower 
of GCC versions is build. I also think that glibc should reference the 
libgcc_s.so from some gcc-stage1:lib output, which has to be build 
without any libc. Then the modification of the specs to pre-load that 
library via -lgcc_s may become obsolete.

Except for other compilers like clang, this makes a libstdc++ package 
obsolete. I did not care about using clang with lbstdc++ yet, but I 
might do so in the future. I also did not try to use the same functions 
on older GCC versions nor GCC 13, which was not existing in Guix when I 
started this endeavor.

I hope this message triggers some interest and helps solving some 
existing troubles.


Bye

Stefan


P.S. If someone likes to play with the attached files, please note the 
needed file structure:
…/embedded.scm
…/zephyr.scm
…/zephyr/modules.scm
…/zephyr/apps.scm
…/zephyr/zmk.scm


¹ https://lists.gnu.org/archive/html/help-guix/2023-09/msg00025.html
² https://lists.gnu.org/archive/html/guix-devel/2020-06/msg00096.html
³ https://issues.guix.gnu.org/37801https://issues.guix.gnu.org/43579https://issues.guix.gnu.org/49935

[-- Attachment #2: embedded.scm --]
[-- Type: text/x-scheme, Size: 29323 bytes --]

;;; GNU Guix --- Functional package management for GNU
;;;
;;; Copyright © 2023 Stefan <stefan-guix@vodafonemail.de>
;;;
;;; This file is not 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 (embedded)
  #:use-module (guix build-system)
  #:use-module (guix build-system gnu)
  #:use-module (guix build-system trivial)
  #:use-module (guix build gnu-build-system)
  #:use-module ((guix build utils) #:select (alist-replace
                                             modify-phases))
  #:use-module (guix download)
  #:use-module (guix gexp)
  #:use-module ((guix licenses) #:prefix license:)
  #:use-module (guix packages)
  #:use-module (guix utils)
  #:use-module (gnu packages)
  #:use-module (gnu packages autotools)
  #:use-module (gnu packages base)
  #:use-module ((gnu packages bootstrap) #:select (glibc-dynamic-linker))
  #:use-module (gnu packages commencement)
  #:use-module (gnu packages compression)
  #:use-module (gnu packages cross-base)
  #:use-module (gnu packages flex)
  #:use-module (gnu packages gcc)
  #:use-module (gnu packages gdb)
  #:use-module (gnu packages multiprecision)
  #:use-module (gnu packages pkg-config)
  #:use-module (gnu packages texinfo)
  #:use-module (ice-9 optargs)
  #:use-module (srfi srfi-1))

(define-public (gcc-phase-fix-environment)
  "Give a build-phase for the GCC compilation to fix environment variables."
  #~(lambda* (#:key inputs #:allow-other-keys)
      (use-modules (srfi srfi-1))
      (setenv
       "LD_LIBRARY_PATH"
       ;; The built gcc will have a workaround to ensure that glibc will always
       ;; find libgcc_s.so.  Unfortunately during the configuration of libatomic
       ;; and other libraries, the libgcc_s.so is not yet available in its final
       ;; installation directory and this workaround causes trouble to the
       ;; configure script during "checking whether we are cross compiling".  As
       ;; a mitigation we set LD_LIBRARY_PATH to the location of the not yet
       ;; installed libgcc_s.so. This would not be necessary, if glibc had a
       ;; reference to the gcc used to build it.
       (string-append (getcwd) "/build/gcc"))
      (format #t "environment variable `LD_LIBRARY_PATH' set to `~a'~%"
              (getenv "LD_LIBRARY_PATH"))
      (when (getenv "CPLUS_INCLUDE_PATH")
        (let* ((libc (assoc-ref inputs "libc"))
               (gcc (assoc-ref inputs "gcc")) ; This is different to #$gcc!
               (paths-to-delete
                (map (lambda (package) (string-append package "/include"))
                     (filter-map identity (list libc gcc)))))
          (setenv
           "CPLUS_INCLUDE_PATH"
           ;; The gcc package used by the gnu-build-system to build this gcc
           ;; puts the include paths to the C++ headers and to the libc headers
           ;; into CPLUS_INCLUDE_PATH.  This causes trouble in the gcc build
           ;; process when -nostdinc++ is used.  As a mitigation we remove them.
           ;; This would not be necessary, when using this built gcc instead.
           (string-join
            (remove (lambda (path) (member path paths-to-delete))
                    (string-split (getenv "CPLUS_INCLUDE_PATH") #\:))
            ":"))
          (format #t "environment variable `CPLUS_INCLUDE_PATH' set to `~a'~%"
                     (getenv "CPLUS_INCLUDE_PATH"))))))

(define*-public (gcc-phase-pre-configure
                 #:key
                 (libc glibc)
                 (dynamic-linker (glibc-dynamic-linker))
                 (startfile-dir "/lib/"))
  "Give a build-phase for the GCC compilation to modify the source-code.  Use
the LIBC package for embedded search-paths.  The path DYNAMIC-LINKER is appended
to LIBC to form the absolute path to the dynamic-linker.  The STRTFILE-DIR is a
suffix for LIBC to form the path to startfiles like crt0.o from newlib.  All
default values match for glibc.  For newlib omit DYNAMIC-LINKER and set
STARTFILE-DIR to (string-append \"/\" target \"/lib/\")."
  #~(lambda _
      (substitute* "Makefile.in"
        ;; Don't store configure arguments, to avoid retaining references to
        ;; build-time dependencies like "--with-…=/gnu/store/…".
        (("@TOPLEVEL_CONFIGURE_ARGUMENTS@") ""))
      (substitute* (find-files "gcc/config")
        ;; Enforce any /lib64 directory to just be /lib.
        (("/lib64") "/lib"))
      (when #$dynamic-linker
        (substitute* (find-files "gcc/config" "^.+\\.h$" #:directories? #t)
          ;; Enforce anything looking like some /lib/ld.so.2 to be the linker.
          (("[^ :;\"{]+/ld.*[_.]so(\\.[0-9]+)?")
           (string-append #$libc #$dynamic-linker))))
      (substitute* "gcc/configure"
        ;; Prevent auto-host.h in output:lib to create a cyclic dependency
        ;; referencing output:out.
        (("PREFIX_INCLUDE_DIR \"\\$prefix/include\"")
         "PREFIX_INCLUDE_DIR \"$libdir/include\""))
      (substitute* "gcc/genmultilib"
        ;; Enforce proper invokations of sh.
        (("#!/bin/sh") (string-append "#!" (which "sh"))))
      (substitute* "gcc/gcc.cc"
        ;; The STARTFILE_PREFIX_SPEC defines where to find crt1.o and other
        ;; start files of libc.  Replace it with a proper path to the libc.
        ;; Note: This path is relative to the sysroot which therefore must be /.
        (("startfile_prefix_spec = STARTFILE_PREFIX_SPEC;")
         (string-append "startfile_prefix_spec = \""
                        #$libc #$startfile-dir "\";")))
      (substitute* "libstdc++-v3/python/Makefile.in"
        ;; Change pythondir from #$output:out to #$output:lib to prevent
        ;; #$output:lib/lib/libstdc++.so.*-gdb.py to create a cyclic dependency
        ;; to #$output:out/share/…/python.  This moves all python files to
        ;; #$output:lib.  The option --with-python-dir is still usable.
        (("pythondir = \\$\\(datadir\\)") "pythondir = $(libdir)/share")
        (("pythondir = \\$\\(prefix\\)")  "pythondir = $(libdir)"))
      #!(substitute* "libsanitizer/asan/asan_linux.cpp"
        ;; Ensure PATH_MAX is defined by including the proper header file.
        ;; https://gcc.gnu.org/bugzilla//show_bug.cgi?id=99476
        ;; https://gcc.gnu.org/bugzilla//show_bug.cgi?id=106998
        ;; The gcc package used by the gnu-build-system is to blame here.  This
        ;; patch is unnecessary when not using --disable-bootstrap.
        (("#include <limits\\.h>")
         #$(string-join (list "#include <limits.h>"
                              "#if SANITIZER_LINUX"
                              "#include <linux/limits.h>"
                              "#endif")
                        "\n")))!#))

(define*-public (gcc-configure-flags
                 #:key
                 (libc glibc)
                 (kernel-headers
                  (car (assoc-ref (package-propagated-inputs glibc)
                                  "kernel-headers"))))
  "Give the configure-flags for the GCC compilation referring to the LIBC and
KERNEL-HEADERS packages.  The defaults refer to glibc and the kernel-headers
used to build glibc."
  #~(list
      ;; The first set of include paths consinsts of #$gcc/include-c++/… and
      ;; #$gcc:lib/…/include.  Second is usually /usr/local, which we replace
      ;; with the empty #$output:lib/include path.
      (string-append "--with-local-prefix=" #$output:lib)
      ;; Third is #$gcc:lib/…/include-fixed, which expects #$libc/include and
      ;; #$kernel-headers/include to follow in this order.
      ;; Fourth and usually the last include path is /usr/include containing all
      ;; system headers.  It is only possible to specify one path for this.  Set
      ;; the #$libc/include path and prevent the use of /usr/include.
      (string-append "--with-native-system-header-dir=" #$libc "/include")
      ;; The final include path has to be #$kernel-headers/include, which has to
      ;; be after #$libc/include.  There is only -idirafter left to achieve
      ;; this.  Add #$output:lib/lib as a built-in link-time search path.
      ;; Add #$libc/lib and #$libc/static as built-in link-time search paths.
      ;; Add a runtime search path to #$libc/lib, if not linked statically.
      ;; This search path to #$libc/lib may not be technically necessary because
      ;; of the dynamic linker in there, but better use a complete list.
      ;; Actually libc needs a DT_RUNPATH entry to the libgcc_s.so of the GCC
      ;; used to build it.  This is missing and may requires a fix.  As a
      ;; workaround one can force any program built by this GCC and not linked
      ;; statically to load libgcc_s.so by embedding the -lgcc_s option and
      ;; adding a runtime search path to libgcc_s.so as well.
      (string-append
       "--with-specs="
       ;; Embed the path to the kernel-headers.
       "-idirafter " #$kernel-headers "/include "
       ;; Embed the link-time search path to libgcc_s.so, libstdc++.so, etc.
       "%x{-L" #$output:lib "/lib} "
       ;; Embed the link-time search paths to #$libc/lib and #$libc:static/lib.
       "%x{-L" #$libc "/lib} %x{-L" #$libc:static "/lib} "
       ;; Embed the runtime search path to #$libc/lib, if not staticlally
       ;; linked.
       "%{nolibc|nostdlib|static:; :%x{-rpath=" #$libc "/lib}} "
       ;; This is a workaround to ensure a pre-loaded libgcc_s.so for libc if
       ;; not statically linking.  The libstdc++.so and other shared libraries
       ;; are statically linked but use the option -lgcc_s.  Unfortunately it
       ;; seems to be impossible to check for the presence of -lgcc_s.  Adding
       ;; the -rpath option conditionally, if not linking statically, has the
       ;; risk to not add it although needed.  Adding it unconditionally may add
       ;; it needlessly, which prevents building the dynamic linker of libc,
       ;; but makes the make-flags #~(list "LDFLAGS=-Wl,-rpath=$(libdir)/lib")
       ;; obsolete.  As a GCC referencing the dynamic linker cannot be used to
       ;; build it, the -rpath is added unconditionally here.
       "%{nodefaultlibs|nostdlib|static|static-libgcc|static-pie:; "
       ":%x{-lgcc_s}} %x{-rpath=" #$output:lib "/lib}")
      ;; Prevent the C++ headers in #$output:lib, put them in #$output:out
      ;; instead.  Use an unconventional path to prevent it from being added to
      ;; the environment variables C_INCLUDE_PATH and CPLUS_INCLUDE_PATH.
      "--with-gxx-include-dir=$(prefix)/include-c++"
      ;; As libc is limited, gcc will not be usable for 32 and 64 bit builds.
      "--disable-multilib"
      ;; Disable all language frontends except for C and C++.
      "--enable-languages=c,c++"
      ;; Save space by disabling pre-compiled libstdc++ headers.
      "--disable-libstdcxx-pch"
      ;; Use the zlib package instead of the zlib bundled with gcc.
      "--with-system-zlib"
      ;; Avoid parallel linking to not crash on systems with limited memory.
      "--enable-link-serialization"
      ;; Prevent the built gcc to build itself again to save time.
      #!"--disable-bootstrap"!#))

(define-public (make-gcc12 phase-pre-configure configure-flags)
  "Make a GCC package using the PHASE-PRE-CONFIGURE and the CONFIGURE-FLAGS in
the build-process."
  (package
    (name "gcc12")
    (version "12.2.0")
    (source (origin
              (method url-fetch)
              (uri (string-append "mirror://gnu/gcc/gcc-"
                                  version "/gcc-" version ".tar.xz"))
              (sha256
               (base32 "1zrhca90c7hqnjz3jgr1vl675q3h5lrd92b5ggi00jjryffcyjg5"))
              (patches
               (search-patches "gcc-12-strmov-store-file-names.patch"))))
    (build-system gnu-build-system)
    (outputs '("out" "lib" "debug"))
    (inputs (list gmp mpfr mpc isl zstd zlib))
    (arguments
      (list #:tests? #f
            #:out-of-source? #t
            #:configure-flags configure-flags
            #:phases
            #~(modify-phases %standard-phases
                (add-after 'set-paths 'fix-environment
                  #$(gcc-phase-fix-environment))
                (add-before 'configure 'pre-configure
                  #$phase-pre-configure))))
    (native-search-paths
     (list (search-path-specification (variable "CPLUS_INCLUDE_PATH")
                                      (files (list "include")))
           (search-path-specification (variable "C_INCLUDE_PATH")
                                      (files (list "include")))
           (search-path-specification (variable "LIBRARY_PATH")
                                      (files (list "lib")))))
    (synopsis "GNU Compiler Collection")
    (description
      "GCC is the GNU Compiler Collection.  It provides compiler front-ends for
several languages, including C, C++, Objective-C, Fortran, Java, Ada, and
Go.  It also includes runtime support libraries for these languages.")
    (home-page "https://gcc.gnu.org/")
    (license license:gpl3+)))

(define-public gcc12
  (make-gcc12 (gcc-phase-pre-configure) (gcc-configure-flags)))

(define-public guix-locpath
  (package
    (name "guix-locpath")
    (version "1.0")
    (source #f)
    (build-system trivial-build-system)
    (arguments (list #:builder #~(mkdir #$output)))
    (native-search-paths
     (list (search-path-specification (variable "GUIX_LOCPATH")
                                      (files '("lib/locale")))))
    (home-page #f)
    (synopsis "Access for glibc to locales")
    (description "The guix-locpath package sets the environment variable
GUIX_LOCPATH to make all locale related functions of glibc usable without
propagating glibc itself.  This is usefull to prevent glibc include paths to be
exposed via C_INCLUDE_PATH and similar environment variables, to keep a defined
include order with embedded paths in GCC to glibc.")
    (license (package-license glibc-utf8-locales))))

(define*-public (make-c-toolchain gcc binutils #:optional ld-wrapper)
  "Make a C-toolchain consisting of GCC, BINUTILS, guix-locpath and
optionally LD-WRAPPER packages.  The result can be used by the transformation
function 'package-with-c-toolchain' and to build a GCC-toolchain package with
'make-gcc-toolchain-package'.

The guix-locpath package is used instead of the glibc package to prevent glibc
and the kernel-headers from appearing in the C_INCLUDE_PATH, CPLUS_INCLUDE_PATH,
and similar environment variables.  The GCC package is expected to have the
necessary paths build-in to preserve a necessary include-order."
  (let ((build-inputs (list (list "guix-locpath" guix-locpath)
                            (list "gcc" gcc)
                            (list "binutils" binutils))))
    (if ld-wrapper
        ;; The ld-wrapper has to be in front of binutils.
        (cons (list "ld-wrapper" ld-wrapper) build-inputs)
        build-inputs)))

(define*-public (make-gcc-toolchain-package c-toolchain)
  "Make a GCC-toolchain package from C-TOOLCHAIN.  The C-TOOLCHAIN argument must
be a list of inputs (label/package tuples) providing equivalent functionality as
the 'gcc-toolchain' package as passed to 'package-with-c-toolchain'."
  (let ((gcc (car (assoc-ref c-toolchain "gcc"))))
    (package
      (name (string-append (package-name gcc) "-toolchain"))
      (version (package-version gcc))
      (source #f)
      (build-system trivial-build-system)
      (arguments
        (list
         #:modules '((guix build union))
         #:builder
         #~(begin
             (use-modules ((guix build union)))
             (union-build #$output (quote #$(map second c-toolchain))))))
      (synopsis "Complete GCC toolchain for C/C++ development")
      (description "This package provides a complete GCC toolchain for C/C++
development to be installed in user profiles.  This includes GCC and Binutils.
GCC is the GNU Compiler Collection.")
      (home-page "https://gcc.gnu.org/")
      (license license:gpl3+))))

(define-public gcc12-c-toolchain
  (make-c-toolchain gcc12 binutils ld-wrapper))

(define-public gcc12-toolchain
  (make-gcc-toolchain-package gcc12-c-toolchain))

(define*-public (make-newlib-4.3 target
                                 #:key
                                 (configure-flags
                                  '("--disable-newlib-supplied-syscalls"
                                    "--enable-newlib-io-long-long"
                                    "--enable-newlib-io-c99-formats"
                                    "--enable-newlib-mb"
                                    "--enable-newlib-reent-check-verify"
                                    "--enable-newlib-register-fini"
                                    "--enable-newlib-retargetable-locking"
                                    "--disable-dependency-tracking"))
                                 (cross-gcc (cross-gcc target))
                                 (cross-binutils (cross-binutils target)))
  "Make a newlib package for TARGET with the given CONFIGURE-FLAGS, CROSS-GCC
and CROSS-BINUTLIS packages for building."
  (package
    (name (string-append "newlib-" target))
    (version "4.3.0")
    (source
      (origin
        (method url-fetch)
        (uri "ftp://sourceware.org/pub/newlib/newlib-4.3.0.20230120.tar.gz")
        (sha256
          (base32 "0l2iycz12m9r8czc08isykzh1mr4xs9d13n5n2wqxqsrmycjm9l3"))))
    (build-system gnu-build-system)
    (arguments
     (list #:out-of-source? #t
           ;; The configure-flags are taken from newlib_configure found in:
           ;; https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu/12.2.mpacbti-bet1/manifest/arm-gnu-toolchain-arm-none-eabi-abe-manifest.txt
           ;; Got that link in section "Linaro ABE example manifest files for
           ;; Linux hosted cross toolchains" form:
           ;; https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads
           #:configure-flags
           #~(quote #$(cons (string-append "--target=" target)
                            configure-flags))
           #:phases
           #~(modify-phases %standard-phases
               ;; TODO: Remove nano related files after installation.
               (add-after 'unpack 'fix-references-to-/bin/sh
                 (lambda _
                   (substitute* (find-files "libgloss" "^Makefile\\.in$")
                     ;; There are plenty Makefile.in below libgloss which
                     ;; reference /bin/sh. These must be fixed.
                     (("/bin/sh") (which "sh"))))))))
    (native-inputs (list cross-gcc cross-binutils texinfo))
    (home-page "https://www.sourceware.org/newlib/")
    (synopsis "C library for use on embedded systems")
    (description "Newlib is a C library intended for use on embedded
systems.  It is a conglomeration of several library parts that are easily
usable on embedded products.")
    (license (license:non-copyleft
              "https://www.sourceware.org/newlib/COPYING.NEWLIB"))))

(define*-public (make-newlib-nano-4.3 target
                                      #:key
                                      (cross-gcc (cross-gcc target))
                                      (cross-binutils (cross-binutils target)))
  "Make a newlib-nano package for TARGET with the given CROSS-GCC and
CROSS-BINUTLIS packages for building."
  (package
    (inherit (make-newlib-4.3 target
              #:configure-flags
              '("--disable-newlib-fseek-optimization"
                "--disable-newlib-fvwrite-in-streamio"
                "--disable-newlib-supplied-syscalls"
                "--disable-newlib-unbuf-stream-opt"
                "--disable-newlib-wide-orient"
                "--enable-lite-exit"
                "--enable-newlib-global-atexit"
                "--enable-newlib-nano-formatted-io"
                "--enable-newlib-nano-malloc"
                "--enable-newlib-reent-check-verify"
                "--enable-newlib-reent-small"
                "--enable-newlib-retargetable-locking"
                "--disable-dependency-tracking")
                #:cross-gcc cross-gcc
                #:cross-binutils cross-binutils))
    (name (string-append "newlib-nano-" target))
    ;; TODO: Add nano suffix to installed files, keep nano related files.
    (synopsis "C library for use on embedded systems with limited memory")))

(define-public newlib-arm-none-eabi-4.3
  (make-newlib-4.3 "arm-none-eabi"))

(define-public newlib-nano-arm-none-eabi-4.3
  (make-newlib-nano-4.3 "arm-none-eabi"))

(define-public (gcc-cross-newlib-arm-configure-flags target libc)
  "Modify configure-flags to build a GCC cross-compiler for the Arm target
TARGET using newlib as LIBC."
  #~(list
      (string-append "--target=" #$target)
      ;; All paths to --with-… options are relative to the sysroot.  As store
      ;; pathes are absolute, the sysroot needs to be set to /.
      "--with-sysroot=/"
      ;; The first set of include paths consinsts of #$gcc/include-c++/… and
      ;; #$gcc:lib/…/include.  Second is usually /usr/local, which we replace
      ;; with the empty #$output:lib/include path.
      (string-append "--with-local-prefix=" #$output:lib)
      ;; Third is #$gcc:lib/…/include-fixed, which expects #$libc/include and
      ;; #$kernel-headers/include to follow in this order.
      ;; Fourth and usually the last include path is /usr/include containing all
      ;; system headers.  It is only possible to specify one path for this.  Set
      ;; the #$libc/include path and prevent the use of /usr/include.
      ;; Using newlib as libc for bare-metal does not require kernel-headers.
      (string-append
       "--with-native-system-header-dir=" #$libc "/" #$target "/include")
      ;; Add #$output/#$target/lib and #$libc/#$target/lib as a built-in
      ;; link-time search path.
      (string-append
       "--with-specs="
       ;; Embed the link-time search path to libgcc, libstdc++, etc.
       "%x{-L" #$output "/" #$target "/lib} "
       ;; Embed the link-time search paths to libc.
       "%x{-L" #$libc "/" #$target "/lib}")
      ;; Prevent the C++ headers in #$output:lib, put them in #$output:out
      ;; instead.  Use an unconventional path to prevent it from being added to
      ;; the environment variables C_INCLUDE_PATH and CPLUS_INCLUDE_PATH.
      "--with-gxx-include-dir=$(prefix)/include-c++"
      ;; Ensure GCC is build for newlib.
      "--with-newlib"
      ;; As newlib has multiple target libraries, enable their support.
      "--enable-multilib"
      "--with-multilib-list=aprofile,rmprofile"
      ;; As newlib does not have a dynamik-linker, disable shared builds.
      "--disable-shared"
      ;; Disable all language frontends except for C and C++.
      "--enable-languages=c,c++"
      ;; Save space by disabling pre-compiled libstdc++ headers.
      "--disable-libstdcxx-pch"
      ;; Use the zlib package instead of the zlib bundled with gcc.
      "--with-system-zlib"
      ;; Avoid parallel linking to not crash on systems with limited memory.
      "--enable-link-serialization"))

(define*-public (make-cross-gcc gcc cross-libc cross-binutils)
  "Make a GCC cross-compiler package based on a still native but modified GCC
package using the CROSS-LIBC and CROSS-BINUTILS packages."
  (package
    (inherit gcc)
    (name (string-append (package-name gcc)
                         "-cross-"
                         (package-name cross-libc)))
    (inputs '())
    (native-inputs (append (list (list "cross-binutils" cross-binutils))
                           (package-inputs gcc)))))

(define*-public (make-cross-c-toolchain target cross-gcc cross-binutils
                                        #:optional cross-ld-wrapper)
  "Make a cross-C-toolchain targeting TARGET of the CROSS-GCC, CROSS-BINUTILS,
guix-locpath and optionally CROSS-LD-WRAPPER packages.  The result can be used
by the transformation function 'package-with-c-toolchain' and to build a
GCC-toolchain package with 'make-gcc-toolchain-package'.

The guix-locpath package is used instead of the glibc package to prevent glibc
and the kernel-headers from appearing in the C_INCLUDE_PATH, CPLUS_INCLUDE_PATH,
and similar environment variables.  The GCC package is expected to have the
necessary paths build-in to preserve a necessary include-order."
  (append (make-c-toolchain cross-gcc cross-binutils cross-ld-wrapper)
          ;; GCC looks for as and other tools from cross-binutils in PATH.
          ;; However, the cross-binutils package contains only executables
          ;; pefixed with the target name in its bin directory.  The ones
          ;; not prefixed reside in its target/bin directory.  We create a
          ;; new package, which links that #$target/bin directory as bin.
          ;; Note: The linker is invoked by collect2, which has different lookup
          ;; rules as GCC and finds a prefixed target-ld.
          (list
           (list "binutils-for-toolchain"
                 (package
                   (inherit cross-binutils)
                   (name (string-append (package-name cross-binutils)
                                        "-for-toolchain"))
                   (version (package-version cross-binutils))
                   (source #f)
                   (build-system trivial-build-system)
                   (arguments
                     (list
                      #:builder
                      #~(begin
                          (mkdir #$output)
                          (symlink
                           #$(file-append cross-binutils "/" target "/bin")
                           (string-append #$output "/bin"))))))))))

(define*-public (make-cross-arm-none-eabi-c-toolchain
                 gcc
                 make-gcc
                 make-cross-newlib)
  "Make a C-toolchain targeting arm-none-eabi consisting of a cross-compiler,
cross-compiled Binutils, and a newlib C-library.  The cross-compiler will be
build with another toolchain using the given host GCC and Binutils.  The
function MAKE-GCC is used to create a GCC cross-compiler package using the
newlib C-library package created with MAKE-CROSS-NEWLIB.  The result can be used
by the transformation function 'package-with-c-toolchain' and to build a
GCC-toolchain package with 'make-gcc-toolchain-package'."
  (let* ((target "arm-none-eabi")
         (c-toolchain (make-c-toolchain gcc binutils ld-wrapper))
         (cross-binutils (cross-binutils target))
         (cross-newlib (make-cross-newlib target))
         (cross-gcc
          (package-with-c-toolchain
           (make-cross-gcc
            (make-gcc (gcc-phase-pre-configure
                       #:libc cross-newlib
                       #:startfile-dir (string-append "/" target "/lib/"))
                      (gcc-cross-newlib-arm-configure-flags target
                                                            cross-newlib))
            cross-newlib
            cross-binutils)
           c-toolchain)))
    (make-cross-c-toolchain target cross-gcc cross-binutils)))

(define-public gcc12-cross-newlib-arm-none-eabi-c-toolchain
  (make-cross-arm-none-eabi-c-toolchain gcc12 make-gcc12 make-newlib-4.3))

(define-public gcc12-cross-newlib-nano-arm-none-eabi-c-toolchain
  (make-cross-arm-none-eabi-c-toolchain gcc12 make-gcc12 make-newlib-nano-4.3))

(define-public gcc12-cross-newlib-arm-none-eabi-toolchain
  (package
    (inherit (make-gcc-toolchain-package
              gcc12-cross-newlib-arm-none-eabi-c-toolchain))
    (synopsis
     "Complete GCC toolchain for C/C++ cross development on ARM Cortex-A and
Cortex-M micro-controllers with newlib")
    (description "This package provides a complete GCC toolchain for C/C++ cross
development on ARM Cortex-A and Cortex-M micro-controllers to be installed in
user profiles.  This includes GCC, newlib and Binutils.  GCC is the GNU
Compiler Collection.")))

(define-public gcc12-cross-newlib-nano-arm-none-eabi-toolchain
  (package
    (inherit (make-gcc-toolchain-package
              gcc12-cross-newlib-nano-arm-none-eabi-c-toolchain))
    (synopsis
     "Complete GCC toolchain for C/C++ cross development on ARM Cortex-A and
Cortex-M micro-controllers with newlib-nano")
    (description "This package provides a complete GCC toolchain for C/C++ cross
development on ARM Cortex-A and Cortex-M micro-controllers to be installed in
user profiles.  This includes GCC, newlib-nano and Binutils.  GCC is the GNU
Compiler Collection.")))

(define-public gcc12-cross-newlib-arm-none-eabi
  (car (assoc-ref gcc12-cross-newlib-arm-none-eabi-c-toolchain "gcc")))

(define-public gcc12-cross-newlib-nano-arm-none-eabi
  (car (assoc-ref gcc12-cross-newlib-nano-arm-none-eabi-c-toolchain "gcc")))

(define-public binutils-cross-arm-none-eabi
  (car (assoc-ref gcc12-cross-newlib-nano-arm-none-eabi-c-toolchain "binutils")))


[-- Attachment #3: zephyr.scm --]
[-- Type: text/x-scheme, Size: 5442 bytes --]

;;; GNU Guix --- Functional package management for GNU
;;;
;;; Copyright © 2023 Stefan <stefan-guix@vodafonemail.de>
;;;
;;; This file is not 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 (zephyr)
  #:use-module (embedded)
  #:use-module (gnu packages bootloaders)
  #:use-module (gnu packages python)
  #:use-module (gnu packages python-xyz)
  #:use-module (guix build-system copy)
  #:use-module (guix build-system trivial)
  #:use-module (guix gexp)
  #:use-module (guix git-download)
  #:use-module ((guix licenses) #:prefix license:)
  #:use-module (guix packages))

;; This is the common directory name for zephyr-modules to look for search-paths
;; to collect in the ZEPHYR_MODULES environment variable.  The build-system of
;; Zephyr searches for a file zephyr/module.yml in all paths listed in the
;; environment variable ZEPHYR_MODULES.  If that file is missing a name
;; property, then the parent directory name is used as the module name. Having
;; two modules with the same name is treated as an error.  As Guix needs a
;; common directory name for search-path-specification, we need this
;; intermediate directory as a pattern to find unique module names.
;; Unfortunately search-path-specification is not powerful enough, so
;; complete-zephyr-application needs the correct-ZEPHYR_MODULES build-phase.
(define-public %zephyr-module "zephyr-module")

(define-public zephyr
  (let ((version "3.4.0"))
    (package
      (name "zephyr")
      (version version)
      (source
        (origin
          (method git-fetch)
          (uri (git-reference
                (url "https://github.com/zephyrproject-rtos/zephyr")
                (commit (string-append "zephyr-v" version))))
          (file-name (git-file-name name version))
          (sha256 (base32
                   "1gcry9fxv88js5nymi9akgrkghkwavggj3wqdgg2cz6brr5wg284"))))
      (build-system copy-build-system)
      (arguments
        (list
         #:install-plan
         #~(list (list "." "zephyr-base"))
         #:phases
         #~(modify-phases %standard-phases
             (add-after 'unpack 'set-USER_CACHE_DIR-and-BUILD_VERSION
               (lambda _
                ;; Avoid fetching the BUILD_VERSION from the git repository.
                (substitute* "CMakeLists.txt"
	  	          (("if *\\(DEFINED BUILD_VERSION\\)")
                   (string-append
                    "if (NOT DEFINED BUILD_VERSION)\n"
                    "  set(BUILD_VERSION " #$version ")\n"
                    "elseif (DEFINED BUILD_VERSION)")))
                ;; Avoid USER_CACHE_DIR to point to XDG_CACHE_HOME, HOME, or to
                ;; ZEPHYR_BASE inside the store. Instead use the build dir.
                (with-output-to-file
                 "cmake/modules/user_cache.cmake"
                 (lambda ()
                   (display
                    "set(USER_CACHE_DIR ${CMAKE_BINARY_DIR}/cache)\n"))))))))
      (native-search-paths
       (list (search-path-specification
              (variable "ZEPHYR_BASE")
              (files '("zephyr-base"))
              (separator #f))
             (search-path-specification
              (variable "ZEPHYR_MODULES")
              (files (list %zephyr-module)))))
      (home-page "https://zephyrproject.org")
      (synopsis "Zephyr Project RTOS")
      (description "The Zephyr Project is a scalable real-time operating system
(RTOS) supporting multiple hardware architectures, optimized for resource
constrained devices, and built with security in mind.")
      (license license:apsl2))))

(define-public zephyr-3.2+zmk-fixes
  (let ((revision "1")
        (commit "0a586db7b58269fb08248b081bdc3b43452da5f4"))
    (package/inherit zephyr
      (name "zephyr+zmk-fixes")
      (version (git-version "3.2.0" revision commit))
      (source
        (origin
          (method git-fetch)
          (uri (git-reference
                (url "https://github.com/zmkfirmware/zephyr")
                (commit commit)))
          (file-name (git-file-name name version))
          (sha256 (base32
                   "04mqgzhgjb43ybryvpwrbq2g9i0gk7wd7s2ds3fsrakl4hxqss78"))))
      (home-page "https://github.com/zmkfirmware/zephyr"))))

(define-public zephyr-build-tools
  (package
    (name "zephyr-build-tools")
    (version "1.0.0")
    (source #f)
    (build-system trivial-build-system)
    (arguments (list #:builder #~(mkdir #$output)))
    (propagated-inputs (list dtc
                             python
                             python-pyelftools
                             python-pykwalify
                             python-pyyaml
                             python-packaging))
    (home-page "https://zephyrproject.org")
    (synopsis "Zephyr build tools")
    (description "Required build tools to build the Zephyr RTOS.")
    (license license:apsl2)))


[-- Attachment #4: modules.scm --]
[-- Type: text/x-scheme, Size: 11166 bytes --]

;;; GNU Guix --- Functional package management for GNU
;;;
;;; Copyright © 2023 Stefan <stefan-guix@vodafonemail.de>
;;;
;;; This file is not 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 (zephyr modules)
  #:use-module (guix gexp)
  #:use-module (guix build-system copy)
  #:use-module (guix git-download)
  #:use-module ((guix licenses) #:prefix license:)
  #:use-module (guix packages)
  #:use-module (ice-9 regex)
  #:use-module (zephyr))

(define-public (zephyr-module-installation-target zephyr-module)
  "Return the target directory for the install-plan of the copy-build-system for
the ZEPHYR-MODULE package.  It needs to match a certain pattern to collect
search-paths for zephyr-modules in the ZEPHYR_MOUDULES environment variable."
  (string-append
   ;; Add the needed prefix to the module name.
   "/" %zephyr-module "/"
   ;; Get the module name from the usually prefixed package name.
   (regexp-substitute #f (string-match "^(zephyr-module-|)"
                                       (package-name zephyr-module))
                         "" 'post)))

(define-public zephyr-module-template
  "Return a template package to inherit zephyr module packages from.  It
provides the build-system and proper arguments."
  (package
    (name "zephyr-module-template")
    (version "0")
    (source #f)
    (build-system copy-build-system)
    (arguments
      (list #:install-plan
            #~(list
               (list "." #$(zephyr-module-installation-target this-package)))))
    (home-page #f)
    (synopsis #f)
    (description #f)
    (license #f)))

(define-public zephyr-module-cmsis
  (let ((revision "1")
        (commit "74981bf893e8b10931464b9945e2143d99a3f0a3"))
    (package/inherit zephyr-module-template
      (name "zephyr-module-cmsis")
      (version (git-version "5.8.0" revision commit))
      (source (origin
                (method git-fetch)
                (uri (git-reference
                      (url "https://github.com/zephyrproject-rtos/cmsis")
                      (commit commit)))
                      (file-name (git-file-name name version))
                (sha256
                 (base32
                  "11wwcdwzi5ac8k881616p69v7cz356cwvbqsmmfw3w3v63b9dsmy"))))
    (home-page "https://github.com/zephyrproject-rtos/cmsis")
    (synopsis "Zephyr module for CMSIS")
    (description "Zephyr module providing the Common Microcontroller
Software Interface Standard.")
      (license license:apsl2))))

(define-public zephyr-module-hal-nordic
  (let ((revision "1")
        (commit "cf6e9fc5f7c2c98df26f2a4227a95df9a50823e7"))
    (package/inherit zephyr-module-template
      (name "zephyr-module-hal-nordic")
      (version (git-version "3.1.0" revision commit))
      (source (origin
		        (method git-fetch)
		        (uri (git-reference
			          (url "https://github.com/zephyrproject-rtos/hal_nordic")
			          (commit commit)))
		        (file-name (git-file-name name version))
		        (sha256
		         (base32
		          "1zbnhf7r9sg67xjhbdh6fn4gvccc71pxqcmbfnsi6a75bhfv9y55"))))
      (home-page "https://github.com/zephyrproject-rtos/hal_nordic")
	  (synopsis "Zephyr module for Nordic Semiconductor's SoCs and SiPs")
	  (description "Zephyr module providing the Hardware Abstraction Layer for
Nordic Semiconductor's SoCs and SiPs.

Supported SoCs and SiPs:
@itemize
@item nRF51 Series
@item nRF52805
@item nRF52810
@item nRF52811
@item nRF52820
@item nRF52832
@item nRF52833
@item nRF52840
@item nRF5340
@item nRF9131
@item nRF9160
@item nRF9161
@end itemize
")
	(license license:bsd-3))))

(define-public zephyr-module-hal-nordic-2.11
  (let ((revision "0")
        (commit "5644a13252e5d12e3e841105d106cfdeb40e59f9"))
    (package/inherit zephyr-module-hal-nordic
      (name "zephyr-module-hal-nordic")
      (version (git-version "2.11.0" revision commit))
      (source (origin
		        (method git-fetch)
		        (uri (git-reference
			          (url "https://github.com/zephyrproject-rtos/hal_nordic")
			          (commit commit)))
		        (file-name (git-file-name name version))
		        (sha256
		         (base32
		          "0lj7wlbfp9pb5pv819h9kbddmlzfbdnbmxhpm1i4xmf89z9v14sm")))))))

(define-public zephyr-module-hal-stm32
  (let ((revision "1")
        (commit "d466dc8421ee0c6592bb5682aa93a671bc948107"))
    (package/inherit zephyr-module-template
      (name "zephyr-module-hal-stm32")
      ;; Using highest version number listed in:
      ;; https://github.com/zephyrproject-rtos/hal_stm32/blob/main/stm32cube/common_ll/README.rst
      (version (git-version "1.27.1" revision commit))
      (source (origin
		        (method git-fetch)
		        (uri (git-reference
			          (url "https://github.com/zephyrproject-rtos/hal_stm32")
			          (commit commit)))
		        (file-name (git-file-name name version))
		        (sha256
		         (base32
		          "0q0ckial6a3lvlag44zm65dklbbdnqpzr1vbh85dhwx7acpjd5ni"))))
      (home-page "https://github.com/zephyrproject-rtos/hal_stm32")
	  (synopsis "Zephyr module for STM32 microcontrollers")
	  (description "Zephyr module providing the required STM32cube packages,
dtsi files and libraries needed to build a Zephyr application running on STM32
silicon.")
	(license license:bsd-3))))

(define-public zephyr-module-lvgl-8.2.0
  (let ((revision "1")
        (commit "70a7849726be8375e3d941153dc417823ea7f355"))
    (package/inherit zephyr-module-template
      (name "zephyr-module-lvgl")
      (version (git-version "8.2.0" revision commit)) ; Taken from lvgl.h.
      (source (origin
		        (method git-fetch)
		        (uri (git-reference
			          (url "https://github.com/zmkfirmware/lvgl")
			          (commit commit)))
		        (file-name (git-file-name name version))
		        (sha256
		         (base32
		          "147mykkb72nwbjhrw4z7h0kkxw4p7kvy0w001s44rgplxhqqsg98"))))
      (home-page "https://github.com/zmkfirmware/lvgl")
	  (synopsis "Zephyr module for LVGL")
	  (description "Zephyr module providing LVGL, the Light and Versatile
Graphics Library for an embedded GUI with graphical elements, visual effects
and a low memory footprint.")
	  (license license:apsl2))))

(define-public zephyr-module-lvgl
  (let ((revision "2")
        (commit "5da257f782a8f9c6e265bdc60ebc2a93fdee24de"))
    (package/inherit zephyr-module-lvgl-8.2.0
      (name "zephyr-module-lvgl")
      (version (git-version "8.3.7" revision commit)) ; Taken from lvgl.h.
      (source (origin
		        (method git-fetch)
		        (uri (git-reference
			          (url "https://github.com/zephyrproject-rtos/lvgl")
			          (commit commit)))
		        (file-name (git-file-name name version))
		        (sha256
		         (base32
		          "14isczxi36dasks1w4hwdlbzpvja4wal458i0km0hi92bbxayg0a")))))))

(define-public zephyr-module-mbedtls
  (let ((revision "1")
        (commit "c38dc78d9a8dcbe43b898cc1171ab33ba3e6fc26"))
    (package/inherit zephyr-module-template
      (name "zephyr-module-mbedtls")
      (version (git-version "3.4.0" revision commit))
      (source (origin
		        (method git-fetch)
		        (uri (git-reference
			          (url "https://github.com/zephyrproject-rtos/mbedtls")
			          (commit commit)))
		        (file-name (git-file-name name version))
		        (sha256
		         (base32
		          "0661myy0wjz38nypbyfw51x10mzg57syb5c28irblgjm2w25wbi7"))))
      (home-page "https://github.com/zephyrproject-rtos/mbedtls")
	  (synopsis "Zephyr module for Mbed TLS")
	  (description "Zephyr module providing Mbed TLS, a C library that
implements cryptographic primitives, X.509 certificate manipulation and the
SSL/TLS and DTLS protocols.  Its small code footprint makes it suitable for
embedded systems.")
	  (license license:apsl2))))

(define-public zephyr-module-mcuboot
  (let ((revision "1")
        (commit "76d19b3b8885ea7ae25a6f4f5d8501f7ec646447"))
    (package/inherit zephyr-module-template
      (name "zephyr-module-mcuboot")
      (version (git-version "1.11.0-dev" revision commit))
      (source (origin
		        (method git-fetch)
		        (uri (git-reference
			          (url "https://github.com/zephyrproject-rtos/mcuboot")
			          (commit commit)))
		        (file-name (git-file-name name version))
		        (sha256
		         (base32
		          "1frm9330bir1cz7h87qq26r74igy3pvrz3iqpvc7r6l7silj0fxf"))))
      (home-page "https://github.com/zephyrproject-rtos/mcuboot")
	  (synopsis "Zephyr module for MCUboot")
	  (description "Zephyr module providing the secure bootloader MCUboot for
32-bit microcontrollers.  It defines a common infrastructure for the bootloader
and the system flash layout on microcontroller systems, and provides a secure
bootloader that enables easy software upgrade.")
	  (license license:apsl2))))

(define-public zephyr-module-tinycrypt
  (let ((revision "1")
        (commit "3e9a49d2672ec01435ffbf0d788db6d95ef28de0"))
    (package/inherit zephyr-module-template
      (name "zephyr-module-tinycrypt")
      (version (git-version "0.2.8" revision commit))
      (source (origin
		        (method git-fetch)
		        (uri (git-reference
			          (url "https://github.com/zephyrproject-rtos/tinycrypt")
			          (commit commit)))
		        (file-name (git-file-name name version))
		        (sha256
		         (base32
		          "19d2q9y23yzz9i383q3cldjl3k5mryx9762cab23zy3ijdnmj2z6"))))
      (home-page "https://github.com/zephyrproject-rtos/tinycrypt")
	  (synopsis "Zephyr module for the TinyCrypt library")
	  (description "Zephyr module providing the TinyCrypt library.")
	  (license (license:non-copyleft "file://README.zephyr")))))

(define-public zephyr-module-zcbor
  (let ((revision "1")
        (commit "67fd8bb88d3136738661fa8bb5f9989103f4599e"))
    (package/inherit zephyr-module-template
      (name "zephyr-module-zcbor")
      (version (git-version "0.7.0" revision commit))
      (source (origin
		        (method git-fetch)
		        (uri (git-reference
			          (url "https://github.com/zephyrproject-rtos/zcbor")
			          (commit commit)))
		        (file-name (git-file-name name version))
		        (sha256
		         (base32
		          "16138k7xlahf63dfvplm8c2m0kxs1g17gcx1fa31y4gcfbi3b0k7"))))
      (home-page "https://github.com/zephyrproject-rtos/zcbor")
	  (synopsis "Zephyr module for the zcbor library")
	  (description "Zephyr module providing the zcbor low footprint CBOR
(Concise Binary Object Representation) library in the C language, tailored for
use in microcontrollers.")
	  (license license:apsl2))))


[-- Attachment #5: apps.scm --]
[-- Type: text/x-scheme, Size: 12588 bytes --]

;;; GNU Guix --- Functional package management for GNU
;;;
;;; Copyright © 2023 Stefan <stefan-guix@vodafonemail.de>
;;;
;;; This file is not 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 (zephyr apps)
  #:use-module (embedded)
  #:use-module (gnu packages)
  #:use-module (gnu packages python-crypto)
  #:use-module (gnu packages python-xyz)
  #:use-module (gnu packages python-web)
  #:use-module (gnu packages tls)
  #:use-module (guix build-system cmake)
  #:use-module (guix build-system python)
  #:use-module (guix build-system trivial)
  #:use-module (guix gexp)
  #:use-module (guix git-download)
  #:use-module ((guix licenses) #:prefix license:)
  #:use-module (guix packages)
  #:use-module (guix utils)
  #:use-module (ice-9 optargs)
  #:use-module ((srfi srfi-1) #:select (delete-duplicates second))
  #:use-module (zephyr)
  #:use-module (zephyr modules))

(define-public (complete-zephyr-application zephyr-application
                                            zephyr
                                            zephyr-build-tools
                                            target
                                            toolchain
                                            c-toolchain
                                            directory-to-install-from
                                            source-prefix
                                            target-prefix)
  "Complete an incomplete ZEPHYR-APPLICATION package with target specific Zephyr
boilerplate."
  (let ((extended-inputs (modify-inputs
                          (package-inputs zephyr-application)
                          (prepend zephyr zephyr-build-tools)))
        (cross-compile-prefix (file-append toolchain "/bin/" target "-")))
    (package-with-c-toolchain
     (package
       (inherit zephyr-application)
       (build-system cmake-build-system)
       (arguments
         (append
          (substitute-keyword-arguments (package-arguments zephyr-application)
           ((#:configure-flags configure-flags #~(list))
             #~(append #$configure-flags
                (list
                 "-DZEPHYR_TOOLCHAIN_VARIANT=cross-compile"
                 (string-append "-DCROSS_COMPILE=" #$cross-compile-prefix)))))
          (list
           #:tests? #f
           #:phases
           #~(modify-phases %standard-phases
             (add-after 'set-paths 'correct-ZEPHYR_MODULES
               ;; The search-path-specification is not powerful enough. Zephyr
               ;; modules contain a zephyr-module/<name>/zephyr directory
               ;; without a common pattern for <name> and the ZEPHYR_MODULES
               ;; environment variable needs to list the zephyr-module/<name>
               ;; directories separated by semicolons.
               (lambda _
                 (use-modules (ice-9 regex))
                 (let ((zephyr-modules (getenv "ZEPHYR_MODULES")))
                   (when zephyr-modules
                     (setenv "ZEPHYR_MODULES"
                             (string-join
                              (map
                               (lambda (module)
                                 (dirname
                                  (car
                                   (let ((length (string-length module)))
                                     (find-files module
                                                 (lambda (name stat)
                                                   (string-match
                                                    "^/[^/]+/zephyr$"
                                                    (string-drop name length)))
                                                 #:directories? #t)))))
                               (string-split zephyr-modules #\:))
                              ";"))
                     (format
                      #t
                      "environment variable `ZEPHYR_MODULES' set to `~a'~%"
                      (getenv "ZEPHYR_MODULES"))))))
               (replace 'install
                 (lambda _
                   (let* ((source-prefix
                           #$(string-append directory-to-install-from "/"
                                            source-prefix))
                          (target-prefix
                           #$(string-append directory-to-install-from "/"
                                            target-prefix))
                          (source-prefix-length (string-length source-prefix))
                          (primary-files-to-install
                           (list (string-append source-prefix ".uf2")))
                          (files-to-install
                           (map (lambda (suffix)
                                  (string-append source-prefix suffix))
                                '(".bin" ".dts" ".elf" ".hex" ".map" ".stat")))
                          (source-files
                           (find-files #$directory-to-install-from
                                       (lambda (file stat)
                                         (member file primary-files-to-install))
                                       #:directories? #f))
                          (source-files
                           (if (null? source-files)
                               (find-files #$directory-to-install-from
                                           (lambda (file stat)
                                             (member file files-to-install))
                                           #:directories? #f)
                               source-files))
                          (target-files
                           (map (lambda (file)
                                  (string-replace
                                   file target-prefix 0 source-prefix-length))
                                 source-files)))
                     (for-each rename-file source-files target-files)
                     (mkdir #$output)
                     (for-each (lambda (file) (install-file file #$output))
                               target-files))))))))
       (inputs extended-inputs)
       (license (delete-duplicates
                 (cons (package-license zephyr-application)
                       (map (compose package-license second)
                            (filter package? extended-inputs))))))
    c-toolchain)))

(define*-public (make-zephyr-application-for-arm zephyr-application
                                                 #:key
                                                 (zephyr zephyr)
                                                 files-to-install
                                                 (directory-to-install-from
                                                  "zephyr")
                                                 (source-prefix "zephyr")
                                                 target-prefix)
  "Make a Zephyr application for Arm microcontrollers by completing the
incomplete ZEPHYR-APPLICATION package with Arm specific Zephyr boilerplate."
  (complete-zephyr-application zephyr-application
                               zephyr
                               zephyr-build-tools
                               "arm-none-eabi"
                               gcc12-cross-newlib-arm-none-eabi-toolchain
                               gcc12-cross-newlib-arm-none-eabi-c-toolchain
                               directory-to-install-from
                               source-prefix
                               (or target-prefix
                                   (package-name zephyr-application))))

(define-public zephyr-hello-world
  (make-zephyr-application-for-arm
   (package
     (name "zephyr-hello-world")
     (version (package-version zephyr))
     (source (file-append zephyr "/zephyr-base/samples/hello_world"))
     (build-system #f)
     (arguments
      (list
       #:configure-flags
       #~(list "-DBOARD=nucleo_wb55rg"
               "-DCONFIG_BT=y"
               "-DCONFIG_BT_STM32_IPM=y"
               "-DCMAKE_C_FLAGS=-DCFG_BLE_LSE_SOURCE=1"
               "-DCMAKE_BUILD_TYPE=RelMinSize")))
     (inputs (list zephyr-module-cmsis
                   zephyr-module-hal-stm32
                   zephyr-module-tinycrypt))
     (home-page (package-home-page zephyr))
     (synopsis "Hello-world sample of the Zephyr Project")
     (description "A simple Zephyr Project sample that prints \"Hello World\" to
the console.")
     (license (package-license zephyr)))))

(define*-public (make-zephyr-mcuboot-for-arm board extra-inputs
                 #:key
                 (configure-flags #~(list))
                 (key (file-append zephyr-module-mcuboot
                                   (zephyr-module-installation-target
                                    zephyr-module-mcuboot)
                                   "/main/root-rsa-2048.pem")))
  "Make an MCUboot bootloader package for Zephyr targeting the Arm
microcontroller BOARD.  Add EXTRA-INPUTS to the build and use the list of
optional CONFIGURE-FLAGS.  Use the public KEY file for firmware decryption."
  (make-zephyr-application-for-arm
   (package/inherit zephyr-module-mcuboot
     (name (string-append "zephyr-mcuboot-" board))
     (source
       (file-append zephyr-module-mcuboot
                    (zephyr-module-installation-target zephyr-module-mcuboot)))
     (arguments
      (list
       #:configure-flags
       #~(append #$configure-flags
                 (list "-S../source/boot/zephyr"
                       (string-append "-DBOARD=" #$board)
                       (string-append "-DTINYCRYPT_DIR=PATH:"
                                      #$zephyr-module-tinycrypt
                                      "/zephyr-module/tinycrypt/lib")))))
     (inputs (append extra-inputs (list python-cbor2
                                        python-click
                                        python-cryptography
                                        python-intelhex
                                        zephyr-module-mcuboot
                                        zephyr-module-mbedtls
                                        zephyr-module-zcbor))))))

(define-public zephyr-mcuboot-nrf52840dongle_nrf52840
 (make-zephyr-mcuboot-for-arm "nrf52840dongle_nrf52840"
                              (list zephyr-module-hal-nordic
                                    zephyr-module-cmsis)
                              #:configure-flags
                              #~(list "-DMCUBOOT_USE_MBED_TLS=y"
                                      "-DCONFIG_LOG=y")))

(define-public imgtool
    (package
      (name "imgtool")
      (version (package-version zephyr-module-mcuboot))
      (source (file-append zephyr-module-mcuboot
                           (zephyr-module-installation-target
                            zephyr-module-mcuboot)
                           "mcuboot/scripts"))
      (build-system python-build-system)
      (arguments
        (list #:phases
              #~(modify-phases %standard-phases
                 (add-after 'unpack 'disable-broken-tests
                  (lambda _
                    (substitute* "imgtool/keys/ecdsa_test.py"
                     ;; This one is calling _unsupported, which raises an
                     ;; ECDSAUsageError.
                     (("def test_keygen") "def broken_test_keygen")
                     ;; This one is failing with an AttributeError.
                     (("def test_emit_pub") "def broken_test_emit_pub")))))))
      (propagated-inputs (list openssl-3.0
                               python-cbor2
                               python-click
                               python-cryptography
			                   python-intelhex
                               python-pyyaml))
	  (home-page (package-home-page zephyr-module-mcuboot))
	  (synopsis "MCUboot's image signing and key management")
	  (description "A tool to securely sign firmware images for booting by
MCUboot.")
	  (license (package-license zephyr-module-mcuboot))))

[-- Attachment #6: zmk.scm --]
[-- Type: text/x-scheme, Size: 24011 bytes --]

;;; GNU Guix --- Functional package management for GNU
;;;
;;; Copyright © 2023 Stefan <stefan-guix@vodafonemail.de>
;;;
;;; This file is not 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 (zephyr zmk)
  #:use-module (guix build union)
  #:use-module (guix build utils)
  #:use-module (guix build-system trivial)
  #:use-module (guix gexp)
  #:use-module (guix git-download)
  #:use-module ((guix licenses) #:prefix license:)
  #:use-module (guix packages)
  #:use-module (ice-9 match)
  #:use-module (ice-9 optargs)
  #:use-module (srfi srfi-1)
  #:use-module (zephyr)
  #:use-module (zephyr apps)
  #:use-module (zephyr modules))

(define zmk-config
  (package
    (name "zmk-config")
    (version "0")
    (source #f)
    (build-system trivial-build-system)
    (arguments (list #:builder #~(mkdir #$output)))
    (native-search-paths
     (list
      (search-path-specification
      (variable "ZMK_CONFIG")
      (files '("zmk-config"))
      (separator #f)
      (file-type 'directory)
      (file-pattern "^config$"))))
    (home-page "https://zmk.dev/docs/config#config-file-locations")
    (synopsis "ZMK firmware configuration")
    (description "This ZMK Firmware configuration is a helper to set the
ZMK_CONFIG environment varibale during a ZMK Firmware package build to its
configuration input.  Add a file-like object like a file-union or a package
containing a zmk-config/config folder as build input to a ZMK Firmare packege.")
    (license license:expat)))


(define*-public (make-zmk board
                          #:key
                          (shield "")
                          (extra-inputs '())
                          (extra-name "")
                          (patches '())
                          snippet)
  "Make a ZMK firmware package for a keyboard consisting of an Arm
microcontroller BOARD with a SHIELD PCB using the list of EXTRA-INPUTS.  Add an
EXTRA-NAME with a trailing hyphen to customize the package name.  Use PATCHES or
SNIPPET to modify the ZMK sources."
  (make-zephyr-application-for-arm
   (let* ((revision "1")
          (commit "9d714c0b69fee2098a010d29e534051aeca26386")
          (underscore->hyphen (lambda (name)
                                (string-map (lambda (char)
                                              (if (char=? char #\_)
                                                  #\-
                                                  char))
                                             name)))
          (board-name (underscore->hyphen board))
          (shield-name (underscore->hyphen shield))
          (shield (if (string-null? shield) #f shield)))
     (package
       (name (string-append shield-name (if shield "-" "")
                            extra-name board-name "-zmk"))
       (version (git-version "2023.06.12" revision commit))
       (source (origin
                 (method git-fetch)
                 (uri (git-reference
                      (url "https://github.com/zmkfirmware/zmk")
                      (commit commit)))
                 (file-name (git-file-name name version))
                 (sha256
                  (base32
                   "08mihhcdlb9hh1qa0l6limggmvy98qiq6051p9qhnh6zbs8021h7"))
                 (patches patches)
                 (snippet snippet)))
       (build-system #f)
       (arguments
        (list
         #:out-of-source? #t
         #:configure-flags
         #~(append (list "-S../source/app"
                         (string-append "-DBOARD=" #$board))
                   (if #$shield
                       (list (string-append "-DSHIELD=" #$shield))
                       '()))))
       (inputs (append extra-inputs
                       (list zephyr-module-cmsis
                             zephyr-module-lvgl-8.2.0
                             zephyr-module-tinycrypt
                             zmk-config)))
       (home-page "https://zmk.dev")
       (synopsis (if shield (format #f "ZMK Firmware for a ~a keyboard with ~a"
                                       shield-name board-name)
                            (format #f "ZMK Firmware for a ~a keyboard"
                                       board-name)))
       (description "ZMK Firmware is an open source (MIT) keyboard firmware
built on the Zephyr™ Project Real Time Operating System (RTOS).")
       (license license:expat)))
    #:zephyr zephyr-3.2+zmk-fixes
    #:source-prefix "zmk"))

(define*-public (make-zmk-union zmk-packages #:key name synopsis)
  "Make a union of several ZMK Firmware packages for left and right hand or
settings-reset firmware files."
  (package
    (inherit (car zmk-packages))
    (name (or name (package-name (car zmk-packages))))
    (source #f)
    (build-system trivial-build-system)
    (arguments
      (list #:modules '((guix build union))
            #:builder
            #~(begin
                (use-modules ((guix build union)))
                (union-build #$output (quote #$zmk-packages)))))
    (synopsis (or synopsis (package-synopsis (car zmk-packages))))))

(define*-public (make-nrfmicro-13-zmk shield #:key zmk-config (extra-name ""))
  "Make a ZMK firmware package for a keyboard consisting of the nrfmicro 1.3/1.4
board with a SHIELD PCB.  Use the ZMK-CONFIG directory containing optional
boards/ or dts/ directories, or .conf, .keypad, .overlay files prefixed with
shield or board names."
  (make-zmk
   "nrfmicro_13"
   #:shield shield
   #:extra-name extra-name
   #:extra-inputs (append (list zephyr-module-hal-nordic-2.11)
                          (if zmk-config (list zmk-config)
                                         '()))
   #:snippet
   #~(begin
       (use-modules (guix build utils))
       (substitute* "app/CMakeLists.txt"
         ;; Move combo.c and behaviour_tap_dance.c above all other behaviors.
         ;; This fix is needed to get working layer-tap-dance. 
         (("^  target_sources\\(app PRIVATE src/combo.c\\)\n") "")
         (("^  target_sources\\(app PRIVATE src/behaviors/behavior_tap_dance.c\\)\n")
          "")
         (("^  target_sources\\(app PRIVATE src/hid.c\\)\n" line)
          (string-append
           line
           "  target_sources(app PRIVATE src/combo.c)\n"
           "  target_sources(app PRIVATE src/behaviors/behavior_tap_dance.c)\n"))))))

(define (hid-modifier modifier)
  "Map a symbol for a MODIFIER key into a macro symbol for a ZMK keymap file.
An unknown MODIFIER symbol is just returned."
  (define hid-modifier->zmk-macro
    '((⇧ . LS) (⌃ . LC) (⌥ . LA) (⌘ . LG)
      (R⌘ . RG) (R⌥ . RA) (R⌃ . RC) (R⇧ . RS)))
  (or (assoc-ref hid-modifier->zmk-macro modifier) modifier))

(define-public (special-bindings key-label)
  "Map a KEY-LABEL matching a special-binding into a binding symbol for a
ZMK keymap file.  An unknown KEY-LABEL symbol is just returned."
  (define special-bindings->zmk-name
    '(;; A whole in the keyboard matrix without meaning to ZMK.
      (◌ . "")
      ;; No functionality.
      (☒ . &none)
      ;; Fall-through to the next active lower layer.
      (☐ . &trans)
      ;; Keypress on sensor, requires two parameters for up and down keycodes.
      (⟳ . &inc_dec_kp)
      ;; Reset and bootloader, on split keyboards this is side specific.
      (⎊ . &sys_reset) (↯ . &bootloader)
      ;; Bluetooth, requires one or two parameters.
      (⌔ . &bt)
      ;; Backlight, requires one parameter.
      (☼ . &bl)))
  (or (assoc-ref special-bindings->zmk-name key-label) key-label))

(define-public (hid key-label)
  "Map a HID KEY-LABEL into a macro symbol for a ZMK keymap file. Any other
KEY-LABEL will be treated by 'special-bindings'."
  (define hid->zmk-name
    '((⎋ . ESC) (⎙ . PSCRN) (⤓ . SLCK) (⎉ . PAUSE_BREAK)
      (^ . GRAVE) (- . MINUS)
      (= . EQUAL) (⌫ . BSPC)
      (⇥ . TAB) (⟦ . LBKT) (⟧ . RBKT) (↲ . RET) (⏎ . RET) (↩ . RET)
      (⇪ . CAPS) (⍮ . SEMI) (⍘ . SQT) (⋕ . NUHS)
      (⇧ . LSHFT) (\ . NUBS)
      (‚ . COMMA) (· . DOT) (/ . SLASH) (R⇧ . RSHFT)
      (⌃ . LCTRL) (⌥ . LALT) (⌘ . LGUI) (␣ . SPC)
      (R⌘ . RGUI) (R⌥ . RALT) (R⌃ . RCTRL) (☰ . K_APP)
      (⌵ . INS) (⇱ . HOME) (↖ . HOME) (⇞ . PG_UP)
      (⌦ . DEL) (⇲ . END) (↘ . END) (⇟ . PG_DN)
      (← . LEFT) (↓ . DOWN) (↑ . UP) (→ . RIGHT)
      (⇠ . LEFT) (⇣ . DOWN) (⇡ . UP) (⇢ . RIGHT)
      (⇭ . KP_NUMLOCK) (NUM . KP_NUMLOCK)
      (⌧ . KP_CLEAR) (⟨ . KP_LPAR) (⟩ . KP_RPAR) (P= . KP_EQUAL)
      (÷ . KP_DIVIDE) (* . KP_MULTIPLY) (− . KP_MINUS) (+ . KP_PLUS)
      (P1 . KP_N1) (P2 . KP_N2) (P3 . KP_N3) (P4 . KP_N4) (P5 . KP_N5)
      (P6 . KP_N6) (P7 . KP_N7) (P8 . KP_N8) (P9 . KP_N9) (P0 . KP_N0)
      (P. . KP_DOT) (P, . KP_COMMA) (⌤ . ENTER)
      (✄ . C_AC_CUT) (◫ . C_AC_COPY) (⎀ . C_AC_PASTE)
      (↶ . C_AC_UNDO) (↷ . C_AC_REDO)
      (⌨ . C_AL_KEYBOARD_LAYOUT)))
  (special-bindings (or (assoc-ref hid->zmk-name key-label) key-label)))

(define-public (de key-label)
  "Map a german KEY-LABEL based on the QWERTZ-layout into an international HID
key-label, if needed, and return a symbol for a ZMK keymap file."
  (define de->hid
    '((ß . -) (´ . =)
      (Z . Y) (Ü . ⟦) (+ . ⟧)
      (Ö . ⍮) (Ä . ⍘)
      (< . \) (Y . Z) (- . /)
      (P+ . +) (P, . P.) (P. . P,)))
  (hid (or (assoc-ref de->hid key-label) key-label)))

(define-public (neo key-label)
  "Map a german KEY-LABEL based on the neo-layout into the international HID
key-label, if needed, and return a symbol as needed by a ZMK keymap file."
  (define neo->de
   '((T1 . ^)
               (X . Q) (V . W) (L . E) (C . R) (W . T)
     (M3 . ⇪)  (U . A) (I . S) (A . D) (E . F) (O . G)
     (M4 . <) (Ü . Y) (Ö . X) (Ä . C) (P . V) (Z . B)
                                             (- . ß) (T2 . ´)
     (K . Z) (H . U) (G . I) (F . O) (Q . P) (ẞ . Ü) (T3 . +)
     (S . H) (N . J) (R . K) (T . L) (D . Ö) (Y . Ä) (RM3 . ⋕)
     (B . N)                         (J . -) (RM4 . R⌥)
     (P⇥ . ⇭)))
  (de (or (assoc-ref neo->de key-label) key-label)))

(define*-public (zmk-keymap #:key (properties '())
                                  (behaviors '())
                                  (combos '())
                                  (conditional_layers '())
                                  (layers '())
                                  (macros '()))
  "Generate the content of a keymap file for ZMK.  Each layer in LAYERS has a
name, a layout and multiple rows, of which each contains the key-bindings.  The
last row contains the bindings for sensors.  The key-bindings use symbols from
the layout.  The BEHAVIORS, COMBOS, MACROS and CONDITIONAL-LAYERS contain lists
of strings to inject own appropiate definitions for ZMK.  PROPERTIES may contain
properties for behaviors or even C macro definitions."
  (define (include file)
    "Return an include statement for file"
    (string-append "#include <" file ">"))
  (define (include-binding file)
    "Return an include statement for file defining bindings."
    (include (string-append "dt-bindings/zmk/" file)))
  (define (includes)
    "Return all include statements offered by ZMK for keymap files."
    (append (map include '("behaviors.dtsi"))
            (map include-binding '("backlight.h" "bt.h" "ext_power.h"
                                   "hid_usage.h" "hid_usage_pages.h" "keys.h"
                                   "kscan_mock.h" "matrix_transform.h"
                                   "modifiers.h" "outputs.h" "reset.h"
                                   "rgb.h"))))

  (define* (keymap-layer name layout rows)
    "Return a string with a keymap layer definition NAME for a ZMK keymap file,
consisting of ROWS of keys with their labels based on LAYOUT."

    (define (zmk-name->string zmk-name)
      "Tansform a ZMK-NAME into a string."
      (cond ((string? zmk-name) zmk-name)
            ((number? zmk-name) (number->string zmk-name))
            (else (symbol->string zmk-name))))

    (define (key-label->zmk key-label)
      "Tansform a key-label based on a keyboard-layout into a ZMK string."
      (zmk-name->string (layout key-label)))

    (define (modified-key->zmk modified-key)
      "Transform a possibly MODIFIED-KEY like '(⇧ ⌥ ⎋) into the \"LS((LA(ESC))\"
respresentation of ZMK."
      (match modified-key
        ((modifier modifier-or-key . rest)
         (string-append (zmk-name->string (hid-modifier modifier))
                        "("
                        (modified-key->zmk (cdr modified-key))
                        ")"))
        ((unmodified-key)
         (modified-key->zmk unmodified-key))
        (key-label
         (key-label->zmk key-label))))

    (define (behavior->zmk behavior strings-of-layers-and-modified-keys)
      "Join a BEHAVIOR symbol like '&mt with STRINGS-OF-LAYERS-AND-MODIFIED-KEYS
as parameters like '(\"LALT\" \"ESC\") into the \"&mt LALT ESC\" respresentation
of ZMK."
      (string-join (cons (key-label->zmk behavior)
                   strings-of-layers-and-modified-keys)))

    (define (&-symbol? symbol)
      "Predicate to identify a symbol as a ZMK behavior prefixed with &."
      (string=? "&" (string-take (key-label->zmk symbol) 1)))

    (define (key-binding->zmk key-binding)
      "Transform the KEY-BINDING, which could be a key-label, a modified key, or
a behavior with layer and modified key parameters, into the representation of a
ZMK behavior for a keymap layer."
      (match key-binding
        (((? &-symbol? behavior) . parameters)
          ;; A list starting with an &-symbol is a behavior with parameters.
          ;; The parameters themselves may be layers or modified keys.
          (behavior->zmk behavior (map modified-key->zmk parameters)))
        (modified-key
          (let ((modified-key (modified-key->zmk modified-key)))
            (if (or (string-null? modified-key)
                    (&-symbol? modified-key))
                ;; There is nothing or a behavior is present, just use it.
                modified-key
                ;; Add a key-press behavior to the modified-key and start over.
                (behavior->zmk '&kp (list modified-key)))))))

    (define (keys->zmk key-bindings)
      "Transform a list of KEY-BINDINGS into ZMK behaviors for a keymap layer."
      (string-join (map (lambda (zmk-behavior)
                          (string-pad-right
                           zmk-behavior
                           (max 12 (string-length zmk-behavior))))
                        (map key-binding->zmk key-bindings))))

    (string-append  "    " name "_layer {"
                    "\n      bindings = <"
                    (string-join (map keys->zmk (drop-right rows 1))
                                 "\n        " 'prefix)
                    "\n      >;"
                    (if (null? (last rows))
                        ""
                        (string-append
                         "\n      sensor-bindings = <"
                         (string-join (map keys->zmk (last rows))
                                      "\n        " 'prefix)
                         "\n      >;"))
                    "\n    };"))

  (define (layer layer)
    "Return a string for a ZMK keymap file containing a layer definition."
    (match layer
      ((name layout . rows)
       (keymap-layer name layout rows))))

  (string-join (append (includes)
                       properties
                       (list "/ {"
                             "  behaviors {")
                       behaviors
                       (list "  };"
                             "  combos {"
                             "    compatible = \"zmk,combos\";")
                       combos
                       (list "  };"
                             "  conditional_layers {"
                             "    compatible = \"zmk,conditional_layers\";")
                       conditional_layers
                       (list "  };"
                             "  keymap {"
                             "    compatible = \"zmk,keymap\";")
                       (map layer layers)
                       (list "  };"
                             "  macros {")
                       macros
                       (list "  };"
                             "};"))
               "\n"))

;; This is a hold-tap behavior for a key, which momentarily activates a layer,
;; if hold, or switches to that layer, if tapped.
(define-public layer-hold-tap
"    /omit-if-no-ref/ lht: behavior_layer_hold_tap {
      compatible = \"zmk,behavior-hold-tap\";
      label = \"LAYER_HOLD_TAP\";
      #binding-cells = <2>;
      flavor = \"balanced\";
      tapping-term-ms = <200>;
      bindings = <&mo>, <&to>;
    };
")

(define-public (layer-tap-dance n)
  "Give a tap-dance behavior '&ltdN', which counts the taps for the layer number
and momentarily activates that layer on hold, or switches to that layer on tap.
If the parameter N is 0, then taps select the layers 1, 2, 3.  If N is 1, taps
select the layers 0, 2, 3, and so on."
  (let ((first (if (>= n 1) "0 0" "1 1"))
         (second (if (>= n 2) "1 1" "2 2"))
         (third (if (>= n 3) "2 2" "3 3"))
         (n (number->string n)))
    (string-append
"    /omit-if-no-ref/ ltd" n ": behavior_layer_tap_dance" n " {
      compatible = \"zmk,behavior-tap-dance\";
      label = \"LAYER_TAP_DANCE" n "\";
      #binding-cells = <0>;
      tapping-term-ms = <200>;
      bindings = <&lht " first ">, <&lht " second ">, <&lht " third ">;
    };
")))

(define-public settings-reset-nrfmicro-13-zmk
  (package
    (inherit (make-nrfmicro-13-zmk "settings_reset"))
    (synopsis "ZMK settings reset firmware for split-keyboards with nrfmicro
1.3/1.4 boards")
    (description "Pairing issues of ZMK firmware split-keyboard halves can be
resolved by flashing this settings reset firmware to both controllers.")))

(define-public redox-left-nrfmicro-13-zmk
  (make-nrfmicro-13-zmk "redox_left"))

(define-public redox-right-nrfmicro-13-zmk
  (make-nrfmicro-13-zmk "redox_right"))

(define-public redox-nrfmicro-13-zmk
  (make-zmk-union
   (list settings-reset-nrfmicro-13-zmk
         redox-left-nrfmicro-13-zmk
         redox-right-nrfmicro-13-zmk)
   #:name "redox-nrfmicro-13-zmk"
   #:synopsis "ZMK firmware for a Redox shield with nrfmicro-1.3/1.4 board"))

(define-public redox-neo-keymap
  (let* ((M3Y '(&mt RM3 Y))
         (⇧- '(&mt ⇧ -))
         (R⇧ẞ '(&mt R⇧ ẞ))
         (⌥- '(&mt ⌥ -))
         (⌥. '(&mt ⌥ ·))
         (l1␣ '(&lt 1 ␣))
         (l0 '(&ltd0)) ; Layer tap-dance for layer 0.
         (l1 '(&ltd1)) ; Layer tap-dance for layer 1.
         (l2 '(&ltd2)) ; Layer tap-dance for layer 2.
         (l3 '(&ltd3)) ; Layer tap-dance for layer 3.
         (⌔1 '(⌔ BT_SEL 0))
         (⌔2 '(⌔ BT_SEL 1))
         (⌔3 '(⌔ BT_SEL 2))
         (⌔4 '(⌔ BT_SEL 3))
         (⌔5 '(⌔ BT_SEL 4))
         (⌔⌧ '(⌔ BT_CLR))
         (⌔→ '(⌔ BT_NXT))
         (⌔← '(⌔ BT_PRV))
         (keymap
          (zmk-keymap
           #:layers
           `(("default" ,neo
              ( ⎋   N1  N2  N3  N4  N5  ◌   ◌   ◌   ◌   N6  N7  N8  N9  N0  ⌫  )
              ( ⇥   X   V   L   C   W   T2  ◌   ◌   ☰   K   H   G   F   Q   ↲  )
              ( M3  U   I   A   E   O   T3  ◌   ◌   ⌵   S   N   R   T   D  ,M3Y)
              (,⇧-  Ü   Ö   Ä   P   Z   ⇧   ⌘   R⌘  R⇧  B   M   ‚   ·   J  ,R⇧ẞ)
              ( ⌃   ⌥   T1  ⌦  ,l0  ◌   ⌃   M4  RM4 R⌃  ◌  ,l1␣ ⌦  ,l0 ,⌥-  R⌃ )
              ())
             ("cursor" ,neo
              ( ⎋   F1  F2  F3  F4  F5  ◌   ◌   ◌   ◌   F6  F7  F8  F9  F10 ⌫  )
              ( ⇥   ⇞   ⌫   ↑   ⌦   ⇟   ⎉   ◌   ◌   ☒   ⇞   ⌫   ↑   ⌦   ⇟   ↲  )
              ( ☒   ⇱   ←   ↓   →   ⇲   ☒   ◌   ◌   ☒   ⇱   ←   ↓   →   ⇲   ☒  )
              (,⇧-  ⎋   ⇥   ⎀   ↲   ↶   ⇧   ⌘   R⌘  ⇧   ⎋   ⇥   ⎀   ↲   ↶   R⇧ )
              ( ⌃   ⌥   ⎙   ⌦  ,l1  ◌   ⌃   ⌥   ⌥   R⌃  ◌   ␣   ⌦  ,l1 ,⌥-  R⌃ )
              ())
             ("keypad" ,neo
              ( ⎋   F11 F12 F13 F14 F15 ◌   ◌   ◌   ◌   ⎋   P⇥  ÷   *   −   ⌫  )
              ( ⇥   ⇞   ⌫   ↑   ⌦   ⇟   ☒   ◌   ◌   ⇭   ☒   P7  P8  P9  P+  ↲  )
              ( M3  ⇱   ←   ↓   →   ⇲   ☒   ◌   ◌   ☒   ☒   P4  P5  P6  P=  M3 )
              (,⇧-  ⎋   ⇥   ⎀   ↲   ↶   ⇧   ⌘   R⌘  R⇧  ␣   P1  P2  P3  ⌤   R⇧ )
              ( ⌃   ⌥   ☒   ⌦  ,l2  ◌   ⌃   M4  RM4 R⌃  ◌   P0  ⌦   P, ,⌥.  R⌃ )
              ())
             ("zmk" ,neo
              ( ⎊  ,⌔1 ,⌔2 ,⌔3 ,⌔4 ,⌔5  ◌   ◌   ◌   ◌   ☒   ☒   ☒   ☒   ☒   ⎊  )
              ( ↯   ☒   ☒   ☒   ☒   ☒   ☒   ◌   ◌   ☒   ☒   ☒   ☒   ☒   ☒   ↯  )
              ( ☒   ☒  ,⌔← ,⌔⌧ ,⌔→  ☒   ☒   ◌   ◌   ☒   ☒   ☒   ☒   ☒   ☒   ☒  )
              ( ☒   ☒   ☒   ☒   ☒   ☒   ☒   ☒   ☒   ☒   ☒   ☒   ☒   ☒   ☒   ☒  )
              ( ☒   ☒   ☒   ☒  ,l3  ◌   ☒   ☒   ☒   ☒   ☒   ☒   ☒  ,l3  ☒   ☒  )
              ()))
           #:properties (list "&lt {quick-tap-ms = <200>;};"
                              "&mt {quick-tap-ms = <200>;};")
           #:combos (list "    combo_up {" ; G F ⇒ ↑
                          "      key-positions = <22 23>;"
                          "      bindings = <&kp UP>;"
                          "    };"
                          "    combo_left {" ; N R ⇒ ←
                          "      key-positions = <35 36>;"
                          "      bindings = <&kp LEFT>;"
                          "    };"
                          "    combo_down {" ; R T ⇒ ↓
                          "      key-positions = <36 37>;"
                          "      bindings = <&kp DOWN>;"
                          "    };"
                          "    combo_right {" ; T D ⇒ →
                          "      key-positions = <37 38>;"
                          "      bindings = <&kp RIGHT>;"
                          "    };")
           #:behaviors (list layer-hold-tap
                             (layer-tap-dance 0)
                             (layer-tap-dance 1)
                             (layer-tap-dance 2)
                             (layer-tap-dance 3)))))
    (file-union "redox-config"
                (list (list "zmk-config/config/redox.keymap"
                            (plain-file "redox-neo.keymap" keymap))))))

(define-public redox-left-neo-nrfmicro-13-zmk
  (make-nrfmicro-13-zmk "redox_left"
                        #:zmk-config redox-neo-keymap
                        #:extra-name "neo-"))

(define-public redox-right-neo-nrfmicro-13-zmk
  (make-nrfmicro-13-zmk "redox_right"
                        #:zmk-config redox-neo-keymap
                        #:extra-name "neo-"))

(define-public redox-neo-nrfmicro-13-zmk
  (make-zmk-union
   (list settings-reset-nrfmicro-13-zmk
         redox-left-neo-nrfmicro-13-zmk
         redox-right-neo-nrfmicro-13-zmk)
   #:name "redox-neo-nrfmicro-13-zmk"
   #:synopsis
   "Neo layout ZMK firmware for a Redox shield with nrfmicro-1.3/1.4 board"))


             reply	other threads:[~2023-10-02 19:09 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-10-02 18:55 Stefan [this message]
2023-12-21 13:32 ` A different way to build GCC to overcome issues, especially with C++ for embedded systems Attila Lendvai
2023-12-21 14:01   ` Jean-Pierre De Jesus Diaz
2023-12-22  9:56     ` Stefan
2023-12-22  9:33   ` Stefan
2023-12-22 10:56     ` Attila Lendvai
  -- strict thread matches above, loose matches on Subject: below --
2024-05-18 12:45 Sergio Pastor Pérez
2024-05-19 22:06 ` Stefan
2024-05-20  6:59   ` Attila Lendvai
2024-05-24 15:48     ` Sergio Pastor Pérez
2024-05-24 17:05       ` Jean-Pierre De Jesus Diaz
2024-05-25 22:20         ` Ricardo Wurmus
2024-05-27 10:48           ` Jean-Pierre De Jesus Diaz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=0db7f8a7-906f-6552-26e8-93162f6d266e@vodafonemail.de \
    --to=stefan-guix@vodafonemail.de \
    --cc=guix-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/guix.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.