This is an automated email from the ASF dual-hosted git repository.

tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new ae1f8f73f7 [DOCKER] Refactor bash.sh: auto-detect rootless, add 
--shell, TVM_DEV_MOUNTS (#18854)
ae1f8f73f7 is described below

commit ae1f8f73f7fe069ee456f46e427229e75a6fd1f4
Author: Tianqi Chen <[email protected]>
AuthorDate: Sat Feb 28 16:21:32 2026 -0500

    [DOCKER] Refactor bash.sh: auto-detect rootless, add --shell, 
TVM_DEV_MOUNTS (#18854)
    
    ## Summary
    
    Refactor `docker/bash.sh` incorporating lessons from tdev's
    `docker/run_container.sh`:
    
    - **Auto-detect rootless**: Replace `$DOCKER_IS_ROOTLESS` env var with
    `docker info` query (env var kept as CI override)
    - **`--shell <name>`**: New flag to specify interactive shell (e.g.,
    `--shell zsh`)
    - **`TVM_DEV_MOUNTS`**: New env var for declarative mount configuration
    (space-separated paths)
    - **GPU cleanup**: Remove legacy `nvidia-docker` detection and Vulkan
    ICD mounting; use `--gpus all`
    - **Code organization**: Extract `detect_rootless()`, `validate_mount()`
    helpers; organize into labeled sections
    
    All existing flags and CI behavior preserved. Backwards compatible.
    
    ## Test plan
    
    - [x] `bash -n docker/bash.sh` — no syntax errors
    - [x] `bash docker/bash.sh --help` — shows updated usage
    - [ ] Manual test in CI-compatible container
---
 docker/bash.sh | 299 +++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 203 insertions(+), 96 deletions(-)

diff --git a/docker/bash.sh b/docker/bash.sh
index a530af705c..ff4c902fbc 100755
--- a/docker/bash.sh
+++ b/docker/bash.sh
@@ -21,7 +21,8 @@
 # Start a bash, mount REPO_MOUNT_POINT to be current directory.
 #
 # Usage: docker/bash.sh [-i|--interactive] [--net=host] [-t|--tty]
-#          [--mount MOUNT_DIR] [--repo-mount-point REPO_MOUNT_POINT]
+#          [--shell SHELL] [--mount MOUNT_DIR]
+#          [--repo-mount-point REPO_MOUNT_POINT]
 #          [--dry-run] [--name NAME] [--privileged]
 #          <DOCKER_IMAGE_NAME> [--] [COMMAND]
 #
@@ -35,10 +36,45 @@
 
 set -euo pipefail
 
+##############################################
+### Section 1: Constants and defaults      ###
+##############################################
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+REPO_DIR="$(dirname "${SCRIPT_DIR}")"
+
+DRY_RUN=false
+INTERACTIVE=false
+TTY=false
+USE_NET_HOST=false
+USE_GPU=true
+DOCKER_IMAGE_NAME=
+COMMAND=bash
+MOUNT_DIRS=( )
+CONTAINER_NAME=
+USER_SHELL=
+
+# TODO(Lunderberg): Remove this if statement and always set to
+# "${REPO_DIR}".  The consistent directory for Jenkins is currently
+# necessary to allow CMake build commands to run in CI after the build
+# steps.
+# TODO(https://github.com/apache/tvm/issues/11952):
+# Figure out a better way to keep the same path
+# between build and testing stages.
+if [[ -n "${JENKINS_HOME:-}" ]]; then
+    REPO_MOUNT_POINT=/workspace
+else
+    REPO_MOUNT_POINT="${REPO_DIR}"
+fi
+
+##############################################
+### Section 2: Helper functions            ###
+##############################################
+
 function show_usage() {
     cat <<EOF
 Usage: docker/bash.sh [-i|--interactive] [--net=host] [-t|--tty]
-          [--cpus NUM_CPUS] [--mount MOUNT_DIR]
+          [--shell SHELL] [--cpus NUM_CPUS] [--mount MOUNT_DIR]
           [--repo-mount-point REPO_MOUNT_POINT]
           [--dry-run] [--name NAME]
           <DOCKER_IMAGE_NAME> [--] [COMMAND]
@@ -56,6 +92,12 @@ Usage: docker/bash.sh [-i|--interactive] [--net=host] 
[-t|--tty]
 
     Start the docker session with a pseudo terminal (tty).
 
+--shell SHELL
+
+    Specify a shell (e.g. --shell zsh) to use instead of bash
+    when entering auto-interactive mode (no command or command
+    is "bash").
+
 --cpus NUM_CPUS
 
     Limit the number of CPU cores to be used.
@@ -126,40 +168,18 @@ COMMAND
     contains dash-prefixed arguments, the command should be preceded
     by -- to indicate arguments that are not intended for bash.sh.
 
-EOF
-}
+Environment Variables:
 
+TVM_DEV_DOCKER_MOUNTS
 
-#################################
-### Start of argument parsing ###
-#################################
-
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
-REPO_DIR="$(dirname "${SCRIPT_DIR}")"
-
-DRY_RUN=false
-INTERACTIVE=false
-TTY=false
-USE_NET_HOST=false
-USE_GPU=true
-DOCKER_IMAGE_NAME=
-COMMAND=bash
-MOUNT_DIRS=( )
-CONTAINER_NAME=
-
-# TODO(Lunderberg): Remove this if statement and always set to
-# "${REPO_DIR}".  The consistent directory for Jenkins is currently
-# necessary to allow CMake build commands to run in CI after the build
-# steps.
-# TODO(https://github.com/apache/tvm/issues/11952):
-# Figure out a better way to keep the same path
-# between build and testing stages.
-if [[ -n "${JENKINS_HOME:-}" ]]; then
-    REPO_MOUNT_POINT=/workspace
-else
-    REPO_MOUNT_POINT="${REPO_DIR}"
-fi
+    Space-separated list of additional mount specifications.
+    Each entry is either:
+      /path/to/dir          - bind mount at same path inside container
+      /src:/dst             - bind mount src to dst inside container
+    Source paths are validated; missing sources are skipped with a warning.
 
+EOF
+}
 
 function parse_error() {
     echo "$@" >&2
@@ -167,6 +187,47 @@ function parse_error() {
     exit 1
 }
 
+# Detect if Docker daemon is running in rootless mode.
+# Uses DOCKER_IS_ROOTLESS env var as override (for CI), otherwise auto-detects.
+# Result is cached after first call.
+_ROOTLESS_CACHED=
+_ROOTLESS_RESULT=1
+function detect_rootless() {
+    if [ -n "${_ROOTLESS_CACHED}" ]; then
+        return $_ROOTLESS_RESULT
+    fi
+    _ROOTLESS_CACHED=1
+    if [ -n "${DOCKER_IS_ROOTLESS:-}" ]; then
+        # env var override for CI
+        _ROOTLESS_RESULT=0
+        return 0
+    fi
+    if docker info -f '{{.SecurityOptions}}' 2>/dev/null | grep -q "rootless"; 
then
+        _ROOTLESS_RESULT=0
+        return 0
+    fi
+    _ROOTLESS_RESULT=1
+    return 1
+}
+
+# Validate a mount source path exists, warn if missing.
+# Usage: validate_mount /path/to/source
+# Returns 0 if source exists, 1 if missing.
+function validate_mount() {
+    local src="$1"
+    if [ -e "$src" ]; then
+        return 0
+    else
+        echo "Warning: Mount source '$src' not found on host, skipping." >&2
+        return 1
+    fi
+}
+
+
+##############################################
+### Section 3: Argument parsing            ###
+##############################################
+
 # Handle joined flags, such as interpreting -ih as -i -h.  Either rewrites
 # the current argument if it is a joined argument, or shifts all arguments
 # otherwise.  Should be called as "eval $break_joined_flag" where joined
@@ -191,7 +252,21 @@ while (( $# )); do
 
         -t*|--tty)
             TTY=true
-            eval $break_joined_flag
+            if [[ "$1" == "--tty" ]]; then
+                shift
+            else
+                # Short form: -t or combined like -th
+                eval $break_joined_flag
+            fi
+            ;;
+
+        --shell)
+            if (( $# >= 2 )) && [[ -n "$2" ]]; then
+                USER_SHELL="$2"
+                shift 2
+            else
+                parse_error 'ERROR: --shell requires a non-empty argument'
+            fi
             ;;
 
         --cpus)
@@ -320,19 +395,34 @@ if [[ -z "${DOCKER_IMAGE_NAME}" ]]; then
     show_usage >&2
 fi
 
+# Smart defaults: "bash" command triggers interactive mode
 if [[ ${COMMAND[@]+"${COMMAND[@]}"} = bash ]]; then
     INTERACTIVE=true
     TTY=true
 fi
 
 
+##############################################
+### Section 4: Image resolution            ###
+##############################################
 
-###############################
-### End of argument parsing ###
-###############################
+# Save user-set DOCKER_IS_ROOTLESS before dev_common.sh overwrites it.
+# detect_rootless() should only see the user's explicit env var, not
+# the auto-detection done by dev_common.sh.
+_USER_ROOTLESS="${DOCKER_IS_ROOTLESS+__set__}"
+_USER_ROOTLESS_VAL="${DOCKER_IS_ROOTLESS:-}"
 
 source "$(dirname $0)/dev_common.sh" || exit 2
 
+# Restore: if user had set DOCKER_IS_ROOTLESS, keep their value;
+# otherwise unset the one dev_common.sh created.
+if [ "$_USER_ROOTLESS" = "__set__" ]; then
+    DOCKER_IS_ROOTLESS="$_USER_ROOTLESS_VAL"
+else
+    unset DOCKER_IS_ROOTLESS
+fi
+unset _USER_ROOTLESS _USER_ROOTLESS_VAL
+
 DOCKER_MOUNT=( )
 DOCKER_DEVICES=( )
 # If the user gave a shortcut defined in the Jenkinsfile, use it.
@@ -349,14 +439,55 @@ if [ -n "${EXPANDED_SHORTCUT}" ]; then
     fi
 fi
 
-# Set up working directories
 
+##############################################
+### Section 5: Mount construction          ###
+##############################################
+
+# Working directories
 DOCKER_FLAGS+=( --workdir "${REPO_MOUNT_POINT}" )
 DOCKER_MOUNT+=( --volume "${REPO_DIR}":"${REPO_MOUNT_POINT}"
                 --volume "${SCRIPT_DIR}":/docker
               )
 
-# Set up CI-specific environment variables
+# Expose external directories from --mount flags
+for MOUNT_DIR in ${MOUNT_DIRS[@]+"${MOUNT_DIRS[@]}"}; do
+    DOCKER_MOUNT+=( --volume "${MOUNT_DIR}:${MOUNT_DIR}" )
+done
+
+# Parse TVM_DEV_DOCKER_MOUNTS env var: space-separated mount specs
+# Each entry is either /path (same path) or /src:/dst
+if [[ -n "${TVM_DEV_DOCKER_MOUNTS:-}" ]]; then
+    for mount_spec in ${TVM_DEV_DOCKER_MOUNTS}; do
+        if [[ "$mount_spec" == *:* ]]; then
+            # src:dst format
+            mount_src="${mount_spec%%:*}"
+            if validate_mount "$mount_src"; then
+                DOCKER_MOUNT+=( --volume "$mount_spec" )
+            fi
+        else
+            # Same path format
+            if validate_mount "$mount_spec"; then
+                DOCKER_MOUNT+=( --volume "$mount_spec:$mount_spec" )
+            fi
+        fi
+    done
+fi
+
+# When running from a git worktree, also mount the original git dir.
+if [ -f "${REPO_DIR}/.git" ]; then
+    git_dir=$(cd ${REPO_DIR} && git rev-parse --git-common-dir)
+    if [ "${git_dir}" != "${REPO_DIR}/.git" ]; then
+        DOCKER_MOUNT+=( --volume "${git_dir}:${git_dir}" )
+    fi
+fi
+
+
+##############################################
+### Section 6: Environment construction    ###
+##############################################
+
+# CI-specific environment variables
 DOCKER_ENV+=( --env CI_BUILD_HOME="${REPO_MOUNT_POINT}"
               --env CI_BUILD_USER="$(id -u -n)"
               --env CI_BUILD_UID="$(id -u)"
@@ -373,11 +504,10 @@ DOCKER_FLAGS+=(--rm)
 # have pid 1 and SIGKILL is propagated to the process inside, allowing
 # jenkins to kill it if needed.  This is only necessary for docker
 # daemons running as root.
-if [ -z "${DOCKER_IS_ROOTLESS}" ]; then
+if ! detect_rootless; then
     DOCKER_FLAGS+=(--pid=host)
 fi
 
-
 # Expose services running in container to the host.
 if $USE_NET_HOST; then
     if [[ $(uname) == "Darwin" ]]; then
@@ -403,62 +533,30 @@ if [[ ! -z "${CONTAINER_NAME}" ]]; then
     DOCKER_FLAGS+=( --name ${CONTAINER_NAME} --hostname ${CONTAINER_NAME})
 fi
 
-# Expose external directories to the docker container
-for MOUNT_DIR in ${MOUNT_DIRS[@]+"${MOUNT_DIRS[@]}"}; do
-    DOCKER_MOUNT+=( --volume "${MOUNT_DIR}:${MOUNT_DIR}" )
-done
-
-# Use nvidia-docker for GPU container.  If nvidia-docker is not
-# available, fall back to using "--gpus all" flag, requires docker
-# version 19.03 or higher.
-if [[ "$USE_GPU" == "true" ]] && [[ "${DOCKER_IMAGE_NAME}" == *"gpu"* || 
"${DOCKER_IMAGE_NAME}" == *"cuda"* ]]; then
-    if type nvidia-docker 1> /dev/null 2> /dev/null; then
-        DOCKER_BINARY=nvidia-docker
-    else
-        DOCKER_BINARY=docker
-        DOCKER_FLAGS+=( --gpus all )
-    fi
-
-    # nvidia-docker treats Vulkan as a graphics API, so we need to
-    # request passthrough of graphics APIs.  This could also be set in
-    # the Dockerfile.
-    DOCKER_ENV+=( --env NVIDIA_DRIVER_CAPABILITIES=compute,graphics,utility )
-
-    # But as of nvidia-docker version 2.6.0-1, we still need to pass
-    # through the nvidia icd files ourselves.
-    ICD_SEARCH_LOCATIONS=(
-        # 
https://github.com/KhronosGroup/Vulkan-Loader/blob/master/loader/LoaderAndLayerInterface.md#icd-discovery-on-linux
-        /usr/local/etc/vulkan/icd.d
-        /usr/local/share/vulkan/icd.d
-        /etc/vulkan/icd.d
-        /usr/share/vulkan/icd.d
-        # 
https://github.com/NVIDIA/libglvnd/blob/master/src/EGL/icd_enumeration.md#icd-installation
-        /etc/glvnd/egl_vendor.d
-        /usr/share/glvnd/egl_vendor.d
-    )
-    for filename in $(find "${ICD_SEARCH_LOCATIONS[@]}" -name "*nvidia*.json" 
2> /dev/null); do
-        DOCKER_MOUNT+=( --volume "${filename}":"${filename}":ro )
-    done
-
-else
-    DOCKER_BINARY=docker
-fi
-
-
-
 # Pass any restrictions of allowed CUDA devices from the host to the
 # docker container.
 if [[ -n ${CUDA_VISIBLE_DEVICES:-} ]]; then
     DOCKER_ENV+=( --env CUDA_VISIBLE_DEVICES="${CUDA_VISIBLE_DEVICES}" )
 fi
 
-
-
 # Set TVM import path inside the docker image
 if [[ "${DOCKER_IMAGE_NAME}" == *"ci"* ]]; then
     DOCKER_ENV+=( --env PYTHONPATH="${REPO_MOUNT_POINT}"/python )
 fi
 
+
+##############################################
+### Section 7: GPU/device setup            ###
+##############################################
+
+DOCKER_BINARY=docker
+
+# Use --gpus all for GPU/CUDA containers
+if [[ "$USE_GPU" == "true" ]] && [[ "${DOCKER_IMAGE_NAME}" == *"gpu"* || 
"${DOCKER_IMAGE_NAME}" == *"cuda"* ]]; then
+    DOCKER_FLAGS+=( --gpus all )
+    DOCKER_ENV+=( --env NVIDIA_DRIVER_CAPABILITIES=compute,graphics,utility )
+fi
+
 # Add ROCm devices and set ROCM_ENABLED=1 which is used in the 
with_the_same_user script
 # to add the user to the video group
 if [[ "${DOCKER_IMAGE_NAME}" == *"rocm"* && -d "/dev/dri" ]]; then
@@ -466,24 +564,33 @@ if [[ "${DOCKER_IMAGE_NAME}" == *"rocm"* && -d "/dev/dri" 
]]; then
     DOCKER_ENV+=( --env ROCM_ENABLED=1 )
 fi
 
-# When running from a git worktree, also mount the original git dir.
-if [ -f "${REPO_DIR}/.git" ]; then
-    git_dir=$(cd ${REPO_DIR} && git rev-parse --git-common-dir)
-    if [ "${git_dir}" != "${REPO_DIR}/.git" ]; then
-        DOCKER_MOUNT+=( --volume "${git_dir}:${git_dir}" )
-    fi
+
+##############################################
+### Section 8: Entry command               ###
+##############################################
+
+# Apply --shell override: when command is "bash" (auto-interactive)
+# and a custom shell was requested via --shell <name>, use that shell
+if [[ ${COMMAND[@]+"${COMMAND[@]}"} = bash ]] && [[ -n "$USER_SHELL" ]]; then
+    COMMAND=( "$USER_SHELL" )
 fi
 
-# If the docker daemon is running as root, use the TVM-provided
-# "with_the_same_user" script to update the PID.  When using rootless
-# docker, this step is unnecessary.
-if [ -z "${DOCKER_IS_ROOTLESS}" ]; then
+# Build the entry command
+if ! detect_rootless; then
+    # If the docker daemon is running as root, use the TVM-provided
+    # "with_the_same_user" script to update the PID.  When using rootless
+    # docker, this step is unnecessary.
     COMMAND=(
         bash --login /docker/with_the_same_user
         ${COMMAND[@]+"${COMMAND[@]}"}
     )
 fi
 
+
+##############################################
+### Section 9: Final docker run            ###
+##############################################
+
 # Print arguments.
 echo "REPO_DIR: ${REPO_DIR}"
 echo "DOCKER CONTAINER NAME: ${DOCKER_IMAGE_NAME}"

Reply via email to