Skip to main content

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:

//
buck2_lab/greeter_bin
:

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 under path/to/dir, including sub-dir

  • buck build //path/to/dir: build all targets under package path/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 builds
  • buck 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;