diff --git a/private/packager.go b/private/packager.go index 0260878..3d8a3e9 100644 --- a/private/packager.go +++ b/private/packager.go @@ -71,8 +71,14 @@ func writeTarEntries(parsedSpec *BinarySpec, tw *tar.Writer) error { entries = append(entries, entry) } + var allFiles []*File + allFiles = append(allFiles, parsedSpec.BinaryRunfiles.Files...) + if parsedSpec.RepoMappingManifest != nil { + allFiles = append(allFiles, parsedSpec.RepoMappingManifest) + } + eg := errgroup.Group{} - for _, runfile := range parsedSpec.BinaryRunfiles.Files { + for _, runfile := range allFiles { runfile := runfile eg.Go(func() error { contents, err := os.ReadFile(runfile.Path) @@ -86,7 +92,7 @@ func writeTarEntries(parsedSpec *BinarySpec, tw *tar.Writer) error { push(tarEntry{ header: &tar.Header{ - Name: nameInOutputArchive(runfile, parsedSpec.WorkspaceName, parsedSpec.BinaryTargetExecutableFile, parsedSpec.ExecutableNameInArchive), + Name: nameInOutputArchive(runfile, parsedSpec.WorkspaceName, parsedSpec.BinaryTargetExecutableFile, parsedSpec.RepoMappingManifest, parsedSpec.ExecutableNameInArchive), Mode: int64(fileInfo.Mode().Perm()), Size: int64(len(contents)), }, @@ -114,16 +120,19 @@ func writeTarEntries(parsedSpec *BinarySpec, tw *tar.Writer) error { return nil } -func nameInOutputArchive(runfile *File, workspaceName string, executable *File, executableNameInArchive string) string { +func nameInOutputArchive(runfile *File, workspaceName string, executable, repoMappingManifest *File, executableNameInArchive string) string { // The layout here was inferred from // https://docs.google.com/document/d/1skNx5o-8k5-YXUAyEETvr39eKoh9fecJbGUquPh5iy8/edit // and from looking at example outputs of executables. - // - // TODO - reddaly: Review https://github.com/fmeum/proposals/blob/main/designs/2022-07-21-locating-runfiles-with-bzlmod.md if runfile.Path == executable.Path { return executableNameInArchive } runfilesRoot := executableNameInArchive + ".runfiles" + if repoMappingManifest != nil && runfile.Path == repoMappingManifest.Path { + // The _repo_mapping should appear within the runfiles directory. + // https://github.com/bazelbuild/proposals/blob/main/designs/2022-07-21-locating-runfiles-with-bzlmod.md#1-emit-a-repository-mapping-manifest-for-each-executable-target + return path.Join(runfilesRoot, "_repo_mapping") + } // Data dependencies in repositories other than the root repo have prefix "../". withoutPrefix := strings.TrimPrefix(runfile.ShortPath, "../") @@ -163,6 +172,15 @@ type BinarySpec struct { // The name of the file in the output. ExecutableNameInArchive string `json:"executable_name_in_archive"` + + // The reppository mapping file used to "translate an apparent repository + // name to a canonical repository name" according to the [proposal] + // that added this functionality to Bazel. + // + // This file may not be present when bzlmod is not enabled. + // + // [proposal]: https://github.com/bazelbuild/proposals/blob/main/designs/2022-07-21-locating-runfiles-with-bzlmod.md + RepoMappingManifest *File `json:"repo_mapping_manifest"` } // Runfiles contains information about a bazel runfiles object. diff --git a/private/pkg_with_runfiles.bzl b/private/pkg_with_runfiles.bzl index 5e2517a..2f9c126 100644 --- a/private/pkg_with_runfiles.bzl +++ b/private/pkg_with_runfiles.bzl @@ -32,12 +32,18 @@ def _generate_input_spec(ctx): extra_data_label[DefaultInfo].default_runfiles for extra_data_label in ctx.attr.extra_data ]) + repo_mapping_manifest = _get_repo_mapping_manifest(target_info) inputs_to_packager = ( target_info.files.to_list() + target_runfiles.files.to_list() ) + if repo_mapping_manifest: + inputs_to_packager.append(repo_mapping_manifest) + + #fail("repo_mapping_manifest = {}".format(repo_mapping_manifest)) + return struct( inputs_to_packager = inputs_to_packager, spec_json = json.encode_indent( @@ -54,6 +60,7 @@ def _generate_input_spec(ctx): for f in target_info.files.to_list() ], "binary_runfiles": _runfiles_to_dict(target_runfiles), + "repo_mapping_manifest": _file_to_dict(repo_mapping_manifest) if repo_mapping_manifest else None }, indent = " ", ), @@ -93,6 +100,38 @@ def _file_to_dict(file): "owner": str(file.owner), } +def _get_files_to_run_provider(default_info): + """Safely retrieve FilesToRunProvider from a DefaultInfo. + + Args: + default_info: A DefaultInfo instance of a target. + + Returns: + FilesToRunProvider or None: FilesToRunProvider if found in target + provider, otherwise None. FilesToRunProvider should always + be returned for executable targets with a newer version of + bazel. + """ + if not hasattr(default_info, "files_to_run"): + return None + return default_info.files_to_run + +def _get_repo_mapping_manifest(default_info): + """Safely retrieve repo_mapping_manifest from a DefaultInfo, if it exists. + + Args: + default_info: A DefaultInfo instance of a target. + + Returns: + File or None: repo_mapping_manifest + """ + files_to_run_provider = _get_files_to_run_provider(default_info) + if files_to_run_provider: + # repo_mapping_manifest may not exist in older Bazel versions (<7.0.0) + # https://github.com/bazelbuild/bazel/issues/19937 + return getattr(files_to_run_provider, "repo_mapping_manifest") + return None + pkg_with_runfiles = rule( implementation = _pkg_with_runfiles_impl, attrs = { diff --git a/tests/bzlmod-example-intermod/rooty/BUILD.bazel b/tests/bzlmod-example-intermod/rooty/BUILD.bazel index 8bbf36e..3a83014 100644 --- a/tests/bzlmod-example-intermod/rooty/BUILD.bazel +++ b/tests/bzlmod-example-intermod/rooty/BUILD.bazel @@ -8,7 +8,10 @@ gazelle(name = "gazelle") go_library( name = "rooty_lib", srcs = ["main.go"], - data = ["@data1_from_rooty//:message.txt"], + data = [ + "@data1_from_rooty//:message.txt", + ":unused.txt", + ], importpath = "example.com/rooty", visibility = ["//visibility:private"], deps = ["@rules_go//go/runfiles:go_default_library"], diff --git a/tests/bzlmod-example-intermod/rooty/main.go b/tests/bzlmod-example-intermod/rooty/main.go index fc268fc..6e99f29 100644 --- a/tests/bzlmod-example-intermod/rooty/main.go +++ b/tests/bzlmod-example-intermod/rooty/main.go @@ -13,11 +13,13 @@ func main() { fmt.Printf("error loading runfiles: %v\n", err) os.Exit(1) } - loc, err := rfiles.Rlocation("data1_from_rooty/message.txt") + runfileSpec := "data1_from_rooty/message.txt" + loc, err := rfiles.Rlocation(runfileSpec) if err != nil { fmt.Printf("error determining runfile message.txt: %v\n", err) os.Exit(1) } + fmt.Printf("Rlocation(%q) = %s\n", runfileSpec, loc) contents, err := os.ReadFile(loc) if err != nil { fmt.Printf("error loading runfile message.txt: %v\n", err) diff --git a/tests/bzlmod-example-intermod/rooty/unused.txt b/tests/bzlmod-example-intermod/rooty/unused.txt new file mode 100644 index 0000000..e69de29