# GNUMake snippet for java compilation and execution # # SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: 2024-2025 Jonas Smedegaard # # Setup: # In main Makefile... # * set variable JAVA_PROJECTMODULES, or PROJECT for one simply named # * set variable JAVA_MAINCLASSES with at least one main()-method class, # where the first one listed will be used by default # * set variables JAVA_CLASSPATHS JAVA_MODULEPATHS JAVA_MODULESOURCEPATH # JAVA_ROOT JAVA_EXTRACLASSES JAVA_MODULES JAR_FILE # if needed, # * include this make snippet # # Variables may add suffix _$(PROJECTMODULE), in full or "host-only", # e.g. JAVA_MODULES_org.example.mydemo or JAVA_MODULES_mydemo. # resolve Java version JAVA_MAJOR_VERSION = $(shell $(JAVA) -version 2>&1 \ | grep -Pom1 '"\K\d+' ) # defaults overridable at make invocation JAVA_ROOT ?= src/main/java JAVAC ?= javac JAVA ?= java JAR ?= jar # check javadoc only with JDK 23+ supporting Markdown-flavored javadoc ifeq ($(shell expr $(JAVA_MAJOR_VERSION) \>= 23), 1) JAVACFLAGS ?= -Xlint -Xdoclint -implicit:none else JAVACFLAGS ?= -Xlint -implicit:none endif # account for one annoyingly popular class of non-POSIX system _java_path_separator := $(if $(filter Windows%,$($(shell uname))),;,:) _java_module_separator = , # expansions optionally stem- or last-word-of-stem-based # first try fully suffixed, then "host"-suffixed, then bare variable _java_projvar = $(strip $(foreach v,$1,$(or \ $(if $(or $2,$*),$(or \ $(value $v_$(or $2,$*)),\ $(value $v_$(lastword $(subst ., $(),$(or $2,$*)))))),\ $(value $v)))) _java_root = $(call _java_projvar,JAVA_ROOT) _java_flag = $(eval x = $(call _java_projvar,$2))$(strip $(if $4$x$5,\ --$1 $(subst $() ,$(value _java_$3_separator),$(strip $4 $x $5)))) _java_common_flags = $(strip \ $(call _java_flag,class-path,JAVA_CLASSPATHS,path) \ $(call _java_flag,module-path,JAVA_MODULEPATHS,path) \ $(call _java_flag,add-modules,JAVA_MODULES,module)) # projectmodules with default main function _java_mainclassmodules = $(strip \ $(foreach p,$(JAVA_PROJECTMODULES),\ $(if $(call _java_projvar,JAVA_MAINCLASSES,$p),$p))) # projectmodules with non-default main function expanded, dot-appended _java_extramainclassmodules = $(strip \ $(foreach p,$(JAVA_PROJECTMODULES),\ $(eval classes = $(call _java_projvar,JAVA_MAINCLASSES,$p))\ $(foreach c,$(filter-out $(firstword $(classes)),$(classes)),\ $p.$(subst /,.,$c)))) # TODO: formally define a builddir, and drop that on clean #clean:: # rm -f $(CLASSES) $(JAVA_PROJECTMODULES:%=build-%): build-%: rm -rf mods/$* $(JAVAC) $(strip \ $(call _java_flag,module-source-path,JAVA_MODULESOURCEPATH) \ $(_java_common_flags) $(JAVACFLAGS) \ $(call _java_projvar,JAVACFLAGS) \ -d mods/$* \ $(wildcard $(_java_root)/module-info.java) \ $(foreach c,\ $(call _java_projvar,JAVA_MAINCLASSES JAVA_EXTRACLASSES),\ $(patsubst %,$(_java_root)/%.java,\ $(subst .,/,\ $(if $(wildcard $(_java_root)/module-info.java),$*/))$c))) $(JAVA_PROJECTMODULES:%=check-%): check-%: checkstyle -c _checkstyle/checks.xml $(strip \ $(foreach c,\ $(call _java_projvar,JAVA_MAINCLASSES JAVA_EXTRACLASSES),\ $(_java_root)/$(subst .,/,$*)/$c.java)) $(JAVA_PROJECTMODULES:%=jar-%): jar-%: build-% $(JAR) --create $(strip \ --file $(or $(call _java_projvar,JAR_FILE),$*.jar) \ $(addprefix --main-class $*.,\ $(firstword $(call _java_projvar,JAVA_MAINCLASSES))) \ $(call _java_projvar,JARFLAGS) \ -C mods/$* .) $(addprefix runsrc-,$(_java_mainclassmodules)): runsrc-%: $(strip \ $(call _java_projvar,JAVA_RUNTIME_ENV) \ $(JAVA) \ $(_java_common_flags) \ $(call _java_projvar,JAVAFLAGS) \ $(patsubst %,$(_java_root)/%.java,\ $(subst .,/,$(or \ $(_java_main_class),\ $(strip $(if $(wildcard $(_java_root)/module-info.java),$*/)\ )$(firstword $(call _java_projvar,JAVA_MAINCLASSES))))) \ $(call _java_projvar,JAVA_RUNTIME_ARGS)) $(addprefix runclass-,$(_java_mainclassmodules)): runclass-%: build-% $(strip \ $(eval JAVA_MODULEPATHS_$* = $(strip \ mods/$* $(call _java_projvar,JAVA_MODULEPATHS)))\ $(call _java_projvar,JAVA_RUNTIME_ENV) \ $(JAVA) \ $(_java_common_flags) \ $(call _java_projvar,JAVAFLAGS) \ -m $*/$(or $(_java_main_class),\ $*.$(firstword $(call _java_projvar,JAVA_MAINCLASSES))) \ $(call _java_projvar,JAVA_RUNTIME_ARGS)) $(addprefix runjar-,$(_java_mainclassmodules)): runjar-%: jar-% $(strip \ $(call _java_projvar,JAVA_RUNTIME_ENV) \ $(JAVA) \ $(call _java_flag,class-path,JAVA_CLASSPATHS,path) \ $(call _java_flag,module-path,JAVA_MODULEPATHS,path,\ $(or $(call _java_projvar,JAR_FILE),$*.jar)) \ $(call _java_projvar,JAVAFLAGS) \ --module $*$(_java_main_class:%=/%) \ $(call _java_projvar,JAVA_RUNTIME_ARGS)) $(addprefix runsrc-,$(_java_extramainclassmodules)): runsrc-%: $(strip _java_main_class=$* \ $(MAKE) runsrc-$(strip $(foreach p,$(JAVA_PROJECTMODULES),\ $(if $(filter $p%,$*),$p)))) $(addprefix runclass-,$(_java_extramainclassmodules)): runclass-%: $(strip _java_main_class=$* \ $(MAKE) runclass-$(strip $(foreach p,$(JAVA_PROJECTMODULES),\ $(if $(filter $p%,$*),$p)))) $(addprefix runjar-,$(_java_extramainclassmodules)): runjar-%: $(strip _java_main_class=$* \ $(MAKE) runjar-$(strip $(foreach p,$(JAVA_PROJECTMODULES),\ $(if $(filter $p%,$*),$p)))) .PHONY: \ $(foreach p,$(JAVA_PROJECTMODULES),build-$p check-$p) \ $(foreach p,$(_java_mainclassmodules) $(_java_extramainclassmodules),\ runclass-$p runsrc-$p runjar-$p)