"""Rule for running paket commands using the Bazel-managed dotnet toolchain."""
def _paket_deps_impl(ctx):
toolchain = ctx.toolchains["@rules_dotnet//dotnet:toolchain_type"]
dotnet = toolchain.runtime.files_to_run.executable
is_windows = ctx.target_platform_has_constraint(ctx.attr._windows_constraint[platform_common.ConstraintValueInfo])
if is_windows:
script = _create_windows_script(ctx, dotnet)
else:
script = _create_unix_script(ctx, dotnet)
runfiles = ctx.runfiles(files = [dotnet])
runfiles = runfiles.merge(toolchain.runtime.default_runfiles)
return [
DefaultInfo(
executable = script,
runfiles = runfiles,
),
]
def _to_runfiles_path(short_path):
"""Convert a short_path to a runfiles path."""
if short_path.startswith("../"):
return short_path[3:]
return "_main/" + short_path
def _create_unix_script(ctx, dotnet):
"""Create bash script for Unix/macOS/Linux."""
dotnet_runfiles_path = _to_runfiles_path(dotnet.short_path)
mode = ctx.attr.mode
script_content = """#!/usr/bin/env bash
set -euo pipefail
# Locate runfiles directory
if [[ -d "$0.runfiles/_main" ]]; then
RUNFILES_DIR="$0.runfiles"
elif [[ -n "${{RUNFILES_DIR:-}}" ]]; then
RUNFILES_DIR="$RUNFILES_DIR"
else
echo "ERROR: Could not locate runfiles directory" >&2
exit 1
fi
DOTNET="$RUNFILES_DIR/{dotnet}"
# Find the workspace root (where dotnet/.config/dotnet-tools.json lives)
WORKSPACE_ROOT="${{BUILD_WORKSPACE_DIRECTORY:-$RUNFILES_DIR/_main}}"
DOTNET_DIR="$WORKSPACE_ROOT/dotnet"
if [[ ! -f "$DOTNET_DIR/.config/dotnet-tools.json" ]]; then
echo "ERROR: Could not find dotnet/.config/dotnet-tools.json" >&2
echo "Make sure you're running from the workspace root" >&2
exit 1
fi
cd "$DOTNET_DIR"
echo "Restoring dotnet tools..."
"$DOTNET" tool restore
echo "Running paket {mode}..."
"$DOTNET" tool run paket {mode}
echo "Done. Now run: bazel run @rules_dotnet//tools/paket2bazel:paket2bazel -- --dependencies-file $(pwd)/paket.dependencies --output-folder $(pwd)"
""".format(
dotnet = dotnet_runfiles_path,
mode = mode,
)
script = ctx.actions.declare_file(ctx.label.name + ".sh")
ctx.actions.write(
output = script,
content = script_content,
is_executable = True,
)
return script
def _create_windows_script(ctx, dotnet):
"""Create batch script for Windows."""
dotnet_runfiles_path = _to_runfiles_path(dotnet.short_path).replace("/", "\\")
mode = ctx.attr.mode
script_content = """@echo off
setlocal
set RUNFILES_DIR=%~dp0%~n0.runfiles
set DOTNET=%RUNFILES_DIR%\\{dotnet_path}
if defined BUILD_WORKSPACE_DIRECTORY (
set WORKSPACE_ROOT=%BUILD_WORKSPACE_DIRECTORY%
) else (
set WORKSPACE_ROOT=%RUNFILES_DIR%\\_main
)
set DOTNET_DIR=%WORKSPACE_ROOT%\\dotnet
if not exist "%DOTNET_DIR%\\.config\\dotnet-tools.json" (
echo ERROR: Could not find dotnet\\.config\\dotnet-tools.json >&2
exit /b 1
)
cd /d "%DOTNET_DIR%"
echo Restoring dotnet tools...
"%DOTNET%" tool restore
if %ERRORLEVEL% neq 0 exit /b %ERRORLEVEL%
echo Running paket {mode}...
"%DOTNET%" tool run paket {mode}
if %ERRORLEVEL% neq 0 exit /b %ERRORLEVEL%
echo Done. Now run: bazel run @rules_dotnet//tools/paket2bazel:paket2bazel -- --dependencies-file %cd%\\paket.dependencies --output-folder %cd%
""".format(
dotnet_path = dotnet_runfiles_path,
mode = mode,
)
script = ctx.actions.declare_file(ctx.label.name + ".bat")
ctx.actions.write(
output = script,
content = script_content,
is_executable = True,
)
return script
paket_deps = rule(
implementation = _paket_deps_impl,
attrs = {
"mode": attr.string(
doc = "Paket command to run: 'update' for latest versions, 'install' to sync lockfile",
mandatory = True,
values = ["update", "install"],
),
"_windows_constraint": attr.label(
default = "@platforms//os:windows",
),
},
executable = True,
toolchains = ["@rules_dotnet//dotnet:toolchain_type"],
)