如何用类GDB调试器逐步调试Bazel Skylark脚本(C++编译场景)
Great question! Debugging Starlark (formerly known as Skylark, the language powering Bazel's build scripts) to trace execution flow is totally possible, and there are a couple of solid approaches that mirror the step-by-step control you get with GDB for C++. Let’s break them down:
1. Use Bazel’s Built-In Debugger (Official, GDB-like Experience)
Bazel ships with a dedicated Starlark debugger that lets you set breakpoints, step through code, inspect variables, and more. It’s the closest thing to GDB for your build scripts.
How to use it:
- Start your build with the debugger flag. For recent Bazel versions, this is simply:
(Older versions might requirebazel build //your:target --debugger--experimental_debuggerinstead.) - This will drop you into an interactive debug session with a
(debug)prompt.
Key Debugger Commands:
- Set breakpoints:
- By line number:
break //path/to/your/script.bzl:42(targets line 42 in the specified.bzlfile) - By function/rule name:
break my_custom_rule(stops when themy_custom_rulefunction is called) - Conditional breakpoints:
break //script.bzl:10 if ctx.attr.min_size > 100(triggers only when the condition is true)
- By line number:
- Step through execution:
step: Step into the next function callnext: Execute the current line and move to the next (skips function calls)finish: Run until the current function exitscontinue: Resume execution until the next breakpoint or build completion
- Inspect variables:
print my_variable: Print the value of a variable in the current scopelocals: List all local variables in the current contextprint ctx.attr: Inspect attributes passed to a rule context
- Manage breakpoints:
clear: Remove all breakpointsclear //script.bzl:42: Remove a specific breakpointdisable 1: Disable breakpoint number 1 (useinfo breakpointsto list all breakpoints with IDs)
2. Quick-and-Dirty Debugging with Print Statements
If you don’t need full debugger control, adding print statements to your Starlark code is a fast way to trace execution and variable values.
Tips for effective printing:
- Add prints at key entry/exit points of functions:
def my_custom_rule(ctx): print("Entering my_custom_rule with ctx:", ctx) # ... rest of your code ... print("Exiting my_custom_rule, output files:", ctx.outputs) - For complex objects (like
ctx), usestr()to convert them to readable strings:print("Rule attributes:", str(ctx.attr)) - To ensure prints show up in your build output, run Bazel with standard verbosity (no need for extra flags unless you’ve silenced output).
3. Additional Tools for Context
While not direct debuggers, these tools help you understand build flow alongside debugging:
bazel query //your:target: Shows the dependency graph of your target, helping you see which scripts are executed--verbose_failures: When your build fails, this flag prints detailed stack traces for Starlark errors, pointing you to exact lines in your scriptsassertstatements: Add sanity checks to your code to catch unexpected values early:assert ctx.attr.srcs != [], "my_custom_rule requires at least one source file"
Step-by-Step Tracing Workflow Example
Let’s walk through a typical debugging session:
- Start the debugger:
bazel build //my/package:my_target --debugger - Set a breakpoint at your rule’s entry line:
break //my/package:rules.bzl:15 - Run to the breakpoint:
continue - Inspect the context:
print ctx.attr.name - Step into a helper function:
step - List local variables in the helper:
locals - Continue execution until the next breakpoint:
continue - Exit the debugger when done:
quit
Remember, this debugger works during Bazel’s analysis phase (when Starlark scripts are executed to generate build actions). For debugging the actual C++ compilation or binary execution, you’ll still use GDB/lldb on the compiled artifacts.
内容的提问来源于stack exchange,提问作者Jiewen Zheng

