diff --git a/Makefile b/Makefile index 3783aa641d..c4c6ee8538 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ ifneq "$(wildcard ${SORT_DIRECTORY_ORDER})" "${SORT_DIRECTORY_ORDER}" SORT_DIRECTORY_ORDER := sort endif -JAVA_RELEASE_NUMBER := $(shell java -version 2>&1 | head -1 | cut -d'"' -f2 | sed '/^1\./s///' | cut -d'.' -f1 | sed 's/-ea//') +include ${DAIKONDIR}/java/java-home.mk ########################################################################## @@ -75,7 +75,7 @@ SCRIPT_PATHS := $(addprefix scripts/,${SCRIPT_FILES}) # This is so troublesome that it isn't used except as a list of dependences for make commands DAIKON_JAVA_FILES := $(shell find java -name '*daikon-java*' -prune -o -name '*.java' -print | ${SORT_DIRECTORY_ORDER}) # If we're building on JDK 23 or less, remove the java24 files from the list. -ifeq ($(shell test ${JAVA_RELEASE_NUMBER} -lt 24; echo $$?),0) +ifneq ($(call java_at_least,24),1) DAIKON_JAVA_FILES := $(filter-out %24.java, ${DAIKON_JAVA_FILES}) endif @@ -624,7 +624,7 @@ daikon.jar: ${DAIKON_JAVA_FILES} $(patsubst %,java/%,${DAIKON_RESOURCE_FILES}) # careful about not including extraneous files in the distribution, and one # could make a distribution even if there were diffs in the current # checkout. -daikon.tar daikon.zip: kvasir ${README_PATHS} ${DAIKON_JAVA_FILES} java/Makefile +daikon.tar daikon.zip: kvasir ${README_PATHS} ${DAIKON_JAVA_FILES} java/Makefile java/java-home.mk ${MAKE} doc-all # `make doc-all` just did the work, but check that the files exist. ${MAKE} ${DOC_PATHS} @@ -682,6 +682,7 @@ daikon.tar daikon.zip: kvasir ${README_PATHS} ${DAIKON_JAVA_FILES} java/Makefile (cd java; tar chf ${TMPDIR}/daikon-java.tar --exclude daikon-java --exclude daikon-output --exclude Makefile.user --exclude PrototypeChecker.java --exclude VIndexChecker.java --exclude VIndexAnnotatedTypeFactory.java daikon jtb lib typequals) (mkdir ${TMPDIR}/daikon/java; cd ${TMPDIR}/daikon/java; tar xf ${TMPDIR}/daikon-java.tar; rm ${TMPDIR}/daikon-java.tar) cp -p java/Makefile ${TMPDIR}/daikon/java/Makefile + cp -p java/java-home.mk ${TMPDIR}/daikon/java/java-home.mk # Maybe I should do ${MAKE} javadoc # Don't do ${MAKE} clean which deletes .class files (cd ${TMPDIR}/daikon/java; ${RM_TEMP_FILES}) diff --git a/java/Makefile b/java/Makefile index ce436e5d69..498bc8888e 100644 --- a/java/Makefile +++ b/java/Makefile @@ -25,68 +25,35 @@ JAR ?= jar BuildJDKTool = BuildJDK -ifeq ($(shell uname), Darwin) - JAVA_HOME ?= $(shell /usr/libexec/java_home) -else -ifeq ($(shell which javac > /dev/null 2>&1 && echo found || echo nonexistent), found) - JAVA_HOME ?= $(shell readlink -f $(shell which javac) | sed "s:/bin/javac::") -else - $(info Failure: Here is the output of: which javac) - $(info $(shell which javac)) - $(error 'which javac' had exit status 1) -endif +include ${DAIKONDIR}/java/java-home.mk +ifeq (,$(call is_jdk,${JAVA_HOME})) + $(error No JDK found at JAVA_HOME="${JAVA_HOME}"; set JAVA_HOME or put javac on the PATH) endif # The "JAVANN" variables mean "at least version NN", except JAVA8. -JAVA_VERSION_STRING_WITH_EA := $(shell javac -version 2>&1 | head -1 | cut "-d " -f2) -JAVA_VERSION_STRING := $(shell javac -version 2>&1 | head -1 | cut "-d " -f2 | sed 's/-ea//') -ifneq (,$(findstring start1.8,start${JAVA_VERSION_STRING})) - JAVA8 := 1 - JAVA17 := 0 - JAVA21 := 0 - JAVA24 := 0 - JAVA25 := 0 - JAVA27 := 0 - JAVA_RELEASE_NUMBER := 8 -else - JAVA8 := 0 - JAVA17 := 0 - JAVA21 := 0 - JAVA24 := 0 - JAVA25 := 0 - JAVA27 := 0 - JAVA_RELEASE_NUMBER := $(shell echo ${JAVA_VERSION_STRING} | sed 's/\([0-9]*\)\..*/\1/') -endif -ifeq ($(shell test ${JAVA_RELEASE_NUMBER} -ge 17; echo $$?),0) - JAVA17 := 1 -endif -ifeq ($(shell test ${JAVA_RELEASE_NUMBER} -ge 21; echo $$?),0) - JAVA21 := 1 -endif +JAVA8 := $(if $(filter 8,${JAVA_RELEASE_NUMBER}),1,0) +JAVA17 := $(if $(call java_at_least,17),1,0) +JAVA21 := $(if $(call java_at_least,21),1,0) # JDK 24 isn't an LTS version, but we need it to know whether to compile Instrument24.java. -ifeq ($(shell test ${JAVA_RELEASE_NUMBER} -ge 24; echo $$?),0) - JAVA24 := 1 +JAVA24 := $(if $(call java_at_least,24),1,0) +ifeq (${JAVA24}, 1) BuildJDKTool = BuildJDK24 endif -ifeq ($(shell test ${JAVA_RELEASE_NUMBER} -ge 25; echo $$?),0) - JAVA25 := 1 -endif -ifeq ($(shell test ${JAVA_RELEASE_NUMBER} -eq 25; echo $$?),0) +JAVA25 := $(if $(call java_at_least,25),1,0) +JAVA27 := $(if $(call java_at_least,27),1,0) +ifeq (${JAVA_RELEASE_NUMBER},25) ifndef JAVA25_HOME JAVA25_HOME := ${JAVA_HOME} endif endif -ifeq ($(shell test ${JAVA_RELEASE_NUMBER} -ge 27; echo $$?),0) - JAVA27 := 1 -endif -# Use -ge 26, not -ge 27, so that JDK 26 has a value for the --system flag below. -ifeq ($(shell test ${JAVA_RELEASE_NUMBER} -ge 26; echo $$?),0) +# Use 26, not 27, so that JDK 26 has a value for the --system flag below. +ifeq ($(call java_at_least,26),1) ifndef JAVA27_HOME JAVA27_HOME := ${JAVA_HOME} endif endif -ifeq ($(shell test ${JAVA_RELEASE_NUMBER} -gt 20; echo $$?),0) +ifeq ($(call java_at_least,21),1) LINT_THIS_ESCAPE=-Xlint:-this-escape endif @@ -185,7 +152,7 @@ DOCLETPATH := ${DAIKONDIR}/java:$(wildcard ${DAIKONDIR}/java/lib/bcel-util-*.jar DOCLET_FLAGS := LINKOFFLINE := https://docs.oracle.com/javase/8/docs/api/ -ifeq ($(shell expr ${JAVA_RELEASE_NUMBER} \>= 11),1) +ifeq ($(call java_at_least,11),1) DOCLET_FLAGS := --add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED LINKOFFLINE := https://docs.oracle.com/en/java/javase/17/docs/api/ endif @@ -516,7 +483,7 @@ showvars-brief: @echo "CLASSPATH = ${CLASSPATH}" @echo "DAIKON_CLASSPATH = ${DAIKON_CLASSPATH}" @echo "JAVA_RELEASE_NUMBER = ${JAVA_RELEASE_NUMBER}" - @echo "JAVA_VERSION_STRING_WITH_EA = ${JAVA_VERSION_STRING_WITH_EA}" + @echo "JAVA_VERSION_STRING = ${JAVA_VERSION_STRING}" .PHONY: xlint xlint: daikon/Daikon.class @@ -1339,7 +1306,7 @@ endif ## Make Javadoc with private fields for people working on source code. ## It is generated in the standard location, directory api/ api-private: ${PLUME_SCRIPTS} ${AUTO_GENERATED_FILES} -ifeq ($(shell test ${JAVA_RELEASE_NUMBER} -eq 8; echo $$?),0) +ifeq (${JAVA8}, 1) @echo "Skipping api-private target under Java 8" else mkdir -p ${JAVADOC_DEST} diff --git a/java/java-home.mk b/java/java-home.mk new file mode 100644 index 0000000000..1c5ad2177f --- /dev/null +++ b/java/java-home.mk @@ -0,0 +1,39 @@ +# Makefile fragment that sets JAVA_HOME, if it is not already set to a non-empty +# value, and defines variables and functions about the JDK there. +# It is included by the top-level Makefile, java/Makefile, and tests/Makefile.common. +# scripts/daikon.bashrc finds JAVA_HOME in the same way. + +# $(call is_jdk,DIR) is non-empty if DIR contains a JDK. +# JDK 9 and later contain lib/jrt-fs.jar; JDK 8 contains jre/lib/rt.jar. +is_jdk = $(and $(strip $(1)),$(wildcard $(1)/bin/javac),$(wildcard $(1)/lib/jrt-fs.jar $(1)/jre/lib/rt.jar)) + +# If the javac on the PATH is (a symbolic link to) a file in a JDK, use that +# JDK. If javac is a wrapper script (as used by jenv, asdf, or mise), set +# JAVA_HOME instead. +ifeq ($(strip ${JAVA_HOME}),) + JAVAC_ON_PATH_JDK := $(patsubst %/bin/javac,%,$(filter %/bin/javac,$(realpath $(shell command -v javac 2> /dev/null)))) + JAVA_HOME := $(if $(call is_jdk,${JAVAC_ON_PATH_JDK}),${JAVAC_ON_PATH_JDK}) +endif +ifeq ($(strip ${JAVA_HOME}),) + ifeq ($(shell uname), Darwin) + JAVA_HOME := $(shell /usr/libexec/java_home 2> /dev/null) + endif +endif +# Commands run by make, including recursive invocations of make, use this value. +export JAVA_HOME + +# The version of the JDK at JAVA_HOME, such as "1.8.0_462", "25.0.1", or +# "27-ea". It is empty if JAVA_HOME does not contain a JDK. +JAVA_VERSION_STRING := +ifneq (,$(call is_jdk,${JAVA_HOME})) + JAVA_VERSION_STRING := $(shell '${JAVA_HOME}/bin/java' -version 2>&1 | sed -n 's/^.* version "\([^"]*\)".*/\1/p' | head -1) + ifeq (,${JAVA_VERSION_STRING}) + $(error Cannot determine the Java version of JAVA_HOME="${JAVA_HOME}") + endif +endif +# The major version, such as "8" or "25". It is empty if JAVA_VERSION_STRING is. +JAVA_RELEASE_NUMBER := $(firstword $(subst ., ,$(subst -, ,$(subst +, ,$(subst _, ,$(patsubst 1.%,%,${JAVA_VERSION_STRING})))))) + +# $(call java_at_least,NN) is "1" if the JDK at JAVA_HOME is version NN or +# later, and is empty otherwise (including if there is no JDK). +java_at_least = $(shell test 0${JAVA_RELEASE_NUMBER} -ge $(1) && echo 1) diff --git a/scripts/daikon.bashrc b/scripts/daikon.bashrc index 9949bdde44..9cf9b7a6ec 100644 --- a/scripts/daikon.bashrc +++ b/scripts/daikon.bashrc @@ -8,11 +8,23 @@ ## files to .class files but have not re-made the daikon.jar file. ## You should not need to edit this file. -if [ "$(uname)" = "Darwin" ] ; then - export JAVA_HOME=${JAVA_HOME:-$(/usr/libexec/java_home)} -else - export JAVA_HOME=${JAVA_HOME:-$(dirname $(dirname $(readlink -f $(which javac))))} +# This logic is the same as in java/java-home.mk: if javac on the PATH is (a +# symbolic link to) a file in a JDK, use that JDK; otherwise, on macOS, use +# /usr/libexec/java_home. If javac is a wrapper script (as used by jenv, asdf, +# or mise), set JAVA_HOME instead. +if [ -z "$JAVA_HOME" ] ; then + javac_realpath=$(realpath "$(type -P javac)" 2> /dev/null) + javac_jdk=${javac_realpath%/bin/javac} + # JDK 9 and later contain lib/jrt-fs.jar; JDK 8 contains jre/lib/rt.jar. + if [ "$javac_jdk" != "$javac_realpath" ] && [ -x "$javac_jdk/bin/javac" ] \ + && { [ -f "$javac_jdk/lib/jrt-fs.jar" ] || [ -f "$javac_jdk/jre/lib/rt.jar" ]; } ; then + JAVA_HOME=$javac_jdk + elif [ "$(uname)" = "Darwin" ] ; then + JAVA_HOME=$(/usr/libexec/java_home 2> /dev/null) + fi + unset javac_realpath javac_jdk fi +export JAVA_HOME if [ ! -d "$JAVA_HOME" ]; then echo "Cannot infer JAVA_HOME; please set it. Aborting daikon.bashrc ." return 2 diff --git a/tests/Makefile.common b/tests/Makefile.common index 38dde8e8cb..5f389bfc15 100644 --- a/tests/Makefile.common +++ b/tests/Makefile.common @@ -156,6 +156,12 @@ endif -include Makefile.user -include $(COMMON_HERE)/Makefile.user +# This is included after Makefile.user, so that Makefile.user can set JAVA_HOME. +include ${DAIKONDIR}/java/java-home.mk +ifeq (,$(call is_jdk,${JAVA_HOME})) + $(error No JDK found at JAVA_HOME="${JAVA_HOME}"; set JAVA_HOME or put javac on the PATH) +endif + # SOURCES is where all of the source code to be examined is located SOURCES?=$(COMMON_HERE)/sources @@ -186,17 +192,6 @@ ECLAT_SCRATCH = $(SCRATCH_FOR_DAIKON_BASE)/$(PROJECT)_eclat # CP_PARENTS := cp -pf --parents CP_PARENTS := rsync -aR -ifeq ($(shell uname), Darwin) - JAVA_HOME ?= $(/usr/libexec/java_home) -else -ifeq ($(shell which javac > /dev/null 2>&1 && echo found || echo nonexistent), found) - JAVA_HOME ?= $(shell readlink -f $(shell which javac) | sed "s:/bin/javac::") -else - $(info Failure: Here is the output of: which javac) - $(info $(shell which javac)) - $(error 'which javac' had exit status 1) -endif -endif JAVA := ${JAVA_HOME}/bin/java JAVAC ?= ${JAVA_HOME}/bin/javac @@ -228,32 +223,16 @@ export LC_ALL=C default: help -# The general form of a Java version number prior to Java 9 -# is 1.n.n_n. For Java 9 or later it is n.n.n.n. -# Currently, we only look for the release number, which is -# the first 'n' in either case. -# Note that version 11 (or 21 or ...) requires the 'start' anchor below -# to make the old vs. new test work properly. - -JAVA_VERSION_STRING := $(shell javac -version 2>&1 | head -1 | cut "-d " -f2 | sed 's/-ea//') -startJVS := $(shell echo start${JAVA_VERSION_STRING}) -ifneq (,$(findstring start1.8,$(startJVS))) - JAVA_RELEASE_NUMBER := 8 -else - # Old Java releases (e.g. 1.7) will set JAVA_RELEASE_NUMBER to 1. - # This will cause the JAVA8_OR_NEWER test to fail as desired. - JAVA_RELEASE_NUMBER := $(shell echo ${JAVA_VERSION_STRING} | sed 's/\([0-9]\+\)\(\..*\)\?\(-.*\)\?/\1/') -endif - -JAVA23_OR_NEWER := $(shell expr ${JAVA_RELEASE_NUMBER} \>= 23) -JAVA21_OR_NEWER := $(shell expr ${JAVA_RELEASE_NUMBER} \>= 21) -JAVA19_OR_NEWER := $(shell expr ${JAVA_RELEASE_NUMBER} \>= 19) -JAVA17_OR_NEWER := $(shell expr ${JAVA_RELEASE_NUMBER} \>= 17) -JAVA11_OR_NEWER := $(shell expr ${JAVA_RELEASE_NUMBER} \>= 11) -JAVA8_OR_NEWER := $(shell expr ${JAVA_RELEASE_NUMBER} \>= 8) +# JAVA_RELEASE_NUMBER and java_at_least are defined by java-home.mk. +JAVA23_OR_NEWER := $(call java_at_least,23) +JAVA21_OR_NEWER := $(call java_at_least,21) +JAVA19_OR_NEWER := $(call java_at_least,19) +JAVA17_OR_NEWER := $(call java_at_least,17) +JAVA11_OR_NEWER := $(call java_at_least,11) +JAVA8_OR_NEWER := $(call java_at_least,8) ifneq (${JAVA8_OR_NEWER}, 1) - $(error Must use Java version 8 or later; found ${JAVA_RELEASE_NUMBER} in ${JAVA_VERSION_STRING}) + $(error Must use Java version 8 or later; found ${JAVA_RELEASE_NUMBER} in ${JAVA_VERSION_STRING}) endif # goal_name is a function that will be called like "$(call goal_name, FILENAME)" diff --git a/tests/daikon-tests/junit/Makefile.user b/tests/daikon-tests/junit/Makefile.user index 9c2889b925..da30afc81a 100644 --- a/tests/daikon-tests/junit/Makefile.user +++ b/tests/daikon-tests/junit/Makefile.user @@ -1,17 +1,6 @@ ########################################################################### -ifeq ($(shell uname), Darwin) - JAVA_HOME ?= $(/usr/libexec/java_home) -else -ifeq ($(shell which javac > /dev/null 2>&1 && echo found || echo nonexistent), found) - JAVA_HOME ?= $(shell readlink -f $(shell which javac) | sed "s:/bin/javac::") -else - $(info Failure: Here is the output of: which javac) - $(info $(shell which javac)) - $(error 'which javac' had exit status 1) -endif -endif - # need junit.jar on classpath to compile all junit references JUNIT_CLASSPATH := ${DAIKONDIR}/java/lib/* -JAVAC := $(JAVA_HOME)/bin/javac -cp .:$(JUNIT_CLASSPATH) +# "=", not ":=", because JAVA_HOME is set after this file is included. +JAVAC = ${JAVA_HOME}/bin/javac -cp .:$(JUNIT_CLASSPATH) diff --git a/tests/daikon-tests/junit5/Makefile.user b/tests/daikon-tests/junit5/Makefile.user index d10d1f57d7..ad3c351c7e 100644 --- a/tests/daikon-tests/junit5/Makefile.user +++ b/tests/daikon-tests/junit5/Makefile.user @@ -1,18 +1,7 @@ ########################################################################### -ifeq ($(shell uname), Darwin) - JAVA_HOME ?= $(/usr/libexec/java_home) -else -ifeq ($(shell which javac > /dev/null 2>&1 && echo found || echo nonexistent), found) - JAVA_HOME ?= $(shell readlink -f $(shell which javac) | sed "s:/bin/javac::") -else - $(info Failure: Here is the output of: which javac) - $(info $(shell which javac)) - $(error 'which javac' had exit status 1) -endif -endif - # need junit.jar on classpath to compile all junit references # explicit order is necessary to get versions built with JDK 8 not 5 JUNIT_CLASSPATH := ${DAIKONDIR}/java/lib/junit-4.13.2-Daikon.jar:${DAIKONDIR}/java/lib/hamcrest-core-1.3-Daikon.jar:${DAIKONDIR}/java/lib/* -JAVAC := $(JAVA_HOME)/bin/javac -cp .:$(JUNIT_CLASSPATH) +# "=", not ":=", because JAVA_HOME is set after this file is included. +JAVAC = ${JAVA_HOME}/bin/javac -cp .:$(JUNIT_CLASSPATH)