Skip to content

Commit 20bf08e

Browse files
committed
Update clang-format action and add a test
1 parent 66eda9c commit 20bf08e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+23131
-18
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# A workflow to test clang-format composite action
2+
3+
name: Test CI Clang-format Action
4+
# Run on pull_request
5+
on:
6+
pull_request:
7+
paths:
8+
- ci_clangformat/**
9+
- tests/ci_clangformat/**
10+
- .github/workflows/check-clang-format.yaml
11+
branches:
12+
- main
13+
defaults:
14+
run:
15+
shell: bash
16+
jobs:
17+
run-clang-format:
18+
runs-on: "linux-x86-n2-16"
19+
container: "us-central1-docker.pkg.dev/tensorflow-sigs/tensorflow/ml-build:latest"
20+
timeout-minutes: 10
21+
if: |
22+
github.event.sender.type == 'User' ||
23+
contains(github.event.pull_request.body, 'RUN_CLANG_FORMAT')
24+
steps:
25+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
26+
with:
27+
persist-credentials: false
28+
- name: "Run clang-format action"
29+
uses: ./ci_clangformat/

ci_clangformat/README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ root; otherwise, it will use the .clang-format.default under this folder.
1111
This action offers the following configuration through its inputs:
1212
* `clang_format_version`: Choose the exact clang-format version to use,
1313
with `20.1.5` as the default to align with recent stable releases.
14-
* `branch_name`: Specify the name of your repository branch (`main` by
15-
default) for comparing changes within the pull requests.
1614

1715
## Resolving Formatting Failures
1816
If a workflow run fails due to formatting violations, you're expected to

ci_clangformat/action.yaml

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2024 Google LLC
1+
# Copyright 2025 Google LLC
22

33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -18,34 +18,37 @@ inputs:
1818
description: 'The clang-format version to use.'
1919
required: true
2020
default: "20.1.5"
21-
branch_name:
22-
description: 'The repository branch used for fetching comparisons.'
23-
required: true
24-
default: 'main'
2521

2622
runs:
2723
using: "composite"
2824
steps:
2925
- name: "Checking out repository"
3026
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
31-
- name: "Fetch HEAD of ${{ inputs.branch_name }} branch"
32-
shell: bash
33-
run: |
34-
# Silence safe.directory warnings when a job is running within a container
35-
/usr/bin/git config --global --add safe.directory '*'
36-
/usr/bin/git fetch origin ${{ inputs.branch_name }} --depth=1
27+
with:
28+
fetch-depth: 0 # Fetch full history for accurate diffing
3729
- name: Run clang-format check
3830
id: check-format
3931
shell: bash
32+
env:
33+
REPO_CLANG_FORMAT: .clang-format
34+
ACTION_DEFAULT_CLANG_FORMAT: ${{ github.action_path }}.clang-format.default
35+
CLANG_FORMAT_VERSION: ${{ inputs.clang_format_version }}
36+
TARGET_BRANCH: ${{ github.base_ref }}
4037
run: |
41-
REPO_CLANG_FORMAT=".clang-format"
42-
ACTION_DEFAULT_CLANG_FORMAT="${{ github.action_path }}/.clang-format.default"
38+
FILE_PATTERNS="*.cc *.h"
39+
CLANG_FORMAT_COMMON_ARGS="--dry-run --Werror --verbose"
40+
41+
# Add this line to resolve the dubious ownership error
42+
git config --global --add safe.directory "$GITHUB_WORKSPACE"
43+
44+
# Compare PR head against the base branch to find changed files.
45+
GIT_DIFF_CMD="git diff -z --name-only --diff-filter=d origin/$TARGET_BRANCH HEAD -- $FILE_PATTERNS"
4346
4447
if [ -f "$REPO_CLANG_FORMAT" ]; then
4548
echo "::notice::Using repository's .clang-format file."
4649
# uvx (an alias for `uv tool run`) installs and runs clang-format with a specific version.
47-
uvx clang-format==${{ inputs.clang_format_version }} --dry-run --Werror --verbose $(git diff --name-only origin/main HEAD -- '*.cc' '*.h')
50+
$GIT_DIFF_CMD | xargs -0 uvx clang-format==$CLANG_FORMAT_VERSION $CLANG_FORMAT_COMMON_ARGS
4851
else
49-
echo "::notice::Repository does not have a .clang-format file. Using the action's default."
50-
uvx clang-format==${{ inputs.clang_format_version }} -style=file:$ACTION_DEFAULT_CLANG_FORMAT --dry-run --Werror --verbose $(git diff --name-only origin/main HEAD -- '*.cc' '*.h')
52+
echo "::notice::Repository does not have a .clang-format file. Using the action's default under $ACTION_DEFAULT_CLANG_FORMAT."
53+
$GIT_DIFF_CMD | xargs -0 uvx clang-format==$CLANG_FORMAT_VERSION -style=file:$ACTION_DEFAULT_CLANG_FORMAT $CLANG_FORMAT_COMMON_ARGS
5154
fi
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/* Copyright 2023 The JAX Authors.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
==============================================================================*/
15+
16+
#ifndef JAXLIB_ABSL_STATUS_CASTERS_H_
17+
#define JAXLIB_ABSL_STATUS_CASTERS_H_
18+
19+
#include <stdexcept>
20+
21+
#include "absl/status/status.h"
22+
#include "absl/status/statusor.h"
23+
24+
namespace jax {
25+
26+
// C++ -> Python caster helpers.
27+
//
28+
// Failing statuses become Python exceptions; OK Status() becomes None.
29+
//
30+
// For example:
31+
//
32+
// - Functions without arguments:
33+
// m.def("my_func", []() { ThrowIfError(MyFunc()); }
34+
// - Classes with a single argument:
35+
// py_class.def("delete", [](Buffer& self) {
36+
// ThrowIfError(self.Delete());
37+
// }
38+
//
39+
// For functions with more arguments, you can either inline the arguments,
40+
// or use the `ThrowIfErrorWrapper` wrapper defined below:
41+
//
42+
// m.def("my_func", ThrowIfErrorWrapper(MyFunc));
43+
//
44+
// Nonstatic member functions can be wrapped by passing a
45+
// pointer-to-member-function:
46+
// ThrowIfErrorWrapper(&MyClass::MyMethod)
47+
48+
inline void ThrowIfError(absl::Status src) {
49+
if (!src.ok()) {
50+
throw std::runtime_error(src.ToString());
51+
}
52+
}
53+
54+
// If one does not want to have to define a lambda specifying the inputs
55+
// arguments, on can use the `ThrowIfErrorWrapper` wrapper.
56+
//
57+
// There are three specializations:
58+
// - For free functions, `Sig` is the function type and `F` is `Sig&`.
59+
// - For callable types, `Sig` is the pointer to member function type
60+
// and `F` is the type of the callable.
61+
// - For a nonstatic member function of a class `C`, `Sig` is the function type
62+
// and `F` is Sig C::*.
63+
//
64+
// In the first two cases, the wrapper returns a callable with signature `Sig`;
65+
// in the third case, the wrapper returns callable with a modified signature
66+
// that takes a C instance as the first argument.
67+
template <typename Sig, typename F>
68+
struct ThrowIfErrorWrapper;
69+
70+
// C++17 "deduction guide" that guides class template argument deduction (CTAD)
71+
// For free functions.
72+
template <typename F>
73+
ThrowIfErrorWrapper(F) -> ThrowIfErrorWrapper<decltype(&F::operator()), F>;
74+
75+
// For callable types (with operator()).
76+
template <typename... Args>
77+
ThrowIfErrorWrapper(absl::Status (&)(Args...))
78+
-> ThrowIfErrorWrapper<absl::Status(Args...), absl::Status (&)(Args...)>;
79+
80+
// For unbound nonstatic member functions.
81+
template <typename C, typename... Args>
82+
ThrowIfErrorWrapper(absl::Status (C::*)(Args...))
83+
-> ThrowIfErrorWrapper<absl::Status(Args...), C>;
84+
85+
// Template specializations.
86+
87+
// For free functions.
88+
template <typename... Args>
89+
struct ThrowIfErrorWrapper<absl::Status(Args...), absl::Status (&)(Args...)> {
90+
explicit ThrowIfErrorWrapper(absl::Status (&f)(Args...)) : func(f) {}
91+
void operator()(Args... args) {
92+
ThrowIfError(func(std::forward<Args>(args)...));
93+
}
94+
absl::Status (&func)(Args...);
95+
};
96+
97+
// For callable types (with operator()), non-const and const versions.
98+
template <typename C, typename... Args, typename F>
99+
struct ThrowIfErrorWrapper<absl::Status (C::*)(Args...), F> {
100+
explicit ThrowIfErrorWrapper(F &&f) : func(std::move(f)) {}
101+
void operator()(Args... args) {
102+
ThrowIfError(func(std::forward<Args>(args)...));
103+
}
104+
F func;
105+
};
106+
template <typename C, typename... Args, typename F>
107+
struct ThrowIfErrorWrapper<absl::Status (C::*)(Args...) const, F> {
108+
explicit ThrowIfErrorWrapper(F &&f) : func(std::move(f)) {}
109+
void operator()(Args... args) const {
110+
ThrowIfError(func(std::forward<Args>(args)...));
111+
}
112+
F func;
113+
};
114+
115+
// For unbound nonstatic member functions, non-const and const versions.
116+
// `ptmf` stands for "pointer to member function".
117+
template <typename C, typename... Args>
118+
struct ThrowIfErrorWrapper<absl::Status(Args...), C> {
119+
explicit ThrowIfErrorWrapper(absl::Status (C::*ptmf)(Args...)) : ptmf(ptmf) {}
120+
void operator()(C &instance, Args... args) {
121+
ThrowIfError((instance.*ptmf)(std::forward<Args>(args)...));
122+
}
123+
absl::Status (C::*ptmf)(Args...);
124+
};
125+
template <typename C, typename... Args>
126+
struct ThrowIfErrorWrapper<absl::Status(Args...) const, C> {
127+
explicit ThrowIfErrorWrapper(absl::Status (C::*ptmf)(Args...) const)
128+
: ptmf(ptmf) {}
129+
void operator()(const C &instance, Args... args) const {
130+
ThrowIfError((instance.*ptmf)(std::forward<Args>(args)...));
131+
}
132+
absl::Status (C::*ptmf)(Args...) const;
133+
};
134+
135+
// Utilities for `StatusOr`.
136+
template <typename T>
137+
T ValueOrThrow(absl::StatusOr<T> v) {
138+
if (!v.ok()) {
139+
throw std::runtime_error(v.status().ToString());
140+
}
141+
return std::move(v).value();
142+
}
143+
144+
template <typename Sig, typename F>
145+
struct ValueOrThrowWrapper;
146+
147+
template <typename F>
148+
ValueOrThrowWrapper(F) -> ValueOrThrowWrapper<decltype(&F::operator()), F>;
149+
150+
template <typename R, typename... Args>
151+
ValueOrThrowWrapper(absl::StatusOr<R> (&)(Args...))
152+
-> ValueOrThrowWrapper<absl::StatusOr<R>(Args...),
153+
absl::StatusOr<R> (&)(Args...)>;
154+
155+
template <typename C, typename R, typename... Args>
156+
ValueOrThrowWrapper(absl::StatusOr<R> (C::*)(Args...))
157+
-> ValueOrThrowWrapper<absl::StatusOr<R>(Args...), C>;
158+
159+
// Deduction guide for const methods.
160+
template <typename C, typename R, typename... Args>
161+
ValueOrThrowWrapper(absl::StatusOr<R> (C::*)(Args...) const)
162+
-> ValueOrThrowWrapper<absl::StatusOr<R>(Args...) const, C>;
163+
164+
template <typename R, typename... Args>
165+
struct ValueOrThrowWrapper<absl::StatusOr<R>(Args...),
166+
absl::StatusOr<R> (&)(Args...)> {
167+
explicit ValueOrThrowWrapper(absl::StatusOr<R> (&f)(Args...)) : func(f) {}
168+
R operator()(Args... args) const {
169+
return ValueOrThrow(func(std::forward<Args>(args)...));
170+
}
171+
absl::StatusOr<R> (&func)(Args...);
172+
};
173+
template <typename R, typename C, typename... Args, typename F>
174+
struct ValueOrThrowWrapper<absl::StatusOr<R> (C::*)(Args...), F> {
175+
explicit ValueOrThrowWrapper(F &&f) : func(std::move(f)) {}
176+
R operator()(Args... args) const {
177+
return ValueOrThrow(func(std::forward<Args>(args)...));
178+
}
179+
F func;
180+
};
181+
template <typename R, typename C, typename... Args, typename F>
182+
struct ValueOrThrowWrapper<absl::StatusOr<R> (C::*)(Args...) const, F> {
183+
explicit ValueOrThrowWrapper(F &&f) : func(std::move(f)) {}
184+
R operator()(Args... args) const {
185+
return ValueOrThrow(func(std::forward<Args>(args)...));
186+
}
187+
F func;
188+
};
189+
190+
// For unbound nonstatic member functions, non-const and const versions.
191+
// `ptmf` stands for "pointer to member function".
192+
template <typename R, typename C, typename... Args>
193+
struct ValueOrThrowWrapper<absl::StatusOr<R>(Args...), C> {
194+
explicit ValueOrThrowWrapper(absl::StatusOr<R> (C::*ptmf)(Args...))
195+
: ptmf(ptmf) {}
196+
R operator()(C &instance, Args... args) {
197+
return ValueOrThrow((instance.*ptmf)(std::forward<Args>(args)...));
198+
}
199+
absl::StatusOr<R> (C::*ptmf)(Args...);
200+
};
201+
template <typename R, typename C, typename... Args>
202+
struct ValueOrThrowWrapper<absl::StatusOr<R>(Args...) const, C> {
203+
explicit ValueOrThrowWrapper(absl::StatusOr<R> (C::*ptmf)(Args...) const)
204+
: ptmf(ptmf) {}
205+
R operator()(const C &instance, Args... args) const {
206+
return ValueOrThrow((instance.*ptmf)(std::forward<Args>(args)...));
207+
}
208+
absl::StatusOr<R> (C::*ptmf)(Args...) const;
209+
};
210+
211+
} // namespace jax
212+
213+
#endif // JAXLIB_ABSL_STATUS_CASTERS_H_

0 commit comments

Comments
 (0)