Tuesday, April 24, 2018

Lightweight, R7RS Compliant and in palm of my hand - Compiling chibi-scheme on Android

I'm eyeing this Programming Praxis exercise as one to tackle next but there's a catch before I can start programming. TinyScheme, my current Scheme Interpreter of choice, doesn't have support for generating random numbers, and the exercise requires this.

I could hack something together using TinyScheme (and probably the Programming Praxis Standard Prelude). Or, I could do as John Cowen suggested, and try out a different implementation. He suggested Chibi-Scheme. Chibi Scheme is lightweight, but unlike TinyScheme, it's also quite modern. This means, among other things, it should give me easy access to the SRFI-27: Sources of Random Bits library. Longer term, it would be ideal to have access to R7RS features and other modern niceties.

I busted out my cell phone and hardware keyboard. I opened up Termux and went to work. Here's my attempt at getting chibi-scheme running on Android:

Let's be clever and build this on the SD card.

[localhost]{~}(1)$ cd storage/external-1/src

[localhost]{src}(1)$ git clone https://github.com/ashinn/chibi-scheme.git
Cloning into 'chibi-scheme'...
remote: Counting objects: 18101, done.
remote: Compressing objects:  ....
Receiving objects: 100% 
Resolving deltas:   0% 

[localhost]{src}(1)$ cd chibi-scheme

[localhost]{chibi-scheme}(1)$ ./configure
bash: ./configure: /bin/sh: bad interpreter: Permission denied

D'oh. OK, don't panic.

[localhost]{chibi-scheme}(1)$ sh configure
Autoconf is an evil piece bloatware encouraging cargo-cult programming.
Make, on the other hand, is a beautiful little prolog for the filesystem.
Just run 'make'.

Oooh, sassy software. I like it.

[localhost]{chibi-scheme}(1)$ make
The program 'make' is not installed. Install it by executing:
 pkg install make

No problem, I'll just do what Termux tells me to do...

[localhost]{chibi-scheme}(1)$ pkg install make
Building dependency tree...
Reading state information...
All packages are up to date.
The following packages were automatically installed and are no longer required:
  binutils libandroid-support-dev libffi libllvm ndk-stl ndk-sysroot
Use 'apt autoremove' to remove them.
The following NEW packages will be installed:
  make
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 76.5 kB of archives.
After this operation, 246 kB of additional disk space will be used.
Get:1 https://termux.net stable/main aarch64 make aarch64 4.2.1 [76.5 kB]
Preparing to unpack .../make_4.2.1_aarch64.deb ...



[localhost]{chibi-scheme}(1)$ make
echo '#define sexp_so_extension "'.so'"' > include/chibi/install.h
echo '#define sexp_default_module_path "'/data/data/com.termux/files/usr/share/chibi:/data/data/com.termux/files/usr/lib/chibi'"' >> include/chibi/install.h
echo '#define sexp_platform "'android'"' >> include/chibi/install.h
echo '#define sexp_version "'0.8.0'"' >> include/chibi/install.h
echo '#define sexp_release_name "'`cat RELEASE`'"' >> include/chibi/install.h
cc -c  -Iinclude  -Wall -g -g3 -O3  -o main.o main.c
make: cc: Command not found
make: *** [Makefile:149: main.o] Error 127

Whoops. No compiler. After attempts to install 'gcc' and search for a compiler
package in general I finally tripped over 'clang.' That will apparently install
a C compiler.

[localhost]{chibi-scheme}(1)$ pkg install clang
The following NEW packages will be installed:
  clang
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 12.9 MB of archives.
After this operation, 61.2 MB of additional disk space will be used.
Get:1 https://termux.net stable/main aarch64 clang aarch64 6.0.0-1 [12.9 MB]
Preparing to unpack .../clang_6.0.0-1_aarch64.deb ...

[localhost]{chibi-scheme}(1)$ make
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -o main.o main.c
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o gc.o gc.c


It's working! It's really working!

cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o sexp.o sexp.c
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o bignum.o bignum.c
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o gc_heap.o gc_heap.c
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o opcodes.o opcodes.c
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o vm.o vm.c
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o eval.o eval.c
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o simplify.o simplify.c
cc -fPIC -shared -Wl,-soname,libchibi-scheme.so.0 -o libchibi-scheme.so.0.8.0 gc.o sexp.o bignum.o gc_heap.o opcodes.o vm.o eval.o simplify.o    -ldl -lm
ln -sf libchibi-scheme.so.0.8.0 libchibi-scheme.so.0
ln: libchibi-scheme.so.0: Operation not permitted
make: *** [Makefile:162: libchibi-scheme.so.0] Error 1

And it broke. Ugh.  After a much debugging I realized that my issue
was permission based. Apparently Android doesn't allow you to perform
certain operations on your SD card. I moved the source tree to internal 
storage and tried again.

[localhost]{chibi-scheme}(1)$ cd ..
[localhost]{src}(1)$ mv chibi-scheme ~/

[localhost]{src}(1)$ cd

[localhost]{~}(1)$ cd chibi-scheme
[localhost]{chibi-scheme}(1)$ make
ln -sf libchibi-scheme.so.0.8.0 libchibi-scheme.so.0
ln -sf libchibi-scheme.so.0 libchibi-scheme.so

It's working again. Whooo!

cc -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o chibi-scheme main.o -L. -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/filesystem.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/filesystem.so lib/chibi/filesystem.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/weak.so lib/chibi/weak.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/heap-stats.so lib/chibi/heap-stats.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/disasm.so lib/chibi/disasm.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/ast.so lib/chibi/ast.c  -L. -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/emscripten.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/emscripten.so lib/chibi/emscripten.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/process.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/process.so lib/chibi/process.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/time.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/time.so lib/chibi/time.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/system.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/system.so lib/chibi/system.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/stty.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/stty.so lib/chibi/stty.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/net.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/net.so lib/chibi/net.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/io/io.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/io/io.so lib/chibi/io/io.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/optimize/rest.so lib/chibi/optimize/rest.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/optimize/profile.so lib/chibi/optimize/profile.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/crypto/crypto.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/crypto/crypto.so lib/chibi/crypto/crypto.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/27/rand.so lib/srfi/27/rand.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/151/bit.so lib/srfi/151/bit.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/39/param.so lib/srfi/39/param.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/69/hash.so lib/srfi/69/hash.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/95/qsort.so lib/srfi/95/qsort.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/98/env.so lib/srfi/98/env.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/srfi/144/math.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/144/math.so lib/srfi/144/math.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/scheme/time.so lib/scheme/time.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/18/threads.so lib/srfi/18/threads.c -L.  -lchibi-scheme
echo "# pkg-config" > chibi-scheme.pc
echo "prefix=/data/data/com.termux/files/usr" >> chibi-scheme.pc
echo "exec_prefix=\${prefix}" >> chibi-scheme.pc
echo "libdir=/data/data/com.termux/files/usr/lib" >> chibi-scheme.pc
echo "includedir=\${prefix}/include" >> chibi-scheme.pc
echo "version=0.8.0" >> chibi-scheme.pc
echo "" >> chibi-scheme.pc
cat chibi-scheme.pc.in >> chibi-scheme.pc
find lib/chibi/ -name \*.sld | \
 LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme tools/generate-install-meta.scm 0.8.0 > lib/.chibi.meta
find lib/srfi/ -name \*.sld | \
 LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme tools/generate-install-meta.scm 0.8.0 > lib/.srfi.meta
find lib/scheme/ -name \*.sld | \
 LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme tools/generate-install-meta.scm 0.8.0 > lib/.scheme.meta

Amazing - it built without issue. How about installing, will that work?

[localhost]{chibi-scheme}(1)$ make install
install -d /data/data/com.termux/files/usr/bin
install -m0755 chibi-scheme /data/data/com.termux/files/usr/bin/
install -m0755 tools/chibi-ffi /data/data/com.termux/files/usr/bin/
install -m0755 tools/chibi-doc /data/data/com.termux/files/usr/bin/
install -m0755 tools/snow-chibi /data/data/com.termux/files/usr/bin/
install -m0755 tools/snow-chibi.scm /data/data/com.termux/files/usr/bin/

many boring 'install' lines removed. You're welcome.

if type ldconfig >/dev/null 2>/dev/null; then ldconfig; fi
echo "Generating images"
Generating images
cd / && LD_LIBRARY_PATH="/data/data/com.termux/files/usr/lib:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH="/data/data/com.termux/files/usr/lib:" /data/data/com.termux/files/usr/bin/chibi-scheme -d /data/data/com.termux/files/usr/share/chibi/chibi.img
cd / && LD_LIBRARY_PATH="/data/data/com.termux/files/usr/lib:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH="/data/data/com.termux/files/usr/lib:" /data/data/com.termux/files/usr/bin/chibi-scheme -xscheme.red -d /data/data/com.termux/files/usr/share/chibi/red.img
cd / && LD_LIBRARY_PATH="/data/data/com.termux/files/usr/lib:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH="/data/data/com.termux/files/usr/lib:" /data/data/com.termux/files/usr/bin/chibi-scheme -mchibi.snow.commands -mchibi.snow.interface -mchibi.snow.package -mchibi.snow.utils -d /data/data/com.termux/files/usr/share/chibi/snow.img

Holy smokes, it's done. Let's see if it really worked.

[localhost]{chibi-scheme}(1)$ cd

[localhost]{~}(1)$ chibi-scheme
> (+ 1 2 3)
6

We have scheme! Let's try something a bit more advanced

> (import (srfi 27) (srfi 1))
> (random-integer 100)
56
> 

The R7RS library functionality is working and 
srfi's are installed by default. We have success!

And here are some screenshots to show this really was all executed on my Galaxy S9 Plus:

2 comments:

  1. On my blog, I discussed installation of Guile Scheme and Chez Scheme on an Android tablet. See HERE, HERE and HERE. I am delighted with my current setup.

    ReplyDelete
  2. Thanks for sharing -- chibi-scheme seems like a hit but I do appreciate the other options.

    GNUroot is amazing, but I found that Termux was just a tad bit more useful for me. The biggest issue I had with GNUroot was an android quirk: the only way I could get back to a GNUroot instance was through the notification menu. I couldn't link a keystroke to it. That always irked me.

    Termux also has some interesting paid addons, which are a fun way to support the author. You can add commands to your home screen, for example.

    Regardless, it's amazing the options you have for running bash and related tools on Android.

    ReplyDelete