Main
Posted on 4/16/14

p I used Gambit Scheme to build my iOS game Cloud Breaker. Here are the steps you can take to get Gambit up and running on your iPhone or iPad. I assume you have the latest version of Xcode installed (I have Xcode 5.1.1).

p I originally used jlongster’s post to get started. It has a lot of good information as well, but some things have changed since that post was written. My goal with this guide is to give a more up-to-date overview (until everything changes again).

h2 Overview

p Gambit Scheme is a compiler that translates Scheme code into C code. The generated C code can then run on just about anything (iPhones and iPads in this case), but we also need to link that C code to a build of the Gambit Runtime, libgambc.a, which handles garbage collection, standard library calls, etc.

p Xcode is happy to compile the generated C code just like any other source files, but we’ll build the runtime library separately so that we don’t have to rebuild it after a project clean, and so that we can link it to multiple independent projects.

h2 Step 1: Get the Gambit Source

pre $ git clone https://github.com/feeley/gambit.git

h2 Step 2: Build the Gambit Compiler for Your System

p We want a build of Gambit for our system to get gsc and gsi, the compiler and the interpreter, so that we can compile our Scheme code into something the device can run. It’s also handy to be able to interpet the Scheme code on our system, which is faster for building and testing than pushing to the device every time.

p Gambit compiles faster and generates a faster runtime with recent gcc versions (versus the gcc that Apple provides which uses LLVM). So before building Gambit, I installed gcc 4.8 via homebrew.

pre. $ brew install gcc48

p Now let’s build Gambit using our new compiler. pre. $ cd gambit $ CC=/usr/local/bin/gcc-4.8 ./configure –enable-single-host $ make -j4 from-scratch $ make check $ sudo make install $ gsc -v pre.output. v4.7.2 20140411185957 x86_64-apple-darwin13.1.0 “./configure ‘–enable-single-host’ ‘CC=/usr/local/bin/gcc-4.8’” p The -j4 option tells make to use 4 simultaenous jobs, which is the number of CPU cores on my system. Modify this as necessary.

p After this we have a Gambit compiler (gsc) and intepreter/REPL (gsi).

pre. $ gsi pre.output. Gambit v4.7.2 pre. > (+ 1 2) pre.output. 3 pre. > ,q

h2 Step 3: Build the Gambit Runtime Library

p You need to build the Gambit runtime library for each architecture you want to run on. I do armv7 (iPhone 3GS through iPhone 4S), armv7s (iPhone 5 and later), and i386 (for the Simulator). We’ll pack them all into one library for convenience sake.

p I believe that the newer devices will run armv7 code, but the armv7s code should run faster.

p I recommend using my build script instead of the current one in the main repo. There are some changes for compatibility with the latest Xcode versions and some other tweaks that should save some build time. pre $ wget https://raw.github.com/asivitz/gambit/master/misc/build-gambit-iOS -O misc/build-gambit-iOS

pre. $ cd misc $ sh build-gambit-iOS –no-update -j 4 p The script builds the two arm architectures and the i386 arch, and packs it into a library. This takes a long time (over a couple hours on my laptop). The reason it’s slow is that we have to use Apple’s LLVM-based compilers to build the ARM runtimes.

p The –no-update flag stops the script from updating the downloaded distribution to HEAD, and instead builds from the latest release version.

p -j 4 specifies the number of make jobs, again, equal to my CPU cores.

p Anyway, once it’s done, check it to make sure it has all your desired architectures: pre. $ file gambit-iOS/current/lib/libgambc.a pre.output. libgambc.a: Mach-O universal binary with 3 architectures libgambc.a (for architecture i386):i386 current ar archive random library libgambc.a (for architecture armv7):armv7 current ar archive random library libgambc.a (for architecture armv7s):armv7s current ar archive random library

h2 Step 4: Link the Gambit Library to Your App

p In your Xcode project, go to your build settings and find the Search Paths section.

p Add a library search path… pre Library Search Paths PATH_TO_GAMBIT/misc/gambit-iOS/current/lib p And a user header search path… pre User Header Search Paths PATH_TO_GAMBIT/misc/gambit-iOS/current/include

p In the Linking section, add a linker flag for the gambit library. pre Other Linker Flags -lgambc

p Since we’re embedding the gambit runtime into our app, we need to tell gambit not to generate a main function. So we need to define _LIBRARY. In my Xcode 5.1, you do that in the Apple LLVM 5.1 - Preprocessing section. pre Preprocessor Macros Not Used In Procompiled Headers _LIBRARY

h2 Step 5: Write some Scheme Code

p Here’s a good starting point. Save it in a file called “init.scm” in your project’s source folder. pre. ; pure Scheme call (println “We’re here in Scheme land!”)

; Scheme function callable from C (c-define (c-add-five num) (int) void “add_five” "" (+ 5 num))

; C function callable from Scheme (define nslog (c-lambda (char-string) void " NSString * str = [NSString stringWithCString:___arg1 encoding:NSASCIIStringEncoding]; NSLog(@"Logging: %@", str); "))

(nslog “Hello Obj-C World!”)

h2 Step 6: Compile the Scheme code into C

p Although we want to be able to run this code on ARM, the Gambit compiler produces the exact same C code no matter what platform the compiler is run on. So we’ll use the normal system compiler.

p Compile each of your Scheme files into .m files. (Right now you just have one.) pre. # From your project’s source folder, where you put init.scm $ gsc -c -o init.m init.scm $ ls init* pre.output. init.m init.scm

p Create a link file out of your new .m file(s). pre. $ gsc -link -o init_.m init.m $ ls init* pre.output. init.m init_.m init.scm

p Go into Xcode. Add init.m and init_.m to your project. Make sure they are added to your target, or else they won’t get compiled in.

h2 Step 7: Execute the Gambit Runtime in Your App

p Add this code to your ViewController.m class above its implementation. Be sure to match the ___VERSION define with your Gambit scheme version.

pre.prettyprint. /* These function defs match what is declared in your scheme code. */ void add_five(int num);

/* This version number must match what’s written in include/gambit.h.in in * the gambit source distribution. 407002 is the version number for Gambit 4.7.2 */ #define ___VERSION 407002 #include “gambit.h”

/ Define SCHEME_LIBRARY_LINKER as the name of the Scheme library * prefixed with “____20_” and suffixed with "__". This is the * function that initializes the Scheme library. * I use init because my main scheme file is called init.scm */

#define SCHEME_LIBRARY_LINKER ____20_init__

_BEGIN_C_LINKAGE extern _mod_or_lnk SCHEME_LIBRARY_LINKER (___global_state_struct*); ___END_C_LINKAGE

void gambit_setup() { / Setup the Scheme library by calling "_setup" with appropriate * parameters. The call to "_setup_params_reset" sets all * parameters to their default setting. */

  int debug_settings = ___DEBUG_SETTINGS_INITIAL;

  ___setup_params_struct setup_params;

  ___setup_params_reset (&setup_params);

  setup_params.version        = ___VERSION;
  setup_params.linker         = SCHEME_LIBRARY_LINKER;
  setup_params.debug_settings = debug_settings;

  ___setup (&setup_params);

}

void gambit_cleanup() { ___cleanup (); }

p Add gambit_cleanup() to your dealloc method. [super dealloc] omitted because I assume you’re using ARC.

pre. - (void)dealloc { … gambit_cleanup(); }

p Add gambit_setup() to your viewDidLoad method

pre. - (void)viewDidLoad { [super viewDidLoad]; … gambit_setup(); }

h2 Step 8: ??? p We’re ready to go! Build and run the app and celebrate! p Or curse the errors you’re getting and ping me on twitter. h2 Step 9: Profit!