about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--fun/gemma/default.nix5
-rw-r--r--fun/🕰️/default.nix9
-rw-r--r--lisp/dns/default.nix4
-rw-r--r--lisp/klatre/klatre.lisp3
-rw-r--r--nix/buildLisp/default.nix161
-rw-r--r--third_party/lisp/bordeaux-threads.nix19
-rw-r--r--third_party/lisp/cffi.nix9
-rw-r--r--third_party/lisp/cl-fad.nix4
-rw-r--r--third_party/lisp/cl-plus-ssl.nix4
-rw-r--r--third_party/lisp/cl-smtp.nix4
-rw-r--r--third_party/lisp/cl-unicode.nix5
-rw-r--r--third_party/lisp/closer-mop.nix5
-rw-r--r--third_party/lisp/drakma.nix4
-rw-r--r--third_party/lisp/easy-routes.nix3
-rw-r--r--third_party/lisp/hunchentoot.nix4
-rw-r--r--third_party/lisp/ironclad.nix37
-rw-r--r--third_party/lisp/lisp-binary.nix4
-rw-r--r--third_party/lisp/md5.nix7
-rw-r--r--third_party/lisp/moptilities.nix4
-rw-r--r--third_party/lisp/nibbles.nix9
-rw-r--r--third_party/lisp/postmodern.nix4
-rw-r--r--third_party/lisp/restas.nix3
-rw-r--r--third_party/lisp/trivial-features.nix5
-rw-r--r--third_party/lisp/trivial-ldap.nix4
-rw-r--r--third_party/lisp/trivial-mimes.nix10
-rw-r--r--third_party/lisp/uax-15.nix1
-rw-r--r--third_party/lisp/usocket.nix8
-rw-r--r--third_party/nixpkgs/default.nix1
-rw-r--r--third_party/overlays/ecl-static.nix41
-rw-r--r--users/sterni/clhs-lookup/default.nix5
-rw-r--r--web/panettone/default.nix4
31 files changed, 346 insertions, 44 deletions
diff --git a/fun/gemma/default.nix b/fun/gemma/default.nix
index 902f5857db42..20acace2d007 100644
--- a/fun/gemma/default.nix
+++ b/fun/gemma/default.nix
@@ -47,4 +47,9 @@ in depot.nix.buildLisp.program {
     ./src/gemma.lisp
     injectFrontend
   ];
+
+  # depends on SBCL
+  brokenOn = [
+    "ecl"
+  ];
 }
diff --git a/fun/🕰️/default.nix b/fun/🕰️/default.nix
index d6fd5fc35ef6..230d9f02f137 100644
--- a/fun/🕰️/default.nix
+++ b/fun/🕰️/default.nix
@@ -21,7 +21,10 @@ let
     deps = [
       depot.third_party.lisp.unix-opts
       depot.lisp.klatre
-      (buildLisp.bundled "uiop")
+      {
+        default = buildLisp.bundled "asdf";
+        sbcl = buildLisp.bundled "uiop";
+      }
       lib
     ];
 
@@ -30,6 +33,10 @@ let
     ];
 
     main = "🕰️.bin:🚂";
+
+    brokenOn = [
+      "ecl" # refuses to create non-ASCII paths even on POSIX…
+    ];
   };
 in bin // {
   inherit lib;
diff --git a/lisp/dns/default.nix b/lisp/dns/default.nix
index 43e7ea5030b7..cb2445b460c9 100644
--- a/lisp/dns/default.nix
+++ b/lisp/dns/default.nix
@@ -14,4 +14,8 @@ depot.nix.buildLisp.library {
     ./message.lisp
     ./client.lisp
   ];
+
+  brokenOn = [
+    "ecl" # dynamic cffi
+  ];
 }
diff --git a/lisp/klatre/klatre.lisp b/lisp/klatre/klatre.lisp
index 0b986ac25125..79c7259752c6 100644
--- a/lisp/klatre/klatre.lisp
+++ b/lisp/klatre/klatre.lisp
@@ -105,7 +105,8 @@ separated by SEP."
   "Attempt to parse STR as an integer, returning nil if it is invalid."
   (check-type str string)
   (handler-case (parse-integer str)
-    (sb-int:simple-parse-error (_) (declare (ignore _)) nil)))
+    (#+sbcl sb-int:simple-parse-error
+     #-sbcl parse-error (_) (declare (ignore _)) nil)))
 
 ;;;
 ;;; Function utilities
diff --git a/nix/buildLisp/default.nix b/nix/buildLisp/default.nix
index c214a542de87..ec42cc66f30b 100644
--- a/nix/buildLisp/default.nix
+++ b/nix/buildLisp/default.nix
@@ -8,7 +8,7 @@
 
 let
   inherit (builtins) map elemAt match filter;
-  inherit (pkgs) lib runCommandNoCC makeWrapper writeText writeShellScriptBin sbcl;
+  inherit (pkgs) lib runCommandNoCC makeWrapper writeText writeShellScriptBin sbcl ecl-static;
 
   #
   # Internal helper definitions
@@ -16,6 +16,19 @@ let
 
   defaultImplementation = "sbcl";
 
+  # Many Common Lisp implementations (like ECL and CCL) will occasionally drop
+  # you into an interactive debugger even when executing something as a script.
+  # In nix builds we don't want such a situation: Any error should make the
+  # script exit non-zero. Luckily the ANSI standard specifies *debugger-hook*
+  # which is invoked before the debugger letting us just do that.
+  disableDebugger = writeText "disable-debugger.lisp" ''
+    (setf *debugger-hook*
+          (lambda (error hook)
+            (declare (ignore hook))
+            (format *error-output* "~%Unhandled error: ~a~%" error)
+            #+ecl (ext:quit 1)))
+  '';
+
   # Process a list of arbitrary values which also contains “implementation
   # filter sets” which describe conditonal inclusion of elements depending
   # on the CL implementation used. Elements are processed in the following
@@ -199,6 +212,9 @@ let
   #   executable which runs 'main' (and exits) where 'main' is available from
   #   'deps'. The executable should be created as "$out/bin/${name}", usually
   #   by dumping the lisp image with the replaced toplevel function replaced.
+  # - wrapProgram :: boolean
+  #   Whether to wrap the resulting binary / image with a wrapper script setting
+  #   `LD_LIBRARY_PATH`.
   # - genTestLisp :: { name, srcs, deps, expression } -> file
   #   Builds a lisp file which loads the given 'deps' and 'srcs' files and
   #   then evaluates 'expression'. Depending on whether 'expression' returns
@@ -291,6 +307,8 @@ let
                              :purify t))
       '';
 
+      wrapProgram = true;
+
       genTestLisp = genTestLispGeneric impls.sbcl;
 
       lispWith = deps:
@@ -304,6 +322,141 @@ let
           } $@
         '';
     };
+    ecl = {
+      runScript = "${ecl-static}/bin/ecl --load ${disableDebugger} --shell";
+      faslExt = "fasc";
+      genLoadLisp = genLoadLispGeneric impls.ecl;
+      genCompileLisp = { name, srcs, deps }: writeText "ecl-compile.lisp" ''
+        ;; This seems to be required to bring make the 'c' package available
+        ;; early, otherwise ECL tends to fail with a read failure…
+        (ext:install-c-compiler)
+
+        ;; Load dependencies
+        ${impls.ecl.genLoadLisp deps}
+
+        (defun getenv-or-fail (var)
+          (or (ext:getenv var)
+              (error (format nil "Missing expected environment variable ~A" var))))
+
+        (defun nix-compile-file (srcfile &key native)
+          "Compile the given srcfile into a compilation unit in :out-dir using
+          a unique name based on srcfile as the filename which is returned after
+          compilation. If :native is true, create an native object file,
+          otherwise a byte-compile fasc file is built and immediately loaded."
+
+          (let* ((unique-name (substitute #\_ #\/ srcfile))
+                 (out-file (make-pathname :type (if native "o" "fasc")
+                                          :directory (getenv-or-fail "NIX_BUILD_TOP")
+                                          :name unique-name)))
+            (multiple-value-bind (out-truename _warnings-p failure-p)
+                (compile-file srcfile :system-p native
+                                      :load (not native)
+                                      :output-file out-file
+                                      :verbose t :print t)
+              (if failure-p (ext:quit 1) out-truename))))
+
+        (let* ((out-dir (getenv-or-fail "out"))
+               (nix-build-dir (getenv-or-fail "NIX_BUILD_TOP"))
+               (srcs
+                ;; These forms are inserted by the Nix build
+                '(${lib.concatMapStringsSep "\n" (src: "\"${src}\"") srcs})))
+
+          ;; First, we'll byte compile loadable FASL files and load them
+          ;; immediately. Since we are using a statically linked ECL, there's
+          ;; no way to load native objects, so we rely on byte compilation
+          ;; for all our loading — which is crucial in compilation of course.
+          (ext:install-bytecodes-compiler)
+
+          ;; ECL's bytecode FASLs can just be concatenated to create a bundle
+          ;; at least since a recent bugfix which we apply as a patch.
+          ;; See also: https://gitlab.com/embeddable-common-lisp/ecl/-/issues/649
+          (let ((bundle-out (make-pathname :type "fasc" :name "${name}"
+                                           :directory out-dir)))
+
+            (with-open-file (fasc-stream bundle-out :direction :output)
+              (ext:run-program "cat"
+                               (mapcar (lambda (f)
+                                         (namestring
+                                          (nix-compile-file f :native nil)))
+                                       srcs)
+                               :output fasc-stream)))
+
+          (ext:install-c-compiler)
+
+          ;; Build a (natively compiled) static archive (.a) file. We want to
+          ;; use this for (statically) linking an executable later. The bytecode
+          ;; dance is only required because we can't load such archives.
+          (c:build-static-library
+           (make-pathname :type "a" :name "${name}" :directory out-dir)
+           :lisp-files (mapcar (lambda (x)
+                                 (nix-compile-file x :native t))
+                               srcs)))
+      '';
+      genDumpLisp = { name, main, deps }: writeText "ecl-dump.lisp" ''
+        (defun getenv-or-fail (var)
+          (or (ext:getenv var)
+              (error (format nil "Missing expected environment variable ~A" var))))
+
+        ${impls.ecl.genLoadLisp deps}
+
+        ;; makes a 'c' package available that can link executables
+        (ext:install-c-compiler)
+
+        (c:build-program
+         (make-pathname :name "${name}"
+                        :directory (concatenate 'string
+                                                (getenv-or-fail "out")
+                                                "/bin"))
+         :epilogue-code `(progn
+                          ;; UIOP doesn't understand ECL, so we need to make it
+                          ;; aware that we are a proper executable, causing it
+                          ;; to handle argument parsing and such properly. Since
+                          ;; this needs to work even when we're not using UIOP,
+                          ;; we need to do some compile-time acrobatics.
+                          ,(when (find-package 'uiop)
+                            `(setf ,(find-symbol "*IMAGE-DUMPED-P*" :uiop) :executable))
+                          ;; Run the actual application…
+                          (${main})
+                          ;; … and exit.
+                          (ext:quit))
+         ;; ECL can't remember these from its own build…
+         :ld-flags '("-static")
+         :lisp-files
+         ;; The following forms are inserted by the Nix build
+         '(${
+             lib.concatMapStrings (dep: ''
+               "${dep}/${dep.lispName}.a"
+             '') (allDeps impls.ecl deps)
+           }))
+      '';
+
+      wrapProgram = false;
+
+      genTestLisp = genTestLispGeneric impls.ecl;
+
+      lispWith = deps:
+        let lispDeps = filter (d: !d.lispBinary) (allDeps impls.ecl deps);
+        in writeShellScriptBin "ecl" ''
+          exec ${ecl-static}/bin/ecl ${
+            lib.optionalString (deps != [])
+              "--load ${writeText "load.lisp" (impls.ecl.genLoadLisp lispDeps)}"
+          } $@
+        '';
+
+      bundled = name: runCommandNoCC "${name}-cllib" {
+        passthru = {
+          lispName = name;
+          lispNativeDeps = [];
+          lispDeps = [];
+          lispBinary = false;
+          repl = impls.ecl.lispWith [ (impls.ecl.bundled name) ];
+        };
+      } ''
+        mkdir -p "$out"
+        ln -s "${ecl-static}/lib/ecl-${ecl-static.version}/${name}.${impls.ecl.faslExt}" -t "$out"
+        ln -s "${ecl-static}/lib/ecl-${ecl-static.version}/lib${name}.a" "$out/${name}.a"
+      '';
+    };
   };
 
   #
@@ -412,7 +565,7 @@ let
         lispBinary = true;
         tests = testDrv;
       };
-    } ''
+    } (''
       ${if ! isNull testDrv
         then "echo 'Test ${testDrv} succeeded'"
         else ""}
@@ -424,9 +577,9 @@ let
           deps = ([ selfLib ] ++ lispDeps);
         }
       }
-
+    '' + lib.optionalString impl.wrapProgram ''
       wrapProgram $out/bin/${name} --prefix LD_LIBRARY_PATH : "${libPath}"
-    '');
+    ''));
 
   # 'bundled' creates a "library" which makes a built-in package available,
   # such as any of SBCL's sb-* packages or ASDF. By default this is done
diff --git a/third_party/lisp/bordeaux-threads.nix b/third_party/lisp/bordeaux-threads.nix
index b2596672bad2..92bc1f2629c4 100644
--- a/third_party/lisp/bordeaux-threads.nix
+++ b/third_party/lisp/bordeaux-threads.nix
@@ -2,18 +2,25 @@
 # in Common Lisp simple.
 { depot, ... }:
 
-let src = builtins.fetchGit {
-  url = "https://github.com/sionescu/bordeaux-threads.git";
-  rev = "499b6d3f0ce635417d6096acf0a671d8bf3f6e5f";
-};
+let
+  src = builtins.fetchGit {
+    url = "https://github.com/sionescu/bordeaux-threads.git";
+    rev = "499b6d3f0ce635417d6096acf0a671d8bf3f6e5f";
+  };
+  getSrc = f: "${src}/src/${f}";
 in depot.nix.buildLisp.library {
   name = "bordeaux-threads";
   deps = [ depot.third_party.lisp.alexandria ];
 
-  srcs = map (f: src + ("/src/" + f)) [
+  srcs = map getSrc [
     "pkgdcl.lisp"
     "bordeaux-threads.lisp"
-    "impl-sbcl.lisp"
+  ] ++ [
+    {
+      sbcl = getSrc "impl-sbcl.lisp";
+      ecl = getSrc "impl-ecl.lisp";
+    }
+  ] ++ map getSrc [
     "default-implementations.lisp"
   ];
 }
diff --git a/third_party/lisp/cffi.nix b/third_party/lisp/cffi.nix
index 9a50e57e0551..89fe9fcad4f4 100644
--- a/third_party/lisp/cffi.nix
+++ b/third_party/lisp/cffi.nix
@@ -13,11 +13,14 @@ in buildLisp.library {
     babel
     trivial-features
     (buildLisp.bundled "asdf")
-    (buildLisp.bundled "uiop")
   ];
 
-  srcs = map (f: src + ("/src/" + f)) [
-    "cffi-sbcl.lisp"
+  srcs = [
+    {
+      ecl = src + "/src/cffi-ecl.lisp";
+      sbcl = src + "/src/cffi-sbcl.lisp";
+    }
+  ] ++ map (f: src + ("/src/" + f)) [
     "package.lisp"
     "utils.lisp"
     "libraries.lisp"
diff --git a/third_party/lisp/cl-fad.nix b/third_party/lisp/cl-fad.nix
index 54f6328d53e6..2249db66ac18 100644
--- a/third_party/lisp/cl-fad.nix
+++ b/third_party/lisp/cl-fad.nix
@@ -15,7 +15,9 @@ in buildLisp.library {
   deps = with depot.third_party.lisp; [
     alexandria
     bordeaux-threads
-    (buildLisp.bundled "sb-posix")
+    {
+      sbcl = buildLisp.bundled "sb-posix";
+    }
   ];
 
   srcs = map (f: src + ("/" + f)) [
diff --git a/third_party/lisp/cl-plus-ssl.nix b/third_party/lisp/cl-plus-ssl.nix
index e4f3fd95e406..1dab7c3abba4 100644
--- a/third_party/lisp/cl-plus-ssl.nix
+++ b/third_party/lisp/cl-plus-ssl.nix
@@ -37,4 +37,8 @@ in buildLisp.library {
     "context.lisp"
     "verify-hostname.lisp"
   ];
+
+  brokenOn = [
+    "ecl" # dynamic cffi
+  ];
 }
diff --git a/third_party/lisp/cl-smtp.nix b/third_party/lisp/cl-smtp.nix
index 6b6b415a03a5..a9905b5ef6f5 100644
--- a/third_party/lisp/cl-smtp.nix
+++ b/third_party/lisp/cl-smtp.nix
@@ -25,4 +25,8 @@ in depot.nix.buildLisp.library {
     "cl-smtp.lisp"
     "mime-types.lisp"
   ];
+
+  brokenOn = [
+    "ecl" # dynamic cffi
+  ];
 }
diff --git a/third_party/lisp/cl-unicode.nix b/third_party/lisp/cl-unicode.nix
index 8b42e2eaec80..5fff1fbe6bb2 100644
--- a/third_party/lisp/cl-unicode.nix
+++ b/third_party/lisp/cl-unicode.nix
@@ -29,7 +29,10 @@ let
     deps = with depot.third_party.lisp; [
       cl-unicode-base
       flexi-streams
-      (bundled "uiop")
+      {
+        ecl = bundled "asdf";
+        default = bundled "uiop";
+      }
     ];
 
     srcs = (map (f: src + ("/build/" + f)) [
diff --git a/third_party/lisp/closer-mop.nix b/third_party/lisp/closer-mop.nix
index 9e6aac36c163..d6a677625e4d 100644
--- a/third_party/lisp/closer-mop.nix
+++ b/third_party/lisp/closer-mop.nix
@@ -15,6 +15,9 @@ in depot.nix.buildLisp.library {
   srcs = [
     "${src}/closer-mop-packages.lisp"
     "${src}/closer-mop-shared.lisp"
-    "${src}/closer-sbcl.lisp"
+    {
+      sbcl = "${src}/closer-sbcl.lisp";
+      ecl = "${src}/closer-ecl.lisp";
+    }
   ];
 }
diff --git a/third_party/lisp/drakma.nix b/third_party/lisp/drakma.nix
index 80c82aee1f6f..3757aad7b144 100644
--- a/third_party/lisp/drakma.nix
+++ b/third_party/lisp/drakma.nix
@@ -32,4 +32,8 @@ in depot.nix.buildLisp.library {
     "encoding.lisp"
     "request.lisp"
   ];
+
+  brokenOn = [
+    "ecl" # dynamic cffi
+  ];
 }
diff --git a/third_party/lisp/easy-routes.nix b/third_party/lisp/easy-routes.nix
index 63eb8b5e3844..93aed8a66765 100644
--- a/third_party/lisp/easy-routes.nix
+++ b/third_party/lisp/easy-routes.nix
@@ -23,4 +23,7 @@ in depot.nix.buildLisp.library {
     "routes-map-printer.lisp"
   ];
 
+  brokenOn = [
+    "ecl" # dynamic cffi
+  ];
 }
diff --git a/third_party/lisp/hunchentoot.nix b/third_party/lisp/hunchentoot.nix
index 3006f5fd72d7..24eae6a348cf 100644
--- a/third_party/lisp/hunchentoot.nix
+++ b/third_party/lisp/hunchentoot.nix
@@ -58,4 +58,8 @@ in depot.nix.buildLisp.library {
     "acceptor.lisp"
     "easy-handlers.lisp"
   ];
+
+  brokenOn = [
+    "ecl" # dynamic cffi
+  ];
 }
diff --git a/third_party/lisp/ironclad.nix b/third_party/lisp/ironclad.nix
index fa860d4d0a06..fe0e052c32bc 100644
--- a/third_party/lisp/ironclad.nix
+++ b/third_party/lisp/ironclad.nix
@@ -10,33 +10,40 @@ let
     sha256 = "0k4bib9mbrzalbl9ivkw4a7g4c7bbad1l5jw4pzkifqszy2swkr5";
   };
 
+  getSrc = f: "${src}/src/${f}";
+
 in depot.nix.buildLisp.library {
   name = "ironclad";
 
   deps = with depot.third_party.lisp; [
     (bundled "asdf")
-    (bundled "sb-rotate-byte")
-    (bundled "sb-posix")
+    { sbcl = bundled "sb-rotate-byte"; }
+    { sbcl = bundled "sb-posix"; }
     alexandria
     bordeaux-threads
     nibbles
   ];
 
   srcs = [
-    "${src}/ironclad.asd"
-    # TODO(grfn): Figure out how to get this compiling with the assembly
-    # optimization eventually - see https://cl.tvl.fyi/c/depot/+/1333
-    (runCommand "package.lisp" {} ''
-      substitute ${src}/src/package.lisp $out \
-        --replace \#-ecl-bytecmp "" \
-        --replace '(pushnew :ironclad-assembly *features*)' ""
-    '')
-  ] ++ (map (f: src + ("/src/" + f)) [
+    {
+      # TODO(grfn): Figure out how to get this compiling with the assembly
+      # optimization eventually - see https://cl.tvl.fyi/c/depot/+/1333
+      sbcl = runCommand "package.lisp" {} ''
+        substitute ${src}/src/package.lisp $out \
+          --replace \#-ecl-bytecmp "" \
+          --replace '(pushnew :ironclad-assembly *features*)' ""
+      '';
+      default = getSrc "package.lisp";
+    }
+  ] ++ map getSrc [
     "macro-utils.lisp"
+  ] ++ [
+    { sbcl = getSrc "opt/sbcl/fndb.lisp"; }
+    { sbcl = getSrc "opt/sbcl/cpu-features.lisp"; }
+    { sbcl = getSrc "opt/sbcl/x86oid-vm.lisp"; }
 
-    "opt/sbcl/fndb.lisp"
-    "opt/sbcl/cpu-features.lisp"
-    "opt/sbcl/x86oid-vm.lisp"
+    { ecl = getSrc "opt/ecl/c-functions.lisp"; }
+  ] ++ map getSrc [
 
     "common.lisp"
     "conditions.lisp"
@@ -142,5 +149,5 @@ in depot.nix.buildLisp.library {
     "public-key/elgamal.lisp"
     "public-key/pkcs1.lisp"
     "public-key/rsa.lisp"
-  ]);
+  ];
 }
diff --git a/third_party/lisp/lisp-binary.nix b/third_party/lisp/lisp-binary.nix
index e6111c20a7d8..3e7a43b8ac67 100644
--- a/third_party/lisp/lisp-binary.nix
+++ b/third_party/lisp/lisp-binary.nix
@@ -28,4 +28,8 @@ in depot.nix.buildLisp.library {
     "binary-2.lisp"
     "types.lisp"
   ];
+
+  brokenOn = [
+    "ecl" # dynamic cffi
+  ];
 }
diff --git a/third_party/lisp/md5.nix b/third_party/lisp/md5.nix
index a789f7bc2af3..ef265d5b6e8f 100644
--- a/third_party/lisp/md5.nix
+++ b/third_party/lisp/md5.nix
@@ -11,6 +11,11 @@ let src = pkgs.fetchFromGitHub {
 };
 in buildLisp.library {
   name = "md5";
-  deps = [ (buildLisp.bundled "sb-rotate-byte") ];
+  deps = [
+    {
+      sbcl = buildLisp.bundled "sb-rotate-byte";
+      default = depot.third_party.lisp.flexi-streams;
+    }
+  ];
   srcs = [ (src + "/md5.lisp") ];
 }
diff --git a/third_party/lisp/moptilities.nix b/third_party/lisp/moptilities.nix
index 89cfb9a938ed..a8a387ab914f 100644
--- a/third_party/lisp/moptilities.nix
+++ b/third_party/lisp/moptilities.nix
@@ -11,4 +11,8 @@ in depot.nix.buildLisp.library {
   name = "moptilities";
   deps = [ depot.third_party.lisp.closer-mop ];
   srcs = [ "${src}/dev/moptilities.lisp" ];
+
+  brokenOn = [
+    "ecl" # TODO(sterni): https://gitlab.com/embeddable-common-lisp/ecl/-/issues/651
+  ];
 }
diff --git a/third_party/lisp/nibbles.nix b/third_party/lisp/nibbles.nix
index ec4e6e6b1042..b797c83a5fa1 100644
--- a/third_party/lisp/nibbles.nix
+++ b/third_party/lisp/nibbles.nix
@@ -24,9 +24,10 @@ in depot.nix.buildLisp.library {
     "types.lisp"
     "vectors.lisp"
     "streams.lisp"
-    "sbcl-opt/fndb.lisp"
-    "sbcl-opt/nib-tran.lisp"
-    "sbcl-opt/x86-vm.lisp"
-    "sbcl-opt/x86-64-vm.lisp"
+  ] ++ [
+    { sbcl = "${src}/sbcl-opt/fndb.lisp"; }
+    { sbcl = "${src}/sbcl-opt/nib-tran.lisp"; }
+    { sbcl = "${src}/sbcl-opt/x86-vm.lisp"; }
+    { sbcl = "${src}/sbcl-opt/x86-64-vm.lisp"; }
   ];
 }
diff --git a/third_party/lisp/postmodern.nix b/third_party/lisp/postmodern.nix
index 10a0da38921e..333a9d9b770f 100644
--- a/third_party/lisp/postmodern.nix
+++ b/third_party/lisp/postmodern.nix
@@ -83,6 +83,10 @@ let
       "table.lisp"
       "deftable.lisp"
     ]);
+
+    brokenOn = [
+      "ecl" # TODO(sterni): https://gitlab.com/embeddable-common-lisp/ecl/-/issues/651
+    ];
   };
 
 in postmodern // {
diff --git a/third_party/lisp/restas.nix b/third_party/lisp/restas.nix
index 8a0b5f907f29..cf231286e79a 100644
--- a/third_party/lisp/restas.nix
+++ b/third_party/lisp/restas.nix
@@ -35,4 +35,7 @@ in depot.nix.buildLisp.library {
     "policy.lisp"
   ];
 
+  brokenOn = [
+    "ecl" # dynamic cffi
+  ];
 }
diff --git a/third_party/lisp/trivial-features.nix b/third_party/lisp/trivial-features.nix
index 647ae9a3b091..3ad424b8abdc 100644
--- a/third_party/lisp/trivial-features.nix
+++ b/third_party/lisp/trivial-features.nix
@@ -7,6 +7,9 @@ let src = builtins.fetchGit {
 in depot.nix.buildLisp.library {
   name = "trivial-features";
   srcs = [
-    (src + "/src/tf-sbcl.lisp")
+    {
+      sbcl = src + "/src/tf-sbcl.lisp";
+      ecl = src + "/src/tf-ecl.lisp";
+    }
   ];
 }
diff --git a/third_party/lisp/trivial-ldap.nix b/third_party/lisp/trivial-ldap.nix
index ec111bc682e0..c8a27431c687 100644
--- a/third_party/lisp/trivial-ldap.nix
+++ b/third_party/lisp/trivial-ldap.nix
@@ -19,4 +19,8 @@ in depot.nix.buildLisp.library {
     "package.lisp"
     "trivial-ldap.lisp"
   ];
+
+  brokenOn = [
+    "ecl" # dynamic cffi
+  ];
 }
diff --git a/third_party/lisp/trivial-mimes.nix b/third_party/lisp/trivial-mimes.nix
index c4b21045c5f4..ce45993d05e9 100644
--- a/third_party/lisp/trivial-mimes.nix
+++ b/third_party/lisp/trivial-mimes.nix
@@ -10,14 +10,20 @@ let
 
   mime-types = pkgs.runCommand "mime-types.lisp" {} ''
     substitute ${src}/mime-types.lisp $out \
-      --replace /etc/mime.types ${src}/mime.types
+      --replace /etc/mime.types ${src}/mime.types \
+      --replace "(asdf:system-source-directory :trivial-mimes)" '"/bogus-dir"'
+      # We want to prevent an ASDF lookup at build time since this will
+      # generally fail — we are not using ASDF after all.
   '';
 
 in depot.nix.buildLisp.library {
   name = "trivial-mimes";
 
   deps = [
-    (depot.nix.buildLisp.bundled "uiop")
+    {
+      sbcl = depot.nix.buildLisp.bundled "uiop";
+      default = depot.nix.buildLisp.bundled "asdf";
+    }
   ];
 
   srcs = [ mime-types ];
diff --git a/third_party/lisp/uax-15.nix b/third_party/lisp/uax-15.nix
index 8d420d26f692..a13e5c1690d3 100644
--- a/third_party/lisp/uax-15.nix
+++ b/third_party/lisp/uax-15.nix
@@ -19,7 +19,6 @@ in depot.nix.buildLisp.library {
   deps = with depot.third_party.lisp; [
     split-sequence
     cl-ppcre
-    (bundled "uiop")
     (bundled "asdf")
   ];
 
diff --git a/third_party/lisp/usocket.nix b/third_party/lisp/usocket.nix
index 888d5e01a0d5..dbbfd2fbf110 100644
--- a/third_party/lisp/usocket.nix
+++ b/third_party/lisp/usocket.nix
@@ -33,6 +33,12 @@ in buildLisp.library {
     "package.lisp"
     "usocket.lisp"
     "condition.lisp"
-    "backend/sbcl.lisp"
+  ] ++ [
+    { sbcl = "${src}/backend/sbcl.lisp"; }
+
+    # ECL actually has two files, it supports the SBCL backend,
+    # but usocket also has some ECL specific code
+    { ecl = "${src}/backend/sbcl.lisp"; }
+    { ecl = "${src}/backend/ecl.lisp"; }
   ]);
 }
diff --git a/third_party/nixpkgs/default.nix b/third_party/nixpkgs/default.nix
index d04bfeea1577..c19809fc5a91 100644
--- a/third_party/nixpkgs/default.nix
+++ b/third_party/nixpkgs/default.nix
@@ -66,5 +66,6 @@ in import nixpkgsSrc {
     depot.third_party.overlays.tvl
     depot.third_party.overlays.haskell
     depot.third_party.overlays.emacs
+    depot.third_party.overlays.ecl-static
   ];
 }
diff --git a/third_party/overlays/ecl-static.nix b/third_party/overlays/ecl-static.nix
new file mode 100644
index 000000000000..beda6641a0a6
--- /dev/null
+++ b/third_party/overlays/ecl-static.nix
@@ -0,0 +1,41 @@
+{ ... }:
+
+self: super:
+
+{
+  # Statically linked ECL with statically linked dependencies.
+  # Works quite well, but solving this properly in a nixpkgs
+  # context will require figuring out cross compilation (for
+  # pkgsStatic), so we're gonna use this override for now.
+  #
+  # Note that ecl-static does mean that we have things
+  # statically linked against GMP and ECL which are LGPL.
+  # I believe this should be alright: The way ppl are gonna
+  # interact with the distributed binaries (i. e. the binary
+  # cache) is Nix in the depot monorepo, so the separability
+  # requirement should be satisfied: Source code or overriding
+  # would be available as ways to swap out the used GMP in the
+  # program.
+  # See https://www.gnu.org/licenses/gpl-faq.en.html#LGPLStaticVsDynamic
+  ecl-static = (super.pkgsMusl.ecl.override {
+    inherit (self.pkgsStatic) gmp libffi boehmgc;
+  }).overrideAttrs (drv: {
+    # Patches that make .fasc files concatenable again
+    patches = drv.patches ++ [
+      (self.fetchpatch {
+        name = "make-bytecode-fasl-concatenatable-1.patch";
+        url = "https://gitlab.com/embeddable-common-lisp/ecl/-/commit/fbb75a0fc524e3280d89d8abf3be2ee9924955c8.patch";
+        sha256 = "0k6cx1bh835rl0j0wbbi5nj0aa2rwbyfyz5q2jw643iqc62l16kv";
+      })
+      (self.fetchpatch {
+        name = "make-bytecode-fasl-concatenatable-2.patch";
+        url = "https://gitlab.com/embeddable-common-lisp/ecl/-/commit/a8b1c0da43f89800d09c23a27832d0b4c9dcc1e8.patch";
+        sha256 = "18hl79lss0dxglpa34hszqb6ajvs8rs4b4g1qlrqrvgh1gs667n0";
+      })
+    ];
+    configureFlags = drv.configureFlags ++ [
+      "--disable-shared"
+      "--with-dffi=no" # will fail at runtime anyways if statically linked
+    ];
+  });
+}
diff --git a/users/sterni/clhs-lookup/default.nix b/users/sterni/clhs-lookup/default.nix
index 951b94d72f19..b6a0bd06790f 100644
--- a/users/sterni/clhs-lookup/default.nix
+++ b/users/sterni/clhs-lookup/default.nix
@@ -23,7 +23,10 @@ let
     name = "clhs-lookup";
 
     deps = [
-      (buildLisp.bundled "uiop")
+      {
+        default = buildLisp.bundled "asdf";
+        sbcl = buildLisp.bundled "uiop";
+      }
     ];
 
     srcs = [
diff --git a/web/panettone/default.nix b/web/panettone/default.nix
index 095ccaac6ef2..8dd4543af0d2 100644
--- a/web/panettone/default.nix
+++ b/web/panettone/default.nix
@@ -47,4 +47,8 @@ depot.nix.buildLisp.program {
 
     expression = "(fiveam:run!)";
   };
+
+  brokenOn = [
+    "ecl" # dependencies use dynamic cffi
+  ];
 }