Android用にMonoをbuildする

AndroidスタンドアロンのMonoを使いたかったのでMonoをbuildしました。
その手順を忘れないように書いておきます。

対象となるMonoのバージョンは2.10.5です。
2.10.5の理由は手元のUbuntuのapt-getでinstallされたのが2.10.5だったからです。

以下の手順でbuildを行います。

  1. Androidのソースツリーを取得
  2. externalの下に「androidmono」という名前でディレクトリを作成
  3. external/androidmonoの中でgit cloneなりtarを展開するなりしてMonoのソースを展開
  4. 展開したMonoのディレクトリを「mono」という名前に変更
  5. Android.mkとApplication.mkを作成
  6. パッチを適用する
  7. ビルドに必要なヘッダファイル群を展開する

という感じです。

Android.mkの中身はこんな感じですね。

LOCAL_PATH := $(call my-dir)
MONO_PATH := $(LOCAL_PATH)/mono

MONO_ASSEMBLY_DIR := \"/data/local/mono/lib/\"
MONO_CONFIG_DIR   := \"/data/local/mono/bin/\"
MONO_BINARY_DIR   := \"/data/local/mono/bin/\"

MONO_CFLAGS := -DMONO_ASSEMBLIES=$(MONO_ASSEMBLY_DIR) \
               -DMONO_CFG_DIR=$(MONO_CONFIG_DIR) \
               -DMONO_BINDIR=$(MONO_BINARY_DIR) \
               -DHAVE_CONFIG_H -DARM_FPU_VFP -D_REENTRANT \
               -DPAGE_SIZE=4096 -DS_IWRITE=S_IWUSR \
               -DPLATFORM_ANDROID -D__linux__ -DREDIRECT_MALLOC=GC_malloc \
               -DHAVE_BOEHM_GC -DHAVE_GC_H -DUSE_INCLUDED_LIBGC -DHAVE_GC_GCJ_MALLOC -DHAVE_GC_ENABLE \
               -D_GNU_SOURCE -DUSE_MMAP -DUSE_MUNMAP -D_FILE_OFFSET_BITS=64 -DNO_UNALIGNED_ACCESS \
               -D__environ=environ

MONO_LIBGC_CFLAGS := -DPACKAGE_NAME=\"libgc-mono\" -DPACKAGE_VERSION=\"6.6\" -DPACKAGE_STRING=\"libgc-mono\ 6.6\" \
                     -DGC_LINUX_THREADS=1 -D_REENTRANT=1 \
                     -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 \
                     -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_UNISTD_H=1 -DHAVE_DLFCN_H=1 \
                     -DLT_OBJDIR=\".libs/\" -DSILENT=1 -DNO_SIGNALS=1 -DNO_EXECUTE_PERMISSION=1 \
                     -DGC_GCJ_SUPPORT=1 -DJAVA_FINALIZATION=1 -DATOMIC_UNCOLLECTABLE=1 -D_IN_LIBGC=1 \

MONO_INCLUDES := $(MONO_PATH)/ \
                 $(MONO_PATH)/mono/ \
                 $(MONO_PATH)/eglib/src/ \
                 $(MONO_PATH)/mono/mini/ \
                 $(MONO_PATH)/libgc/include \
                 $(LOCAL_PATH)/configs \
                 $(LOCAL_PATH)/configs/eglib \
                 $(LOCAL_PATH)/configs/mono/mini \
                 $(LOCAL_PATH)/configs/mono/arch/arm \
                 bionic/linker

MONO_LDLIBS := -ldl

# build 'libeglib'
include $(CLEAR_VARS)

LOCAL_MODULE := eglib
LOCAL_MODULE_TAGS := eng
LOCAL_PATH := $(MONO_PATH)/eglib/src
LOCAL_SRC_FILES := garray.c gerror.c ghashtable.c gmem.c goutput.c \
    gstr.c gslist.c gstring.c gptrarray.c glist.c gqueue.c gpath.c \
    gshell.c gspawn.c gfile.c gfile-posix.c gpattern.c gmarkup.c \
    gutf8.c gunicode.c gdate-unix.c gdir-unix.c gfile-unix.c \
    gmisc-unix.c gmodule-unix.c gtimer-unix.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libmonoutils'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := monoutils
LOCAL_MODULE_TAGS := eng
LOCAL_PATH := $(MONO_PATH)/mono/utils
LOCAL_SRC_FILES := mono-semaphore.c mono-md5.c \
    mono-sha1.c mono-logger.c mono-codeman.c dlmalloc.h dlmalloc.c \
    mono-counters.c mono-compiler.h mono-dl.c mono-dl.h \
    mono-internal-hash.c mono-internal-hash.h \
    mono-io-portability.c mono-io-portability.h monobitset.c \
    mono-math.c mono-mmap.c mono-mmap.h mono-proclib.c \
    mono-proclib.h mono-time.c mono-time.h strtod.h strtod.c \
    strenc.h strenc.c mono-uri.c mono-poll.c mono-path.c \
    mono-stdlib.c mono-property-hash.h mono-property-hash.c \
    mono-value-hash.h mono-value-hash.c freebsd-elf_common.h \
    freebsd-elf32.h freebsd-elf64.h freebsd-dwarf.h dtrace.h \
    mono-filemap.c mono-networkinterfaces.c mono-error.c \
    mono-publib.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -ldl

include $(BUILD_STATIC_LIBRARY)

# build 'libmonoruntime'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := monoruntime
LOCAL_PATH := $(MONO_PATH)/mono/metadata
LOCAL_SRC_FILES := runtime.c \
    mono-basic-block.c mono-wsq.c mono-hash.c reflection.c object.c object-internals.h \
    icall.c icall-def.h char-conversions.h decimal.c decimal.h \
    boehm-gc.c sgen-gc.c sgen-gc.h gc.c gc-internal.h method-builder.h method-builder.c \
    mono-mlist.c mono-mlist.h tabledefs.h threads-types.h threadpool-internals.h \
    file-io.c file-io.h socket-io.c socket-io.h exception.c exception.h \
    debug-mono-symfile.h debug-mono-symfile.c mono-debug.h mono-debug.c \
    mono-debug-debugger.h mono-debug-debugger.c profiler-private.h attach.h attach.c \
    rand.h rand.c security.c security.h security-core-clr.c security-core-clr.h \
    string-icalls.c string-icalls.h sysmath.h sysmath.c process.c process.h \
    environment.c environment.h locales.c locales.h normalization-tables.h \
    filewatcher.c filewatcher.h culture-info.h culture-info-tables.h \
    security-manager.c security-manager.h console-io.h console-unix.c \
    coree.c coree.h domain.c domain-internals.h opcodes.c cil-coff.h \
    metadata.c metadata-internals.h number-formatter.h verify.c verify-internals.h \
    mono-endian.c mono-endian.h mono-config.c loader.c \
    class.c class-internals.h wrapper-types.h mempool.c \
    mono-perfcounters.c mono-perfcounters.h mono-perfcounters-def.h \
    debug-helpers.c mempool-internals.h metadata-verify.c \
    mono-cq.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libmonoruntimearm'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := monoruntimearm
LOCAL_MODULE_TAGS := eng
LOCAL_PATH := $(MONO_PATH)/mono/metadata
LOCAL_SRC_FILES := \
    monitor.c marshal.c threadpool.c threads.c appdomain.c \
    profiler.c cominterop.c assembly.c image.c null-gc.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libwapi'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := wapi
LOCAL_MODULE_TAGS := eng
LOCAL_PATH := $(MONO_PATH)/mono/io-layer
LOCAL_SRC_FILES := access.h atomic.c atomic.h \
    collection.h context.c context.h critical-sections.c \
    critical-sections.h critical-section-private.h error.c error.h \
    events.c events.h event-private.h handles.h \
    handles-private.h io.h io-portability.c io-portability.h \
    io-private.h io-layer.h macros.h messages.c messages.h misc.c \
    misc-private.h mutexes.c mutexes.h mutex-private.h \
    mono-mutex.c mono-mutex.h mono-spinlock.h processes.c \
    processes.h process-private.h security.c security.h \
    semaphores.c semaphores.h semaphore-private.h shared.c \
    shared.h sockets.c sockets.h socket-private.h \
    socket-wrappers.h status.h system.c system.h \
    threads.h thread-private.h timefuncs.c timefuncs.h \
    timefuncs-private.h types.h uglify.h versioninfo.c \
    versioninfo.h wait.c wait.h wapi_glob.h wapi_glob.c locking.c posix.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libwapiarm'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := wapiarm
LOCAL_MODULE_TAG := eng
LOCAL_PATH := $(MONO_PATH)/mono/io-layer
LOCAL_SRC_FILES := collection.c io.c wthreads.c handles.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libgc'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := gc
LOCAL_MODULE_TAG := eng
LOCAL_PATH := $(MONO_PATH)/libgc
LOCAL_SRC_FILES := allchblk.c blacklst.c \
    checksums.c dyn_load.c \
    headers.c mark.c \
    new_hblk.c pcr_interface.c \
    ptr_chck.c real_malloc.c solaris_pthreads.c \
    solaris_threads.c specific.c stubborn.c backgraph.c \
    win32_threads.c pthread_stop_world.c \
    darwin_stop_world.c mach_dep.c
LOCAL_CFLAGS := $(MONO_CFLAGS) $(MONO_LIBGC_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libgcarm'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := gcarm
LOCAL_MODULE_TAG := eng
LOCAL_PATH := $(MONO_PATH)/libgc
LOCAL_SRC_FILES := alloc.c dbg_mlc.c finalize.c gc_dlopen.c gcj_mlc.c malloc.c \
    mallocx.c  mark_rts.c obj_map.c os_dep.c misc.c reclaim.c pthread_support.c typd_mlc.c
LOCAL_CFLAGS := $(MONO_CFLAGS) $(MONO_LIBGC_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libmini'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := mini
LOCAL_MODULE_TAG := eng
LOCAL_PATH := $(MONO_PATH)/mono/mini
LOCAL_SRC_FILES := mini.c method-to-ir.c \
    xdebug.c decompose.c \
    jit-icalls.c trace.c \
    mini-arch.h dominators.c cfold.c regalloc.c \
    helpers.c liveness.c ssa.c abcremoval.c ssapre.c \
    local-propagation.c driver.c debug-mini.c \
    linear-scan.c aot-runtime.c \
    graph.c mini-exceptions.c mini-codegen.c mini-trampolines.c \
    declsec.c wapihandles.c branch-opts.c \
    mini-generic-sharing.c regalloc2.c simd-intrinsics.c \
    unwind.h unwind.c mini-posix.c \
    mini-arm.c exceptions-arm.c tramp-arm.c image-writer.c \
    dwarfwriter.c mini-gc.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libminiarm'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := miniarm
LOCAL_MODULE_TAG := eng
LOCAL_PATH := $(MONO_PATH)/mono/mini
LOCAL_SRC_FILES := aot-compiler.c debugger-agent.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libmono'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := libmono
LOCAL_MODULE_TAGS := eng
LOCAL_PATH := $(MONO_PATH)/mono/mini
LOCAL_SRC_FILES := mini.c method-to-ir.c \
    xdebug.c decompose.c \
    jit-icalls.c trace.c \
    mini-arch.h dominators.c cfold.c regalloc.c \
    helpers.c liveness.c ssa.c abcremoval.c ssapre.c \
    local-propagation.c driver.c debug-mini.c \
    linear-scan.c aot-runtime.c \
    graph.c mini-exceptions.c mini-codegen.c mini-trampolines.c \
    declsec.c wapihandles.c branch-opts.c \
    mini-generic-sharing.c regalloc2.c simd-intrinsics.c \
    unwind.h unwind.c mini-posix.c \
    mini-arm.c exceptions-arm.c tramp-arm.c image-writer.c \
    dwarfwriter.c mini-gc.c
LOCAL_PRELINK_MODULE := false
LOCAL_CFLAGS := $(MONO_CFLAGS) -fPIC
LOCAL_C_INCLUDES := $(MONO_INCLUDES)
LOCAL_WHOLE_STATIC_LIBRARIES := monoutils gcarm monoruntime
LOCAL_STATIC_LIBRARIES := wapiarm monoruntimearm eglib gc wapi mini miniarm
LOCAL_LDFLAGS := -Wall -L$(SYSROOT)/usr/lib -llog -ldl -lm -lc
#LOCAL_STRIP_MODULE := false

include $(BUILD_SHARED_LIBRARY)

# build 'mono'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := mono
LOCAL_MODULE_TAGS := eng
LOCAL_PATH := $(MONO_PATH)/mono/mini
LOCAL_SRC_FILES := main.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)
LOCAL_SHARED_LIBRARIES := libmono
LOCAL_LDFLAGS := -llog -ldl -lm -lc

include $(BUILD_EXECUTABLE)

長いですね・・・。あとでgithubかなんかに突っ込んで終わりにしたいですね。

Application.mkは適当に

APP_ABI := armeabi armeabi-v7a

とか書いておけば良いです。

あてるパッチはこんな感じです。

diff --git a/libgc/dyn_load.c b/libgc/dyn_load.c
index e4be3c7..d8ce7ec 100644
--- a/libgc/dyn_load.c
+++ b/libgc/dyn_load.c
@@ -99,7 +99,9 @@
 # if !defined(OPENBSD)
 #   include <elf.h>
 # endif
+# if defined(HAVE_LINK_H)
 #   include <link.h>
+# endif
 #endif

 /* Newer versions of GNU/Linux define this macro.  We
diff --git a/mono/mini/aot-runtime.c b/mono/mini/aot-runtime.c
index a3c5746..867e437 100644
--- a/mono/mini/aot-runtime.c
+++ b/mono/mini/aot-runtime.c
@@ -38,8 +38,12 @@
 #endif

 #ifdef HAVE_DL_ITERATE_PHDR
+#ifdef PLATFORM_ANDROID
+#include <linker.h>
+#else
 #include <link.h>
 #endif
+#endif

 #include <mono/metadata/tabledefs.h>
 #include <mono/metadata/class.h>
@@ -59,6 +63,21 @@
 #include "mono/utils/mono-compiler.h"
 #include <mono/utils/mono-counters.h>

+#if defined(PLATFORM_ANDROID)
+#ifdef PSR_c
+#undef PSR_c
+#endif
+#ifdef PSR_x
+#undef PSR_x
+#endif
+#ifdef PSR_s
+#undef PSR_s
+#endif
+#ifdef PSR_f
+#undef PSR_f
+#endif
+#endif
+
 #include "mini.h"
 #include "version.h"

diff --git a/mono/mini/mini-posix.c b/mono/mini/mini-posix.c
index c15b098..92aa901 100644
--- a/mono/mini/mini-posix.c
+++ b/mono/mini/mini-posix.c
@@ -631,7 +631,7 @@ mono_runtime_syscall_fork ()
    return (pid_t) syscall (SYS_fork);
 #else
    g_assert_not_reached ();
-   return;
+   return -1;
 #endif
 }

これもあとでどっかにファイルおくだけにしたいですね・・・。

さて残りはヘッダなんですが、ソースをそのまま貼り付けるとえらいことになるので、
あとでgithubにでも突っ込んでおきます。
突っ込んだらこの記事も更新するつもりです。