| // Copyright 2024 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.buildtool; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.devtools.build.lib.skyframe.ProjectFilesLookupFunction.PROJECT_FILE_NAME; |
| import static org.junit.Assert.assertThrows; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.devtools.build.lib.analysis.Project; |
| import com.google.devtools.build.lib.analysis.util.AnalysisMock; |
| import com.google.devtools.build.lib.buildtool.util.BuildIntegrationTestCase; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions; |
| import com.google.devtools.build.lib.pkgcache.LoadingFailedException; |
| import com.google.devtools.build.lib.pkgcache.PackageOptions; |
| import com.google.devtools.build.lib.pkgcache.PathPackageLocator; |
| import com.google.devtools.build.lib.runtime.QuiescingExecutorsImpl; |
| import com.google.devtools.build.lib.skyframe.BazelSkyframeExecutorConstants; |
| import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor; |
| import com.google.devtools.build.lib.vfs.Root; |
| import com.google.devtools.common.options.Options; |
| import java.util.UUID; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** |
| * Tests how Bazel finds the right {@link Project} for a build. |
| * |
| * <p>This is an integration test between {@link Project} and the build process. It specifically |
| * tests how builds call {@link Project} and use the results meaningfully. For direct unit tests on |
| * projects, use {@link ProjectTest}. |
| */ |
| @RunWith(JUnit4.class) |
| public class ProjectResolutionTest extends BuildIntegrationTestCase { |
| @Before |
| public void setupSkyframePackageSemantics() { |
| getSkyframeExecutor() |
| .preparePackageLoading( |
| new PathPackageLocator( |
| getOutputBase(), |
| ImmutableList.of(Root.fromPath(getWorkspace())), |
| BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY), |
| Options.getDefaults(PackageOptions.class), |
| Options.getDefaults(BuildLanguageOptions.class), |
| UUID.randomUUID(), |
| ImmutableMap.of(), |
| QuiescingExecutorsImpl.forTesting(), |
| new TimestampGranularityMonitor(null)); |
| getSkyframeExecutor().injectExtraPrecomputedValues(AnalysisMock.get().getPrecomputedValues()); |
| } |
| |
| @Test |
| public void buildWithNoProjectFiles() throws Exception { |
| write("pkg/BUILD", "genrule(name='f', cmd = '', srcs=[], outs=['a.out'])"); |
| |
| assertThat( |
| BuildTool.getProjectFile( |
| ImmutableList.of(Label.parseCanonical("//pkg:f")), |
| getSkyframeExecutor(), |
| events.reporter())) |
| .isNull(); |
| } |
| |
| @Test |
| public void buildWithOneProjectFile() throws Exception { |
| write("pkg/BUILD", "genrule(name='f', cmd = '', srcs=[], outs=['a.out'])"); |
| write("pkg/" + PROJECT_FILE_NAME); |
| |
| assertThat( |
| BuildTool.getProjectFile( |
| ImmutableList.of(Label.parseCanonical("//pkg:f")), |
| getSkyframeExecutor(), |
| events.reporter())) |
| .isEqualTo(Label.parseCanonical("//pkg:" + PROJECT_FILE_NAME)); |
| } |
| |
| @Test |
| public void buildWithTwoProjectFiles() throws Exception { |
| write("foo/bar/BUILD", "genrule(name='f', cmd = '', srcs=[], outs=['a.out'])"); |
| write("foo/BUILD"); |
| write("foo/" + PROJECT_FILE_NAME); |
| write("foo/bar/" + PROJECT_FILE_NAME); |
| |
| var thrown = |
| assertThrows( |
| LoadingFailedException.class, |
| () -> |
| BuildTool.getProjectFile( |
| ImmutableList.of(Label.parseCanonical("//foo/bar:f")), |
| getSkyframeExecutor(), |
| events.reporter())); |
| assertThat(thrown) |
| .hasMessageThat() |
| .contains( |
| String.format( |
| "Multiple project files found: [//foo/bar:%s, //foo:%s]", |
| PROJECT_FILE_NAME, PROJECT_FILE_NAME)); |
| } |
| |
| @Test |
| public void twoTargetsSameProjectFile() throws Exception { |
| write("foo/bar/BUILD", "genrule(name='child', cmd = '', srcs=[], outs=['c.out'])"); |
| write("foo/BUILD", "genrule(name='parent', cmd = '', srcs=[], outs=['p.out'])"); |
| write("foo/" + PROJECT_FILE_NAME); |
| |
| assertThat( |
| BuildTool.getProjectFile( |
| ImmutableList.of( |
| Label.parseCanonical("//foo:parent"), Label.parseCanonical("//foo/bar:child")), |
| getSkyframeExecutor(), |
| events.reporter())) |
| .isEqualTo(Label.parseCanonical("//foo:" + PROJECT_FILE_NAME)); |
| } |
| |
| @Test |
| public void twoTargetsDifferentProjectFiles() throws Exception { |
| write("foo/BUILD", "genrule(name='f', cmd = '', srcs=[], outs=['f.out'])"); |
| write("bar/BUILD", "genrule(name='g', cmd = '', srcs=[], outs=['g.out'])"); |
| write("foo/" + PROJECT_FILE_NAME); |
| write("bar/" + PROJECT_FILE_NAME); |
| |
| var thrown = |
| assertThrows( |
| LoadingFailedException.class, |
| () -> |
| BuildTool.getProjectFile( |
| ImmutableList.of( |
| Label.parseCanonical("//foo:f"), Label.parseCanonical("//bar:g")), |
| getSkyframeExecutor(), |
| events.reporter())); |
| assertThat(thrown) |
| .hasMessageThat() |
| .contains( |
| String.format( |
| "Targets have different project settings. " |
| + "For example: [//foo:%s]: //foo:f [//bar:%s]: //bar:g", |
| PROJECT_FILE_NAME, PROJECT_FILE_NAME)); |
| } |
| |
| @Test |
| public void innermostPackageIsAParentDirectory() throws Exception { |
| write("pkg/BUILD", "genrule(name='f', cmd = '', srcs=[], outs=['a.out'])"); |
| write("pkg/" + PROJECT_FILE_NAME); |
| write("pkg/subdir/not_a_build_file"); |
| // Doesn't count because it's not colocated with a BUILD file: |
| write("pkg/subdir" + PROJECT_FILE_NAME); |
| |
| assertThat( |
| BuildTool.getProjectFile( |
| ImmutableList.of(Label.parseCanonical("//pkg/subdir:fake_target")), |
| getSkyframeExecutor(), |
| events.reporter())) |
| .isEqualTo(Label.parseCanonical("//pkg:" + PROJECT_FILE_NAME)); |
| } |
| } |