JavaFX app를 command로 빌드하기

오래 전 JavaFX 프로젝트에서 빌드를 IDE 대신에 커맨드로 빌드하도록 구축해 보았다.

JavaFX 란?

JavaFX는 자바로 윈도우용 GUI 프로그램을 만들기 위한 라이브러리로 기존의 AWT와 Swing을 대체하는 라이브러리이다. JavaFX는 UI 구성을 xml 파일로 작성한다. FXML로 UI를 작성하려면 Scene Builder를 설치하여 이용하면 된다.

빌드 환경 구성

Oracle JDK 1.8에는 JavaFX가 포함되어 있었다. (단, OpenJDK 1.8에는 포함되어 있지 않았음) 그런데 JDK11부터는 Oracle JDK, OpenJDK 모두 JDK에 JavaFX가 포함되어 있지 않고, 별도의 모듈로 분리되어 있다. 따라서 JDK를 설치하고, JavaFX SDK를 다운받아서 압축을 풀어서 설치해야 한다.

VS Code에서 빌드하기

이 프로젝트는 오래 전에 Oracle JDK 1.8과 JavaFX를 사용하여 이클립스에서 빌드했었다. VS Code에서 빌드하려면 .classpath 파일을 아래와 같이 작성하였다. (JDK11 설치 경로는 D:/javafx-sdk-11/)

<?xml version="1.0" encoding="UTF-8"?>
<classpath>
    <classpathentry kind="src" path="src"/>
    <classpathentry kind="output" path="build/classes"/>
    <classpathentry kind="lib" path="D:/javafx-sdk-11/lib/javafx.base.jar"/>
    <classpathentry kind="lib" path="D:/javafx-sdk-11/lib/javafx.controls.jar"/>
    <classpathentry kind="lib" path="D:/javafx-sdk-11/lib/javafx.fxml.jar"/>    
    <classpathentry kind="lib" path="D:/javafx-sdk-11/lib/javafx.graphics.jar"/>
    <classpathentry kind="lib" path="D:/javafx-sdk-11/lib/javafx.web.jar"/>
    <classpathentry kind="lib" path="lib/org.apache.commons.io.jar"/>
    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
</classpath>

그런데 실행하면 “Error: JavaFX runtime components are missing, and are required to run this application” 에러가 발생하였다. 구글링을 해보니, 이것은 실행시 vmArgs 아규먼트에 --module-path 옵션으로 JavaFX SDK 경로를 세팅하고, --add-modules 옵션으로 사용하는 JavaFX 모듈로 세팅하면 된다고 하였다. 이를 위해, launch.json 파일을 아래와 같이 작성하였다.

{
  "configurations": [
    {
      "type": "java",
      "name": "Launch Main",
      "request": "launch",
      "vmArgs": "--module-path D:/javafx-sdk-11/lib --add-modules javafx.controls,javafx.fxml,javafx.web",
      "mainClass": "hellofx.Main"
    }
  ]
}

참고로 Eclipse에서 빌드할 때는 Run -> Run Configuration… 메뉴를 실행한 후에, Java Application 항목을 생성한 후에, Argements 탭에서 VM arguments 항목에 --module-path D:/javafx-sdk-11.0.2/lib --add-modules=javafx.controls,javafx.fxml,javafx.web 내용을 추가하면 된다.

그런데 바로 실행은 정상적으로 실행되었지만, jar 파일을 생성해 보니 크기가 기존 JDK1.8을 사용했을 때보다 크게 나왔고, 실행도 정상적으로 되지 않았다. 그래서 그냥 command line으로 빌드하도록 구성해 보기로 하였다.

Command로 빌드하기 (JDK8)

JDK

JDK는 JavaFX를 포함하고 있는 Oracle JDK 1.8을 설치하였다.

Manifest.mf 파일

아래 예와 같이 Manifest.mf 파일을 작성하였다. (아래 예에서 패키지 명과 클래스 명은 mypackage.MyClass 임, 마지막 줄은 빈 라인이라야 함)

Class-Path: .
Main-Class: mypackage.MyClass

Makefile 파일

아래 예와 같이 Makefile 파일을 작성하였다.

SRC_DIR = src
LIB_DIR = lib
BIN_DIR = bin
RES_DIR = resources
OUT_DIR = out

TARGET = MyClass.jar

SRCS = $(wildcard $(SRC_DIR)/*.java)

all:
    @mkdir $(OUT_DIR)
    @cp -rf $(RES_DIR)/ $(OUT_DIR)/
    javac -cp "$(LIB_DIR)/*" -encoding utf-8 -d $(OUT_DIR) $(SRCS)
    jar -cmf Manifest.mf $(BIN_DIR)/$(TARGET) -C $(OUT_DIR) .
    @ls -lgGh $(BIN_DIR)/$(TARGET)

clean:
    @rm -rf $(OUT_DIR)/

빌드 및 실행 결과

이제 콘솔에서 make 명령을 실행하니 jar 파일이 생성되었고 정상적으로 실행도 잘 되었다. (이렇게 빌드하니 빌드도 편리할 뿐더러, 추가적으로 얻은 장점은 기존에 이클립스에서 jar 파일을 얻었을 때 보다도 약 2MB 정도 더 크기가 줄었다는 것이다.
이는 이클립스에서는 필요없는 파일까지 포함되었기 때문으로, 역시 IDE가 자동으로 빌드해 주는 것보다 수동으로 빌드 환경을 구축하는 것이 더 좋을 때가 있다. 😉)

물론 배포는 launch4j를 이용하여 exe 파일로 바꾸어서 배포하였다. 참고로 launch4j를 이용하면 bundle 된 JRE 폴더를 사용하게 할 수 있으므로 (없는 경우에는 시스템의 JRE를 사용하도록 fallback 옵션 추가), 해당 JRE가 설치되지 않은 환경을 지원하게 할 수 있다.

Command로 빌드하기 (JDK11 이상)

JDK

JDK11 부터는 JavaFX를 포함하고 있지 않아서, JavaFX를 사용하는 경우에는 JDK 뿐만 아니라 JavaFX SDK를 추가로 설치해야 한다. JavaFX SDK를 추가한 이후에는 PATH_TO_FX 환경 변수를 JavaFX SDK의 lib 경로로 세팅하였다.

Makefile 파일

아래 예와 같이 Makefile 파일을 작성하였다.

SRC_DIR = src
LIB_DIR = lib
BIN_DIR = bin
RES_DIR = resources
OUT_DIR = out

PACKAGE_NAME = mypackage
TARGET = MyClass.jar

JAVAFX_ARGS = --module-path $(PATH_TO_FX) --add-modules=javafx.controls,javafx.fxml,javafx.media,javafx.web
SRCS = $(wildcard $(SRC_DIR)/*.java)

all:
    @test -d $(OUT_DIR) || $(MAKE) --no-print-directory extract_jar
    @cp -rf $(RES_DIR)/ $(OUT_DIR)/
    javac -cp "$(LIB_DIR)/*" -encoding utf-8 $(JAVAFX_ARGS) -d $(OUT_DIR) $(SRCS)
    jar --create --file=$(BIN_DIR)/$(TARGET) --main-class=$(PACKAGE_NAME).Launcher -C $(OUT_DIR) .
    @ls -lgGh $(BIN_DIR)/$(TARGET)

extract_jar:
    @mkdir $(OUT_DIR)
    @cd $(OUT_DIR) && \
        jar xf $(PATH_TO_FX)/javafx.base.jar && \
        jar xf $(PATH_TO_FX)/javafx.controls.jar && \
        jar xf $(PATH_TO_FX)/javafx.fxml.jar && \
        jar xf $(PATH_TO_FX)/javafx.graphics.jar && \
        jar xf $(PATH_TO_FX)/javafx.media.jar
    @cp -af $(PATH_TO_FX)/../bin/glass.dll $(OUT_DIR)/
    @cp -af $(PATH_TO_FX)/../bin/javafx*.dll $(OUT_DIR)/
    @cp -af $(PATH_TO_FX)/../bin/prism*.dll $(OUT_DIR)/
    @rm -rf $(OUT_DIR)/META-INF $(OUT_DIR)/module-info.class

clean:
    @rm -rf $(OUT_DIR)/

빌드 및 실행 결과

이제 콘솔에서 make 명령을 실행하면 jar 파일이 생성되고 정상적으로 잘 실행된다. (참고로 JavaFX WebView를 사용하는 경우에는 위 Makefile에서 javafx.web.jar 파일과 jfxwebkit.dll 부분을 추가해 주면 된다)

카테고리:

업데이트: