Bazel labels

Bazel labels are string references to source files and items defined in BUILD.bazel files. Their general format is:

@<repository_name>//<package_name>:<target_name>

Where:

  • @<repository_name>// designates the directory of a named Bazel workspace.

    As a convenience, this can be abbreviated as simply // for the current workspace (the one that contains the current BUILD.bazel file). Note also that @// is used to designate the project's root workspace, even when used in external repositories.

  • <package_name> is the package's directory path, relative to the workspace directory. For example, in the labels //src:main.cc or //src/lib:foo, the package names are src and src/lib respectively.

    This can be empty, e.g. //:BUILD.bazel points to the build file in the current workspace's top-level directory.

  • For source files, <target_name> is the file path relative to its parent package's directory, and may include a sub-directory part. For example for //src:main.ccor //src:extra/extra.cc, the target name is main.cc and extra/extra.cc respectively.

  • For other items, <target_name> corresponds to an item (build artifact, build setting, configuration condition, etc) defined in a BUILD.bazel file.

    By convention, its name attribute should not include a directory separator, except in very rare cases, to avoid confusion with sources.

    This can be confusing to developers coming from other build systems which differentiate the type of items in their build graph (e.g. GN uses "Targets", "Configs", "Toolchains" and "Pools" to designate different things).

Shortened expressions for labels are also supported:

  • If the label begins with a repository name and does not include a colon, it is a package path, and points to an item with the same name. For example //src/foo is equivalent to //src/foo:foo.

  • If the label begins with a colon, it is a name relative to the current package. For example ":bar" and ":extra/bar.cc" that appear in src/foo/BUILD.bazel are equivalent to //src/foo:bar and //src/foo:extra/bar.cc respectively.

  • If the label has no repository name and no colon, it is always a name relative to the current package, even if it includes a directory separator. E.g. "bar/bar.cc" in src/foo/BUILD.bazel always refer to //src/foo:bar/bar.cc.

    Note that this is not the same as //src/foo/bar:bar.cc

Relative labels and package ownership

Since each source file can only belong to a single package, relative labels can be invalid. For example, in a project that looks like the following:

/home/user/project/
    WORKSPACE.bazel
    src/
        BUILD.bazel
        main.cc
        extra/
            extra.cc
        lib/
            BUILD.bazel
            foo.cc
            foo.h

The foo.cc file belongs to the package src/lib, so its label must be //src/lib:foo.cc.

Using a label like src:lib/foo.cc in src/BUILD.bazel is an error:

# From src/BUILD.bazel
cc_binary(
  name = "program",
  srcs = [
    "extra/extra.cc",
    "lib/foo.cc",       # Error: Label '//src:lib/foo.cc' is invalid because 'src/lib' is a subpackage
    "lib/foo.h"         # Error: Label '//src:lib/foo.h' is invalid because 'src/lib' is a subpackage
    "main.cc",
  ],
)

Source file access from other packages

By default, the source files of a given package cannot be accessed from other packages, and relative package labels are invalid, as in:

# From src/BUILD.bazel
cc_binary(
  name = "program",
  srcs = [
    "extra/extra.cc",
    "lib:foo.cc",    # Error: invalid label 'lib:foo.cc': absolute label must begin with '@' or '//'
    "lib:foo.h"      # Error: invalid label 'lib:foo.h': absolute label must begin with '@' or '//'
    "main.cc",
  ],
)

And even when using the right absolute label, and error happens:

# From src/BUILD.bazel
cc_binary(
  name = "program",
  srcs = [
    "extra/extra.cc",
    "//src/lib:foo.cc",    # Error: no such target '//src/lib:foo.cc': target 'foo.h' not declared in package 'src/lib'
    "//src/lib:foo.h"      # Error: no such target '//src/lib:foo.h': target 'foo.h' not declared in package 'src/lib'
    "main.cc",
  ],
)

Direct access to files across package boundaries can be granted by export_files():

# From src/lib/BUILD.bazel
export_files([
  "foo.cc" ,
  "foo.h" ,
])

# From src/BUILD.bazel
cc_binary(
  name = "program",
  srcs = [
    "extra/extra.cc",
    "//src/lib:foo.cc",    # OK
    "//src/lib:foo.h"      # OK
    "main.cc",
  ],
)

Target access from other packages

Labelled items defined in a BUILD.bazel file that are not source files need no export, but their visibility attribute must allow their use outside of their own package:

# From src/lib/BUILD.bazel
cc_library(
  name = " lib" ,
  srcs = [ " foo.cc"  ],
  hdrs = [ " foo.h"  ],
  visibility = [ " //visibility:public" ],  # Anyone can reference this directly!
)

# From src/BUILD.bazel
cc_binary(
  name = "program",
  srcs = [
    "extra/extra.cc",
    "main.cc",
  ],
  deps = [ "lib" ],   # OK!
)

By default, items are only visible to other items in the same package. This can be changed by using a package() directive to change the default visibility of all items defined in a package:

# From src/lib/BUILD.bazel

# Ensure that all items defined in this file are visible to anyone
package(default_visibility = ["//visibility:public"])

cc_library(
  name = " lib" ,
  srcs = [ "foo.cc" ],
  hdrs = [ "foo.h" ],
)

# From src/BUILD.bazel
cc_binary(
  name = "program",
  srcs = [
    "extra/extra.cc",
    "main.cc",
  ],
  deps = [ "lib" ],   # OK!
)

A warning about virtual packages

Avoid creating top-level directories in a project with the following names:

  • conditions
  • command_line_option
  • external
  • visibility

Because Bazel uses a number of hard-coded "virtual packages" in labels within BUILD.bazel files. For example:

  //visibility:public
  //conditions:default
  //command_line_option:copt

The case of external is a bit different: it does not appear in BUILD.bazel files, but used internally to manage external repositories. This confuses Bazel when used as a project directory

Canonical repository names

Since Bazel 6.0, repository names in labels can also begin with @@.

When the optional BzlMod feature is enabled, these labels are used as alternative but unique label names for external repositories, which becomes important when complex transitive dependency trees are used in a project.

For example, @@com_acme_anvil.1.0.3 could be a canonical name for the workspace directory identified by @anvil in the project's own BUILD.bazel files, and by @acme_anvil when it appears in an external repository (e.g. inside @foo//:BUILD.bazel). All three labels would refer to the content of the same directory.

Canonical repository names do not appear in BUILD.bazel files, however, they will appear during the analysis phase (when executing Starlark functions that look at label values), or when looking at the result of Bazel queries.