Launch4jはjavaを起動するexeを作成する。
このjava起動部のコードは「head」と呼ばれる部分であり、これはMinGWのgccでコンパイルされている。
Launch4jのツールでexeを作成する際には、このコンパイル済みの*.o, *.aファイルをリンクしてexeが作成される。
この*.o, *.aファイルは何も指定しなければ標準のものが使われるが、明示的に自分がビルドしたものに差し替えることができる。
本稿では、Launch4jのver3.12の標準のheadをカスタマイズし、独自のheadによるjava起動を実現する方法を示すものである。
Launch4jのheadのカスタマイズは、カスタマイズの仕組みが用意されており、それが可能であることは明かであるが、実際の手順については説明がなく、ネット上にも、それを説明した文書がみつからなかったためである。
おそらく、headのカスタマイズにはC言語とWin32を少し知っていれば決して難解ではないためであろうが、しかし、それを行うためには、まず開発環境の準備が必要で、その準備には、いつかの注意ポイントがある。
- C言語の処理系とバージョン = MinGWの32版のgcc 4.6.1
- 想定している開発環境(IDE) = Dev-C++ 5.0あたり
- Maven Pluginでheadのビルドをサポートするバージョン = com.akathist.maven.plugins.launch4:launch4j-maven-plugin の 1.7.25
これらについて説明するものである。
また、Launch4jのバージョンが上がって、headに使うgcc処理系が変わったのであれば、これらの開発環境も変わるものと思われる。
ここで想定しているのは、Launch4j 3.12 (このバージョンに対応するmavenプラグインとしては、com.akathist.maven.plugins.launch4:launch4j-maven-plugin の 1.7.25) である。
Launch4j ver3.12のhead部(Java起動部)は、MinGWを使ってコンパイルされている。(VC++系ではない)
Launch4j本体は、*.o, *.aファイルをリンクしたり、各種設定値をリソースファイルに格納するため、MinGWのツールセットのうち、
- ld.exe (リンカー)
- windres.exe (リソースコンパイラ)
の2つを同梱している。
このMinGWは、binutils 2.22
である。1
このMinGWのバージョンに対する、gccは、おそらく、gcc 4.6.2
あたりである。
また、Launch4jが生成するexeは常に32ビットである。2
開発ツールとしては、Dev-C++ 5.0.0.9 あたりの32ビット版gcc4.6.2入りを使うのが良い。
(ポータブル版のDev-C++ 5.0.0.9 あたりを使うとレジストリやAPPDATAフォルダを汚さない。)
https://sourceforge.net/projects/orwelldevcpp/files/Portable%20Releases/
launch4jのソースを入手し、展開する。
git clone https://git.code.sf.net/p/launch4j/git launch4j
そのうち、以下のフォルダがheadに関連するフォルダである。
\launch4j
├─head
├─head_src
│ ├─consolehead
│ └─guihead
└─w32api
head_src
がソースの位置となる。
- head コンパイル済み*.oファイル (*.devのプロジェクトの出力先)
- head_src ヘッド部のソース
- w32api コンパイル済み*.oファイルとリンクされるライブラリ*.aファイル (コンパイル時のgccのバージョンと一致したもの)
これを自分の作業フォルダを作ってコピーする。
(なお、Launch4jのheadはMITライセンスで公開されている。)
ソースフォルダは以下のようになっている。
(ここではベータ版であるjniヘッドは使わないので無視している)
\launch4j\head_src
│ head.c
│ head.h
│ LICENSE.txt
│ resource.h
│
├─consolehead
│ .gitignore
│ consolehead.c
│ consolehead.dev
│ Makefile.win
│
└─guihead
.gitignore
guihead.c
guihead.dev
guihead.h
Makefile.win
このうち、*.dev がDev-C++のプロジェクトファイルである。
たとえば、GUIヘッド
をビルドしたいのであれば、head_src\guihead\guihead.dev
を開く。
Consoleヘッド
であれば、head_src\consolehead\consolehead.dev
となる。
しかし、どちらで開いても共通部は、head_src\head.c
に記述されている。
Windowsアプリとして起動する部分(WinMain
)があるのが、GUIヘッドのguihead.c
で、
コンソールアプリとして起動する部分(main
)があるのが、CONSOLEヘッドのconsolehead.c
である。
設定値の読み込みやJAVAの起動といった主要な機能は、すべてhead.c
に記載されている。
プロジェクト設定は*.devファイルに設定済みであり、また、ビルド手順であるMakefile.win
も、そのまま使える。3
まずは、この*.devをDev-C++
で開いて、そのままコンパイルできることを確認する。
ビルドした*.oファイルを、Mavenの com.akathist.maven.plugins.launch4:launch4j-maven-plugin
プラグインで使う場合は、以下のように感じで指定できる。
この場合、必ず、バージョン1.7.25
以降のプラグインを使うこと。4
<plugin>
<!-- Launch4jによるjarファイルのexe化を行う. http://launch4j.sourceforge.net/docs.html -->
<groupId>com.akathist.maven.plugins.launch4j</groupId>
<artifactId>launch4j-maven-plugin</artifactId>
<version>1.7.25</version>
<executions>
<execution>
<id>l4j-gui</id>
<phase>package</phase>
<goals>
<goal>launch4j</goal>
</goals>
<configuration>
<headerType>gui</headerType>
<outfile>target/${project.artifactId}.exe</outfile>
<jar>target/${project.artifactId}.jar</jar>
<errTitle>Failed to execute the ${project.artifactId}</errTitle>
<downloadUrl>https://adoptopenjdk.net/</downloadUrl>
<supportUrl>https://github.com/seraphy/Launch4jHead</supportUrl>
<objs>
<obj>src/Launch4JStub/w32api/crt2.o</obj>
<obj>src/Launch4JStub/head/head.o</obj>
<obj>src/Launch4JStub/head/guihead.o</obj>
</objs>
<libs>
<lib>src/Launch4jStub/w32api/libmingw32.a</lib>
<lib>src/Launch4jStub/w32api/libgcc.a</lib>
<lib>src/Launch4jStub/w32api/libmsvcrt.a</lib>
<lib>src/Launch4jStub/w32api/libkernel32.a</lib>
<lib>src/Launch4jStub/w32api/libuser32.a</lib>
<lib>src/Launch4jStub/w32api/libadvapi32.a</lib>
<lib>src/Launch4jStub/w32api/libshell32.a</lib>
<lib>src/Launch4jStub/w32api/libshfolder.a</lib>
</libs>
<jre>
<path>jre</path>
k <minVersion>1.8.0_60</minVersion>
<initialHeapSize>64</initialHeapSize>
<maxHeapSize>72</maxHeapSize>
<runtimeBits>64/32</runtimeBits>
</jre>
<versionInfo>
<fileVersion>${project.version}</fileVersion>
<txtFileVersion>${project.version}</txtFileVersion>
<fileDescription>${project.artifactId} ${project.version}</fileDescription>
<copyright><![CDATA[${maven.build.timestamp} ${project.developers[0].id}]]></copyright>
<productVersion>${project.version}</productVersion>
<txtProductVersion>${project.version}</txtProductVersion>
<productName>${project.artifactId}</productName>
<internalName>${project.artifactId}</internalName>
<originalFilename>${project.artifactId}.exe</originalFilename>
</versionInfo>
</configuration>
</execution>
</executions>
</plugin>
この例では、src\Launch4jStub
フォルダの下にhead
とw32api
フォルダをコピーしてきている。
launch4j-maven-plugin
のドキュメントには、headのカスタマイズのためのobjs, libsタグの説明がほとんどないが、ソースコード上にはLaunch4jにパラメータを引き渡す処理が記述されており、上記例のように、objs, libsタグによって指定する。ここで指定されたファイルは、このmavenプラグインによって、Launch4jの作業ディレクトリにコピーされる。(Launch4j側では自分のワーク下にある*.o, *.a以外は不正と判定する処理が入っているため、必ず作業ディレクトリにコピーしなければならない。1.7.24までは、この処理に不備があるため、1.7.25以降を使う必要がある。)
src\Launch4jStub\head
フォルダには、先のDev-C++でビルドした結果得られた、*.oファイルと、共通ランタイムの```crt2.o``をいれる。
src\Launch4jStub\w32api
フォルダには、Dev-C++で参照しているgccのバージョンのライブラリ*.aのうち、必要なものをコピーする。
コンパイルに使ったバージョンと異なるライブラリとリンクさせると、リンクに失敗するか、リンクできたとしても動きがおかしくなるので、必ず、コンパイルに使ったgccのものでコピーしなおすこと。
標準では
- libgcc.a
- libmingw32.a
- libmsvcrt.a
- libkernel32.a
- libuser32.a
- libadvapi32.a
- libshell32.a
が使われており、head.cで使うWin32APIによっては、必要なライブラリは増減する。
なお、libgcc.a
はWin32のライブラリではなく、gccのライブラリであり、*.aの場所も異なる。
MinGW32\lib\gcc\mingw32\4.6.1\libgcc.a
Win32のライブラリとlibmingwの*.aファイルは、だいたい以下にある。
MinGW32\lib\*.a
これでMavenを実行してexeを作成させてみる。
リンクに失敗した場合は、*.aファイルと*.oファイルのgccバージョン不一致等々が考えられる。
上記の手順で自分で用意した開発環境でのヘッドのビルドと、Launch4jでのexe化が成功したら、自分の好きなようにheadをカスタマイズをできる準備が整ったことになる。
この開発環境の準備さえできれば、あとは簡単だといえる。
head.c
に、ほとんどの処理がかかれており、コード行数は1600行前後にすぎないので、俯瞰するのに、それほど手間はかからない。
また、書かれているコードもつとめて平易に書かれており、トリッキーなことはなく、どこかを叩くと、どこかが飛び出るような不可思議なコードではないので、安心してカスタマイズできるはずである。
Java11時代になり、OracleからのJRE/JDKの無償配布が終了したり、クライアント向けJREの配布がなくなったりして、従来のように、ユーザーがJAVAランタイムをインストールするとレジストリにJRE/JDKの在処が記録される、というような運用が期待できなくなりつつある。
Launch4jはバンドルJRE、レジストリによるJRE/JDKの探索、といった方法でJAVAランタイムを探しに行くが、第3の方法として、いずれも見つからなかった場合には、ユーザーに直接、JAVA_HOMEの場所を選択してもらう という処理を入れたくなったので、ヘッドをカスタマイズしてみた。
https://github.com/seraphy/Launch4jHead
このほかにも、起動するjava.exe, javaw.exeが32/64ビットのいずれかであるかを示す、特殊変数%JRE_ARCH% を用意して、javaから参照する外部ソフトのdllの参照時に32ビット版のdllを使うか、64ビット版のdllを使うかを分けられるようにする、といった処理ができるようにもしている。5
以上、メモ終了
Footnotes
-
http://launch4j.sourceforge.net/ の Info の節参照 ↩
-
head.cで自身が64ビットOSではwow64で動作していることを前提としたコードがある。また、Launch4jが生成するexeは32ビットだが、javaを起動するときにはhead.cでCreateProcess APIを使っているため、32/64ビット版どちらのJavaも起動できる。 ↩
-
Dev-C++の標準ではexeファイルを作成するビルド手順を自動的に作成するが、Launch4j用ヘッドは*.oを得るのが目的であるため、独自のMakefile.winを用意してexeは生成しないようになっている。 ↩
-
1.7.24以前は、このobjs, libsタグで指定したソースを作業ディレクトリにコピーする処理に不具合があり、カスタムビルドを実質的に使用できなかったため。プルリクエストして修正してもらいました。(Launch4j付属のAntタスクでビルドする場合には特に注意点はありません。) ↩
-
たとえば、環境変数PATHと、システムプロパティjava.library.pathに、それらの情報を入れないと動かないようなレガシーなソフトであっても、Launch4jプラグインのvarsタグとjre/optsタグと、カスタム化で追加した特殊変数%JRE_ARCH%を併用することで、うまくPATHやjava.library.pathの設定ができるようになる。 ↩