Skip to content

Commit

Permalink
Stage repository mapping manifest in sandboxes
Browse files Browse the repository at this point in the history
By adding the repository mapping manifest to runfiles as a root symlink,
it is staged as `foo.runfiles/_repo_mapping` with all execution
strategies. This includes sandboxed and remote execution, which
previously did not stage the manifest at all.

As a side effect, runfiles libraries can now find the repository
mapping manifest via `rlocation("_repo_mapping")`.
  • Loading branch information
fmeum committed Nov 3, 2022
1 parent be75b2a commit 26e7211
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,16 @@ private static RunfilesSupport create(
}
Preconditions.checkState(!runfiles.isEmpty());

Artifact repoMappingManifest =
createRepoMappingManifestAction(ruleContext, runfiles, owningExecutable);
if (repoMappingManifest != null) {
runfiles = new Runfiles.Builder(ruleContext.getWorkspaceName(),
ruleContext.getConfiguration().legacyExternalRunfiles())
.merge(runfiles)
.addRootSymlink(PathFragment.create("_repo_mapping"), repoMappingManifest)
.build();
}

Artifact runfilesInputManifest;
Artifact runfilesManifest;
if (createManifest) {
Expand All @@ -142,8 +152,6 @@ private static RunfilesSupport create(
runfilesInputManifest = null;
runfilesManifest = null;
}
Artifact repoMappingManifest =
createRepoMappingManifestAction(ruleContext, runfiles, owningExecutable);
Artifact runfilesMiddleman =
createRunfilesMiddleman(
ruleContext, owningExecutable, runfiles, runfilesManifest, repoMappingManifest);
Expand Down
59 changes: 36 additions & 23 deletions src/test/py/bazel/bzlmod/bazel_module_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,27 +583,29 @@ def testRunfilesRepoMappingManifest(self):
self.main_registry.setModuleBasePath('projects')
projects_dir = self.main_registry.projects

# Set up a "bare_rule" module that contains the "bare_binary" rule which
# Set up a "bare_rule" module that contains the "bare_test" rule which
# passes runfiles along
self.main_registry.createLocalPathModule('bare_rule', '1.0', 'bare_rule')
projects_dir.joinpath('bare_rule').mkdir(exist_ok=True)
scratchFile(projects_dir.joinpath('bare_rule', 'WORKSPACE'))
scratchFile(projects_dir.joinpath('bare_rule', 'BUILD'))
# The working directory of a test is the subdirectory of the runfiles
# directory corresponding to the main repository.
scratchFile(
projects_dir.joinpath('bare_rule', 'defs.bzl'), [
'def _bare_binary_impl(ctx):',
'def _bare_test_impl(ctx):',
' exe = ctx.actions.declare_file(ctx.label.name)',
' ctx.actions.write(exe,',
' "#/bin/bash\\nif [[ ! -f \\"$0.repo_mapping\\" ]]; then\\necho >&2 \\"ERROR: cannot find repo mapping manifest file\\"\\nexit 1\\nfi",',
' "#/bin/bash\\nif [[ ! -f ../_repo_mapping || ! -s ../_repo_mapping ]]; then\\necho >&2 \\"ERROR: cannot find repo mapping manifest file\\"\\nexit 1\\nfi",',
' True)',
' runfiles = ctx.runfiles(files=ctx.files.data)',
' for data in ctx.attr.data:',
' runfiles = runfiles.merge(data[DefaultInfo].default_runfiles)',
' return DefaultInfo(files=depset(direct=[exe]), executable=exe, runfiles=runfiles)',
'bare_binary=rule(',
' implementation=_bare_binary_impl,',
'bare_test=rule(',
' implementation=_bare_test_impl,',
' attrs={"data":attr.label_list(allow_files=True)},',
' executable=True,',
' test=True,',
')',
])

Expand All @@ -617,8 +619,8 @@ def testRunfilesRepoMappingManifest(self):
self.ScratchFile('WORKSPACE')
self.ScratchFile('WORKSPACE.bzlmod', ['workspace(name="me_ws")'])
self.ScratchFile('BUILD', [
'load("@bare_rule//:defs.bzl", "bare_binary")',
'bare_binary(name="me",data=["@foo"])',
'load("@bare_rule//:defs.bzl", "bare_test")',
'bare_test(name="me",data=["@foo"])',
])
self.main_registry.createLocalPathModule('foo', '1.0', 'foo', {
'quux': '1.0',
Expand All @@ -633,40 +635,51 @@ def testRunfilesRepoMappingManifest(self):
self.main_registry.createLocalPathModule('quux', '2.0', 'quux2',
{'bare_rule': '1.0'})
for dir_name, build_file in [
('foo', 'bare_binary(name="foo",data=["@quux"])'),
('bar', 'bare_binary(name="bar",data=["@quux"])'),
('quux1', 'bare_binary(name="quux")'),
('quux2', 'bare_binary(name="quux")'),
('foo', 'bare_test(name="foo",data=["@quux"])'),
('bar', 'bare_test(name="bar",data=["@quux"])'),
('quux1', 'bare_test(name="quux")'),
('quux2', 'bare_test(name="quux")'),
]:
projects_dir.joinpath(dir_name).mkdir(exist_ok=True)
scratchFile(projects_dir.joinpath(dir_name, 'WORKSPACE'))
scratchFile(
projects_dir.joinpath(dir_name, 'BUILD'), [
'load("@bare_rule//:defs.bzl", "bare_binary")',
'load("@bare_rule//:defs.bzl", "bare_test")',
'package(default_visibility=["//visibility:public"])',
build_file,
])

# We use a shell script to check that the binary itself can see the repo
# mapping manifest. This obviously doesn't work on Windows, so we just build
# the target. TODO(wyv): make this work on Windows by using Batch.
bazel_command = 'build' if self.IsWindows() else 'run'
# On Linux and macOS, the script is executed in the sandbox, so we verify
# that the repository mapping is present in it.
bazel_command = 'build' if self.IsWindows() else 'test'

# Finally we get to build stuff!
self.RunBazel([bazel_command, '//:me'], allow_failure=False)
with open(self.Path('bazel-bin/me.repo_mapping'), 'r') as f:
self.assertEqual(
f.read().strip(), """,foo,foo~1.0
exit_code, stderr, stdout = self.RunBazel(
[bazel_command, '//:me', '--test_output=errors'],
allow_failure=True)
self.AssertExitCode(0, exit_code, stderr, stdout)
for path in ('bazel-bin/me.repo_mapping',
'bazel-bin/me.runfiles/_repo_mapping'):
with open(self.Path(path), 'r') as f:
self.assertEqual(
f.read().strip(), """,foo,foo~1.0
,me,_main
,me_ws,_main
foo~1.0,foo,foo~1.0
foo~1.0,quux,quux~2.0
quux~2.0,quux,quux~2.0""")
self.RunBazel([bazel_command, '@bar//:bar'], allow_failure=False)
with open(self.Path('bazel-bin/external/bar~2.0/bar.repo_mapping'),
'r') as f:
self.assertEqual(
f.read().strip(), """bar~2.0,bar,bar~2.0
exit_code, stderr, stdout = self.RunBazel(
[bazel_command, '@bar//:bar', '--test_output=errors'],
allow_failure=True)
self.AssertExitCode(0, exit_code, stderr, stdout)
for path in ('bazel-bin/external/bar~2.0/bar.repo_mapping',
'bazel-bin/external/bar~2.0/bar.runfiles/_repo_mapping'):
with open(self.Path(path), 'r') as f:
self.assertEqual(
f.read().strip(), """bar~2.0,bar,bar~2.0
bar~2.0,quux,quux~2.0
quux~2.0,quux,quux~2.0""")

Expand Down

0 comments on commit 26e7211

Please sign in to comment.