diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index bc4e679a6..6d6133e3d 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -4,8 +4,7 @@ matrix: bazel: [ 7.4.1, 8.0.0rc7, -# Temporarily disabled until java_stub_template.txt is public -# last_green, + last_green, ] tools_flags: &tools_flags diff --git a/toolchains/android/toolchain.bzl b/toolchains/android/toolchain.bzl index 7c053bded..7e42cd49b 100644 --- a/toolchains/android/toolchain.bzl +++ b/toolchains/android/toolchain.bzl @@ -179,7 +179,7 @@ _ATTRS = dict( java_stub = attr.label( allow_files = True, # used in android_local_test - default = "@bazel_tools//tools/java:java_stub_template.txt", + default = "//tools/jdk:java_stub_template.txt", ), jdeps_tool = attr.label( allow_files = True, diff --git a/tools/jdk/BUILD b/tools/jdk/BUILD index 1255a9fe6..30b681d66 100644 --- a/tools/jdk/BUILD +++ b/tools/jdk/BUILD @@ -86,3 +86,5 @@ alias( actual = "@rules_java//toolchains:current_host_java_runtime", visibility = ["//visibility:public"], ) + +exports_files(["java_stub_template.txt"]) diff --git a/tools/jdk/java_stub_template.txt b/tools/jdk/java_stub_template.txt new file mode 100644 index 000000000..115b46e63 --- /dev/null +++ b/tools/jdk/java_stub_template.txt @@ -0,0 +1,410 @@ +#!/usr/bin/env bash +# Copyright 2014 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# This script was generated from java_stub_template.txt. Please +# don't edit it directly. +# +# If present, these flags should either be at the beginning of the command +# line, or they should be wrapped in a --wrapper_script_flag=FLAG argument. +# +# --debug Launch the JVM in remote debugging mode listening +# --debug= to the specified port or the port set in the +# DEFAULT_JVM_DEBUG_PORT environment variable (e.g. +# 'export DEFAULT_JVM_DEBUG_PORT=8000') or else the +# default port of 5005. The JVM starts suspended +# unless the DEFAULT_JVM_DEBUG_SUSPEND environment +# variable is set to 'n'. +# --main_advice= Run an alternate main class with the usual main +# program and arguments appended as arguments. +# --main_advice_classpath= +# Prepend additional class path entries. +# --jvm_flag= Pass to the "java" command itself. +# may contain spaces. Can be used multiple times. +# --jvm_flags= Pass space-separated flags to the "java" command +# itself. Can be used multiple times. +# --singlejar Start the program from the packed-up deployment +# jar rather than from the classpath. +# --print_javabin Print the location of java executable binary and exit. +# --classpath_limit= +# Specify the maximum classpath length. If the classpath +# is shorter, this script passes it to Java as a command +# line flag, otherwise it creates a classpath jar. +# +# The remainder of the command line is passed to the program. + +set -o posix + +# Make it easy to insert 'set -x' or similar commands when debugging problems with this script. +eval "$JAVA_STUB_DEBUG" + +# Prevent problems where the caller has exported CLASSPATH, causing our +# computed value to be copied into the environment and double-counted +# against the argv limit. +unset CLASSPATH + +JVM_FLAGS_CMDLINE=() + +# Processes an argument for the wrapper. Returns 0 if the given argument +# was recognized as an argument for this wrapper, and 1 if it was not. +function process_wrapper_argument() { + case "$1" in + --debug) JVM_DEBUG_PORT="${DEFAULT_JVM_DEBUG_PORT:-5005}" ;; + --debug=*) JVM_DEBUG_PORT="${1#--debug=}" ;; + --main_advice=*) MAIN_ADVICE="${1#--main_advice=}" ;; + --main_advice_classpath=*) MAIN_ADVICE_CLASSPATH="${1#--main_advice_classpath=}" ;; + --jvm_flag=*) JVM_FLAGS_CMDLINE+=( "${1#--jvm_flag=}" ) ;; + --jvm_flags=*) JVM_FLAGS_CMDLINE+=( ${1#--jvm_flags=} ) ;; + --singlejar) SINGLEJAR=1 ;; + --print_javabin) PRINT_JAVABIN=1 ;; + --classpath_limit=*) + CLASSPATH_LIMIT="${1#--classpath_limit=}" + echo "$CLASSPATH_LIMIT" | grep -q '^[0-9]\+$' || \ + die "ERROR: $self failed, --classpath_limit is not a number" + ;; + *) + return 1 ;; + esac + return 0 +} + +die() { + printf "%s: $1\n" "$0" "${@:2}" >&2 + exit 1 +} + +# Windows +function is_windows() { + [[ "${OSTYPE}" =~ msys* ]] || [[ "${OSTYPE}" =~ cygwin* ]] +} + +# macOS +function is_macos() { + [[ "${OSTYPE}" =~ darwin* ]] +} + +function available_utf8_locale() { + # Both C.UTF-8 and en_US.UTF-8 do not cause any language-specific effects + # when set as LC_CTYPE, but neither is certain to exist on all systems. + # + # https://github.com/bazelbuild/bazel/pull/17670: Note that the use of "env" + # is important in these calls. Without "env", bash itself seems to pick up + # the LC_CTYPE change as soon as the variable is defined and may emit a + # warning when the locale files are not present. By using "env", bash never + # sees the change and the 2>/dev/null redirection does the right thing. + if [[ "$(env LC_CTYPE=C.UTF-8 locale charmap 2>/dev/null)" == "UTF-8" ]]; then + echo "C.UTF-8" + elif [[ "$(env LC_CTYPE=en_US.UTF-8 locale charmap 2>/dev/null)" == "UTF-8" ]]; then + echo "en_US.UTF-8" + fi +} + +# Parse arguments sequentially until the first unrecognized arg is encountered. +# Scan the remaining args for --wrapper_script_flag=X options and process them. +ARGS=() +for ARG in "$@"; do + if [[ "$ARG" == --wrapper_script_flag=* ]]; then + process_wrapper_argument "${ARG#--wrapper_script_flag=}" \ + || die "invalid wrapper argument '%s'" "$ARG" + elif [[ "${#ARGS}" -gt 0 ]] || ! process_wrapper_argument "$ARG"; then + ARGS+=( "$ARG" ) + fi +done + +# Find our runfiles tree. We need this to construct the classpath +# (unless --singlejar was passed). +# +# Call this program X. X was generated by a java_binary or java_test rule. +# X may be invoked in many ways: +# 1a) directly by a user, with $0 in the output tree +# 1b) via 'bazel run' (similar to case 1a) +# 2) directly by a user, with $0 in X's runfiles tree +# 3) by another program Y which has a data dependency on X, with $0 in Y's runfiles tree +# 4) via 'bazel test' +# 5) by a genrule cmd, with $0 in the output tree +# 6) case 3 in the context of a genrule +# +# For case 1, $0 will be a regular file, and the runfiles tree will be +# at $0.runfiles. +# For case 2, $0 will be a symlink to the file seen in case 1. +# For case 3, we use Y's runfiles tree, which will be a superset of X's. +# For case 4, $JAVA_RUNFILES and $TEST_SRCDIR should already be set. +# Case 5 is handled like case 1. +# Case 6 is handled like case 3. + +# If we are running on Windows, convert the windows style path +# to unix style for detecting runfiles path. +if is_windows; then + self=$(cygpath --unix "$0") +else + self="$0" +fi + +if [[ "$self" != /* ]]; then + self="$PWD/$self" +fi + +if [[ "$SINGLEJAR" != 1 || "%needs_runfiles%" == 1 ]]; then + if [[ -z "$JAVA_RUNFILES" ]]; then + while true; do + if [[ -e "$self.runfiles" ]]; then + JAVA_RUNFILES="$self.runfiles" + break + fi + if [[ $self == *.runfiles/* ]]; then + JAVA_RUNFILES="${self%.runfiles/*}.runfiles" + break + fi + if [[ ! -L "$self" ]]; then + break + fi + readlink="$(readlink "$self")" + if [[ "$readlink" = /* ]]; then + self="$readlink" + else + # resolve relative symlink + self="${self%/*}/$readlink" + fi + done + if [[ -n "$JAVA_RUNFILES" ]]; then + export TEST_SRCDIR=${TEST_SRCDIR:-$JAVA_RUNFILES} + elif [[ -f "${self}_deploy.jar" && "%needs_runfiles%" == 0 ]]; then + SINGLEJAR=1; + else + die 'Cannot locate runfiles directory. (Set $JAVA_RUNFILES to inhibit searching.)' + fi + fi +fi + +# If we are running on Windows, we need a windows style runfiles path for constructing CLASSPATH +if is_windows; then + JAVA_RUNFILES=$(cygpath --windows "$JAVA_RUNFILES") +fi + +export JAVA_RUNFILES +export RUNFILES_MANIFEST_FILE="${JAVA_RUNFILES}/MANIFEST" +export RUNFILES_MANIFEST_ONLY=%runfiles_manifest_only% + +if [ -z "$RUNFILES_MANIFEST_ONLY" ]; then + function rlocation() { + if [[ "$1" = /* ]]; then + echo $1 + else + echo "$(dirname $RUNFILES_MANIFEST_FILE)/$1" + fi + } +else + if ! is_macos; then + # Read file into my_array + oifs=$IFS + IFS=$'\n' + my_array=( $(sed -e 's/\r//g' "$RUNFILES_MANIFEST_FILE") ) + IFS=$oifs + + # Process each runfile line into a [key,value] entry in runfiles_array + # declare -A is not supported on macOS because an old version of bash is used. + declare -A runfiles_array + for line in "${my_array[@]}" + do + line_split=($line) + runfiles_array[${line_split[0]}]=${line_split[@]:1} + done + fi + + function rlocation() { + if [[ "$1" = /* ]]; then + echo $1 + else + if is_macos; then + # Print the rest of line after the first space + # First, set the first column to empty and print rest of the line + # Second, use a trick of awk to remove leading and trailing spaces. + echo $(grep "^$1 " $RUNFILES_MANIFEST_FILE | awk '{ $1=""; print }' | awk '{ $1=$1; print }') + else + echo ${runfiles_array[$1]} + fi + fi + } +fi + +# Set JAVABIN to the path to the JVM launcher. +%javabin% + +if [[ "$PRINT_JAVABIN" == 1 || "%java_start_class%" == "--print_javabin" ]]; then + echo -n "$JAVABIN" + exit 0 +fi + +if [[ "$SINGLEJAR" == 1 ]]; then + CLASSPATH="${self}_deploy.jar" + # Check for the deploy jar now. If it doesn't exist, we can print a + # more helpful error message than the JVM. + [[ -r "$CLASSPATH" ]] \ + || die "Option --singlejar was passed, but %s does not exist.\n (You may need to build it explicitly.)" "$CLASSPATH" +else + # Create the shortest classpath we can, by making it relative if possible. + RUNPATH="${JAVA_RUNFILES}/%workspace_prefix%" + RUNPATH="${RUNPATH#$PWD/}" + CLASSPATH=%classpath% +fi + +# Export the locations which will be used to find the location of the classes from the classpath file. +export SELF_LOCATION="$self" +export CLASSLOADER_PREFIX_PATH="${RUNPATH}" + +# If using Jacoco in offline instrumentation mode, the CLASSPATH contains instrumented files. +# We need to make the metadata jar with uninstrumented classes available for generating +# the lcov-compatible coverage report, and we don't want it on the classpath. +%set_jacoco_metadata% +%set_jacoco_main_class% +%set_jacoco_java_runfiles_root% +# export JACOCO_IS_JAR_WRAPPED for compatibility with older versions of +# JacocoCoverageRunner that check for this and not CLASSPATH_JAR +# TODO(cmita): Remove when this is no longer required +export JACOCO_IS_JAR_WRAPPED=0 +export CLASSPATH_JAR="" + +if [[ -n "$JVM_DEBUG_PORT" ]]; then + JVM_DEBUG_SUSPEND=${DEFAULT_JVM_DEBUG_SUSPEND:-"y"} + JVM_DEBUG_FLAGS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=${JVM_DEBUG_SUSPEND},address=${JVM_DEBUG_PORT}" +fi + +if [[ -n "$MAIN_ADVICE_CLASSPATH" ]]; then + CLASSPATH="${MAIN_ADVICE_CLASSPATH}:${CLASSPATH}" +fi + +# Check if TEST_TMPDIR is available to use for scratch. +if [[ -n "$TEST_TMPDIR" && -d "$TEST_TMPDIR" ]]; then + JVM_FLAGS+=" -Djava.io.tmpdir=$TEST_TMPDIR" +fi + +ARGS=( + ${JVM_DEBUG_FLAGS} + ${JVM_FLAGS} + %jvm_flags% + "${JVM_FLAGS_CMDLINE[@]}" + ${MAIN_ADVICE} + %java_start_class% + "${ARGS[@]}") + + +# Creates a JAR containing the classpath and put the result to stdout +function create_classpath_jar() { + # Build class path. + MANIFEST_CLASSPATH=() + if is_windows; then + CLASSPATH_SEPARATOR=";" + else + CLASSPATH_SEPARATOR=":" + fi + + OLDIFS="$IFS" + IFS="${CLASSPATH_SEPARATOR}" # Use a custom separator for the loop. + current_dir=$(pwd) + for path in ${CLASSPATH}; do + # Loop through the characters of the path and convert characters that are + # not alphanumeric nor -_.~/ to their 2-digit hexadecimal representation + if [[ ! $path =~ ^[-_.~/a-zA-Z0-9]*$ ]]; then + local i c buff + local converted_path="" + + for ((i=0; i<${#path}; i++)); do + c=${path:$i:1} + case ${c} in + [-_.~/a-zA-Z0-9] ) buff=${c} ;; + * ) printf -v buff '%%%02x' "'$c'" + esac + converted_path+="${buff}" + done + path=${converted_path} + fi + + if is_windows; then + path="file:/${path}" # e.g. "file:/C:/temp/foo.jar" + else + # If not absolute, qualify the path + case "${path}" in + /*) ;; # Already an absolute path + *) path="${current_dir}/${path}";; # Now qualified + esac + path="file:${path}" # e.g. "file:/usr/local/foo.jar" + fi + + MANIFEST_CLASSPATH+=("${path}") + done + IFS="$OLDIFS" + + # Create manifest file + MANIFEST_FILE="$(mktemp -t XXXXXXXX.jar_manifest)" + ( + echo "Manifest-Version: 1.0" + + CLASSPATH_LINE="Class-Path: ${MANIFEST_CLASSPATH[*]}" + CLASSPATH_MANIFEST_LINES=$(sed -E $'s/(.{71})/\\1\\\n /g' <<< "${CLASSPATH_LINE}") + + echo "$CLASSPATH_MANIFEST_LINES" + echo "Created-By: Bazel" + ) >$MANIFEST_FILE + + # Create classpath JAR file + MANIFEST_JAR_FILE="$(mktemp -t XXXXXXXX-classpath.jar)" + if is_windows; then + MANIFEST_JAR_FILE="$(cygpath --windows "$MANIFEST_JAR_FILE")" + MANIFEST_FILE="$(cygpath --windows "$MANIFEST_FILE")" + fi + if is_windows; then + JARBIN="${JARBIN:=${JAVABIN%/java.exe}/jar.exe}" + else + JARBIN="${JARBIN:=${JAVABIN%/java}/jar}" + fi + $JARBIN cvfm "$MANIFEST_JAR_FILE" "$MANIFEST_FILE" >/dev/null || \ + die "ERROR: $self failed because $JARBIN failed" + rm -f "$MANIFEST_FILE" + + echo "$MANIFEST_JAR_FILE" +} + +# If the user didn't specify a --classpath_limit, use the default value. +if [ -z "$CLASSPATH_LIMIT" ]; then + # Windows per-arg limit MAX_ARG_STRLEN == 8k + # Linux per-arg limit MAX_ARG_STRLEN == 128k + is_windows && CLASSPATH_LIMIT=7000 || CLASSPATH_LIMIT=120000 +fi + +# On non-macOS Unix, without any locale variable set, the JVM would use +# using ASCII rather than UTF-8 as the encoding for file system paths. +if ! is_macos; then + if [ -z ${LC_CTYPE+x} ] && [ -z ${LC_ALL+x} ] && [ -z ${LANG+x} ]; then + UTF8_LOCALE=$(available_utf8_locale) + if [[ -n "$UTF8_LOCALE" ]]; then + export LC_CTYPE="$UTF8_LOCALE" + fi + fi +fi + +if (("${#CLASSPATH}" > ${CLASSPATH_LIMIT})); then + # TODO(cmtia): Remove JACOCO_IS_JAR_WRAPPED when JacocoCoverageRunner will + # never need it anymore. + export JACOCO_IS_JAR_WRAPPED=1 + CLASSPATH_MANIFEST_JAR=$(create_classpath_jar) + export CLASSPATH_JAR="$(basename $CLASSPATH_MANIFEST_JAR)" + "$JAVABIN" -classpath "$CLASSPATH_MANIFEST_JAR" "${ARGS[@]}" + exit_code=$? + rm -f "$CLASSPATH_MANIFEST_JAR" + exit $exit_code +else + export JACOCO_IS_JAR_WRAPPED=0 + export CLASSPATH_JAR="" + exec "$JAVABIN" -classpath $CLASSPATH "${ARGS[@]}" +fi