1#lang scribble/doc 2@(require "utils.rkt") 3 4@bc-title[#:tag "Writing Racket Extensions"]{Writing Racket Extensions} 5 6@section-index["extending Racket"] 7 8As noted in @secref["embedding-and-extending"], writing Racket code 9and using the @seclink["top" #:doc '(lib 10"scribblings/foreign/foreign.scrbl")]{foreign-function interface} is 11usually a better option than writing an extension to Racket, but 12Racket also supports C-implemented extensions that plug more directly 13into the run-time system. 14(Racket CS does not have a similar extension interface.) 15 16The process of creating an extension for Racket 3m or Racket CGC (see 17@secref["CGC versus 3m"]) is essentially the same, but the process for 183m is most easily understood as a variant of the process for CGC. 19 20@section{CGC Extensions} 21 22 23To write a C/C++-based extension for Racket CGC, follow these 24steps: 25 26@itemize[ 27 28 @item{@index['("header files")]{For} each C/C++ file that uses 29 Racket library functions, @cpp{#include} the file 30 @as-index{@filepath{escheme.h}}. 31 32 This file is distributed with the Racket software in an 33 @filepath{include} directory, but if @|mzc| is used to 34 compile, this path is found automatically.} 35 36 37 @item{Define the C function @cppi{scheme_initialize}, which takes a 38 @cpp{Scheme_Env*} namespace (see @secref["im:env"]) and returns a 39 @cpp{Scheme_Object*} Racket value. 40 41 This initialization function can install new global primitive 42 procedures or other values into the namespace, or it can simply 43 return a Racket value. The initialization function is called when the 44 extension is loaded with @racket[load-extension] the first time in a 45 given @|tech-place|; the return value from @cpp{scheme_initialize} is used 46 as the return value for @racket[load-extension]. The namespace 47 provided to @cpp{scheme_initialize} is the current namespace when 48 @racket[load-extension] is called.} 49 50 51 @item{Define the C function @cppi{scheme_reload}, which has the same 52 arguments and return type as @cpp{scheme_initialize}. 53 54 This function is called if @racket[load-extension] is called a second 55 time (or more times) for an extension in a given @|tech-place|. Like 56 @cpp{scheme_initialize}, the return value from this function is the 57 return value for @racket[load-extension].} 58 59 60 @item{Define the C function @cppi{scheme_module_name}, which takes 61 no arguments and returns a @cpp{Scheme_Object*} value, either a 62 symbol or @cpp{scheme_false}. 63 64 The function should return a symbol when the effect of calling 65 @cpp{scheme_initialize} and @cpp{scheme_reload} is only to declare 66 a module with the returned name. This function is called when the 67 extension is loaded to satisfy a @racket[require] declaration. 68 69 The @cpp{scheme_module_name} function may be called before 70 @cpp{scheme_initialize} and @cpp{scheme_reload}, after those 71 functions, or both before and after, depending on how the extension 72 is loaded and re-loaded.} 73 74 75 @item{Compile the extension C/C++ files to create platform-specific 76 object files. 77 78 The @as-index{@|mzc|} compiler, which is distributed with Racket, 79 compiles plain C files when the @as-index{@DFlag{cc}} flag is 80 specified. More precisely, @|mzc| does not compile the files itself, 81 but it locates a C compiler on the system and launches it with the 82 appropriate compilation flags. If the platform is a relatively 83 standard Unix system, a Windows system with either Microsoft's C 84 compiler or @exec{gcc} in the path, or a Mac OS system with Apple's 85 developer tools installed, then using @|mzc| is typically easier than 86 working with the C compiler directly. Use the @as-index{@DFlag{cgc}} 87 flag to indicate that the build is for use with Racket CGC.} 88 89 90 @item{Link the extension C/C++ files with 91 @as-index{@filepath{mzdyn.o}} (Unix, Mac OS) or 92 @as-index{@filepath{mzdyn.obj}} (Windows) to create a shared object. The 93 resulting shared object should use the extension @filepath{.so} (Unix), 94 @filepath{.dll} (Windows), or @filepath{.dylib} (Mac OS). 95 96 The @filepath{mzdyn} object file is distributed in the installation's 97 @filepath{lib} directory. For Windows, the object file is in a 98 compiler-specific sub-directory of @filepath{racket\lib}. 99 100 The @|mzc| compiler links object files into an extension when the 101 @as-index{@DFlag{ld}} flag is specified, automatically locating 102 @filepath{mzdyn}. Again, use the @DFlag{cgc} flag with @|mzc|.} 103 104 @item{Load the shared object within Racket using 105 @racket[(load-extension _path)], where @racket[_path] is the name of 106 the extension file generated in the previous step. 107 108 Alternately, if the extension defines a module (i.e., 109 @cpp{scheme_module_name} returns a symbol), then place the shared 110 object in a special directory with a special name, so that it is 111 detected by the module loader when @racket[require] is used. The 112 special directory is a platform-specific path that can be obtained by 113 evaluating @racket[(build-path "compiled" "native" 114 (system-library-subpath))]; see @racket[load/use-compiled] for more 115 information. For example, if the shared object's name is 116 @filepath{example_rkt.dll}, then @racket[(require "example.rkt")] will 117 be redirected to @filepath{example_rkt.dll} if the latter is placed in 118 the sub-directory @racket[(build-path "compiled" "native" 119 (system-library-subpath))] and if @filepath{example.rkt} does not 120 exist or has an earlier timestamp. 121 122 Note that @racket[(load-extension _path)] within a @racket[module] 123 does @italic{not} introduce the extension's definitions into the 124 module, because @racket[load-extension] is a run-time operation. To 125 introduce an extension's bindings into a module, make sure that the 126 extension defines a module, put the extension in the 127 platform-specific location as described above, and use 128 @racket[require].} 129 130] 131 132@index['("allocation")]{@bold{IMPORTANT:}} With Racket CGC, Racket 133values are garbage collected using a conservative garbage collector, 134so pointers to Racket objects can be kept in registers, stack 135variables, or structures allocated with @cppi{scheme_malloc}. However, 136static variables that contain pointers to collectable memory must be 137registered using @cppi{scheme_register_extension_global} (see 138@secref["im:memoryalloc"]); even then, such static variables must be 139thread-local (in the OS-thread sense) to work with multiple 140@|tech-place|s (see @secref["places"]). 141 142As an example, the following C code defines an extension that returns 143@racket["hello world"] when it is loaded: 144 145@verbatim[#:indent 2]{ 146 #include "escheme.h" 147 Scheme_Object *scheme_initialize(Scheme_Env *env) { 148 return scheme_make_utf8_string("hello world"); 149 } 150 Scheme_Object *scheme_reload(Scheme_Env *env) { 151 return scheme_initialize(env); /* Nothing special for reload */ 152 } 153 Scheme_Object *scheme_module_name() { 154 return scheme_false; 155 } 156} 157 158Assuming that this code is in the file @filepath{hw.c}, the extension 159is compiled on Unix with the following two commands: 160 161@commandline{raco ctool --cgc --cc hw.c} 162@commandline{raco ctool --cgc --ld hw.so hw.o} 163 164(Note that the @DFlag{cgc}, @DFlag{cc}, and @DFlag{ld} flags are each 165prefixed by two dashes, not one.) 166 167The @filepath{collects/mzscheme/examples} directory in the Racket 168distribution contains additional examples. 169 170@section{3m Extensions} 171 172To build an extension to work with Racket 3m, the CGC instructions 173must be extended as follows: 174 175@itemize[ 176 177 @item{Adjust code to cooperate with the garbage collector as 178 described in @secref["im:3m"]. Using @|mzc| with the 179 @as-index{@DFlag{xform}} might convert your code to implement part of 180 the conversion, as described in @secref["im:3m:mzc"].} 181 182 @item{In either your source in the in compiler command line, 183 @cpp{#define} @cpp{MZ_PRECISE_GC} before including 184 @filepath{escheme.h}. When using @|mzc| with the @DFlag{cc} and 185 @as-index{@DFlag{3m}} flags, @cpp{MZ_PRECISE_GC} is automatically 186 defined.} 187 188 @item{Link with @as-index{@filepath{mzdyn3m.o}} (Unix, Mac OS) or 189 @as-index{@filepath{mzdyn3m.obj}} (Windows) to create a shared 190 object. When using @|mzc|, use the @DFlag{ld} and @DFlag{3m} flags 191 to link to these libraries.} 192 193] 194 195For a relatively simple extension @filepath{hw.c}, the extension is 196compiled on Unix for 3m with the following three commands: 197 198@commandline{raco ctool --xform hw.c} 199@commandline{raco ctool --3m --cc hw.3m.c} 200@commandline{raco ctool --3m --ld hw.so hw_3m.o} 201 202Some examples in @filepath{collects/mzscheme/examples} work with 203Racket 3m in this way. A few examples are manually instrumented, in 204which case the @DFlag{xform} step should be skipped. 205 206@section{Declaring a Module in an Extension} 207 208To create an extension that behaves as a module, return a symbol from 209@cpp{scheme_module_name}, and have @cpp{scheme_initialize} and 210@cpp{scheme_reload} declare a module using @cpp{scheme_primitive_module}. 211 212For example, the following extension implements a module named 213@racket[hi] that exports a binding @racket[greeting]: 214 215@verbatim[#:indent 2]{ 216 #include "escheme.h" 217 218 Scheme_Object *scheme_initialize(Scheme_Env *env) { 219 Scheme_Env *mod_env; 220 mod_env = scheme_primitive_module(scheme_intern_symbol("hi"), 221 env); 222 scheme_add_global("greeting", 223 scheme_make_utf8_string("hello"), 224 mod_env); 225 scheme_finish_primitive_module(mod_env); 226 return scheme_void; 227 } 228 229 Scheme_Object *scheme_reload(Scheme_Env *env) { 230 return scheme_initialize(env); /* Nothing special for reload */ 231 } 232 233 Scheme_Object *scheme_module_name() { 234 return scheme_intern_symbol("hi"); 235 } 236} 237 238This extension could be compiled for 3m on i386 Linux, for 239example, using the following sequence of @exec{mzc} commands: 240 241@commandline{raco ctool --xform hi.c} 242@commandline{raco ctool --3m --cc hi.3m.c} 243@commandline{mkdir -p compiled/native/i386-linux/3m} 244@commandline{raco ctool --3m --ld compiled/native/i386-linux/3m/hi_rkt.so hi_3m.o} 245 246The resulting module can be loaded with 247 248@racketblock[(require "hi.rkt")] 249 250@; ---------------------------------------------------------------------- 251 252