format.sh 6.1 KB
Newer Older
1
#!/usr/bin/env bash
2
3
# Usage:
#    # Do work and commit your work.
4
#
5
6
#    # Format files that differ from origin/main.
#    bash format.sh
7
8
9
#
#    # Format all files.
#    bash format.sh --all
10
11
12
13
14
15
16
17
#
#
# YAPF + Clang formatter (if installed). This script formats all changed files from the last mergebase.
# You are encouraged to run this locally before pushing changes for review.

# Cause the script to exit if a single command fails
set -eo pipefail

18
19
20
21
22
if [[ -z "${BASH_VERSION}" ]]; then
    echo "Please run this script using bash." >&2
    exit 1
fi

23
24
25
26
27
# this stops git rev-parse from failing if we run this from the .git directory
builtin cd "$(dirname "${BASH_SOURCE:-$0}")"
ROOT="$(git rev-parse --show-toplevel)"
builtin cd "$ROOT" || exit 1

28
29
30
31
ALL_FILES=''
ONLY_CHANGED=''
FILES=()
if (($# == 0)); then
32
    # Default: allow dirty workspace; run on changed files (committed + worktree)
33
34
35
    ONLY_CHANGED='true'
else
    while (($# > 0)); do
36
        case "$1" in
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
        --files)
            shift
            while (($# > 0)); do
                FILES+=("$1")
                shift
            done
            ;;
        --all)
            ALL_FILES='true'
            shift
            ;;
        *)
            echo "Unknown argument: '$1'" >&2
            exit 1
            ;;
        esac
    done
fi
55

56
57
MERGE_BASE=""
get_merge_base() {
58
    UPSTREAM_REPO="https://github.com/tile-ai/tilelang"
59
    if git ls-remote --exit-code "${UPSTREAM_REPO}" main &>/dev/null; then
60
        # First try to use the upstream repository directly
61
        MERGE_BASE="$(git fetch "${UPSTREAM_REPO}" main &>/dev/null && git merge-base FETCH_HEAD HEAD)"
62
63
    elif git show-ref --verify --quiet refs/remotes/origin/main; then
        # Fall back to origin/main if available
64
        BASE_BRANCH="origin/main"
65
        MERGE_BASE="$(git merge-base "${BASE_BRANCH}" HEAD)"
66
    else
67
        # Last resort, use local main
68
        BASE_BRANCH="main"
69
        MERGE_BASE="$(git merge-base "${BASE_BRANCH}" HEAD)"
70
    fi
71
    echo "${MERGE_BASE}"
72
73
}

74
75
76
77
if [[ -n "${ALL_FILES}" ]]; then
    echo "Checking all files..." >&2
elif [[ -n "${ONLY_CHANGED}" ]]; then
    MERGE_BASE="$(get_merge_base)"
78
    echo "Checking changed files vs merge base (${MERGE_BASE}) and working tree..." >&2
79
80
elif [[ "${#FILES[@]}" -gt 0 ]]; then
    echo "Checking specified files: ${FILES[*]}..." >&2
81
82
fi

83
84
85
# Some systems set pip's default to --user, which breaks isolated virtualenvs.
export PIP_USER=0

86
87
88
# If pre-commit is not installed, install it.
if ! python3 -m pre_commit --version &>/dev/null; then
    python3 -m pip install pre-commit
89
90
fi

91
echo 'tile-lang pre-commit: Check Start'
92

93
94
95
if [[ -n "${ALL_FILES}" ]]; then
    python3 -m pre_commit run --all-files
elif [[ -n "${ONLY_CHANGED}" ]]; then
96
97
98
99
100
101
102
103
104
105
106
    # Collect changed files (committed since merge-base + current worktree)
    CHANGED_FILES="$(git diff --name-only --diff-filter=ACM "${MERGE_BASE}" 2>/dev/null || true)"
    if [[ -n "${CHANGED_FILES}" ]]; then
        echo "Running pre-commit on changed files:"
        echo "${CHANGED_FILES}"
        # Convert newline-separated files to space-separated and run pre-commit once
        CHANGED_FILES_SPACE="$(echo "${CHANGED_FILES}" | tr '\n' ' ')"
        python3 -m pre_commit run --files ${CHANGED_FILES_SPACE}
    else
        echo "No files changed relative to merge base and worktree. Skipping pre-commit."
    fi
107
108
elif [[ "${#FILES[@]}" -gt 0 ]]; then
    python3 -m pre_commit run --files "${FILES[@]}"
109
fi
110
111

echo 'tile-lang pre-commit: Done'
112

113
114
echo 'tile-lang clang-tidy: Check Start'
# If clang-tidy is available, run it; otherwise, skip
115
if [[ -x "$(command -v run-clang-tidy)" ]]; then
116
    # Check if clang-tidy is available
117
118
119
120
121
122
    if [[ ! -x "$(command -v clang-tidy)" ]]; then
        python3 -m pip install --upgrade --requirements "${ROOT}/requirements-lint.txt"
    fi
    # Get clang-tidy version
    CLANG_TIDY_VERSION="$(clang-tidy --version | head -n1 | awk '{print $4}')"
    echo "Using clang-tidy version: ${CLANG_TIDY_VERSION}"
123

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
    # Check if build directory exists
    if [[ ! -d "${ROOT}/build" ]]; then
        echo "Build directory not found. Skipping clang-tidy checks."
    else
        # Run clang-tidy on specified files
        clang_tidy_files() {
            run-clang-tidy -j 64 "$@" -p build
        }

        # Run clang-tidy on all C/C++ source files
        clang_tidy_all() {
            run-clang-tidy -j 64 src/*.cc -p build
        }

        # Run clang-tidy on changed C/C++ files relative to main
        clang_tidy_changed() {
            # Get changed C/C++ files
            CHANGED_FILES="$(git diff --name-only --diff-filter=ACM "${MERGE_BASE}" -- '*.c' '*.cc' '*.cpp' '*.h' '*.hpp' 2>/dev/null || true)"

            if [[ -n "${CHANGED_FILES}" ]]; then
                echo "Running clang-tidy on changed files:"
                echo "${CHANGED_FILES}"
                # Convert newline-separated files to space-separated and run clang-tidy once
                CHANGED_FILES_SPACE="$(echo "${CHANGED_FILES}" | tr '\n' ' ')"
                run-clang-tidy -j 64 ${CHANGED_FILES_SPACE} -p build -fix
149
            else
150
                echo "No C/C++ files changed. Skipping clang-tidy."
151
            fi
152
153
154
155
156
157
158
159
160
161
162
        }

        if [[ -n "${ALL_FILES}" ]]; then
            # If --all is given, run clang-tidy on all source files
            clang_tidy_all
        elif [[ -n "${ONLY_CHANGED}" ]]; then
            # Otherwise, run clang-tidy only on changed C/C++ files
            clang_tidy_changed
        elif [[ "${#FILES[@]}" -gt 0 ]]; then
            # If --files is given, run clang-tidy only on the provided files
            clang_tidy_files "${FILES[@]}"
163
164
        fi
    fi
165

166
167
168
169
170
171
else
    echo "run-clang-tidy not found. Skipping clang-tidy checks."
    echo "To install clang-tidy tools, you may need to install clang-tidy and run-clang-tidy."
fi
echo 'tile-lang clang-tidy: Done'

172
173
174
175
176
177
178
179
180
181
182
183
# Check if there are any uncommitted changes after all formatting steps.
# If there are, ask the user to review and stage them.
if ! git diff --quiet &>/dev/null; then
    echo 'Reformatted files. Please review and stage the changes.'
    echo 'Changes not staged for commit:'
    echo
    git --no-pager diff --name-only

    exit 1
fi

echo 'tile-lang: All checks passed'