: O. Yuanying

Building a JNI Universal Application with Xcode 3.0

アップルにある「Building a JNI Universal Application with Xcode」というドキュメントがちょっと古くてXcode 3.0(Leopard)だとうまくチュートリアルの通りに進まなかったのでメモ。

Creating the Initial Application

まず、最初からつまづく。なんといってもNew Projectに「Java Swing Application」が見当たらない。出来上がりのアプリケーションから察するにXcode 3.0では単純に「Java Application」と名前を変えているらしい。

jni01.png

プロジェクトの構成

そして一番の問題がプロジェクトがほとんどAnt-basedになっちゃってること。

jni02.png

そのせいで新規にビルドフェーズをXcode上から追加できない。全部自分でbuild.xmlをいじらなくちゃいけない模様。

まあJava開発者ならAntの方が使いやすいだろうという配慮なんだけど私には余計なお世話でした…。

まあとりあえずBuilding a JNI Universal Application with Xcode通りにnativeメソッドをJavaのソースコードに追加して、ライブラリをロードするところまで進める。

Adding the Native Method Declaration and Call

public native String getMyFullName();

Loading the Dynamic Library

static { System.loadLibrary( "MyFirstJNILib" ); }

Creating the Header File

さて、ここら辺からドキュメントが役立たずになってきますよ。

今回はヘッダファイルを作るのに(ドキュメントにあるJavaHeadersターゲットは作成せずに、)antタスクのjavahを使います。javahでヘッダファイルを作るためにいくつかの変数をbuild.xmlに追加します。

build.xml

initターゲットとjarターゲットを修正。

	<!-- Initialization target, for any prelimary setup needed to build -->
	<target name="init" description="Preparation">
		<!-- Get properties from environment -->
		<property environment="env"/>	
		<property name="curr_build_dir" location="build/${env.CONFIGURATION}"/>
		<property name="curr_header_dir" location="build/${env.CONFIGURATION}/Headers"/>

		<mkdir dir="${curr_build_dir}"/>
		<mkdir dir="${curr_header_dir}"/>	

		<mkdir dir="${src}"/>
		<mkdir dir="${lib}"/>
	</target>

	<target name="jar" depends="compile" description="Build jar">
		<mkdir dir="${jars}"/>
		<jar jarfile="${jars}/${ant.project.name}.jar" basedir="${bin}" manifest="${resources}/Manifest">
			<!-- Inject resources -->
			<fileset dir="${resources}/"
				excludes="${resources}/Manifest"
			/>
			<!-- Merge library jars into final jar file -->
			<zipgroupfileset refid="lib.jars"/>
		</jar>

		<!-- generate the header files -->
		<javah classpath=""${jars}/${ant.project.name}.jar" outputfile="${curr_header_dir}/MyFirstJNILib.h">
			<class name="MyLeopardJNIProject"/>
		</javah>
	</target>

propertyとjavacタスクを追加したよ。

とりあえずここまででビルドしてみると、うまくいってればbuild/${env.CONFIGURATION}/HeadersフォルダにMyFirstJNIProject.hができているはず。

Adding the JNI Library Target

ここのセクションはいくつかの相違点以外は基本的にそのまま。

相違点

  • ヘッダ検索パスの追加
  • ヘッダファイルのincludeの指定の仕方
  • MyFirstJNILibターゲットのDependenciesにJavaHeadersを追加しない
  • build.xmlにターゲットを追加する

ヘッダ検索パスの追加

ドキュメントではヘッダ検索パスとして$(SDKROOT)/System/Library/Frameworks/JavaVM.framework/Headersを追加していたが、さらに"${TARGET_BUILD_DIR}/Headers"も追加する。

jni03.png

ヘッダファイルのincludeの指定の仕方

MyFirstJNILib.cはMyFirstJNILib.hをincludeするが、MyFirstJNILib.hはヘッダ検索パスを前項で追加したため、#include "MyFirstJNILib.h"ではなく、以下のように指定する。

#include <MyFirstJNILib.h>

build.xmlにターゲットを追加する

追加したMyFirstJNILibターゲットをAntからコンパイルするようにbuild.xmlを編集する。

まず、build.xmlの最初の方にネイティブコードをコンパイルするための変数を追加。

property追加

...
	<property name="application.resources" location="${dist}/${ant.project.name}.app/Contents/Resources"/>
	<property name="application.resources.java" location="${dist}/${ant.project.name}.app/Contents/Resources/Java"/>

    <property name="native.target" value="MyFirstJNILib"/>
    <property name="native.project" value="MyLeopardJNIProject.xcodeproj"/>
    <property name="native.library" value="libMyFirstJNILib.jnilib"/>
    <property name="env.CONFIGURATION" value="Release"/>

	<!-- lib directory should contain any pre-built jar files needed to build the project
		 AppleJavaExtensions.jar is included to allow the built jars to run cross-platform if you depend on Apple eAWT or eIO classes.
		 See http://developer.apple.com/samplecode/AppleJavaExtensions/index.html for more information -->
...

ネイティブコードのコンパイル

ネイティブコードをXcodeを利用してコンパイルするターゲットを追加。

	<!-- Note: this target requires that Xcode Tools be installed -->
	<target name="nativelib" depends="jar">
		<exec executable="/usr/bin/xcodebuild">
			<arg line="-project ${native.project}"/>
			<arg line="-target ${native.target}"/>
			<arg line="-configuration ${env.CONFIGURATION}"/>
		</exec>		
        <copy file="${curr_build_dir}/${native.library}" toDir="${jars}" failonerror="true" verbose="true"/>
	</target> 

パッケージング

packageターゲットの依存関係にnativelibターゲットを追加し、*.jnilibをリソースとして追加するタスクを追加する。

	<target name="package" depends="nativelib" description="Make a double-clickable Mac OS X application">
		<mkdir dir="${dist}"/>
		<mkdir dir="${application.resources.java}"/>
		<mkdir dir="${application.macos}"/>
		<!-- copy jars -->
		<copy toDir="${application.resources.java}">
			<fileset dir="${jars}">
				<include name="*.jar"/>
				<include name="*.jnilib" />
			</fileset>
		</copy>

実行

以上で設定終わり。「ビルドして進行」ボタンを押せばJNIを利用したSwingアプリケーションができあがってるはず。

感想

LeopardになってからJavaが蔑ろにされてる気が…。

ところでJavaでJNI使ってる人ってまだいるのかしらん?

ダウンロード

念のため今回作ってみたプロジェクト。