hako pushed a commit to branch wip-zig-bootstrap
in repository guix.

commit 40bd139762c2bf3e14fdd00b19776101c8c3be4b
Author: Hilton Chain <[email protected]>
AuthorDate: Wed Nov 20 07:41:06 2024 +0800

    build-system: zig: Support Zig package manager.
    
    * guix/build-system/zig.scm (zig-build,zig-cross-build)
    [#:install-source?,#:skip-build?]: New arguments.
    [#:tests?]: Honor #:skip-build?.
    * guix/build/zig-build-system.scm (zig-source-install-path)
    (zig-input-install-path,unpack-dependencies): New procedures.
    (%standard-phases): Add 'unpack-dependencies.
    (build,install): Honor #:skip-build?.
    * doc/guix.texi (Build Systems)[zig-build-system]: Update documentation.
    * gnu/packages/zig.scm (zig-0.9)[native-search-paths]: Add
    GUIX_ZIG_PACKAGE_PATH.
    Use search paths defined in (guix search-paths).
    (add-build.zig.zon,rename-zig-dependencies): New procedures.
    * gnu/packages/ncdu.scm (ncdu)[arguments]: Don't install source.
    * gnu/packages/zig-xyz.scm (river,tigerbeetle,zig-zls)[arguments]: Likewise.
    
    Change-Id: I76ba483b67fdd26b33fa707a8d3cccc9f6a37e76
---
 doc/guix.texi                   |  35 ++++++++----
 gnu/packages/ncdu.scm           |   3 +-
 gnu/packages/zig-xyz.scm        |   7 ++-
 gnu/packages/zig.scm            |  63 ++++++++++++++++++----
 guix/build-system/zig.scm       |  12 ++++-
 guix/build/zig-build-system.scm | 116 ++++++++++++++++++++++++++++++++--------
 6 files changed, 187 insertions(+), 49 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index dbcb39010a..867079981e 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -10189,17 +10189,30 @@ build system (@command{zig build} command).
 Selecting this build system adds @code{zig} to the package inputs, in
 addition to the packages of @code{gnu-build-system}.
 
-There is no @code{configure} phase because Zig packages typically do not
-need to be configured.  The @code{#:zig-build-flags} parameter is a list of
-flags that are passed to the @code{zig} command during the build.  The
-@code{#:zig-test-flags} parameter is a list of flags that are passed to the
-@code{zig test} command during the @code{check} phase.  The default compiler
-package can be overridden with the @code{#:zig} argument.
-
-The optional @code{zig-release-type} parameter declares the type of release.
-Possible values are: @code{safe}, @code{fast}, or @code{small}. The default
-value is @code{#f}, which causes the release flag to be omitted from the
-@code{zig} command.  That results in a @code{debug} build.
+This build system by default installs package source to output.  This
+behavior can be disabled by setting @code{#:install-source?} parameter
+to @code{#f}.
+
+For packages that don't install anything and don't come with a test
+suite (likely library packages to be used by other Zig packages), you
+can set @code{#:skip-build?} parameter to @code{#t}, which skips
+@code{build} and @code{check} phases.
+
+The @code{configure} phase sets up environment for @command{zig build}.
+You need to add custom phases after it if you want to invoke
+@command{zig}.
+
+The @code{#:zig-build-flags} parameter is a list of flags that are
+passed to @command{zig build} in @code{build} phase.  The
+@code{#:zig-test-flags} parameter is a list of flags that are passed to
+@command{zig build test} in @code{check} phase.  The default compiler
+package can be overridden with the @code{#:zig} parameter.
+
+The optional @code{#:zig-release-type} parameter declares the type of
+release.  Possible values are: @code{"safe"}, @code{"fast"}, or
+@code{"small"}.  The default value is @code{#f}, which causes the
+release flag to be omitted from the @code{zig} command and results in a
+@code{"debug"} build.
 @end defvar
 
 @defvar scons-build-system
diff --git a/gnu/packages/ncdu.scm b/gnu/packages/ncdu.scm
index ab88f41dad..c49fefc077 100644
--- a/gnu/packages/ncdu.scm
+++ b/gnu/packages/ncdu.scm
@@ -72,7 +72,8 @@ ncurses installed.")
                 "01g5mpvsm78lkd0yin82gyancrl23npy69qcp3d60vmm72yiwirz"))))
     (build-system zig-build-system)
     (arguments
-     (list #:zig zig-0.12))
+     (list #:zig zig-0.12
+           #:install-source? #f))
     (inputs (list ncurses `(,zstd "lib")))
     (native-inputs (list pkg-config))
     (properties `((tunable? . #t)))))
diff --git a/gnu/packages/zig-xyz.scm b/gnu/packages/zig-xyz.scm
index 0cd08b6ac6..9c86dde556 100644
--- a/gnu/packages/zig-xyz.scm
+++ b/gnu/packages/zig-xyz.scm
@@ -53,6 +53,7 @@
     (build-system zig-build-system)
     (arguments
      (list
+      #:install-source? #f
       #:phases
       #~(modify-phases %standard-phases
           (add-after 'install 'install-wayland-session
@@ -98,6 +99,7 @@ directly from a tty using KMS/DRM.")
     (arguments
      (list
       #:zig zig-0.9
+      #:install-source? #f
       #:zig-release-type "safe"))
     (synopsis "Distributed financial accounting database")
     (description "TigerBeetle is a financial accounting database designed for
@@ -122,8 +124,9 @@ mission-critical safety and performance for financial 
services.")
     (build-system zig-build-system)
     (inputs (list zig-0.10 python))
     (arguments
-     ;; The tests fail with memory leaks.
-     (list #:tests? #f))
+     (list #:install-source? #f
+           ;; The tests fail with memory leaks.
+           #:tests? #f))
     (synopsis "Zig language server")
     (description
      "Zig Language Server is a language server implementing the @acronym{LSP,
diff --git a/gnu/packages/zig.scm b/gnu/packages/zig.scm
index 2d57732bdf..0ac21ff24c 100644
--- a/gnu/packages/zig.scm
+++ b/gnu/packages/zig.scm
@@ -23,6 +23,7 @@
   #:use-module (guix gexp)
   #:use-module (guix packages)
   #:use-module (guix platform)
+  #:use-module (guix search-paths)
   #:use-module (guix utils)
   #:use-module (guix download)
   #:use-module (guix git-download)
@@ -32,7 +33,51 @@
   #:use-module (gnu packages compression)
   #:use-module (gnu packages llvm)
   #:use-module (gnu packages llvm-meta)
-  #:use-module (gnu packages web))
+  #:use-module (gnu packages web)
+  #:export (add-build.zig.zon
+            rename-zig-dependencies))
+
+(define* (add-build.zig.zon name version dependencies #:optional (paths '("")))
+  "Snippet to generate build.zig.zon of DEPENDENCIES for package NAME@VERSION."
+  `(let ((port (open-file "build.zig.zon" "w" #:encoding "utf8")))
+     (format port "\
+.{
+    .name = \"~a\",
+    .version = \"~a\",
+    .paths = .{
+~{\
+        \"~a\",
+~}\
+    },
+    .dependencies = .{
+~{\
+        .@\"~a\" = .{
+            .url = \"\",
+        },
+~}\
+    },
+}~%" ,name ,version (quote ,paths) (quote ,dependencies))
+     (close-port port)))
+
+(define* (rename-zig-dependencies mapping #:optional (directories '(".")))
+  "Snippet to rename Zig dependencies in build.zig and build.zig.zon."
+  `(begin
+     (use-modules (ice-9 match)
+                  (guix build utils))
+     (for-each
+      (match-lambda
+        ((old-name . new-name)
+         (for-each
+          (lambda (directory)
+            (with-directory-excursion directory
+              (substitute* "build.zig"
+                (((string-append "([Dd]ependency.\")" old-name) _ prefix)
+                 (string-append prefix new-name)))
+              (substitute* "build.zig.zon"
+                (((format #f "\\.(@\")?~a\"?" old-name))
+                 (format #f ".@\"~a\"" new-name)))))
+          (quote ,directories))))
+      (quote ,mapping))))
 
 (define (zig-source version commit hash)
   (origin
@@ -168,16 +213,12 @@
      (list llvm-13
            zig-0.9-glibc-abi-tool))
     (native-search-paths
-     (list
-      (search-path-specification
-       (variable "C_INCLUDE_PATH")
-       (files '("include")))
-      (search-path-specification
-       (variable "CPLUS_INCLUDE_PATH")
-       (files '("include/c++" "include")))
-      (search-path-specification
-       (variable "LIBRARY_PATH")
-       (files '("lib" "lib64")))))
+     (list $C_INCLUDE_PATH
+           $CPLUS_INCLUDE_PATH
+           $LIBRARY_PATH
+           (search-path-specification
+            (variable "GUIX_ZIG_PACKAGE_PATH")
+            (files '("src/zig")))))
     (synopsis "General purpose programming language and toolchain")
     (description "Zig is a general-purpose programming language and
 toolchain.  Among other features it provides
diff --git a/guix/build-system/zig.scm b/guix/build-system/zig.scm
index 82df75729c..6d16303794 100644
--- a/guix/build-system/zig.scm
+++ b/guix/build-system/zig.scm
@@ -48,6 +48,8 @@
                     source
                     (tests? #t)
                     (test-target #f)
+                    (install-source? #t)
+                    (skip-build? #f)
                     (zig-build-flags ''())
                     (zig-test-flags ''())
                     (zig-release-type #f)
@@ -68,13 +70,15 @@
                      #:source #+source
                      #:system #$system
                      #:test-target #$test-target
+                     #:install-source? #$install-source?
+                     #:skip-build? #$skip-build?
                      #:zig-build-flags #$zig-build-flags
                      ;; For reproducibility.
                      #:zig-build-target #$(platform-target
                                            (lookup-platform-by-system system))
                      #:zig-test-flags #$zig-test-flags
                      #:zig-release-type #$zig-release-type
-                     #:tests? #$tests?
+                     #:tests? #$(and tests? (not skip-build?))
                      #:phases #$phases
                      #:outputs #$(outputs->gexp outputs)
                      #:search-paths '#$(sexp->gexp
@@ -98,6 +102,8 @@
                           (native-search-paths '())
                           (tests? #t)
                           (test-target #f)
+                          (install-source? #t)
+                          (skip-build? #f)
                           (zig-build-flags ''())
                           (zig-test-flags ''())
                           (zig-destdir "out")
@@ -141,13 +147,15 @@
                      #:native-search-paths '#$(map
                                                 search-path-specification->sexp
                                                 native-search-paths)
+                     #:install-source? #$install-source?
+                     #:skip-build? #$skip-build?
                      #:zig-build-flags #$zig-build-flags
                      #:zig-build-target #$target
                      #:zig-test-flags #$zig-test-flags
                      #:zig-release-type #$zig-release-type
                      #:zig-destdir #$zig-destdir
                      #:zig-test-destdir #$zig-test-destdir
-                     #:tests? #$tests?
+                     #:tests? #$(and tests? (not skip-build?))
                      #:search-paths '#$(sexp->gexp
                                         (map search-path-specification->sexp
                                              search-paths))))))
diff --git a/guix/build/zig-build-system.scm b/guix/build/zig-build-system.scm
index d721baf57c..8aabe43c80 100644
--- a/guix/build/zig-build-system.scm
+++ b/guix/build/zig-build-system.scm
@@ -23,38 +23,103 @@
   #:use-module (ice-9 popen)
   #:use-module (ice-9 rdelim)
   #:use-module (ice-9 ftw)
-  #:use-module (ice-9 format)
   #:use-module (ice-9 match)
-  #:use-module (rnrs io ports)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:export (%standard-phases
-            zig-build))
+            zig-build
+            zig-source-install-path))
 
 ;; Interesting guide here:
 ;; https://github.com/riverwm/river/blob/master/PACKAGING.md
 
+(define (zig-source-install-path output)
+  (string-append output "/src/zig/" (strip-store-file-name output)))
+
+(define (zig-input-install-path input)
+  (zig-source-install-path
+   (dirname (dirname (dirname (canonicalize-path input))))))
+
+;; Notes on Zig package manager (`build.zig.zon')
+;; 1. Dependency definition (name -> URL + hash)
+;; - Dependency names are not necessarily consistent across packages.
+;; - `zig fetch <url> --name=<name>' fetches <url> to Zig cache, computes its
+;; hash, then overwrites dependency <name> with <url> and the computed hash.
+;; 2. Dependency lookup
+;; Lookup hash in Zig cache, fetch URL when missing.
+;; 3. Recursive dependency is possible
+;; `build.zig.zon' -> dependency (`build.zig.zon' -> next dependency).
+;;
+;; With our naming convention and different expectation package sources, we'll
+;; need to change dependency names and hashes for nearly every Zig package.
+;; - `rename-zig-dependencies' in `(gnu packages zig)' takes care of names.
+;; - `unpack-dependencies' below is for hashes.  Ideally we can parse
+;; `build.zig.zon' and only `zig fetch' required dependencies for current
+;; package, this way recursive dependencies are resolved by fetching store 
paths
+;; in their URLs.  However Zig package manager currently doesn't support file
+;; URLs[0] so we are fetching all dependencies needed for the build process and
+;; they are added to all involved `build.zig.zon' as a side effect.
+;;
+;; [0]: https://github.com/ziglang/zig/pull/21931
+(define (unpack-dependencies . _)
+  "Unpack Zig dependencies from GUIX_ZIG_PACKAGE_PATH."
+  (define zig-inputs
+    (append-map
+     (lambda (directory)
+       (map (lambda (input-name)
+              (cons input-name
+                    (in-vicinity directory input-name)))
+            (scandir directory (negate (cut member <> '("." ".."))))))
+     (or (and=> (getenv "GUIX_ZIG_PACKAGE_PATH")
+                (cut string-split <> #\:))
+         '())))
+  ;; Asserting "out" output, as in `install'.
+  ;; zig-aaa-0.1.2-3.456789a -> zig-aaa
+  ;; zig-bbb-2.3.3 -> zig-bbb
+  (define (strip-version input)
+    (let* ((name+version (string-split input #\-))
+           (guix-revision?
+            (not (= (length (string-split (last name+version) #\.))
+                    3))))
+      (string-join
+       (drop-right name+version (if guix-revision? 2 1))
+       "-")))
+  (for-each
+   (lambda (build.zig)
+     (with-directory-excursion (dirname build.zig)
+       (for-each
+        (match-lambda
+          ((input-name . input-path)
+           (let ((call `("zig" "fetch"
+                         ,(zig-input-install-path input-path)
+                         ,(string-append "--save=" (strip-version 
input-name)))))
+             (format #t "running: ~s~%" call)
+             (apply invoke call))))
+        (reverse zig-inputs))))
+   (find-files "." "^build\\.zig$")))
+
 (define* (build #:key
                 zig-build-flags
                 zig-build-target
-                zig-release-type       ;; "safe", "fast" or "small" empty for a
-                                       ;; debug build"
+                ;; "safe", "fast" or "small", empty for a "debug" build.
+                zig-release-type
+                skip-build?
                 #:allow-other-keys)
   "Build a given Zig package."
-
-  (setenv "DESTDIR" "out")
-  (let ((call `("zig" "build"
-                     "--prefix"             ""            ;; Don't add /usr
-                     "--prefix-lib-dir"     "lib"
-                     "--prefix-exe-dir"     "bin"
-                     "--prefix-include-dir" "include"
-                     ,(string-append "-Dtarget=" (zig-target zig-build-target))
-                     ,@(if zig-release-type
-                         (list (string-append "-Drelease-" zig-release-type))
-                         '())
-                     ,@zig-build-flags)))
-  (format #t "running: ~s~%" call)
-  (apply invoke call)))
+  (when (not skip-build?)
+    (setenv "DESTDIR" "out")
+    (let ((call `("zig" "build"
+                  "--prefix"             ""            ;; Don't add /usr
+                  "--prefix-lib-dir"     "lib"
+                  "--prefix-exe-dir"     "bin"
+                  "--prefix-include-dir" "include"
+                  ,(string-append "-Dtarget=" (zig-target zig-build-target))
+                  ,@(if zig-release-type
+                        (list (string-append "-Drelease-" zig-release-type))
+                        '())
+                  ,@zig-build-flags)))
+      (format #t "running: ~s~%" call)
+      (apply invoke call))))
 
 (define* (check #:key tests?
                 zig-test-flags
@@ -72,15 +137,22 @@
         (setenv "DESTDIR" old-destdir)
         (unsetenv "DESTDIR")))))
 
-(define* (install #:key inputs outputs #:allow-other-keys)
+(define* (install #:key outputs install-source? #:allow-other-keys)
   "Install a given Zig package."
-  (let ((out (assoc-ref outputs "out")))
-    (copy-recursively "out" out)))
+  (let* ((out (assoc-ref outputs "out"))
+         (source-install-path (zig-source-install-path out)))
+    (when (file-exists? "out")
+      (copy-recursively "out" out)
+      (delete-file-recursively "out"))
+    (when install-source?
+      (mkdir-p source-install-path)
+      (copy-recursively "." source-install-path))))
 
 (define %standard-phases
   (modify-phases gnu:%standard-phases
     (delete 'bootstrap)
     (replace 'configure zig-configure)
+    (add-after 'configure 'unpack-dependencies unpack-dependencies)
     (replace 'build build)
     (replace 'check check)
     (replace 'install install)))

Reply via email to