#
# Makefile for JNA native bits
# Copyright (c) 2007-2014 Timothy Wall All Rights Reserved
#
# You may need to run 'ant javah' at the root before performing a build from
# this directory.
#
# To build with debug information, use 'make DEBUG=true'
#
# Supported platforms (built and tested):
#
#   Windows (x86/amd64/aarch64)
#   Windows CE/Mobile (arm)
#   Darwin/OS X (i386/x86-64/ppc/aarch64)
#   Linux (i386/amd64/ppc/arm)
#   Solaris (i386/amd64/sparc/sparcv9)
#   AIX (ppc/ppc64)
#   DragonFly (x86-64)
#   FreeBSD (i386/amd64/aarch64)
#   OpenBSD/NetBSD (i386/amd64)
#   Android (arm/armv7/aarch64/x86/x86-64/mipsel/mips64el)
#
# Built, but with outstanding bugs (not necessarily within JNA):
#
#   Linux (ppc64/ppc64le/ia64)
#
# The w32ce build requires cegcc and phoneME for cross-compilation; if these
# tools are available on the path then "ant -Dos.prefix=w32ce-arm" should
# result in a proper build.
#
# The android build requires the android SDK+NDK for cross-compilation;
# make the tools available on the path and compile with
# "ant -Dos.prefix=android-{arm|x86}".  Put the NDK tools in the path and adjust
# NDK_PLATFORM below or set it in your environment.
#
# The windows build requires a cygwin installation.  The build will use MSVC
# for compilation if cl.exe is found on the PATH, otherwise the build will fall 
# back to mingw.

# Systems which support POSIX signals may be able to support VM crash
# protection simply by defining HAVE_PROTECTION.  This option has been only
# been enabled for those platforms on which it has been tested successfully.

OS=$(shell uname | sed -e 's/CYGWIN.*/win32/g' \
	                -e 's/MINGW32.*/win32/g' \
                        -e 's/SunOS.*/solaris/g' \
                        -e 's/NetBSD/netbsd/g' \
                        -e 's/GNU\/kFreeBSD/kfreebsd/g' \
                        -e 's/FreeBSD/freebsd/g' \
                        -e 's/DragonFly/dragonfly/g' \
                        -e 's/OpenBSD/openbsd/g' \
                        -e 's/Darwin.*/darwin/g' \
                        -e 's/AIX.*/aix/g' \
                        -e 's/Linux.*/linux/g')

JNA_JNI_VERSION=5.1.0 # auto-generated by ant
CHECKSUM=74e8f8e397c43487738c5c1f1363498b # auto-generated by ant

JAVA_INCLUDES=-I"$(JAVA_HOME)/include" \
              -I"$(JAVA_HOME)/include/$(OS)"

BUILD=../build/native
JAVAH=$(BUILD)
INSTALLDIR=../build/$(OS)
JNIDISPATCH_OBJS=$(BUILD)/dispatch.o $(BUILD)/callback.o $(EXTRAOBJS)
RSRC=$(BUILD)/rsrc.o
DLLCB=$(BUILD)/dll-callback.o
ifneq ($(DYNAMIC_LIBFFI),true)
FFI_SRC=$(shell pwd)/libffi
FFI_BUILD=$(BUILD)/libffi
FFI_LIB=$(FFI_BUILD)/.libs/libffi$(ARSFX)
FFI_ENV=CC="$(CC)" CFLAGS="$(COPT) $(CDEBUG) -DFFI_STATIC_BUILD" CPPFLAGS="$(CDEFINES) -DFFI_STATIC_BUILD"
FFI_CONFIG=--enable-static --disable-shared --with-pic=yes
endif
LIBRARY=$(BUILD)/$(LIBPFX)jnidispatch$(JNISFX)
TESTLIB=$(BUILD)/$(LIBPFX)testlib$(LIBSFX)
TESTLIB_JAR=$(BUILD)/$(LIBPFX)testlib-jar$(LIBSFX)
TESTLIB_PATH=$(BUILD)/$(LIBPFX)testlib-path$(LIBSFX)
TESTLIB_TRUNC=$(BUILD)/testlib-truncated
TESTLIB2=$(BUILD)/$(LIBPFX)testlib2$(LIBSFX)

# Reasonable defaults based on GCC
LIBPFX=lib
LIBSFX=.so
ARSFX=.a
JNISFX=$(LIBSFX)
CC=gcc
LD=$(CC)
LIBS=
# Default to Sun recommendations for JNI compilation
COPT=-O2 -fno-omit-frame-pointer -fno-strict-aliasing
COPT+=-Wno-implicit-function-declaration
CASM=-S
ifeq ($(DEBUG),true)
CDEBUG=-g
endif
CFLAGS_EXTRA=
COUT=-o $@
CINCLUDES=$(JAVA_INCLUDES) -I"$(JAVAH)" -I$(FFI_BUILD)/include
CDEFINES=-D_REENTRANT
PCFLAGS=-W -Wall -Wno-unused -Wno-parentheses
CFLAGS=$(PCFLAGS) $(CFLAGS_EXTRA) $(COPT) $(CDEBUG) $(CDEFINES) $(CINCLUDES) \
       -DJNA_JNI_VERSION='"$(JNA_JNI_VERSION)"' -DCHECKSUM='"$(CHECKSUM)"'
LDFLAGS=-o $@ -shared
ifeq ($(DYNAMIC_LIBFFI),true)
CFLAGS += $(shell pkg-config --cflags libffi 2>/dev/null || echo)
LIBS += $(shell pkg-config --libs libffi 2>/dev/null || echo -lffi)
else
# -static-libgcc avoids gcc library incompatibilities across linux systems
LDFLAGS += -static-libgcc
endif
# Avoid bug in X11-based 1.5/1.6 VMs; dynamically load instead of linking
# See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6539705
#LIBS=-L"$(LIBDIR)" -ljawt
STRIP=strip -x
# end defaults

# Android build (cross-compile) requires the android NDK.
# Ensure the following tools are in your path and adjust NDK_PLATFORM as needed
ifeq ($(OS),android)
AARCH=$(ARCH)
ALIBDIR=/usr/lib
ifeq ($(ARCH),arm)
PREFIX=arm-linux-androideabi-
COPT+= -mthumb-interwork -march=armv5te -mtune=xscale -msoft-float -fstack-protector 
HOST=arm-linux-eabi
else
ifeq ($(ARCH),armv7)
PREFIX=arm-linux-androideabi-
COPT+= -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -Wl,--fix-cortex-a8
HOST=arm-linux-eabi
AARCH=arm
else
ifeq ($(ARCH),aarch64)
PREFIX=aarch64-linux-android-
HOST=aarch64-linux-android
AARCH=arm64
ALIBDIR=/usr/lib64
else
ifeq ($(ARCH),x86)
PREFIX=i686-linux-android-
COPT+= -march=i686
HOST=i686-linux-android
else
ifeq ($(ARCH),x86-64)
PREFIX=x86_64-linux-android-
COPT+= -m64
HOST=x86_64-linux-android
AARCH=x86_64
ALIBDIR=/usr/lib64
else
ifeq ($(ARCH),mips)
PREFIX=mipsel-linux-android-
HOST=mipsel-linux-android
COPT+=
else
ifeq ($(ARCH),mips64)
PREFIX=mips64el-linux-android-
HOST=mips64el-linux-android
COPT+=
ALIBDIR=/usr/lib64
else
ERROR = $(error "Unsupported android architecture '$(ARCH)'")
endif
endif
endif
endif
endif
endif
endif
NDK?=/Developer/Applications/android-ndk-r10e
NDK_PLATFORM?=$(NDK)/platforms/android-21
SYSROOT=$(NDK_PLATFORM)/arch-$(AARCH)
CC=$(PREFIX)gcc --sysroot $(SYSROOT)
CPP=$(PREFIX)cpp
LD=$(CC)
RANLIB=$(PREFIX)ranlib
STRIP=$(PREFIX)strip -x
CDEFINES=-DFFI_STATIC_BUILD -DNO_JAWT -DNO_WEAK_GLOBALS -DFFI_MMAP_EXEC_WRIT=1 -DFFI_MMAP_EXEC_SELINUX=0 -Dmalloc_getpagesize='getpagesize()'
COPT+=-fpic -ffunction-sections -funwind-tables -fno-short-enums
JAVA_INCLUDES=
CINCLUDES+=-I"$(NDK_PLATFORM)/arch-$(AARCH)/usr/include" # -I/usr/include
LIBS=-nostdlib -L"$(NDK_PLATFORM)/arch-$(AARCH)$(ALIBDIR)/" -lgcc -lc -ldl -lm
LDFLAGS+=-Wl,-shared,-Bsymbolic -Wl,--build-id=sha1 -Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384
FFI_ENV=CPP="$(CPP)" CC="$(CC)" CFLAGS="$(COPT) $(CDEBUG) $(CINCLUDES)" CPPFLAGS="$(CDEFINES) $(CINCLUDES)" LIBS="$(LIBS)" RANLIB="$(RANLIB)"
FFI_CONFIG=--enable-static --disable-shared --with-pic=yes --host=$(HOST)
endif

# W32CE build requires cegcc cross-compiler and phoneME JavaME implementation
# cegcc: http://sf.net/projects/cegcc
# phoneme: http://davy.preuveneers.net/
ifeq ($(OS),w32ce)
ARCH=arm
CC=arm-mingw32ce-gcc -mwin32
CDEFINES=-D_WIN32_IE=0x400 -D_WIN32_WCE=0x0420 -DNO_JAWT -DNO_NIO_BUFFERS -DWINCE -D_WIN32_STDARG_H
CFLAGS+=-Wno-unknown-pragmas
LD=arm-mingw32ce-gcc
LDFLAGS=-o $@ -shared
LIBS=-lcoredll -lgcc -ltoolhelp
WINDRES=arm-mingw32ce-windres
EXTRAOBJS=$(RSRC)
STRIP=arm-mingw32ce-strip -x
LIBPFX=
LIBSFX=.dll
HOST_CONFIG=--host=arm-mingw32ce
# libffi must use GNU ld; jnidispatch.dll requires gcc
FFI_ENV+=LD=arm-mingw32ce-ld CPP=cpp CFLAGS="$(CDEFINES)"
PHONEME=../phoneme/cdc/src
JAVA_INCLUDES=-I$(PHONEME)/share/javavm/export -I$(PHONEME)/share -I$(PHONEME)/win32 -I$(PHONEME)/win32-arm
TESTLIB_TRUNC=$(BUILD)/testlib-truncated.dll
endif

ifeq ($(OS),win32)
ifeq ($(shell type cl >& /dev/null && echo found),found)
USE_MSVC=true
endif
# This will be overridden when called from ant
ARCH=$(shell uname -m | sed 's/i.86/i386/g')
CDEFINES=-DPSAPI_VERSION=1 -DFFI_BUILDING -DUNICODE -D_UNICODE
WINDRES=windres
EXTRAOBJS=$(RSRC)
STRIP=@echo
LIBPFX=
LIBSFX=.dll
TESTLIB_TRUNC=$(BUILD)/testlib-truncated.dll

ifeq ($(ARCH),amd64)
MINGW_PREFIX?=x86_64-w64-mingw32-
FFI_CONFIG+=--host=x86_64-w64-mingw32
else
FFI_CONFIG+=--host=i686-w64-mingw32
MINGW_PREFIX?=i686-w64-mingw32-
endif
# Need windres from mingw distribution, even if building with MSVC
WINDRES=$(MINGW_PREFIX)windres
MINGW=$(MINGW_PREFIX)gcc

ifeq ($(USE_MSVC),true)
# MS compiler
CC=$(FFI_SRC)/msvcc.sh
CDEFINES+=-DHAVE_PROTECTION 
ifneq ($(DYNAMIC_LIBFFI),true)
CDEFINES+=-DUSE_STATIC_RTL
endif
COPT=
CPP=cl -nologo -EP
LD=link
LDFLAGS=/DLL /OUT:$@
LIBS=psapi.lib
ARSFX=.lib
ifeq ($(ARCH),amd64)
CC+= -m64
FFI_TARGET=$(FFI_SRC)/src/x86/ffitarget.h
else ifeq ($(ARCH),aarch64)
# Force $(CC) fallback; avoid arch mismatch
WINDRES=false
# Disable mingw; no aarch64 support
MINGW=
FFI_CONFIG+=--host=aarch64-cygwin
# Skip building assembly callback functions
CDEFINES+= -DASMFN_OFF
DLLCB=
CC+= -marm64
FFI_CONFIG+=--host=aarch64-cygwin
endif
FFI_TARGET?=$(FFI_SRC)/src/$(ARCH)/ffitarget.h
FFI_CONFIG+= && rm -f include/ffitarget.h && cp $(FFI_SRC)/include/*.h $(FFI_TARGET) include
FFI_ENV+=LD="$(LD)" CPP="$(CPP)" CXXCPP="$(CPP)"
EXTRAOBJS+=$(DLLCB)

else
# Mingw compiler
LDFLAGS=-o $@ -shared
FFI_ENV+=CXXCPP="$(CPP)"
CC=$(MINGW_PREFIX)gcc
CPP=$(MINGW_PREFIX)gcc -E
LDFLAGS+=-Wl,--add-stdcall-alias
LIBS= -lmingwex -lpsapi -lkernel32 -lmsvcrt
endif

endif

ifeq ($(OS),linux)
ARCH=$(shell uname -m | sed 's/i.86/i386/g')
PCFLAGS+=-fPIC
CDEFINES+=-DHAVE_PROTECTION
ifeq ($(DYNAMIC_LIBFFI),true)
LDFLAGS+=-Wl,-soname,$@
else
# Ensure we bind to local libffi symbols
LDFLAGS+=-Wl,-soname,$@,-Bsymbolic
endif
endif

ifneq (,$(or $(findstring bsd,$(OS)),$(findstring dragonfly,$(OS))))
ARCH=$(shell uname -m | sed 's/i.86/i386/g')
PCFLAGS+=-fPIC
# This is a mess: X11 headers locate in /usr/local/include on FreeBSD and
# DragonFly, in /usr/X11R7/include on NetBSD, and in /usr/X11R6/include on
# OpenBSD.
CINCLUDES+=-I/usr/local/include -I/usr/X11R7/include -I/usr/X11R6/include
CINCLUDES+=$(X11INC)  # Allow extra X11 include path if necessary.
LDFLAGS=-o $@ -shared
CDEFINES+=-DHAVE_PROTECTION -DFFI_MMAP_EXEC_WRIT -DUSE_DEAFULT_LIBNAME_ENCODING
endif

ifeq ($(OS),solaris)
ifeq ($(ARCH),)
ARCH=$(shell uname -p)
endif
PCFLAGS+=-fPIC -std=gnu99
CDEFINES+=-DHAVE_PROTECTION -DFFI_MMAP_EXEC_WRIT -DUSE_DEFAULT_LIBNAME_ENCODING
ifeq ($(ARCH), sparcv9)
  # alter CC instead of PCFLAGS, since we need to pass it down to libffi
  # configure and some of the other settings in PCFLAGS might make the build
  # choke
  CC += -m64
  LD += -m64
endif
endif


ifeq ($(OS),aix)
LIBSFX=.a
PCFLAGS+=-fPIC
CDEFINES+=-DHAVE_PROTECTION -DNO_JAWT -Wall -D_AIX -DPOWERPC_AIX -D_THREAD_SAFE_ERRNO
COPT+=-D_AIX -DPOWERPC_AIX -mxl-compat
LDFLAGS+=-Wl,-lc128,-lc,-lm,-lpthread
FFI_ENV+=AR_FLAGS="-X32_64 cru"
STRIP=echo strip -X32_64

ifeq ($(ARCH),ppc64)
  COPT+=-maix64
  LDFLAGS+=$(COPT)
  HOST_CONFIG=--host=ppc-aix64
endif
ifeq ($(ARCH),ppc)
  COPT+=-maix32
  LDFLAGS+=$(COPT)
  HOST_CONFIG=--host=ppc-aix
endif
endif

# CC_OPTS only applied to objects build for jnidispatch, not for libffi
# -Wno-unknown-warning-option
#                => Suppress warning for unknown warnings
# -Werror        => Treat warnings as errors
# -Wno-clobbered => Silence GCC warning about clobbered automatic variables.
#                   The "longjmp" case only happens in protect.h in the logic
#                   that implements "protected" mode. In that case an exception
#                   is raised and the value of the potentially clobbered
#                   variables is ignored.
# -Wno-alloca-larger-than => Silence warnings about unbounded alloca calls in
#                   the dispatch logic. GCC tries to assert, that the size of
#                   the allocated memory is bounded and thus a check for the
#                   variable needs to be inplace. For the JNA case, the size
#                   comes from the Java side, so checks are not visible on the
#                   C side and thus can't be checked there.
#
# Enable this only on GCC versions, that can work with it. It is assumed, that
# GCC version 4.X is the lower bound. That version is problematic, as it
# does not support -Wno-unknown-warning-option
ifeq ($(CC),gcc)
    GCC_MAJOR_VERSION = $(shell gcc -dumpversion | cut -f 1 -d '.')
    ifneq ($(GCC_MAJOR_VERSION),4)
	ifneq ($(GCC_MAJOR_VERSION),3)
	    LOC_CC_OPTS=-Wno-unknown-warning-option -Werror -Wno-clobbered -Wno-unused-variable -Wno-alloca-larger-than
	endif
    endif
else
    LOC_CC_OPTS=-Wno-unknown-warning-option -Werror -Wno-clobbered -Wno-unused-variable
endif

# Enable 64-bit builds if the arch demands it
ifeq ($(CC),gcc)
ifneq ($(OS),aix)
ifeq ($(ARCH),amd64)
  CC += -m64
  LD += -m64
endif
ifeq ($(ARCH),ppc64)
  CC += -m64
  LD += -m64
endif
ifeq ($(ARCH),ppc64le)
  CC += -m64
  LD += -m64
endif
ifeq ($(ARCH),ppc)
  CC += -m32
  LD += -m32
  HOST_CONFIG=--host=ppc-linux
endif
ifeq ($(ARCH),i386)
  CC += -m32
  LD += -m32
endif
endif
endif


ifeq ($(OS),darwin)

DARWIN_ARCH=$(ARCH)
ifeq ($(ARCH),aarch64)
  DARWIN_ARCH=arm64
else 
ifeq ($(ARCH),x86-64)
  DARWIN_ARCH=x86_64
else
ifeq ($(ARCH),x86)
  DARWIN_ARCH=x86
else 
endif
endif
endif

XCODE_VERSION=$(shell xcodebuild -version | grep Xcode | sed 's/^Xcode \([1-9][0-9]*\).*/\1/g')
MACOS_VERSION_MAJOR=$(shell sw_vers -productVersion | cut -d. -f 1)
MACOS_VERSION_MINOR=$(shell sw_vers -productVersion | cut -d. -f 2)
JAVA_INCLUDES+=-I/System/Library/Frameworks/JavaVM.framework/Headers
# Actual deployment target depends on SDK support
MACOSX_DEPLOYMENT_TARGET=10.3
DEFAULT_ARCH=$(shell arch)
HOST_CONFIG=--host $(DARWIN_ARCH)-apple-darwin
FFI_ENV += CC="$(CC)" CFLAGS="-mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET) -arch $(DARWIN_ARCH) $(ISYSROOT) $(COPT) $(CDEBUG)" CPPFLAGS="$(CDEFINES)" LD="$(LD) -arch $(DARWIN_ARCH)"
LIBSFX=.dylib
JNISFX=.jnilib
# JAWT no longer supported on OSX
CDEFINES+=-DTARGET_RT_MAC_CFM=0 -DFFI_MMAP_EXEC_WRIT -DNO_JAWT

ifneq ($(ARCH),ppc)
ifneq ($(XCODE_VERSION),3)
# Xcode 4+ options
NO_COMPACT_UNWIND=-Wl,-no_compact_unwind
endif
endif
ifneq ($(SDKROOT),)
SYSLIBROOT=-Wl,-syslibroot,$(SDKROOT)
ISYSROOT=-isysroot $(SDKROOT)
ARCHFLAGS+=-arch $(DARWIN_ARCH)
endif

PCFLAGS+=$(ISYSROOT) -x objective-c

# MAJOR < 10 || (MAJOR = 10 && MINOR < 7)
MACOS_LT_10_7 = $(shell [ ${MACOS_VERSION_MAJOR} -lt 10 -o ${MACOS_VERSION_MAJOR} -eq 10 -a ${MACOS_VERSION_MINOR} -lt 7 ] && echo true)

# JavaVM.framwork is "Deprecated in OS X v10.7."
ifeq ($(MACOS_LT_10_7),true)
FRAMEWORK=-framework JavaVM
endif

LDFLAGS=$(ARCHFLAGS) -dynamiclib -o $@ $(FRAMEWORK) \
  -compatibility_version $(shell echo ${JNA_JNI_VERSION}|sed 's/^\([0-9][0-9]*\).*/\1/g') \
  -current_version $(JNA_JNI_VERSION) \
  -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET) \
  -framework Foundation \
  $(NO_COMPACT_UNWIND) \
  -install_name ${@F} \
  $(SYSLIBROOT)
# JAWT linkage handled by -framework JavaVM
LIBS=
endif

# Unfortunately, we have to use different libffi include files depending on
# the target, so we can't do a simple universal build on darwin.  Do
# separate builds, then merge the results.
$(BUILD)/%.o : %.c dispatch.h protect.h $(FFI_LIB)
	@mkdir -p $(BUILD)
ifneq ($(SDKROOT),)
	$(CC) $(LOC_CC_OPTS) -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET) -arch $(DARWIN_ARCH) $(CFLAGS) -c $< -o $@
else
	$(CC) $(CFLAGS) $(LOC_CC_OPTS) -c $< $(COUT)
endif

all: $(LIBRARY) $(TESTLIB) $(TESTLIB2) $(TESTLIB_JAR) $(TESTLIB_PATH) $(TESTLIB_TRUNC)

install:
	mkdir $(INSTALLDIR)
	cp $(LIBRARY) $(INSTALLDIR)

ifeq ($(ARCH), amd64)
$(DLLCB): dll-callback.c 
	$(MINGW) -DDEFINE_CALLBACKS -c $< $(COUT)
endif

$(RSRC): $(BUILD)/jnidispatch.rc $(BUILD)/$(JNA_JNI_VERSION).stamp
	$(WINDRES) -i $< -o $@ \
          || (echo > $@.c && $(CC) $(CFLAGS) -c $@.c $(COUT))

$(BUILD)/$(JNA_JNI_VERSION).stamp:
	@touch $@

$(LIBRARY): $(JNIDISPATCH_OBJS) $(FFI_LIB)
	$(LD) $(LDFLAGS) $(JNIDISPATCH_OBJS) $(FFI_LIB) $(LIBS)
	$(STRIP) $@

$(TESTLIB): $(BUILD)/testlib.o
	$(LD) $(LDFLAGS) $< $(LIBS)

# These targets provide for different shared library loading methods
# without getting into native library load conflicts
$(TESTLIB_JAR) $(TESTLIB_PATH) $(TESTLIB_TRUNC): $(TESTLIB)
	@cp $< $@

ifeq ($(ARSFX),.lib)
TESTDEP=$(TESTLIB:.dll=.lib)
else
TESTDEP=$(TESTLIB)
endif
$(TESTLIB2): $(BUILD)/testlib2.o
	$(LD) $(LDFLAGS) $< $(TESTDEP) $(LIBS)

ifneq ($(DYNAMIC_LIBFFI),true)
$(FFI_LIB): 
	@mkdir -p $(FFI_BUILD)
	@if [ ! -f $(FFI_SRC)/configure ]; then \
	  echo "Generating configure"; \
	  (cd $(FFI_SRC); /bin/sh autogen.sh); \
	fi
	@if [ ! -f $(FFI_BUILD)/Makefile ]; then \
	  echo "Configuring libffi ($(ARCH))"; \
	  (cd $(FFI_BUILD) \
	    && $(FFI_ENV) $(FFI_SRC)/configure $(FFI_CONFIG) $(HOST_CONFIG)); \
	fi
	$(MAKE) -C $(FFI_BUILD)
endif

clean:
	$(RM) -rf $(BUILD)

version:
	@echo version=$(JNA_JNI_VERSION)

#EOF