diff --git a/clean.sh b/clean.sh index 677094d68..58be5e7b8 100755 --- a/clean.sh +++ b/clean.sh @@ -68,7 +68,8 @@ find . \( \ -name CVS -o \ -name .hg -o \ -name .git -o \ - -name .svn \) -type d -prune -o \ + -name .svn -o \ + -name android-libs \) -type d -prune -o \ \( \ \( -type l -path ./src/runtime/\* \) -o \ -name '*~' -o \ diff --git a/contrib/make-contrib.lisp b/contrib/make-contrib.lisp index 02be0fa47..e5cd74b45 100644 --- a/contrib/make-contrib.lisp +++ b/contrib/make-contrib.lisp @@ -12,6 +12,11 @@ (defun run-defs-to-lisp (inputs output) (flet ((invoke (string &rest args) + #+android + (when (string= string "RUN-C-COMPILER") + (format t "~a ~{~a~^ ~}~%" string args) + (sleep 5) ;; FIXME: should check if the file was compiled + (return-from invoke 0)) (apply (find-symbol string "SB-GROVEL") args))) (let ((c-file (merge-pathnames "runme.c" output)) (all-headers) diff --git a/doc/internals-notes/Android-build.txt b/doc/internals-notes/Android-build.txt new file mode 100644 index 000000000..3d43f5ee1 --- /dev/null +++ b/doc/internals-notes/Android-build.txt @@ -0,0 +1,59 @@ +Changes made to the source code: + * src/runtime/globals.h: + Linking the shared library was failing because symbols + current_control_frame_pointer and current_binding_stack_pointer + could not be found. + It seems that #ifdef lines were messed up in + commit 6793d7dd32d1fa48d2ee395e240e1b7ff857912e. + So I fixed them (as I could). + * Since it uses both :linux and :android flags, + #+(and linux (not android)) was used instead of #+linux + in two places: + - src/cold/build-order.lisp-expr: don't load linux-os.lisp. + - src/code/run-program.lisp: don't define software-version function. + * In src/runtime/android-os.h: include where bzero + function is defined. + * In src/runtime/android-os.c: include where strdup + function is defined. + * src/runtime/run-program.c: there is no getdtablesize function on + android, so use the alternative (with #if defined). + * New (empty) *-android-os.c and *-android-os.h files. + +Changes made to the build system: + * make-config.sh now recognises android specific flags. + --with-android enables the build + --android-api defines the android API level used. Defaults to 21, + that corresponds to the Android 5.0 (Lollipop). + Also controlled by an environment variable ANDROID_API. + --ndk sets the path to Android NDK. + Also controlled by an environment variable NDK. + * In the GNUMakefile adds $SOFLAGS variable. + It is used to pass -Wl,-soname,libsbcl.so when building for android. + (NDK crosscompiler doesn't set soname by default.) + * clean.sh now ignores the android-libs folder. + (clean.sh deletes all *.so files, but we want to be able to link + against precompiled libzstd for example.) + * ADB is used to determine the CPU architecture in make-config.sh when + building for android. + * In make-config.sh: output/ndk-config is created to set up NDK + specific variables (mostly the path to the C crosscompiler). + * In make-config.sh: when building for android, copy Config files + instead of creating symbolic links - adb push doesn't like them. + * When building for android, ADB is used to run C programs. + tools-for-build/android_run.sh defines shell function to do that. + It is used in the following scripts: + - tools-for-build/grovel-features.sh + - make-target-1.sh + - make-config.sh + * In make-contrib.lisp: there is no C compiler on the android device. + So when it needs to compile a C program, it just prints its name. + In the make-android.sh there is a wrapper that compiles the C + program on host and pushes it on the device when it is needed. + * make-android.sh is similar to make.sh, but takes care of copying + files to the android device through adb and dealing with the problem + of compiling C programs when building contribs. + * make-target-contrib-android.sh is similar to make-target-contrib.sh, + but it doesn't use GNU make, since it is not available on the + android device. + * run-sbcl.sh now sets LD_LIBRARY_PATH to android-libs when it exists. + * New Config.*-android files. diff --git a/make-android.sh b/make-android.sh new file mode 100755 index 000000000..8ef83c969 --- /dev/null +++ b/make-android.sh @@ -0,0 +1,63 @@ +#!/bin/sh +set -e + +build_started=`date` + +./make-config.sh "$@" --with-android --without-gcc-tls --check-host-lisp || exit $? + +. output/prefix.def +. output/build-config + +$SBCL_XC_HOST < tools-for-build/canonicalize-whitespace.lisp || exit 1 + +./make-host-1.sh +./make-target-1.sh +./make-host-2.sh + +adb push ./ /data/local/tmp/sbcl/ + +adb shell "cd /data/local/tmp/sbcl ; LD_LIBRARY_PATH=/data/local/tmp/sbcl/android-libs sh make-target-2.sh" + +# Hack needed to replace SB-GROVEL:RUN-C-COMPILER +compile_one() { + bin=temp-compile-from-android + adb pull /data/local/tmp/sbcl/contrib/asdf/$2 $bin.c + $CC $bin.c -o $bin + dest=$(echo $3 | sed 's/\r//g') # On older android line terminates with \r + adb push $bin /data/local/tmp/sbcl/contrib/asdf/$dest + echo "done" + rm $bin + rm $bin.c +} + +adb shell "cd /data/local/tmp/sbcl ; LD_LIBRARY_PATH=/data/local/tmp/sbcl/android-libs sh make-target-contrib-android.sh" | \ + while read line ; + do echo "$line" ; + echo $line | grep "RUN-C-COMPILER" | while read line ; do compile_one $line ; done ; + done + +adb pull /data/local/tmp/sbcl/obj +adb pull /data/local/tmp/sbcl/output + +./make-shared-library.sh + +NPASSED=`ls obj/sbcl-home/contrib/sb-*.fasl | wc -l` +echo +echo "The build seems to have finished successfully, including $NPASSED (out of $NCONTRIBS)" +echo "contributed modules. If you would like to run more extensive tests on" +echo "the new SBCL, you can try:" +echo +echo " cd ./tests && sh ./run-tests.sh" +echo +echo "To build documentation:" +echo +echo " cd ./doc/manual && make" +echo +echo "To install SBCL (more information in INSTALL):" +echo +echo " sh install.sh" + +build_finished=`date` +echo +echo "//build started: $build_started" +echo "//build finished: $build_finished" diff --git a/make-config.sh b/make-config.sh index 1485aaec6..0be42f451 100755 --- a/make-config.sh +++ b/make-config.sh @@ -48,6 +48,10 @@ SBCL_CONTRIB_BLOCKLIST=${SBCL_CONTRIB_BLOCKLIST:-""} perform_host_lisp_check=no fancy=false some_options=false +android=false +if [ -z $ANDROID_API ]; then + ANDROID_API=21 +fi for option do optarg_ok=true @@ -92,6 +96,10 @@ do ;; --with) WITH_FEATURES="$WITH_FEATURES :$optarg" + if [ "$optarg" = "android" ] + then + android=true + fi ;; --without) WITHOUT_FEATURES="$WITHOUT_FEATURES :$optarg" @@ -100,6 +108,12 @@ do SBCL_CONTRIB_BLOCKLIST="$SBCL_CONTRIB_BLOCKLIST $optarg" ;; esac ;; + --android-api=) + $optarg_ok && ANDROID_API=$optarg + ;; + --ndk=) + $optarg_ok && NDK=$optarg + ;; --fancy) WITH_FEATURES="$WITH_FEATURES $FANCY_FEATURES" # Lower down we add :sb-thread for platforms where it can be built. @@ -277,6 +291,7 @@ fi if [ -n "$SBCL_TARGET_LOCATION" ]; then echo "SBCL_TARGET_LOCATION=\"$SBCL_TARGET_LOCATION\"; export SBCL_TARGET_LOCATION" >> output/build-config fi +echo "android=$android; export android" >> output/build-config # And now, sorting out the per-target dependencies... @@ -337,6 +352,9 @@ link_or_copy() { else cp -r "$1" "$2" fi + elif $android ; then + # adb push doesn't like symlinks on unrooted devices. + cp -r "$1" "$2" else ln -s "$1" "$2" fi @@ -366,7 +384,14 @@ echo //ensuring the existence of output/ directory if [ ! -d output ] ; then mkdir output; fi echo //guessing default target CPU architecture from host architecture -case `uname -m` in +if $android +then + uname_arch=`adb shell uname -m` +else + uname_arch=`uname -m` +fi + +case $uname_arch in *86) guessed_sbcl_arch=x86 ;; i86pc) guessed_sbcl_arch=x86 ;; *x86_64) guessed_sbcl_arch=x86-64 ;; @@ -430,6 +455,32 @@ if [ "$sbcl_arch" = "" ] ; then echo "can't guess target SBCL architecture, please specify --arch=" exit 1 fi + +if $android +then + case $sbcl_arch in + arm64) TARGET_TAG=aarch64-linux-android ;; + arm) TARGET_TAG=armv7a-linux-androideabi + echo "Unsupported configuration" + exit 1 + ;; + x86) TARGET_TAG=i686-linux-android + echo "Unsupported configuration" + exit 1 + ;; + x86-64) TARGET_TAG=x86_64-linux-android ;; + esac + HOST_TAG=$sbcl_os-x86_64 + TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/$HOST_TAG + export CC=$TOOLCHAIN/bin/$TARGET_TAG$ANDROID_API-clang + echo "CC=$CC; export CC" >> output/build-config + echo "NDK=$NDK" > output/ndk-config + echo "HOST_TAG=$HOST_TAG" >> output/ndk-config + echo "TARGET_TAG=$TARGET_TAG" >> output/ndk-config + echo "TOOLCHAIN=$TOOLCHAIN" >> output/ndk-config + echo "ANDROID_API=$ANDROID_API" >> output/ndk-config +fi + if $fancy then # If --fancy, enable threads on platforms where they can be built. @@ -526,10 +577,16 @@ case "$sbcl_os" in printf ' :largefile' >> $ltf ;; esac - - link_or_copy Config.$sbcl_arch-linux Config - link_or_copy $sbcl_arch-linux-os.h target-arch-os.h - link_or_copy linux-os.h target-os.h + if $android + then + link_or_copy Config.$sbcl_arch-android Config + link_or_copy $sbcl_arch-android-os.h target-arch-os.h + link_or_copy android-os.h target-os.h + else + link_or_copy Config.$sbcl_arch-linux Config + link_or_copy $sbcl_arch-linux-os.h target-arch-os.h + link_or_copy linux-os.h target-os.h + fi ;; haiku) printf ' :unix :haiku :elf :int4-breakpoints' >> $ltf @@ -582,6 +639,9 @@ case "$sbcl_os" in if [ $sbcl_arch = "arm64" ]; then printf ' :darwin-jit :gcc-tls' >> $ltf fi + if $android; then + echo "Android build is unsupported on darwin" + fi link_or_copy $sbcl_arch-darwin-os.h target-arch-os.h link_or_copy bsd-os.h target-os.h link_or_copy Config.$sbcl_arch-darwin Config @@ -633,6 +693,11 @@ cd "$original_dir" # (define-feature :c-stack-grows-downwards-not-upwards (features) # (member :x86 features)) +if $android +then + . tools-for-build/android_run.sh +fi + case "$sbcl_arch" in x86) if [ "$sbcl_os" = "darwin" ]; then @@ -652,8 +717,15 @@ case "$sbcl_arch" in x86-64) printf ' :sb-simd-pack :sb-simd-pack-256 :avx2' >> $ltf # not mandatory - if ! $GNUMAKE -C tools-for-build avx2 2> /dev/null || tools-for-build/avx2 ; then - SBCL_CONTRIB_BLOCKLIST="$SBCL_CONTRIB_BLOCKLIST sb-simd" + if $android; then + $GNUMAKE -C tools-for-build avx2 2> /dev/null + if ! android_run tools-for-build/avx2 ; then + SBCL_CONTRIB_BLOCKLIST="$SBCL_CONTRIB_BLOCKLIST sb-simd" + fi + else + if ! $GNUMAKE -C tools-for-build avx2 2> /dev/null || tools-for-build/avx2 ; then + SBCL_CONTRIB_BLOCKLIST="$SBCL_CONTRIB_BLOCKLIST sb-simd" + fi fi case "$sbcl_os" in @@ -697,10 +769,16 @@ esac # cross-compilers! # # FIXME: integrate to grovel-features, mayhaps -$GNUMAKE -C tools-for-build determine-endianness -I ../src/runtime -tools-for-build/determine-endianness >> $ltf +if $android +then + $CC tools-for-build/determine-endianness.c -o tools-for-build/determine-endianness + android_run tools-for-build/determine-endianness >> $ltf +else + $GNUMAKE -C tools-for-build determine-endianness -I ../src/runtime + tools-for-build/determine-endianness >> $ltf +fi -export sbcl_os sbcl_arch +export sbcl_os sbcl_arch android sh tools-for-build/grovel-features.sh >> $ltf echo //finishing $ltf diff --git a/make-target-1.sh b/make-target-1.sh index 7a24ea3fb..0428ae868 100755 --- a/make-target-1.sh +++ b/make-target-1.sh @@ -42,8 +42,15 @@ $GNUMAKE $SBCL_MAKE_JOBS -C src/runtime all # Use a little C program to grab stuff from the C header files and # smash it into Lisp source code. # -C tools-for-build is broken on some gnu make versions. -( cd tools-for-build; $GNUMAKE -I../src/runtime grovel-headers ) -tools-for-build/grovel-headers > output/stuff-groveled-from-headers.lisp +if $android +then + ( cd tools-for-build; $CC -I../src/runtime -ldl -o grovel-headers grovel-headers.c) + . ./tools-for-build/android_run.sh + android_run tools-for-build/grovel-headers > output/stuff-groveled-from-headers.lisp +else + ( cd tools-for-build; $GNUMAKE -I../src/runtime grovel-headers ) + tools-for-build/grovel-headers > output/stuff-groveled-from-headers.lisp +fi touch -r tools-for-build/grovel-headers.c output/stuff-groveled-from-headers.lisp if [ -n "$SBCL_HOST_LOCATION" ]; then diff --git a/make-target-contrib-android.sh b/make-target-contrib-android.sh new file mode 100644 index 000000000..f850744c2 --- /dev/null +++ b/make-target-contrib-android.sh @@ -0,0 +1,71 @@ +#!/bin/sh +set -e + +export SBCL_TOP="../.." +export SBCL_HOME="$SBCL_TOP/obj/sbcl-home" +export SBCL="$SBCL_TOP/src/runtime/sbcl --noinform --core $SBCL_TOP/output/sbcl.core \ + --lose-on-corruption --disable-debugger --no-sysinit --no-userinit" +export UNAME=Linux +export DEST=$SBCL_TOP/obj/sbcl-home/contrib + +build_asdf () { + export ASDF_FASL=$DEST/asdf.fasl + export UIOP_FASL=$DEST/uiop.fasl + FROB_READTABLE="(setf (sb-ext:readtable-base-char-preference *readtable*) :both)" + export FASL="$ASDF_FASL $UIOP_FASL" + + cd contrib/asdf + mkdir -p $DEST + + $SBCL --eval "$FROB_READTABLE" \ + --eval "(compile-file #p\"SYS:CONTRIB;ASDF;UIOP.LISP\" \ + :print nil :output-file (merge-pathnames (parse-native-namestring \"$UIOP_FASL\")))" +#include extern char * software_version () { struct utsname u; diff --git a/src/runtime/android-os.h b/src/runtime/android-os.h index a2f661130..dd3fdb80f 100644 --- a/src/runtime/android-os.h +++ b/src/runtime/android-os.h @@ -14,6 +14,7 @@ #include #include #include +#include /* #include */ #include #include diff --git a/src/runtime/arm64-android-os.c b/src/runtime/arm64-android-os.c new file mode 100644 index 000000000..dbc89ec8e --- /dev/null +++ b/src/runtime/arm64-android-os.c @@ -0,0 +1,16 @@ +/* + * This is the ARM64 Android incarnation of arch-dependent OS-dependent + * routines. See also "android-os.c". Most of the OS-dependent things + * are implemented in arm64-linux-os.c + */ + +/* + * This software is part of the SBCL system. See the README file for + * more information. + * + * This software is derived from the CMU CL system, which was + * written at Carnegie Mellon University and released into the + * public domain. The software is in the public domain and is + * provided with absolutely no warranty. See the COPYING and CREDITS + * files for more information. + */ diff --git a/src/runtime/arm64-android-os.h b/src/runtime/arm64-android-os.h new file mode 100644 index 000000000..7cd0b0937 --- /dev/null +++ b/src/runtime/arm64-android-os.h @@ -0,0 +1,15 @@ +#ifndef _ARM64_LINUX_OS_H +#define _ARM64_LINUX_OS_H + +typedef ucontext_t os_context_t; +typedef long os_context_register_t; + +#include "arch-os-generic.inc" + +unsigned long os_context_fp_control(os_context_t *context); +#define RESTORE_FP_CONTROL_FROM_CONTEXT +void os_restore_fp_control(os_context_t *context); + +#define OS_CONTEXT_PC(context) context->uc_mcontext.pc + +#endif /* _ARM64_LINUX_OS_H */ diff --git a/src/runtime/globals.h b/src/runtime/globals.h index f6c0f539a..86497344b 100644 --- a/src/runtime/globals.h +++ b/src/runtime/globals.h @@ -161,8 +161,10 @@ EXTERN(foreign_function_call_active) #if !defined(LISP_FEATURE_SB_THREAD) && !defined(LISP_FEATURE_C_STACK_IS_CONTROL_STACK) EXTERN(current_control_stack_pointer) #endif +#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64) || !defined(LISP_FEATURE_SB_THREAD) EXTERN(current_control_frame_pointer) -# if !defined(LISP_FEATURE_X86) && !defined(LISP_FEATURE_X86_64) +#endif +# if !defined(LISP_FEATURE_X86) && !defined(LISP_FEATURE_X86_64) && !defined(LISP_FEATURE_SB_THREAD) EXTERN(current_binding_stack_pointer) # endif diff --git a/src/runtime/run-program.c b/src/runtime/run-program.c index ee74273b7..8324f2d99 100644 --- a/src/runtime/run-program.c +++ b/src/runtime/run-program.c @@ -144,7 +144,7 @@ void closefds_range(unsigned int first, unsigned int last) unsigned int close_fd; if (last == ~0U) { -#ifdef SVR4 +#if defined SVR4 || defined LISP_FEATURE_ANDROID last = sysconf(_SC_OPEN_MAX)-1; #else last = getdtablesize()-1; diff --git a/src/runtime/x86-64-android-os.c b/src/runtime/x86-64-android-os.c new file mode 100644 index 000000000..bcbc165af --- /dev/null +++ b/src/runtime/x86-64-android-os.c @@ -0,0 +1,16 @@ +/* + * This is the x86-64 Android incarnation of arch-dependent OS-dependent + * routines. See also "android-os.c". Most of the OS-dependent things + * are implemented in x86-64-linux-os.c + */ + +/* + * This software is part of the SBCL system. See the README file for + * more information. + * + * This software is derived from the CMU CL system, which was + * written at Carnegie Mellon University and released into the + * public domain. The software is in the public domain and is + * provided with absolutely no warranty. See the COPYING and CREDITS + * files for more information. + */ diff --git a/src/runtime/x86-64-android-os.h b/src/runtime/x86-64-android-os.h new file mode 100644 index 000000000..93d6c7bd6 --- /dev/null +++ b/src/runtime/x86-64-android-os.h @@ -0,0 +1,15 @@ +#ifndef _X86_64_ANDROID_OS_H +#define _X86_64_ANDROID_OS_H + +typedef ucontext_t os_context_t; +typedef long os_context_register_t; + +#include "arch-os-generic.inc" + +unsigned long os_context_fp_control(os_context_t *context); +#define RESTORE_FP_CONTROL_FROM_CONTEXT +void os_restore_fp_control(os_context_t *context); + +#define OS_CONTEXT_PC(context) context->uc_mcontext.gregs[REG_RIP] + +#endif /* _X86_64_ANDROID_OS_H */ diff --git a/tools-for-build/android_run.sh b/tools-for-build/android_run.sh new file mode 100644 index 000000000..7f4f97c7f --- /dev/null +++ b/tools-for-build/android_run.sh @@ -0,0 +1,13 @@ +android_run() { + adb push $1 /data/local/tmp/temp.out > /dev/null + adb shell chmod +x /data/local/tmp/temp.out > /dev/null + adb shell ./data/local/tmp/temp.out + adb shell rm /data/local/tmp/temp.out > /dev/null +} + +android_run_for_exit_code() { + adb push $1 /data/local/tmp/$1 > /dev/null + adb shell chmod +x /data/local/tmp/$1 > /dev/null 2>&1 + adb shell "echo input | ./data/local/tmp/$1 > /dev/null 2>&1 ; echo \"\$?\"" 2>/dev/null + adb shell rm /data/local/tmp/$1 > /dev/null 2>&1 +} diff --git a/tools-for-build/grovel-features.sh b/tools-for-build/grovel-features.sh index 73925d9dd..50d5b14dc 100644 --- a/tools-for-build/grovel-features.sh +++ b/tools-for-build/grovel-features.sh @@ -1,5 +1,6 @@ # Automated platform feature testing cd ./tools-for-build > /dev/null +. ./android_run.sh # FIXME: Use this to test for dlopen presence and hence # load-shared-object buildability @@ -12,8 +13,15 @@ featurep() { bin="$1-test" featurename=${2:-$1} rm -f $bin - $GNUMAKE $bin -I ../src/runtime > /dev/null 2>&1 && echo "input" | ./$bin> /dev/null 2>&1 - if [ "$?" -eq 104 ] + if $android + then + $CC -I../src/runtime -ldl -o $bin $bin.c > /dev/null 2>&1 + exit_code=`android_run_for_exit_code $bin` + else + $GNUMAKE $bin -I ../src/runtime > /dev/null 2>&1 && echo "input" | ./$bin> /dev/null 2>&1 + exit_code="$?" + fi + if [ "$exit_code" -eq 104 ] then printf " :$featurename" fi