Bazel Book

exports_files()

recommended

Most cross-package labels in Bazel point at rule targets. exports_files() is the exception for the case where another package genuinely needs a raw file from yours. By default, source files are owned by their package, as in 0.1.3 Package, and are not visible outside it; exports_files() is the explicit way to publish selected files across that boundary1,2.

Exporting a Raw File

exports_files() declares that specific files in the current package may be referenced from other packages3. The exported file becomes a source-file target with a normal label:

# //frobber/data/BUILD
exports_files(["readme.txt"])

# //frobber/bin/BUILD
cc_binary(
    name = "my-program",
    data = ["//frobber/data:readme.txt"],
)

This is useful when another package needs the file itself rather than a rule wrapped around it. The important distinction is that //frobber/data:readme.txt is still just a file target, not a library or binary target with its own behavior.

Visibility Still Applies

exports_files() takes the same idea of visibility from 0.2.4 Visibility, but attaches it directly to the exported files2,3. One detail is easy to miss: if you omit the visibility parameter, the exported files are visible to every package3.

That makes the no-argument form convenient, but broad. If only one subtree should see the file, say so explicitly:

exports_files(
    ["schema.sql"],
    visibility = ["//state/indexer:__subpackages__"],
)

That is the targeted fix shown in a real cross-package file-visibility error and follow-up example4. The rule stays simple: exported files can be shared, but they do not have to be public.

One boundary to keep in mind: exports_files() is for source files. It may not be used to override the visibility of a generated file2.

extra

Legacy Implicit File Export

Historically, Bazel sometimes let source files become visible outside the package without an explicit exports_files() call. That legacy behavior depended on the --incompatible_no_implicit_file_export flag and could fall back to the package's default_visibility from 0.2.6 package() Function2.

The official guidance is not to rely on that behavior. If a raw source file needs non-private visibility, write an explicit exports_files() declaration for it2.

Prefer a Rule When Possible

exports_files() is useful, but it is not the preferred way to publish most build-facing interfaces. When possible, expose a rule target instead of a bare source file2. The official example is to wrap a .java file in a non-private java_library instead of exporting the file directly2.

key takeaway

Use exports_files() when another package truly needs a raw file label like //pkg:data.txt. Keep it explicit, set visibility deliberately, and prefer exporting a rule target instead whenever the file is really part of a broader package API.

Check your understanding

1.What is exports_files() used for?

2.What happens if you call exports_files() without specifying a visibility parameter?

3.When should you prefer a rule target over exports_files()?

Answer all questions to check

Footnotes

  1. Bazel Training 101 (Part 9): Packages, Rules, Targets, and Labels — source files are owned by their package and need exports_files() for cross-package visibility

  2. Visibility — source-file target visibility, generated-file caveat, legacy implicit export behavior, and best practice to prefer rule targets 1 2 3 4 5 6 7

  3. BUILD filesexports_files(srcs, visibility, licenses) and the default-public behavior when visibility is omitted 1 2 3

  4. Building a Go project using Bazel — real error message and targeted exports_files(..., visibility = [...]) fix for a cross-package source file