Compiling lisp game to C with ECL
This commit is contained in:
parent
90e927ced2
commit
b8770d04e3
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,3 +8,4 @@
|
|||||||
/boids_main
|
/boids_main
|
||||||
/libboids.so
|
/libboids.so
|
||||||
/game.fasl
|
/game.fasl
|
||||||
|
/writeimage
|
||||||
|
270
ECL-INSTALL
Normal file
270
ECL-INSTALL
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
You will find detailed installation instructions in the ECL manual
|
||||||
|
https://common-lisp.net/project/ecl/static/manual/Building-ECL.html
|
||||||
|
If you do not have access to the online version, follow the following recipies.
|
||||||
|
|
||||||
|
* Unix and similar platforms.
|
||||||
|
1. Type
|
||||||
|
./configure --help
|
||||||
|
to get a list of the flags with which ECL can be configured.
|
||||||
|
2. Enter
|
||||||
|
./configure ...
|
||||||
|
where "..." is the set of flags you have chosen.
|
||||||
|
3. Use "make" followed by "make install" to build and install ECL.
|
||||||
|
|
||||||
|
* Windows with Visual Studio C++ 2008
|
||||||
|
1. Open the Visual Studio x86 or x64 native tools command prompt
|
||||||
|
2. Enter the msvc directory
|
||||||
|
3. Read the file Makefile to find the configuration options. They
|
||||||
|
typically have the form ECL_UNICODE=1, ECL_THREADS=1, etc
|
||||||
|
4. Enter
|
||||||
|
nmake ...
|
||||||
|
followed by zero or more of those options
|
||||||
|
5. Use "nmake install" to create a directory called "package" with ECL in it.
|
||||||
|
6. Move that directory wherever you need.
|
||||||
|
|
||||||
|
* Cross-compile for the android platform (from the UNIX machine)
|
||||||
|
1. Build the host ECL
|
||||||
|
#+BEGIN_SRC shell-script
|
||||||
|
# C99 complex numbers are not fully supported on Android
|
||||||
|
./configure ABI=32 CFLAGS="-m32 -g -O2" LDFLAGS="-m32 -g -O2"\
|
||||||
|
--prefix=`pwd`/ecl-android-host --disable-c99complex
|
||||||
|
make -j9
|
||||||
|
make install
|
||||||
|
rm -r build
|
||||||
|
export ECL_TO_RUN=`pwd`/ecl-android-host/bin/ecl
|
||||||
|
#+END_SRC
|
||||||
|
2. Configure the toolchain (requires android-ndk version 15 or higher, known to work with version 17c)
|
||||||
|
and export the necessary paths:
|
||||||
|
#+BEGIN_SRC shell-script
|
||||||
|
export NDK_PATH=/opt/android-ndk
|
||||||
|
export ANDROID_API=23
|
||||||
|
export TOOLCHAIN_PATH=`pwd`/android-toolchain
|
||||||
|
${NDK_PATH}/build/tools/make_standalone_toolchain.py --arch arm --install-dir ${TOOLCHAIN_PATH} --api ${ANDROID_API}
|
||||||
|
export SYSROOT=${TOOLCHAIN_PATH}/sysroot
|
||||||
|
export PATH=${TOOLCHAIN_PATH}/bin:$PATH
|
||||||
|
#+END_SRC
|
||||||
|
3. Build and install the target library
|
||||||
|
#+BEGIN_SRC shell-script
|
||||||
|
# boehm GC is not compatible with ld.gold linker, force use of ld.bfd
|
||||||
|
export LDFLAGS="--sysroot=${SYSROOT} -D__ANDROID_API__=${ANDROID_API} -fuse-ld=bfd"
|
||||||
|
export CPPFLAGS="--sysroot=${SYSROOT} -D__ANDROID_API__=${ANDROID_API} -isystem ${SYSROOT}/usr/include/arm-linux-androideabi"
|
||||||
|
export CC=arm-linux-androideabi-clang
|
||||||
|
./configure --host=arm-linux-androideabi \
|
||||||
|
--prefix=`pwd`/ecl-android \
|
||||||
|
--disable-c99complex \
|
||||||
|
--with-cross-config=`pwd`/src/util/android-arm.cross_config
|
||||||
|
make -j9
|
||||||
|
make install
|
||||||
|
#+END_SRC
|
||||||
|
4. Library and assets in the ecl-android directory are ready to run on
|
||||||
|
the Android system.
|
||||||
|
|
||||||
|
** Building ecl-android on Darwin (OSX)
|
||||||
|
If your host platform is darwin, then the host compiler should be
|
||||||
|
built with the Apple's GCC (not the GCC from Macports). Using the
|
||||||
|
MacPort command:
|
||||||
|
#+BEGIN_SRC shell-script
|
||||||
|
sudo port select --set gcc none
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Hint provided by Pascal J. Bourguignon.
|
||||||
|
|
||||||
|
|
||||||
|
* Cross-compile for the iOS platform (needs Xcode 11 or higher)
|
||||||
|
1. Build the host ECL
|
||||||
|
#+BEGIN_SRC shell-script
|
||||||
|
./configure CFLAGS="-DECL_C_COMPATIBLE_VARIADIC_DISPATCH" --prefix=`pwd`/ecl-iOS-host --disable-c99complex
|
||||||
|
make -j9
|
||||||
|
make install
|
||||||
|
rm -r build
|
||||||
|
export ECL_TO_RUN=`pwd`/ecl-iOS-host/bin/ecl
|
||||||
|
#+END_SRC
|
||||||
|
2. Configure the toolchain
|
||||||
|
#+BEGIN_SRC shell-script
|
||||||
|
export IOS_VERSION_MIN="8.0"
|
||||||
|
export IOS_SDK_DIR="`xcode-select --print-path`/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/"
|
||||||
|
|
||||||
|
export CC="clang"
|
||||||
|
export CXX="clang++"
|
||||||
|
|
||||||
|
export CFLAGS="-arch arm64 -miphoneos-version-min=${IOS_VERSION_MIN} -isysroot ${IOS_SDK_DIR}"
|
||||||
|
export CFLAGS="$CFLAGS -pipe -Wno-trigraphs -Wreturn-type -Wunused-variable"
|
||||||
|
export CFLAGS="$CFLAGS -fpascal-strings -fasm-blocks -fmessage-length=0 -fvisibility=hidden"
|
||||||
|
export CFLAGS="$CFLAGS -O2 -DNO_ASM"
|
||||||
|
|
||||||
|
export LD="ld"
|
||||||
|
export LDFLAGS="-arch arm64 -pipe -std=c99 -gdwarf-2 -isysroot ${IOS_SDK_DIR}"
|
||||||
|
export LIBS="-framework Foundation"
|
||||||
|
#+END_SRC
|
||||||
|
3. Build and install the target library
|
||||||
|
#+BEGIN_SRC shell-script
|
||||||
|
export CFLAGS="$CFLAGS -DGC_DISABLE_INCREMENTAL -DECL_RWLOCK"
|
||||||
|
export CXXFLAGS="$CFLAGS"
|
||||||
|
./configure --host=aarch64-apple-darwin \
|
||||||
|
--prefix=`pwd`/ecl-iOS \
|
||||||
|
--disable-c99complex \
|
||||||
|
--disable-shared \
|
||||||
|
--with-cross-config=`pwd`/src/util/iOS-arm64.cross_config
|
||||||
|
make -j9
|
||||||
|
make install
|
||||||
|
#+END_SRC
|
||||||
|
4. Library and assets in the ecl-iOS directory are ready to run on
|
||||||
|
the iOS system.
|
||||||
|
|
||||||
|
* Cross-compile for the WASM platform (via emscripten)
|
||||||
|
|
||||||
|
Emscripten target is a little fickle so keep in mind that:
|
||||||
|
|
||||||
|
- shared libraries are supported but come with some drawbacks (e.g.
|
||||||
|
increased code size if used together with -sASYNCIFY), therefore the
|
||||||
|
build instructions default to disable-shared
|
||||||
|
|
||||||
|
- disable-tcp is needed, because accept can't be found (lack of
|
||||||
|
-lsockets or something in this spirit?)
|
||||||
|
|
||||||
|
- select for interactive streams does not work, because reading operations are
|
||||||
|
blocking (as they should be!), so there is no EOF returned -- clear-input will
|
||||||
|
hang without a proper file-cnt
|
||||||
|
|
||||||
|
- to build emscripten you need to use their SDK that provides the toolchain, and
|
||||||
|
set the environment variable EMSDK_PATH
|
||||||
|
|
||||||
|
- for the garbage collector to be able to identify roots on the stack,
|
||||||
|
you need to pass the -sBINARYEN_EXTRA_PASSES=--spill-pointers option
|
||||||
|
to the linker for all of your code that might store pointer on the
|
||||||
|
stack (for instance when embedding ECL)
|
||||||
|
|
||||||
|
- the optimization level -O0 is used because higher optimization
|
||||||
|
levels seem to interfere with the binaryen options needed to get the
|
||||||
|
garbage collector to work correctly and tend slow down the program
|
||||||
|
(might be worth experimenting with the optimization options)
|
||||||
|
|
||||||
|
1. Build the host ECL
|
||||||
|
|
||||||
|
#+begin_src shell-script
|
||||||
|
./configure ABI=32 CFLAGS="-m32 -g -O2 -DECL_C_COMPATIBLE_VARIADIC_DISPATCH" LDFLAGS="-m32 -g -O2" \
|
||||||
|
--prefix=`pwd`/ecl-emscripten-host --disable-threads
|
||||||
|
|
||||||
|
make -j16 && make install
|
||||||
|
rm -rf build/
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
2. Configure the toolchain
|
||||||
|
|
||||||
|
Install the Emscripten SDK using the official instructions:
|
||||||
|
|
||||||
|
https://emscripten.org/docs/getting_started/downloads.html
|
||||||
|
|
||||||
|
These build instructions were tested against ~emsdk 3.1.41~. If things doesn't
|
||||||
|
work try that version instead of ~latest~.
|
||||||
|
|
||||||
|
After that activate the toolchain and configure build flags:
|
||||||
|
|
||||||
|
#+begin_src shell-script
|
||||||
|
source ${EMSDK_PATH}/emsdk_env.sh
|
||||||
|
export ECL_TO_RUN=`pwd`/ecl-emscripten-host/bin/ecl
|
||||||
|
# You may customize various emscripten flags here, i.e:
|
||||||
|
# export LDFLAGS="-sASYNCIFY=1"
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
3. Build the core environment and install it
|
||||||
|
|
||||||
|
#+begin_src shell-script
|
||||||
|
emconfigure ./configure \
|
||||||
|
--host=wasm32-unknown-emscripten \
|
||||||
|
--build=x86_64-pc-linux-gnu \
|
||||||
|
--with-cross-config=`pwd`/src/util/wasm32-unknown-emscripten.cross_config \
|
||||||
|
--prefix=`pwd`/ecl-emscripten \
|
||||||
|
--disable-shared \
|
||||||
|
--with-tcp=no \
|
||||||
|
--with-cmp=no
|
||||||
|
|
||||||
|
emmake make && emmake make install
|
||||||
|
|
||||||
|
# some files need to be copied manually
|
||||||
|
cp build/bin/ecl.js build/bin/ecl.wasm ecl-emscripten/
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
4. ECL may be hosted on a web page. Assuming that you have quicklisp installed:
|
||||||
|
|
||||||
|
#+begin_src shell-script
|
||||||
|
export WEBSERVER=`pwd`/src/util/webserver.lisp
|
||||||
|
pushd ecl-emscripten/
|
||||||
|
lisp --load $WEBSERVER
|
||||||
|
# After the server is loaded run:
|
||||||
|
# firefox localhost:8888/ecl.html
|
||||||
|
popd
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
If the output does not show on the webpage then open the javascript console.
|
||||||
|
This is a default html website produced by emscripten.
|
||||||
|
|
||||||
|
5. Build an external program linked against libecl.a
|
||||||
|
|
||||||
|
The default stack size proposed by emscripten is 64KB. This is too little for
|
||||||
|
ECL, so when you build a program that is linked against libecl.a, then it is
|
||||||
|
imoprtant to specify a different size. For example:
|
||||||
|
|
||||||
|
#+begin_src shell-script
|
||||||
|
emcc program.c -sSTACK_SIZE=1048576 lib/*.a -I./include -o program.o
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
* Build using Cosmopolitan toolchain (experimental)
|
||||||
|
|
||||||
|
Binaries built with cosmopolitan toolchain can be executed on numerous platforms
|
||||||
|
without changes. An inititial ECL port has been completed.
|
||||||
|
|
||||||
|
1. Download cosmopolitan toolchain and activate it
|
||||||
|
|
||||||
|
Instructions for downloading the toolchain are available in its [[https://github.com/jart/cosmopolitan/blob/master/README.md ][repository]].
|
||||||
|
For example:
|
||||||
|
|
||||||
|
#+begin_src sh
|
||||||
|
mkdir cosmocc
|
||||||
|
pushd cosmocc
|
||||||
|
curl -o https://cosmo.zip/pub/cosmocc/cosmocc.zip
|
||||||
|
unzip *zip
|
||||||
|
bin/make --version # sanity test
|
||||||
|
export PATH="`pwd`/bin:${PATH}"
|
||||||
|
export CC=x86_64-unknown-cosmo-cc
|
||||||
|
export CXX=x86_64-unknown-cosmo-c++
|
||||||
|
popd
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
2. Build ECL
|
||||||
|
|
||||||
|
#+begin_src sh
|
||||||
|
./configure --disable-shared --prefix=/tmp/cosmo-cl
|
||||||
|
make -j15
|
||||||
|
make install
|
||||||
|
# make check
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
- The platform is reported in *FEATURES* as :COSMO and as :LINUX
|
||||||
|
- make check will fail because libc segfaults when using fenv.h
|
||||||
|
|
||||||
|
This platform is not upstreamed yet in libgc, so it may be necessary to add the
|
||||||
|
following code to the file gcconfig.h:
|
||||||
|
|
||||||
|
#+begin_src c
|
||||||
|
# if defined(__COSMOPOLITAN__)
|
||||||
|
# if defined(__x86_64__)
|
||||||
|
# define mach_type_known
|
||||||
|
# define X86_64
|
||||||
|
# define LINUX // optional?
|
||||||
|
# elif defined(__aarch64__)
|
||||||
|
# define mach_type_known
|
||||||
|
# define AARCH64
|
||||||
|
# define LINUX // optional?
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
For details see: https://github.com/jart/cosmopolitan/issues/939.
|
||||||
|
|
||||||
|
Cosmopolitan condition variables can't be used with mutexes that are not
|
||||||
|
initialized as PTHREAD_MUTEX_NORMAL. That means that recursive locks and
|
||||||
|
deterministic deadlock detection are not supported on this platform.
|
||||||
|
|
||||||
|
Moreover floating point exception handling is not working and it is libc issue
|
||||||
|
that manifests itself with segfaults.
|
12
README.org
Normal file
12
README.org
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
#+begin_src shell
|
||||||
|
|
||||||
|
gcc -lecl ecl_test.c -o test-game libtest-game.a
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_src lisp
|
||||||
|
(uiop:getcwd)
|
||||||
|
(uiop:chdir "Development/tinyswords/")
|
||||||
|
(compile-file "game.lisp" :system-p t)
|
||||||
|
(c:build-static-library "test-game" :lisp-files '("game.o") :init-name "game")
|
||||||
|
#+end_src
|
15
ecl_test.c
Normal file
15
ecl_test.c
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include <ecl/ecl.h>
|
||||||
|
|
||||||
|
extern void game(cl_object cblock);
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
/* setup the lisp runtime */
|
||||||
|
cl_boot(argc, argv);
|
||||||
|
/* call the init function via read_VV */
|
||||||
|
read_VV(OBJNULL, game);
|
||||||
|
/* ... */
|
||||||
|
/* shutdown the lisp runtime */
|
||||||
|
cl_shutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
(load "~/quicklisp/setup.lisp")
|
||||||
|
|
||||||
(eval-when (:compile-toplevel :load-toplevel :execute)
|
(eval-when (:compile-toplevel :load-toplevel :execute)
|
||||||
(ql:quickload :cl-raylib))
|
(ql:quickload :cl-raylib))
|
||||||
|
|
||||||
@ -66,7 +68,7 @@
|
|||||||
:white)
|
:white)
|
||||||
(rl:draw-fps 10 5))
|
(rl:draw-fps 10 5))
|
||||||
|
|
||||||
(defun main ()
|
(defun game ()
|
||||||
(let* ((screen-width 900)
|
(let* ((screen-width 900)
|
||||||
(screen-height 500))
|
(screen-height 500))
|
||||||
(rl:with-window (screen-width screen-height "RTS")
|
(rl:with-window (screen-width screen-height "RTS")
|
||||||
@ -80,4 +82,4 @@
|
|||||||
(loop for value being the hash-values of (game-state-textures *game-state*)
|
(loop for value being the hash-values of (game-state-textures *game-state*)
|
||||||
do (rl:unload-texture value)))))
|
do (rl:unload-texture value)))))
|
||||||
|
|
||||||
(main)
|
(game)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user