LoginSignup
5
6

More than 3 years have passed since last update.

TornadoFXで書いたアプリをEXEファイルにして配布する

Last updated at Posted at 2019-05-16

はじめに

ここで言う配布とは会社等の内製ツールとしてチームへ配布する想定です。商売する等の目的で配布する場合はライセンスにご注意ください。

前回はFXLauncherで色々と試みましたが、exeファイルの生成には失敗したので、別の方法を模索してみました。こちらのページを参考に手を加えています。

結論

Gradleプラグインのus.kirchmeier.capsuleedu.sc.seis.launch4jを使って実現出来ました。exeファイルを生成する工程は前回も出てきたlaunch4jを使っています。

手順

順を追って説明していきます。IntelliJでTornadoFXプロジェクトを作成するところまでは前回と同じなので省きます。

[1] fat jarを作る

us.kirchmeier.capsuleを使ってfat jarを作成します。build.gradleに以下の変更を加えます。

version = '0.0.1'// 追加

buildscript {
    ext.kotlin_version = "1.2.60"
    ext.tornadofx_version = "1.7.17"
    ext.junit_version = "5.1.0"

    repositories {
        mavenLocal()
        mavenCentral()
        maven {
            url "https://plugins.gradle.org/m2/"// 追加
        }
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "org.junit.platform:junit-platform-gradle-plugin:1.1.0"
        classpath "us.kirchmeier:gradle-capsule-plugin:1.0.2" // 追加
    }
}

apply plugin: "kotlin"
apply plugin: "application"
apply plugin: "org.junit.platform.gradle.plugin"
apply plugin: "us.kirchmeier.capsule"// 追加

// ~中略~

// fatJarを生成するタスク
task fatJar(type: FatCapsule) {
    applicationClass mainClassName
}

この段階でコンソールから

gradle fatJar

をすると build/libs 以下にjarファイルが2つ生成されます。ファイル名のサフィックスに"-capsule"がついている方が目的のjarファイルです。

[2] exeファイルを作る

edu.sc.seis.launch4jを使って作成します。まずはbuild.gradleを以下のように変更します。

buildscript {
    ext.kotlin_version = "1.2.60"
    ext.tornadofx_version = "1.7.17"
    ext.junit_version = "5.1.0"

    repositories {
        mavenLocal()
        mavenCentral()
        maven {
            url "https://plugins.gradle.org/m2/"
        }
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "org.junit.platform:junit-platform-gradle-plugin:1.1.0"
        classpath "us.kirchmeier:gradle-capsule-plugin:1.0.2"
        classpath "edu.sc.seis.launch4j:launch4j:2.4.6" //追加
    }
}

apply plugin: "kotlin"
apply plugin: "application"
apply plugin: "org.junit.platform.gradle.plugin"
apply plugin: "us.kirchmeier.capsule"
apply plugin: "edu.sc.seis.launch4j"// 追加

// ~中略~

// exe化するタスク
createExe {
    headerType = 'gui' // cuiアプリならconsole
    copyConfigurable = project.tasks.fatJar.outputs.files
    jar = "lib/${project.tasks.fatJar.archiveName}" //対象のjarファイル(今回はfatJarタスクが生成したファイル)
    mainClassName = mainClassName
    bundledJrePath = 'jre-win' //実行時に参照するJREのパス(exeファイルからみた相対パス)
    bundledJre64Bit = true
    outputDir = "output"//exeファイルの出力先
}

createExeというタスクでexeファイルを生成します。

gradle createExe

クリーンビルドする場合は

gradle clean createExe

ビルドが成功すると、build/outputにexeファイルが出力されます。

オプションが色々とありますが、詳しいことはgradle-launch4jのgithubにある程度まとまっています。空欄のものもあるので全ては把握出来ませんが…。注意点はbundledJrePathで指定するパスはexeファイル実行時に参照するJREのパスです。ですので、このあとJREを同梱するタスクを追加しますが、そこで同梱したJREへのexeファイルからみた相対パスです。

[3] 配布用にまとめてzipでアーカイブする

build.gradleにタスクを追加します。

// アーカイブ時のテンポラリディレクトリ
ext {
    archiveTmpDir = "${buildDir}/${project.name}_tmp"
}

// アーカイブ前に対象のファイルをテンポラリディレクトリにコピーするタスク
task prePack(dependsOn: createExe) doLast {
    copy {
        from "${buildDir}/${createExe.outputDir}/${project.name}.exe"
        into archiveTmpDir
    }
    copy {
        from 'D:/Amazon Corretto/jre8'// 同梱するJREのパス。例えば左記のような感じ。
        into "${archiveTmpDir}/${createExe.bundledJrePath}"
    }
}

// zipでアーカイブ
task pack(type: Zip, dependsOn: prePack) {
    from archiveTmpDir
    archiveName "MyApp.zip"
}

この段階でコンソールで

gradle pack

クリーンビルドする場合は

gradle clean pack

とすると、build/distributions以下にMyApp.zipというzipファイルが出来上がります。このzipファイルを配布すればJREがインストールされていない環境でも動作するはずです。

まとめ

ひとまずTornadoFXで作成したアプリをJAR、更にはexeファイルにまとめるという目的は達しましたが、JREがまるごと配布ファイルに含まれるということでHello World程度のアプリケーションでもzipファイルが巨大です。もっといいパッケージングの方法があるかもしれません。

また、今回は非常にシンプルなアプリであるため上手く動いている可能性があります。複雑なGUIアプリケーションをTornadoFXで作成した場合に上手くいかないかもしれません。

最後にbuild.gradle全体を載せます。


version = '0.0.1'

buildscript {
    ext.kotlin_version = "1.2.60"
    ext.tornadofx_version = "1.7.17"
    ext.junit_version = "5.1.0"

    repositories {
        mavenLocal()
        mavenCentral()
        maven {
            url "https://plugins.gradle.org/m2/"
        }
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "org.junit.platform:junit-platform-gradle-plugin:1.1.0"
        classpath "us.kirchmeier:gradle-capsule-plugin:1.0.2"
        classpath "edu.sc.seis.launch4j:launch4j:2.4.6"
    }
}

apply plugin: "kotlin"
apply plugin: "application"
apply plugin: "org.junit.platform.gradle.plugin"
apply plugin: "us.kirchmeier.capsule"
apply plugin: "edu.sc.seis.launch4j"

compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

repositories {
    mavenLocal()
    mavenCentral()
    maven {
        url "https://oss.sonatype.org/content/repositories/snapshots/"
    }
}

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"

    compile "no.tornado:tornadofx:$tornadofx_version"

    testCompile "org.junit.jupiter:junit-jupiter-api:$junit_version"
    testRuntime "org.junit.jupiter:junit-jupiter-engine:$junit_version"
}

mainClassName = "com.example.demo.app.MyApp"

jar {
    manifest {
        attributes(
                "Class-Path": configurations.compile.collect { it.getName() }.join(" "),
                "Main-Class": mainClassName
        )
    }
    from(configurations.compile.collect { entry -> zipTree(entry) }) {
        exclude "META-INF/MANIFEST.MF"
        exclude "META-INF/*.SF"
        exclude "META-INF/*.DSA"
        exclude "META-INF/*.RSA"
    }
}

ext {
    archiveTmpDir = "${buildDir}/${project.name}_tmp"
}

task fatJar(type: FatCapsule) {
    applicationClass mainClassName
}

task prePack(dependsOn: createExe) doLast {
    copy {
        from "${buildDir}/${createExe.outputDir}/${project.name}.exe"
        into archiveTmpDir
    }
    copy {
        from '同梱したいJREへのパス' // 例えば D:/Amazon Corretto/jre8
        into "${archiveTmpDir}/${createExe.bundledJrePath}"
    }
}

task pack(type: Zip, dependsOn: prePack) {
    from archiveTmpDir
    archiveName "MyApp.zip"
}

createExe {
    headerType = 'gui'
    copyConfigurable = project.tasks.fatJar.outputs.files
    jar = "lib/${project.tasks.fatJar.archiveName}"
    mainClassName = mainClassName
    bundledJrePath = 'jre-win'
    bundledJre64Bit = true
    outputDir = "output"
}

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6