use-sccache.sh 6.12 KB
Newer Older
1
#!/bin/bash
2
# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
4
5
6
7
8
9
# SPDX-License-Identifier: Apache-2.0

set -euo pipefail

# sccache management script
# This script handles sccache installation, environment setup, and statistics display

10
SCCACHE_VERSION="v0.14.0"
11
12
13
14
15
16
17


usage() {
    cat << EOF
Usage: $0 [COMMAND] [OPTIONS]

Commands:
18
    install         Install sccache binary (architecture auto-detected via uname -m)
19
    setup-env       Print export statements to configure sccache for compilation
20
21
22
    show-stats      Display sccache statistics with optional build name
    help            Show this help message

23
24
25
26
27
28
29
30
setup-env modes:
    setup-env           Wraps CC/CXX with sccache + sets RUSTC_WRAPPER.
                        Works for autotools (make) and Meson builds.
    setup-env cmake     Also sets CMAKE_C/CXX/CUDA_COMPILER_LAUNCHER.
                        Use for CMake-based builds.

    Usage: eval \$($0 setup-env [cmake])

31
32
33
34
35
36
37
Environment variables:
    USE_SCCACHE             Set to 'true' to enable sccache
    SCCACHE_BUCKET          S3 bucket name (fallback if not passed as parameter)
    SCCACHE_REGION          S3 region (fallback if not passed as parameter)
    ARCH                    Architecture for S3 key prefix (fallback if not passed as parameter)

Examples:
38
    $0 install                     # architecture auto-detected via uname -m
39
40
    eval \$($0 setup-env)          # autotools / Meson
    eval \$($0 setup-env cmake)    # CMake builds
41
42
43
44
45
    $0 show-stats "UCX"
EOF
}

install_sccache() {
46
47
48
49
50
51
52
    # Derive arch from TARGETARCH (set by BuildKit) with uname -m fallback
    local arch_alt
    if [ -n "${TARGETARCH:-}" ]; then
        arch_alt=$([ "$TARGETARCH" = "amd64" ] && echo "x86_64" || echo "aarch64")
    else
        arch_alt=$(uname -m)
    fi
53
54
55
    if command -v sccache >/dev/null 2>&1; then
        echo "sccache already installed at $(command -v sccache), skipping download"
    else
56
        echo "Installing sccache ${SCCACHE_VERSION} for architecture ${arch_alt}..."
57
        wget --tries=3 --waitretry=5 \
58
59
60
            "https://github.com/mozilla/sccache/releases/download/${SCCACHE_VERSION}/sccache-${SCCACHE_VERSION}-${arch_alt}-unknown-linux-musl.tar.gz"
        tar -xzf "sccache-${SCCACHE_VERSION}-${arch_alt}-unknown-linux-musl.tar.gz"
        mv "sccache-${SCCACHE_VERSION}-${arch_alt}-unknown-linux-musl/sccache" /usr/local/bin/
61
        rm -rf sccache*
62
    fi
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

    # Create compiler wrapper scripts for autotools/Meson compatibility.
    # Autoconf breaks with CC="sccache gcc" (multi-word value), so we provide
    # single-binary wrappers that autoconf sees as a regular compiler.
    # The real compiler path is passed at runtime via SCCACHE_CC_REAL / SCCACHE_CXX_REAL.
    cat > /usr/local/bin/sccache-cc <<'WRAPPER'
#!/bin/sh
# Only use sccache for pure compilations (-c flag).
# Autoconf tests the compiler with combined compile+link (no -c), and sccache
# can interfere with those tests. sccache only caches compilations anyway,
# so routing linking/other invocations directly to gcc loses nothing.
case " $* " in
    *" -c "*) exec sccache "${SCCACHE_CC_REAL:-gcc}" "$@" ;;
    *)        exec "${SCCACHE_CC_REAL:-gcc}" "$@" ;;
esac
WRAPPER
    chmod +x /usr/local/bin/sccache-cc

    cat > /usr/local/bin/sccache-cxx <<'WRAPPER'
#!/bin/sh
case " $* " in
    *" -c "*) exec sccache "${SCCACHE_CXX_REAL:-g++}" "$@" ;;
    *)        exec "${SCCACHE_CXX_REAL:-g++}" "$@" ;;
esac
WRAPPER
    chmod +x /usr/local/bin/sccache-cxx

90
91
92
    echo "sccache installed successfully"
}

93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
setup_env() {
    local mode="${1:-default}"

    # Output a conditional block: only configure sccache if the server starts
    # successfully. The server needs working S3 credentials (mounted via
    # --mount=type=secret); if they're missing or invalid, we skip sccache
    # entirely so the build continues with normal compilers.
    #
    # Use a per-step Unix domain socket so concurrent builds on the same
    # buildkit worker don't collide on the default TCP port 4226.
    echo 'export SCCACHE_SERVER_UDS="/tmp/sccache-$(mktemp -u XXXXXX).sock";'
    echo 'if sccache --start-server; then'
    echo '  export SCCACHE_IDLE_TIMEOUT=0;'
    echo '  export RUSTC_WRAPPER="sccache";'

    if [ "$mode" = "cmake" ]; then
        echo '  export CMAKE_C_COMPILER_LAUNCHER="sccache";'
        echo '  export CMAKE_CXX_COMPILER_LAUNCHER="sccache";'
        echo '  export CMAKE_CUDA_COMPILER_LAUNCHER="sccache";'
    else
        # Wrapper scripts (installed during sccache install) route only pure
        # compilations (-c flag) through sccache; linking goes directly to
        # the real compiler so autoconf's link tests pass.
        echo '  export SCCACHE_CC_REAL="${CC:-gcc}";'
        echo '  export SCCACHE_CXX_REAL="${CXX:-g++}";'
        echo '  export CC="/usr/local/bin/sccache-cc";'
        echo '  export CXX="/usr/local/bin/sccache-cxx";'
    fi

    echo 'else'
    echo '  echo "WARNING: sccache server failed to start, building without cache";'
    echo 'fi'
}

127
128
show_stats() {
    if command -v sccache >/dev/null 2>&1; then
Nate Mailhot's avatar
Nate Mailhot committed
129
130
131
132
        # Generate timestamp in ISO 8601 format
        local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

        # Output human-readable text format first
133
134
        echo "=== sccache statistics AFTER $1 ==="
        sccache --show-stats
Nate Mailhot's avatar
Nate Mailhot committed
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
        echo ""

        # Output JSON markers for deterministic parsing
        echo "=== SCCACHE_JSON_BEGIN ==="

        # Create JSON wrapper with section metadata
        cat <<EOF
{
  "section": "$1",
  "timestamp": "$timestamp",
  "sccache_stats": $(sccache --show-stats --stats-format json)
}
EOF

        echo "=== SCCACHE_JSON_END ==="
150
151
152
153
154
155
156
157
158
159
    else
        echo "sccache is not available"
    fi
}

main() {
    case "${1:-help}" in
        install)
            install_sccache
            ;;
160
161
162
        setup-env)
            shift
            setup_env "$@"
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
            ;;
        show-stats)
            shift  # Remove the command from arguments
            show_stats "$@"  # Pass all remaining arguments
            ;;
        help|--help|-h)
            usage
            ;;
        *)
            echo "Unknown command: $1"
            usage
            exit 1
            ;;
    esac
}

179
main "$@"