Guile on the NGW100


You are here: Andrew Ho > Software > NGW100 > Packages > Guile

Guile is a GNU implementation of the Scheme programming language, which is designed to be easily embeddable and extensible in C. It also conveniently includes bindings for Unix functions like file and socket I/O, making it a good general purpose scripting language. Among Scheme interpreters, its footprint and speed are in the middle of the road.

This page documents some hacks I have had to apply to build Guile for the NGW100. For information on setting up the buildroot based cross-compilation environment, see my page on buildroot for NGW100 on CentOS 5.3; and for other packages, see my page on building packages for the NGW100.

For the examples on this page, I use $BUILDROOT to represent the buildroot top-level directory, which is the one with the top level makefile; $AVR32 to represent $BUILDROOT/build_avr32/staging_dir, which contains the actual cross-compilation tools; and $NGW100 to represent $BUILDROOT/project_build_avr32/atngw100/root, which contains the actual root filesystem for the NGW100 target.

I built the most recent versions available at the time of build: Guile 1.8.7, Libtool 2.2, and GMP 4.3.1.

Prerequisites

The Guile build process requires GNU libtool and the GMP bignum library. It is sufficient to cross compile these and have the static libraries available during the Guile build time; you do not need to install them separately on the NGW100, and I did not figure out how to make these available as DLLs.

Building libtool

TODO: add details.

CC=$AVR32/bin/avr32-linux-gcc \
LDFLAGS='-L$AVR32/lib' \
CPPFLAGS='-I$AVR32/include' \
CPP=$AVR32/bin/avr32-linux-cpp \
CXX=$AVR32/bin/avr32-linux-g++ \
CXXCPP=$AVR32/bin/avr32-linux-cpp \
./configure --host=avr --build=avr-unknown-none \
--enable-shared --enable-ltdl-install

Install it in a temporary location:

make install prefix=/var/tmp/guile-support

Building libgmp

TODO: add details.

CC=$AVR32/bin/avr32-linux-gcc \
LDFLAGS='-L$AVR32/lib' \
CPPFLAGS='-I$AVR32/include' \
CPP=$AVR32/bin/avr32-linux-cpp \
CXX=$AVR32/bin/avr32-linux-g++ \
CXXCPP=$AVR32/bin/avr32-linux-cpp \
./configure --host=avr --build=avr-unknown-none \
--enable-shared

Install it in a temporary location:

make install prefix=/var/tmp/guile-support

Building Guile

TODO: add detailed notes. I had to build libtool and libgmp (although these did not need to be installed onto the NGW100, and were only needed during building). Then, I had to perform some manual configure hacking, edit config.h after configure, create a fake library that exports an empty __cxa_atexit() function, and manually run one of the build steps.

Modify configure

Disable some configure tests which fail, apparently spuriously:

@@ -12299,6 +12299,7 @@
       conftest$ac_exeext conftest.$ac_ext
 LIBS=$ac_check_lib_save_LIBS
 fi
+cat >> /dev/null << end_libltdl_check
 { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_ltdl_lt_dlinit" >&5
 $as_echo "$ac_cv_lib_ltdl_lt_dlinit" >&6; }
 if test "x$ac_cv_lib_ltdl_lt_dlinit" = x""yes; then
@@ -12313,6 +12314,7 @@
 $as_echo "$as_me: error: libltdl not found.  See README." >&2;}
    { (exit 1); exit 1; }; }
 fi
+end_libltdl_check
 
 if test "${ac_cv_header_ltdl_h+set}" = set; then
   { $as_echo "$as_me:$LINENO: checking for ltdl.h" >&5
@@ -35053,6 +35055,7 @@
 
 if test "$with_threads" = pthreads; then
 
+cat >> /dev/null << end_pthread_attr_getstack_check_part1
 { $as_echo "$as_me:$LINENO: checking whether pthread_attr_getstack works for the main thread" >&5
 $as_echo_n "checking whether pthread_attr_getstack works for the main thread... " >&6; }
 old_CFLAGS="$CFLAGS"
@@ -35121,12 +35124,14 @@
   ac_status=$?
   $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
   (exit $ac_status); }; }; then
+end_pthread_attr_getstack_check_part1
   works=yes
 
 cat >>confdefs.h <<\_ACEOF
 #define PTHREAD_ATTR_GETSTACK_WORKS 1
 _ACEOF
 
+cat >> /dev/null << end_pthread_attr_getstack_check_part2
 else
   $as_echo "$as_me: program exited with status $ac_status" >&5
 $as_echo "$as_me: failed program was:" >&5
@@ -35138,6 +35143,7 @@
 rm -rf conftest.dSYM
 rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
 fi
+end_pthread_attr_getstack_check_part2
 
 
 CFLAGS="$old_CFLAGS"

Modify config.h

Manually disable a couple library functions which had unresolved symbol errors when run on the NGW100 with only stock libraries:

@@ -708,7 +708,7 @@
 #define HAVE_TM_ZONE 1
 
 /* Define to 1 if you have the `trunc' function. */
-#define HAVE_TRUNC 1
+#define HAVE_TRUNC 0
 
 /* Define to 1 if you have the `truncate' function. */
 #define HAVE_TRUNCATE 1
@@ -739,7 +739,7 @@
 #define HAVE_UNSETENV 1
 
 /* Define to 1 if csqrt is bug-free */
-#define HAVE_USABLE_CSQRT 1
+#define HAVE_USABLE_CSQRT 0
 
 /* Define to 1 if you have the `usleep' function. */
 #define HAVE_USLEEP 1

TODO: add details on error messages.

guile: can't resolve symbol '__cxa_atexit'
guile: can't resolve symbol 'trunc'
Segmentation fault

Disable __cxa_atexit()

TODO: add background, explanation, and links.

Even with a --disable-__cxa_atexit configure option, the generated guile binary still dies on the NGW100 with a "can't resolve symbol '__cxa_atexit'" error. The solution is to stub it out with a dummy function.

Create a file disable__cxa_atexit.c in the libguile subdirectory, with the following line in it:

void __cxa_atexit(void (*arg1)(void*), void* arg2, void* arg3) {}

Create an object file using the cross compiler:

$AVR32/bin/avr32-linux-gcc -c disable__cxa_atexit.c

Build Guile

Run the configure script:

CC=$AVR32/bin/avr32-linux-gcc \
LDFLAGS='-L$AVR32/lib -L/var/tmp/guile-support/lib' \
CPPFLAGS='-I$AVR32/include -I/var/tmp/guile-support/include' \
CPP=$AVR32/bin/avr32-linux-cpp \
CXX=$AVR32/bin/avr32-linux-g++ \
CXXCPP=$AVR32/bin/avr32-linux-cpp \
./configure --host=avr --build=avr-unknown-none \
--disable-discouraged --disable-deprecated --disable-elisp \
--disable-__cxa_atexit

The make command dies on the link step when trying to create the guile binary. You have to change to the libguile subdirectory, and re-run the failing link step, adding the path to libltdl.a, the path to disable__cta_atexit.o, and an -lltdl library link flag.

TODO: add details, including error messages and command lines.

Install Standard Library

Guile will not start up (even for guile -h) without at least some basic ice-9 libraries, which you can point it at with the GUILE_LOAD_PATH environment variable, if necessary:

ERROR: In procedure primitive-load-path:
ERROR: Unable to find file "ice-9/boot-9.scm" in load path

To fix this, I simply installed Guile 1.8 on a regular Linux box, and just copied /usr/share/guile/1.8 to /usr/local/share/guile/1.8 on the NGW100, which happens to already be in the built-in %load-path list on the Guile built using the instructions on this page.

Build Verification

For a Guile "hello, world" equivalent, run guile to get to the interactive Guile read-eval-print loop:

(+ 1 1)

You should see the expected result of 2. Test that networking works by fetching a web page (this example is similar to one from the network socket examples from the Guile reference manual):

(use-modules (ice-9 rdelim))

(define (last-in-vector vec)
        (vector-ref vec (- (vector-length vec) 1)) )

(define (hostname->ip hostname)
        (car (map inet-ntoa (last-in-vector (gethostbyname hostname)))) )

(let ((s (socket PF_INET SOCK_STREAM 0)))
     (connect s AF_INET (inet-aton (hostname->ip "www.google.com")) 80)
     (display "GET / HTTP/1.0\r\n\r\n" s)
     (do ((line (read-line s) (read-line s)))
         ((eof-object? line))
         (display line)
         (newline) ))

If successful, you should see an HTTP/1.1 response, including headers, with an entity body with the Google front page HTML.



Andrew Ho (andrew@zeuscat.com)