Skip to content

Commit 722e65c

Browse files
authored
[dotnet] Add bazel rules for dotnet format and paket deps (#16986)
* [dotnet] Add bazel rules for dotnet format and paket deps * [dotnet] Integrate dotnet format into format.sh and rake tasks
1 parent d87a134 commit 722e65c

File tree

6 files changed

+295
-2
lines changed

6 files changed

+295
-2
lines changed

BUILD.bazel

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ filegroup(
2525
name = "rakefile",
2626
srcs = [
2727
"Rakefile",
28-
] + glob(["rake_tasks/*.rake", "rake_tasks/*.rb"]),
28+
] + glob([
29+
"rake_tasks/*.rake",
30+
"rake_tasks/*.rb",
31+
]),
2932
visibility = ["//rb:__subpackages__"],
3033
)
3134

dotnet/BUILD.bazel

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
load("@rules_pkg//pkg:zip.bzl", "pkg_zip")
2-
load("//dotnet:defs.bzl", "nuget_push")
2+
load("//dotnet:defs.bzl", "dotnet_format", "nuget_push", "paket_deps")
33
load("//dotnet/private:docfx.bzl", "docfx")
44

55
exports_files([
@@ -51,3 +51,17 @@ nuget_push(
5151
"//dotnet/src/webdriver:webdriver-pack",
5252
],
5353
)
54+
55+
paket_deps(
56+
name = "paket-update",
57+
mode = "update",
58+
)
59+
60+
paket_deps(
61+
name = "paket-install",
62+
mode = "install",
63+
)
64+
65+
dotnet_format(
66+
name = "format",
67+
)

dotnet/defs.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
load("@rules_dotnet//dotnet:defs.bzl", _csharp_binary = "csharp_binary", _csharp_library = "csharp_library", _csharp_test = "csharp_test")
22
load("//dotnet:selenium-dotnet-version.bzl", "SUPPORTED_DEVTOOLS_VERSIONS")
3+
load("//dotnet/private:dotnet_format.bzl", _dotnet_format = "dotnet_format")
34
load("//dotnet/private:dotnet_nunit_test_suite.bzl", _dotnet_nunit_test_suite = "dotnet_nunit_test_suite")
45
load("//dotnet/private:generate_devtools.bzl", _generate_devtools = "generate_devtools")
56
load("//dotnet/private:generate_resources.bzl", _generated_resource_utilities = "generated_resource_utilities")
@@ -8,6 +9,7 @@ load("//dotnet/private:nuget_pack.bzl", _nuget_pack = "nuget_pack")
89
load("//dotnet/private:nuget_package.bzl", _nuget_package = "nuget_package")
910
load("//dotnet/private:nuget_push.bzl", _nuget_push = "nuget_push")
1011
load("//dotnet/private:nunit_test.bzl", _nunit_test = "nunit_test")
12+
load("//dotnet/private:paket_deps.bzl", _paket_deps = "paket_deps")
1113

1214
def devtools_version_targets():
1315
targets = []
@@ -18,6 +20,7 @@ def devtools_version_targets():
1820
csharp_binary = _csharp_binary
1921
csharp_library = _csharp_library
2022
csharp_test = _csharp_test
23+
dotnet_format = _dotnet_format
2124
dotnet_nunit_test_suite = _dotnet_nunit_test_suite
2225
generate_devtools = _generate_devtools
2326
generated_resource_utilities = _generated_resource_utilities
@@ -26,3 +29,4 @@ nuget_pack = _nuget_pack
2629
nuget_package = _nuget_package
2730
nuget_push = _nuget_push
2831
nunit_test = _nunit_test
32+
paket_deps = _paket_deps

dotnet/private/dotnet_format.bzl

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
"""Rule for running dotnet format using the Bazel-managed dotnet toolchain."""
2+
3+
def _dotnet_format_impl(ctx):
4+
toolchain = ctx.toolchains["@rules_dotnet//dotnet:toolchain_type"]
5+
dotnet = toolchain.runtime.files_to_run.executable
6+
7+
is_windows = ctx.target_platform_has_constraint(ctx.attr._windows_constraint[platform_common.ConstraintValueInfo])
8+
9+
if is_windows:
10+
script = _create_windows_script(ctx, dotnet)
11+
else:
12+
script = _create_unix_script(ctx, dotnet)
13+
14+
runfiles = ctx.runfiles(files = [dotnet])
15+
runfiles = runfiles.merge(toolchain.runtime.default_runfiles)
16+
17+
return [
18+
DefaultInfo(
19+
executable = script,
20+
runfiles = runfiles,
21+
),
22+
]
23+
24+
def _to_runfiles_path(short_path):
25+
"""Convert a short_path to a runfiles path."""
26+
if short_path.startswith("../"):
27+
return short_path[3:]
28+
return "_main/" + short_path
29+
30+
def _create_unix_script(ctx, dotnet):
31+
"""Create bash script for Unix/macOS/Linux."""
32+
dotnet_runfiles_path = _to_runfiles_path(dotnet.short_path)
33+
34+
script_content = """#!/usr/bin/env bash
35+
set -euo pipefail
36+
37+
# Locate runfiles directory
38+
if [[ -d "$0.runfiles/_main" ]]; then
39+
RUNFILES_DIR="$0.runfiles"
40+
elif [[ -n "${{RUNFILES_DIR:-}}" ]]; then
41+
RUNFILES_DIR="$RUNFILES_DIR"
42+
else
43+
echo "ERROR: Could not locate runfiles directory" >&2
44+
exit 1
45+
fi
46+
47+
DOTNET="$RUNFILES_DIR/{dotnet}"
48+
49+
# Find the workspace root
50+
WORKSPACE_ROOT="${{BUILD_WORKSPACE_DIRECTORY:-$RUNFILES_DIR/_main}}"
51+
DOTNET_DIR="$WORKSPACE_ROOT/dotnet"
52+
53+
cd "$DOTNET_DIR"
54+
55+
echo "Running dotnet format on all projects..."
56+
find "$DOTNET_DIR/src" "$DOTNET_DIR/test" -name "*.csproj" 2>/dev/null | while read -r proj; do
57+
echo " Formatting $proj..."
58+
"$DOTNET" format "$proj" || exit 1
59+
done || exit 1
60+
61+
echo "Done."
62+
""".format(
63+
dotnet = dotnet_runfiles_path,
64+
)
65+
66+
script = ctx.actions.declare_file(ctx.label.name + ".sh")
67+
ctx.actions.write(
68+
output = script,
69+
content = script_content,
70+
is_executable = True,
71+
)
72+
return script
73+
74+
def _create_windows_script(ctx, dotnet):
75+
"""Create batch script for Windows."""
76+
dotnet_runfiles_path = _to_runfiles_path(dotnet.short_path).replace("/", "\\")
77+
78+
script_content = """@echo off
79+
setlocal
80+
81+
set RUNFILES_DIR=%~dp0%~n0.runfiles
82+
set DOTNET=%RUNFILES_DIR%\\{dotnet_path}
83+
84+
if defined BUILD_WORKSPACE_DIRECTORY (
85+
set WORKSPACE_ROOT=%BUILD_WORKSPACE_DIRECTORY%
86+
) else (
87+
set WORKSPACE_ROOT=%RUNFILES_DIR%\\_main
88+
)
89+
set DOTNET_DIR=%WORKSPACE_ROOT%\\dotnet
90+
91+
cd /d "%DOTNET_DIR%"
92+
93+
echo Running dotnet format on all projects...
94+
for /r "%DOTNET_DIR%\\src" %%%%p in (*.csproj) do (
95+
echo Formatting %%%%p...
96+
"%DOTNET%" format "%%%%p" || exit /b 1
97+
)
98+
for /r "%DOTNET_DIR%\\test" %%%%p in (*.csproj) do (
99+
echo Formatting %%%%p...
100+
"%DOTNET%" format "%%%%p" || exit /b 1
101+
)
102+
103+
echo Done.
104+
""".format(
105+
dotnet_path = dotnet_runfiles_path,
106+
)
107+
108+
script = ctx.actions.declare_file(ctx.label.name + ".bat")
109+
ctx.actions.write(
110+
output = script,
111+
content = script_content,
112+
is_executable = True,
113+
)
114+
return script
115+
116+
dotnet_format = rule(
117+
implementation = _dotnet_format_impl,
118+
attrs = {
119+
"_windows_constraint": attr.label(
120+
default = "@platforms//os:windows",
121+
),
122+
},
123+
executable = True,
124+
toolchains = ["@rules_dotnet//dotnet:toolchain_type"],
125+
)

dotnet/private/paket_deps.bzl

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
"""Rule for running paket commands using the Bazel-managed dotnet toolchain."""
2+
3+
def _paket_deps_impl(ctx):
4+
toolchain = ctx.toolchains["@rules_dotnet//dotnet:toolchain_type"]
5+
dotnet = toolchain.runtime.files_to_run.executable
6+
7+
is_windows = ctx.target_platform_has_constraint(ctx.attr._windows_constraint[platform_common.ConstraintValueInfo])
8+
9+
if is_windows:
10+
script = _create_windows_script(ctx, dotnet)
11+
else:
12+
script = _create_unix_script(ctx, dotnet)
13+
14+
runfiles = ctx.runfiles(files = [dotnet])
15+
runfiles = runfiles.merge(toolchain.runtime.default_runfiles)
16+
17+
return [
18+
DefaultInfo(
19+
executable = script,
20+
runfiles = runfiles,
21+
),
22+
]
23+
24+
def _to_runfiles_path(short_path):
25+
"""Convert a short_path to a runfiles path."""
26+
if short_path.startswith("../"):
27+
return short_path[3:]
28+
return "_main/" + short_path
29+
30+
def _create_unix_script(ctx, dotnet):
31+
"""Create bash script for Unix/macOS/Linux."""
32+
dotnet_runfiles_path = _to_runfiles_path(dotnet.short_path)
33+
mode = ctx.attr.mode
34+
35+
script_content = """#!/usr/bin/env bash
36+
set -euo pipefail
37+
38+
# Locate runfiles directory
39+
if [[ -d "$0.runfiles/_main" ]]; then
40+
RUNFILES_DIR="$0.runfiles"
41+
elif [[ -n "${{RUNFILES_DIR:-}}" ]]; then
42+
RUNFILES_DIR="$RUNFILES_DIR"
43+
else
44+
echo "ERROR: Could not locate runfiles directory" >&2
45+
exit 1
46+
fi
47+
48+
DOTNET="$RUNFILES_DIR/{dotnet}"
49+
50+
# Find the workspace root (where dotnet/.config/dotnet-tools.json lives)
51+
WORKSPACE_ROOT="${{BUILD_WORKSPACE_DIRECTORY:-$RUNFILES_DIR/_main}}"
52+
DOTNET_DIR="$WORKSPACE_ROOT/dotnet"
53+
54+
if [[ ! -f "$DOTNET_DIR/.config/dotnet-tools.json" ]]; then
55+
echo "ERROR: Could not find dotnet/.config/dotnet-tools.json" >&2
56+
echo "Make sure you're running from the workspace root" >&2
57+
exit 1
58+
fi
59+
60+
cd "$DOTNET_DIR"
61+
62+
echo "Restoring dotnet tools..."
63+
"$DOTNET" tool restore
64+
65+
echo "Running paket {mode}..."
66+
"$DOTNET" tool run paket {mode}
67+
68+
echo "Done. Now run: bazel run @rules_dotnet//tools/paket2bazel:paket2bazel -- --dependencies-file $(pwd)/paket.dependencies --output-folder $(pwd)"
69+
""".format(
70+
dotnet = dotnet_runfiles_path,
71+
mode = mode,
72+
)
73+
74+
script = ctx.actions.declare_file(ctx.label.name + ".sh")
75+
ctx.actions.write(
76+
output = script,
77+
content = script_content,
78+
is_executable = True,
79+
)
80+
return script
81+
82+
def _create_windows_script(ctx, dotnet):
83+
"""Create batch script for Windows."""
84+
dotnet_runfiles_path = _to_runfiles_path(dotnet.short_path).replace("/", "\\")
85+
mode = ctx.attr.mode
86+
87+
script_content = """@echo off
88+
setlocal
89+
90+
set RUNFILES_DIR=%~dp0%~n0.runfiles
91+
set DOTNET=%RUNFILES_DIR%\\{dotnet_path}
92+
93+
if defined BUILD_WORKSPACE_DIRECTORY (
94+
set WORKSPACE_ROOT=%BUILD_WORKSPACE_DIRECTORY%
95+
) else (
96+
set WORKSPACE_ROOT=%RUNFILES_DIR%\\_main
97+
)
98+
set DOTNET_DIR=%WORKSPACE_ROOT%\\dotnet
99+
100+
if not exist "%DOTNET_DIR%\\.config\\dotnet-tools.json" (
101+
echo ERROR: Could not find dotnet\\.config\\dotnet-tools.json >&2
102+
exit /b 1
103+
)
104+
105+
cd /d "%DOTNET_DIR%"
106+
107+
echo Restoring dotnet tools...
108+
"%DOTNET%" tool restore
109+
if %ERRORLEVEL% neq 0 exit /b %ERRORLEVEL%
110+
111+
echo Running paket {mode}...
112+
"%DOTNET%" tool run paket {mode}
113+
if %ERRORLEVEL% neq 0 exit /b %ERRORLEVEL%
114+
115+
echo Done. Now run: bazel run @rules_dotnet//tools/paket2bazel:paket2bazel -- --dependencies-file %cd%\\paket.dependencies --output-folder %cd%
116+
""".format(
117+
dotnet_path = dotnet_runfiles_path,
118+
mode = mode,
119+
)
120+
121+
script = ctx.actions.declare_file(ctx.label.name + ".bat")
122+
ctx.actions.write(
123+
output = script,
124+
content = script_content,
125+
is_executable = True,
126+
)
127+
return script
128+
129+
paket_deps = rule(
130+
implementation = _paket_deps_impl,
131+
attrs = {
132+
"mode": attr.string(
133+
doc = "Paket command to run: 'update' for latest versions, 'install' to sync lockfile",
134+
mandatory = True,
135+
values = ["update", "install"],
136+
),
137+
"_windows_constraint": attr.label(
138+
default = "@platforms//os:windows",
139+
),
140+
},
141+
executable = True,
142+
toolchains = ["@rules_dotnet//dotnet:toolchain_type"],
143+
)

scripts/format.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ section "Buildifier"
1414
echo " buildifier" >&2
1515
bazel run //:buildifier
1616

17+
section "Dotnet"
18+
echo " dotnet format" >&2
19+
bazel run //dotnet:format
20+
1721
section "Java"
1822
echo " google-java-format" >&2
1923
find "$PWD/java" -type f -name '*.java' | xargs "$GOOGLE_JAVA_FORMAT" --replace

0 commit comments

Comments
 (0)