diff mbox series

[bug#53878,v3,09/15] gnu: Add racket-vm-cgc.

Message ID 20220219064228.246917-10-philip@philipmcgrath.com
State Accepted
Headers show
Series Update Racket to 8.4. Adjust Chez Scheme packages. | expand

Checks

Context Check Description
cbaines/comparison success View comparision
cbaines/git branch success View Git branch
cbaines/applying patch success View Laminar job
cbaines/issue success View issue

Commit Message

Philip McGrath Feb. 19, 2022, 6:42 a.m. UTC
* gnu/packages/patches/racket-enable-scheme-backport.patch: New patch.
* gnu/local.mk (dist_patch_DATA): Add it.
* gnu/packages/chez-and-racket-bootstrap.scm (unbundle-chez-submodules,
%racket-version, %racket-origin, racket-vm-cgc): New variables.
(chez-scheme)[source]<snippet>: Use 'unbundle-chez-submodules'.
---
 gnu/local.mk                                  |   1 +
 gnu/packages/chez-and-racket-bootstrap.scm    | 320 +++++++++++-
 .../racket-enable-scheme-backport.patch       | 465 ++++++++++++++++++
 3 files changed, 776 insertions(+), 10 deletions(-)
 create mode 100644 gnu/packages/patches/racket-enable-scheme-backport.patch

Comments

Liliana Marie Prikler Feb. 19, 2022, 8:46 p.m. UTC | #1
Am Samstag, dem 19.02.2022 um 01:42 -0500 schrieb Philip McGrath:
> * gnu/packages/patches/racket-enable-scheme-backport.patch: New
> patch.
> * gnu/local.mk (dist_patch_DATA): Add it.
> * gnu/packages/chez-and-racket-bootstrap.scm (unbundle-chez-
> submodules,
> %racket-version, %racket-origin, racket-vm-cgc): New variables.
> (chez-scheme)[source]<snippet>: Use 'unbundle-chez-submodules'.
Something weird happened to me just now trying to build this series. 
While compiling emacs-xyz, an error was raised regarding %racket-
version not existing and and import being missing.  Assuming that you
didn't mess up an include somewhere, there could be a cycle meaning
we'd have to pass the version and origin by function as I originally
said in my reply to v2.

I'll clean up my work tree and try to reapply it, but that will take
some time.  Since you mentioned re-exports causing issues, I'd like to
ask if you've made a similar experience, but the results got somehow
hidden after the right incantations.

Cheers
Liliana Marie Prikler Feb. 20, 2022, 9:03 a.m. UTC | #2
Am Samstag, dem 19.02.2022 um 17:27 -0500 schrieb Philip McGrath:
> Hi,
> 
> On Saturday, February 19, 2022 3:46:47 PM EST you wrote:
> > Am Samstag, dem 19.02.2022 um 01:42 -0500 schrieb Philip McGrath:
> > > * gnu/packages/patches/racket-enable-scheme-backport.patch: New
> > > patch.
> > > * gnu/local.mk (dist_patch_DATA): Add it.
> > > * gnu/packages/chez-and-racket-bootstrap.scm (unbundle-chez-
> > > submodules,
> > > %racket-version, %racket-origin, racket-vm-cgc): New variables.
> > > (chez-scheme)[source]<snippet>: Use 'unbundle-chez-submodules'.
> > 
> > Something weird happened to me just now trying to build this
> > series.  While compiling emacs-xyz, an error was raised regarding
> > %racket-version not existing and and import being missing. 
> > Assuming that you didn't mess up an include somewhere, there could
> > be a cycle meaning we'd have to pass the version and origin by
> > function as I originally said in my reply to v2.
> > 
> > I'll clean up my work tree and try to reapply it, but that will
> > take some time.  Since you mentioned re-exports causing issues, I'd
> > like to ask if you've made a similar experience, but the results
> > got somehow hidden after the right incantations.
> 
> I haven't seen errors from emacs-xyz, but I have gotten errors about
> %racket-version not existing: at the time I thought it was just a
> problem with incremental rebuilds while moving back and forward
> through history, but, having just refreshed my memory on more details
> of the cyclic issues, I think it may be related. I'll send another
> email presently with details once I've gathered references.
> 
> For now, I found that `rm gnu/packages/*.go` was enough to get `make`
> to succeed again.
As you said, the clean build succeeds, but I still think we should do
something about incremental rebuilds.  So here's my suggestion:

Inside chez-and-racket-bootstrap, define (make-<package>) functions for
the following:
- chez-bootstrap-bootfiles, chez-for-racket-bootstrap-bootfiles:
  Taking version and origin.
- racket-vm-cgc: Taking version and origin.
- racket-vm-bc: Taking racket-vm-cgc.
- racket-vm-cs: Taking racket-vm-bc.

Inside chez, define chez-scheme, as well as non-bootstrapped versions
of stex et al.
Also define make-chez-scheme-for-racket, taking version and origin as
parameter.  Finally, define chez-scheme-for-system, which uses
(resolve-interface '(gnu packages racket)) to get racket's version and
origin.

Inside racket, define %racket-version, %racket-origin, racket-minimal
and racket.  It'd also be good if you made local definitions
(define racket-vm-cgc (make-racket-vm-cgc %racket-version %racket-
origin))
(define racket-vm-bc (make-racket-vm-bc racket-vm-cgc))
...
in this file.

With this split, it should be possible to do incremental builds without
stuff breaking.

Cheers
Philip McGrath Feb. 20, 2022, 2:09 p.m. UTC | #3
Hi,

On Sunday, February 20, 2022 4:03:26 AM EST Liliana Marie Prikler wrote:
> So here's my suggestion:
> 
> Inside chez-and-racket-bootstrap, define (make-<package>) functions for
> the following:
> - chez-bootstrap-bootfiles, chez-for-racket-bootstrap-bootfiles:
>   Taking version and origin.
> - racket-vm-cgc: Taking version and origin.
> - racket-vm-bc: Taking racket-vm-cgc.
> - racket-vm-cs: Taking racket-vm-bc.
> 
> Inside chez, define chez-scheme, as well as non-bootstrapped versions
> of stex et al.
> Also define make-chez-scheme-for-racket, taking version and origin as
> parameter.  Finally, define chez-scheme-for-system, which uses
> (resolve-interface '(gnu packages racket)) to get racket's version and
> origin.
> 
> Inside racket, define %racket-version, %racket-origin, racket-minimal
> and racket.  It'd also be good if you made local definitions
> (define racket-vm-cgc (make-racket-vm-cgc %racket-version %racket-
> origin))
> (define racket-vm-bc (make-racket-vm-bc racket-vm-cgc))
> ...
> in this file.

My understanding—which is not very good!—is that this would have the same 
problem we do currently. It would be analogous to my example from
<https://issues.guix.gnu.org/53878#93>:

> But  Ludo’'s examples show that's wrong: those uses of `chez scheme` are in
> what the "expansion contexts" model would call "expression contexts".
> 
> Instead, I think rule № 2 prohibits any reference to a variable imported
> from
> another (gnu packages ...) module that will be evaluated when the (gnu
> packages ...) modules are—visited? instantiated? [2][3]—IDK when exactly,
> but, for practical purposes, any variable reference that is not underneath
> a lambda abstraction.
> 
> If that's right, IIUC, it would mean that:
>
>     (define chez-scheme-for-racket
>       (make-chez-scheme-for-racket ...))
> 
> would also be prohibited.
> 
> On the other hand, uses of `(racket-vm-for-system)` and `(chez-scheme-for-
> system)` in an `imports` field should still be fine, thanks to the implicit
> thunks.

The reference to `make-chez-scheme-for-racket` or `make-racket-vm-cs` or any 
such procedure defined in "chez-and-racket-bootstrap.scm" would be evaluated 
when "racket.scm" is instantiated—or whenever precisely it is that causes the 
problem.

-Philip
Liliana Marie Prikler Feb. 20, 2022, 4:48 p.m. UTC | #4
Am Sonntag, dem 20.02.2022 um 09:09 -0500 schrieb Philip McGrath:
> Hi,
> 
> On Sunday, February 20, 2022 4:03:26 AM EST Liliana Marie Prikler
> wrote:
> > So here's my suggestion:
> > 
> > Inside chez-and-racket-bootstrap, define (make-<package>) functions
> > for
> > the following:
> > - chez-bootstrap-bootfiles, chez-for-racket-bootstrap-bootfiles:
> >   Taking version and origin.
> > - racket-vm-cgc: Taking version and origin.
> > - racket-vm-bc: Taking racket-vm-cgc.
> > - racket-vm-cs: Taking racket-vm-bc.
> > 
> > Inside chez, define chez-scheme, as well as non-bootstrapped
> > versions of stex et al.  Also define make-chez-scheme-for-racket,
> > taking version and origin as parameter.  Finally, define chez-
> > scheme-for-system, which uses (resolve-interface '(gnu packages
> > racket)) to get racket's version and
> > origin.
> > 
> > Inside racket, define %racket-version, %racket-origin, racket-
> > minimal
> > and racket.  It'd also be good if you made local definitions
> > (define racket-vm-cgc (make-racket-vm-cgc %racket-version %racket-
> > origin))
> > (define racket-vm-bc (make-racket-vm-bc racket-vm-cgc))
> > ...
> > in this file.
> 
> My understanding—which is not very good!—is that this would have the
> same problem we do currently. It would be analogous to my example
> from <https://issues.guix.gnu.org/53878#93>:
Well, for one this claim is both verifiable and falsifiable by way of
implementation.  For another, I don't really see the issue however. 
Since those bindings would be local to racket.scm and not used anywhere
else, I don't think there is a cycle to be found anywhere.

> 
> > But  Ludo’'s examples show that's wrong: those uses of `chez
> > scheme` are in what the "expansion contexts" model would call
> > "expression contexts".
> > 
> > Instead, I think rule № 2 prohibits any reference to a variable
> > imported from another (gnu packages ...) module that will be
> > evaluated when the (gnu packages ...) modules are—visited?
> > instantiated? [2][3]—IDK when exactly, but, for practical purposes,
> > any variable reference that is not underneath a lambda abstraction.
> > 
> > If that's right, IIUC, it would mean that:
> > 
> >     (define chez-scheme-for-racket
> >       (make-chez-scheme-for-racket ...))
> > 
> > would also be prohibited.
> > 
> > On the other hand, uses of `(racket-vm-for-system)` and `(chez-
> > scheme-for-system)` in an `imports` field should still be fine,
> > thanks to the implicit thunks. 
> The reference to `make-chez-scheme-for-racket` or `make-racket-vm-cs`
> or any such procedure defined in "chez-and-racket-bootstrap.scm"
> would be evaluated when "racket.scm" is instantiated—or whenever
> precisely it is that causes the  problem.
I don't think that'd be a problem since make-chez-scheme-for-racket is
itself a function.  If it still is, one pair of brackets makes it not
so.  This is a well-explored technique of resolving chains, used for
example in our build system code.  chez-and-racket-bootstrap.scm should
imo not be imported anywhere but chez.scm and racket.scm, so it by
itself can not form a cycle.  Only chez.scm and racket.scm can, but
there are ways of making those well-formed.

See (standard-packages) in guix/build-system/gnu.scm pointing to (gnu
packages commencement) or (default-python) in guix/build-
system/python.scm for pointers.

Cheers
Philip McGrath Feb. 20, 2022, 5:52 p.m. UTC | #5
Hi,

On Sunday, February 20, 2022 11:48:01 AM EST Liliana Marie Prikler wrote:
> Am Sonntag, dem 20.02.2022 um 09:09 -0500 schrieb Philip McGrath:
> > Hi,
> > 
> > On Sunday, February 20, 2022 4:03:26 AM EST Liliana Marie Prikler
> > 
> > wrote:
> > > So here's my suggestion:
> > > 
> > > Inside chez-and-racket-bootstrap, define (make-<package>) functions
> > > for
> > > the following:
> > > - chez-bootstrap-bootfiles, chez-for-racket-bootstrap-bootfiles:
> > >   Taking version and origin.
> > > - racket-vm-cgc: Taking version and origin.
> > > - racket-vm-bc: Taking racket-vm-cgc.
> > > - racket-vm-cs: Taking racket-vm-bc.
> > > 
> > > Inside chez, define chez-scheme, as well as non-bootstrapped
> > > versions of stex et al.  Also define make-chez-scheme-for-racket,
> > > taking version and origin as parameter.  Finally, define chez-
> > > scheme-for-system, which uses (resolve-interface '(gnu packages
> > > racket)) to get racket's version and
> > > origin.
> > > 
> > > Inside racket, define %racket-version, %racket-origin, racket-
> > > minimal
> > > and racket.  It'd also be good if you made local definitions
> > > (define racket-vm-cgc (make-racket-vm-cgc %racket-version %racket-
> > > origin))
> > > (define racket-vm-bc (make-racket-vm-bc racket-vm-cgc))
> > > ...
> > > in this file.
> > 
> > My understanding—which is not very good!—is that this would have the
> > same problem we do currently. It would be analogous to my example
> 
> > from <https://issues.guix.gnu.org/53878#93>:
> Well, for one this claim is both verifiable and falsifiable by way of
> implementation.  For another, I don't really see the issue however.
> Since those bindings would be local to racket.scm and not used anywhere
> else, I don't think there is a cycle to be found anywhere.
> 

While working on v4, I did try using `racket-vm-for-system` at what Ludo’ 
called "the top level of a module", and I encountered the same error.

> > > But  Ludo’'s examples show that's wrong: those uses of `chez
> > > scheme` are in what the "expansion contexts" model would call
> > > "expression contexts".
> > > 
> > > Instead, I think rule № 2 prohibits any reference to a variable
> > > imported from another (gnu packages ...) module that will be
> > > evaluated when the (gnu packages ...) modules are—visited?
> > > instantiated? [2][3]—IDK when exactly, but, for practical purposes,
> > > any variable reference that is not underneath a lambda abstraction.
> > > 
> > > If that's right, IIUC, it would mean that:
> > > 
> > >     (define chez-scheme-for-racket
> > >       (make-chez-scheme-for-racket ...))
> > > 
> > > would also be prohibited.
> > > 
> > > On the other hand, uses of `(racket-vm-for-system)` and `(chez-
> > > scheme-for-system)` in an `imports` field should still be fine,
> > > thanks to the implicit thunks.
> > 
> > The reference to `make-chez-scheme-for-racket` or `make-racket-vm-cs`
> > or any such procedure defined in "chez-and-racket-bootstrap.scm"
> > would be evaluated when "racket.scm" is instantiated—or whenever
> > precisely it is that causes the  problem.
> 
> I don't think that'd be a problem since make-chez-scheme-for-racket is
> itself a function.  If it still is, one pair of brackets makes it not
> so.  This is a well-explored technique of resolving chains, used for
> example in our build system code.  chez-and-racket-bootstrap.scm should
> imo not be imported anywhere but chez.scm and racket.scm, so it by
> itself can not form a cycle.  Only chez.scm and racket.scm can, but
> there are ways of making those well-formed.

AFAICT the error is not from applying the function bound to the variable 
`racket-vm-for-system` (or `make-racket-vm-bc`, or whatever), but from 
evaluating the reference to a variable imported from a sibling
`(gnu packages ...)` module, regardless of the value to which the variable is 
bound.

My current best guess at Guile's semantics for mutually-recursive modules is 
that it's something like the `letrec` restriction, e.g. the difference between 
these two examples (given an implementation that fully enforces `letrec` 
semantics, as opposed to `letrec*`):

--8<---------------cut here---------------start------------->8---
$ scheme 
Chez Scheme Version 9.5.6
Copyright 1984-2021 Cisco Systems, Inc.

> (letrec ((a (lambda () 1))
           (b a))
    2)
Exception: attempt to reference undefined variable a
Type (debug) to enter the debugger.
> (letrec ((a (lambda () 1))
           (b (lambda () (a))))
    (b))
1
--8<---------------cut here---------------end--------------->8---

> 
> See (standard-packages) in guix/build-system/gnu.scm pointing to (gnu
> packages commencement) or (default-python) in guix/build-
> system/python.scm for pointers.

Given Ludo’'s explanation, I think the difference (or at least an important 
difference) is that those functions are defined in `(guix ...)` modules, as 
opposed to `(gnu packages ...)` modules.

But I wish I knew with any degree of certainty *why* this would be true, if 
indeed it is.

Maybe Maxime knows?

-Philip
Liliana Marie Prikler Feb. 20, 2022, 6:13 p.m. UTC | #6
Hi,

Am Sonntag, dem 20.02.2022 um 12:52 -0500 schrieb Philip McGrath:
> AFAICT the error is not from applying the function bound to the
> variable `racket-vm-for-system` (or `make-racket-vm-bc`, or
> whatever), but from evaluating the reference to a variable imported
> from a sibling `(gnu packages ...)` module, regardless of the value
> to which the variable is bound.
> 
> My current best guess at Guile's semantics for mutually-recursive
> modules is that it's something like the `letrec` restriction, e.g.
> the difference between these two examples (given an implementation
> that fully enforces `letrec` semantics, as opposed to `letrec*`):
> 
> --8<---------------cut here---------------start------------->8---
> $ scheme 
> Chez Scheme Version 9.5.6
> Copyright 1984-2021 Cisco Systems, Inc.
> 
> > (letrec ((a (lambda () 1))
>            (b a))
>     2)
> Exception: attempt to reference undefined variable a
> Type (debug) to enter the debugger.
> > (letrec ((a (lambda () 1))
>            (b (lambda () (a))))
>     (b))
> 1
> --8<---------------cut here---------------end--------------->8---
> 
> > 
> > See (standard-packages) in guix/build-system/gnu.scm pointing to
> > (gnu
> > packages commencement) or (default-python) in guix/build-
> > system/python.scm for pointers.
> 
> Given Ludo’'s explanation, I think the difference (or at least an
> important difference) is that those functions are defined in `(guix
> ...)` modules, as  opposed to `(gnu packages ...)` modules.
> 
> But I wish I knew with any degree of certainty *why* this would be
> true, if indeed it is.
> 
> Maybe Maxime knows?
Both Ludo and Maxime already explained this, but to be extra clear,
it's the thunking.

(define foo <something-in-bar.scm>)
(define bar <something-in-foo.scm>) <- problem

(define (foo) <something-in-bar.scm>)
(define bar <something-in-foo.scm>) <- no problem.

Since inputs are thunked, you can define chez in chez.scm and racket in
racket.scm and use them as input to each other without breaking the
compiler (you will break the package builder though).  If you want
something more meaningful, you can define racket-minimal in racket.scm
and use it in chez.scm as input to a package, then use that package as
input to racket.  This does not work for (source ) and (inherit )
however, because those forms are not thunked.  You would have to thunk
them until you reach a save haven (like a package's inputs), where you
can call the thunk to produce a value.

Cheers
diff mbox series

Patch

diff --git a/gnu/local.mk b/gnu/local.mk
index d963625a63..3a6a4a48b3 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1751,6 +1751,7 @@  dist_patch_DATA =						\
   %D%/packages/patches/ripperx-missing-file.patch		\
   %D%/packages/patches/rpcbind-CVE-2017-8779.patch		\
   %D%/packages/patches/rtags-separate-rct.patch			\
+  %D%/packages/patches/racket-enable-scheme-backport.patch	\
   %D%/packages/patches/racket-minimal-sh-via-rktio.patch	\
   %D%/packages/patches/remake-impure-dirs.patch			\
   %D%/packages/patches/restic-0.9.6-fix-tests-for-go1.15.patch	\
diff --git a/gnu/packages/chez-and-racket-bootstrap.scm b/gnu/packages/chez-and-racket-bootstrap.scm
index 366815634d..5eedd9a11f 100644
--- a/gnu/packages/chez-and-racket-bootstrap.scm
+++ b/gnu/packages/chez-and-racket-bootstrap.scm
@@ -34,15 +34,20 @@  (define-module (gnu packages chez-and-racket-bootstrap)
   #:use-module (guix build-system copy)
   #:use-module (guix build-system gnu)
   #:use-module (gnu packages)
+  #:use-module (gnu packages autotools)
+  #:use-module (gnu packages bash)
   #:use-module (gnu packages compression)
   #:use-module (gnu packages ghostscript)
+  #:use-module (gnu packages libffi)
   #:use-module (gnu packages linux)
   #:use-module (gnu packages ncurses)
   #:use-module (gnu packages netpbm)
   #:use-module (gnu packages tex)
   #:use-module (gnu packages xorg)
   #:use-module ((guix licenses)
-                #:prefix license:))
+                #:prefix license:)
+  #:export (%racket-version
+            %racket-origin))
 
 ;; Commentary:
 ;;
@@ -62,6 +67,136 @@  (define-module (gnu packages chez-and-racket-bootstrap)
 ;; Putting the relevant definitions together in this module avoids having to
 ;; work around dependency cycles.
 ;;
+;; Anatomy of Racket:
+;; ------------------
+;;
+;; The main Racket Git repository (<https://github.com/racket/racket>) is
+;; organized broadly like this:
+;;
+;;     .
+;;     ├── Makefile
+;;     ├── pkgs/
+;;     └── racket/
+;;         ├── collects/
+;;         └── src/
+;;             ├── configure
+;;             ├── Makefile.in
+;;             ├── bc/
+;;             ├── cs/
+;;             ├── ChezScheme/
+;;             └── ...
+;;
+;; The 'racket/src/' directory contains the source of the runtime system, core
+;; compiler, and primitives for the major Racket implementations: this layer
+;; is called the ``Racket VM''. It is basically a normal autotools
+;; project. (Even when Racket VM implementations use components implemented in
+;; Racket, they are compiled in special modes to produce VM primitives.)
+;; (There are or have been experimental Racket VM implementations elsewhere,
+;; e.g. <https://github.com/pycket/pycket>.)
+;;
+;; The 'racket/collects/' directory contains ``built in'' Racket libraries
+;; that are not part of any package, including the implementation of
+;; 'racket/base': in particular, it must contain enough to implement `raco pkg
+;; install'. It is theoretically possible to use the Racket VM layer without
+;; the main collections, but it is not stable or useful.
+;;
+;; The 'pkgs/' directory contains Racket packages that are especially closely
+;; tied to the implementation of the Racket VM, including 'compiler-lib',
+;; 'racket-doc', and 'racket-test'. Some of these packages depend on Racket
+;; packages that are developed in other Git repositories, predominantly but
+;; not exclusively under the 'racket' GitHub organization. Conversely, not all
+;; of the packages developed in the main Git repository are part of the main
+;; Racket distribution.  (Additionally, components of the Racket VM that are
+;; implemented in Racket can be installed as packages, mostly for ease of
+;; development.)
+;;
+;; The top-level 'Makefile' is more like a directory of scripts: it has
+;; convienience targets for developing Racket, and it cooperates with the
+;; 'distro-build' package to assemble custom Racket distributions. It is not
+;; part of Racket source distributions: the root of a source distribution is
+;; basically 'racket/src' with some extra package sources and configuration
+;; added.
+;;
+;; A ''minimal Racket'' installation includes two packages: 'base', which is a
+;; sort of bridge between the current ``built-in'' collections and the package
+;; system's model of dependencies, and 'racket-lib', which, for installations
+;; that can not rely on a system package manager, pulls in the SQLite and
+;; OpenSSL shared libraries as platform-specific dependencies for use by the
+;; ``built-in'' collections.
+;;
+;; The main Racket distribution consists of installing the 'main-distribution'
+;; package and all of its dependencies.
+;;
+;; The default mode when building Racket (or installing it with the released
+;; installers) is an ``in-place build'', which produces a self-contained,
+;; relocatable, roughly FHS-like directory. (Racket also supports
+;; ``Unix-style'' installations, which rearrange the parts of an in-place
+;; build into Racket-specific subdirectories and generally tries to work for
+;; installation into an FHS-based system.) Certain tools, e.g. 'distro-build'
+;; and 'raco cross', are able to work with an in-place Racket build.
+;;
+;; This file defines the packages 'racket-vm-cgc', 'racket-vm-bc', and
+;; 'racket-vm-cs'. All three are in-place builds of 'racket/src/' and
+;; 'racket/collects/' and are installed to 'opt/racket-vm/' in the store
+;; output. The function 'racket-vm-for-system' returns the recomended Racket
+;; VM package for a given system.
+;;
+;; The file 'racket.scm' builds on these packages to define 'racket-minimal'
+;; and 'racket' packages. These use Racket's support for ``layered
+;; installations'', which allow an immutable base layer to be extended with
+;; additional packages. They use the layer configuration directly provide
+;; ready-to-install FHS-like trees, rather than relying on the built in
+;; ``Unix-style install'' mechanism.
+;;
+;; Bootstrapping Racket:
+;; ---------------------
+;;
+;; Here's how bootstrapping Racket works:
+;;
+;;   - Racket BC [CGC] can be built with only a C compiler (except for
+;;     one caveat discussed below).
+;;   - Racket BC [3M] needs an existing Racket to run "xform",
+;;     which transforms its own C source code to add additional annotations
+;;     for the precise garbage collector.
+;;   - Racket CS needs (bootfiles for) Racket's fork of Chez Scheme.
+;;     It also needs an existing Racket to compile Racket-implemented
+;;     parts of the runtime system to R6RS libraries.
+;;   - Chez Scheme also needs bootfiles for itself, but Racket can simulate
+;;     enough of Chez Scheme to load Racket's fork of the Chez Scheme compiler
+;;     purely from source into Racket and apply the compiler to itself,
+;;     producing the needed bootfiles (albeit very slowly).
+;;     Any variant of Racket since version 7.1 can run the simulation.
+;;
+;; So, we build CGC to build 3M to build bootfiles and CS.
+;;
+;; (Note: since the CGC variant is basically only for bootstrapping, we
+;; often use "BC" to mean "3M", consistent with `(banner)` and the
+;; suffixes used on executables when more than one variant co-exists.)
+;;
+;; One remaining bootstrapping limitation is that Racket's reader, module
+;; system, and macro expander are implemented in Racket. For Racket CS,
+;; they are compiled to R6RS libraries as discussed above. This note from the
+;; README file applies to all such subsystems:
+;;
+;;     The Racket version must be practically the same as the current Racket
+;;     verson, although it can be the Racket BC implementation (instead of
+;;     the Racket CS implementation).
+;;
+;;     Unlike Chez Scheme boot files, the files generated in "schemified"
+;;     are human-readable and -editable Scheme code. That provides a way
+;;     out of bootstrapping black holes, even without BC.
+;;
+;; However, other Racket subsystems implemented in Racket for Racket CS
+;; use older C implementations for Racket BC, whereas the reader, expander,
+;; and module system were completely replaced with the Racket implementation
+;;
+;; For Racket BC, the compiled "linklet" s-expressions (primitive modules)
+;; are embeded in C as a static string constant. Eventually, they are further
+;; compiled by the C-implemented Racket BC bytecode and JIT compilers.
+;; (On platforms where Racket BC's JIT is not supported, yet another compiler
+;; instead compiles the linklets to C code, but this is not a bootstrapping
+;; issue.)
+;;
 ;; Code:
 
 (define (chez-machine->nonthreaded machine)
@@ -169,6 +304,179 @@  (define* (chez-upstream-features-for-system #:optional
    (else
     '(threads))))
 
+;;
+;; Chez auxiliary G-expressions:
+;;
+
+(define unbundle-chez-submodules
+  #~(begin
+      (use-modules (guix build utils))
+      (for-each (lambda (dir)
+                  (when (directory-exists? dir)
+                    (delete-file-recursively dir)))
+                '("stex"
+                  "nanopass"
+                  "lz4"
+                  "zlib"))))
+
+;;
+;; Racket VM:
+;;
+
+(define %racket-version "8.4")
+(define %racket-origin
+  (origin
+    (method git-fetch)
+    (uri (git-reference
+          (url "https://github.com/racket/racket")
+          (commit (string-append "v" %racket-version))))
+    (sha256
+     (base32 "1vpl66gdgc8rnldmn8rmb7ar9l057jqjvgpfn29k57i3c5skr8s6"))
+    (file-name (git-file-name "racket" %racket-version))
+    (patches (search-patches "racket-minimal-sh-via-rktio.patch"
+                             ;; Remove by Racket 8.5:
+                             "racket-enable-scheme-backport.patch"))
+    (modules '((guix build utils)))
+    (snippet
+     #~(begin
+         ;; Unbundle Chez submodules.
+         (with-directory-excursion "racket/src/ChezScheme"
+           #$unbundle-chez-submodules)
+         ;; Unbundle libffi.
+         (delete-file-recursively "racket/src/bc/foreign/libffi")))))
+
+(define (racket-vm-common-configure-flags)
+  ;; under a lambda extraction to avoid evaluating bash-minimal too early
+  #~`(,@(cond
+         ((false-if-exception
+           (search-input-file %build-inputs "/bin/libtool"))
+          => (lambda (libtool)
+               (list (string-append "--enable-lt=" libtool))))
+         (else
+          '()))
+      ,@(cond
+         ((false-if-exception
+           (search-input-file %build-inputs "/opt/racket-vm/bin/racket"))
+          => (lambda (racket)
+               (list (string-append "--enable-racket=" racket))))
+         (else
+          '()))
+      ,(string-append "CPPFLAGS=-DGUIX_RKTIO_PATCH_BIN_SH="
+                      #$(file-append bash-minimal "/bin/sh"))
+      "--disable-strip"
+      "--enable-origtree"))
+
+(define-public racket-vm-cgc
+  ;; Eventually, it may make sense for some vm packages to not be hidden,
+  ;; but this one is especially likely to remain hidden.
+  (hidden-package
+   (package
+     (name "racket-vm-cgc")
+     (version %racket-version)
+     (source %racket-origin)
+     (inputs (list ncurses ;; <- common to all variants (for #%terminal)
+                   bash-minimal ;; <- common to all variants (for `system`)
+                   libffi)) ;; <- only for BC variants
+     (native-inputs (list libtool)) ;; <- only for BC variants
+     (outputs '("out" "debug"))
+     (build-system gnu-build-system)
+     (arguments
+      (list
+       #:configure-flags
+       #~(cons "--enable-cgcdefault"
+               #$(racket-vm-common-configure-flags))
+       ;; Tests are in packages like racket-test-core and
+       ;; main-distribution-test that aren't part of the main
+       ;; distribution.
+       #:tests? #f
+       ;; Upstream recommends #:out-of-source?, and it does
+       ;; help with debugging, but it confuses `install-license-files`.
+       #:modules '((ice-9 match)
+                   (ice-9 regex)
+                   (guix build gnu-build-system)
+                   (guix build utils))
+       #:strip-directories #~'("opt/racket-vm/bin"
+                               "opt/racket-vm/lib")
+       #:phases
+       #~(let ()
+           (define* ((wrap-racket-vm-outputs phase) . args)
+             (apply
+              phase
+              (let loop ((args args))
+                (match args
+                  ((#:outputs outputs . args)
+                   `(#:outputs
+                     ,(let loop ((outputs outputs))
+                        (match outputs
+                          ((("out" . out) . outputs)
+                           `(("out" . ,(string-append out "/opt/racket-vm/"))
+                             ,@outputs))
+                          ((other . outputs)
+                           (cons other (loop outputs)))))
+                     ,@args))
+                  ((arg . args)
+                   (cons arg (loop args)))))))
+           (modify-phases %standard-phases
+             (add-before 'configure 'initialize-config.rktd
+               (lambda* (#:key inputs #:allow-other-keys)
+                 (define (write-racket-hash alist)
+                   ;; inside must use dotted pair notation
+                   (display "#hash(")
+                   (for-each (match-lambda
+                               ((k . v)
+                                (format #t "(~s . ~s)" k v)))
+                             alist)
+                   (display ")\n"))
+                 (define maybe-release-catalog
+                   (let ((v #$(package-version this-package)))
+                     (if (string-match "^[0-9]+\\.[0-9]+($|\\.[0-8][0-9]*$)"
+                                       v)
+                         `(,(string-append
+                             "https://download.racket-lang.org/releases/"
+                             v
+                             "/catalog/"))
+                         '())))
+                 (mkdir-p "racket/etc")
+                 (with-output-to-file "racket/etc/config.rktd"
+                   (lambda ()
+                     (write-racket-hash
+                      `((build-stamp . "")
+                        (catalogs ,@maybe-release-catalog
+                                  #f)))))))
+             (add-before 'configure 'chdir
+               (lambda _
+                 (chdir "racket/src")))
+             (replace 'configure
+               (wrap-racket-vm-outputs
+                (assoc-ref %standard-phases 'configure)))
+             (replace 'patch-shebangs
+               (wrap-racket-vm-outputs
+                (assoc-ref %standard-phases 'patch-shebangs)))
+             (replace 'validate-runpath
+               (wrap-racket-vm-outputs
+                (assoc-ref %standard-phases 'validate-runpath)))
+             (replace 'make-dynamic-linker-cache
+               (wrap-racket-vm-outputs
+                (assoc-ref %standard-phases 'make-dynamic-linker-cache)))
+             (replace 'patch-dot-desktop-files
+               (wrap-racket-vm-outputs
+                (assoc-ref %standard-phases 'patch-dot-desktop-files)))))))
+     (home-page "https://racket-lang.org")
+     (synopsis "Old Racket implementation used for bootstrapping")
+     (description "This variant of the Racket BC (``before Chez'' or
+``bytecode'') implementation is not recommended for general use.  It uses
+CGC (a ``Conservative Garbage Collector''), which was succeeded as default in
+PLT Scheme version 370 (which translates to 3.7 in the current versioning
+scheme) by the 3M variant, which in turn was succeeded in version 8.0 by the
+Racket CS implementation.
+
+Racket CGC is primarily used for bootstrapping Racket BC [3M].  It may
+also be used for embedding applications without the annotations needed in C
+code to use the 3M garbage collector.")
+     ;; https://download.racket-lang.org/license.html
+     ;; The LGPL components are only used by Racket BC.
+     (license (list license:lgpl3+ license:asl2.0 license:expat)))))
+
 ;;
 ;; Chez Scheme:
 ;;
@@ -188,15 +496,7 @@  (define-public chez-scheme
                (base32
                 "07s433hn1z2slfc026sidrpzxv3a8narcd40qqr1xrpb9012xdky"))
               (file-name (git-file-name "chez-scheme" version))
-              (snippet #~(begin
-                           (use-modules (guix build utils))
-                           (for-each (lambda (dir)
-                                       (when (directory-exists? dir)
-                                         (delete-file-recursively dir)))
-                                     '("stex"
-                                       "nanopass"
-                                       "lz4"
-                                       "zlib"))))))
+              (snippet unbundle-chez-submodules)))
     (build-system gnu-build-system)
     (inputs
      (list
diff --git a/gnu/packages/patches/racket-enable-scheme-backport.patch b/gnu/packages/patches/racket-enable-scheme-backport.patch
new file mode 100644
index 0000000000..3a5a4a3d82
--- /dev/null
+++ b/gnu/packages/patches/racket-enable-scheme-backport.patch
@@ -0,0 +1,465 @@ 
+From 8d7687842f099e3e7e60d3a83fed58b2c6a92863 Mon Sep 17 00:00:00 2001
+From: Matthew Flatt <mflatt@racket-lang.org>
+Date: Sun, 6 Feb 2022 10:36:09 -0700
+Subject: [PATCH 1/2] Chez Scheme: adapt bootfile build for supplied `Scheme=`
+
+(cherry picked from commit fca1e02349664060e10278ca2ce6577a949bebf5)
+
+(Fixed conflicts by dropping pbchunks and pbarch changes.)
+---
+ racket/src/ChezScheme/configure  | 15 ++++++++++++++-
+ racket/src/ChezScheme/s/Mf-base  |  4 ++--
+ racket/src/ChezScheme/s/Mf-cross |  4 +++-
+ 3 files changed, 19 insertions(+), 4 deletions(-)
+
+diff --git a/racket/src/ChezScheme/configure b/racket/src/ChezScheme/configure
+index 4515ffc105..0098829091 100755
+--- a/racket/src/ChezScheme/configure
++++ b/racket/src/ChezScheme/configure
+@@ -45,6 +45,7 @@ threads=yes
+ nothreads=no
+ temproot=""
+ help=no
++forceworkarea=no
+ gzipmanpages=yes
+ installowner=""
+ installgroup=""
+@@ -205,6 +206,9 @@ while [ $# != 0 ] ; do
+     --pb)
+       pb=yes
+       ;;
++    --force)
++      forceworkarea=yes
++      ;;
+     --installprefix=*)
+       installprefix=`echo $1 | sed -e 's/^--installprefix=//'`
+       ;;
+@@ -439,6 +443,7 @@ if [ "$help" = "yes" ]; then
+   echo "  --toolprefix=<prefix>             prefix tool (compiler, linker, ...) names"
+   echo "  --[no]gzip-man-pages              compress manual pages ($gzipmanpages)"
+   echo "  --workarea=<pathname>             build directory ($w)"
++  echo "  --force                           configure even without boot files"
+   echo "  CC=<C compiler>                   C compiler"
+   echo "  CPPFLAGS=<C preprocessor flags>   C preprocessor flags"
+   echo "  CFLAGS=<C compiler flags>         C compiler flags"
+@@ -721,8 +726,16 @@ case "${flagsmuni}" in
+         ;;
+ esac
+ 
++if [ "$w" = "$m" ] ; then
++    configuringin=""
++else
++    configuringin=" in $w"
++fi
++
+ if [ -f boot/$m/scheme.boot -o -f "$srcdir"/boot/$m/scheme.boot ] ; then
+-  echo "Configuring for $m"
++    echo "Configuring for $m$configuringin"
++elif [ "$forceworkarea" = yes ] ; then
++    echo "Configuring for $m$configuringin despite missing boot files"
+ else
+   if [ "$m" = "" ] ; then
+       maybem="<machine type>"
+diff --git a/racket/src/ChezScheme/s/Mf-base b/racket/src/ChezScheme/s/Mf-base
+index cc6178c973..1f4a967998 100644
+--- a/racket/src/ChezScheme/s/Mf-base
++++ b/racket/src/ChezScheme/s/Mf-base
+@@ -94,7 +94,7 @@ endif
+ # that Scheme and SCHEMEHEAPDIRS are set by Mf-cross to point to the host Scheme
+ # implementation
+ Scheme = ../bin/$m/scheme${ExeSuffix}
+-export SCHEMEHEAPDIRS=../boot/%m
++export SCHEMEHEAPDIRS=../boot/$m
+ export CHEZSCHEMELIBDIRS=.
+ 
+ # Define the libdirs separator character
+@@ -691,4 +691,4 @@ reset-one:
+ 
+ .PHONY: run
+ run:
+-	env SCHEMEHEAPDIRS=../boot/$m/ ../bin/$m/scheme $(ARGS)
++	env SCHEMEHEAPDIRS=${SCHEMEHEAPDIRS} ${Scheme} $(ARGS)
+diff --git a/racket/src/ChezScheme/s/Mf-cross b/racket/src/ChezScheme/s/Mf-cross
+index d796cbb459..397af59a28 100644
+--- a/racket/src/ChezScheme/s/Mf-cross
++++ b/racket/src/ChezScheme/s/Mf-cross
+@@ -43,5 +43,7 @@ x$(xm).$(m):
+ 	$(MAKE) -f Mf-cross m=$(m) xm=$(xm) i=f o=3 d=0 xpatch
+ 	mv xpatch x$(xm).$(m)
+ 
++ifneq ($(SCHEMEHEAPDIRS),:)
+ # Ensure that cross-compiling "nanopass.so" is rebuilt if the host compiler changed
+-nanopass.so: ${SCHEME} ${SCHEMEHEAPDIRS}/petite.boot ${SCHEMEHEAPDIRS}/scheme.boot
++nanopass.so: ${Scheme} ${SCHEMEHEAPDIRS}/petite.boot ${SCHEMEHEAPDIRS}/scheme.boot
++endif
+-- 
+2.32.0
+
+
+From 26c8e2c1d9b02ad85acef8bda40d92154cf0b699 Mon Sep 17 00:00:00 2001
+From: Matthew Flatt <mflatt@racket-lang.org>
+Date: Sun, 6 Feb 2022 11:03:30 -0700
+Subject: [PATCH 2/2] configure: make `--enable-scheme` work with an executable
+
+When the same Chez Scheme version as used by Racket is already
+available, then `--enable-scheme=...` can supply an executable. For
+cross builds, `--enable-scheme=...` can still supply a build
+directory, instead, as before.
+
+(cherry picked from commit 4f0e76855ce7e86107de495292a553469daf0b3f)
+---
+ racket/src/ChezScheme/makefiles/Makefile.in |  3 ++
+ racket/src/README.txt                       | 30 +++++++++++---
+ racket/src/configure                        |  8 +++-
+ racket/src/cs/README.txt                    |  6 ++-
+ racket/src/cs/c/Makefile.in                 | 44 ++++++++++++++++-----
+ racket/src/cs/c/configure                   | 24 +++++++++--
+ racket/src/cs/c/configure.ac                | 21 ++++++++--
+ 7 files changed, 112 insertions(+), 24 deletions(-)
+
+diff --git a/racket/src/ChezScheme/makefiles/Makefile.in b/racket/src/ChezScheme/makefiles/Makefile.in
+index c396efc851..3998ef9ccd 100644
+--- a/racket/src/ChezScheme/makefiles/Makefile.in
++++ b/racket/src/ChezScheme/makefiles/Makefile.in
+@@ -59,6 +59,9 @@ reset:
+ %.boot:
+ 	(cd $(workarea) && $(MAKE) $*.boot)
+ 
++auto.boot:
++	(cd $(workarea) && $(MAKE) $(defaultm).boot)
++
+ # <machine>.bootquick to build boot files for <machine>
+ # with o=3 d=0 for the cross compiler, and only after
+ # building the kernel for the configured machine
+diff --git a/racket/src/README.txt b/racket/src/README.txt
+index 98647aebce..d77310b4a4 100644
+--- a/racket/src/README.txt
++++ b/racket/src/README.txt
+@@ -354,6 +354,10 @@ variant of MinGW without "libdelayimp.a", get the implementation of
+ ========================================================================
+ 
+ Cross-compilation requires at least two flags to `configure`:
++`--host=OS` and either `--enable-racket=RACKET` or (for Racket CS)
++`--enable-scheme-SCHEME`.
++
++More information:
+ 
+  * `--host=OS`, where OS is something like `i386-gnu-linux` to
+    indicate the target platform.
+@@ -374,11 +378,27 @@ Cross-compilation requires at least two flags to `configure`:
+    run `configure` again (with no arguments) in a "local" subdirectory
+    to create a build for the current platform.
+ 
+-An additional flag is needed for building Racket CS, unless the flag
+-`--enable-racket=auto` is used:
+-
+- * `--enable-scheme=DIR`, where DIR is a path that has a "ChezScheme"
+-   directory where Chez Scheme is built for the host system.
++ * `--enable-scheme=SCHEME`, where SCHEME is a Chez Scheme executable
++   executable that runs on the build platform; the executable must be
++   the same version as used in Racket built for the target platform.
++ 
++   Supplying `--enable-scheme=DIR` is also supported in cross-build
++   mode, where DIR is a path that has a "ChezScheme" directory where
++   Chez Scheme is built for the host system.
++
++The `--enable-racket=RACKET` and `--enable-scheme=SCHEME` flags are
++allowed for non-cross builds, too:
++
++ * For Racket CS, supplying either selects a Racket or Chez Scheme
++   implementation used to create boot files to the build platform.
++   Suppling Chez Scheme is a much more direct path, but when Racket is
++   supplied, its version does not have to match the version being
++   built.
++
++ * For Racket BC, `--enable-racket=RACKET` selects a Racket for
++   prepare C sources to cooperate with garbage collection. Its version
++   needs to be close to the one being built, and potentially exactly
++   the same version.
+ 
+ Some less commonly needed `configure` flags are for Racket BC:
+ 
+diff --git a/racket/src/configure b/racket/src/configure
+index c9f3ba4419..1b53ec7ce2 100755
+--- a/racket/src/configure
++++ b/racket/src/configure
+@@ -9,6 +9,7 @@ pb_dir="$dir/ChezScheme/boot/pb"
+ use_cs=maybe
+ use_bc=maybe
+ supplied_racket=no
++supplied_scheme=no
+ enable_boothelp=
+ 
+ # We don't have to detect conflicts like `--enable-csdefault --enable-bcdefault`,
+@@ -34,6 +35,9 @@ for arg in $*; do
+         --enable-racket=*)
+             supplied_racket=yes
+             ;;
++        --enable-scheme=*)
++            supplied_scheme=yes
++            ;;
+         --help | -h)
+             echo $0:
+             echo see --help-bc or --help-cs, since the Racket CS build and the
+@@ -70,8 +74,8 @@ elif test "$use_cs" = "maybe" ; then
+ fi
+ 
+ if test "$use_cs" = "yes" ; then
+-    if test $use_bc = no  -a $supplied_racket = no -a ! -d "$pb_dir" ; then
+-        echo $0: must have $pb_dir or --enable-racket=... for --enable-csonly
++    if test $use_bc = no  -a $supplied_racket = no  -a $supplied_scheme = no  -a ! -d "$pb_dir" ; then
++        echo $0: must have $pb_dir, --enable-racket=... or --enable-scheme=... for --enable-csonly
+         exit 1
+     fi
+ 
+diff --git a/racket/src/cs/README.txt b/racket/src/cs/README.txt
+index 2ece417b78..8e6fc57b74 100644
+--- a/racket/src/cs/README.txt
++++ b/racket/src/cs/README.txt
+@@ -39,6 +39,11 @@ build:
+    installed in the "../ChezScheme/boot/pb" directory as described by
+    "../ChezScheme/BUILDING".
+ 
++   Supplying `--enable-scheme=...` is also an option if you alerady
++   have the same version of Chez Scheme built on the current platform.
++   Another build will be created, anyway, but more quickly than
++   without Chez Scheme.
++
+  * Racket is needed to generate the files in the "schemified"
+    directory from the sources in sibling directories like "../io". The
+    Racket version must be practically the same as the current Racket
+@@ -48,7 +53,6 @@ build:
+    Unlike Chez Scheme boot files, the files generated in "schemified"
+    are human-readable and -editable Scheme code. That provides a way
+    out of bootstrapping black holes, even without BC.
+-   
+ 
+ 
+ ========================================================================
+diff --git a/racket/src/cs/c/Makefile.in b/racket/src/cs/c/Makefile.in
+index 54a644a1d9..d73993f0fc 100644
+--- a/racket/src/cs/c/Makefile.in
++++ b/racket/src/cs/c/Makefile.in
+@@ -12,7 +12,9 @@ CS_HOST_WORKAREA_PREFIX = @CS_HOST_WORKAREA_PREFIX@
+ SCHEME_HOST_WORKAREA = $(CS_HOST_WORKAREA_PREFIX)$(SCHEME_WORKAREA)
+ SCHEME_BIN = $(SCHEME_HOST_WORKAREA)/$(MACH)/bin/$(MACH)/scheme
+ SCHEME_INC = $(SCHEME_HOST_WORKAREA)/$(MACH)/boot/$(MACH)
+-SCHEME = $(SCHEME_BIN) -B $(SCHEME_INC)/petite.boot -B $(SCHEME_INC)/scheme.boot
++SCHEME_built = $(SCHEME_BIN) -B $(SCHEME_INC)/petite.boot -B $(SCHEME_INC)/scheme.boot
++SCHEME_existing = @MAKE_SCHEME_SCHEME@
++SCHEME = $(SCHEME@USE_SCHEME_MODE@)
+ 
+ TARGET_MACH = @TARGET_MACH@
+ SCHEME_TARGET_INC = $(SCHEME_WORKAREA)/$(TARGET_MACH)/boot/$(TARGET_MACH)
+@@ -88,7 +90,7 @@ mainsrcdir = @srcdir@/../..
+ @INCLUDEDEP@ @srcdir@/../../version/version.mak
+ 
+ cs:
+-	$(MAKE) scheme@T_CROSS_MODE@
++	$(MAKE) scheme@MAKE_SCHEME_MODE@
+ 	$(MAKE) racket-so
+ 	cd rktio; $(MAKE)
+ 	$(MAKE) racketcs
+@@ -121,9 +123,13 @@ racket-so:
+ 
+ RACKET_SO_ENV = @CONFIGURE_RACKET_SO_COMPILE@
+ 
++TARGET_MACH_built = $(TARGET_MACH)
++TARGET_MACH_existing = xc-$(TARGET_MACH)
++XPATCH_FILE = $(SCHEME_WORKAREA)/$(TARGET_MACH@USE_SCHEME_MODE@)/s/xpatch
++
+ CS_PROGS = SCHEME="$(SCHEME)"
+ CS_OPTS = COMPRESS_COMP=@COMPRESS_COMP@ @ENABLE_OR_DISABLE_WPO@
+-CS_OPTScross = $(CS_OPTS) CSO=$(MACH) CROSS_COMP="--xpatch $(SCHEME_WORKAREA)/$(TARGET_MACH)/s/xpatch"
++CS_OPTScross = $(CS_OPTS) CSO=$(MACH) CROSS_COMP="--xpatch $(XPATCH_FILE)"
+ PASS_COMPILE_DEPS = EXTRA_COMPILE_DEPS="$(SCHEME_INC)/petite.boot $(SCHEME_INC)/scheme.boot"
+ 
+ build-racket-so:
+@@ -163,6 +169,15 @@ pb-bootquick:
+ 	cd $(SCHEME_WORKAREA) && $(MAKE) reset
+ 	$(SHELL) $(srcdir)/ready_boot.sh $(MACH) $(SCHEME_WORKAREA)
+ 
++scheme-via-scheme:
++	$(MAKE) $(SCHEME_WORKAREA)/boot/$(MACH)/scheme.boot
++	$(MAKE) mach-make
++
++$(SCHEME_WORKAREA)/boot/$(MACH)/scheme.boot:
++	mkdir -p $(SCHEME_WORKAREA)
++	$(MAKE) config-scheme CONFIG_SCHEME_MODE="$(CONFIG_SCHEME_MODE) --force"
++	cd $(SCHEME_WORKAREA) && $(MAKE) $(MACH).boot Scheme="$(SCHEME)" SCHEMEHEAPDIRS=: o=3 d=0 what=all
++
+ mach-make:
+ 	$(MAKE) config-scheme
+ 	cd $(SCHEME_WORKAREA) && $(MAKE)
+@@ -182,24 +197,33 @@ config-scheme:
+ 
+ scheme-cross:
+ 	env MAKE_BOOT_FOR_CROSS=yes SCHEME_SRC="$(SCHEME_DIR)" SCHEME_WORKAREA=$(SCHEME_WORKAREA) MACH="$(TARGET_MACH)" $(BOOTSTRAP_RACKET) "$(SCHEME_DIR)"/rktboot/make-boot.rkt
++	$(MAKE) finish-scheme-cross
++
++finish-scheme-cross:
+ 	$(SHELL) $(srcdir)/reset_boot.sh $(TARGET_MACH) $(SCHEME_WORKAREA)
+ 	cd $(SCHEME_WORKAREA) && "$(UP_SCHEME_DIR)"/configure @SCHEME_CROSS_CONFIG_ARGS@ $(SCHEME_CONFIG_VARS)
+ 	cd $(SCHEME_WORKAREA)/$(TARGET_MACH)/c && $(CHOST_HACK@T_CROSS_MODE@) $(MAKE) o=o cross=t
+-	$(MAKE) $(SCHEME_WORKAREA)/$(TARGET_MACH)/s/xpatch
++	$(MAKE) $(XPATCH_FILE)
++
++scheme-cross-via-scheme:
++	$(MAKE) $(SCHEME_WORKAREA)/boot/$(TARGET_MACH)/scheme.boot MACH=$(TARGET_MACH)
++	$(MAKE) finish-scheme-cross
+ 
+ # Rebuild patch file and cross "petite.boot" and "scheme.boot" when older
+-# than the build-host "scheme.boot" or when "make-boot.rkt" touchs dummy boot files
+-XPATCH_DEPS = $(SCHEME_HOST_WORKAREA)/$(MACH)/boot/$(MACH)/scheme.boot \
+-              $(SCHEME_WORKAREA)/boot/$(TARGET_MACH)/scheme.boot
++# than the build-<host "scheme.boot" or when "make-boot.rkt" touchs dummy boot files
++XPATCH_DEPS_built = $(SCHEME_HOST_WORKAREA)/$(MACH)/boot/$(MACH)/scheme.boot \
++                    $(SCHEME_WORKAREA)/boot/$(TARGET_MACH)/scheme.boot
++XPATCH_DEPS_existing = 
++XPATCH_DEPS = $(XPATCH_DEPS@USE_SCHEME_MODE@)
+ 
+-$(SCHEME_WORKAREA)/$(TARGET_MACH)/s/xpatch: $(XPATCH_DEPS)
++$(XPATCH_FILE): $(XPATCH_DEPS)
+ 	$(MAKE) bounce TARGET=build-xpatch-using-host
+ 
+ build-xpatch-using-host:
+ 	cd $(SCHEME_WORKAREA)/$(TARGET_MACH)/s && $(MAKE) -f Mf-cross m=$(MACH) xm=$(TARGET_MACH) Scheme="$(SCHEME_BIN)" SCHEMEHEAPDIRS="$(SCHEME_INC)"
+ 
+ XPATCH =
+-XPATCHcross = --xpatch $(SCHEME_WORKAREA)/$(TARGET_MACH)/s/xpatch
++XPATCHcross = --xpatch $(XPATCH_FILE)
+ 
+ racket.boot: racket.so
+ 	$(SCHEME) --script $(srcdir)/convert-to-boot.ss @BOOT_COMPRESS_COMP@ $(XPATCH@CROSS_MODE@) racket.so racket.boot $(TARGET_MACH)
+@@ -410,7 +434,7 @@ install-cross:
+ 	$(MAKE) compile-xpatch.$(TARGET_MACH)
+ 	$(MAKE) library-xpatch.$(TARGET_MACH)
+ 
+-SCHEME_XPATCH = $(SCHEME_WORKAREA)/$(TARGET_MACH)/s/xpatch
++SCHEME_XPATCH = $(XPATCH_FILE)
+ 
+ CROSS_SERVE_DEPS = $(srcdir)/mk-cross-serve.ss $(srcdir)/cross-serve.ss \
+                    $(srcdir)/../expander/env.ss $(srcdir)/../linklet/config.ss
+diff --git a/racket/src/cs/c/configure b/racket/src/cs/c/configure
+index 21695a431a..1eeef57753 100755
+--- a/racket/src/cs/c/configure
++++ b/racket/src/cs/c/configure
+@@ -654,6 +654,9 @@ MINGW
+ NOT_OSX
+ OSX
+ SETUP_BOOT_MODE
++USE_SCHEME_MODE
++MAKE_SCHEME_SCHEME
++MAKE_SCHEME_MODE
+ TT_CROSS_MODE
+ T_CROSS_MODE
+ CROSS_MODE
+@@ -1448,7 +1451,7 @@ Optional Features:
+   --enable-docs           build docs on install (enabled by default)
+   --enable-usersetup      setup user-specific files on install
+   --enable-racket=<path>  use <path> as Racket for build; or "auto" to create
+-  --enable-scheme=<path>  use <path> as host's build directory for cross
++  --enable-scheme=<path>  use <path> as host build for cross
+   --enable-mach=<mach>    use Chez Scheme machine type <mach>
+   --enable-target=<mach>  cross-build for Chez Scheme machine type <mach>
+   --enable-portable       prefer portable to host-specific
+@@ -2867,7 +2870,7 @@ show_explicitly_enabled "${enable_xonx}" "Unix style"
+ show_explicitly_enabled "${enable_libzo}" 'Compiled ".zo" files moved to lib'
+ 
+ show_explicitly_set "${enable_racket}" "Racket"
+-show_explicitly_set "${enable_scheme}" "Chez Scheme build directory"
++show_explicitly_set "${enable_scheme}" "Chez Scheme for build"
+ show_explicitly_set "${enable_mach}" "machine type"
+ show_explicitly_set "${enable_target}" "cross-build machine type"
+ show_explicitly_enabled "${enable_portable}" "portable"
+@@ -4745,9 +4748,21 @@ esac
+ 
+ SCHEME_DIR=${srcdir}/../../ChezScheme
+ MAKE_BUILD_SCHEME=checkout
++USE_SCHEME_MODE="_built"
++MAKE_SCHEME_MODE="${T_CROSS_MODE}"
+ 
+ if test "${enable_scheme}" != "" ; then
+-  CS_HOST_WORKAREA_PREFIX="${enable_scheme}/"
++   if test -d "${enable_scheme}" ; then
++     # Directory exists, so use it as a build directory
++     echo "Using supplied Scheme path as a build directory"
++     CS_HOST_WORKAREA_PREFIX="${enable_scheme}/"
++   else
++     # Directory does not exist, so assume it's an executable
++     echo "Using supplied Scheme path as an executable"
++     MAKE_SCHEME_MODE="${T_CROSS_MODE}-via-scheme"
++     MAKE_SCHEME_SCHEME="${enable_scheme}"
++     USE_SCHEME_MODE="_existing"
++   fi
+ fi
+ 
+ if test "${enable_racket}" != "" ; then
+@@ -6012,6 +6027,9 @@ SCHEME_CROSS_CONFIG_ARGS="--machine=${TARGET_MACH} --disable-x11 ${cs_auto_flags
+ 
+ 
+ 
++
++
++
+ 
+ 
+ 
+diff --git a/racket/src/cs/c/configure.ac b/racket/src/cs/c/configure.ac
+index 464ebe1760..aaee88156d 100644
+--- a/racket/src/cs/c/configure.ac
++++ b/racket/src/cs/c/configure.ac
+@@ -23,7 +23,7 @@ AC_ARG_ENABLE(compressmore, [  --enable-compressmore   compress compiled code ev
+ AC_ARG_ENABLE(compressboot, [  --enable-compressboot   compress boot files])
+ m4_include(../ac/path_arg.m4)
+ AC_ARG_ENABLE(racket,     [  --enable-racket=<path>  use <path> as Racket for build; or "auto" to create])
+-AC_ARG_ENABLE(scheme,     [  --enable-scheme=<path>  use <path> as host's build directory for cross])
++AC_ARG_ENABLE(scheme,     [  --enable-scheme=<path>  use <path> as host build for cross])
+ AC_ARG_ENABLE(mach,       [  --enable-mach=<mach>    use Chez Scheme machine type <mach>])
+ AC_ARG_ENABLE(target,     [  --enable-target=<mach>  cross-build for Chez Scheme machine type <mach>])
+ m4_include(../ac/portable_arg.m4)
+@@ -81,7 +81,7 @@ show_explicitly_disabled "${enable_compressboot}" "Compressed boot files"
+ show_explicitly_enabled "${enable_xonx}" "Unix style"
+ m4_include(../ac/path_show.m4)
+ show_explicitly_set "${enable_racket}" "Racket"
+-show_explicitly_set "${enable_scheme}" "Chez Scheme build directory"
++show_explicitly_set "${enable_scheme}" "Chez Scheme for build"
+ show_explicitly_set "${enable_mach}" "machine type"
+ show_explicitly_set "${enable_target}" "cross-build machine type"
+ m4_include(../ac/portable_show.m4)
+@@ -504,9 +504,21 @@ esac
+ 
+ SCHEME_DIR=${srcdir}/../../ChezScheme
+ MAKE_BUILD_SCHEME=checkout
++USE_SCHEME_MODE="_built"
++MAKE_SCHEME_MODE="${T_CROSS_MODE}"
+ 
+ if test "${enable_scheme}" != "" ; then
+-  CS_HOST_WORKAREA_PREFIX="${enable_scheme}/"
++   if test -d "${enable_scheme}" ; then
++     # Directory exists, so use it as a build directory
++     echo "Using supplied Scheme path as a build directory"
++     CS_HOST_WORKAREA_PREFIX="${enable_scheme}/"
++   else
++     # Directory does not exist, so assume it's an executable
++     echo "Using supplied Scheme path as an executable"
++     MAKE_SCHEME_MODE="${T_CROSS_MODE}-via-scheme"
++     MAKE_SCHEME_SCHEME="${enable_scheme}"
++     USE_SCHEME_MODE="_existing"
++   fi     
+ fi
+ 
+ if test "${enable_racket}" != "" ; then
+@@ -821,6 +833,9 @@ AC_SUBST(DIFF_MACH)
+ AC_SUBST(CROSS_MODE)
+ AC_SUBST(T_CROSS_MODE)
+ AC_SUBST(TT_CROSS_MODE)
++AC_SUBST(MAKE_SCHEME_MODE)
++AC_SUBST(MAKE_SCHEME_SCHEME)
++AC_SUBST(USE_SCHEME_MODE)
+ AC_SUBST(SETUP_BOOT_MODE)
+ AC_SUBST(OSX)
+ AC_SUBST(NOT_OSX)
+-- 
+2.32.0
+