Skip to content

Commit

Permalink
Merge pull request #4 from gonzojive/repo-mapping
Browse files Browse the repository at this point in the history
Include repo_mapping in output of pkg_with_runfiles tarball.
  • Loading branch information
gonzojive authored Oct 7, 2024
2 parents 214f115 + 2d261cf commit d9e76e2
Show file tree
Hide file tree
Showing 19 changed files with 401 additions and 10 deletions.
1 change: 1 addition & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module(
name = "pkg_with_runfiles",
version = "0.1",
compatibility_level = 0,
)

bazel_dep(name = "rules_go", version = "0.50.1")
Expand Down
2 changes: 1 addition & 1 deletion private/BUILD → private/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ go_library(
)

go_binary(
name = "private",
name = "packager",
embed = [":private_lib"],
visibility = ["//visibility:public"],
)
28 changes: 23 additions & 5 deletions private/packager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)),
},
Expand Down Expand Up @@ -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, "../")
Expand Down Expand Up @@ -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.
Expand Down
41 changes: 40 additions & 1 deletion private/pkg_with_runfiles.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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 = " ",
),
Expand Down Expand Up @@ -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 = {
Expand All @@ -112,7 +151,7 @@ pkg_with_runfiles = rule(
allow_files = True,
),
"_packager": attr.label(
default = Label("//go/cmd/packager:packager"),
default = Label("//private:packager"),
allow_single_file = True,
executable = True,
cfg = "exec",
Expand Down
1 change: 1 addition & 0 deletions tests/bzlmod-example-intermod/data1fork/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports_files(["message.txt"], visibility = ["//visibility:public"])
4 changes: 4 additions & 0 deletions tests/bzlmod-example-intermod/data1fork/MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module(
name = "data1fork",
version = "0.1",
)
64 changes: 64 additions & 0 deletions tests/bzlmod-example-intermod/data1fork/MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tests/bzlmod-example-intermod/data1fork/message.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello, fork
13 changes: 12 additions & 1 deletion tests/bzlmod-example-intermod/rooty/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
load("@gazelle//:def.bzl", "gazelle")
load("@rules_go//go:def.bzl", "go_binary", "go_library")
load("@pkg_with_runfiles//:defs.bzl", "pkg_with_runfiles")

# gazelle:prefix example.com/rooty
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"],
Expand All @@ -18,3 +22,10 @@ go_binary(
embed = [":rooty_lib"],
visibility = ["//visibility:public"],
)

pkg_with_runfiles(
name = "rooty_packaged",
binary = ":rooty",
binary_path_in_archive = "foo/program",
extra_data = [],
)
8 changes: 8 additions & 0 deletions tests/bzlmod-example-intermod/rooty/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@ module(

bazel_dep(name = "pkg_with_runfiles", version = "0.1")
bazel_dep(name = "data1", version = "0.1", repo_name = "data1_from_rooty")
bazel_dep(name = "userofdata1", version = "0.1")


local_path_override(
module_name = "data1",
path = "../data1",
)

local_path_override(
module_name = "userofdata1",
path = "../userofdata1",
)

local_path_override(
module_name = "pkg_with_runfiles",
path = "../../..",
Expand Down
11 changes: 9 additions & 2 deletions tests/bzlmod-example-intermod/rooty/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@ 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)
os.Exit(1)
}
fmt.Printf("hello, world %s\n", loc)
fmt.Printf("%s", string(contents))
}
Empty file.
23 changes: 23 additions & 0 deletions tests/bzlmod-example-intermod/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash

# For why, see
# https://gist.github.com/mohanpedala/1e2ff5661761d3abd0385e8223e16425
set -euxo pipefail

cd rooty
bazel build //:rooty_packaged

# Create a temporary directory
temp_dir=$(mktemp -d)

# Ensure cleanup of the temporary directory on exit
trap 'rm -rf "$temp_dir"' EXIT

# Copy the tar file to the temporary directory
cp "bazel-bin/rooty_packaged.tar" "$temp_dir/"

# Extract the tar file
tar -xf "$temp_dir/rooty_packaged.tar" -C "$temp_dir"

# Run the program
"$temp_dir/foo/program"
20 changes: 20 additions & 0 deletions tests/bzlmod-example-intermod/userofdata1/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
load("@gazelle//:def.bzl", "gazelle")
load("@rules_go//go:def.bzl", "go_binary", "go_library")

# gazelle:prefix example.com/rooty
gazelle(name = "gazelle")

go_library(
name = "rooty_lib",
srcs = ["main.go"],
data = ["@data1_from_rooty//:message.txt"],
importpath = "example.com/rooty",
visibility = ["//visibility:private"],
deps = ["@rules_go//go/runfiles:go_default_library"],
)

go_binary(
name = "rooty",
embed = [":rooty_lib"],
visibility = ["//visibility:public"],
)
29 changes: 29 additions & 0 deletions tests/bzlmod-example-intermod/userofdata1/MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module(
name = "userofdata1",
version = "0.1",
)

bazel_dep(name = "pkg_with_runfiles", version = "0.1")
bazel_dep(name = "data1", version = "0.1", repo_name = "data1_from_userofdata1")
local_path_override(
module_name = "data1",
path = "../data1",
)

local_path_override(
module_name = "pkg_with_runfiles",
path = "../../..",
)

# The following is necessary to set up Go for the test binary.

bazel_dep(name = "rules_go", version = "0.50.1")
bazel_dep(name = "gazelle", version = "0.39.1")

go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")

# Download an SDK for the host OS & architecture as well as common remote execution platforms.
go_sdk.download(version = "1.23.1")

go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
Loading

0 comments on commit d9e76e2

Please sign in to comment.