diff --git a/Makefile b/Makefile index 3783aa641d..1e7a12d0b4 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,7 @@ DIST_VERSION_FILES := ${README_PATHS} doc/daikon.texinfo doc/developer.texinfo \ # Need to include the Dockerfiles as they are referenced from 'Requirements for # compiling Daikon' in the Daikon Developer manual; which is in turn referenced # from the 'Installation' section of the Daikon manual. -SCRIPT_FILES := Makefile \ +SCRIPT_FILES := Makefile java-home.mak \ daikon.bashrc \ dfepl dtrace-perl dtype-perl \ java-cpp \ @@ -454,7 +454,7 @@ staging: @echo "]2;Building Javadoc" mkdir ${STAGING_DIR}/download/api # Javadoc is skipped on Java 8. Hard-coded JAVA_HOME path is a hack. - (export PATH=/usr/lib/jvm/java-11/bin:${PATH}; cd java; ${MAKE} SKIP_JAVADOC=0 'JAVADOC_DEST=${STAGING_DIR}/download/api' javadoc) + (if [ -d /usr/lib/jvm/java-11 ]; then export JAVA_HOME=/usr/lib/jvm/java-11 PATH=/usr/lib/jvm/java-11/bin:$${PATH}; fi; cd java; ${MAKE} SKIP_JAVADOC=0 'JAVADOC_DEST=${STAGING_DIR}/download/api' javadoc) # Copy the documentation @echo "]2;Copying documentation" mkdir ${STAGING_DIR}/download/doc diff --git a/java/Makefile b/java/Makefile index ce436e5d69..4f152f2186 100644 --- a/java/Makefile +++ b/java/Makefile @@ -25,17 +25,7 @@ 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 -endif +include ${DAIKONDIR}/scripts/java-home.mak # 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) diff --git a/scripts/daikon.bashrc b/scripts/daikon.bashrc index 9949bdde44..6ef2d27bed 100644 --- a/scripts/daikon.bashrc +++ b/scripts/daikon.bashrc @@ -8,10 +8,18 @@ ## 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-home.mak: use the JDK that contains the +# javac on the PATH. On macOS, /usr/bin/javac is a stub; then use the JDK that +# /usr/libexec/java_home reports. +if [ -z "$JAVA_HOME" ] ; then + DAIKON_JAVAC_REALPATH=$(type -P javac > /dev/null && readlink -f "$(type -P javac)") + if [ "$(uname)" = "Darwin" ] && [ "$DAIKON_JAVAC_REALPATH" = "/usr/bin/javac" ] ; then + JAVA_HOME=$(/usr/libexec/java_home) + elif [ -n "$DAIKON_JAVAC_REALPATH" ] ; then + JAVA_HOME=$(dirname "$(dirname "$DAIKON_JAVAC_REALPATH")") + fi + unset DAIKON_JAVAC_REALPATH + export JAVA_HOME fi if [ ! -d "$JAVA_HOME" ]; then echo "Cannot infer JAVA_HOME; please set it. Aborting daikon.bashrc ." diff --git a/scripts/java-home.mak b/scripts/java-home.mak new file mode 100644 index 0000000000..a6aeb1af98 --- /dev/null +++ b/scripts/java-home.mak @@ -0,0 +1,61 @@ +# Sets JAVA_HOME, if it is not already set, to the JDK that contains the javac +# on the PATH. If JAVA_HOME is already set, checks that it agrees with the +# javac on the PATH. Requires javac to be on the PATH. +# A JDK is recognized by its "release" file. +# Paths that contain whitespace are not supported, because Make splits on it. + +JAVAC_ON_PATH := $(shell command -v javac 2>/dev/null) +ifeq (,$(JAVAC_ON_PATH)) + $(error javac is not on the PATH) +endif +ifneq (1,$(words $(JAVAC_ON_PATH))) + $(error The javac on the PATH, "$(JAVAC_ON_PATH)", contains whitespace, which is not supported) +endif +# Resolve symbolic links such as /usr/bin/javac -> /etc/alternatives/javac. +JAVAC_ON_PATH_REALPATH := $(realpath $(JAVAC_ON_PATH)) + +# On macOS, /usr/bin/javac is a stub that runs the javac of JAVA_HOME if it is +# set, or else of the JDK that /usr/libexec/java_home reports. +JAVAC_ON_PATH_IS_MACOS_STUB := +ifeq ($(shell uname), Darwin) + ifeq ($(JAVAC_ON_PATH_REALPATH), /usr/bin/javac) + JAVAC_ON_PATH_IS_MACOS_STUB := 1 + endif +endif + +# The JDK that contains the javac on the PATH, or empty if that javac is not +# within a JDK (for example, it is a wrapper script or a jenv shim). +JAVAC_ON_PATH_JDK := +ifndef JAVAC_ON_PATH_IS_MACOS_STUB + ifneq (,$(filter %/bin/javac,$(JAVAC_ON_PATH_REALPATH))) + ifneq (,$(wildcard $(patsubst %/bin/javac,%,$(JAVAC_ON_PATH_REALPATH))/release)) + JAVAC_ON_PATH_JDK := $(patsubst %/bin/javac,%,$(JAVAC_ON_PATH_REALPATH)) + endif + endif +endif + +ifndef JAVA_HOME + ifdef JAVAC_ON_PATH_IS_MACOS_STUB + JAVA_HOME := $(shell /usr/libexec/java_home 2>/dev/null) + ifeq (,$(JAVA_HOME)) + $(error /usr/libexec/java_home did not find a JDK; set JAVA_HOME or put a JDK's bin directory on the PATH) + endif + else ifdef JAVAC_ON_PATH_JDK + JAVA_HOME := $(JAVAC_ON_PATH_JDK) + else + $(error Cannot determine JAVA_HOME: $(JAVAC_ON_PATH) (which resolves to $(JAVAC_ON_PATH_REALPATH)) is not within a JDK; set JAVA_HOME) + endif +else + ifneq (1,$(words $(JAVA_HOME))) + $(error JAVA_HOME, "$(JAVA_HOME)", contains whitespace, which is not supported) + endif + ifeq (,$(wildcard $(JAVA_HOME)/bin/javac)) + $(error JAVA_HOME is $(JAVA_HOME), but $(JAVA_HOME)/bin/javac does not exist) + endif + # A javac that is not within a JDK is not checked. + ifdef JAVAC_ON_PATH_JDK + ifneq ($(realpath $(JAVA_HOME)/bin/javac),$(JAVAC_ON_PATH_REALPATH)) + $(error JAVA_HOME is $(JAVA_HOME), but the javac on the PATH is $(JAVAC_ON_PATH_REALPATH); make them agree) + endif + endif +endif diff --git a/tests/Makefile.common b/tests/Makefile.common index 38dde8e8cb..ba9216ad45 100644 --- a/tests/Makefile.common +++ b/tests/Makefile.common @@ -133,13 +133,11 @@ ifdef ESC_ON endif +# MAKEFILE_COMMON_DIR is the directory containing this file, even if the user set DAIKONDIR. +MAKEFILE_COMMON_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) + # DAIKONDIR is the clone of the 'daikon' project. -# Would like to use simple '?=' but this is recursively expanded while ':=' is simply expanded. -# The include of Makefile.user (if it exists) will change the value of MAKEFILE_LIST and thus -# '?=' will not evaluate as we desire. -ifeq ($(origin DAIKONDIR), undefined) - DAIKONDIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))..) -endif +DAIKONDIR ?= $(realpath ${MAKEFILE_COMMON_DIR}/..) # COMMON_HERE is where the Makefile.common is located. COMMON_HERE?=${DAIKONDIR}/tests @@ -186,21 +184,16 @@ 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 +# This follows the Makefile.user includes, so that a JAVA_HOME set there takes precedence. +include ${DAIKONDIR}/scripts/java-home.mak JAVA := ${JAVA_HOME}/bin/java -JAVAC ?= ${JAVA_HOME}/bin/javac - JUNIT_CLASSPATH ?= +ifneq (,${JUNIT_CLASSPATH}) + # The JUnit tests require JUNIT_CLASSPATH, even if the environment sets JAVAC. + JAVAC := ${JAVA_HOME}/bin/javac -cp .:${JUNIT_CLASSPATH} +else + JAVAC ?= ${JAVA_HOME}/bin/javac +endif JAVALIB_CLASSPATH := ${JAVA_HOME}/jre/lib/rt.jar:${JAVA_HOME}/lib/tools.jar diff --git a/tests/daikon-tests/junit/Makefile.user b/tests/daikon-tests/junit/Makefile.user index 9c2889b925..c0917ab212 100644 --- a/tests/daikon-tests/junit/Makefile.user +++ b/tests/daikon-tests/junit/Makefile.user @@ -1,17 +1,4 @@ ########################################################################### -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) diff --git a/tests/daikon-tests/junit5/Makefile.user b/tests/daikon-tests/junit5/Makefile.user index d10d1f57d7..8a61e160f6 100644 --- a/tests/daikon-tests/junit5/Makefile.user +++ b/tests/daikon-tests/junit5/Makefile.user @@ -1,18 +1,5 @@ ########################################################################### -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)