From 239a22c9711c4103e51f15013316ea8f8771a09f Mon Sep 17 00:00:00 2001 From: rczb Date: Thu, 12 Dec 2024 17:40:14 +0800 Subject: [PATCH] writers: add writeGuile[Bin] --- pkgs/build-support/writers/scripts.nix | 129 +++++++++++++++++++++++++ pkgs/build-support/writers/test.nix | 55 +++++++++++ 2 files changed, 184 insertions(+) diff --git a/pkgs/build-support/writers/scripts.nix b/pkgs/build-support/writers/scripts.nix index f0d7d7393fb8..4450b79a8fa8 100644 --- a/pkgs/build-support/writers/scripts.nix +++ b/pkgs/build-support/writers/scripts.nix @@ -613,6 +613,135 @@ rec { */ writeBabashkaBin = name: writeBabashka "/bin/${name}"; + /** + `writeGuile` returns a derivation that creates an executable Guile script. + + # Inputs + + `nameOrPath` (String) + : Name of or path to the script. The semantics is the same as that of + `makeScriptWriter`. + + `config` (AttrSet) + : `guile` (Optional, Derivation, Default: `pkgs.guile`) + : Guile package used for the script. + : `libraries` (Optional, [ Derivation ], Default: []) + : Extra Guile libraries exposed to the script. + : `r6rs` and `r7rs` (Optional, Boolean, Default: false) + : Whether to adapt Guile’s initial environment to better support R6RS/ + R7RS. See the [Guile Reference Manual](https://www.gnu.org/software/guile/manual/html_node/index.html) + for details. + : `srfi` (Optional, [ Int ], Default: []) + : SRFI module to be loaded into the interpreter before evaluating a + script file or starting the REPL. See the Guile Reference Manual to + know which SRFI are supported. + : Other attributes are directly passed to `makeScriptWriter`. + + `content` (String) + : Content of the script. + + # Examples + + :::{.example} + ## `pkgs.writers.writeGuile` with default config + + ```nix + writeGuile "guile-script" { } + '' + (display "Hello, world!") + '' + ``` + ::: + + :::{.example} + ## `pkgs.writers.writeGuile` with SRFI-1 enabled and extra libraries + + ```nix + writeGuile "guile-script" { + libraries = [ pkgs.guile-semver ]; + srfi = [ 1 ]; + } + '' + (use-modules (semver)) + (make-semver 1 (third '(2 3 4)) 5) ; => # + '' + ``` + ::: + */ + writeGuile = + nameOrPath: + { + guile ? pkgs.guile, + libraries ? [ ], + r6rs ? false, + r7rs ? false, + srfi ? [ ], + ... + }@config: + content: + assert builtins.all builtins.isInt srfi; + let + finalGuile = pkgs.buildEnv { + name = "guile-env"; + paths = [ guile ] ++ libraries; + passthru = { + inherit (guile) siteDir siteCcacheDir; + }; + meta.mainProgram = guile.meta.mainProgram or "guile"; + }; + in + makeScriptWriter + ( + (builtins.removeAttrs config [ + "guile" + "libraries" + "r6rs" + "r7rs" + "srfi" + ]) + // { + interpreter = "${lib.getExe finalGuile} \\"; + makeWrapperArgs = [ + "--set" + "GUILE_LOAD_PATH" + "${finalGuile}/${finalGuile.siteDir}:${finalGuile}/lib/scheme-libs" + "--set" + "GUILE_LOAD_COMPILED_PATH" + "${finalGuile}/${finalGuile.siteCcacheDir}:${finalGuile}/lib/libobj" + "--set" + "LD_LIBRARY_PATH" + "${finalGuile}/lib/ffi" + "--set" + "DYLD_LIBRARY_PATH" + "${finalGuile}/lib/ffi" + ]; + } + ) + nameOrPath + /* + Spaces, newlines and tabs are significant for the "meta switch" of Guile, so + certain complication must be made to ensure correctness. + */ + ( + lib.concatStringsSep "\n" [ + (lib.concatStringsSep " " ( + [ "--no-auto-compile" ] + ++ lib.optional r6rs "--r6rs" + ++ lib.optional r7rs "--r7rs" + ++ lib.optional (srfi != [ ]) ("--use-srfi=" + concatMapStringsSep "," builtins.toString srfi) + ++ [ "-s" ] + )) + "!#" + content + ] + ); + + /** + writeGuileBin takes the same arguments as writeGuile but outputs a directory + (like writeScriptBin) + */ + writeGuileBin = name: writeGuile "/bin/${name}"; + /** writeHaskell takes a name, an attrset with libraries and haskell version (both optional) and some haskell source code and returns an executable. diff --git a/pkgs/build-support/writers/test.nix b/pkgs/build-support/writers/test.nix index 50419d532c40..47c35ef5448f 100644 --- a/pkgs/build-support/writers/test.nix +++ b/pkgs/build-support/writers/test.nix @@ -1,6 +1,8 @@ { haskellPackages, lib, + guile-lib, + akkuPackages, nodePackages, perlPackages, python3Packages, @@ -26,6 +28,8 @@ let writeFish writeFishBin writeFSharp + writeGuile + writeGuileBin writeHaskell writeHaskellBin writeJS @@ -109,6 +113,12 @@ recurseIntoAttrs { '' ); + guile = expectSuccessBin ( + writeGuileBin "test-writers-guile-bin" { } '' + (display "success\n") + '' + ); + rust = expectSuccessBin ( writeRustBin "test-writers-rust-bin" { } '' fn main(){ @@ -249,6 +259,51 @@ recurseIntoAttrs { '' ); + guile = expectSuccess ( + writeGuile "test-writers-guile" + { + libraries = [ guile-lib ]; + srfi = [ 1 ]; + } + '' + (use-modules (unit-test)) + (assert-true (= (second '(1 2 3)) + 2)) + (display "success\n") + '' + ); + + guileR6RS = expectSuccess ( + writeGuile "test-writers-guile-r6rs" + { + r6rs = true; + libraries = with akkuPackages; [ r6rs-slice ]; + } + '' + (import (rnrs base (6)) + (rnrs io simple (6)) + (slice)) + (assert (equal? (slice '(1 2 3) 0 2) + '(1 2))) + (display "success\n") + '' + ); + + guileR7RS = expectSuccess ( + writeGuile "test-writers-guile-r7rs" + { + r7rs = true; + } + '' + (import (scheme write) + (srfi 1)) + (unless (= (second '(1 2 3)) + 2) + (error "The value should be 2.")) + (display "success\n") + '' + ); + haskell = expectSuccess ( writeHaskell "test-writers-haskell" { libraries = [ haskellPackages.acme-default ]; } '' import Data.Default