2011/05/16

全Linux生活:第16天 - 在Linux下編譯Windows DLLs

From Evernote:

全Linux生活:第16天 - 在Linux下編譯Windows DLLs

最近工作上要做一個Windows下的JNI DLL,這原本在純Windows下是很簡單的事,但在Linux下,是有那麼一點麻煩。在Linux下要做cross platform compile,有幾個選擇:MingGW,Cygwin。但如果考量到team work的話,要強迫其他人也要使用Linux來寫程式也說不過去,所以目前暫時不考慮"純種"的cross platform compilation,我的作法是使用Windows下的C/C++ Compiler來進行編譯,考量到Comiler大小與不要有runtime相依性問題,那選擇有3個Visual C++ 6.0,MinGW Windows版,Borland C++ 5.5(什麼﹍這鬼東西還有人用嗎?)。當然,可以免費使用的只有MinGW與Borland C++ 5.5,而且Size最小的是Borland C++ 5.5,只需要50MB左右,最大的是Visual C++ 6.0,約180MB,第二名是MinGW,約120MB。

可是這些東西在Linux下可以用嗎?當然是不能直接用,但是透過WINE就可以。這樣做最大的好處是同一個專案可以在Linux與Windows下通用(甚至Mac OS X,但一樣要裝WINE才行)。

不囉唆,先準備JNI環境:
  1. 首先在Eclipse建立一個HelloWorldJNI專案
  2. 建立目錄結構如下圖:
    helloworldjni_structure
    其中jdk_include目錄內的檔案請從32位元Windows版的JDK中複製取得。

如果想用Visual C++ 6.0,請服用:
  1. 取得VIsual C++ 6.0:噓﹍不要問很恐怖﹍
  2. 安裝Visual C++ 6.0:

    $ wine <PATH-TO-MSVC60-CD>/setup.exe

  3. 在Eclipse建立MSVC60專案(General Project就可以了)
  4. 複製Compiler檔案:

     $ cp -rvf "~/.wine/drive_c/Program Files/Microsoft Visual Studio/Common" <PATH-TO-MSVC60-PROJECT>
    $ cp -rvf "~/.wine/drive_c/Program Files/Microsoft Visual Studio/VC98" <PATH-TO-MSVC60-PROJECT>

    msvc60_structure

  5. 複製並修改vcvars32.bat:

    $ cp <PATH-TO-MSVC60-PROJECT>/VC98/Bin/VCVARS32.BAT <PATH-TO-HELLOWORLDJNI-PROJECT>/src/jni/bin/vcvars32.bat
    $ vi <PATH-TO-HELLOWORLDJNI-PROJECT>/src/jni/bin/vcvars32.bat

    @echo off
    set MSVC60_HOME=%CD%\..\..\..\msvc60

    rem
    rem Root of Visual Developer Studio Common files.
    set VSCommonDir=%MSVC60_HOME%\Common

    rem
    rem Root of Visual Developer Studio installed files.
    rem
    set MSDevDir=%MSVC60_HOME%\Common\msdev98

    rem
    rem Root of Visual C++ installed files.
    rem
    set MSVCDir=%MSVC60_HOME%\VC98

    rem
    rem VcOsDir is used to help create either a Windows 95 or Windows NT specific path.
    rem
    set VcOsDir=WIN95
    if "%OS%" == "Windows_NT" set VcOsDir=WINNT

    rem
    echo Setting environment for using Microsoft Visual C++ tools.
    rem

    if "%OS%" == "Windows_NT" set PATH=%MSDevDir%\BIN;%MSVCDir%\BIN;%VSCommonDir%\TOOLS\%VcOsDir%;%VSCommonDir%\TOOLS;%PATH%
    if "%OS%" == "" set PATH="%MSDevDir%\BIN";"%MSVCDir%\BIN";"%VSCommonDir%\TOOLS\%VcOsDir%";"%VSCommonDir%\TOOLS";"%windir%\SYSTEM";"%PATH%"
    set INCLUDE=%MSVCDir%\ATL\INCLUDE;%MSVCDir%\INCLUDE;%MSVCDir%\MFC\INCLUDE;%INCLUDE%
    set LIB=%MSVCDir%\LIB;%MSVCDir%\MFC\LIB;%LIB%

    set VcOsDir=
    set VSCommonDir=

  6. 建立cl.bat:

    $ vi <PATH-TO-HELLOWORLDJNI-PROJECT>/src/jni/bin/cl.bat

    echo off
    @call bin\vcvars32.bat
    cl %*

  7. 編輯build.xml:

    $ vi <PATH-TO-HELLOWORLDJNI-PROJECT>/build.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project name="Packaging Generator" default="all">

        <property name="dir.src" value="src" />
        <property name="dir.src.java" value="${dir.src}/java" />
        <property name="dir.src.junit" value="${dir.src}/junit" />
        <property name="dir.src.jni" value="${dir.src}/jni" />
        <property name="dir.src.jdkinclude" value="${dir.src}/jni/jdk_include" />
        <property name="dir.etc" value="etc" />
        <property name="dir.lib" value="lib" />
        <property name="dir.deploy" value="deploy" />
        <property name="dir.classes" value="tmp" />
        <property name="dir.build" value="build" />

        <target name="all">
            <antcall target="HelloWorld.dll" />
        </target>

        <target name="HelloWorld.dll">
            <property name="moduleName" value="HelloWorld" />
            <mkdir dir="${dir.deploy}" />
            <delete>
                <fileset dir="${dir.src.jni}" includes="${moduleName}.*" />
            </delete>
            <javah class="test.${moduleName}" outputFile="${dir.src.jni}/${moduleName}.h" classpath="${dir.classes}" />
            <exec dir="${dir.src.jni}" executable="wine" os="Linux,Mac OS X">
                <arg line="cmd /c bin\\cl -Ijdk_include -Ijdk_include\\win32 -I${moduleName} -MD -LD ${moduleName}\\${moduleName}.c -Fe${moduleName}.dll" />
            </exec>
            <exec dir="${dir.src.jni}" executable="cmd" os="Windows 95,Windows 98,Windows NT,Windows 2000,Windows XP,Windows Vista,Windows 7">
                <arg line="/c bin\\cl -Ijdk_include -Ijdk_include\\win32 -I${moduleName} -MD -LD ${moduleName}\\${moduleName}.c -Fe${moduleName}.dll" />
            </exec>
            <copy todir="${dir.deploy}" overwrite="true" failonerror="false">
                <fileset dir="${dir.src.jni}">
                    <include name="${moduleName}.dll" />
                    <include name="${moduleName}.lib" />
                </fileset>
            </copy>
            <delete>
                <fileset dir="${dir.src.jni}">
                    <include name="${moduleName}.*" />
                </fileset>
            </delete>
        </target>

    </project>

  8. 執行build.xml,產生HelloWorld.dll:

    Screenshot-Java - HelloWorldJNI-build.xml - Eclipse

如果想用MingGW,請服用:
  1. 取得MingGW:MinGW官網

    $ wget http://sourceforge.net/projects/mingw/files/Automated%20MinGW%20Installer/mingw-get-inst/mingw-get-inst-20110316/mingw-get-inst-20110316.exe/download


  2. 安裝MingGW:

    $ wine <PATH-TO-DOWNLOAD-DIR>/mingw-get-inst-20110316.exe

  3. 在Eclipse建立MINGW專案(General Project就可以了)
  4. 複製Compiler檔案:

     $ cp -rvf ~/.wine/drive_c/MingGW/* <PATH-TO-MINGW-PROJECT>

    mingw_structure

  5. 建立mingwenv.bat:
     
    $ vi <PATH-TO-HELLOWORLDJNI-PROJECT>/src/jni/bin/mingwenv.bat

    @echo off
    set MINGW_HOME=..\..\..\mingw
    set PATH=%MINGW_HOME%\bin;%MINGW_HOME%\mingw32\bin;%PATH%
     
  6. 建立gcc.bat:

    $ vi <PATH-TO-HELLOWORLDJNI-PROJECT>/src/jni/bin/gcc.bat

    echo off
    @call bin\mingwenv.bat
    gcc %*

  7. 編輯build.xml:

    $ vi <PATH-TO-HELLOWORLDJNI-PROJECT>/build.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project name="Packaging Generator" default="all">

        <property name="dir.src" value="src" />
        <property name="dir.src.java" value="${dir.src}/java" />
        <property name="dir.src.junit" value="${dir.src}/junit" />
        <property name="dir.src.jni" value="${dir.src}/jni" />
        <property name="dir.src.jdkinclude" value="${dir.src}/jni/jdk_include" />
        <property name="dir.etc" value="etc" />
        <property name="dir.lib" value="lib" />
        <property name="dir.deploy" value="deploy" />
        <property name="dir.classes" value="tmp" />
        <property name="dir.build" value="build" />

        <target name="all">
            <antcall target="HelloWorld.dll" />
        </target>

        <target name="HelloWorld.dll">
            <property name="moduleName" value="HelloWorld" />
            <mkdir dir="${dir.deploy}" />
            <delete>
                <fileset dir="${dir.src.jni}" includes="${moduleName}.*" />
            </delete>
            <javah class="test.${moduleName}" outputFile="${dir.src.jni}/${moduleName}.h" classpath="${dir.classes}" />
            <exec dir="${dir.src.jni}" executable="wine" os="Linux,Mac OS X">
                <arg line="cmd /c bin\\gcc -Wall -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -Ijdk_include -Ijdk_include\\win32 -I${moduleName} -shared ${moduleName}\\${moduleName}.c -o ${moduleName}.dll" />
            </exec>
            <exec dir="${dir.src.jni}" executable="cmd" os="Windows 95,Windows 98,Windows NT,Windows 2000,Windows XP,Windows Vista,Windows 7">
                <arg line="/c bin\\gcc -Wall -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -Ijdk_include -Ijdk_include\\win32 -I${moduleName} -shared ${moduleName}\\${moduleName}.c -o ${moduleName}.dll" />
            </exec>
            <copy todir="${dir.deploy}" overwrite="true" failonerror="false">
                <fileset dir="${dir.src.jni}">
                    <include name="${moduleName}.dll" />
                    <include name="${moduleName}.lib" />
                </fileset>
            </copy>
            <delete>
                <fileset dir="${dir.src.jni}">
                    <include name="${moduleName}.*" />
                </fileset>
            </delete>
        </target>

    </project>

  8. 執行build.xml,產生HelloWorld.dll:

    Screenshot-Java - HelloWorldJNI-build.xml - Eclipse -1


如果想用Borland C++ 5.5,請服用:
  1. 取得Borland C++ 5.5:Codegear官網
  2. 安裝Borland C++ 5.5:

    $ wine <PATH-TO-DOWNLOAD-DIR>/freecommandLinetools.exe

  3. 在Eclipse建立BCC55專案(General Project就可以了)
  4. 複製Compiler檔案:

     $ cp -rvf ~/.wine/drive_c/Borland/BCC55/* <PATH-TO-BCC55-PROJECT>

    bcc55_structure

  5. 建立bcc55env.bat:
     
    $ vi <PATH-TO-HELLOWORLDJNI-PROJECT>/src/jni/bin/bcc55env.bat

    @echo off
    set BCC55_HOME=..\..\..\bcc55
    set PATH=%BCC55_HOME%\bin;%PATH%
     
  6. 建立bcc32.bat:

    $ vi <PATH-TO-HELLOWORLDJNI-PROJECT>/src/jni/bin/bcc32.bat

    echo off
    @call bin\bcc55env.bat
    bcc32 %*

  7. 編輯build.xml:

    $ vi <PATH-TO-HELLOWORLDJNI-PROJECT>/build.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project name="Packaging Generator" default="all">

        <property name="dir.src" value="src" />
        <property name="dir.src.java" value="${dir.src}/java" />
        <property name="dir.src.junit" value="${dir.src}/junit" />
        <property name="dir.src.jni" value="${dir.src}/jni" />
        <property name="dir.src.jdkinclude" value="${dir.src}/jni/jdk_include" />
        <property name="dir.etc" value="etc" />
        <property name="dir.lib" value="lib" />
        <property name="dir.deploy" value="deploy" />
        <property name="dir.classes" value="tmp" />
        <property name="dir.build" value="build" />

        <target name="all">
            <antcall target="HelloWorld.dll" />
        </target>

        <target name="HelloWorld.dll">
            <property name="moduleName" value="HelloWorld" />
            <mkdir dir="${dir.deploy}" />
            <delete>
                <fileset dir="${dir.src.jni}" includes="${moduleName}.*" />
            </delete>
            <javah class="test.${moduleName}" outputFile="${dir.src.jni}/${moduleName}.h" classpath="${dir.classes}" />
            <exec dir="${dir.src.jni}" executable="wine" os="Linux,Mac OS X">
                <arg line="cmd /c bin\\bcc32 -Ijdk_include -Ijdk_include\\win32 -I..\\..\\..\\bcc55\\Include -L..\\..\\..\\bcc55\\Lib -I${moduleName} -WD ${moduleName}\\${moduleName}.c -o${moduleName}.dll" />
            </exec>
            <exec dir="${dir.src.jni}" executable="cmd" os="Windows 95,Windows 98,Windows NT,Windows 2000,Windows XP,Windows Vista,Windows 7">
                <arg line="/c bin\\bcc32 -Ijdk_include -Ijdk_include\\win32 -I..\\..\\..\\bcc55\\Include -L..\\..\\..\\bcc55\\Lib -I${moduleName} -WD ${moduleName}\\${moduleName}.c -o${moduleName}.dll" />
            </exec>
            <copy todir="${dir.deploy}" overwrite="true" failonerror="false">
                <fileset dir="${dir.src.jni}">
                    <include name="${moduleName}.dll" />
                    <include name="${moduleName}.lib" />
                </fileset>
            </copy>
            <delete>
                <fileset dir="${dir.src.jni}">
                    <include name="${moduleName}.*" />
                </fileset>
            </delete>
        </target>

    </project>
     
  8. 執行build.xml,產生HelloWorld.dll:

    Screenshot-Java - HelloWorldJNI-build.xml - Eclipse -2


No comments: