JNI 專案一旦涉及多種 UNIX 平台(FreeBSD/x86、Linux/ia64/ppc32/ppc64/s390/s390x/x86/x86_64…),編譯器旗標、鏈接器參數、系統呼叫、第三方庫路徑全都不同。若為每平台維護獨立建構腳本,維護成本呈指數級增長。本文展示如何整合 Ant + GCC + Make + Subversion,打造「平台自動偵測、建構步驟與旗標分離、模組與平台雙向可插拔」的企業級建構系統。

project-root/
├── build.xml # 總建構腳本
├── properties.xml # 共用屬性、宏定義
├── make/
│ ├── defines.mk # 通用巨集(CC、LD、JDK Include 路徑)
│ ├── rules.mk # 通用建構規則(編譯、鏈接、產出 .so)
│ └── platform/ # 平台專屬旗標
│ ├── linux.x86.mk
│ ├── linux.ppc64.mk
│ ├── freebsd.x86.mk
│ └── ...
├── modules/
│ ├── ui/ # 純 Java 模組
│ │ ├── src/java/...
│ │ └── build.xml
│ ├── thread/ # JNI 模組
│ │ ├── src/java/...
│ │ ├── src/native/
│ │ │ ├── include/ # javah 產出 .h 放這裡
│ │ │ ├── linux.x86/hello.c
│ │ │ ├── linux.ppc64/hello.c
│ │ │ └── Makefile # 只含通用步驟,include $(HY_PLATFORM).mk
│ │ └── build.xml
│ ├── archive/...
│ └── net/...

<macrodef name="make">
<attribute name="dir"/>
<sequential>
<exec executable="${make.command}" dir="@{dir}" failonerror="true">
<env key="HY_ARCH" value="${hy.arch}"/>
<env key="HY_OS" value="${hy.os}"/>
</exec>
</sequential>
</macrodef>
<!-- 模組 build.xml 中調用 -->
<make dir="${p.root}/${path.src}/thread/native"/>
<!-- properties.xml -->
<condition property="hy.os" value="linux"><os name="linux"/></condition>
<condition property="hy.os" value="freebsd"><os name="freebsd"/></condition>
<condition property="hy.os" value="aix"><os name="aix"/></condition>
<property name="hy.os" value="${os.name}"/> <!-- fallback -->
<condition property="hy.arch" value="x86"><os arch="x86"/></condition>
<condition property="hy.arch" value="ppc64"><os arch="ppc64"/></condition>
<property name="hy.arch" value="${os.arch}"/>
<property name="HY_PLATFORM" value="${hy.os}.${hy.arch}"/> <!-- 如 linux.x86 -->
屬性經 make 宏注入環境變數 HY_OS、HY_ARCH,Makefile 再組合成 HY_PLATFORM。
# modules/thread/native/Makefile
include ../../make/defines.mk
include ../../make/platform/$(HY_PLATFORM).mk # 平台旗標注入
CFLAGS += $(SRCS) $(INCLUDES) $(OPT) -static -lc
PROGNAME = libhellolib.so
include ../../make/rules.mk
# make/defines.mk
CC = gcc
ifeq ($(HY_OS),aix)
DLL_LD = $(LD)
else
DLL_LD = $(CC)
endif
INCLUDES = -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/$(HY_OS)
SRCS = $(HY_PLATFORM)/hello.c
# make/platform/linux.x86.mk
OPT = -march=pentium3
# Linux/x86 無特殊 OSLIBS/ASFLAGS/LDFLAGS/XLIBS
# make/platform/linux.ppc64.mk
OPT = -m64
ASFLAGS = -a64
LDFLAGS = -m64
XLIBS = -L/usr/X11R6/lib64 -lX11 -lXft
# make/rules.mk
all: $(PROGNAME)
$(PROGNAME):
$(DLL_LD) -o $(PROGNAME) -shared -Wl,-soname,libhello.so $(CFLAGS)

<target name="compile-java">
<mkdir dir="${path.dist.classes}"/>
<call-modules target="compile-java"/> <!-- 宏遍歷 modules/*/build.xml -->
</target>
<target name="compile-native">
<call-modules target="compile-native"/>
</target>
<macrodef name="call-modules">
<attribute name="target"/>
<sequential>
<subant target="@{target}">
<dirset dir="modules" includes="${build.module}" excludes="${exclude.module}"/>
</subant>
</sequential>
</macrodef>
<target name="svn-prop">
<exec executable="svn">
<arg value="--non-interactive"/><arg value="info"/>
<redirector outputproperty="svn.revision">
<outputfilterchain>
<linecontains><contains value="Revision:"/></linecontains>
<tokenfilter><replacestring from="Revision:" to=""/></tokenfilter>
</outputfilterchain>
</redirector>
</exec>
</target>



make/platform/ 加入 <os>.<arch>.mk,在各 JNI 模組 src/native/<os>.<arch>/ 放入平台專屬 .c/.h。modules/ 建立目錄結構,撰寫 build.xml 繼承總腳本屬性與宏,無需修改總腳本。<platform>.mk 以 XLIBS、OSLIBS 注入路徑與連結旗標。將「建構步驟(常量)」與「編譯旗標、平台代碼(變量)」徹底分離,配合 Ant 內建 <os> 條件自動偵測平台,實現:
完整可運行原型碼見附件 autobuild.zip,可直接作為企業專案骨架擴充。