The build system uses autotools (autoconf/automake) so a simple build would be:
autoreconf --install (to install the autotools support files)
./configure (to configure the system)
make (to compile everything)
make install (to install everything)
For further details read INSTALL generated by running autoreconf.
The only unusual aspect here is that Oaklisp is written in Oaklisp, i.e., is self-hosting. This causes a bootstrap issue.
There are only two files required to actually run Oaklisp:
file | purpose |
---|---|
oaklisp | the executable / emulator / virtual machine |
oakworld.bin | pre-built oaklisp (read in by the executable) |
The file oakworld.bin contains the pre-built, pre-compiled Oaklisp system: the parts that are written in Oaklisp itself. This means you need an existing oakworld.bin in order to run "oaklisp" in order to build a new oakworld.bin. Oaklisp cannot run without an existing oakworld.bin file. You can however build the executable "oaklisp" (which is written in C) without a working Oaklisp. So all you really need is oakworld.bin. The executable "oaklisp" will look for oakworld.bin at a compile-time-specified location, but can be instructed to look elsewhere.
The ./configure script will look for oakworld.bin in a few places, and use it in the build. If it cannot find oakworld.bin automatically, you can give an explicit location
./configure --with-world=/complete/path/to/oakworld.bin
See ./configure --help for details of these options.
There are other ways to pass this information to the compiled emulator at build time, e.g.,
make OAKWORLD=$(pwd)/prebuilt/src/world/oakworld.bin
or
make OAKWORLD=/usr/local/lib/oaklisp/oakworld.bin
If you already have Oaklisp installed, you can just use that:
make OAK=/usr/local/bin/oaklisp
Oaklisp is sensitive to the endianness of the CPU http://en.wikipedia.org/wiki/Endianness. There are oakworld.bin files included in the git branch containing prebuilt files, or available for download as a separate files, for both little-endian and big-endian architectures. The subdirectory is el32/ for little-endian and eb32/ for big-endian.
However Oaklisp can easily be made to cross build an oakworld.bin for a big-endian or little-endian architecture, given an opposite-endian prebuilt world as well as access to systems of both endianities:
On machine with endianity of prebuilt world:
$ autoreconf --install
$ ./configure
$ make -C src
Copy tree to other-endian machine, and there:
$ ./configure
$ make -C src/emulator clean
$ make -C src/emulator
$ rm src/world/*.bin
$ touch src/world/system-version.oa
$ touch src/world/new.cold
$ make -C src/world
You should now be the proud possessor of an opposite-endian src/emulator/oaklisp and src/world/oakworld.bin.
The executable "oaklisp" currently only works in 32-bit mode. It can be built as a 32-bit executable on a 64-bit machines, assuming that a 32-bit-pointer executable model is available and that an appropriate toolchain exists to target that model: compiler and development libraries.
A 32-bit memory model is available for on most amd64-based systems, sometimes as an optional package. E.g., on Debian or Debian-derived distributions like Ubuntu, the correct package is gcc-multilib. Compiling using GCC or clang on an x86_64 aka amd64 architecture machine, one should use the -m32 which builds an i386 architecture executable. A similar situation holds with many 64-bit architectures. The appropriate GCC options will be added automatically by ./configure when necessary on many 64-bit architectures, thanks to an exhaustive list of appropriate options for all applicable Debian architectures provided by James Cowgill [email protected] in discussion of his bug report https://bugs.debian.org/780353/. These options also seem to work, in many cases, with the clang compiler. New architectures can be added to configure.ac as clauses in the AS_CASE([${host_cpu}],...) statement.
On x86_64 the -mx32 flag, which uses the x86_64 instruction set with 32-bit pointers, also works provided that x32 kernel support is enabled and the x32 development files are available. Initial benchmarks showed that an src/emulator/oaklisp built with -m32 was slightly faster than one built with -mx32.
It would be great if someone were to do a 64-bit port. In order to port the system to 64-bit pointers and therefore 64-bit cells, a number of 32-bit-cell assumptions need to be dealt with.
-
The bytecode engine has 16-bit instructions, packed two/cell. This becomes four/cell when cells go 64-bit. This requires changes to the bytecode interpreter in src/emulator/ as well as to the assembler which may need to insert extra padding before inline literal references. The compiler proper might not need to be touched at all.
-
Characters in strings would become packed seven/cell instead of three/cell, but that's pretty minor, and they could certainly be left three/cell until the system is otherwise converted. This impacts src/emulator/ where Oaklisp strings are converted to C strings for, e.g., passing to fopen(), and the string packing/unpacking code in src/world/.
-
The cold world builder (generates new.cold) might need attention.
-
Bignums are stored in base 10,000, the largest power of ten less than (sqrt (expt 2 (- 32 3))), where the 3 bits subtracted there are for the tag (2 bits) and the sign (1 bit). These would become base 1,000,000,000, the largest power of ten less than (sqrt (expt 2 (- 64 3))). Also the arithmetic overflow code in src/emulator/ would need to be looked at. But, bignums could wait until the system is otherwise up and running.
This would all be facilitated by the fact that a working 32-bit oaklisp could be used during the bootstrap, and due to bignums its word size should not affect its ability to generate 64-bit bootstrap files.
Currently the system is started by executing the emulator, which loads a binary world. Different varieties of the system can be obtained by booting different worlds, and by passing various options like the desired locale. Another option would be to make a new world format which can be directly executed. This could be accomplished in one of three ways. (1) The binary world could have a magic number recognized by the OS kernel, allowing it to be executed in a special idiosyncratic fashion. (2) The binary world could start with
#! /usr/bin/env oaklisp
or
#! /usr/bin/oaklisp
and the emulator would recognize this situation and load the apppropriate world. (3) The world could be an ELF executable which could be executed in native mode, and the binary data for the world itself could live in its own ELF segment or even be bound to a symbol in the ELF file.