exports_files()
recommendedMost 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.
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.
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.
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()?
Footnotes
-
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 ↩ -
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
-
BUILD files —
exports_files(srcs, visibility, licenses)and the default-public behavior when visibility is omitted ↩1 ↩2 ↩3 -
Building a Go project using Bazel — real error message and targeted
exports_files(..., visibility = [...])fix for a cross-package source file ↩