From Tutorial to Concepts
In the previous tutorials, we’ve created buck files, defined a couple of buck targets and successfully built and ran the “Hello World” rust binary and even wrote and ran a test. Great job done! Now, let’s go through the journey again, weaving in the core concepts as we go along to see what gets involved during this learning process.
Understanding Target Labels
This is one of the most important concepts to understand when using Buck2. It is a precise way to identify any buildable unit in your codebase.
In the tutorials, you encoutered the following target label like
root//buck2_lab/greeter_bin:main
.
Here is the anatomy of a target label:
Cell
Cell defines a directory tree of one or
more buck packages. The root of a buck
cell contains a global configuration file called
.buckconfig
.
You can run buck2 audit cell
to inspect the abs path of each cell root.
Package
The existence of a BUCK file (buck2_lab/greeter_bin/BUCK
) defines a buck
package buck2_lab/greeter_bin
isn't just a directory. If a buck target
uses the source file as input, that target is regarded as the owner of the
source.
Target name
The name of the target in the package. It is the name we defined in the BUCK file.
rust_binary(
name = "main",
srcs = ["main.rs"],
...
)
It should be unique within the package.
Buck File
In the lab, you’ve already created three build files:
greeter_bin/BUCK
greeter_lib/BUCK
logging_lib/BUCK
Although configurable, the name of the build file normally is just BUCK.
In these BUCK files, you’ve written a couple of
buck targets, :main
, :library
,
:logging_lib
and :test
. Buck targets are instances of
build rules, which defines how the target should
be built. For example, target :main is of rule type
rust_binary, the output artifact will
be a binary that’s runnable, while :library
, :logging_lib
are of rule type
rust_library, the output of which will
be a library that can be linked to the binary.
Referring to buck targets in BUCK files and CLI commands need to follow a special target pattern, which looks like:
cell//path/to/dir:target
or cell//path/to/dir/...
you will soon become very familiar with these patterns during daily development.
Tips:
- Buck targets can be either build rules or macros, which are wrappers/extensions around native build rules, macros are usually defined .bzl files.
- Buck uses starlark language which is a dialect of python, to define build rules and macros.
Visualizing Your Tutorial Project
Now that we understand the basic terminology, let's visualize what you built. We'll start with the simple file structure, then explore how Buck2 interprets these files as packages and targets.
File Structure Overview
Here's the complete project structure you built through the tutorials:
From Files to Targets
Now that we understand packages and target concepts, let's see how your BUCK files define targets and their relationships:
The Complete Picture
Finally, let's put it all together. This comprehensive diagram shows how your file structure, Buck2 packages, targets, and dependencies all interconnect to form a cohesive build system:
Diagram Legend:
- Dotted arrows: Show how BUCK files define targets
- Thick arrows: Show dependency relationships between targets
- Double circles: Represent Buck2 targets with 🎯 icon
- Curly braces: Contain target attributes and configurations
- Subgraphs: Group targets with their attributes
- 📁 Icons: Represent directories and packages
- 📄 Icons: Represent files (BUCK files and source files)
Load Function and Attributes
Some buck file starts with one or more load functions, which load macros from
.bzl files for this buck file to use, this is similar to load or include
functions in other programming languages, load function takes the following
syntax: load("@cell//path/to/bzl:some_bzl.bzl", "some_macro")
Each buck target has a set of attributes, which provide powerful ways to define
and customize how the build should be done, you can inspect the
rule definition to see what these
attributes are and the syntax to define them. Some attributes are mandatory and
some are optional with defaults. Most attributes are rule-specific but there are
common ones such as name
, srcs
, deps
and
visibility.
Tips:
- Without using load function, buck will default to using native rules with the same name;
- One load function can load multiple macros from the same
.bzl
file - Deps and visibility are important attributes to understand, please read the docs!
Dependencies and Dependency Graph
As you craft your program, you may need to rely on other targets, known as
dependencies. In the "Hello World"
exercise, :library
and :logging_lib
targets are
dependencies of the :main
target. One target can depend on multiple
dependencies, which in turn can have their own dependencies to form a web of
connections, a so-called
dependency graph, our
lab renders a very simple dependency graph with maximum depth of 2, in real
world, the graph will be much bigger and one top level target could have tens of
thousands dependencies.
Understanding and managing the dependency graph of your buck target is important for effective development. Buck also offers powerful query tools to explore the dependency graph.
Tips:
- Dependency graph size affects build speed and memory usage greatly
- The graph is a DAG graph. So cycles in the dependency graph (circular
dependency), something like
A->B->...->X->A
, are not allowed, buck will emit error when cycle is detected.
Buck Commands
In the lab, once buck and source files are in place, we use
buck2 build :main --show-output
to build the :main
target. This uses the
buck build command to compile and link your rust
code into a binary. Now let’s take a closer look at this command. A buck command
is usually composed of a command type ( build
, run
, test
...), some
target pattern, and options. Command options
can offer extra configurations to do the build.
Buck accepts multiple targets in one command, such as:
-
buck build target1 target2 target3
builds 3 targets in one command -
buck build //path/to/dir/...
builds all targets underpath/to/dir
, including sub-dir -
buck build //path/to/dir:
build all targets under packagepath/to/dir
, without sub packages -
buck build @a-file
builds targets listed in a-file, which is a plain text file
Buck builds multiple targets in parallel, unless there are dependencies between them.
Once target is built, you can use buck run, buck test and buck install to test the code.
As you become more adept, you can explore other powerful buck commands, such as:
buck query
with various options to analyze the dependency graph, this set of commands is by far the most powerful and complicated buck commands to use;buck kill
to stop running buck daemon, this is sometimes needed to recover from a failed build due to bad daemon state;buck clean
to remove build artifacts from buck-out,this is a remedy to recover from failed build due to either bad daemon or bad artifacts in cache;buck log
to see information about previous buildsbuck bxl
to run bxl scripts. BXL is a buck2 script language using starlark syntax to write complex query or build logic.
Tips:
- Buck offers help menus for all commands, try the -h option, you can buck -h or buck build -h to see all available commands and options
Buck2 Command Flow
Here's how Buck2 commands work in your tutorial workflow:
Console and Output
During the lab, you’ve seen the console output during build and test process, these outputs give information about the execution progress and state. Buck offers different kinds of consoles for various purposes, and the interactive feature helps during debugging.
Buck-out
So you’ve successfully built the target and run it. Finally, let’s briefly talk about buck-out, which is an important concept yet hard to understand initially. We know that buck builds complicated targets with big dependency graphs and generates tons of outputs for test and run. Where should these output artifacts be stored at? The outputs should not be stored at the source directory which is tracked by the source control system. The outputs also need to be reused/cached for later builds to save build time. So here comes buck-out,it has the following characteristics:
- It’s under repo root;
- It has a unique file structure, some directories are hashed for caching purpose;
- It stores a lot of data including output artifacts, buck log files, tmp data, etc;
- It is managed by buck and developers normally should not manipulate;
Tips:
- Do NOT delete artifacts manually from buck-out directory and expect buck to rebuild them, buck doesn’t track things under buck-out, use buck clean instead;