0.2.7 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. //frobber/data:readme.txt remains 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. 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. Exported files can be shared without being public.

The opposite mistake is exporting a raw file too narrowly, then depending on it from another package:

Won't build
# //data/BUILD.bazel
exports_files(
    ["private.txt"],
    visibility = ["//visibility:private"],
)

# //app/BUILD.bazel
genrule(
    name = "copy_private",
    srcs = ["//data:private.txt"],
    outs = ["private_copy.txt"],
    cmd = "cp $(location //data:private.txt) $@",
)
ERROR: .../app/BUILD.bazel:1:8: in genrule rule //app:copy_private: Visibility error:
target '//data:private.txt' is not visible from
target '//app:copy_private'
Recommendation: modify the visibility declaration if you think the dependency is legitimate. ... To set the visibility of that source file target, use the exports_files() function

Read this as an analysis-phase visibility error on a source-file target, not as a missing file. The fix is to grant the consuming package access, or to wrap the file in a rule target if the file is really part of a broader API:

exports_files(
    ["shared.txt"],
    visibility = ["//app:__pkg__"],
)

Reproduce this error

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.

Do not 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. For example, wrap a .java file in a non-private java_library instead of exporting the file directly2.

The generated-output filegroup shows the complementary pattern: generated files are exposed through their producing rule, not exports_files().

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 · 2 questions

1.True or false: how exports_files() works.

Choose True or False for each sentence

If you omit the visibility parameter in exports_files(), the exported files are visible to every package by default.
exports_files() can be used to override the visibility of a generated file produced by a rule.
exports_files() is the preferred way to share build-facing interfaces — it should be used instead of wrapping files in a rule target.
In the recommended explicit-visibility model, source files need exports_files() before other packages can reference them by label.

2.Which scenario is the correct use case for exports_files()?

Select one answer

0 of 2 answered

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