| // Copyright 2022 The Bazel Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| package com.google.devtools.build.lib.analysis; |
| |
| import static com.google.common.collect.ImmutableList.toImmutableList; |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.createModuleKey; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.devtools.build.lib.actions.Action; |
| import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; |
| import com.google.devtools.build.lib.bazel.bzlmod.BazelLockFileFunction; |
| import com.google.devtools.build.lib.bazel.bzlmod.BazelModuleResolutionFunction; |
| import com.google.devtools.build.lib.bazel.bzlmod.FakeRegistry; |
| import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileFunction; |
| import com.google.devtools.build.lib.bazel.repository.RepositoryOptions.BazelCompatibilityMode; |
| import com.google.devtools.build.lib.bazel.repository.RepositoryOptions.CheckDirectDepsMode; |
| import com.google.devtools.build.lib.bazel.repository.RepositoryOptions.LockfileMode; |
| import com.google.devtools.build.lib.skyframe.PrecomputedValue; |
| import com.google.devtools.build.lib.skyframe.PrecomputedValue.Injected; |
| import com.google.devtools.build.lib.vfs.Path; |
| import java.util.Map.Entry; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** Tests that the repo mapping manifest file is properly generated for runfiles. */ |
| @RunWith(JUnit4.class) |
| public class RunfilesRepoMappingManifestTest extends BuildViewTestCase { |
| private Path moduleRoot; |
| private FakeRegistry registry; |
| |
| @Override |
| protected ImmutableList<Injected> extraPrecomputedValues() throws Exception { |
| moduleRoot = scratch.dir("modules"); |
| registry = FakeRegistry.DEFAULT_FACTORY.newFakeRegistry(moduleRoot.getPathString()); |
| return ImmutableList.of( |
| PrecomputedValue.injected( |
| ModuleFileFunction.REGISTRIES, ImmutableList.of(registry.getUrl())), |
| PrecomputedValue.injected(ModuleFileFunction.IGNORE_DEV_DEPS, false), |
| PrecomputedValue.injected(ModuleFileFunction.MODULE_OVERRIDES, ImmutableMap.of()), |
| PrecomputedValue.injected( |
| BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES, CheckDirectDepsMode.WARNING), |
| PrecomputedValue.injected( |
| BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE, BazelCompatibilityMode.ERROR), |
| PrecomputedValue.injected(BazelLockFileFunction.LOCKFILE_MODE, LockfileMode.OFF), |
| PrecomputedValue.injected( |
| BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS, ImmutableList.of())); |
| } |
| |
| @Before |
| public void enableBzlmod() throws Exception { |
| setBuildLanguageOptions("--enable_bzlmod"); |
| } |
| |
| /** |
| * Sets up a Bazel module bare_rule@1.0, which provides a bare_binary rule that passes along |
| * runfiles in the data attribute, and does nothing else. |
| */ |
| @Before |
| public void setupBareBinaryRule() throws Exception { |
| registry.addModule( |
| createModuleKey("bare_rule", "1.0"), "module(name='bare_rule',version='1.0')"); |
| scratch.overwriteFile(moduleRoot.getRelative("bare_rule~1.0/WORKSPACE").getPathString()); |
| scratch.overwriteFile( |
| moduleRoot.getRelative("bare_rule~1.0/defs.bzl").getPathString(), |
| "def _bare_binary_impl(ctx):", |
| " exe = ctx.actions.declare_file(ctx.label.name)", |
| " ctx.actions.write(exe, 'i got nothing', 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,", |
| " attrs={'data':attr.label_list(allow_files=True)},", |
| " executable=True,", |
| ")"); |
| scratch.overwriteFile( |
| moduleRoot.getRelative("bare_rule~1.0/BUILD").getPathString(), |
| "load('//:defs.bzl', 'bare_binary')", |
| "bare_binary(name='bare_binary')"); |
| } |
| |
| private ImmutableList<String> getRepoMappingManifestForTarget(String label) throws Exception { |
| Action action = getGeneratingAction(getRunfilesSupport(label).getRepoMappingManifest()); |
| assertThat(action).isInstanceOf(RepoMappingManifestAction.class); |
| return ((RepoMappingManifestAction) action) |
| .newDeterministicWriter(null) |
| .getBytes() |
| .toStringUtf8() |
| .lines() |
| .collect(toImmutableList()); |
| } |
| |
| @Test |
| public void diamond() throws Exception { |
| rewriteWorkspace("workspace(name='aaa_ws')"); |
| scratch.overwriteFile( |
| "MODULE.bazel", |
| "module(name='aaa',version='1.0')", |
| "bazel_dep(name='bbb',version='1.0')", |
| "bazel_dep(name='ccc',version='2.0')", |
| "bazel_dep(name='bare_rule',version='1.0')"); |
| registry.addModule( |
| createModuleKey("bbb", "1.0"), |
| "module(name='bbb',version='1.0')", |
| "bazel_dep(name='ddd',version='1.0')", |
| "bazel_dep(name='bare_rule',version='1.0')"); |
| registry.addModule( |
| createModuleKey("ccc", "2.0"), |
| "module(name='ccc',version='2.0')", |
| "bazel_dep(name='ddd',version='2.0')", |
| "bazel_dep(name='bare_rule',version='1.0')"); |
| registry.addModule( |
| createModuleKey("ddd", "1.0"), |
| "module(name='ddd',version='1.0')", |
| "bazel_dep(name='bare_rule',version='1.0')"); |
| registry.addModule( |
| createModuleKey("ddd", "2.0"), |
| "module(name='ddd',version='2.0')", |
| "bazel_dep(name='bare_rule',version='1.0')"); |
| |
| scratch.overwriteFile( |
| "BUILD", |
| "load('@bare_rule//:defs.bzl', 'bare_binary')", |
| "bare_binary(name='aaa',data=['@bbb'])"); |
| ImmutableMap<String, String> buildFiles = |
| ImmutableMap.of( |
| "bbb~1.0", "bare_binary(name='bbb',data=['@ddd'])", |
| "ccc~2.0", "bare_binary(name='ccc',data=['@ddd'])", |
| "ddd~1.0", "bare_binary(name='ddd')", |
| "ddd~2.0", "bare_binary(name='ddd')"); |
| for (Entry<String, String> entry : buildFiles.entrySet()) { |
| scratch.overwriteFile( |
| moduleRoot.getRelative(entry.getKey()).getRelative("WORKSPACE").getPathString()); |
| scratch.overwriteFile( |
| moduleRoot.getRelative(entry.getKey()).getRelative("BUILD").getPathString(), |
| "load('@bare_rule//:defs.bzl', 'bare_binary')", |
| entry.getValue()); |
| } |
| |
| assertThat(getRepoMappingManifestForTarget("//:aaa")) |
| .containsExactly( |
| ",aaa,_main", |
| ",aaa_ws,_main", |
| ",bbb,bbb~1.0", |
| "bbb~1.0,bbb,bbb~1.0", |
| "bbb~1.0,ddd,ddd~2.0", |
| "ddd~2.0,ddd,ddd~2.0") |
| .inOrder(); |
| assertThat(getRepoMappingManifestForTarget("@@ccc~2.0//:ccc")) |
| .containsExactly("ccc~2.0,ccc,ccc~2.0", "ccc~2.0,ddd,ddd~2.0", "ddd~2.0,ddd,ddd~2.0") |
| .inOrder(); |
| } |
| |
| @Test |
| public void runfilesFromToolchain() throws Exception { |
| rewriteWorkspace("workspace(name='main')"); |
| scratch.overwriteFile("MODULE.bazel", "bazel_dep(name='tooled_rule',version='1.0')"); |
| // tooled_rule offers a tooled_binary rule, which uses a toolchain backed by a binary from |
| // bare_rule. tooled_binary explicitly requests that runfiles from this binary are included in |
| // its runfiles tree, which would mean that bare_rule should be included in the repo mapping |
| // manifest. |
| registry.addModule( |
| createModuleKey("tooled_rule", "1.0"), |
| "module(name='tooled_rule',version='1.0')", |
| "bazel_dep(name='bare_rule',version='1.0')", |
| "register_toolchains('//:all')"); |
| scratch.overwriteFile(moduleRoot.getRelative("tooled_rule~1.0/WORKSPACE").getPathString()); |
| scratch.overwriteFile( |
| moduleRoot.getRelative("tooled_rule~1.0/defs.bzl").getPathString(), |
| "def _tooled_binary_impl(ctx):", |
| " exe = ctx.actions.declare_file(ctx.label.name)", |
| " ctx.actions.write(exe, 'i got something', True)", |
| " runfiles = ctx.runfiles(files=ctx.files.data)", |
| " for data in ctx.attr.data:", |
| " runfiles = runfiles.merge(data[DefaultInfo].default_runfiles)", |
| " runfiles = runfiles.merge(", |
| " ctx.toolchains['//:toolchain_type'].tooled_info[DefaultInfo].default_runfiles)", |
| " return DefaultInfo(files=depset(direct=[exe]), executable=exe, runfiles=runfiles)", |
| "tooled_binary = rule(", |
| " implementation=_tooled_binary_impl,", |
| " attrs={'data':attr.label_list(allow_files=True)},", |
| " executable=True,", |
| " toolchains=['//:toolchain_type'],", |
| ")", |
| "", |
| "def _tooled_toolchain_rule_impl(ctx):", |
| " return [platform_common.ToolchainInfo(tooled_info = ctx.attr.backing_binary)]", |
| "tooled_toolchain_rule=rule(_tooled_toolchain_rule_impl,", |
| " attrs={'backing_binary':attr.label()})", |
| "def tooled_toolchain(name, backing_binary):", |
| " tooled_toolchain_rule(name=name+'_impl',backing_binary=backing_binary)", |
| " native.toolchain(", |
| " name=name,", |
| " toolchain=':'+name+'_impl',", |
| " toolchain_type=Label('//:toolchain_type'),", |
| " )"); |
| scratch.overwriteFile( |
| moduleRoot.getRelative("tooled_rule~1.0/BUILD").getPathString(), |
| "load('//:defs.bzl', 'tooled_toolchain')", |
| "toolchain_type(name='toolchain_type')", |
| "tooled_toolchain(name='tooled_toolchain', backing_binary='@bare_rule//:bare_binary')"); |
| |
| scratch.overwriteFile( |
| "BUILD", |
| "load('@tooled_rule//:defs.bzl', 'tooled_binary')", |
| "tooled_binary(name='tooled')"); |
| |
| assertThat(getRepoMappingManifestForTarget("//:tooled")) |
| .containsExactly( |
| ",main,_main", |
| "bare_rule~1.0,bare_rule,bare_rule~1.0", |
| "tooled_rule~1.0,bare_rule,bare_rule~1.0") |
| .inOrder(); |
| } |
| } |