cs: enable embedding in other applications

The "Inside: Racket C API" manual now has a CS part and a BC part.
This commit is contained in:
Matthew Flatt 2020-03-06 12:23:22 -07:00
parent d6d30d2fcc
commit 00b6803e36
34 changed files with 1581 additions and 299 deletions

View File

@ -87,13 +87,16 @@ Racket is available in three implementation variants: @deftech{3m},
]
The @tech{3m} and @tech{CGC} variants are collectively known as the
@deftech{BC} (``before CS'') variant.
In general, Racket programs should run the same in all variants.
Furthermore, the performance characteristics of Racket program should
be similar in the @tech{3m} and @tech{CS} variants. The cases where a
program may depends on the variant will typically involve interactions
with foreign libraries; in particular, the Racket C API described in
@other-doc[inside-doc] is available only for the virtual machine of
the @tech{3m} and @tech{CGC} variants.
@other-doc[inside-doc] is different for the @tech{3m} and @tech{CGC}
variants versus the @tech{CS} variant.
@; ----------------------------------------------------------------------

View File

@ -0,0 +1,30 @@
#lang scribble/base
@title[#:style '(toc grouper) #:tag "bc"]{Inside Racket BC (3m and CGC)}
The Racket BC API was originally designed for a tight integration with
C code. As a result, the BC API is considerably larger than the Racket
CS API.
@local-table-of-contents[]
@include-section["overview.scrbl"]
@include-section["embedding.scrbl"]
@include-section["extensions.scrbl"]
@include-section["values.scrbl"]
@include-section["memory.scrbl"]
@include-section["namespaces.scrbl"]
@include-section["procedures.scrbl"]
@include-section["eval.scrbl"]
@include-section["exns.scrbl"]
@include-section["threads.scrbl"]
@include-section["params.scrbl"]
@include-section["contmarks.scrbl"]
@include-section["strings.scrbl"]
@include-section["numbers.scrbl"]
@include-section["ports.scrbl"]
@include-section["structures.scrbl"]
@include-section["security.scrbl"]
@include-section["custodians.scrbl"]
@include-section["subprocesses.scrbl"]
@include-section["misc.scrbl"]

View File

@ -0,0 +1,254 @@
#lang scribble/doc
@(require "utils.rkt"
scribble/bnf)
@title[#:tag "cs-embedding"]{Embedding into a Program}
@section-index["embedding Racket CS"]
To embed Racket CS in a program, follow these steps:
@itemize[
@item{Locate or build the Racket CS library.
On Unix, the library is @as-index{@filepath{libracketcs.a}}.
Building from source and installing places the libraries into the
installation's @filepath{lib} directory.
On Windows, link to @filepath{libracketcs@italic{x}.dll} (where
@italic{x} represents the version number). At run time, either
@filepath{libracketcs@italic{x}.dll} must be moved to a location in
the standard DLL search path, or your embedding application must
``delayload'' link the DLLs and explicitly load them before use.
(@filepath{Racket.exe} uses the latter strategy.)
On Mac OS, besides @as-index{@filepath{libracketcs.a}} for static
linking, a dynamic library is provided by the @filepath{Racket}
framework, which is typically installed in @filepath{lib}
sub-directory of the installation. Supply @exec{-framework Racket}
to @exec{gcc} when linking, along with @exec{-F} and a path to the
@filepath{lib} directory. At run time, either
@filepath{Racket.framework} must be moved to a location in the
standard framework search path, or your embedding executable must
provide a specific path to the framework (possibly an
executable-relative path using the Mach-O @tt["@executable_path"]
prefix).}
@item{For each C file that uses Racket library functions,
@cpp{#include} the files @as-index{@filepath{chezscheme.h}}
and @as-index{@filepath{racketcs.h}}.
The @filepath{chezscheme.h} and @filepath{racketcs.h} files are
distributed with the Racket software in the installation's
@filepath{include} directory. Building and installing from source
also places this file in the installation's @filepath{include}
directory.}
@item{In your program, call @cppi{racket_boot}. The
@cppi{racket_boot} function takes a pointer to a
@cpp{racket_boot_arguments_t} for configuring the Racket instance.
After zeroing out the @cpp{racket_boot_arguments_t} value
(typicially with @cpp{memset}), only the following fields are
required to be set:
@itemlist[
@item{@cpp{exec_file} --- a path to be reported by
@racket[(find-system-path 'exec-file)], usually
@cpp{argv[0]} for the @cpp{argv} received by your program's
@cpp{main}.}
@item{@cpp{boot1_path} --- a path to @filepath{petite.boot}. Use
a path that includes at least one directory separator.}
@item{@cpp{boot2_path} --- a path to @filepath{scheme.boot} (with
a separator).}
@item{@cpp{boot3_path} --- a path to @filepath{racket.boot}
(which a separator).}
]
The @filepath{petite.boot}, @filepath{scheme.boot}, and
@filepath{racket.boot} files are distributed with the Racket
software in the installation's @filepath{lib} directory. These files
can be combined into a single file---or even embedded into the
executable---as long as the @cpp{boot1_offset}, @cpp{boot2_offset},
and @cpp{boot3_offset} fields of @cpp{racket_boot_arguments_t} are
set to identify the starting offset of each boot image in the file.}
@item{Configure the main thread's namespace by adding module
declarations. The initial namespace contains declarations only for a
few primitive modules, such as @racket['#%kernel], and no bindings
are imported into the top-level environment.
To embed a module like @racketmodname[racket/base] (along with all
its dependencies), use
@seclink["c-mods" #:doc raco-doc]{@exec{raco ctool --c-mods @nonterm{dest}}},
which generates a C file @nonterm{dest}
that contains modules in bytecode form as encapsulated in a static
array. The generated C file defines a @cppi{declare_modules}
function that takes no arguments and installs the modules into
the environment, and it adjusts the module name resolver to access the
embedded declarations. If embedded modules refer to runtime files
that need to be carried along, supply @DFlag{runtime} to
@exec{raco ctool --c-mods} to collect the runtime files into a
directory; see @secref[#:doc raco-doc "c-mods"] for more information.
Alternatively, set fields like @cpp{collects_dir}, @cpp{config_dir},
and/or @cpp{argv} in the @cpp{racket_boot_arguments_t} passed to
@cppi{scheme_boot} to locate collections/packages and initialize the
namespace the same way as when running the @exec{racket} executable.
On Windows, @exec{raco ctool --c-mods @nonterm{dest} --runtime
@nonterm{dest-dir}} includes in @nonterm{dest-dir} optional DLLs
that are referenced by the Racket library to support
@racket[bytes-open-converter]. Set @cpp{dll_dir} in
@cpp{racket_boot_arguments_t} to register @nonterm{dest-dir} so that
those DLLs can be found at run time.}
@item{Access Racket through @cppi{racket_dynamic_require},
@cppi{racket_eval}, and/or other functions described in this manual.
If the embedding program configures built-in parameters in a way
that should be considered part of the default configuration, then
call the @racketidfont{seal} function provided by the primitive
@racketidfont{#%boot} module afterward. The snapshot of parameter
values taken by @cpp{scheme_seal_parameters} is used for certain
privileged operations, such as installing a @|PLaneT| package.}
@item{Compile the program and link it with the Racket libraries.}
]
@index['("allocation")]{Racket} values may be moved or garbage
collected any time that @cpp{racket_...} functions are used to run
Racket code. Do not retain a reference to any Racket value across such
a call.
For example, the following is a simple embedding program that runs a
module @filepath{run.rkt}, assuming that @filepath{run.c} is created
as
@commandline{raco ctool --c-mods run.c "run.rkt"}
to generate @filepath{run.c}, which encapsulates the compiled form of
@filepath{run.rkt} and all of its transitive imports (so that they
need not be found separately a run time). Copies of
@filepath{petite.boot}, @filepath{scheme.boot}, and
@filepath{racket.boot} must be in the current directory on startup.
@filebox["main.c"]{
@verbatim[#:indent 2]{
#include <string.h>
#include "chezscheme.h"
#include "racketcs.h"
#include "run.c"
int main(int argc, char *argv[])
{
racket_boot_arguments_t ba;
memset(&ba, 0, sizeof(ba));
ba.boot1_path = "./petite.boot";
ba.boot2_path = "./scheme.boot";
ba.boot3_path = "./racket.boot";
ba.exec_file = argv[0];
racket_boot(&ba);
declare_modules();
ptr mod = Scons(Sstring_to_symbol("quote"),
Scons(Sstring_to_symbol("run"),
Snil));
racket_dynamic_require(mod, Sfalse);
return 0;
}
}}
As another example, the following is a simple embedding program that
evaluates all expressions provided on the command line and displays
the results, then runs a @racket[read]-@racket[eval]-@racket[print]
loop, all using @racketmodname[racket/base]. Run
@commandline{raco ctool --c-mods base.c ++lib racket/base}
to generate @filepath{base.c}, which encapsulates @racket[racket/base]
and all of its transitive imports.
@filebox["main.c"]{
@verbatim[#:indent 2]{
#include <string.h>
#include "chezscheme.h"
#include "racketcs.h"
#include "base.c"
static ptr to_bytevector(char *s);
int main(int argc, char *argv[])
{
racket_boot_arguments_t ba;
memset(&ba, 0, sizeof(ba));
ba.boot1_path = "./petite.boot";
ba.boot2_path = "./scheme.boot";
ba.boot3_path = "./racket.boot";
ba.exec_file = argv[0];
racket_boot(&ba);
declare_modules();
racket_namespace_require(Sstring_to_symbol("racket/base"));
{
int i;
for (i = 1; i < argc; i++) {
ptr e = to_bytevector(argv[i]);
e = Scons(Sstring_to_symbol("open-input-bytes"),
Scons(e, Snil));
e = Scons(Sstring_to_symbol("read"), Scons(e, Snil));
e = Scons(Sstring_to_symbol("eval"), Scons(e, Snil));
e = Scons(Sstring_to_symbol("println"), Scons(e, Snil));
racket_eval(e);
}
}
{
ptr rbase_sym = Sstring_to_symbol("racket/base");
ptr repl_sym = Sstring_to_symbol("read-eval-print-loop");
racket_apply(Scar(racket_dynamic_require(rbase_sym,
repl_sym)),
Snil);
}
return 0;
}
static ptr to_bytevector(char *s)
{
iptr len = strlen(s);
ptr bv = Smake_bytevector(len, 0);
memcpy(Sbytevector_data(bv), s, len);
return bv;
}
}}
If modules embedded in the executable need to access runtime files
(via @racketmodname[racket/runtime-path] forms), supply the
@DFlag{runtime} flag to @seclink["ctool" #:doc raco-doc]{@exec{raco ctool}}, specifying a directory
where the runtime files are to be gathered. The modules in the
generated @filepath{.c} file will then refer to the files in that
directory.

View File

@ -0,0 +1,40 @@
#lang scribble/doc
@(require "utils.rkt"
(for-label ffi/unsafe/vm))
@title[#:tag "cs-eval"]{Evaluation and Running Modules}
The @cppi{racket_apply} function provides basic evaluation support,
but @cppi{racket_eval}, @cppi{racket_dynamic_require}, and
@cppi{racket_namespace_require} provide higher-level support for the
most common evaluation tasks.
@function[(ptr racket_eval [ptr s_expr])]{
Evaluates @var{s_expr} in the initial Racket thread using its current
@tech[#:doc reference-doc]{namespace}, the same as calling
@racket[eval]. The @var{s_expr} can be an S-expression constructed
with pairs, symbols, etc., or it can be a @tech[#:doc
reference-doc]{syntax object}.
Use @cppi{racket_namespace_require} to initialize a namespace, or use
@cppi{racket_dynamic_require} to access functionality without going
through a top-level namespace. Although those functions are the same
as using @racket[namespace-require] and @racket[dynamic-require], they
work without having those identifiers bound in a namespace already.}
@function[(ptr racket_dynamic_require [ptr module_path] [ptr sym_or_false])]{
The same as calling @racket[dynamic-require] in the initial Racket
thread using its current namespace. See also @cppi{racket_eval}.}
@function[(ptr racket_namespace_require [ptr module_path])]{
The same as calling @racket[namespace-require] in the initial Racket
thread using its current namespace. See also @cppi{racket_eval}.}
@function[(ptr racket_primitive [const-char* name])]{
Accesses a primitive function in the same sense as
@racket[vm-primitive] from @racketmodname[ffi/unsafe/vm].}

View File

@ -0,0 +1,96 @@
#lang scribble/doc
@(require "utils.rkt")
@title[#:tag "cs-overview"]{Overview}
The Racket CS run-time system is implemented by a wrapper around the
Chez Scheme kernel. The wrapper implements additional glue to the
operating system (e.g., for I/O and networking) and provides entry
points into the Racket layer's evaluator.
@; ----------------------------------------------------------------------
@section{``S'' versus ``Racket''}
In the C API for Racket CS, names that start with @cpp{S} are from the
Chez Scheme layer, while names that start with @cpp{racket_} are from
the Racket wrapper.
@; ----------------------------------------------------------------------
@section{Building Racket CS from Source}
The normal Racket distribution includes @filepath{.rkt} sources for
collection-based libraries. After modifying library files, run
@exec{raco setup} (see @secref[#:doc '(lib
"scribblings/raco/raco.scrbl") "setup"]) to rebuild installed
libraries.
The normal Racket distribution does not include the C sources for
Racket's run-time system. To build Racket from scratch, download a
source distribution from @url{http://download.racket-lang.org};
detailed build instructions are in the @filepath{README} file in the
top-level @filepath{src} directory. You can also get the latest
sources from the @tt{git} repository at
@url{https://github.com/racket/racket}, but beware that the repository is
one step away from a normal source distribution, and it provides build
modes that are more suitable for developing Racket itself; see
@filepath{INSTALL.txt} in the @tt{git} repository for more
information.
@; ----------------------------------------------------------------------
@section[#:tag "cs-memory"]{Racket CS Memory Management}
@index['("allocation")]{Racket} values may be moved or garbage
collected any time that @cpp{racket_...} functions are used to run
Racket code. Do not retain a reference to any Racket value across such
a call. This requirement contrasts with the 3m and CGC variants of
Racket, which provide a way for C code to more directly cooperate with
the memory manager.
API functions that start with @cpp{S} do not collect or move objects
unless noted otherwise, so references to Racket values across such
calls is safe.
@; ----------------------------------------------------------------------
@section[#:tag "cs-places"]{Racket CS and Places}
Each Racket @|tech-place| corresponds to a Chez Scheme thread, which
also corresponds to an OS-implemented thread. Chez Scheme threads
share a global allocation space, so GC-managed objects can be safely
be communicated from one place to another. Beware, however, that Chez
Scheme threads are unsafe; any synchronization needed to safely share
a value across places is must be implemented explicitly. Racket-level
functions for places will only share values across places when they
can be safely used in both places.
In an @seclink["cs-embedding"]{embedding application}, the OS thread
that originally calls @cpp{racket_boot} is the OS thread of the
original place.
@; ----------------------------------------------------------------------
@section{Racket CS and Threads}
Racket implements threads for Racket programs without aid from the
operating system or Chez Scheme's threads, so that Racket threads are
cooperative from the perspective of C code. Stand-alone Racket uses a
few private OS-implemented threads for background tasks, but these
OS-implemented threads are never exposed by the Racket API.
Racket can co-exist with additional OS-implemented threads, but care
must be taken when calling @cpp{S} functions, and additional OS or
Chez Scheme threads must not call any @cpp{racket_} function. For
other OS threads to call @cpp{S} functions, the thread must be first
activated as a Chez Scheme thread using @cppi{Sactivate_thread}.
@; ----------------------------------------------------------------------
@section[#:tag "cs-intsize"]{Racket CS Integers}
The C type @cpp{iptr} is defined by Racket CS headers to be an integer
type that is big enough to hold a pointer value. In other words, it is
an alias for @cpp{intptr_t}. The @cpp{uptr} type is the unsigned variant.

View File

@ -0,0 +1,60 @@
#lang scribble/doc
@(require "utils.rkt")
@title[#:tag "cs-procs"]{Calling Procedures}
Normally, C programs should call Racket procedures by using
@cppi{racket_apply}, which calls the procedure in the initial Racket
thread of the main Racket place. Chez Scheme entry points such as
@cppi{Scall0} and @cppi{Scall} directly call a procedure outside of
any Racket thread, which will not work correctly with Racket
facilities such as threads, parameters, continuations, or continuation
marks.
@; ----------------------------------------------------------------------
@function[(ptr racket_apply [ptr proc] [ptr arg_list])]{
Applies the Racket procedure @var{proc} to the list of arguments
@var{arg_list}. The procedure is called in the original Racket thread
of the main Racket place. Applying @var{proc} must not raise an
exception or otherwise escape from the call to @var{proc}.
The result is a list of result values, where a single result from
@var{proc} causes @cpp{racket_apply} to return a list of length one.
Other Racket threads can run during the call to @var{proc}. At the
point that @var{proc} results, all Racket thread scheduling in the
main Racket place is suspended. No garbage collections will occur, so
other Racket places can block waiting for garbage collection.}
@together[(
@function[(ptr Scall0 [ptr proc])]
@function[(ptr Scall1 [ptr proc] [ptr arg1])]
@function[(ptr Scall2 [ptr proc] [ptr arg1] [ptr arg2])]
@function[(ptr Scall3 [ptr proc] [ptr arg1] [ptr arg2] [ptr arg3])]
)]{
Applies the Chez Scheme procedure @var{proc} to zero, one, two, or
three arguments. Beware that not all Racket procedures are Chez Scheme
procedures. (For example, an instance of a structure type that has
@racket[prop:procedure] is not a Chez Scheme procedure.)
The procedure is called outside of any Racket thread, and other Racket
threads are not scheduled during the call to @var{proc}. A garbage
collection may occur.}
@together[(
@function[(void Sinitframe [iptr num_args])]
@function[(void Sput_arg [iptr i] [ptr arg])]
@function[(ptr Scall [ptr proc] [iptr num_args])]
)]{
Similar to @cppi{Scall0}, but these functions are used in sequence to
apply a Chez Scheme procedure to an arbitrary number of arguments.
First, @cppi{Sinitframe} is called with the number of arguments. Then,
each argument is installed with @cppi{Sput_arg}, where the @var{i}
argument indicates the argumenrt position and @var{arg} is the
argument value. Finally, @cppi{Scall} is called with the procedure and
the number of arguments (which must match the number provided to
@cppi{Sinitframe}).}

View File

@ -0,0 +1,116 @@
#lang scribble/doc
@(require "utils.rkt")
@title[#:tag "cs-start"]{Starting and Declaring Initial Modules}
As sketched in @secref["cs-embedding"], and embedded instance of
Racket CS is started with @cppi{racket_boot}. Functions such as
@cppi{racket_embedded_load_bytes} help to initialize a Racket
namespace with already-compiled modules.
For functions and struct fields that contain a path in @cpp{char*}
form, the path is treated as UTF-8 encoded on Windows.
@section[#:tag "cs-boot-arguments"]{Boot and Configuration}
@function[(void racket_boot [racket_boot_arguments_t* boot_args])]{
Initializes a Racket CS instance. A main thread is created and then
suspended, waiting for further evaluation via @cppi{racket_apply},
@cppi{racket_eval}, and similar functions.
A @cpp{racket_boot_arguments_t} struct contains fields to specify how
@cppi{racket_boot} should initialize a Racket instance. New fields may
be added in the future, but in that case, a @cpp{0} or @cpp{NULL}
value for a field will imply backward-compatible default.
Fields in @cppdef{racket_boot_arguments_t}:
@itemlist[
@item{@cpp{const char *} @cppdef{boot1_path} --- a path to a file
containing a Chez Scheme image file with base functionality.
Normally, the file is called @filepath{petite.boot}. The path
should contain a directory separator, otherwise Chez Scheme
will consult its own search path.}
@item{@cpp{long} @cppdef{boot1_offset} --- an offset into
@cpp{boot1_path} to read for the first boot image, which allows
boot images to be combined with other data in a single file.
The image as distributed is self-terminating, so no size or
ending offset is needed.}
@item{@cpp{const char *} @cppdef{boot2_path} --- like
@cpp{boot1_path}, but for the image that contains compiler
functionality, normally called @filepath{scheme.boot}.}
@item{@cpp{long} @cppdef{boot2_offset} --- an offset into
@cpp{boot2_path} to read for the second boot image.}
@item{@cpp{const char *} @cppdef{boot3_path} --- like
@cpp{boot1_path}, but for the image that contains Racket
functionality, normally called @filepath{racket.boot}.}
@item{@cpp{long} @cppdef{boot3_offset} --- an offset into
@cpp{boot2_path} to read for the thirf boot image.}
@item{@cpp{int} @cpp{argc} and @cpp{char **} @cpp{argv} ---
command-line arguments to be processed the same as for a
stand-alone @exec{racket} invocation. If @var{argv} is
@cpp{NULL}, the command line @exec{-n} is used, which loads
boot files without taking any further action.}
@item{@cpp{const char *} @cppdef{exec_file} --- a path to use for
@racket[(system-type 'exec-file)], usually @cpp{argv[0]} using
the @cpp{argv} delivered to a program's @cpp{main}. This
field must not be @cpp{NULL}.}
@item{@cpp{const char *} @cppdef{run_file} --- a path to use for
@racket[(system-type 'run-file)]. If the field is @cpp{NULL},
the value of @cppi{exec_file} is used.}
@item{@cpp{const char *} @cppdef{collects_dir} --- a path to use as
the main @filepath{collects} directory for locating library
collections. If this field holds @cpp{NULL} or @cpp{""}, then
the library-collection search path is initialized as empty.}
@item{@cpp{const char *} @cppdef{config_dir} --- a path to used as an
@filepath{etc} directory that holds configuration information,
including information about installed packages. If the value if
@cpp{NULL}, @cpp{"etc"} is used.}
@item{@cpp{wchar_t *} @cppdef{dll_dir} --- a path used to find DLLs,
such as @exec{iconv} support. Note that this path uses wide
characters, not a UTF-8 byte encoding.}
@item{@cpp{int} @cppdef{cs_compiled_subdir} --- A true value indicates
that the @racket[use-compiled-file-paths] parameter should be
initialized to have a platform-specific subdirectory of
@filepath{compiled}, which is used for a Racket CS installation
that overlays a Racket BC installation.}
]}
@; ----------------------------------------------------------------------
@section[#:tag "cs-embedded-load"]{Loading Racket Modules}
@together[(
@function[(void racket_embedded_load_bytes [const-char* code] [uptr len] [int as_predefined])]
@function[(void racket_embedded_load_file [const-char* path] [int as_predefined])]
@function[(void racket_embedded_load_file_region [const-char* path] [uptr start] [uptr end] [int as_predefined])]
)]{
These functions evaluate Racket code, either in memory as @var{code}
or loaded from @var{path}, in the initial Racket thread. The intent is
that the code is already compiled. Normally, also, the contains module
declarations. The @seclink["c-mods" #:doc raco-doc]{@exec{raco ctool
--c-mods}} and @seclink["c-mods" #:doc raco-doc]{@exec{raco ctool
--mods}} commands generate code suitable for loading with these
functions, and @DFlag{c-mods} mode generates C code that calls
@cppi{racket_embedded_load_bytes}.
If @var{as_predefined} is true, then the code is loaded during the
creation of any new Racket @tech[#:doc reference-doc]{place} in the
new place, so that modules declared by the code are loaded in the new
place, too.}

View File

@ -0,0 +1,34 @@
#lang scribble/doc
@(require "utils.rkt")
@title[#:tag "cs-thread"]{Managing OS-Level Threads}
Chez Scheme functionality can only be accessed from OS-level threads
that are known to the Chez Scheme runtime system. Otherwise, there's a
race condition between such an access and a garbage collection that is
triggered by other threads.
A thread not created by Chez Scheme can be made known to the runtime
system by activating it with @cppi{Sactivate_thread}. As long as a
thread is active by not running Chez Scheme code, the thread prevents
garbage collection in all other running threads. Deactivate a thread
using @cppi{Sdeactivate_thread}. When a deactivated thread
@function[(int Sactivate_thread)]{
Activates the current OS-level thread. An already-activated thread can
be activated again, but each activating must be balanced by a
decativation. The result is @cpp{0} if the thread was previously
activated @cpp{1} otherwise.}
@function[(void Sdeactivate_thread)]{
Deactivates the current OS-level thread---or, at least, balances on
activation, making the thread deactive if there are no remaining
activations to balance with deactivation.}
@function[(int Sdestroy_thread)]{
Releases any Chez Scheme resources associated with the current OS
thread, which must have been previously activated by which must not be
activated still.}

View File

@ -0,0 +1,266 @@
#lang scribble/doc
@(require "utils.rkt")
@title[#:tag "cs-values+types"]{Values and Types}
A Racket value is represented by a pointer-sized value. The low bits
of the value indicate the encoding that it uses. For example, two (on
32-bit platform) or three (on 64-bit platforms) low bits indicates a
fixnum encoding, while a one low bit and zero second-lowest bit
indicates a pair whose address in memory is specified by the other
bits.
The C type for a Racket value is @tt{ptr}. For most Racket types, a
constructor is provided for creating values of the type. For example,
@cpp{Scons} takes two @cpp{ptr} values and returns the @racket[cons]
of the values as a new @cpp{ptr} value. In addition to providing
constructors, Racket defines several global constant Racket values,
such as @cppi{Strue} for @racket[#t].
@; ----------------------------------------------------------------------
@section[#:tag "cs-constants"]{Global Constants}
There are six global constants:
@itemize[
@item{@cppdef{Strue} --- @racket[#t]}
@item{@cppdef{Sfalse} --- @racket[#f]}
@item{@cppdef{Snil} --- @racket[null]}
@item{@cppdef{Seof-object} --- @racket[eof-object]}
@item{@cppdef{Svoid} --- @racket[(void)]}
]
@; ----------------------------------------------------------------------
@section[#:tag "cs-value-funcs"]{Value Functions}
Many of these functions are actually macros.
@(define-syntax-rule (predicates (name ...) desc ...)
(together
(@function[(int name [ptr v])] ...)
desc ...))
@predicates[(Sfixnump
Scharp
Snullp
Seof_objectp
Sbooleanp
Spairp
Ssymbolp
Sprocedurep
Sflonump
Svectorp
Sfxvectorp
Sbytevectorp
Sstringp
Sbignump
Sboxp
Sinexactnump
Sexactnump
Sratnump
Srecordp)]{
Predicates to recognize different kinds of Racket values, such as
fixnums, characters, the empty list, etc. The @cpp{Srecord} predicate
recognizes structures, but some built-in Racket datatypes are also
implemented as records.}
@function[(ptr Sfixnum [int i])]{
Returns a Racket integer value, where @var{i} must fit in a fixnum.}
@together[(
@function[(ptr Sinteger [iptr i])]
@function[(ptr Sunsigned [uptr i])]
@function[(ptr Sinteger32 [int i])]
@function[(ptr Sunsigned32 [unsigned-int i])]
@function[(ptr Sinteger64 [long i])]
@function[(ptr Sunsigned64 [unsigned-long i])]
)]{
Returns an integer value for different conversions from C, where the
result is allocated as a bignum if necessary to hold the value.}
@function[(iptr Sfixnum_value [ptr v])]{
Converts a Racket fixnum to a C integer.}
@together[(
@function[(iptr Sinteger_value [ptr v])]
@function[(uptr Sunsigned_value [ptr v])]
@function[(int Sinteger32_value [ptr v])]
@function[(long Sunsigned32_value [ptr v])]
@function[(long Sinteger64_value [ptr v])]
@function[(unsigned-long Sunsigned64_value [ptr v])]
)]{
Converts a Racket integer (possibly a bignum) to a C integer, assuming
that the integer fits in the return type.}
@function[(ptr Sflonum [double f])]{
Returns a Racket flonum value.}
@function[(double Sflonum_value [ptr v])]{
Converts a Racket flonum value to a C floating-point number.}
@function[(ptr Schar [int ch])]{
Returns a Racket character value. The @var{ch} value must be a legal
Unicode code point (and not a surrogate, for example). All characters
are represented by constant values.}
@function[(ptr Schar_value [ptr ch])]{
Returns the Unicode code point for the Racket character @var{ch}.}
@function[(ptr Sboolean [int bool])]{
Returns @cppi{Strue} or @cppi{Sfalse}.}
@function[(ptr Scons [ptr car] [ptr cdr])]{
Makes a @racket[cons] pair.}
@together[(
@function[(ptr Scar [ptr pr])]
@function[(ptr Scdr [ptr pr])]
)]{
Extracts the @racket[car] or @racket[cdr] of a pair.}
@function[(ptr Sstring_to_symbol [const-char* str])]{
Returns the interned symbol whose name matches @var{str}.}
@function[(ptr Ssymbol_to_string [ptr sym])]{
Returns the Racket immutable string value for the Racket symbol
@var{sym}.}
@together[(
@function[(ptr Smake_string [iptr len] [int ch])]
@function[(ptr Smake_uninitialized_string [iptr len])]
)]{
Allocates a fresh Racket mutable string with @var{len} characters. The
content of the string is either all @var{ch}s when @var{ch} is
provided or unspecified otherwise.}
@together[(
@function[(ptr Sstring [const-char* str])]
@function[(ptr Sstring_of_length [const-char* str] [iptr len])]
@function[(ptr Sstring_utf8 [const-char* str] [iptr len])]
)]{
Allocates a fresh Racket mutable string with the content of @var{str}.
If @var{len} is not provided, @var{str} must be nul-terminated.
In the case of @cppi{Sstring_utf8}, @var{str} is decoded as
UTF-8, otherwise it is decided as Latin-1.}
@function[(uptr string_length [ptr str])]{
Returns the length of the string @var{str}.}
@function[(ptr Sstring_ref [ptr str] [i uptr])]{
Returns the @var{i}th Racket character of the string @var{str}.}
@function[(int Sstring_set [ptr str] [i uptr] [ptr ch])]{
Installs @var{ch} as the @var{i}th Racket character of the string @var{str}.}
@function[(ptr Smake_vector [iptr len] [ptr v])]{
Allocates a fresh mutable @tech[#:doc reference-doc]{vector} of length
@var{len} and with @var{v} initially in every slot.}
@function[(uptr Svector_length [ptr vec])]{
Returns the length of the vector @var{vec}.}
@function[(uptr Svector_ref [ptr vec] [i uptr])]{
Returns the @var{i}th element of the vector @var{vec}.}
@function[(void Svector_set [ptr vec] [i uptr] [ptr v])]{
Installs @var{v} as the @var{i}th element of the vector @var{vec}.}
@function[(ptr Smake_fxvector [iptr len] [ptr v])]{
Allocates a fresh mutable @tech[#:doc reference-doc]{fxvector} of
length @var{len} and with @var{v} initially in every slot.}
@function[(uptr Sfxvector_length [ptr vec])]{
Returns the length of the fxvector @var{vec}.}
@function[(uptr Sfxvector_ref [ptr vec] [i uptr])]{
Returns the @var{i}th fixnum of the fxvector @var{vec}.}
@function[(void Sfxvector_set [ptr vec] [i uptr] [ptr v])]{
Installs the fixnum @var{v} as the @var{i}th element of the fxvector
@var{vec}.}
@function[(ptr Smake_bytevector [iptr len] [int byte])]{
Allocates a fresh mutable @tech[#:doc reference-doc]{byte string} of
length @var{len} and with @var{byte} initially in every slot.}
@function[(uptr Sbytevector_length [ptr bstr])]{
Returns the length of the byte string @var{bstr}.}
@function[(int Sbytevector_u8_ref [ptr bstr] [i uptr])]{
Returns the @var{i}th byte of the byte string @var{bstr}.}
@function[(int Sbytevector_u8_set [ptr bstr] [i uptr] [int byte])]{
Installs @var{byte} as the @var{i}th byte of the byte string @var{bstr}.}
@function[(char* Sbytevector_data [ptr vec])]{
Returns a pointer to the start of the bytes for the byte string @var{bstr}.}
@function[(ptr Sbox [ptr v])]{
Allocates a fresh mutable @tech[#:doc reference-doc]{box} containing
@var{v}.}
@function[(ptr Sunbox [ptr bx])]{
Extract the content of the box @var{bx}.}
@function[(ptr Sset_box [ptr bx] [ptr v])]{
Installs @var{v} as the content of the box @var{bx}.}

View File

@ -0,0 +1,16 @@
#lang scribble/base
@title[#:style '(grouper toc) #:tag "cs"]{Inside Racket CS}
The Racket CS API is a small extension of the Chez Scheme C API as
described in @italic{The Chez Scheme User's Guide}.
@local-table-of-contents[]
@include-section["cs-overview.scrbl"]
@include-section["cs-embedding.scrbl"]
@include-section["cs-values.scrbl"]
@include-section["cs-procs.scrbl"]
@include-section["cs-start.scrbl"]
@include-section["cs-eval.scrbl"]
@include-section["cs-thread.scrbl"]

View File

@ -6,7 +6,7 @@
@title[#:tag "embedding"]{Embedding into a Program}
@section-index["embedding Racket"]
@section-index["embedding Racket BC"]
The Racket run-time system can be embedded into a larger program. The
embedding process for Racket CGC or Racket 3m (see @secref[cgc-v-3m])
@ -66,7 +66,7 @@ To embed Racket CGC in a program, follow these steps:
an executable-relative path using the Mach-O @tt["@executable_path"]
prefix).}
@item{For each C/C++ file that uses Racket library functions,
@item{For each C file that uses Racket library functions,
@cpp{#include} the file @as-index{@filepath{scheme.h}}.
The C preprocessor symbol @cppi{SCHEME_DIRECT_EMBEDDED} is defined
@ -122,7 +122,7 @@ To embed Racket CGC in a program, follow these steps:
that contains modules in bytecode form as encapsulated in a static
array. The generated C file defines a @cppi{declare_modules}
function that takes a @cpp{Scheme_Env*}, installs the modules into
the environment, and adjusts the module name resolver to access the
the environment, and it adjusts the module name resolver to access the
embedded declarations. If embedded modules refer to runtime files
that need to be carried along, supply @DFlag{runtime} to
@exec{raco ctool --c-mods} to collect the runtime files into a

View File

@ -11,6 +11,7 @@ and using the @seclink["top" #:doc '(lib
usually a better option than writing an extension to Racket, but
Racket also supports C-implemented extensions that plug more directly
into the run-time system.
(Racket CS does not have a similar extension interface.)
The process of creating an extension for Racket 3m or Racket CGC (see
@secref["CGC versus 3m"]) is essentially the same, but the process for

View File

@ -6,13 +6,15 @@
@author["Matthew Flatt"]
This manual describes the C interface of Racket's runtime system for
the 3m and CGC variants of Racket (but not the CS variant; see
@secref[#:doc '(lib "scribblings/guide/guide.scrbl")
"virtual-machines"]). The C
interface is relevant primarily when interacting with foreign
libraries as described in @other-manual['(lib
"scribblings/foreign/foreign.scrbl")]; even though interactions with
This manual describes the C interface of Racket's runtime system,
which varies depending on the variant of Racket (see @secref[#:doc
'(lib "scribblings/guide/guide.scrbl") "virtual-machines"]): the CS
variant of Racket has one interface, while the BC (3m and CGC)
variants of Racket have another.
The C interface is relevant to some degree when interacting with
foreign libraries as described in @other-manual['(lib
"scribblings/foreign/foreign.scrbl")]. Even though interactions with
foreign code are constructed in pure Racket using the
@racketmodname[ffi/unsafe] module, many details of representations,
memory management, and concurrency are described here. This manual
@ -23,26 +25,8 @@ and extending Racket directly with C-implemented libraries.
@; ------------------------------------------------------------------------
@include-section["overview.scrbl"]
@include-section["embedding.scrbl"]
@include-section["extensions.scrbl"]
@include-section["values.scrbl"]
@include-section["memory.scrbl"]
@include-section["namespaces.scrbl"]
@include-section["procedures.scrbl"]
@include-section["eval.scrbl"]
@include-section["exns.scrbl"]
@include-section["threads.scrbl"]
@include-section["params.scrbl"]
@include-section["contmarks.scrbl"]
@include-section["strings.scrbl"]
@include-section["numbers.scrbl"]
@include-section["ports.scrbl"]
@include-section["structures.scrbl"]
@include-section["security.scrbl"]
@include-section["custodians.scrbl"]
@include-section["subprocesses.scrbl"]
@include-section["misc.scrbl"]
@include-section["cs.scrbl"]
@include-section["bc.scrbl"]
@; ------------------------------------------------------------------------

View File

@ -20,7 +20,7 @@ all should be renamed to start @cpp{racket_}.
@; ----------------------------------------------------------------------
@section{Building Racket from Source}
@section{Building Racket BC from Source}
The normal Racket distribution includes @filepath{.rkt} sources for
collection-based libraries. After modifying library files, run
@ -93,7 +93,7 @@ to call the library.
@; ----------------------------------------------------------------------
@section[#:tag "places"]{Racket and Places}
@section[#:tag "places"]{Racket BC and Places}
Each Racket @|tech-place| corresponds to a separate OS-implemented
thread. Each place has its own memory manager. Pointers to GC-managed
@ -118,15 +118,13 @@ for the original place.
@; ----------------------------------------------------------------------
@section{Racket and Threads}
@section{Racket BC and Threads}
Racket implements threads for Racket programs without aid from the
operating system, so that Racket threads are cooperative from the
perspective of C code. On Unix, stand-alone Racket uses a single
OS-implemented thread. On Windows and Mac OS, stand-alone
Racket uses a few private OS-implemented threads for background
tasks, but these OS-implemented threads are never exposed by the
Racket API.
perspective of C code. Stand-alone Racket may uses a few private
OS-implemented threads for background tasks, but these OS-implemented
threads are never exposed by the Racket API.
Racket can co-exist with additional OS-implemented threads, but the
additional OS threads must not call any @cpp{scheme_} function. Only
@ -146,7 +144,7 @@ and embedding C code.
@; ----------------------------------------------------------------------
@section[#:tag "im:unicode"]{Racket, Unicode, Characters, and Strings}
@section[#:tag "im:unicode"]{Racket BC, Unicode, Characters, and Strings}
A character in Racket is a Unicode code point. In C, a character
value has type @cppi{mzchar}, which is an alias for @cpp{unsigned} ---
@ -163,7 +161,7 @@ See also @secref["im:strings"] and @secref["im:encodings"].
@; ----------------------------------------------------------------------
@section[#:tag "im:intsize"]{Integers}
@section[#:tag "im:intsize"]{Racket BC Integers}
Racket expects to be compiled in a mode where @cppi{short} is a
16-bit integer, @cppi{int} is a 32-bit integer, and @cppi{intptr_t} has

View File

@ -9,7 +9,7 @@
(provide Racket
mzc cpp cppi cppdef (rename-out [*var var])
function subfunction
function subfunction together
FormatD
tech-place
reference-doc raco-doc
@ -121,6 +121,19 @@
(loop (cdr types) (cdr args)))))))))
(rest-thunk)))))
(define-syntax-rule (together (func ...) expl ...)
(together*
(list func ...)
(lambda () (list expl ...))))
(define (together* funcs body-thunk)
(make-splice
(cons
(make-table
'boxed
(map table-blockss (map car (map splice-run funcs))))
(body-thunk))))
(define (boxed t)
(make-table
'boxed

View File

@ -10,15 +10,18 @@
The @DFlag{c-mods} mode for @exec{raco ctool} takes a set of Racket
modules and generates a C source file that can be used as part of
program that embeds the Racket run-time system. See @secref[#:doc
program that embeds the Racket runtime system. See @secref[#:doc
inside-doc "embedding"] in @other-manual[inside-doc] for an
explanation of embedding programs.
explanation of embedding programs. The @DFlag{mods} mode is similar, but
it generates the raw bytes for the compiled module without encoding
the bytes in C declarations.
The generated source file embeds the specified modules, and it defines
a @tt{declare_modules} function that puts the module declarations into
a namespace. Thus, using the output of @exec{raco ctool --c-mods}, a
program can embed Racket with a set of modules so that it does not
need a @filepath{collects} directory to load modules at run time.
The generated source or compiled file embeds the specified modules.
Generated C source defines a @tt{declare_modules} function that puts
the module declarations into a namespace. Thus, using the output of
@exec{raco ctool --c-mods}, a program can embed Racket with a set of
modules so that it does not need a @filepath{collects} directory to
load modules at run time.
If the embedded modules refer to runtime files, the files can be
gathered by supplying the @DFlag{runtime} argument to @exec{raco ctool

View File

@ -57,7 +57,7 @@
(let ([b (path-element->bytes
(let-values ([(base name dir?) (split-path f)])
name))])
(or (regexp-match? #rx#"[.](?i:pdb|ilk|manifest)$" b)
(or (regexp-match? #rx#"[.](?i:pdb|ilk|manifest|ipdb|iobj)$" b)
(and (not keep-cgc?)
(regexp-match? #rx#"(?i:CGC[.](?:dll|exe))$" b))
(and (not keep-3m?)

9
racket/src/ac/instlib.m4 Normal file
View File

@ -0,0 +1,9 @@
show_explicitly_enabled "${enable_libs}" "Installation of static libraries (if any)"
show_explicitly_disabled "${enable_libs}" "Installation of static libraries (if any)"
INSTALL_LIBS_ENABLE=no-install
if test "${enable_libs}" != "no" ; then
# Intended to be canceled for some platforms:
INSTALL_LIBS_ENABLE=install
fi

View File

@ -0,0 +1 @@
AC_ARG_ENABLE(libs, [ --enable-libs install static libraries (enabled by default for Unix)])

View File

@ -2734,6 +2734,7 @@ if test "${enable_libs+set}" = set; then :
fi
# Check whether --enable-libffi was given.
if test "${enable_libffi+set}" = set; then :
enableval=$enable_libffi;
@ -3284,6 +3285,14 @@ show_explicitly_disabled "${enable_strip}" "Debug-symbol stripping"
show_explicitly_enabled "${enable_libs}" "Installation of static libraries (if any)"
show_explicitly_disabled "${enable_libs}" "Installation of static libraries (if any)"
INSTALL_LIBS_ENABLE=no-install
if test "${enable_libs}" != "no" ; then
# Intended to be canceled for some platforms:
INSTALL_LIBS_ENABLE=install
fi
show_explicitly_disabled "${enable_mac64}" "64-bit Mac OS"
show_explicitly_enabled "${enable_libfw}" "Frameworks-to-system"
@ -3419,8 +3428,6 @@ MAIN_VARIANT=3m
INSTALL_SETUP_FLAGS=
INSTALL_SETUP_RACKET_FLAGS=
INSTALL_LIBS_ENABLE=no-install
use_flag_pthread=yes
use_flag_posix_pthread=no
mzrt_needs_pthread=yes
@ -4804,11 +4811,6 @@ if test "${enable_jit}" = "yes" ; then
check_for_mprotect=yes
fi
if test "${enable_libs}" != "no" ; then
# Canceled for some platforms below:
INSTALL_LIBS_ENABLE=install
fi
############## platform tests ################
# for flags we don't want to use in config tests:

View File

@ -22,7 +22,7 @@ CROSS_COMP =
COMPILE_FILE = $(SCHEME) --script compile-file.ss $(UNSAFE_COMP) $(COMPRESS_COMP) $(DEBUG_COMP) $(CROSS_COMP) --dest "$(BUILDDIR)"
COMPILE_FILE_DEPS = compile-file.ss include.ss place-register.ss
RACKET_SETUP_ARGS = ../../bin/racket ../../bin/racket ../collects ../etc 0 true false 0 ""
RACKET_SETUP_ARGS = false ../../bin/racket ../../bin/racket ../collects ../etc 0 true false 0 ""
PRIMITIVES_TABLES = primitive/kernel.ss primitive/unsafe.ss primitive/flfxnum.ss \
primitive/paramz.ss primitive/extfl.ss primitive/network.ss \

View File

@ -26,6 +26,9 @@ WINDRES = @WINDRES@
STRIP_DEBUG = @STRIP_DEBUG@
STRIP_LIB_DEBUG = @STRIP_LIB_DEBUG@
ICP=@ICP@
ICP_LIB=@ICP_LIB@
DEFAULT_RACKET = ../../racket/racket3m
RACKET = @RACKET@
@ -61,6 +64,7 @@ cs:
$(MAKE) check-racketcs@CROSS_MODE@
$(MAKE) gracketcs
$(MAKE) starter
$(MAKE) repack-@INSTALL_LIBS_ENABLE@-libs
SETUP_BOOT_MODE = @SETUP_BOOT_MODE@
SETUP_COMMON_BOOT = -l- setup $(SETUP_BOOT_MODE) $(srcdir)/../../setup-go.rkt $(builddir)/compiled
@ -344,6 +348,7 @@ install@MINGW@:
plain-install@MINGW@:
$(MAKE) plain-install-upcased CS_INSTALLED=`echo $(CS_INSTALLED) | awk '{print toupper($0)}'`
$(MAKE) unix-install-boot-files
plain-install-upcased:
$(ICP) libracketcsxxxxxxx.dll $(libdir)/libracketcsxxxxxxx.dll
@ -395,11 +400,25 @@ boot.o: $(srcdir)/boot.c $(srcdir)/../../rktio/rktio.inc $(srcdir)/boot.h
starter@NOT_MINGW@: $(srcdir)/../../start/ustart.c
$(CC) $(CFLAGS) -o starter $(srcdir)/../../start/ustart.c
repack-install-libs:
make libracketcs.a SCHEME_SRC="$(ABS_SCHEME_SRC)"
libracketcs.a: $(SCHEME_LIB_DEPS) rktio/librktio.a boot.o
mkdir -p repack
rm -f repack/*
cd repack && @Z_LIB_UNPACK@
cd repack && @LZ4_LIB_UNPACK@
cd repack && $(AR) x ../rktio/librktio.a
cd repack && $(AR) x $(SCHEME_TARGET_INC)/libkernel.a
$(AR) $(ARFLAGS) libracketcs.a repack/*.o boot.o
repack-no-install-libs:
$(NOOP)
# ----------------------------------------
# Install
ICP=@ICP@
install@NOT_MINGW@:
$(MAKE) plain-install
$(MAKE) setup-install
@ -438,16 +457,39 @@ common-install:
$(ICP) $(srcdir)/../../start/starter-sh "$(DESTDIR)$(libpltdir)/starter-sh"
$(RACKET) -cu "$(srcdir)/../../racket/collects-path.rkt" "$(DESTDIR)$(libpltdir)/starter" $(DESTDIR)@COLLECTS_PATH@ $(DESTDIR)@CONFIG_PATH@
$(MAKE) system-install
$(ICP) $(srcdir)/api.h $(includepltdir)/racketcs.h
$(ICP) $(srcdir)/boot.h $(includepltdir)/racketcsboot.h
$(ICP) $(SCHEME_INC)/scheme.h $(includepltdir)/chezscheme.h
$(MAKE) common-@INSTALL_LIBS_ENABLE@-libs
system-install:
$(RACKET) -cu "$(srcdir)/gen-system.rkt" $(DESTDIR)$(libpltdir)/system$(CS_INSTALLED).rktd $(TARGET_MACH) @CROSS_COMPILE_TARGET_KIND@
common-install-libs:
$(ICP_LIB) libracketcs.a "$(DESTDIR)$(libdir)/libracketcs.a"
$(STRIP_LIB_DEBUG) "$(DESTDIR)$(libdir)/libracketcs.a"
common-no-install-libs:
$(NOOP)
unix-install:
$(MAKE) common-install
rm -f "$(DESTDIR)$(libpltdir)/gracket$(CS_INSTALLED)"
$(ICP) gracketcs "$(DESTDIR)$(libpltdir)/gracket$(CS_INSTALLED)"
$(RACKET) -cu "$(srcdir)/../../racket/collects-path.rkt" "$(DESTDIR)$(bindir)/racket$(CS_INSTALLED)" $(DESTDIR)@COLLECTS_PATH@ $(DESTDIR)@CONFIG_PATH@
$(RACKET) -cu "$(srcdir)/../../racket/collects-path.rkt" "$(DESTDIR)$(libpltdir)/gracket$(CS_INSTALLED)" $(DESTDIR)@COLLECTS_PATH@ $(DESTDIR)@CONFIG_PATH@
$(MAKE) unix-@INSTALL_LIBS_ENABLE@-libs
unix-install-libs:
$(MAKE) unix-install-boot-files
unix-install-boot-files:
$(BOOTSTRAP_RACKET) $(srcdir)/add-terminator.rkt petite-v.boot "$(DESTDIR)$(libpltdir)/petite.boot"
$(BOOTSTRAP_RACKET) $(srcdir)/add-terminator.rkt scheme-v.boot "$(DESTDIR)$(libpltdir)/scheme.boot"
$(BOOTSTRAP_RACKET) $(srcdir)/add-terminator.rkt racket-v.boot "$(DESTDIR)$(libpltdir)/racket.boot"
unix-no-install-libs:
$(NOOP)
RKTFWDEST = @FRAMEWORK_INSTALL_DIR@/Racket.framework
FRAMEWORK_REL_PREFIX = "@executable_path/../$(libpltdir_rel)/"
@ -469,6 +511,7 @@ macos-install:
cp $(RKTFWDIR)/boot/racket.boot $(DESTDIR)$(RKTFWDEST)/Versions/$(FWVERSION)_CS/boot/
$(RACKET) -cu "$(srcdir)/../../racket/collects-path.rkt" "$(DESTDIR)$(bindir)/racket$(CS_INSTALLED)" $(DESTDIR)@COLLECTS_PATH@ $(DESTDIR)@CONFIG_PATH@
$(MAKE) macos-install-gracket CS_GR_INSTALLED="`echo $(CS_INSTALLED) | tr a-z A-Z`"
$(MAKE) macos-@INSTALL_LIBS_ENABLE@-libs
macos-install-gracket:
/usr/bin/install_name_tool -change "@executable_path/Racket.framework/Versions/$(FWVERSION)_CS/Racket" "@FRAMEWORK_PREFIX@Racket.framework/Versions/$(FWVERSION)_CS/Racket" $(DESTDIR)"$(bindir)/racket$(CS_INSTALLED)"
@ -481,6 +524,14 @@ macos-install-gracket:
rm -rf $(DESTDIR)"$(libpltdir)/Starter.app"
$(ICP) -r Starter.app $(DESTDIR)"$(libpltdir)/."
macos-install-libs:
$(BOOTSTRAP_RACKET) $(srcdir)/add-terminator.rkt $(RKTFWDIR)/boot/petite.boot "$(DESTDIR)$(libpltdir)/petite.boot"
$(BOOTSTRAP_RACKET) $(srcdir)/add-terminator.rkt $(RKTFWDIR)/boot/scheme.boot "$(DESTDIR)$(libpltdir)/scheme.boot"
$(BOOTSTRAP_RACKET) $(srcdir)/add-terminator.rkt $(RKTFWDIR)/boot/racket.boot "$(DESTDIR)$(libpltdir)/racket.boot"
macos-no-install-libs:
$(NOOP)
# ----------------------------------------
# Check

View File

@ -0,0 +1,28 @@
#lang racket/base
(require racket/cmdline)
(command-line
#:args
(src dest)
(copy-file src dest #t)
(define terminator
(cond
[(equal? #"\0\0\0\0chez" (call-with-input-file*
dest
(lambda (i) (read-bytes 8 i))))
;; Not compressed
#"\177"]
[else
;; Compressed
#"\0"]))
(call-with-output-file*
dest
#:exists 'update
(lambda (o)
(file-position o (file-size dest))
(write-bytes terminator o)))
(void))

27
racket/src/cs/c/api.h Normal file
View File

@ -0,0 +1,27 @@
/* include "chezscheme.h" before this file */
#ifndef RACKETCS_H
#define RACKETCS_H
#ifndef RACKET_API_EXTERN
# define RACKET_API_EXTERN EXPORT
#endif
#ifndef RACKETCS_BOOT_H
# define BOOT_EXTERN EXPORT
# include "racketcsboot.h"
#endif
RACKET_API_EXTERN ptr racket_apply(ptr proc, ptr arg_list);
RACKET_API_EXTERN ptr racket_primitive(const char *name);
RACKET_API_EXTERN ptr racket_eval(ptr s_expr);
RACKET_API_EXTERN ptr racket_dynamic_require(ptr module_path, ptr sym_or_false);
RACKET_API_EXTERN void racket_namespace_require(ptr module_path);
RACKET_API_EXTERN void racket_embedded_load_bytes(const char *code, uptr len, int as_predefined);
RACKET_API_EXTERN void racket_embedded_load_file(const char *path, int as_predefined);
RACKET_API_EXTERN void racket_embedded_load_file_region(const char *path, uptr start, uptr end, int as_predefined);
#endif

View File

@ -10,11 +10,13 @@
#include "rktio.h"
#ifdef WIN32
# define BOOT_EXTERN __declspec(dllexport)
# define RACKET_API_EXTERN __declspec(dllexport)
#else
# define BOOT_EXTERN extern
# define RACKET_API_EXTERN extern
#endif
#define BOOT_EXTERN RACKET_API_EXTERN
#include "boot.h"
#include "api.h"
#define RACKET_AS_BOOT
@ -26,45 +28,6 @@
# define BOOT_O_BINARY 0
#endif
#if defined(OS_X) && !defined(RACKET_XONX)
# include <mach-o/dyld.h>
# define RACKET_USE_FRAMEWORK
const char *get_framework_path() {
int i, c, len;
const char *s;
c = _dyld_image_count();
for (i = 0; i < c; i++) {
s = _dyld_get_image_name(i);
len = strlen(s);
if ((len > 7) && !strcmp("/Racket", s + len - 7)) {
char *s2;
s2 = strdup(s);
strcpy(s2 + len - 6, "boot");
return s2;
}
}
return "???";
}
char *path_append(const char *p1, char *p2) {
int l1, l2;
char *s;
l1 = strlen(p1);
l2 = strlen(p2);
s = malloc(l1 + l2 + 2);
memcpy(s, p1, l1);
s[l1] = '/';
memcpy(s + l1 + 1, p2, l2);
s[l1+l2+1] = 0;
return s;
}
#endif
static ptr Sbytevector(char *s)
{
iptr len = strlen(s);
@ -135,75 +98,42 @@ static void init_foreign()
Sforeign_symbol("racket_errno", (void *)racket_errno);
}
void racket_boot(int argc, char **argv, char *exec_file, char *run_file,
char *boot_exe, long segment_offset,
char *coldir, char *configdir, /* wchar_t * */void *dlldir,
int is_embedded, int pos1, int pos2, int pos3,
int cs_compiled_subdir, int is_gui,
int wm_is_gracket_or_x11_arg_count,
char *gracket_guid_or_x11_args,
void *dll_open, void *dll_find_object, void *dll_close)
/* exe argument already stripped from argv */
void racket_boot(racket_boot_arguments_t *ba)
{
int fd;
#ifdef RACKET_AS_BOOT
int skip_racket_boot = 0;
#endif
#ifdef RACKET_USE_FRAMEWORK
const char *fw_path;
#endif
int cross_server = 0;
#ifdef WIN32
if (dlldir)
rktio_set_dll_path((wchar_t *)dlldir);
if (dll_open)
rktio_set_dll_procs(dll_open, dll_find_object, dll_close);
if (ba->dll_dir)
rktio_set_dll_path((wchar_t *)ba->dll_dir);
if (ba->dll_open)
rktio_set_dll_procs(ba->dll_open, ba->dll_find_object, ba->dll_close);
#endif
Sscheme_init(NULL);
if ((argc == 4) && !strcmp(argv[0], "--cross-server")) {
if ((ba->argc == 4) && !strcmp(ba->argv[0], "--cross-server"))
cross_server = 1;
#ifdef RACKET_AS_BOOT
skip_racket_boot = 1;
#endif
}
#ifdef RACKET_USE_FRAMEWORK
if (!is_embedded) {
fw_path = get_framework_path();
Sregister_boot_file(path_append(fw_path, "petite.boot"));
Sregister_boot_file(path_append(fw_path, "scheme.boot"));
# ifdef RACKET_AS_BOOT
if (!skip_racket_boot)
Sregister_boot_file(path_append(fw_path, "racket.boot"));
# endif
}
#endif
{
int fd1, fd2;
if (is_embedded) {
fd = open(boot_exe, O_RDONLY | BOOT_O_BINARY);
{
int fd1, fd2;
fd1 = dup(fd);
lseek(fd1, pos1, SEEK_SET);
Sregister_boot_file_fd("petite", fd1);
fd1 = open(ba->boot1_path, O_RDONLY | BOOT_O_BINARY);
lseek(fd1, ba->boot1_offset, SEEK_SET);
Sregister_boot_file_fd("petite", fd1);
fd2 = open(boot_exe, O_RDONLY | BOOT_O_BINARY);
lseek(fd2, pos2, SEEK_SET);
Sregister_boot_file_fd("scheme", fd2);
fd2 = open(ba->boot2_path, O_RDONLY | BOOT_O_BINARY);
lseek(fd2, ba->boot2_offset, SEEK_SET);
Sregister_boot_file_fd("scheme", fd2);
# ifdef RACKET_AS_BOOT
if (!skip_racket_boot) {
fd = open(boot_exe, O_RDONLY | BOOT_O_BINARY);
lseek(fd, pos3, SEEK_SET);
Sregister_boot_file_fd("racket", fd);
}
# endif
if (!cross_server) {
int fd3;
fd3 = open(ba->boot3_path, O_RDONLY | BOOT_O_BINARY);
lseek(fd3, ba->boot3_offset, SEEK_SET);
Sregister_boot_file_fd("racket", fd3);
}
# endif
}
Sbuild_heap(NULL, init_foreign);
@ -211,7 +141,7 @@ void racket_boot(int argc, char **argv, char *exec_file, char *run_file,
if (cross_server) {
/* Don't run Racket as usual. Instead, load the patch
file and run `serve-cross-compile` */
run_cross_server(argv);
run_cross_server(ba->argv);
racket_exit(0);
}
@ -220,20 +150,25 @@ void racket_boot(int argc, char **argv, char *exec_file, char *run_file,
int i;
char segment_offset_s[32], wm_is_gracket_s[32];
for (i = argc; i--; ) {
l = Scons(Sbytevector(argv[i]), l);
if (ba->argv) {
for (i = ba->argc; i--; ) {
l = Scons(Sbytevector(ba->argv[i]), l);
}
} else {
l = Scons(Sbytevector("-n"), l);
}
l = Scons(Sbytevector(gracket_guid_or_x11_args), l);
sprintf(wm_is_gracket_s, "%d", wm_is_gracket_or_x11_arg_count);
l = Scons(Sbytevector(ba->gracket_guid_or_x11_args ? ba->gracket_guid_or_x11_args : ""), l);
sprintf(wm_is_gracket_s, "%d", ba->wm_is_gracket_or_x11_arg_count);
l = Scons(Sbytevector(wm_is_gracket_s), l);
l = Scons(Sbytevector(is_gui ? "true" : "false"), l);
l = Scons(Sbytevector(cs_compiled_subdir ? "true" : "false"), l);
sprintf(segment_offset_s, "%ld", segment_offset);
l = Scons(Sbytevector(ba->is_gui ? "true" : "false"), l);
l = Scons(Sbytevector(ba->cs_compiled_subdir ? "true" : "false"), l);
sprintf(segment_offset_s, "%ld", ba->segment_offset);
l = Scons(Sbytevector(segment_offset_s), l);
l = Scons(Sbytevector(configdir), l);
l = Scons(parse_coldirs(coldir), l);
l = Scons(Sbytevector(run_file), l);
l = Scons(Sbytevector(exec_file), l);
l = Scons(Sbytevector(ba->config_dir ? ba->config_dir : "etc"), l);
l = Scons(parse_coldirs(ba->collects_dir ? ba->collects_dir : ""), l);
l = Scons(Sbytevector(ba->run_file ? ba->run_file : ba->exec_file ), l);
l = Scons(Sbytevector(ba->exec_file), l);
l = Scons(Sbytevector(ba->exit_after ? "false" : "true"), l);
#ifdef RACKET_AS_BOOT
{
@ -249,19 +184,14 @@ void racket_boot(int argc, char **argv, char *exec_file, char *run_file,
}
#ifndef RACKET_AS_BOOT
# ifdef RACKET_USE_FRAMEWORK
if (!is_embedded) {
fd = open(path_append(fw_path, "racket.so"), O_RDONLY);
pos3 = 0;
}
# endif
{
ptr c, p;
int f3;
if (pos3) lseek(fd, pos3, SEEK_SET);
fd3 = open(ba->boot3_path, O_RDONLY | BOOT_O_BINARY);
if (boot3_offset) lseek(fd3, ba->boot3_offset, SEEK_SET);
c = Stop_level_value(Sstring_to_symbol("open-fd-input-port"));
p = Scall1(c, Sfixnum(fd));
p = Scall1(c, Sfixnum(fd3));
Slock_object(p);
c = Stop_level_value(Sstring_to_symbol("port-file-compressed!"));
Scall1(c, p);
@ -271,3 +201,83 @@ void racket_boot(int argc, char **argv, char *exec_file, char *run_file,
}
#endif
}
/* **************************************** */
enum {
EMBEDDED_ENTRY_APPLY,
EMBEDDED_ENTRY_PRIMITIVE_LOOKUP,
EMBEDDED_ENTRY_EVAL,
EMBEDDED_ENTRY_DYNAMIC_REQUIRE,
EMBEDDED_ENTRY_NAMESPACE_REQUIRE,
EMBEDDED_ENTRY_EMBEDDED_LOAD
};
static ptr get_embedded_entry(int index)
{
ptr vec;
vec = Stop_level_value(Sstring_to_symbol("embedded-racket-entry-info"));
return Svector_ref(vec, index);
}
ptr racket_apply(ptr proc, ptr arg_list)
{
ptr app = get_embedded_entry(EMBEDDED_ENTRY_APPLY);
return Scall2(app, proc, arg_list);
}
ptr racket_primitive(const char *name)
{
ptr prim_lookup = get_embedded_entry(EMBEDDED_ENTRY_PRIMITIVE_LOOKUP);
return Scall1(prim_lookup, Sstring_to_symbol(name));
}
ptr racket_eval(ptr s_expr)
{
ptr eval = get_embedded_entry(EMBEDDED_ENTRY_EVAL);
return racket_apply(eval, Scons(s_expr, Snil));
}
ptr racket_dynamic_require(ptr module_path, ptr sym_or_false)
{
ptr dy_req = get_embedded_entry(EMBEDDED_ENTRY_DYNAMIC_REQUIRE);
return racket_apply(dy_req, Scons(module_path, Scons(sym_or_false, Snil)));
}
void racket_namespace_require(ptr module_path)
{
ptr ns_req = get_embedded_entry(EMBEDDED_ENTRY_NAMESPACE_REQUIRE);
(void)racket_apply(ns_req, Scons(module_path, Snil));
}
static void embedded_load(ptr path, ptr start, ptr end, ptr bstr, int as_predefined)
{
ptr load = get_embedded_entry(EMBEDDED_ENTRY_EMBEDDED_LOAD);
ptr pre = (as_predefined ? Strue : Sfalse);
(void)racket_apply(load, Scons(path, Scons(start, Scons(end, Scons(bstr, Scons(pre, Snil))))));
}
void racket_embedded_load_bytes(const char *code, uptr len, int as_predefined)
{
ptr bstr = Smake_bytevector(len, 0);
memcpy(Sbytevector_data(bstr), code, len);
embedded_load(Sfalse, Sfalse, Sfalse, bstr, as_predefined);
}
void racket_embedded_load_file(const char *path, int as_predefined)
{
embedded_load(Sbytevector((char *)path), Sfixnum(0), Sfalse, Sfalse, as_predefined);
}
void racket_embedded_load_file_region(const char *path, uptr start, uptr end, int as_predefined)
{
embedded_load(Sbytevector((char *)path), Sfixnum(start), Sfixnum(end), Sfalse, as_predefined);
}

View File

@ -1,15 +1,57 @@
BOOT_EXTERN void racket_boot(int argc, char **argv, char *exec_file, char *run_file,
char *boot_exe, long segment_offset,
char *coldir, char *configdir, /* wchar_t * */void *dlldir,
int is_embedded, int pos1, int pos2, int pos3,
int cs_compiled_subdir, int is_gui,
int wm_is_gracket_or_x11_arg_count, char *gracket_guid_or_x11_args,
void *ddll_open, void *dll_find_object, void *dll_close);
#ifndef RACKETCS_BOOT_H
#define RACKETCS_BOOT_H
typedef void (*racket_boot_t)(int argc, char **argv, char *exec_file, char *run_file,
char* boot_exe, long segment_offset,
char *coldir, char *configdir, /* wchar_t * */void *dlldir,
int is_embedded, int pos1, int pos2, int pos3,
int cs_compiled_subdir, int is_gui,
int wm_is_gracket_or_x11_arg_count, char *gracket_guid_or_x11_args,
void *ddll_open, void *dll_find_object, void *dll_close);
/* This structure type can change, but NULL/0 will be supported as a
default for any new field that is added. */
typedef struct racket_boot_arguments_t {
/* Boot files --- potentially the same path with different offsets.
If a boot image is embedded in a larger file, it must be
terminated with "\0 if the boot image is compressed or "\177" if
the boot image is uncompressed. */
const char *boot1_path; /* REQUIRED; path to "petite.boot" */
long boot1_offset;
const char *boot2_path; /* REQUIRED; path to "scheme.boot" */
long boot2_offset;
const char *boot3_path; /* REQUIRED; path to "racket.boot" */
long boot3_offset;
/* Command-line arguments are handled in the same way as the
`racket` exectuable. The `argv` array should *not* include the
executable name like `argv` passed to `main`. */
int argc;
char **argv; /* NULL => "-n", which does nothing after booting */
/* Racket path configuration, mostly setting the results of
`(find-system-path ...)`: */
const char *exec_file; /* REQUIRED; usually the original argv[0] */
const char *run_file; /* can be NULL to mean the same as `exec_file` */
const char *collects_dir; /* can be NULL or "" to disable collection path */
const char *config_dir; /* use NULL or "etc" if you don't care */
/* wchar_t * */void *dll_dir; /* can be NULL for default */
/* How to initialize `use-compiled-file-paths`: */
int cs_compiled_subdir; /* true => subdirectory of "compiled" */
/* Embedded-code offset, which is added to any `-k` argument. */
long segment_offset; /* use 0 if no `-k` embedding */
/* For embedded DLLs on Windows, if non-NULL: */
void *dll_open;
void *dll_find_object;
void *dll_close;
/* Whether to run as command-line Racket or in embedded mode: */
int exit_after; /* 1 => exit after handling the command-line */
/* For GUI applications; use 0 and "" as defaults: */
int is_gui;
int wm_is_gracket_or_x11_arg_count;
char *gracket_guid_or_x11_args;
} racket_boot_arguments_t;
BOOT_EXTERN void racket_boot(racket_boot_arguments_t *boot_args);
/* Same as `racket_boot` prototype; but in type form: */
typedef void (*racket_boot_t)(racket_boot_arguments_t *boot_args);
#endif

View File

@ -622,6 +622,7 @@ ac_includes_default="\
ac_subst_vars='LTLIBOBJS
LIBOBJS
INSTALL_LIBS_ENABLE
INSTALL_SETUP_RACKET_FLAGS
INSTALL_SETUP_FLAGS
RUN_RACKET
@ -640,8 +641,10 @@ ELF_COMP
BOOT_COMPRESS_COMP
COMPRESS_COMP
CONFIGURE_RACKET_SO_COMPILE
LZ4_LIB_UNPACK
LZ4_LIB
LZ4_LIB_DEP
Z_LIB_UNPACK
Z_LIB
Z_LIB_DEP
NOT_MINGW
@ -664,6 +667,7 @@ POST_LINKER
RKTLINKER
STRIP_LIB_DEBUG
STRIP_DEBUG
ICP_LIB
ICP
WINDRES
STATIC_AR
@ -789,6 +793,7 @@ enable_libfw
enable_userfw
enable_embedfw
enable_mac64
enable_libs
enable_noopt
enable_ubsan
enable_csdefault
@ -1438,6 +1443,7 @@ Optional Features:
--enable-userfw install Mac OS frameworks to ~/Library/Frameworks
--enable-embedfw embed Mac OS framework content in executables
--enable-mac64 allow 64-bit Mac OS build (enabled by default)
--enable-libs install static libraries (enabled by default for Unix)
--enable-strip strip debug on install (usually enabled by default)
--enable-ubsan compile with -fsanitize=undefined
--enable-csdefault use CS as default build
@ -2648,6 +2654,12 @@ else
fi
# Check whether --enable-libs was given.
if test "${enable_libs+set}" = set; then :
enableval=$enable_libs;
fi
# Check whether --enable-noopt was given.
if test "${enable_noopt+set}" = set; then :
enableval=$enable_noopt;
@ -2785,6 +2797,16 @@ if test "${enable_csonly}" = "yes" ; then
fi
show_explicitly_enabled "${enable_csdefault}" "executables without suffix"
show_explicitly_enabled "${enable_libs}" "Installation of static libraries (if any)"
show_explicitly_disabled "${enable_libs}" "Installation of static libraries (if any)"
INSTALL_LIBS_ENABLE=no-install
if test "${enable_libs}" != "no" ; then
# Intended to be canceled for some platforms:
INSTALL_LIBS_ENABLE=install
fi
show_explicitly_disabled "${enable_mac64}" "64-bit Mac OS"
show_explicitly_enabled "${enable_libfw}" "Frameworks-to-system"
@ -3096,8 +3118,10 @@ INSTALL_SETUP_RACKET_FLAGS=
Z_LIB_DEP='$(OWN_Z_LIB)'
Z_LIB='$(OWN_Z_LIB)'
Z_LIB_UNPACK='$(AR) x $(OWN_Z_LIB)'
LZ4_LIB_DEP='$(OWN_LZ4_LIB)'
LZ4_LIB='$(OWN_LZ4_LIB)'
LZ4_LIB_UNPACK='$(AR) x $(OWN_LZ4_LIB)'
enable_pthread_by_default=yes
@ -4603,6 +4627,10 @@ fi
if `which ${host}-windres > /dev/null` ; then
WINDRES="${host}-windres"
fi
# ".a" is typically not useful, since we always build a DLL:
if test "${enable_libs}" = "" ; then
INSTALL_LIBS_ENABLE=no-install
fi
;;
cygwin*)
;;
@ -5239,6 +5267,7 @@ $as_echo "$have_zlib" >&6; }
else
Z_LIB_DEP=""
Z_LIB="-lz"
Z_LIB_UNPACK="$(NOOP)"
extra_scheme_config_args="${extra_scheme_config_args} ZLIB=-lz"
fi
fi
@ -5276,6 +5305,7 @@ $as_echo "$have_lz4" >&6; }
else
LZ4_LIB_DEP=""
LZ4_LIB="-llz4"
LZ4_LIB_UNPACK="$(NOOP)"
extra_scheme_config_args="${extra_scheme_config_args} LZ4=-llz4"
fi
fi
@ -5474,6 +5504,10 @@ SCHEME_CROSS_CONFIG_ARGS="--machine=${TARGET_MACH} --disable-x11 ${disable_curse

View File

@ -25,6 +25,7 @@ AC_ARG_ENABLE(mach, [ --enable-mach=<mach> Use Chez Scheme machine typ
AC_ARG_ENABLE(target, [ --enable-target=<mach> Cross-build for Chez Scheme machine type <mach>])
m4_include(../ac/natipkg_arg.m4)
m4_include(../ac/sdk_arg.m4)
m4_include(../ac/instlib_arg.m4)
m4_include(../ac/strip_arg.m4)
m4_include(../ac/ubsan_arg.m4)
AC_ARG_ENABLE(csdefault, [ --enable-csdefault use CS as default build])
@ -86,6 +87,7 @@ if test "${enable_csonly}" = "yes" ; then
fi
show_explicitly_enabled "${enable_csdefault}" "executables without suffix"
m4_include(../ac/instlib.m4)
m4_include(../ac/sdk_show.m4)
m4_include(../ac/strip_show.m4)
@ -151,8 +153,10 @@ INSTALL_SETUP_RACKET_FLAGS=
Z_LIB_DEP='$(OWN_Z_LIB)'
Z_LIB='$(OWN_Z_LIB)'
Z_LIB_UNPACK='$(AR) x $(OWN_Z_LIB)'
LZ4_LIB_DEP='$(OWN_LZ4_LIB)'
LZ4_LIB='$(OWN_LZ4_LIB)'
LZ4_LIB_UNPACK='$(AR) x $(OWN_LZ4_LIB)'
enable_pthread_by_default=yes
@ -249,6 +253,10 @@ case "$host_os" in
if `which ${host}-windres > /dev/null` ; then
WINDRES="${host}-windres"
fi
# ".a" is typically not useful, since we always build a DLL:
if test "${enable_libs}" = "" ; then
INSTALL_LIBS_ENABLE=no-install
fi
;;
cygwin*)
;;
@ -542,6 +550,7 @@ if test "${enable_libz}" = "yes" ; then
else
Z_LIB_DEP=""
Z_LIB="-lz"
Z_LIB_UNPACK="$(NOOP)"
extra_scheme_config_args="${extra_scheme_config_args} ZLIB=-lz"
fi
fi
@ -563,6 +572,7 @@ if test "${enable_liblz4}" = "yes" ; then
else
LZ4_LIB_DEP=""
LZ4_LIB="-llz4"
LZ4_LIB_UNPACK="$(NOOP)"
extra_scheme_config_args="${extra_scheme_config_args} LZ4=-llz4"
fi
fi
@ -685,6 +695,7 @@ AC_SUBST(RANLIB)
AC_SUBST(STATIC_AR)
AC_SUBST(WINDRES)
AC_SUBST(ICP)
AC_SUBST(ICP_LIB)
AC_SUBST(STRIP_DEBUG)
AC_SUBST(STRIP_LIB_DEBUG)
AC_SUBST(RKTLINKER)
@ -707,8 +718,10 @@ AC_SUBST(MINGW)
AC_SUBST(NOT_MINGW)
AC_SUBST(Z_LIB_DEP)
AC_SUBST(Z_LIB)
AC_SUBST(Z_LIB_UNPACK)
AC_SUBST(LZ4_LIB_DEP)
AC_SUBST(LZ4_LIB)
AC_SUBST(LZ4_LIB_UNPACK)
AC_SUBST(CONFIGURE_RACKET_SO_COMPILE)
AC_SUBST(COMPRESS_COMP)
AC_SUBST(BOOT_COMPRESS_COMP)
@ -727,6 +740,7 @@ AC_SUBST(CROSS_COMPILE_TARGET_KIND)
AC_SUBST(RUN_RACKET)
AC_SUBST(INSTALL_SETUP_FLAGS)
AC_SUBST(INSTALL_SETUP_RACKET_FLAGS)
AC_SUBST(INSTALL_LIBS_ENABLE)
makefiles="Makefile"

View File

@ -48,8 +48,8 @@
(if (compress-enabled?)
;; zero byte stops a gzip-read sequence
#"\0"
;; #!eof encoding stops(!) a fasl-read sequence
#"\44\26\2\f6"))
;; A 127 byte teriminates a fasl-read sequence
#"\177"))
(define data
(bytes-append bstr1 terminator
bstr2 terminator

View File

@ -107,6 +107,45 @@ static long find_rktboot_section(char *me)
}
#endif
#if defined(OS_X) && !defined(RACKET_XONX)
# include <mach-o/dyld.h>
# define RACKET_USE_FRAMEWORK 1
static const char *get_framework_path() {
int i, c, len;
const char *s;
c = _dyld_image_count();
for (i = 0; i < c; i++) {
s = _dyld_get_image_name(i);
len = strlen(s);
if ((len > 7) && !strcmp("/Racket", s + len - 7)) {
char *s2;
s2 = strdup(s);
strcpy(s2 + len - 6, "boot");
return s2;
}
}
return "???";
}
static char *path_append(const char *p1, char *p2) {
int l1, l2;
char *s;
l1 = strlen(p1);
l2 = strlen(p2);
s = malloc(l1 + l2 + 2);
memcpy(s, p1, l1);
s[l1] = '/';
memcpy(s + l1 + 1, p2, l2);
s[l1+l2+1] = 0;
return s;
}
#endif
#if defined(__linux__)
# include <errno.h>
static char *get_self_path(char *exec_file)
@ -375,8 +414,13 @@ static int bytes_main(int argc, char **argv,
/* for Windows and X11 GUI modes */
int wm_is_gracket_or_x11_arg_count, char *gracket_guid_or_x11_args)
{
char *boot_exe, *exec_file = argv[0], *run_file = NULL;
int is_embedded = 1, pos1, pos2, pos3;
char *boot_exe;
char *exec_file = argv[0], *run_file = NULL;
char *boot1_path, *boot2_path, *boot3_path;
int boot1_offset, boot2_offset, boot3_offset;
#ifdef OS_X
int boot_images_in_exe = 1;
#endif
long boot_offset;
long segment_offset;
#ifdef WIN32
@ -392,6 +436,16 @@ static int bytes_main(int argc, char **argv,
argv++;
}
extract_built_in_arguments(&exec_file, &run_file, &argc, &argv);
if (!run_file)
run_file = exec_file;
segment_offset = get_segment_offset();
memcpy(&boot1_offset, boot_file_data + boot_file_offset, sizeof(boot1_offset));
memcpy(&boot2_offset, boot_file_data + boot_file_offset + 4, sizeof(boot2_offset));
memcpy(&boot3_offset, boot_file_data + boot_file_offset + 8, sizeof(boot3_offset));
#ifdef WIN32
parse_embedded_dlls();
register_embedded_dll_hooks();
@ -412,39 +466,70 @@ static int bytes_main(int argc, char **argv,
boot_exe = get_self_path(exec_file);
#endif
extract_built_in_arguments(&exec_file, &run_file, &argc, &argv);
if (!run_file)
run_file = exec_file;
segment_offset = get_segment_offset();
memcpy(&pos1, boot_file_data + boot_file_offset, sizeof(pos1));
memcpy(&pos2, boot_file_data + boot_file_offset + 4, sizeof(pos2));
memcpy(&pos3, boot_file_data + boot_file_offset + 8, sizeof(pos2));
#ifdef ELF_FIND_BOOT_SECTION
boot_offset = find_boot_section(boot_exe);
#elif OS_X
#elif defined(OS_X)
boot_offset = find_rktboot_section(boot_exe);
if (!boot_offset) is_embedded = 0;
if (!boot_offset) boot_images_in_exe = 0;
#elif WIN32
boot_offset = find_resource_offset(dll_path, 259, boot_rsrc_offset);
#else
boot_offset = 0;
#endif
pos1 += boot_offset;
pos2 += boot_offset;
pos3 += boot_offset;
boot1_offset += boot_offset;
boot2_offset += boot_offset;
boot3_offset += boot_offset;
boot1_path = boot2_path = boot3_path = boot_exe;
#if defined(OS_X) && !defined(RACKET_XONX)
if (!boot_images_in_exe) {
const char *fw_path = get_framework_path();
boot1_path = path_append(fw_path, "petite.boot");
boot2_path = path_append(fw_path, "scheme.boot");
boot3_path = path_append(fw_path, "racket.boot");
boot1_offset = boot2_offset = boot3_offset = 0;
}
#endif
{
racket_boot_arguments_t ba;
memset(&ba, 0, sizeof(ba));
ba.boot1_path = boot1_path;
ba.boot1_offset = boot1_offset;
ba.boot2_path = boot2_path;
ba.boot2_offset = boot2_offset;
ba.boot3_path = boot3_path;
ba.boot3_offset = boot3_offset;
ba.argc = argc;
ba.argv = argv;
ba.exec_file = exec_file;
ba.run_file = run_file;
ba.collects_dir = extract_coldir();
ba.config_dir = extract_configdir();
ba.dll_dir = extract_dlldir();
ba.cs_compiled_subdir = CS_COMPILED_SUBDIR;
ba.segment_offset = segment_offset;
ba.dll_open = embedded_dll_open;
ba.dll_find_object = scheme_dll_find_object;
ba.dll_close = embedded_dll_close;
ba.exit_after = 1;
ba.is_gui = RACKET_IS_GUI;
ba.wm_is_gracket_or_x11_arg_count = wm_is_gracket_or_x11_arg_count;
ba.gracket_guid_or_x11_args = gracket_guid_or_x11_args;
racket_boot(&ba);
}
racket_boot(argc, argv, exec_file, run_file,
boot_exe, segment_offset,
extract_coldir(), extract_configdir(), extract_dlldir(),
is_embedded, pos1, pos2, pos3,
CS_COMPILED_SUBDIR, RACKET_IS_GUI,
wm_is_gracket_or_x11_arg_count, gracket_guid_or_x11_args,
embedded_dll_open, scheme_dll_find_object, embedded_dll_close);
return 0;
}

View File

@ -46,7 +46,8 @@
linklet-performance-report!
current-compile-target-machine
compile-target-machine?
add-cross-compiler!))
add-cross-compiler!
primitive-lookup))
(linklet-performance-init!)
(unless omit-debugging?
@ -78,18 +79,20 @@
(define (getenv-bytes str)
(environment-variables-ref (current-environment-variables) (string->utf8 str)))
(define builtin-argc 9)
(define builtin-argc 10)
(seq
(unless (>= (length the-command-line-arguments) builtin-argc)
(error 'racket (string-append
"expected `exec-file`, `run-file`, `collects`, and `etc` paths"
"expected `embedded-interactive-mode?`,"
" `exec-file`, `run-file`, `collects`, and `etc` paths"
" plus `segment-offset`, `cs-compiled-subdir?`, `is-gui?`,"
" `wm-is-gracket-or-x11-arg-count`, and `gracket-guid-or-x11-args`"
" to start")))
(set-exec-file! (->path (list-ref the-command-line-arguments/maybe-bytes 0)))
(set-run-file! (->path (list-ref the-command-line-arguments/maybe-bytes 1))))
(set-exec-file! (->path (list-ref the-command-line-arguments/maybe-bytes 1)))
(set-run-file! (->path (list-ref the-command-line-arguments/maybe-bytes 2))))
(define embedded-interactive-mode? (string=? "true" (list-ref the-command-line-arguments 0)))
(define-values (init-collects-dir collects-pre-extra)
(let ([s (list-ref the-command-line-arguments/maybe-bytes 2)])
(let ([s (list-ref the-command-line-arguments/maybe-bytes 3)])
(cond
[(or (equal? s "")
(equal? s '#vu8()))
@ -99,12 +102,12 @@
(values (->path (car s))
(map ->path (cdr s))))])))
(define init-config-dir (->path (or (getenv-bytes "PLTCONFIGDIR")
(list-ref the-command-line-arguments/maybe-bytes 3))))
(define segment-offset (#%string->number (list-ref the-command-line-arguments 4)))
(define cs-compiled-subdir? (string=? "true" (list-ref the-command-line-arguments 5)))
(define gracket? (string=? "true" (list-ref the-command-line-arguments 6)))
(define wm-is-gracket-or-x11-arg-count (string->number (list-ref the-command-line-arguments 7)))
(define gracket-guid-or-x11-args (list-ref the-command-line-arguments 8))
(list-ref the-command-line-arguments/maybe-bytes 4))))
(define segment-offset (#%string->number (list-ref the-command-line-arguments 5)))
(define cs-compiled-subdir? (string=? "true" (list-ref the-command-line-arguments 6)))
(define gracket? (string=? "true" (list-ref the-command-line-arguments 7)))
(define wm-is-gracket-or-x11-arg-count (string->number (list-ref the-command-line-arguments 8)))
(define gracket-guid-or-x11-args (list-ref the-command-line-arguments 9))
(seq
(when (foreign-entry? "racket_exit")
@ -146,7 +149,7 @@
#f
(machine-type)))
(define compiled-roots-path-list-string (getenv "PLTCOMPILEDROOTS"))
(define embedded-load-in-places #f)
(define embedded-load-in-places '())
(define (see saw . args)
(let loop ([saw saw] [args args])
@ -410,7 +413,7 @@
(set! loads
(cons
(lambda ()
(set! embedded-load-in-places (list n m))
(set! embedded-load-in-places (cons (list #f n m #f) embedded-load-in-places))
(embedded-load n m #f #t)
(embedded-load m p #f #f))
loads)))
@ -760,6 +763,44 @@
(version))])
(path-list-string->path-list s (list (build-path 'same)))))))
;; Called when Racket is embedded in a larger application:
(define (register-embedded-entry-info! escape)
(let ([resume-k #f]) ;; to get back to Racket thread; expects a thunk
((call/cc ;; Scheme-level `call/cc` to escape Racket's thread-engine loop
(lambda (init-resume-k)
(set! resume-k init-resume-k)
(set-top-level-value!
'embedded-racket-entry-info
;; A vector of specific functions:
(vector
;; Resume the main Racket thread to apply `proc` to `args`,
;; and return a list of result values; no exception handling
;; or other such protections
(lambda (proc args)
(call/cc ;; Scheme-level `call/cc` to escape engine loop
(lambda (entry-point-k)
(resume-k
(lambda ()
(let-values ([vals (apply proc args)])
((call/cc
(lambda (latest-resume-k)
(set! resume-k init-resume-k)
(entry-point-k vals))))))))))
;; Functions that are useful to apply and that
;; provide access to everything else:
primitive-lookup
eval
dynamic-require
namespace-require
;; bstr as #f => use path, start, and end
;; path as #f => find executable
;; end as #f => use file size
(lambda (path start end bstr as-predefined?)
(embedded-load start end bstr as-predefined? path)
(when as-predefined?
(set! embedded-load-in-places (cons (list path start end bstr) embedded-load-in-places))))))
(escape))))))
(set-make-place-ports+fds! make-place-ports+fds)
(set-start-place!
@ -768,9 +809,11 @@
(regexp-place-init!)
(expander-place-init!)
(initialize-place!)
(when embedded-load-in-places
(let-values ([(n m) (apply values embedded-load-in-places)])
(embedded-load n m #f #t)))
(let loop ([l (reverse embedded-load-in-places)])
(unless (null? l)
(let-values ([(path n m bstr) (apply values (car l))])
(embedded-load n m bstr #t path))
(loop (cdr l))))
(lambda ()
(let ([f (dynamic-require mod sym)])
(f pch)))))
@ -789,45 +832,53 @@
(when version?
(display (banner)))
(call-in-main-thread
(lambda ()
(initialize-place!)
(when init-library
(namespace-require+ init-library))
(call-with-continuation-prompt
(call/cc ; Chez Scheme's `call/cc`, used here to escape from the Racket-thread engine loop
(lambda (entry-point-k)
(call-in-main-thread
(lambda ()
(for-each (lambda (ld) (ld))
(reverse loads)))
(default-continuation-prompt-tag)
;; If any load escapes, then set the exit value and
;; stop running loads (but maybe continue with the REPL)
(lambda (proc)
(set! exit-value 1)
;; Let the actual default handler report an arity mismatch, etc.
(initialize-place!)
(when init-library
(namespace-require+ init-library))
(call-with-continuation-prompt
(lambda () (abort-current-continuation (default-continuation-prompt-tag) proc)))))
(lambda ()
(for-each (lambda (ld) (ld))
(reverse loads)))
(default-continuation-prompt-tag)
;; If any load escapes, then set the exit value and
;; stop running loads (but maybe continue with the REPL)
(lambda (proc)
(set! exit-value 1)
;; Let the actual default handler report an arity mismatch, etc.
(call-with-continuation-prompt
(lambda () (abort-current-continuation (default-continuation-prompt-tag) proc)))))
(when repl?
(set! exit-value 0)
(when repl-init?
(let ([m (get-repl-init-filename)])
(when m
(call-with-continuation-prompt
(lambda () (dynamic-require m 0))
(default-continuation-prompt-tag)
(lambda args (set! exit-value 1))))))
(|#%app| (if text-repl?
(dynamic-require 'racket/base 'read-eval-print-loop)
(dynamic-require 'racket/gui/init 'graphical-read-eval-print-loop)))
(when text-repl?
(newline)))
(when repl?
(set! exit-value 0)
(when repl-init?
(let ([m (get-repl-init-filename)])
(when m
(call-with-continuation-prompt
(lambda () (dynamic-require m 0))
(default-continuation-prompt-tag)
(lambda args (set! exit-value 1))))))
(|#%app| (if text-repl?
(dynamic-require 'racket/base 'read-eval-print-loop)
(dynamic-require 'racket/gui/init 'graphical-read-eval-print-loop)))
(when text-repl?
(newline)))
(when yield?
(|#%app| (executable-yield-handler) exit-value))
(when yield?
(|#%app| (executable-yield-handler) exit-value))
(exit exit-value))))
(cond
[embedded-interactive-mode?
(register-embedded-entry-info!
(lambda ()
(entry-point-k exit-value)))]
[else
(exit exit-value)]))))))
(define the-command-line-arguments
(or (and (top-level-bound? 'bytes-command-line-arguments)

View File

@ -41,14 +41,21 @@
((current-load/use-compiled) p #f))
;; used for the -k command-line argument:
(define (embedded-load start end str as-predefined?)
(let* ([s (if str
str
(let* ([sp (find-system-path 'exec-file)]
[exe (find-executable-path sp #f)]
[start (or (string->number start) 0)]
[end (or (string->number end) 0)])
(with-input-from-file exe
(define (embedded-load start end bstr as-predefined? [in-path #f])
(let* ([s (if bstr
bstr
(let* ([path (cond
[(bytes? in-path) (bytes->path in-path)]
[(string? in-path) in-path]
[else
(find-executable-path (find-system-path 'exec-file) #f)])]
[start (if (string? start)
(or (string->number start) 0)
start)]
[end (if (string? end)
(or (string->number end) 0)
(or end (file-size path)))])
(with-input-from-file path
(lambda ()
(file-position (current-input-port) start)
(read-bytes (max 0 (- end start)))))))]

View File

@ -50,7 +50,7 @@ m4_include(../ac/natipkg_arg.m4)
AC_ARG_ENABLE(shared, [ --enable-shared create shared libraries (ok, but not recommended)])
AC_ARG_ENABLE(dynlib, [ --enable-dynlib same as --enable-shared])
AC_ARG_ENABLE(lt, [ --enable-lt=<prog> use <prog> instead of libtool; disable to use bundled], LIBTOOLPROG="$enableval", enable_lt=default)
AC_ARG_ENABLE(libs, [ --enable-libs install static libraries (enabled by default for Unix)])
m4_include(../ac/instlib_arg.m4)
AC_ARG_ENABLE(libffi, [ --enable-libffi use installed libffi (enabled by default for Unix)], , enable_libffi=default)
@ -231,8 +231,7 @@ show_explicitly_enabled "${enable_jitframe}" "jitframe"
show_explicitly_enabled "${enable_noopt}" "No-optimization" "Note that this mode is intended only for debugging purposes"
m4_include(../ac/strip_show.m4)
show_explicitly_enabled "${enable_libs}" "Installation of static libraries (if any)"
show_explicitly_disabled "${enable_libs}" "Installation of static libraries (if any)"
m4_include(../ac/instlib.m4)
m4_include(../ac/sdk_show.m4)
@ -299,8 +298,6 @@ MAIN_VARIANT=3m
INSTALL_SETUP_FLAGS=
INSTALL_SETUP_RACKET_FLAGS=
INSTALL_LIBS_ENABLE=no-install
use_flag_pthread=yes
use_flag_posix_pthread=no
mzrt_needs_pthread=yes
@ -539,11 +536,6 @@ if test "${enable_jit}" = "yes" ; then
check_for_mprotect=yes
fi
if test "${enable_libs}" != "no" ; then
# Canceled for some platforms below:
INSTALL_LIBS_ENABLE=install
fi
############## platform tests ################
# for flags we don't want to use in config tests:

View File

@ -253,17 +253,20 @@
"petite"
"scheme")
(system*! (find-exe)
"-O" "info@compiler/cm"
"-l-" "setup" boot-mode "../setup-go.rkt" "..//build/compiled"
"ignored" "../build/ignored.d"
"../cs/c/embed-boot.rkt"
"++exe" "../build/raw_racketcs.exe" (format "../../Racket~a.exe" cs-suffix)
"++exe" "../build/raw_gracketcs.exe" (format "../../lib/GRacket~a.exe" cs-suffix)
"../build/raw_libracketcs.dll" "../../lib/libracketcsxxxxxxx.dll"
"../build/petite-v.boot"
"../build/scheme-v.boot"
"../build/racket-v.boot")
(define (bootstrap-racket! . args)
(apply system*! (find-exe)
"-O" "info@compiler/cm"
"-l-" "setup" boot-mode "../setup-go.rkt" "../build/compiled"
"ignored" "../build/ignored.d"
args))
(bootstrap-racket! "../cs/c/embed-boot.rkt"
"++exe" "../build/raw_racketcs.exe" (format "../../Racket~a.exe" cs-suffix)
"++exe" "../build/raw_gracketcs.exe" (format "../../lib/GRacket~a.exe" cs-suffix)
"../build/raw_libracketcs.dll" "../../lib/libracketcsxxxxxxx.dll"
"../build/petite-v.boot"
"../build/scheme-v.boot"
"../build/racket-v.boot")
(system*! "mt"
"-manifest" "racket/racket.manifest"
@ -325,3 +328,15 @@
(format "../../lib/system~a.rktd" cs-suffix)
machine
"machine")
(bootstrap-racket! "../cs/c/add-terminator.rkt"
"../build/petite-v.boot"
"../../lib/petite.boot")
(bootstrap-racket! "../cs/c/add-terminator.rkt"
"../build/scheme-v.boot"
"../../lib/scheme.boot")
(bootstrap-racket! "../cs/c/add-terminator.rkt"
"../build/racket-v.boot"
"../../lib/racket.boot")